Fix live market time handling in strategy engine
This commit is contained in:
parent
f5bd36df64
commit
88ea093525
@ -4,12 +4,13 @@ import sys
|
|||||||
import threading
|
import threading
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
ENGINE_ROOT = Path(__file__).resolve().parents[3]
|
ENGINE_ROOT = Path(__file__).resolve().parents[3]
|
||||||
if str(ENGINE_ROOT) not in sys.path:
|
if str(ENGINE_ROOT) not in sys.path:
|
||||||
sys.path.append(str(ENGINE_ROOT))
|
sys.path.append(str(ENGINE_ROOT))
|
||||||
|
|
||||||
from indian_paper_trading_strategy.engine.market import is_market_open, align_to_market_open
|
from indian_paper_trading_strategy.engine.market import is_market_open, align_to_market_open, market_now
|
||||||
from indian_paper_trading_strategy.engine.runner import start_engine, stop_engine
|
from indian_paper_trading_strategy.engine.runner import start_engine, stop_engine
|
||||||
from indian_paper_trading_strategy.engine.state import init_paper_state, load_state, save_state
|
from indian_paper_trading_strategy.engine.state import init_paper_state, load_state, save_state
|
||||||
from indian_paper_trading_strategy.engine.broker import PaperBroker
|
from indian_paper_trading_strategy.engine.broker import PaperBroker
|
||||||
@ -38,6 +39,7 @@ SEQ_LOCK = threading.Lock()
|
|||||||
SEQ = 0
|
SEQ = 0
|
||||||
LAST_WAIT_LOG_TS = {}
|
LAST_WAIT_LOG_TS = {}
|
||||||
WAIT_LOG_INTERVAL = timedelta(seconds=60)
|
WAIT_LOG_INTERVAL = timedelta(seconds=60)
|
||||||
|
IST = ZoneInfo("Asia/Kolkata")
|
||||||
|
|
||||||
def init_log_state():
|
def init_log_state():
|
||||||
global SEQ
|
global SEQ
|
||||||
@ -154,7 +156,7 @@ def _maybe_parse_json(value):
|
|||||||
|
|
||||||
|
|
||||||
def _local_tz():
|
def _local_tz():
|
||||||
return datetime.now().astimezone().tzinfo
|
return IST
|
||||||
|
|
||||||
|
|
||||||
def _format_local_ts(value: datetime | None):
|
def _format_local_ts(value: datetime | None):
|
||||||
@ -290,7 +292,7 @@ def reactivate_strategy_config(user_id: str, run_id: str):
|
|||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
def _write_status(user_id: str, run_id: str, status):
|
def _write_status(user_id: str, run_id: str, status):
|
||||||
now_local = datetime.now().astimezone()
|
now_local = market_now()
|
||||||
with db_connection() as conn:
|
with db_connection() as conn:
|
||||||
with conn:
|
with conn:
|
||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
@ -755,7 +757,11 @@ def get_strategy_status(user_id: str):
|
|||||||
if next_eligible:
|
if next_eligible:
|
||||||
try:
|
try:
|
||||||
parsed_next = datetime.fromisoformat(next_eligible)
|
parsed_next = datetime.fromisoformat(next_eligible)
|
||||||
now_cmp = datetime.now(parsed_next.tzinfo) if parsed_next.tzinfo else datetime.now()
|
now_cmp = (
|
||||||
|
datetime.now(parsed_next.tzinfo)
|
||||||
|
if parsed_next.tzinfo
|
||||||
|
else market_now().replace(tzinfo=None)
|
||||||
|
)
|
||||||
if parsed_next > now_cmp:
|
if parsed_next > now_cmp:
|
||||||
status["status"] = "WAITING"
|
status["status"] = "WAITING"
|
||||||
except ValueError:
|
except ValueError:
|
||||||
@ -928,7 +934,7 @@ def _issue_is_stale_for_current_state(
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
if event == "EXECUTION_BLOCKED" and reason_key == "market_closed":
|
if event == "EXECUTION_BLOCKED" and reason_key == "market_closed":
|
||||||
return is_market_open(datetime.now())
|
return is_market_open(market_now())
|
||||||
|
|
||||||
if mode != "LIVE":
|
if mode != "LIVE":
|
||||||
return False
|
return False
|
||||||
@ -1026,7 +1032,7 @@ def get_strategy_summary(user_id: str):
|
|||||||
return summary
|
return summary
|
||||||
|
|
||||||
def get_market_status():
|
def get_market_status():
|
||||||
now = datetime.now()
|
now = market_now()
|
||||||
return {
|
return {
|
||||||
"status": "OPEN" if is_market_open(now) else "CLOSED",
|
"status": "OPEN" if is_market_open(now) else "CLOSED",
|
||||||
"checked_at": now.isoformat(),
|
"checked_at": now.isoformat(),
|
||||||
|
|||||||
@ -12,6 +12,7 @@ from psycopg2.extras import execute_values
|
|||||||
|
|
||||||
from indian_paper_trading_strategy.engine.data import fetch_live_price
|
from indian_paper_trading_strategy.engine.data import fetch_live_price
|
||||||
from indian_paper_trading_strategy.engine.db import db_connection, insert_engine_event, run_with_retry, get_context
|
from indian_paper_trading_strategy.engine.db import db_connection, insert_engine_event, run_with_retry, get_context
|
||||||
|
from indian_paper_trading_strategy.engine.market import market_now
|
||||||
|
|
||||||
|
|
||||||
class Broker(ABC):
|
class Broker(ABC):
|
||||||
@ -50,7 +51,7 @@ class BrokerAuthExpired(BrokerError):
|
|||||||
|
|
||||||
|
|
||||||
def _local_tz():
|
def _local_tz():
|
||||||
return datetime.now().astimezone().tzinfo
|
return market_now().tzinfo
|
||||||
|
|
||||||
|
|
||||||
def _format_utc_ts(value: datetime | None):
|
def _format_utc_ts(value: datetime | None):
|
||||||
|
|||||||
@ -5,6 +5,7 @@ from indian_paper_trading_strategy.engine.state import load_state, save_state
|
|||||||
from indian_paper_trading_strategy.engine.broker import Broker, BrokerAuthExpired
|
from indian_paper_trading_strategy.engine.broker import Broker, BrokerAuthExpired
|
||||||
from indian_paper_trading_strategy.engine.ledger import log_event, event_exists
|
from indian_paper_trading_strategy.engine.ledger import log_event, event_exists
|
||||||
from indian_paper_trading_strategy.engine.db import insert_engine_event, run_with_retry
|
from indian_paper_trading_strategy.engine.db import insert_engine_event, run_with_retry
|
||||||
|
from indian_paper_trading_strategy.engine.market import market_now
|
||||||
from indian_paper_trading_strategy.engine.time_utils import compute_logical_time
|
from indian_paper_trading_strategy.engine.time_utils import compute_logical_time
|
||||||
|
|
||||||
def _as_float(value):
|
def _as_float(value):
|
||||||
@ -21,7 +22,7 @@ def _as_float(value):
|
|||||||
return float(value)
|
return float(value)
|
||||||
|
|
||||||
def _local_tz():
|
def _local_tz():
|
||||||
return datetime.now().astimezone().tzinfo
|
return market_now().tzinfo
|
||||||
|
|
||||||
|
|
||||||
def _normalize_now(now):
|
def _normalize_now(now):
|
||||||
|
|||||||
@ -6,6 +6,10 @@ _MARKET_TZ = pytz.timezone("Asia/Kolkata")
|
|||||||
_OPEN_T = dtime(9, 15)
|
_OPEN_T = dtime(9, 15)
|
||||||
_CLOSE_T = dtime(15, 30)
|
_CLOSE_T = dtime(15, 30)
|
||||||
|
|
||||||
|
|
||||||
|
def market_now() -> datetime:
|
||||||
|
return datetime.now(_MARKET_TZ)
|
||||||
|
|
||||||
def _as_market_tz(value: datetime) -> datetime:
|
def _as_market_tz(value: datetime) -> datetime:
|
||||||
if value.tzinfo is None:
|
if value.tzinfo is None:
|
||||||
return _MARKET_TZ.localize(value)
|
return _MARKET_TZ.localize(value)
|
||||||
@ -16,7 +20,7 @@ def is_market_open(now: datetime) -> bool:
|
|||||||
return now.weekday() < 5 and _OPEN_T <= now.time() <= _CLOSE_T
|
return now.weekday() < 5 and _OPEN_T <= now.time() <= _CLOSE_T
|
||||||
|
|
||||||
def india_market_status():
|
def india_market_status():
|
||||||
now = datetime.now(_MARKET_TZ)
|
now = market_now()
|
||||||
|
|
||||||
return is_market_open(now), now
|
return is_market_open(now), now
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ from datetime import datetime, timedelta, timezone
|
|||||||
|
|
||||||
from psycopg2.extras import Json
|
from psycopg2.extras import Json
|
||||||
|
|
||||||
from indian_paper_trading_strategy.engine.market import is_market_open, align_to_market_open
|
from indian_paper_trading_strategy.engine.market import is_market_open, align_to_market_open, market_now
|
||||||
from indian_paper_trading_strategy.engine.execution import try_execute_sip
|
from indian_paper_trading_strategy.engine.execution import try_execute_sip
|
||||||
from indian_paper_trading_strategy.engine.broker import PaperBroker, LiveZerodhaBroker, BrokerAuthExpired
|
from indian_paper_trading_strategy.engine.broker import PaperBroker, LiveZerodhaBroker, BrokerAuthExpired
|
||||||
from indian_paper_trading_strategy.engine.mtm import log_mtm, should_log_mtm
|
from indian_paper_trading_strategy.engine.mtm import log_mtm, should_log_mtm
|
||||||
@ -315,7 +315,7 @@ def _engine_loop(config, stop_event: threading.Event):
|
|||||||
# Gate 2: time to SIP
|
# Gate 2: time to SIP
|
||||||
last_run = _last_execution_anchor(state, mode)
|
last_run = _last_execution_anchor(state, mode)
|
||||||
is_first_run = last_run is None
|
is_first_run = last_run is None
|
||||||
now = datetime.now()
|
now = market_now()
|
||||||
debug_event(
|
debug_event(
|
||||||
"ENGINE_LOOP_TICK",
|
"ENGINE_LOOP_TICK",
|
||||||
"engine loop tick",
|
"engine loop tick",
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
from indian_paper_trading_strategy.engine.db import db_connection, insert_engine_event, run_with_retry, get_context
|
from indian_paper_trading_strategy.engine.db import db_connection, insert_engine_event, run_with_retry, get_context
|
||||||
|
from indian_paper_trading_strategy.engine.market import market_now
|
||||||
|
|
||||||
DEFAULT_STATE = {
|
DEFAULT_STATE = {
|
||||||
"initial_cash": 0.0,
|
"initial_cash": 0.0,
|
||||||
@ -31,7 +32,7 @@ def _default_state(mode: str | None):
|
|||||||
return DEFAULT_STATE.copy()
|
return DEFAULT_STATE.copy()
|
||||||
|
|
||||||
def _local_tz():
|
def _local_tz():
|
||||||
return datetime.now().astimezone().tzinfo
|
return market_now().tzinfo
|
||||||
|
|
||||||
def _format_local_ts(value: datetime | None):
|
def _format_local_ts(value: datetime | None):
|
||||||
if value is None:
|
if value is None:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user