214b64945d
Live trading optimizations (cumulative: $2,807 net, 81.8% WR, Sharpe 3.97): - #28B: Smart breakeven locks profit at entry + 0.5x ATR instead of fixed $2 - #31B: H1 Price vs EMA20 filter — BUY only when H1 bullish, SELL only when bearish Backtests #26-#32 (7 scripts testing sell improvement, regime-aware entry, confluence scoring, dynamic RR, multi-TF H1, and ML exit optimizer). Winners: #28B (+$229), #31B (+$343). Failed: #26, #27, #29, #30, #32. Also includes: web dashboard redesign, Docker setup, startup scripts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
96 lines
2.6 KiB
Python
96 lines
2.6 KiB
Python
"""
|
|
FastAPI Backend for Web Dashboard (Docker-compatible)
|
|
=====================================================
|
|
Serves trading bot status data to the web frontend.
|
|
|
|
Reads from data/bot_status.json which is written by main_live.py.
|
|
This allows the API to run in Docker without needing MT5 (Windows-only).
|
|
"""
|
|
|
|
import json
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
from zoneinfo import ZoneInfo
|
|
|
|
from fastapi import FastAPI
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
app = FastAPI(title="Trading Bot API", version="2.0.0")
|
|
|
|
# CORS for frontend
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=["*"],
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
# Status file path (mounted as volume in Docker)
|
|
STATUS_FILE = Path("/app/data/bot_status.json")
|
|
|
|
# Default empty response
|
|
DEFAULT_STATUS = {
|
|
"timestamp": "00:00:00",
|
|
"connected": False,
|
|
"price": 0.0,
|
|
"spread": 0.0,
|
|
"priceChange": 0.0,
|
|
"priceHistory": [],
|
|
"balance": 0.0,
|
|
"equity": 0.0,
|
|
"profit": 0.0,
|
|
"equityHistory": [],
|
|
"balanceHistory": [],
|
|
"session": "Unknown",
|
|
"isGoldenTime": False,
|
|
"canTrade": False,
|
|
"dailyLoss": 0.0,
|
|
"dailyProfit": 0.0,
|
|
"consecutiveLosses": 0,
|
|
"riskPercent": 0.0,
|
|
"smc": {"signal": "", "confidence": 0.0, "reason": ""},
|
|
"ml": {"signal": "", "confidence": 0.0, "buyProb": 0.0, "sellProb": 0.0},
|
|
"regime": {"name": "", "volatility": 0.0, "confidence": 0.0},
|
|
"positions": [],
|
|
"logs": [],
|
|
}
|
|
|
|
|
|
@app.get("/api/status")
|
|
async def get_status():
|
|
"""Get current trading status from bot's status file."""
|
|
# Try local path first (non-Docker), then Docker path
|
|
for path in [STATUS_FILE, Path("data/bot_status.json")]:
|
|
if path.exists():
|
|
try:
|
|
data = json.loads(path.read_text())
|
|
return data
|
|
except (json.JSONDecodeError, OSError):
|
|
continue
|
|
|
|
# No status file — bot not running
|
|
now = datetime.now(ZoneInfo("Asia/Jakarta"))
|
|
result = DEFAULT_STATUS.copy()
|
|
result["timestamp"] = now.strftime("%H:%M:%S")
|
|
result["logs"] = [
|
|
{
|
|
"time": now.strftime("%H:%M:%S"),
|
|
"level": "warning",
|
|
"message": "Bot is not running — waiting for bot_status.json",
|
|
}
|
|
]
|
|
return result
|
|
|
|
|
|
@app.get("/api/health")
|
|
async def health():
|
|
"""Health check endpoint."""
|
|
bot_running = STATUS_FILE.exists() or Path("data/bot_status.json").exists()
|
|
return {"status": "ok", "bot_running": bot_running}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import uvicorn
|
|
uvicorn.run(app, host="0.0.0.0", port=8000)
|