import os import sys from pathlib import Path PROJECT_ROOT = Path(__file__).resolve().parents[3] if str(PROJECT_ROOT) not in sys.path: sys.path.append(str(PROJECT_ROOT)) from indian_paper_trading_strategy.engine.broker import PaperBroker from indian_paper_trading_strategy.engine.state import load_state, save_state from indian_paper_trading_strategy.engine.db import engine_context, insert_engine_event from app.services.db import run_with_retry from app.services.run_service import get_active_run_id, get_running_run_id _logged_path = False def _broker(): global _logged_path state = load_state(mode="PAPER") initial_cash = float(state.get("initial_cash", 0)) broker = PaperBroker(initial_cash=initial_cash) if not _logged_path: _logged_path = True print( "PaperBroker store path:", { "cwd": os.getcwd(), "paper_store_path": str(broker.store_path) if hasattr(broker, "store_path") else "NO_STORE_PATH", "abs_store_path": os.path.abspath(str(broker.store_path)) if hasattr(broker, "store_path") else "N/A", }, ) return broker def get_paper_broker(user_id: str): run_id = get_active_run_id(user_id) with engine_context(user_id, run_id): return _broker() def get_funds(user_id: str): run_id = get_active_run_id(user_id) with engine_context(user_id, run_id): return _broker().get_funds() def get_positions(user_id: str): run_id = get_active_run_id(user_id) with engine_context(user_id, run_id): positions = _broker().get_positions() enriched = [] for item in positions: qty = float(item.get("qty", 0)) avg = float(item.get("avg_price", 0)) ltp = float(item.get("last_price", 0)) pnl = (ltp - avg) * qty pnl_pct = ((ltp - avg) / avg * 100) if avg else 0.0 enriched.append( { **item, "pnl": pnl, "pnl_pct": pnl_pct, } ) return enriched def get_orders(user_id: str): run_id = get_active_run_id(user_id) with engine_context(user_id, run_id): return _broker().get_orders() def get_trades(user_id: str): run_id = get_active_run_id(user_id) with engine_context(user_id, run_id): return _broker().get_trades() def get_equity_curve(user_id: str): run_id = get_active_run_id(user_id) with engine_context(user_id, run_id): broker = _broker() points = broker.get_equity_curve() if not points: return [] state = load_state(mode="PAPER") initial_cash = float(state.get("initial_cash", 0)) response = [] for point in points: equity = float(point.get("equity", 0)) pnl = point.get("pnl") if pnl is None: pnl = equity - float(initial_cash) response.append( { "timestamp": point.get("timestamp"), "equity": equity, "pnl": float(pnl), } ) return response def add_cash(user_id: str, amount: float): if amount <= 0: raise ValueError("Amount must be positive") run_id = get_running_run_id(user_id) if not run_id: raise ValueError("Strategy must be running to add cash") def _op(cur, _conn): with engine_context(user_id, run_id): state = load_state(mode="PAPER", cur=cur, for_update=True) initial_cash = float(state.get("initial_cash", 0)) broker = PaperBroker(initial_cash=initial_cash) store = broker._load_store(cur=cur, for_update=True) cash = float(store.get("cash", 0)) + amount store["cash"] = cash broker._save_store(store, cur=cur) state["cash"] = cash state["initial_cash"] = initial_cash + amount state["total_invested"] = float(state.get("total_invested", 0)) + amount save_state( state, mode="PAPER", cur=cur, emit_event=True, event_meta={"source": "add_cash"}, ) insert_engine_event( cur, "CASH_ADDED", data={"amount": amount, "cash": cash}, ) return state return run_with_retry(_op) def reset_paper_state(user_id: str): run_id = get_active_run_id(user_id) def _op(cur, _conn): with engine_context(user_id, run_id): cur.execute( "DELETE FROM strategy_log WHERE user_id = %s AND run_id = %s", (user_id, run_id), ) cur.execute( "DELETE FROM engine_event WHERE user_id = %s AND run_id = %s", (user_id, run_id), ) cur.execute( "DELETE FROM paper_equity_curve WHERE user_id = %s AND run_id = %s", (user_id, run_id), ) cur.execute( "DELETE FROM paper_trade WHERE user_id = %s AND run_id = %s", (user_id, run_id), ) cur.execute( "DELETE FROM paper_order WHERE user_id = %s AND run_id = %s", (user_id, run_id), ) cur.execute( "DELETE FROM paper_position WHERE user_id = %s AND run_id = %s", (user_id, run_id), ) cur.execute( "DELETE FROM paper_broker_account WHERE user_id = %s AND run_id = %s", (user_id, run_id), ) cur.execute( "DELETE FROM mtm_ledger WHERE user_id = %s AND run_id = %s", (user_id, run_id), ) cur.execute( "DELETE FROM event_ledger WHERE user_id = %s AND run_id = %s", (user_id, run_id), ) cur.execute( "DELETE FROM engine_state_paper WHERE user_id = %s AND run_id = %s", (user_id, run_id), ) insert_engine_event(cur, "PAPER_RESET", data={}) run_with_retry(_op)