summaryrefslogtreecommitdiff
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/api/src/trading_api/main.py1
-rw-r--r--services/api/src/trading_api/routers/orders.py1
-rw-r--r--services/api/src/trading_api/routers/portfolio.py3
-rw-r--r--services/api/src/trading_api/routers/strategies.py2
-rw-r--r--services/api/tests/test_api.py5
-rw-r--r--services/data-collector/src/data_collector/main.py4
-rw-r--r--services/order-executor/src/order_executor/main.py4
-rw-r--r--services/order-executor/src/order_executor/risk_manager.py13
-rw-r--r--services/portfolio-manager/src/portfolio_manager/main.py4
-rw-r--r--services/strategy-engine/src/strategy_engine/main.py4
-rw-r--r--services/strategy-engine/strategies/combined_strategy.py3
-rw-r--r--services/strategy-engine/tests/test_combined_strategy.py29
12 files changed, 54 insertions, 19 deletions
diff --git a/services/api/src/trading_api/main.py b/services/api/src/trading_api/main.py
index 61cfe36..39f7b43 100644
--- a/services/api/src/trading_api/main.py
+++ b/services/api/src/trading_api/main.py
@@ -1,4 +1,5 @@
"""Trading Platform REST API."""
+
from contextlib import asynccontextmanager
from fastapi import FastAPI
diff --git a/services/api/src/trading_api/routers/orders.py b/services/api/src/trading_api/routers/orders.py
index 989694f..d0b9fa6 100644
--- a/services/api/src/trading_api/routers/orders.py
+++ b/services/api/src/trading_api/routers/orders.py
@@ -1,4 +1,5 @@
"""Order endpoints."""
+
from fastapi import APIRouter, Request
from shared.sa_models import OrderRow, SignalRow
from sqlalchemy import select
diff --git a/services/api/src/trading_api/routers/portfolio.py b/services/api/src/trading_api/routers/portfolio.py
index f4169cb..3b30e1d 100644
--- a/services/api/src/trading_api/routers/portfolio.py
+++ b/services/api/src/trading_api/routers/portfolio.py
@@ -1,6 +1,7 @@
"""Portfolio endpoints."""
+
from fastapi import APIRouter, Request
-from shared.sa_models import PositionRow, PortfolioSnapshotRow
+from shared.sa_models import PositionRow
from sqlalchemy import select
router = APIRouter()
diff --git a/services/api/src/trading_api/routers/strategies.py b/services/api/src/trading_api/routers/strategies.py
index a8d778d..2861eec 100644
--- a/services/api/src/trading_api/routers/strategies.py
+++ b/services/api/src/trading_api/routers/strategies.py
@@ -1,4 +1,5 @@
"""Strategy endpoints."""
+
import sys
from pathlib import Path
@@ -16,6 +17,7 @@ router = APIRouter()
async def list_strategies():
"""List available strategies."""
from strategy_engine.plugin_loader import load_strategies
+
strategies_dir = _STRATEGY_DIR / "strategies"
strategies = load_strategies(strategies_dir)
return [
diff --git a/services/api/tests/test_api.py b/services/api/tests/test_api.py
index 99cf9e3..669143b 100644
--- a/services/api/tests/test_api.py
+++ b/services/api/tests/test_api.py
@@ -1,12 +1,13 @@
"""Tests for the REST API."""
-import pytest
-from unittest.mock import AsyncMock, MagicMock, patch
+
+from unittest.mock import AsyncMock, patch
from fastapi.testclient import TestClient
def test_health_endpoint():
"""Health endpoint returns ok."""
from trading_api.main import app
+
# Override lifespan to skip DB
with patch("trading_api.main.lifespan") as mock_lifespan:
mock_lifespan.return_value.__aenter__ = AsyncMock()
diff --git a/services/data-collector/src/data_collector/main.py b/services/data-collector/src/data_collector/main.py
index 81e13df..4384985 100644
--- a/services/data-collector/src/data_collector/main.py
+++ b/services/data-collector/src/data_collector/main.py
@@ -49,7 +49,9 @@ async def run() -> None:
on_candle=on_candle,
)
- health = HealthCheckServer("data-collector", port=config.health_port, auth_token=config.metrics_auth_token)
+ health = HealthCheckServer(
+ "data-collector", port=config.health_port, auth_token=config.metrics_auth_token
+ )
health.register_check("redis", broker.ping)
await health.start()
metrics.service_up.labels(service="data-collector").set(1)
diff --git a/services/order-executor/src/order_executor/main.py b/services/order-executor/src/order_executor/main.py
index 32470f6..1eeee7b 100644
--- a/services/order-executor/src/order_executor/main.py
+++ b/services/order-executor/src/order_executor/main.py
@@ -60,7 +60,9 @@ async def run() -> None:
last_id = "$"
stream = "signals"
- health = HealthCheckServer("order-executor", port=config.health_port + 2, auth_token=config.metrics_auth_token)
+ health = HealthCheckServer(
+ "order-executor", port=config.health_port + 2, auth_token=config.metrics_auth_token
+ )
health.register_check("redis", broker.ping)
await health.start()
metrics.service_up.labels(service="order-executor").set(1)
diff --git a/services/order-executor/src/order_executor/risk_manager.py b/services/order-executor/src/order_executor/risk_manager.py
index 2b0a864..c3578a7 100644
--- a/services/order-executor/src/order_executor/risk_manager.py
+++ b/services/order-executor/src/order_executor/risk_manager.py
@@ -1,4 +1,5 @@
"""Risk management for order execution."""
+
from dataclasses import dataclass
from decimal import Decimal
from collections import deque
@@ -16,6 +17,7 @@ class RiskCheckResult:
@dataclass
class TrailingStop:
"""Tracks trailing stop for a symbol."""
+
symbol: str
highest_price: Decimal
stop_pct: Decimal # e.g. 5.0 for 5%
@@ -86,7 +88,11 @@ class RiskManager:
if not history or len(history) < 2:
return None
prices = list(history)
- returns = [(prices[i] - prices[i-1]) / prices[i-1] for i in range(1, len(prices)) if prices[i-1] != 0]
+ returns = [
+ (prices[i] - prices[i - 1]) / prices[i - 1]
+ for i in range(1, len(prices))
+ if prices[i - 1] != 0
+ ]
if not returns:
return None
mean = sum(returns) / len(returns)
@@ -153,7 +159,10 @@ class RiskManager:
if position is not None:
current_position_value = position.quantity * position.current_price
- if balance > 0 and (current_position_value + order_cost) / balance > self.max_position_size:
+ if (
+ balance > 0
+ and (current_position_value + order_cost) / balance > self.max_position_size
+ ):
return RiskCheckResult(allowed=False, reason="Position size exceeded")
return RiskCheckResult(allowed=True, reason="OK")
diff --git a/services/portfolio-manager/src/portfolio_manager/main.py b/services/portfolio-manager/src/portfolio_manager/main.py
index be29a2f..d60e6c9 100644
--- a/services/portfolio-manager/src/portfolio_manager/main.py
+++ b/services/portfolio-manager/src/portfolio_manager/main.py
@@ -63,7 +63,9 @@ async def run() -> None:
broker = RedisBroker(config.redis_url)
tracker = PortfolioTracker()
- health = HealthCheckServer("portfolio-manager", port=config.health_port + 3, auth_token=config.metrics_auth_token)
+ health = HealthCheckServer(
+ "portfolio-manager", port=config.health_port + 3, auth_token=config.metrics_auth_token
+ )
health.register_check("redis", broker.ping)
await health.start()
metrics.service_up.labels(service="portfolio-manager").set(1)
diff --git a/services/strategy-engine/src/strategy_engine/main.py b/services/strategy-engine/src/strategy_engine/main.py
index fabd755..9f3b3c9 100644
--- a/services/strategy-engine/src/strategy_engine/main.py
+++ b/services/strategy-engine/src/strategy_engine/main.py
@@ -43,7 +43,9 @@ async def run() -> None:
engine = StrategyEngine(broker=broker, strategies=strategies)
- health = HealthCheckServer("strategy-engine", port=config.health_port + 1, auth_token=config.metrics_auth_token)
+ health = HealthCheckServer(
+ "strategy-engine", port=config.health_port + 1, auth_token=config.metrics_auth_token
+ )
health.register_check("redis", broker.ping)
await health.start()
metrics.service_up.labels(service="strategy-engine").set(1)
diff --git a/services/strategy-engine/strategies/combined_strategy.py b/services/strategy-engine/strategies/combined_strategy.py
index e99dfdf..507ef5b 100644
--- a/services/strategy-engine/strategies/combined_strategy.py
+++ b/services/strategy-engine/strategies/combined_strategy.py
@@ -1,6 +1,6 @@
"""Combined strategy that aggregates signals from multiple sub-strategies."""
+
from decimal import Decimal
-from typing import Optional
from shared.models import Candle, Signal, OrderSide
from strategies.base import BaseStrategy
@@ -12,6 +12,7 @@ class CombinedStrategy(BaseStrategy):
Each sub-strategy votes BUY (+weight), SELL (-weight), or HOLD (0).
The combined signal fires when the weighted sum exceeds a threshold.
"""
+
name: str = "combined"
def __init__(self) -> None:
diff --git a/services/strategy-engine/tests/test_combined_strategy.py b/services/strategy-engine/tests/test_combined_strategy.py
index b860dca..3408a89 100644
--- a/services/strategy-engine/tests/test_combined_strategy.py
+++ b/services/strategy-engine/tests/test_combined_strategy.py
@@ -1,6 +1,8 @@
"""Tests for Combined strategy."""
+
import sys
from pathlib import Path
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from decimal import Decimal
@@ -24,9 +26,12 @@ class AlwaysBuyStrategy(BaseStrategy):
def on_candle(self, candle: Candle) -> Signal | None:
return Signal(
- strategy=self.name, symbol=candle.symbol,
- side=OrderSide.BUY, price=candle.close,
- quantity=Decimal("0.01"), reason="always buy",
+ strategy=self.name,
+ symbol=candle.symbol,
+ side=OrderSide.BUY,
+ price=candle.close,
+ quantity=Decimal("0.01"),
+ reason="always buy",
)
@@ -42,9 +47,12 @@ class AlwaysSellStrategy(BaseStrategy):
def on_candle(self, candle: Candle) -> Signal | None:
return Signal(
- strategy=self.name, symbol=candle.symbol,
- side=OrderSide.SELL, price=candle.close,
- quantity=Decimal("0.01"), reason="always sell",
+ strategy=self.name,
+ symbol=candle.symbol,
+ side=OrderSide.SELL,
+ price=candle.close,
+ quantity=Decimal("0.01"),
+ reason="always sell",
)
@@ -64,10 +72,13 @@ class NeutralStrategy(BaseStrategy):
def _candle(price=100.0):
return Candle(
- symbol="BTCUSDT", timeframe="1m",
+ symbol="BTCUSDT",
+ timeframe="1m",
open_time=datetime(2025, 1, 1, tzinfo=timezone.utc),
- open=Decimal(str(price)), high=Decimal(str(price+10)),
- low=Decimal(str(price-10)), close=Decimal(str(price)),
+ open=Decimal(str(price)),
+ high=Decimal(str(price + 10)),
+ low=Decimal(str(price - 10)),
+ close=Decimal(str(price)),
volume=Decimal("10"),
)