lucky-loster's picture
Upload folder using huggingface_hub
590a501 verified
Raw
History Blame Contribute Delete
8.74 kB
import uuid
from fastapi import APIRouter, HTTPException
from app.models.schemas import OrderRequest, StrategyConfig
from app.services.backtest_engine import STRATEGY_TEMPLATES, run_backtest
from app.services.market_data import market_data_service
from app.services.order_manager import order_manager
from app.services.pattern_detector import detect_patterns, get_pattern_registry
from app.services.risk_manager import risk_manager
from app.services.strategy_engine import strategy_engine
router = APIRouter()
# ── Market Data ──────────────────────────────────────────────────────────────
@router.get("/market/quotes")
async def get_quotes(category: str | None = None, exchange: str | None = None):
quotes = market_data_service.get_all_market_data()
if category:
quotes = [q for q in quotes if q.category == category]
if exchange:
quotes = [q for q in quotes if q.exchange == exchange]
return quotes
@router.get("/market/klines/{symbol}")
async def get_klines(symbol: str, interval: str = "1m", limit: int = 200):
klines = market_data_service.get_kline_history(symbol, interval, limit)
if not klines:
raise HTTPException(status_code=404, detail=f"No data for symbol {symbol}")
return klines
@router.get("/market/intervals")
async def get_intervals():
return market_data_service.INTERVALS
@router.get("/market/contracts")
async def get_contracts(category: str | None = None, exchange: str | None = None):
return market_data_service.get_contracts(category=category, exchange=exchange)
@router.get("/market/exchanges")
async def get_exchanges():
return market_data_service.get_exchanges()
@router.get("/market/categories")
async def get_categories():
return market_data_service.get_categories()
@router.get("/market/contract/{symbol}")
async def get_contract_detail(symbol: str):
detail = market_data_service.get_contract_details(symbol)
if detail is None:
quote = market_data_service.get_quote(symbol)
if quote:
return {"quote": quote, "contracts": None}
raise HTTPException(status_code=404, detail="Contract not found")
return detail
@router.get("/market/mode")
async def get_market_mode():
return {"mode": market_data_service.mode}
@router.post("/market/mode")
async def set_market_mode(mode: str):
try:
market_data_service.set_mode(mode)
return {"mode": market_data_service.mode}
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
# ── Orders ───────────────────────────────────────────────────────────────────
@router.post("/orders")
async def place_order(req: OrderRequest):
ok, msg = risk_manager.check_order(req)
if not ok:
raise HTTPException(status_code=400, detail=msg)
return order_manager.place_order(req)
@router.get("/orders")
async def get_orders(symbol: str | None = None):
return order_manager.get_orders(symbol)
@router.delete("/orders/{order_id}")
async def cancel_order(order_id: str):
result = order_manager.cancel_order(order_id)
if result is None:
raise HTTPException(status_code=404, detail="Order not found or cannot be cancelled")
return result
# ── Positions ────────────────────────────────────────────────────────────────
@router.get("/positions")
async def get_positions():
return order_manager.get_positions()
# ── Account ──────────────────────────────────────────────────────────────────
@router.get("/account")
async def get_account():
return order_manager.account
# ── Trades ───────────────────────────────────────────────────────────────────
@router.get("/trades")
async def get_trades(limit: int = 100):
return order_manager.get_trades(limit)
# ── Strategies ───────────────────────────────────────────────────────────────
@router.get("/strategies/available")
async def get_available_strategies():
return strategy_engine.get_available_strategies()
@router.get("/strategies")
async def get_strategies():
return strategy_engine.get_running_strategies()
@router.post("/strategies")
async def add_strategy(config: StrategyConfig):
if not config.strategy_id:
config.strategy_id = f"STR-{uuid.uuid4().hex[:8].upper()}"
try:
return strategy_engine.add_strategy(config)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
@router.post("/strategies/{strategy_id}/start")
async def start_strategy(strategy_id: str):
result = strategy_engine.start_strategy(strategy_id)
if result is None:
raise HTTPException(status_code=404, detail="Strategy not found")
return result
@router.post("/strategies/{strategy_id}/stop")
async def stop_strategy(strategy_id: str):
result = strategy_engine.stop_strategy(strategy_id)
if result is None:
raise HTTPException(status_code=404, detail="Strategy not found")
return result
@router.delete("/strategies/{strategy_id}")
async def remove_strategy(strategy_id: str):
strategy_engine.remove_strategy(strategy_id)
return {"status": "removed"}
@router.get("/strategies/{strategy_id}/signals")
async def get_strategy_signals(strategy_id: str):
return strategy_engine.get_strategy_signals(strategy_id)
@router.get("/strategies/{strategy_id}/performance")
async def get_strategy_performance(strategy_id: str):
perf = strategy_engine.get_strategy_performance(strategy_id)
if perf is None:
raise HTTPException(status_code=404, detail="Strategy not found")
return perf
# ── Risk Management ──────────────────────────────────────────────────────────
@router.get("/risk")
async def get_risk_metrics():
return risk_manager.get_risk_metrics()
# ── Pattern Detection (ε―»ζ‰ΎζœΊδΌš) ─────────────────────────────────────────────
@router.get("/patterns/registry")
async def get_pattern_types():
return get_pattern_registry()
@router.get("/patterns/detect/{symbol}")
async def detect_chart_patterns(
symbol: str,
interval: str = "1m",
limit: int = 200,
patterns: str | None = None,
):
klines = market_data_service.get_kline_history(symbol, interval, limit)
if not klines:
raise HTTPException(status_code=404, detail=f"No kline data for {symbol}")
enabled = patterns.split(",") if patterns else None
return detect_patterns(klines, enabled)
# ── Backtesting ──────────────────────────────────────────────────────────────
@router.get("/backtest/templates")
async def get_backtest_templates():
return {k: {"name": v["name"], "description": v["description"], "code": v["code"]} for k, v in STRATEGY_TEMPLATES.items()}
@router.post("/backtest/run")
async def run_backtest_api(payload: dict):
code = payload.get("code", "")
symbol = payload.get("symbol", "θžΊηΊΉι’’")
interval = payload.get("interval", "1d")
limit = payload.get("limit", 200)
initial_capital = payload.get("initial_capital", 1_000_000)
commission = payload.get("commission", 0.0003)
slippage = payload.get("slippage", 0.0001)
if not code.strip():
raise HTTPException(status_code=400, detail="Strategy code is required")
klines = market_data_service.get_kline_history(symbol, interval, limit)
if not klines or len(klines) < 30:
raise HTTPException(status_code=400, detail=f"Insufficient kline data for {symbol} ({interval}): need >= 30 bars, got {len(klines)}")
import asyncio
loop = asyncio.get_event_loop()
result = await loop.run_in_executor(None, run_backtest, code, klines, initial_capital, commission, slippage)
return result