Files
momentum-system/schwab/positions/sync.py
momentum devandClaude Opus 4.8 007cd95c79 Baseline before Postgres migration (WS0)
Snapshot before the DuckDB->PostgreSQL migration. data/, archive/, the 232MB
rebuild zip, the Windows .venv, and .env are excluded via .gitignore.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 23:39:29 -04:00

71 lines
2.2 KiB
Python

"""Sync Schwab account state into the event store."""
from __future__ import annotations
import logging
from dataclasses import asdict
from datetime import datetime
from schwab.positions.reader import PositionsReader
from storage.event_schema import (
AccountPositionsUpdatedEvent,
AccountSummaryUpdatedEvent,
EventMode,
)
from storage.event_store import EventStore
logger = logging.getLogger(__name__)
BROKER_NAME = "schwab"
class AccountSync:
def __init__(
self,
store: EventStore,
reader: PositionsReader | None = None,
session_id: str | None = None,
mode: EventMode = EventMode.LIVE,
):
self.store = store
self.reader = reader or PositionsReader()
self.session_id = session_id
self.mode = mode
def sync_summary(self) -> dict:
summary = self.reader.read_account_summary()
self.store.emit(
AccountSummaryUpdatedEvent(
timestamp=datetime.now(),
mode=self.mode,
correlation_id=self.session_id,
message=f"Schwab account {summary.account_id} equity {summary.total_equity}",
broker_name=BROKER_NAME,
account_id=summary.account_id,
account_desc=summary.account_desc,
total_equity=summary.total_equity,
cash_balance=summary.cash_balance,
buying_power=summary.buying_power,
net_liquidating_value=summary.net_liquidating_value,
)
)
return asdict(summary)
def sync_positions(self) -> dict:
snapshot = self.reader.read_positions()
self.store.emit(
AccountPositionsUpdatedEvent(
timestamp=datetime.now(),
mode=self.mode,
correlation_id=self.session_id,
message=f"Schwab positions: {len(snapshot.positions)}",
broker_name=BROKER_NAME,
account_id=snapshot.account_id,
positions=[asdict(p) for p in snapshot.positions],
)
)
return asdict(snapshot)
def sync_all(self) -> dict:
return {"summary": self.sync_summary(), "positions": self.sync_positions()}