2026-02-06 09:01:35 +07:00
|
|
|
"""
|
2026-02-08 10:33:24 +07:00
|
|
|
FastAPI Backend for Web Dashboard (Docker-compatible)
|
|
|
|
|
=====================================================
|
2026-02-06 09:01:35 +07:00
|
|
|
Serves trading bot status data to the web frontend.
|
2026-02-08 10:33:24 +07:00
|
|
|
|
|
|
|
|
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).
|
2026-02-06 09:01:35 +07:00
|
|
|
"""
|
|
|
|
|
|
2026-02-08 10:33:24 +07:00
|
|
|
import json
|
2026-02-06 09:01:35 +07:00
|
|
|
from pathlib import Path
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
from zoneinfo import ZoneInfo
|
|
|
|
|
|
2026-02-08 10:33:24 +07:00
|
|
|
from fastapi import FastAPI
|
2026-02-06 09:01:35 +07:00
|
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
|
|
2026-02-08 10:33:24 +07:00
|
|
|
app = FastAPI(title="Trading Bot API", version="2.0.0")
|
2026-02-06 09:01:35 +07:00
|
|
|
|
|
|
|
|
# CORS for frontend
|
|
|
|
|
app.add_middleware(
|
|
|
|
|
CORSMiddleware,
|
|
|
|
|
allow_origins=["*"],
|
|
|
|
|
allow_credentials=True,
|
|
|
|
|
allow_methods=["*"],
|
|
|
|
|
allow_headers=["*"],
|
|
|
|
|
)
|
|
|
|
|
|
2026-02-08 10:33:24 +07:00
|
|
|
# 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": [],
|
|
|
|
|
}
|
2026-02-06 09:01:35 +07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.get("/api/status")
|
|
|
|
|
async def get_status():
|
2026-02-08 10:33:24 +07:00
|
|
|
"""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",
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-02-06 09:01:35 +07:00
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.get("/api/health")
|
|
|
|
|
async def health():
|
2026-02-08 10:33:24 +07:00
|
|
|
"""Health check endpoint."""
|
|
|
|
|
bot_running = STATUS_FILE.exists() or Path("data/bot_status.json").exists()
|
|
|
|
|
return {"status": "ok", "bot_running": bot_running}
|
2026-02-06 09:01:35 +07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
import uvicorn
|
|
|
|
|
uvicorn.run(app, host="0.0.0.0", port=8000)
|