#!/usr/bin/env python3
import csv
import json
import math
import os
import urllib.parse
import urllib.request
import ssl
from datetime import datetime, timezone

SYMBOL = os.getenv("SIM_SYMBOL", "BTCUSDT")
INTERVAL = os.getenv("SIM_INTERVAL", "15m")
LIMIT = int(os.getenv("SIM_LIMIT", "800"))
INITIAL_CAPITAL = float(os.getenv("SIM_CAPITAL", "20"))
FEE_RATE = float(os.getenv("SIM_FEE", "0.001"))  # 0.1%
RISK_PER_TRADE = float(os.getenv("SIM_RISK", "0.10"))  # 10% capital
STOP_LOSS = float(os.getenv("SIM_SL", "0.01"))  # 1%
TAKE_PROFIT = float(os.getenv("SIM_TP", "0.015"))  # 1.5%

OUT_DIR = os.path.join(os.path.dirname(__file__), "out")
os.makedirs(OUT_DIR, exist_ok=True)


def fetch_klines(symbol: str, interval: str, limit: int):
    params = urllib.parse.urlencode({"symbol": symbol, "interval": interval, "limit": limit})
    url = f"https://api.binance.com/api/v3/klines?{params}"
    ssl_ctx = ssl._create_unverified_context()
    with urllib.request.urlopen(url, timeout=20, context=ssl_ctx) as resp:
        data = json.loads(resp.read().decode("utf-8"))
    rows = []
    for k in data:
        rows.append({
            "open_time": int(k[0]),
            "open": float(k[1]),
            "high": float(k[2]),
            "low": float(k[3]),
            "close": float(k[4]),
            "volume": float(k[5]),
        })
    return rows


def ema(series, period):
    out = []
    alpha = 2 / (period + 1)
    prev = series[0]
    for x in series:
        prev = alpha * x + (1 - alpha) * prev
        out.append(prev)
    return out


def run_backtest(rows):
    closes = [r["close"] for r in rows]
    ema_fast = ema(closes, 9)
    ema_slow = ema(closes, 21)

    cash = INITIAL_CAPITAL
    position_qty = 0.0
    entry_price = 0.0
    trades = []
    equity_curve = []

    for i, r in enumerate(rows):
        price = r["close"]
        high = r["high"]
        low = r["low"]

        # exit rules if in position
        if position_qty > 0:
            sl_price = entry_price * (1 - STOP_LOSS)
            tp_price = entry_price * (1 + TAKE_PROFIT)
            exit_reason = None
            exit_price = None

            if low <= sl_price:
                exit_price = sl_price
                exit_reason = "SL"
            elif high >= tp_price:
                exit_price = tp_price
                exit_reason = "TP"
            elif ema_fast[i] < ema_slow[i]:
                exit_price = price
                exit_reason = "CROSS_DOWN"

            if exit_reason:
                gross = position_qty * exit_price
                fee = gross * FEE_RATE
                cash += (gross - fee)
                pnl = (exit_price - entry_price) * position_qty - (entry_price * position_qty * FEE_RATE) - fee
                trades.append({
                    "time": r["open_time"],
                    "side": "SELL",
                    "price": exit_price,
                    "qty": position_qty,
                    "fee": fee,
                    "reason": exit_reason,
                    "pnl": pnl,
                })
                position_qty = 0.0
                entry_price = 0.0

        # entry rules
        if position_qty == 0 and i > 0 and ema_fast[i] > ema_slow[i] and ema_fast[i-1] <= ema_slow[i-1]:
            budget = cash * RISK_PER_TRADE
            if budget > 0.5:
                qty = budget / price
                gross = qty * price
                fee = gross * FEE_RATE
                if cash >= gross + fee:
                    cash -= (gross + fee)
                    position_qty = qty
                    entry_price = price
                    trades.append({
                        "time": r["open_time"],
                        "side": "BUY",
                        "price": price,
                        "qty": qty,
                        "fee": fee,
                        "reason": "CROSS_UP",
                        "pnl": 0.0,
                    })

        equity = cash + (position_qty * price)
        equity_curve.append({"time": r["open_time"], "equity": equity, "price": price})

    # close at final candle
    if position_qty > 0:
        final_price = rows[-1]["close"]
        gross = position_qty * final_price
        fee = gross * FEE_RATE
        cash += (gross - fee)
        pnl = (final_price - entry_price) * position_qty - (entry_price * position_qty * FEE_RATE) - fee
        trades.append({
            "time": rows[-1]["open_time"],
            "side": "SELL",
            "price": final_price,
            "qty": position_qty,
            "fee": fee,
            "reason": "EOD",
            "pnl": pnl,
        })
        equity_curve[-1]["equity"] = cash

    realized = sum(t["pnl"] for t in trades if t["side"] == "SELL")
    wins = sum(1 for t in trades if t["side"] == "SELL" and t["pnl"] > 0)
    closed = sum(1 for t in trades if t["side"] == "SELL")
    win_rate = (wins / closed * 100) if closed else 0.0
    roi = ((cash - INITIAL_CAPITAL) / INITIAL_CAPITAL * 100) if INITIAL_CAPITAL else 0.0
    max_eq = -1
    max_dd = 0
    for e in equity_curve:
        max_eq = max(max_eq, e["equity"])
        if max_eq > 0:
            dd = (max_eq - e["equity"]) / max_eq
            max_dd = max(max_dd, dd)

    summary = {
        "symbol": SYMBOL,
        "interval": INTERVAL,
        "candles": len(rows),
        "initial_capital": INITIAL_CAPITAL,
        "final_equity": round(cash, 4),
        "realized_pnl": round(realized, 4),
        "roi_pct": round(roi, 2),
        "closed_trades": closed,
        "win_rate_pct": round(win_rate, 2),
        "max_drawdown_pct": round(max_dd * 100, 2),
        "generated_at": datetime.now(timezone.utc).isoformat(),
    }
    return summary, trades, equity_curve


def write_outputs(summary, trades, equity_curve):
    with open(os.path.join(OUT_DIR, "latest.json"), "w", encoding="utf-8") as f:
        json.dump({"summary": summary, "trades": trades, "equity": equity_curve[-300:]}, f, ensure_ascii=False, indent=2)

    with open(os.path.join(OUT_DIR, "trades.csv"), "w", newline="", encoding="utf-8") as f:
        w = csv.DictWriter(f, fieldnames=["time", "side", "price", "qty", "fee", "reason", "pnl"])
        w.writeheader()
        w.writerows(trades)

    with open(os.path.join(OUT_DIR, "equity.csv"), "w", newline="", encoding="utf-8") as f:
        w = csv.DictWriter(f, fieldnames=["time", "equity", "price"])
        w.writeheader()
        w.writerows(equity_curve)


if __name__ == "__main__":
    rows = fetch_klines(SYMBOL, INTERVAL, LIMIT)
    summary, trades, equity_curve = run_backtest(rows)
    write_outputs(summary, trades, equity_curve)
    print(json.dumps(summary, ensure_ascii=False))
