Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
run: bandit -r app/ -f json -o bandit-report.json || true

- name: Upload security reports
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: security-reports
path: |
Expand Down Expand Up @@ -65,14 +65,15 @@ jobs:
run: pylint app/ --output-format=json > pylint-report.json || true

- name: Upload code quality reports
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: code-quality-reports
path: pylint-report.json

test:
runs-on: ubuntu-latest
name: Unit Tests
timeout-minutes: 15
strategy:
matrix:
python-version: ['3.11', '3.12']
Expand Down
85 changes: 54 additions & 31 deletions app/advanced_order_automation.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
Implements sophisticated order types and automated execution logic
"""

from __future__ import annotations

import asyncio
import json
import os
Expand All @@ -25,6 +27,25 @@
ORDER_MODELS_AVAILABLE = False
print("Warning: Order management models not available")

# Minimal stubs so that class-body annotations and default values
# (e.g. ``status: OrderStatus = OrderStatus.PENDING``) don't raise
# NameError when the real module is unavailable (e.g. sqlalchemy missing).
class OrderSide(Enum): # type: ignore[no-redef]
BUY = "buy"
SELL = "sell"

class OrderStatus(Enum): # type: ignore[no-redef]
PENDING = "pending"
EXECUTING = "executing"
FILLED = "filled"
CANCELLED = "cancelled"
ERROR = "error"

class OrderType(Enum): # type: ignore[no-redef]
MARKET = "market"
LIMIT = "limit"


try:
import ccxt

Expand Down Expand Up @@ -188,8 +209,7 @@ def _init_db(self):
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()

cursor.execute(
"""
cursor.execute("""
CREATE TABLE IF NOT EXISTS advanced_orders (
id TEXT PRIMARY KEY,
order_type TEXT NOT NULL,
Expand All @@ -215,11 +235,9 @@ def _init_db(self):
filled_quantity REAL DEFAULT 0.0,
average_fill_price REAL DEFAULT 0.0
)
"""
)
""")

cursor.execute(
"""
cursor.execute("""
CREATE TABLE IF NOT EXISTS oco_orders (
id TEXT PRIMARY KEY,
primary_order_id TEXT NOT NULL,
Expand All @@ -229,11 +247,9 @@ def _init_db(self):
FOREIGN KEY (primary_order_id) REFERENCES advanced_orders (id),
FOREIGN KEY (secondary_order_id) REFERENCES advanced_orders (id)
)
"""
)
""")

cursor.execute(
"""
cursor.execute("""
CREATE TABLE IF NOT EXISTS bracket_orders (
id TEXT PRIMARY KEY,
parent_order_id TEXT NOT NULL,
Expand All @@ -244,11 +260,9 @@ def _init_db(self):
FOREIGN KEY (take_profit_order_id) REFERENCES advanced_orders (id),
FOREIGN KEY (stop_loss_order_id) REFERENCES advanced_orders (id)
)
"""
)
""")

cursor.execute(
"""
cursor.execute("""
CREATE TABLE IF NOT EXISTS automation_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
order_id TEXT NOT NULL,
Expand All @@ -258,8 +272,7 @@ def _init_db(self):
timestamp TEXT NOT NULL,
details_json TEXT
)
"""
)
""")

conn.commit()

Expand Down Expand Up @@ -360,9 +373,11 @@ def create_bracket_order(
conditions=[
OrderCondition(
id=str(uuid.uuid4()),
condition_type=ConditionType.PRICE_ABOVE
if entry_order.side == OrderSide.BUY
else ConditionType.PRICE_BELOW,
condition_type=(
ConditionType.PRICE_ABOVE
if entry_order.side == OrderSide.BUY
else ConditionType.PRICE_BELOW
),
symbol=entry_order.symbol,
operator=">=",
target_value=take_profit_price,
Expand All @@ -381,9 +396,11 @@ def create_bracket_order(
conditions=[
OrderCondition(
id=str(uuid.uuid4()),
condition_type=ConditionType.PRICE_BELOW
if entry_order.side == OrderSide.BUY
else ConditionType.PRICE_ABOVE,
condition_type=(
ConditionType.PRICE_BELOW
if entry_order.side == OrderSide.BUY
else ConditionType.PRICE_ABOVE
),
symbol=entry_order.symbol,
operator="<=",
target_value=stop_loss_price,
Expand Down Expand Up @@ -812,17 +829,23 @@ def _save_order(self, order: AdvancedOrder):
order.expire_time.isoformat() if order.expire_time else None,
order.min_quantity,
order.display_quantity,
json.dumps([asdict(c) for c in order.conditions])
if order.conditions
else None,
(
json.dumps([asdict(c) for c in order.conditions])
if order.conditions
else None
),
order.parent_order_id,
json.dumps(order.child_order_ids)
if order.child_order_ids
else None,
(
json.dumps(order.child_order_ids)
if order.child_order_ids
else None
),
order.execution_algorithm,
json.dumps(order.algorithm_params)
if order.algorithm_params
else None,
(
json.dumps(order.algorithm_params)
if order.algorithm_params
else None
),
order.status.value,
order.created_at.isoformat(),
order.updated_at.isoformat(),
Expand Down
24 changes: 15 additions & 9 deletions app/advanced_order_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -852,9 +852,11 @@ def create_oco_order(self):
symbol=self.primary_symbol_var.get(),
side=OrderSide(self.primary_side_var.get()),
quantity=float(self.primary_quantity_var.get()),
limit_price=float(self.primary_price_var.get())
if self.primary_price_var.get()
else None,
limit_price=(
float(self.primary_price_var.get())
if self.primary_price_var.get()
else None
),
)

secondary_order = AdvancedOrder(
Expand All @@ -863,9 +865,11 @@ def create_oco_order(self):
symbol=self.secondary_symbol_var.get(),
side=OrderSide(self.secondary_side_var.get()),
quantity=float(self.secondary_quantity_var.get()),
limit_price=float(self.secondary_price_var.get())
if self.secondary_price_var.get()
else None,
limit_price=(
float(self.secondary_price_var.get())
if self.secondary_price_var.get()
else None
),
)

oco_id = self.automation_engine.create_oco_order(
Expand Down Expand Up @@ -938,9 +942,11 @@ def create_bracket_order(self):
symbol=self.bracket_symbol_var.get(),
side=OrderSide(self.bracket_side_var.get()),
quantity=float(self.bracket_quantity_var.get()),
limit_price=float(self.bracket_entry_price_var.get())
if self.bracket_entry_price_var.get()
else None,
limit_price=(
float(self.bracket_entry_price_var.get())
if self.bracket_entry_price_var.get()
else None
),
)

bracket_id = self.automation_engine.create_bracket_order(
Expand Down
24 changes: 8 additions & 16 deletions app/advanced_risk_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -494,8 +494,7 @@ def _setup_database(self):
cursor = conn.cursor()

# Risk alerts table
cursor.execute(
"""
cursor.execute("""
CREATE TABLE IF NOT EXISTS risk_alerts (
id TEXT PRIMARY KEY,
alert_type TEXT NOT NULL,
Expand All @@ -510,12 +509,10 @@ def _setup_database(self):
acknowledged_at DATETIME,
resolved_at DATETIME
)
"""
)
""")

# Risk metrics table
cursor.execute(
"""
cursor.execute("""
CREATE TABLE IF NOT EXISTS risk_metrics (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
Expand All @@ -527,12 +524,10 @@ def _setup_database(self):
symbol TEXT,
timestamp DATETIME NOT NULL
)
"""
)
""")

# Portfolio history table
cursor.execute(
"""
cursor.execute("""
CREATE TABLE IF NOT EXISTS portfolio_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp DATETIME NOT NULL,
Expand All @@ -544,19 +539,16 @@ def _setup_database(self):
var_99 REAL,
num_positions INTEGER NOT NULL
)
"""
)
""")

# Risk limits configuration table
cursor.execute(
"""
cursor.execute("""
CREATE TABLE IF NOT EXISTS risk_limits (
id INTEGER PRIMARY KEY,
config_data TEXT NOT NULL,
updated_at DATETIME NOT NULL
)
"""
)
""")

# Create indexes
cursor.execute(
Expand Down
8 changes: 5 additions & 3 deletions app/advanced_take_profit.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,9 +327,11 @@ def initialize_trailing_profit(self, order_id: str, order_data: Dict) -> bool:
"min_profit_price": min_profit_price,
"trailing_amount": trailing_amount,
"trailing_percent": trailing_percent,
"min_profit_reached": current_price >= min_profit_price
if side.upper() == "SELL"
else current_price <= min_profit_price,
"min_profit_reached": (
current_price >= min_profit_price
if side.upper() == "SELL"
else current_price <= min_profit_price
),
"last_update": datetime.now(),
"max_profit_pct": self._calculate_profit_percentage(
entry_price, current_price, side
Expand Down
22 changes: 13 additions & 9 deletions app/backtesting_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,21 +451,25 @@ def _calculate_performance_metrics(

metrics = {
"total_return": total_return,
"annualized_return": (1 + total_return) ** (252 / len(daily_returns)) - 1
if len(daily_returns) > 0
else 0,
"annualized_return": (
(1 + total_return) ** (252 / len(daily_returns)) - 1
if len(daily_returns) > 0
else 0
),
"volatility": volatility,
"sharpe_ratio": sharpe_ratio,
"max_drawdown": abs(max_drawdown),
"calmar_ratio": (total_return / abs(max_drawdown))
if max_drawdown != 0
else 0,
"calmar_ratio": (
(total_return / abs(max_drawdown)) if max_drawdown != 0 else 0
),
"win_rate": win_rate,
"profit_factor": profit_factor,
"total_trades": len(self.trades),
"avg_trade_duration": np.mean([t.holding_period.days for t in self.trades])
if self.trades
else 0,
"avg_trade_duration": (
np.mean([t.holding_period.days for t in self.trades])
if self.trades
else 0
),
"best_trade": max([t.pnl for t in self.trades]) if self.trades else 0,
"worst_trade": min([t.pnl for t in self.trades]) if self.trades else 0,
}
Expand Down
Loading
Loading