# engine/ledger.py from datetime import datetime, timezone from indian_paper_trading_strategy.engine.db import insert_engine_event, run_with_retry, get_context from indian_paper_trading_strategy.engine.time_utils import normalize_logical_time def _event_exists_in_tx(cur, event, logical_time, user_id: str | None = None, run_id: str | None = None): scope_user, scope_run = get_context(user_id, run_id) logical_ts = normalize_logical_time(logical_time) cur.execute( """ SELECT 1 FROM event_ledger WHERE user_id = %s AND run_id = %s AND event = %s AND logical_time = %s LIMIT 1 """, (scope_user, scope_run, event, logical_ts), ) return cur.fetchone() is not None def event_exists(event, logical_time, *, cur=None, user_id: str | None = None, run_id: str | None = None): if cur is not None: return _event_exists_in_tx(cur, event, logical_time, user_id=user_id, run_id=run_id) def _op(cur, _conn): return _event_exists_in_tx(cur, event, logical_time, user_id=user_id, run_id=run_id) return run_with_retry(_op) def _log_event_in_tx( cur, event, payload, ts, logical_time=None, user_id: str | None = None, run_id: str | None = None, ): scope_user, scope_run = get_context(user_id, run_id) logical_ts = normalize_logical_time(logical_time or ts) cur.execute( """ INSERT INTO event_ledger ( user_id, run_id, timestamp, logical_time, event, nifty_units, gold_units, nifty_price, gold_price, amount ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s) ON CONFLICT DO NOTHING """, ( scope_user, scope_run, ts, logical_ts, event, payload.get("nifty_units"), payload.get("gold_units"), payload.get("nifty_price"), payload.get("gold_price"), payload.get("amount"), ), ) if cur.rowcount: insert_engine_event(cur, event, data=payload, ts=ts) def log_event( event, payload, *, cur=None, ts=None, logical_time=None, user_id: str | None = None, run_id: str | None = None, ): now = ts or logical_time or datetime.utcnow().replace(tzinfo=timezone.utc) if cur is not None: _log_event_in_tx( cur, event, payload, now, logical_time=logical_time, user_id=user_id, run_id=run_id, ) return def _op(cur, _conn): _log_event_in_tx( cur, event, payload, now, logical_time=logical_time, user_id=user_id, run_id=run_id, ) return run_with_retry(_op)