← Back
"""
Trade Log — JSON journal of all trading actions.
Records entries, partial closes, SL hits, and calculates PnL.

Strategy-agnostic: event types are prefixed per strategy (e.g. SCALP_ENTRY, FUND_SL_HIT).
"""

import json
import os
import logging
from datetime import datetime, timezone, timedelta

from src.config import DATA_DIR

logger = logging.getLogger(__name__)

TRADE_LOG_FILE = os.path.join(DATA_DIR, "trade_log.json")

VANCOUVER_TZ = timezone(timedelta(hours=-7))


def now_van() -> datetime:
    return datetime.now(VANCOUVER_TZ)


def load_trade_log() -> list[dict]:
    """Load trade log from file."""
    if not os.path.exists(TRADE_LOG_FILE):
        return []
    try:
        with open(TRADE_LOG_FILE, "r") as f:
            return json.load(f)
    except Exception as e:
        logger.error(f"Failed to load trade log: {e}")
        return []


def save_trade_log(log: list[dict]):
    """Save trade log to file."""
    os.makedirs(DATA_DIR, exist_ok=True)
    try:
        with open(TRADE_LOG_FILE, "w") as f:
            json.dump(log, f, indent=2, ensure_ascii=False)
    except Exception as e:
        logger.error(f"Failed to save trade log: {e}")


def log_event(event_type: str, data: dict) -> dict:
    """
    Append event to trade log.

    event_type should be prefixed with strategy name:
        e.g. FUND_ENTRY, FUND_TP1_HIT, FUND_SL_HIT, FUND_MANUAL_CLOSE
    """
    now = now_van()
    event = {
        "timestamp": now.isoformat(),
        "timestamp_utc": datetime.now(timezone.utc).isoformat(),
        "event": event_type,
        **data,
    }

    log = load_trade_log()
    log.append(event)
    save_trade_log(log)

    logger.info(f"Trade event: {event_type} | {data.get('symbol', '?')}")
    return event


def get_open_trades(prefix: str = "") -> list[dict]:
    """
    Get entries that don't have a closing event — for recovery on restart.

    Args:
        prefix: strategy prefix to filter (e.g. "FUND_"). Empty = all.
    """
    log = load_trade_log()

    open_symbols = {}
    close_suffixes = {"SL_HIT", "TP3_HIT", "MANUAL_CLOSE", "TP", "TIME_STOP"}

    for event in log:
        symbol = event.get("symbol", "")
        evt = event.get("event", "")

        if prefix and not evt.startswith(prefix):
            continue

        if evt.endswith("ENTRY"):
            open_symbols[symbol] = event
        else:
            # Check if this is a close event
            for suffix in close_suffixes:
                if evt.endswith(suffix) and symbol in open_symbols:
                    del open_symbols[symbol]
                    break

    return list(open_symbols.values())


def _filter_by_period(log: list[dict], period: str) -> list[dict]:
    """Filter trade log events by time period."""
    now = now_van()
    if period == "today":
        start = now.replace(hour=0, minute=0, second=0, microsecond=0)
    elif period == "week":
        start = now - timedelta(days=7)
    elif period == "month":
        start = now - timedelta(days=30)
    else:
        return log

    filtered = []
    for e in log:
        try:
            ts = datetime.fromisoformat(e["timestamp"])
            if ts >= start:
                filtered.append(e)
        except Exception:
            continue
    return filtered


def calculate_strategy_summary(prefix: str, period: str = "all") -> dict:
    """
    Calculate PnL summary for a specific strategy.

    Args:
        prefix: strategy event prefix (e.g. "FUND_", "SCALP_")
        period: "today", "week", "month", "all"
    """
    log = load_trade_log()
    filtered = _filter_by_period(log, period)

    # Only events for this strategy
    events = [e for e in filtered if e.get("event", "").startswith(prefix)]

    entries = [e for e in events if e.get("event", "").endswith("ENTRY")]

    # Sum up PnL from all close events
    total_pnl = 0.0
    for e in events:
        if "pnl_usdt" in e:
            total_pnl += e["pnl_usdt"]
        elif "realized_pnl_usdt" in e:
            total_pnl += e["realized_pnl_usdt"]

    # Count wins/losses by trade_id
    trades = {}
    for e in events:
        tid = e.get("trade_id", "")
        if not tid:
            continue
        if tid not in trades:
            trades[tid] = {"won": False, "closed": False}

        evt = e.get("event", "")
        if "TP" in evt and "HIT" in evt:
            trades[tid]["won"] = True
        if evt.endswith("TP"):
            trades[tid]["won"] = True
            trades[tid]["closed"] = True
        for suffix in ("SL_HIT", "TP3_HIT", "MANUAL_CLOSE", "TIME_STOP"):
            if evt.endswith(suffix):
                trades[tid]["closed"] = True
        # DCA strategy: DCA_CLOSE with pnl_usdt
        if evt.endswith("CLOSE"):
            trades[tid]["closed"] = True
            pnl = e.get("pnl_usdt", 0)
            if pnl > 0:
                trades[tid]["won"] = True

    closed = {k: v for k, v in trades.items() if v["closed"]}
    total_closed = len(closed)
    wins = sum(1 for v in closed.values() if v["won"])
    losses = total_closed - wins
    win_rate = (wins / total_closed * 100) if total_closed > 0 else 0

    return {
        "strategy": prefix.rstrip("_"),
        "total_entries": len(entries),
        "total_closed": total_closed,
        "wins": wins,
        "losses": losses,
        "win_rate": round(win_rate, 1),
        "total_pnl_usdt": round(total_pnl, 4),
        "period": period,
    }