File size: 3,035 Bytes
13fb76c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
"""Virtual paper-trading engine. Uses live Deriv prices but never executes."""
from __future__ import annotations
from typing import Any, Optional
from datetime import datetime, timezone
import uuid

from . import supabase_client as sb
from .config import get_settings
from .schemas import PaperTradeRequest


async def get_or_create_portfolio(user_id: Optional[str], mode: str = "demo") -> dict:
    rows = sb.select("portfolio", eq={"mode": mode, "user_id": user_id} if user_id
                     else {"mode": mode}, limit=1)
    if rows:
        return rows[0]
    s = get_settings()
    created = sb.insert("portfolio", {
        "user_id": user_id, "mode": mode,
        "balance": s.DEMO_STARTING_BALANCE,
        "equity": s.DEMO_STARTING_BALANCE,
    })
    return created or {
        "balance": s.DEMO_STARTING_BALANCE,
        "equity": s.DEMO_STARTING_BALANCE,
        "open_positions": 0, "realized_pnl": 0, "unrealized_pnl": 0,
    }


async def open_paper_trade(req: PaperTradeRequest, current_price: float,

                            user_id: Optional[str] = None) -> dict[str, Any]:
    portfolio = await get_or_create_portfolio(user_id, "demo")
    entry = req.entry or current_price
    row = sb.insert("trade_history", {
        "id": str(uuid.uuid4()),
        "user_id": user_id,
        "prediction_id": req.prediction_id,
        "mode": "demo",
        "symbol": req.symbol,
        "side": req.side,
        "entry_price": entry,
        "size": req.size,
        "stop_loss": req.sl,
        "take_profit": req.tp,
        "status": "open",
        "reason_opened": "AI signal accepted",
        "opened_at": datetime.now(timezone.utc).isoformat(),
    })
    if portfolio.get("id"):
        sb.update("portfolio", portfolio["id"], {
            "open_positions": (portfolio.get("open_positions") or 0) + 1,
        })
    return row or {}


async def close_paper_trade(trade_id: str, exit_price: float,

                             reason: str = "manual close") -> dict[str, Any]:
    rows = sb.select("trade_history", eq={"id": trade_id}, limit=1)
    if not rows:
        return {"ok": False, "message": "Trade not found"}
    t = rows[0]
    sign = 1 if t["side"] == "BUY" else -1
    pnl = sign * (exit_price - float(t["entry_price"])) * float(t["size"])
    sb.update("trade_history", trade_id, {
        "exit_price": exit_price,
        "pnl": pnl,
        "status": "closed",
        "reason_closed": reason,
        "closed_at": datetime.now(timezone.utc).isoformat(),
    })
    pf = await get_or_create_portfolio(t.get("user_id"), "demo")
    if pf.get("id"):
        sb.update("portfolio", pf["id"], {
            "realized_pnl": float(pf.get("realized_pnl") or 0) + pnl,
            "balance": float(pf.get("balance") or 0) + pnl,
            "equity": float(pf.get("equity") or 0) + pnl,
            "open_positions": max(0, (pf.get("open_positions") or 1) - 1),
        })
    return {"ok": True, "pnl": pnl}