This commit is contained in:
2026-10-01 11:11:53 -07:00
parent d945624601
commit 365f0c7181
2 changed files with 116 additions and 3 deletions

View File

@@ -26,6 +26,30 @@ def update_universe(cfg: Config) -> None:
print(f"{len(table)} symbols -> {cfg.data.universe}")
def show_rebalance_schedule(cfg: Config, today: dt.date) -> None:
if not cfg.data.bars.exists():
print("rebalance schedule unavailable: no daily bars; run `make bars`")
return
dates = pd.read_csv(cfg.data.bars, usecols=["date"], parse_dates=["date"])["date"]
sessions = pd.DatetimeIndex(dates.dropna().unique()).sort_values()
sessions = sessions[sessions.date <= today]
if sessions.empty:
print("rebalance schedule unavailable: no sessions through today; run `make bars`")
return
last = sessions[-1].date()
st = cfg.strategy
wait = sessions_until_rebalance(sessions, st.anchor, st.rebalance_every)
if wait is None:
schedule = f"first trading session on or after {st.anchor}"
elif wait == 0:
schedule = f"{last} (0 trading sessions remaining)"
else:
schedule = f"in {wait} trading sessions"
print(f"next rebalance: {schedule} (as of {last}; every {st.rebalance_every} trading sessions)")
if last < today:
print(f"Bars end {last}; run `make bars` to update the rebalance countdown for {today}.")
async def check(cfg: Config) -> None:
async with Broker(cfg.ib) as broker:
state = await broker.account_state()
@@ -36,12 +60,37 @@ async def check(cfg: Config) -> None:
print(f"{len(trades)} open orders")
for t in trades:
print(f" {t.contract.symbol} {t.order.action} {t.order.totalQuantity} {t.order.orderType} {t.orderStatus.status}")
show_rebalance_schedule(cfg, dt.datetime.now(NEW_YORK).date())
contracts, _ = await broker.qualify_stocks(["SPY"])
prices = await broker.snapshot_prices(contracts)
if "SPY" not in contracts:
print("no SPY price: IBKR could not qualify the SPY contract")
return
errors: dict[int, str] = {}
def on_error(req_id, code, message, contract):
if contract == contracts["SPY"]:
errors[code] = message
broker.ib.errorEvent += on_error
try:
prices = await broker.snapshot_prices(contracts)
finally:
broker.ib.errorEvent -= on_error
if "SPY" in prices:
print(f"SPY snapshot {prices['SPY']:.2f} (market_data_type={cfg.ib.market_data_type})")
print(f"SPY snapshot {prices['SPY']:.2f} (requested market_data_type={cfg.ib.market_data_type})")
else:
print("no SPY price: check the market data subscription or set ib.market_data_type = 3")
reason = "; ".join(f"IBKR {code}: {message}" for code, message in errors.items()) or "no valid snapshot received"
print(f"no SPY price (requested market_data_type={cfg.ib.market_data_type}): {reason}")
if 10197 in errors:
print(
"Log out of the competing live market-data session, then retry `make check`. "
"Delayed mode (3/4) does not force delayed quotes when live data permissions exist; "
"changing ib.market_data_type cannot resolve this session conflict."
)
elif cfg.ib.market_data_type in (1, 2):
print("Check the market data subscription, or set ib.market_data_type = 3 to allow delayed data.")
else:
print("Delayed data is already requested; check market data permissions and availability in IB Gateway/TWS.")
async def probe_gateway(cfg: Config) -> None: