feat: autonomous trading engine — full implementation
- Database migration 018 with 13 tables for trading engine state - Trading engine service (services/trading/) with 12 pure computation modules: position sizer, stop-loss manager, reserve pool, circuit breaker, risk tier controller, correlation matrix, tax lots, trading window, gradual entry, notifications, micro-trading, backtester - Core TradingEngine with pre-trade evaluation pipeline and integration wiring - FastAPI HTTP service with 14 endpoints (health, config, decisions, metrics, backtest) - Performance tracker with Sharpe ratio, drawdown, profit factor computation - 194 Python tests (165 property-based + 29 integration) - Frontend: 13 TanStack Query hooks, 7 dashboard panels, tabbed Trading Engine page - Helm chart entry, network policy, nginx proxy, ingress for trading-engine - Shared infrastructure: enums, Redis keys, TradingConfig in AppConfig
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
"""Trading window utilities for the autonomous trading engine.
|
||||
|
||||
Pure computation module that determines whether a given timestamp falls
|
||||
within the allowed trading window (9:45 AM – 3:45 PM ET on weekdays),
|
||||
whether the US market is open, and when the next trading window opens.
|
||||
|
||||
Uses ``zoneinfo.ZoneInfo("America/New_York")`` for Eastern Time handling.
|
||||
Does not check market holidays (simplified).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, time, timedelta
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
# US Eastern timezone
|
||||
ET = ZoneInfo("America/New_York")
|
||||
|
||||
# Trading window boundaries (excludes first/last 15 min of market hours)
|
||||
WINDOW_OPEN = time(9, 45)
|
||||
WINDOW_CLOSE = time(15, 45)
|
||||
|
||||
# Full US market hours
|
||||
MARKET_OPEN = time(9, 30)
|
||||
MARKET_CLOSE = time(16, 0)
|
||||
|
||||
# Weekday range: Monday=0 … Friday=4
|
||||
_WEEKDAYS = range(0, 5)
|
||||
|
||||
|
||||
def is_within_trading_window(dt: datetime) -> bool:
|
||||
"""Return True if *dt* is between 9:45 AM ET and 3:45 PM ET on a weekday.
|
||||
|
||||
The timestamp is first converted to US/Eastern time. Weekends are
|
||||
always outside the window. Market holidays are **not** checked
|
||||
(simplified implementation).
|
||||
"""
|
||||
et_dt = dt.astimezone(ET)
|
||||
if et_dt.weekday() not in _WEEKDAYS:
|
||||
return False
|
||||
t = et_dt.time()
|
||||
return WINDOW_OPEN <= t < WINDOW_CLOSE
|
||||
|
||||
|
||||
def next_window_open(dt: datetime) -> datetime:
|
||||
"""Return the next datetime when the trading window opens (9:45 AM ET).
|
||||
|
||||
If *dt* is before 9:45 AM ET on a weekday the same day's open is
|
||||
returned. Otherwise the next weekday's 9:45 AM ET is returned.
|
||||
"""
|
||||
et_dt = dt.astimezone(ET)
|
||||
today_open = et_dt.replace(
|
||||
hour=WINDOW_OPEN.hour,
|
||||
minute=WINDOW_OPEN.minute,
|
||||
second=0,
|
||||
microsecond=0,
|
||||
)
|
||||
|
||||
# If we haven't reached today's open yet and it's a weekday, return today
|
||||
if et_dt < today_open and et_dt.weekday() in _WEEKDAYS:
|
||||
return today_open
|
||||
|
||||
# Otherwise advance to the next weekday
|
||||
candidate = et_dt + timedelta(days=1)
|
||||
candidate = candidate.replace(
|
||||
hour=WINDOW_OPEN.hour,
|
||||
minute=WINDOW_OPEN.minute,
|
||||
second=0,
|
||||
microsecond=0,
|
||||
)
|
||||
while candidate.weekday() not in _WEEKDAYS:
|
||||
candidate += timedelta(days=1)
|
||||
return candidate
|
||||
|
||||
|
||||
def is_market_open(dt: datetime) -> bool:
|
||||
"""Return True if *dt* is during US market hours (9:30 AM – 4:00 PM ET) on a weekday."""
|
||||
et_dt = dt.astimezone(ET)
|
||||
if et_dt.weekday() not in _WEEKDAYS:
|
||||
return False
|
||||
t = et_dt.time()
|
||||
return MARKET_OPEN <= t < MARKET_CLOSE
|
||||
Reference in New Issue
Block a user