Add session state persistence to CSV for restart recovery

This commit is contained in:
Justin Oros
2026-02-05 10:02:59 -07:00
parent c0ba4f25b6
commit d07a62a3df
+87 -1
View File
@@ -30,6 +30,7 @@ VOLATILITY_ANNUALIZATION_FACTOR = 252
SCRIPT_DIR = Path(__file__).parent SCRIPT_DIR = Path(__file__).parent
LOG_PATH = SCRIPT_DIR / "trading.log" LOG_PATH = SCRIPT_DIR / "trading.log"
DEBUG_LOG_PATH = SCRIPT_DIR / "debug.log" DEBUG_LOG_PATH = SCRIPT_DIR / "debug.log"
SESSION_STATE_PATH = SCRIPT_DIR / "session_state.csv"
logging.basicConfig( logging.basicConfig(
level=logging.INFO, level=logging.INFO,
@@ -349,6 +350,71 @@ class PositionState:
signal_state = SignalState() signal_state = SignalState()
position_state = PositionState() position_state = PositionState()
def save_session_state(trades_today, opening_equity, last_bullish_crossover, last_bearish_crossover, session_date):
try:
state_data = {
'timestamp': datetime.now(EASTERN).isoformat(),
'session_date': session_date.strftime('%Y-%m-%d'),
'trades_today': trades_today,
'opening_equity': opening_equity,
'last_bullish_crossover_bar': last_bullish_crossover,
'last_bearish_crossover_bar': last_bearish_crossover
}
df = pd.DataFrame([state_data])
if SESSION_STATE_PATH.exists():
existing = pd.read_csv(SESSION_STATE_PATH)
df = pd.concat([existing, df], ignore_index=True)
df = df.tail(100)
df.to_csv(SESSION_STATE_PATH, index=False)
debug_print(f"Session state saved: trades={trades_today}, equity=${opening_equity:.2f}")
except Exception as e:
debug_print(f"Failed to save session state: {e}")
def load_session_state():
try:
if not SESSION_STATE_PATH.exists():
debug_print("No session state file found, starting fresh")
return None
df = pd.read_csv(SESSION_STATE_PATH)
if len(df) == 0:
debug_print("Session state file empty, starting fresh")
return None
last_state = df.iloc[-1]
last_timestamp = datetime.fromisoformat(last_state['timestamp'])
now = datetime.now(EASTERN)
time_diff = (now - last_timestamp).total_seconds()
if time_diff > 7200:
debug_print(f"Last session state too old ({time_diff/3600:.1f}h ago), starting fresh")
return None
session_date = datetime.strptime(last_state['session_date'], '%Y-%m-%d').date()
if session_date != now.date():
debug_print(f"Last session was on different day ({session_date}), starting fresh")
return None
state = {
'trades_today': int(last_state['trades_today']),
'opening_equity': float(last_state['opening_equity']),
'last_bullish_crossover_bar': int(last_state['last_bullish_crossover_bar']),
'last_bearish_crossover_bar': int(last_state['last_bearish_crossover_bar']),
'timestamp': last_timestamp
}
debug_print(f"Loaded session state from {time_diff/60:.1f}m ago: trades={state['trades_today']}")
logger.info(f"🔄 Resumed session from {time_diff/60:.1f}m ago: {state['trades_today']} trades today")
return state
except Exception as e:
debug_print(f"Failed to load session state: {e}")
return None
def debug_print(message): def debug_print(message):
if DEBUG_MODE: if DEBUG_MODE:
debug_logger.debug(f"🔎 {message}") debug_logger.debug(f"🔎 {message}")
@@ -858,13 +924,15 @@ def main():
logger.info("🔔 Market open - session starting") logger.info("🔔 Market open - session starting")
debug_print("Market open, starting trading session") debug_print("Market open, starting trading session")
current_date = datetime.now(EASTERN)
session_date = current_date.date()
opening_equity = fetch_equity() opening_equity = fetch_equity()
logger.info(f"💵 Starting equity: ${opening_equity:.2f}") logger.info(f"💵 Starting equity: ${opening_equity:.2f}")
settlement_tracker = SettlementTracker() settlement_tracker = SettlementTracker()
if T1_SETTLEMENT_ENABLED: if T1_SETTLEMENT_ENABLED:
current_date = datetime.now(EASTERN)
settlement_tracker.settle_funds(current_date) settlement_tracker.settle_funds(current_date)
position_active = False position_active = False
@@ -879,6 +947,16 @@ def main():
signal_state.reset() signal_state.reset()
position_state.reset() position_state.reset()
restored_state = load_session_state()
if restored_state:
trades_today = restored_state['trades_today']
signal_state.last_bullish_crossover_bar = restored_state['last_bullish_crossover_bar']
signal_state.last_bearish_crossover_bar = restored_state['last_bearish_crossover_bar']
if abs(restored_state['opening_equity'] - opening_equity) < opening_equity * 0.05:
opening_equity = restored_state['opening_equity']
debug_print(f"Restored opening equity: ${opening_equity:.2f}")
logger.info(f"📊 Session restored: {trades_today} trades today")
try: try:
existing_position = api.get_position(SYMBOL) existing_position = api.get_position(SYMBOL)
qty = float(existing_position.qty) qty = float(existing_position.qty)
@@ -1101,6 +1179,14 @@ def main():
status_msg += f" | H:{hourly_trend} | VIX:{vix_level:.1f} | {trades_today}/{MAX_TRADES_PER_DAY}" status_msg += f" | H:{hourly_trend} | VIX:{vix_level:.1f} | {trades_today}/{MAX_TRADES_PER_DAY}"
logger.info(status_msg) logger.info(status_msg)
save_session_state(
trades_today,
opening_equity,
signal_state.last_bullish_crossover_bar,
signal_state.last_bearish_crossover_bar,
session_date
)
debug_print(f"Sleeping {seconds_to_human_readable(POLL_INTERVAL)}...") debug_print(f"Sleeping {seconds_to_human_readable(POLL_INTERVAL)}...")
time.sleep(POLL_INTERVAL) time.sleep(POLL_INTERVAL)