test_run_bb_squeeze_executor.py 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. from dataclasses import replace
  2. from okx_codex_trader.models import Candle
  3. from scripts.run_bb_squeeze_executor import EMPTY_STATE, frame_from_candles, refresh_live_frame, signal_from_frame
  4. def candles_with_latest_breakout() -> list[Candle]:
  5. candles = []
  6. price = 100.0
  7. for index in range(1_001):
  8. if index == 1_000:
  9. price = 101.5
  10. candles.append(
  11. Candle(
  12. symbol="ETH-USDT-SWAP",
  13. ts=1_700_000_000_000 + (index * 900_000),
  14. open=price,
  15. high=price,
  16. low=price,
  17. close=price,
  18. volume=1_000.0,
  19. )
  20. )
  21. return candles
  22. def test_signal_uses_latest_confirmed_candle() -> None:
  23. candles = candles_with_latest_breakout()
  24. frame = frame_from_candles(candles)
  25. next_state, signal = signal_from_frame(frame, EMPTY_STATE)
  26. assert signal["decision_candle_ts"] == candles[-1].ts
  27. assert next_state.last_candle_ts == candles[-1].ts
  28. def test_seen_latest_candle_replays_without_order_signal() -> None:
  29. candles = candles_with_latest_breakout()
  30. frame = frame_from_candles(candles)
  31. state = replace(EMPTY_STATE, last_candle_ts=candles[-1].ts)
  32. _, signal = signal_from_frame(frame, state)
  33. assert signal["decision_candle_ts"] == candles[-1].ts
  34. assert signal["signal"] == "state_replay"
  35. def test_loop_predicate_skips_seen_decision_candle() -> None:
  36. candles = candles_with_latest_breakout()
  37. frame = frame_from_candles(candles)
  38. state = replace(EMPTY_STATE, last_candle_ts=candles[-1].ts)
  39. _, signal = signal_from_frame(frame, state)
  40. assert signal["signal"] == "state_replay"
  41. def test_refresh_live_frame_fetches_recent_candles_after_initial_load() -> None:
  42. class Client:
  43. def __init__(self) -> None:
  44. self.limits = []
  45. def get_candles(self, symbol: str, bar: str, limit: int) -> list[Candle]:
  46. self.limits.append(limit)
  47. return candles_with_latest_breakout()
  48. def get_recent_candles(self, symbol: str, bar: str, limit: int) -> list[Candle]:
  49. self.limits.append(limit)
  50. candles = candles_with_latest_breakout()
  51. return [
  52. *candles[-19:],
  53. Candle(
  54. symbol="ETH-USDT-SWAP",
  55. ts=candles[-1].ts + 900_000,
  56. open=102.0,
  57. high=102.0,
  58. low=102.0,
  59. close=102.0,
  60. volume=1_000.0,
  61. ),
  62. ]
  63. client = Client()
  64. initial = refresh_live_frame(client, None)
  65. refreshed = refresh_live_frame(client, initial)
  66. assert client.limits == [1_200, 20]
  67. assert int(refreshed.iloc[-1]["ts"]) == int(initial.iloc[-1]["ts"]) + 900_000