Use closed candles for trend signals
This commit is contained in:
@@ -76,6 +76,7 @@ class MarketData:
|
|||||||
interval=self.settings.base_interval,
|
interval=self.settings.base_interval,
|
||||||
limit=self.settings.kline_limit,
|
limit=self.settings.kline_limit,
|
||||||
)
|
)
|
||||||
|
candles = _closed_candles(candles, self.settings.base_interval)
|
||||||
add_indicators(candles)
|
add_indicators(candles)
|
||||||
self.candles[symbol] = candles
|
self.candles[symbol] = candles
|
||||||
trend_candles = self.client.klines(
|
trend_candles = self.client.klines(
|
||||||
@@ -83,6 +84,7 @@ class MarketData:
|
|||||||
interval=self.settings.trend_interval,
|
interval=self.settings.trend_interval,
|
||||||
limit=self.settings.trend_kline_limit,
|
limit=self.settings.trend_kline_limit,
|
||||||
)
|
)
|
||||||
|
trend_candles = _closed_candles(trend_candles, self.settings.trend_interval)
|
||||||
add_indicators(trend_candles)
|
add_indicators(trend_candles)
|
||||||
self.trend_candles[symbol] = trend_candles
|
self.trend_candles[symbol] = trend_candles
|
||||||
bid, ask = self.client.orderbook_top(symbol)
|
bid, ask = self.client.orderbook_top(symbol)
|
||||||
@@ -173,6 +175,8 @@ class MarketData:
|
|||||||
start = int(row.get("start", 0))
|
start = int(row.get("start", 0))
|
||||||
if start <= 0:
|
if start <= 0:
|
||||||
continue
|
continue
|
||||||
|
if not _is_closed_kline_row(row, self.settings.base_interval):
|
||||||
|
continue
|
||||||
by_timestamp[start] = Candle(
|
by_timestamp[start] = Candle(
|
||||||
timestamp=start,
|
timestamp=start,
|
||||||
open=_float(row.get("open")),
|
open=_float(row.get("open")),
|
||||||
@@ -231,3 +235,33 @@ class MarketData:
|
|||||||
for symbol in self.symbols
|
for symbol in self.symbols
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _closed_candles(candles: list[Candle], interval: str, now_ms: int | None = None) -> list[Candle]:
|
||||||
|
interval_ms = _interval_ms(interval)
|
||||||
|
if interval_ms <= 0:
|
||||||
|
return candles
|
||||||
|
now_ms = now_ms if now_ms is not None else int(utc_now().timestamp() * 1000)
|
||||||
|
return [candle for candle in candles if candle.timestamp + interval_ms <= now_ms]
|
||||||
|
|
||||||
|
|
||||||
|
def _is_closed_kline_row(row: dict[str, Any], interval: str) -> bool:
|
||||||
|
confirm = row.get("confirm")
|
||||||
|
if isinstance(confirm, bool):
|
||||||
|
return confirm
|
||||||
|
start = int(row.get("start", 0) or 0)
|
||||||
|
interval_ms = _interval_ms(interval)
|
||||||
|
if start <= 0 or interval_ms <= 0:
|
||||||
|
return True
|
||||||
|
return start + interval_ms <= int(utc_now().timestamp() * 1000)
|
||||||
|
|
||||||
|
|
||||||
|
def _interval_ms(interval: str) -> int:
|
||||||
|
normalized = str(interval).strip().upper()
|
||||||
|
if normalized == "D":
|
||||||
|
return 24 * 60 * 60 * 1000
|
||||||
|
if normalized == "W":
|
||||||
|
return 7 * 24 * 60 * 60 * 1000
|
||||||
|
if normalized.isdigit():
|
||||||
|
return int(normalized) * 60 * 1000
|
||||||
|
return 0
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from crypto_spot_bot.market_data import _closed_candles, _is_closed_kline_row
|
||||||
|
from crypto_spot_bot.models import Candle
|
||||||
|
|
||||||
|
|
||||||
|
def test_closed_candles_excludes_current_open_interval() -> None:
|
||||||
|
candles = [
|
||||||
|
Candle(0, 100, 101, 99, 100, 10),
|
||||||
|
Candle(3_600_000, 101, 102, 100, 101, 10),
|
||||||
|
Candle(7_200_000, 102, 103, 101, 102, 10),
|
||||||
|
]
|
||||||
|
|
||||||
|
closed = _closed_candles(candles, "60", now_ms=7_200_000 + 1_000)
|
||||||
|
|
||||||
|
assert [candle.timestamp for candle in closed] == [0, 3_600_000]
|
||||||
|
|
||||||
|
|
||||||
|
def test_websocket_kline_requires_confirmed_candle() -> None:
|
||||||
|
assert _is_closed_kline_row({"start": 7_200_000, "confirm": False}, "60") is False
|
||||||
|
assert _is_closed_kline_row({"start": 7_200_000, "confirm": True}, "60") is True
|
||||||
Reference in New Issue
Block a user