feat: 봇 시작 시 과거 캔들 200개 프리로드 (즉시 신호 계산 가능)

Made-with: Cursor
This commit is contained in:
21in7
2026-03-01 13:02:42 +09:00
parent 8e5ec82244
commit 4940de16fc
2 changed files with 46 additions and 0 deletions

View File

@@ -45,11 +45,37 @@ class KlineStream:
df.set_index("timestamp", inplace=True) df.set_index("timestamp", inplace=True)
return df return df
async def _preload_history(self, client: AsyncClient, limit: int = 200):
"""REST API로 과거 캔들 데이터를 버퍼에 미리 채운다."""
logger.info(f"과거 캔들 {limit}개 로드 중...")
loop = asyncio.get_event_loop()
klines = await loop.run_in_executor(
None,
lambda: client.futures_klines(
symbol=self.symbol.upper(),
interval=self.interval,
limit=limit,
),
)
# 마지막 캔들은 아직 닫히지 않았을 수 있으므로 제외
for k in klines[:-1]:
self.buffer.append({
"timestamp": k[0],
"open": float(k[1]),
"high": float(k[2]),
"low": float(k[3]),
"close": float(k[4]),
"volume": float(k[5]),
"is_closed": True,
})
logger.info(f"과거 캔들 {len(self.buffer)}개 로드 완료 — 즉시 신호 계산 가능")
async def start(self, api_key: str, api_secret: str): async def start(self, api_key: str, api_secret: str):
client = await AsyncClient.create( client = await AsyncClient.create(
api_key=api_key, api_key=api_key,
api_secret=api_secret, api_secret=api_secret,
) )
await self._preload_history(client)
bm = BinanceSocketManager(client) bm = BinanceSocketManager(client)
stream_name = f"{self.symbol}@kline_{self.interval}" stream_name = f"{self.symbol}@kline_{self.interval}"
logger.info(f"WebSocket 스트림 시작: {stream_name}") logger.info(f"WebSocket 스트림 시작: {stream_name}")

View File

@@ -44,3 +44,23 @@ async def test_callback_called_on_closed_candle():
} }
stream.handle_message(raw_msg) stream.handle_message(raw_msg)
assert len(received) == 1 assert len(received) == 1
@pytest.mark.asyncio
async def test_preload_history_fills_buffer():
stream = KlineStream(symbol="XRPUSDT", interval="1m", buffer_size=200)
# REST API 응답 형식: [open_time, open, high, low, close, volume, ...]
fake_klines = [
[1700000000000 + i * 60000, "0.5", "0.51", "0.49", "0.505", "100000",
0, "0", "0", "0", "0", "0"]
for i in range(201) # 201개 반환 → 마지막 1개 제외 → 200개 버퍼
]
mock_client = MagicMock()
mock_client.futures_klines.return_value = fake_klines
await stream._preload_history(mock_client, limit=200)
assert len(stream.buffer) == 200
assert stream.get_dataframe() is not None