summaryrefslogtreecommitdiff
path: root/services/backtester/src
diff options
context:
space:
mode:
Diffstat (limited to 'services/backtester/src')
-rw-r--r--services/backtester/src/backtester/engine.py5
-rw-r--r--services/backtester/src/backtester/main.py4
-rw-r--r--services/backtester/src/backtester/metrics.py2
-rw-r--r--services/backtester/src/backtester/simulator.py19
-rw-r--r--services/backtester/src/backtester/walk_forward.py4
5 files changed, 16 insertions, 18 deletions
diff --git a/services/backtester/src/backtester/engine.py b/services/backtester/src/backtester/engine.py
index b03715d..fcf48f1 100644
--- a/services/backtester/src/backtester/engine.py
+++ b/services/backtester/src/backtester/engine.py
@@ -6,10 +6,9 @@ from dataclasses import dataclass, field
from decimal import Decimal
from typing import Protocol
-from shared.models import Candle, Signal
-
from backtester.metrics import DetailedMetrics, TradeRecord, compute_detailed_metrics
from backtester.simulator import OrderSimulator, SimulatedTrade
+from shared.models import Candle, Signal
class StrategyProtocol(Protocol):
@@ -101,7 +100,7 @@ class BacktestEngine:
final_balance = simulator.balance
if candles:
last_price = candles[-1].close
- for symbol, qty in simulator.positions.items():
+ for qty in simulator.positions.values():
if qty > Decimal("0"):
final_balance += qty * last_price
elif qty < Decimal("0"):
diff --git a/services/backtester/src/backtester/main.py b/services/backtester/src/backtester/main.py
index 084ce02..dbde00b 100644
--- a/services/backtester/src/backtester/main.py
+++ b/services/backtester/src/backtester/main.py
@@ -17,11 +17,11 @@ _STRATEGIES_DIR = Path(
if _STRATEGIES_DIR.parent not in [Path(p) for p in sys.path]:
sys.path.insert(0, str(_STRATEGIES_DIR.parent))
-from shared.db import Database # noqa: E402
-from shared.models import Candle # noqa: E402
from backtester.config import BacktestConfig # noqa: E402
from backtester.engine import BacktestEngine # noqa: E402
from backtester.reporter import format_report # noqa: E402
+from shared.db import Database # noqa: E402
+from shared.models import Candle # noqa: E402
async def run_backtest() -> str:
diff --git a/services/backtester/src/backtester/metrics.py b/services/backtester/src/backtester/metrics.py
index 239cb6f..c7b032b 100644
--- a/services/backtester/src/backtester/metrics.py
+++ b/services/backtester/src/backtester/metrics.py
@@ -266,7 +266,7 @@ def compute_detailed_metrics(
largest_win=largest_win,
largest_loss=largest_loss,
avg_holding_period=avg_holding,
- trade_pairs=[p for p in pairs],
+ trade_pairs=list(pairs),
risk_free_rate=risk_free_rate,
recovery_factor=recovery_factor,
max_consecutive_losses=max_consec_losses,
diff --git a/services/backtester/src/backtester/simulator.py b/services/backtester/src/backtester/simulator.py
index 64c88dd..6bce18b 100644
--- a/services/backtester/src/backtester/simulator.py
+++ b/services/backtester/src/backtester/simulator.py
@@ -1,9 +1,8 @@
"""Simulated order executor for backtesting."""
from dataclasses import dataclass, field
-from datetime import datetime, timezone
+from datetime import UTC, datetime
from decimal import Decimal
-from typing import Optional
from shared.models import OrderSide, Signal
@@ -16,7 +15,7 @@ class SimulatedTrade:
quantity: Decimal
balance_after: Decimal
fee: Decimal = Decimal("0")
- timestamp: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
+ timestamp: datetime = field(default_factory=lambda: datetime.now(UTC))
@dataclass
@@ -27,8 +26,8 @@ class OpenPosition:
side: OrderSide # BUY = long, SELL = short
entry_price: Decimal
quantity: Decimal
- stop_loss: Optional[Decimal] = None
- take_profit: Optional[Decimal] = None
+ stop_loss: Decimal | None = None
+ take_profit: Decimal | None = None
class OrderSimulator:
@@ -70,7 +69,7 @@ class OrderSimulator:
remaining: list[OpenPosition] = []
for pos in self.open_positions:
triggered = False
- exit_price: Optional[Decimal] = None
+ exit_price: Decimal | None = None
if pos.side == OrderSide.BUY: # Long position
if pos.stop_loss is not None and candle_low <= pos.stop_loss:
@@ -125,12 +124,12 @@ class OrderSimulator:
def execute(
self,
signal: Signal,
- timestamp: Optional[datetime] = None,
- stop_loss: Optional[Decimal] = None,
- take_profit: Optional[Decimal] = None,
+ timestamp: datetime | None = None,
+ stop_loss: Decimal | None = None,
+ take_profit: Decimal | None = None,
) -> bool:
"""Execute a signal with slippage and fees. Returns True if accepted."""
- ts = timestamp or datetime.now(timezone.utc)
+ ts = timestamp or datetime.now(UTC)
exec_price = self._apply_slippage(signal.price, signal.side)
fee = self._calculate_fee(exec_price, signal.quantity)
diff --git a/services/backtester/src/backtester/walk_forward.py b/services/backtester/src/backtester/walk_forward.py
index c7b7fd8..720ad5e 100644
--- a/services/backtester/src/backtester/walk_forward.py
+++ b/services/backtester/src/backtester/walk_forward.py
@@ -1,11 +1,11 @@
"""Walk-forward analysis for strategy parameter optimization."""
+from collections.abc import Callable
from dataclasses import dataclass, field
from decimal import Decimal
-from typing import Callable
-from shared.models import Candle
from backtester.engine import BacktestEngine, BacktestResult, StrategyProtocol
+from shared.models import Candle
@dataclass