feat: trading bot MVP — ICT Order Block + Liquidity Sweep strategy

Full-stack trading bot with:
- FastAPI backend with ICT strategy (Order Block + Liquidity Sweep detection)
- Backtester engine with rolling window, spread simulation, and performance metrics
- Hybrid market data service (yfinance + TwelveData with rate limiting + SQLite cache)
- Simulated exchange for paper trading
- React/TypeScript frontend with TradingView lightweight-charts v5
- Live dashboard with candlestick chart, OHLC legend, trade markers
- Backtest page with configurable parameters, equity curve, and trade table
- WebSocket support for real-time updates
- Bot runner with asyncio loop for automated trading

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-24 23:25:51 +01:00
commit 4df8d53b1a
58 changed files with 7484 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
from typing import Optional
from fastapi import APIRouter, Depends, Query
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.database import get_db
from app.models.trade import Trade
router = APIRouter(prefix="/trades", tags=["trades"])
@router.get("")
async def get_trades(
source: Optional[str] = Query(default=None, description="live | backtest"),
status: Optional[str] = Query(default=None, description="open | closed"),
instrument: Optional[str] = Query(default=None),
limit: int = Query(default=100, ge=1, le=1000),
db: AsyncSession = Depends(get_db),
):
stmt = select(Trade).order_by(Trade.opened_at.desc()).limit(limit)
if source:
stmt = stmt.where(Trade.source == source)
if status:
stmt = stmt.where(Trade.status == status)
if instrument:
stmt = stmt.where(Trade.instrument == instrument)
result = await db.execute(stmt)
trades = result.scalars().all()
return {
"total": len(trades),
"trades": [
{
"id": t.id,
"source": t.source,
"instrument": t.instrument,
"direction": t.direction,
"units": t.units,
"entry_price": t.entry_price,
"stop_loss": t.stop_loss,
"take_profit": t.take_profit,
"exit_price": t.exit_price,
"pnl": t.pnl,
"status": t.status,
"signal_type": t.signal_type,
"opened_at": str(t.opened_at),
"closed_at": str(t.closed_at) if t.closed_at else None,
}
for t in trades
],
}