fix: label broker sync events by actual execution broker (tradier stack)
Tradier stack emitted account/positions/orders events hardcoded as broker_name=alpaca_paper while the executor filters snapshots by client.broker_name (tradier) — position cap and re-entry block read zero positions, and the dashboard showed 'Alpaca Paper' over real Tradier values. Mode label now defaults from BROKER/TRADIER_ENV (tradier_live) instead of alpaca_paper. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
485cec0372
commit
3871bda616
4 files changed
+41
-14
No files matched your search
+16
-9
@@ -45,6 +45,13 @@ class AlpacaPaperSync:
|
||||
self.client = client or AlpacaPaperClient()
|
||||
self.session_id = session_id
|
||||
self.mode = mode
|
||||
# Label events by the ACTUAL execution broker (BROKER=tradier swaps the
|
||||
# client): the executor filters position snapshots by client.broker_name,
|
||||
# so a hardcoded "alpaca_paper" here made those queries return empty on
|
||||
# the Tradier stack (position cap / re-entry block saw zero positions).
|
||||
self.broker_name = getattr(self.client, "broker_name", BROKER_NAME)
|
||||
self.account_desc = (
|
||||
"Tradier" if self.broker_name == "tradier" else "Alpaca Paper")
|
||||
# last seen open positions, to detect closes by diffing snapshots. None
|
||||
# until the first sync, when it's seeded from the last PERSISTED snapshot
|
||||
# so a close that happened while the loop was down is still journaled.
|
||||
@@ -57,8 +64,8 @@ class AlpacaPaperSync:
|
||||
timestamp=datetime.now(),
|
||||
mode=self.mode,
|
||||
correlation_id=self.session_id,
|
||||
message=f"alpaca_paper sync degraded: {reason}",
|
||||
broker_name=BROKER_NAME,
|
||||
message=f"{self.broker_name} sync degraded: {reason}",
|
||||
broker_name=self.broker_name,
|
||||
previous_health="healthy",
|
||||
new_health="degraded",
|
||||
health_reason=reason,
|
||||
@@ -78,10 +85,10 @@ class AlpacaPaperSync:
|
||||
timestamp=datetime.now(),
|
||||
mode=self.mode,
|
||||
correlation_id=self.session_id,
|
||||
message=f"Alpaca paper equity {account.get('equity')}",
|
||||
broker_name=BROKER_NAME,
|
||||
message=f"{self.account_desc} equity {account.get('equity')}",
|
||||
broker_name=self.broker_name,
|
||||
account_id=account_id,
|
||||
account_desc="Alpaca Paper",
|
||||
account_desc=self.account_desc,
|
||||
total_equity=float(account.get("equity") or 0),
|
||||
cash_balance=float(account.get("cash") or 0),
|
||||
buying_power=float(account.get("buying_power") or 0),
|
||||
@@ -116,8 +123,8 @@ class AlpacaPaperSync:
|
||||
timestamp=datetime.now(),
|
||||
mode=self.mode,
|
||||
correlation_id=self.session_id,
|
||||
message=f"Alpaca paper positions: {len(positions)}",
|
||||
broker_name=BROKER_NAME,
|
||||
message=f"{self.account_desc} positions: {len(positions)}",
|
||||
broker_name=self.broker_name,
|
||||
account_id="paper",
|
||||
positions=positions,
|
||||
)
|
||||
@@ -166,8 +173,8 @@ class AlpacaPaperSync:
|
||||
timestamp=datetime.now(),
|
||||
mode=self.mode,
|
||||
correlation_id=self.session_id,
|
||||
message=f"Alpaca paper orders: {len(orders)}",
|
||||
broker_name=BROKER_NAME,
|
||||
message=f"{self.account_desc} orders: {len(orders)}",
|
||||
broker_name=self.broker_name,
|
||||
account_id="paper",
|
||||
orders=orders,
|
||||
)
|
||||
|
||||
+2
-1
@@ -32,7 +32,8 @@ def main() -> None:
|
||||
# WATCHER_EVENT_DB_PATH is a legacy fallback only (the path is ignored).
|
||||
db_path = os.environ.get("DATABASE_URL") or os.environ.get(
|
||||
"WATCHER_EVENT_DB_PATH", "momentum")
|
||||
mode = os.environ.get("TRADING_EXECUTION_MODE", "alpaca_paper")
|
||||
from trading_mode import TradingModeSettings
|
||||
mode = TradingModeSettings.from_env().execution_mode
|
||||
# Sparklines: the standalone dashboard needs the SAME research-DB reader the
|
||||
# embedded (orchestrator) mode passes — without it DashboardState.bars() short-
|
||||
# circuits to []. The missing wire was purely that this entrypoint never built
|
||||
|
||||
@@ -123,6 +123,18 @@ def test_execution_request_payload_round_trip():
|
||||
assert clone.stop_loss_price == 9.5
|
||||
|
||||
|
||||
def test_sync_labels_events_by_client_broker(store):
|
||||
client = FakeClient()
|
||||
client.broker_name = "tradier"
|
||||
sync = AlpacaPaperSync(store, client=client, session_id="t")
|
||||
sync.sync_all()
|
||||
accounts = query_account_summary_snapshot(store, broker_name="tradier")
|
||||
assert accounts and accounts[0]["account_desc"] == "Tradier"
|
||||
# executor filters by client.broker_name — events must match it
|
||||
assert query_account_positions_snapshot(store, broker_name="tradier")
|
||||
assert query_account_summary_snapshot(store, broker_name="alpaca_paper") == []
|
||||
|
||||
|
||||
def test_sync_all_populates_snapshots(store):
|
||||
sync = AlpacaPaperSync(store, client=FakeClient(), session_id="t")
|
||||
sync.sync_all()
|
||||
|
||||
+11
-4
@@ -5,7 +5,7 @@ from dataclasses import dataclass
|
||||
from typing import Literal, cast
|
||||
|
||||
|
||||
TradingMode = Literal["schwab_live", "alpaca_paper"]
|
||||
TradingMode = Literal["schwab_live", "alpaca_paper", "tradier_live", "tradier_sandbox"]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -17,7 +17,14 @@ class TradingModeSettings:
|
||||
values = dict(os.environ)
|
||||
if env is not None:
|
||||
values.update(env)
|
||||
mode = values.get("TRADING_EXECUTION_MODE", "alpaca_paper")
|
||||
if mode not in {"schwab_live", "alpaca_paper"}:
|
||||
mode = "alpaca_paper"
|
||||
# BROKER=tradier swaps the execution client, so the default mode label
|
||||
# must follow it — else the Tradier stack reports "alpaca_paper".
|
||||
if values.get("BROKER", "alpaca").strip().lower() == "tradier":
|
||||
sandbox = values.get("TRADIER_ENV", "sandbox").strip().lower() != "live"
|
||||
default = "tradier_sandbox" if sandbox else "tradier_live"
|
||||
else:
|
||||
default = "alpaca_paper"
|
||||
mode = values.get("TRADING_EXECUTION_MODE", default)
|
||||
if mode not in {"schwab_live", "alpaca_paper", "tradier_live", "tradier_sandbox"}:
|
||||
mode = default
|
||||
return cls(execution_mode=cast(TradingMode, mode))
|
||||
Reference in new issue
Block a user