summaryrefslogtreecommitdiff
path: root/services/backtester/src
diff options
context:
space:
mode:
authorTheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com>2026-04-01 17:03:53 +0900
committerTheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com>2026-04-01 17:03:53 +0900
commit32bc579c3f15694308992690f4457524e8842f99 (patch)
tree62910a3193c91703a875f5071a67b2fa9f327a7f /services/backtester/src
parente971d19678a7ce94666e5887909823cdd2a6cab2 (diff)
fix: clean up backtester strategy loading and update Dockerfiles
Diffstat (limited to 'services/backtester/src')
-rw-r--r--services/backtester/src/backtester/main.py51
1 files changed, 27 insertions, 24 deletions
diff --git a/services/backtester/src/backtester/main.py b/services/backtester/src/backtester/main.py
index c9b3890..0a2b47d 100644
--- a/services/backtester/src/backtester/main.py
+++ b/services/backtester/src/backtester/main.py
@@ -1,36 +1,41 @@
"""Main entry point for the backtester service."""
-
+import asyncio
+import importlib
import os
import sys
from decimal import Decimal
+from pathlib import Path
-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
+# Resolve strategies directory from env or relative path
+_STRATEGIES_DIR = Path(os.environ.get(
+ "STRATEGIES_DIR",
+ str(Path(__file__).resolve().parents[4] / "strategy-engine" / "strategies")
+))
+if _STRATEGIES_DIR.parent not in [Path(p) for p in sys.path]:
+ sys.path.insert(0, str(_STRATEGIES_DIR.parent))
-# Allow importing strategies from the strategy-engine service
-_STRATEGY_ENGINE_PATH = os.path.join(os.path.dirname(__file__), "../../../../strategy-engine")
-if _STRATEGY_ENGINE_PATH not in sys.path:
- sys.path.insert(0, _STRATEGY_ENGINE_PATH)
+from shared.db import Database
+from shared.models import Candle
+from backtester.config import BacktestConfig
+from backtester.engine import BacktestEngine
+from backtester.reporter import format_report
async def run_backtest() -> str:
- """Load strategy, fetch candles, run backtest, and return a formatted report."""
config = BacktestConfig()
- # Import strategy dynamically (requires strategy-engine in sys.path)
try:
- from strategies.base import BaseStrategy # noqa: F401
-
- # Try to import concrete strategy by name
- module_name = config.strategy_name
- import importlib
-
- mod = importlib.import_module(f"strategies.{module_name}")
- strategy_cls = getattr(mod, "Strategy")
+ mod = importlib.import_module(f"strategies.{config.strategy_name}")
+ # Find the first BaseStrategy subclass
+ from strategies.base import BaseStrategy
+ strategy_cls = None
+ for attr_name in dir(mod):
+ obj = getattr(mod, attr_name)
+ if isinstance(obj, type) and issubclass(obj, BaseStrategy) and obj is not BaseStrategy:
+ strategy_cls = obj
+ break
+ if strategy_cls is None:
+ raise RuntimeError(f"No strategy class found in {config.strategy_name}")
strategy = strategy_cls()
strategy.configure({})
except Exception as exc:
@@ -41,7 +46,7 @@ async def run_backtest() -> str:
try:
rows = await db.get_candles(config.symbol, config.timeframe, config.candle_limit)
candles = [Candle(**row) for row in rows]
- candles = list(reversed(candles)) # oldest first for strategy processing
+ candles = list(reversed(candles))
finally:
await db.close()
@@ -51,7 +56,5 @@ async def run_backtest() -> str:
if __name__ == "__main__":
- import asyncio
-
report = asyncio.run(run_backtest())
print(report)