Mikecode123 commited on
Commit
c77cde3
·
verified ·
1 Parent(s): 5623caa

Delete main.py

Browse files
Files changed (1) hide show
  1. main.py +0 -251
main.py DELETED
@@ -1,251 +0,0 @@
1
- """
2
- AI Trading Backend — FastAPI entry point.
3
-
4
- Endpoints:
5
- GET /health
6
- GET /models
7
- GET /portfolio?mode=demo
8
- GET /history?mode=demo&limit=50
9
- GET /performance?mode=demo
10
- GET /confidence?symbol=R_10
11
- POST /predict
12
- POST /reason
13
- POST /paper-trade
14
- POST /trade (live, requires DERIV_API_TOKEN)
15
- POST /feedback
16
- POST /retrain-request
17
- """
18
- from __future__ import annotations
19
- import logging
20
- from typing import Optional
21
- from fastapi import FastAPI, HTTPException, Query
22
- from fastapi.middleware.cors import CORSMiddleware
23
-
24
- from . import supabase_client as sb
25
- from .config import get_settings
26
- from .deriv_client import DerivClient, DerivError
27
- from .paper_trader import (open_paper_trade, close_paper_trade,
28
- get_or_create_portfolio)
29
- from .prediction_engine import ensemble, heuristic_forecast
30
- from .qwen_reasoner import reason as qwen_reason
31
- from .risk import validate_trade
32
- from .schemas import (PredictRequest, ReasonRequest, PaperTradeRequest,
33
- TradeRequest, TradeResponse, FeedbackRequest,
34
- RetrainRequest, StrategySignal)
35
- from .strategy_ob_fvg import generate_signal
36
-
37
- log = logging.getLogger("uvicorn.error")
38
- settings = get_settings()
39
-
40
- app = FastAPI(title="AI Trading Backend", version="0.1.0")
41
- app.add_middleware(
42
- CORSMiddleware,
43
- allow_origins=settings.CORS_ORIGINS,
44
- allow_credentials=True,
45
- allow_methods=["*"],
46
- allow_headers=["*"],
47
- )
48
-
49
-
50
- # -------------------------- system ------------------------------------------
51
-
52
- @app.get("/health")
53
- async def health():
54
- return {
55
- "ok": True,
56
- "qwen_configured": bool(settings.HF_TOKEN),
57
- "supabase_configured": bool(sb.sb()),
58
- "deriv_live_enabled": bool(settings.DERIV_API_TOKEN),
59
- "model": settings.QWEN_MODEL,
60
- }
61
-
62
-
63
- @app.get("/models")
64
- async def models():
65
- return {
66
- "reasoner": settings.QWEN_MODEL,
67
- "forecasters": ["heuristic-momentum-v1", "lstm-stub", "xgboost-stub",
68
- "ensemble-v1"],
69
- "strategies": ["ob_fvg"],
70
- }
71
-
72
-
73
- # -------------------------- portfolio / history ------------------------------
74
-
75
- @app.get("/portfolio")
76
- async def portfolio(mode: str = "demo"):
77
- return await get_or_create_portfolio(None, mode)
78
-
79
-
80
- @app.get("/history")
81
- async def history(mode: str = "demo", limit: int = 50):
82
- return sb.select("trade_history", eq={"mode": mode},
83
- order="opened_at", desc=True, limit=limit)
84
-
85
-
86
- @app.get("/performance")
87
- async def performance(mode: str = "demo"):
88
- trades = sb.select("trade_history", eq={"mode": mode, "status": "closed"},
89
- order="closed_at", desc=True, limit=500)
90
- if not trades:
91
- return {"total_trades": 0, "win_rate": 0, "total_pnl": 0,
92
- "profit_factor": 0}
93
- wins = [t for t in trades if (t.get("pnl") or 0) > 0]
94
- losses = [t for t in trades if (t.get("pnl") or 0) < 0]
95
- gross_win = sum(t["pnl"] for t in wins) or 0.0
96
- gross_loss = abs(sum(t["pnl"] for t in losses)) or 1e-9
97
- return {
98
- "total_trades": len(trades),
99
- "winning_trades": len(wins),
100
- "losing_trades": len(losses),
101
- "win_rate": len(wins) / len(trades),
102
- "total_pnl": sum(t["pnl"] or 0 for t in trades),
103
- "profit_factor": gross_win / gross_loss,
104
- }
105
-
106
-
107
- @app.get("/confidence")
108
- async def confidence(symbol: str = "R_10"):
109
- rows = sb.select("predictions", eq={"symbol": symbol},
110
- order="created_at", desc=True, limit=20)
111
- if not rows:
112
- return {"symbol": symbol, "avg_confidence": 0, "samples": 0}
113
- avg = sum(float(r.get("confidence") or 0) for r in rows) / len(rows)
114
- return {"symbol": symbol, "avg_confidence": avg, "samples": len(rows)}
115
-
116
-
117
- # -------------------------- core AI loop ------------------------------------
118
-
119
- @app.post("/predict", response_model=StrategySignal)
120
- async def predict(req: PredictRequest):
121
- """Run OB+FVG strategy on latest Deriv candles and emit a signal."""
122
- granularity = _granularity_seconds(req.timeframe)
123
- client = DerivClient()
124
- try:
125
- candles = await client.candles(req.symbol, granularity=granularity,
126
- count=req.lookback)
127
- except DerivError as e:
128
- raise HTTPException(502, f"Deriv error: {e}")
129
-
130
- signal = generate_signal(req.symbol, req.timeframe, candles)
131
- forecast = ensemble(candles)
132
-
133
- # persist
134
- pred = sb.insert("predictions", {
135
- "symbol": req.symbol,
136
- "timeframe": req.timeframe,
137
- "decision": signal.decision,
138
- "confidence": round(signal.confidence, 3),
139
- "risk_score": round(1 - signal.confidence, 3),
140
- "success_probability": round(signal.confidence, 3),
141
- "reasoning": signal.rationale,
142
- "trade_plan": {"entry": signal.entry, "sl": signal.sl, "tp": signal.tp},
143
- "indicators": signal.indicators,
144
- "market_state": {"forecast": forecast},
145
- "suggested_entry": signal.entry,
146
- "suggested_sl": signal.sl,
147
- "suggested_tp": signal.tp,
148
- "model_version": "ob_fvg-v1",
149
- })
150
- sb.insert("live_signals", {
151
- "symbol": req.symbol,
152
- "decision": signal.decision,
153
- "confidence": round(signal.confidence, 3),
154
- "price": signal.price,
155
- "ob_zone": signal.ob.model_dump() if signal.ob else None,
156
- "fvg_zone": signal.fvg.model_dump() if signal.fvg else None,
157
- "reasoning": signal.rationale,
158
- })
159
- return signal
160
-
161
-
162
- @app.post("/reason")
163
- async def reason_endpoint(req: ReasonRequest):
164
- return await qwen_reason(req.symbol, req.timeframe,
165
- req.indicators, req.prediction, req.market_state)
166
-
167
-
168
- # -------------------------- trading ------------------------------------------
169
-
170
- @app.post("/paper-trade", response_model=TradeResponse)
171
- async def paper_trade(req: PaperTradeRequest):
172
- client = DerivClient()
173
- tick = await client.tick(req.symbol)
174
- price = float(tick.get("quote") or 0)
175
- if not price:
176
- raise HTTPException(502, "Could not fetch current price")
177
-
178
- pf = await get_or_create_portfolio(None, "demo")
179
- check = validate_trade(
180
- balance=float(pf.get("balance") or 0),
181
- open_positions=int(pf.get("open_positions") or 0),
182
- today_pnl=float(pf.get("realized_pnl") or 0),
183
- trade_size=req.size,
184
- confidence=0.7, # passed when called from /predict; user override OK
185
- max_daily_loss=settings.MAX_DAILY_LOSS_DEFAULT,
186
- max_open_trades=settings.MAX_OPEN_TRADES_DEFAULT,
187
- risk_percent=2.0,
188
- )
189
- if not check.ok:
190
- return TradeResponse(ok=False, message=check.reason or "Rejected")
191
-
192
- row = await open_paper_trade(req, current_price=price)
193
- return TradeResponse(ok=True, trade_id=row.get("id"),
194
- message="Paper trade opened")
195
-
196
-
197
- @app.post("/trade", response_model=TradeResponse)
198
- async def live_trade(req: TradeRequest):
199
- if not settings.DERIV_API_TOKEN:
200
- raise HTTPException(403,
201
- "Live trading disabled: set DERIV_API_TOKEN in Space secrets.")
202
- client = DerivClient(token=settings.DERIV_API_TOKEN)
203
- contract_type = "CALL" if req.side == "BUY" else "PUT"
204
- try:
205
- buy = await client.buy_contract(
206
- symbol=req.symbol, contract_type=contract_type,
207
- amount=req.size, duration=5, duration_unit="m",
208
- )
209
- except DerivError as e:
210
- raise HTTPException(502, f"Deriv: {e}")
211
- row = sb.insert("trade_history", {
212
- "mode": "live",
213
- "symbol": req.symbol,
214
- "side": req.side,
215
- "entry_price": float(buy.get("buy_price") or 0),
216
- "size": req.size,
217
- "stop_loss": req.sl,
218
- "take_profit": req.tp,
219
- "status": "open",
220
- "deriv_contract_id": str(buy.get("contract_id") or ""),
221
- "prediction_id": req.prediction_id,
222
- })
223
- return TradeResponse(ok=True, trade_id=(row or {}).get("id"),
224
- contract_id=str(buy.get("contract_id") or ""),
225
- message="Live contract bought")
226
-
227
-
228
- # -------------------------- feedback / retrain -------------------------------
229
-
230
- @app.post("/feedback")
231
- async def feedback(req: FeedbackRequest):
232
- sb.insert("feedback", req.model_dump())
233
- return {"ok": True}
234
-
235
-
236
- @app.post("/retrain-request")
237
- async def retrain(req: RetrainRequest):
238
- sb.insert("logs", {
239
- "level": "info", "source": "retrain",
240
- "message": f"Retrain requested for {req.model_name}",
241
- "meta": req.model_dump(),
242
- })
243
- return {"ok": True, "queued": True}
244
-
245
-
246
- # -------------------------- helpers ------------------------------------------
247
-
248
- def _granularity_seconds(tf: str) -> int:
249
- table = {"1m": 60, "5m": 300, "15m": 900, "30m": 1800,
250
- "1h": 3600, "4h": 14400, "1d": 86400}
251
- return table.get(tf, 60)