dragon_state_machine.py 1.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. from __future__ import annotations
  2. from dataclasses import dataclass
  3. from typing import Iterable
  4. @dataclass(frozen=True)
  5. class CandidateSignal:
  6. date: str
  7. side: str
  8. source: str
  9. reason: str
  10. @dataclass(frozen=True)
  11. class LayeredSignal:
  12. date: str
  13. side: str
  14. layer: str
  15. source: str
  16. reason: str
  17. class DragonStateMachine:
  18. """
  19. Baseline signal splitter.
  20. This intentionally does not encode the full Dragon trading rules yet.
  21. It only turns upstream candidate entry/exit signals into:
  22. - real trades
  23. - auxiliary bullish/bearish signals
  24. """
  25. def split(self, candidates: Iterable[CandidateSignal]) -> list[LayeredSignal]:
  26. state = "flat"
  27. layered: list[LayeredSignal] = []
  28. for signal in candidates:
  29. layer = "aux_signal"
  30. reason = signal.reason
  31. if state == "flat" and signal.side == "BUY":
  32. layer = "real_trade"
  33. state = "long"
  34. elif state == "long" and signal.side == "SELL":
  35. layer = "real_trade"
  36. state = "flat"
  37. elif state == "flat" and signal.side == "SELL":
  38. reason = "bearish_signal_after_exit"
  39. elif state == "long" and signal.side == "BUY":
  40. reason = "bullish_signal_while_holding"
  41. layered.append(
  42. LayeredSignal(
  43. date=signal.date,
  44. side=signal.side,
  45. layer=layer,
  46. source=signal.source,
  47. reason=reason,
  48. )
  49. )
  50. return layered