Skip to content

Commit 74ce431

Browse files
HenryHenry
authored andcommitted
fix(runtime): handle multi-instrument candle panels
1 parent 4fa9e25 commit 74ce431

2 files changed

Lines changed: 50 additions & 8 deletions

File tree

backend_api_python/app/services/trading_executor.py

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2023,11 +2023,32 @@ def _member_key(member: dict[str, Any]) -> str:
20232023
return f"{market}:{symbol}{suffix}"
20242024

20252025

2026-
def _latest_frame_timestamp(frame: pd.DataFrame | None) -> pd.Timestamp | None:
2027-
"""Return the newest candle timestamp without assuming an index timezone."""
2028-
if frame is None or frame.empty:
2029-
return None
2030-
try:
2031-
return pd.Timestamp(frame.index[-1])
2032-
except (TypeError, ValueError, IndexError):
2033-
return None
2026+
def _latest_frame_timestamp(
2027+
frames: pd.DataFrame | dict[str, pd.DataFrame] | None,
2028+
) -> pd.Timestamp | None:
2029+
"""Return the newest candle timestamp in a frame or instrument panel.
2030+
2031+
Live Strategy V2 sessions pass the driving-frequency panel as a mapping of
2032+
instrument keys to data frames. Accepting a single frame as well keeps the
2033+
helper useful for focused callers and tests without confusing the panel
2034+
itself with a pandas object.
2035+
"""
2036+
candidates = frames.values() if isinstance(frames, dict) else (frames,)
2037+
latest: pd.Timestamp | None = None
2038+
for frame in candidates:
2039+
if not isinstance(frame, pd.DataFrame) or frame.empty:
2040+
continue
2041+
try:
2042+
timestamp = pd.Timestamp(frame.index[-1])
2043+
if pd.isna(timestamp):
2044+
continue
2045+
timestamp = (
2046+
timestamp.tz_localize("UTC")
2047+
if timestamp.tzinfo is None
2048+
else timestamp.tz_convert("UTC")
2049+
)
2050+
except (TypeError, ValueError, IndexError):
2051+
continue
2052+
if latest is None or timestamp > latest:
2053+
latest = timestamp
2054+
return latest

backend_api_python/tests/test_runtime_io_optimizations.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,27 @@ def test_latest_frame_timestamp_detects_candle_advancement():
143143
assert _latest_frame_timestamp(second) > _latest_frame_timestamp(first)
144144

145145

146+
def test_latest_frame_timestamp_reads_strategy_instrument_panel():
147+
import pandas as pd
148+
149+
panel = {
150+
"Crypto:BTC/USDT@swap": pd.DataFrame(
151+
{"close": [1.0, 2.0]},
152+
index=pd.DatetimeIndex([
153+
"2026-08-31T12:00:00Z",
154+
"2026-08-31T12:01:00Z",
155+
]),
156+
),
157+
"Crypto:ETH/USDT@swap": pd.DataFrame(
158+
{"close": [3.0]},
159+
index=pd.DatetimeIndex(["2026-08-31T12:02:00Z"]),
160+
),
161+
}
162+
163+
assert _latest_frame_timestamp(panel) == pd.Timestamp("2026-08-31T12:02:00Z")
164+
assert _latest_frame_timestamp({}) is None
165+
166+
146167
def test_finnhub_429_blocks_followup_requests_across_symbols(monkeypatch):
147168
class Client:
148169
def __init__(self):

0 commit comments

Comments
 (0)