strategy.py 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. from typing import Mapping
  2. from okx_codex_trader.models import Candle, TradeSignal
  3. def simple_moving_average(candles: list[Candle], window: int) -> list[float | None]:
  4. averages: list[float | None] = []
  5. running_total = 0.0
  6. for index, candle in enumerate(candles):
  7. running_total += candle.close
  8. if index >= window:
  9. running_total -= candles[index - window].close
  10. divisor = window if index + 1 >= window else index + 1
  11. averages.append(running_total / divisor)
  12. return averages
  13. def validate_signal(payload: Mapping[str, object]) -> TradeSignal:
  14. required_keys = {
  15. "action",
  16. "confidence",
  17. "leverage",
  18. "entry_price",
  19. "take_profit_price",
  20. "stop_loss_price",
  21. "reason",
  22. }
  23. if set(payload) != required_keys:
  24. raise ValueError("signal shape is invalid")
  25. action = payload["action"]
  26. if action not in {"long", "short", "flat"}:
  27. raise ValueError("signal action is invalid")
  28. confidence = payload["confidence"]
  29. if isinstance(confidence, bool):
  30. raise ValueError("signal confidence is invalid")
  31. if not isinstance(confidence, int | float) or not 0 <= float(confidence) <= 1:
  32. raise ValueError("signal confidence is invalid")
  33. leverage = payload["leverage"]
  34. if isinstance(leverage, bool):
  35. raise ValueError("signal leverage is invalid")
  36. if not isinstance(leverage, int) or not 1 <= leverage <= 3:
  37. raise ValueError("signal leverage is invalid")
  38. entry_price = payload["entry_price"]
  39. if entry_price is not None:
  40. if isinstance(entry_price, bool):
  41. raise ValueError("signal entry_price is invalid")
  42. if not isinstance(entry_price, int | float):
  43. raise ValueError("signal entry_price is invalid")
  44. entry_price = float(entry_price)
  45. take_profit_price = payload["take_profit_price"]
  46. if take_profit_price is not None:
  47. if isinstance(take_profit_price, bool):
  48. raise ValueError("signal take_profit_price is invalid")
  49. if not isinstance(take_profit_price, int | float):
  50. raise ValueError("signal take_profit_price is invalid")
  51. take_profit_price = float(take_profit_price)
  52. stop_loss_price = payload["stop_loss_price"]
  53. if stop_loss_price is not None:
  54. if isinstance(stop_loss_price, bool):
  55. raise ValueError("signal stop_loss_price is invalid")
  56. if not isinstance(stop_loss_price, int | float):
  57. raise ValueError("signal stop_loss_price is invalid")
  58. stop_loss_price = float(stop_loss_price)
  59. reason = payload["reason"]
  60. if not isinstance(reason, str) or not reason:
  61. raise ValueError("signal reason is invalid")
  62. return TradeSignal(
  63. action=action,
  64. confidence=float(confidence),
  65. leverage=leverage,
  66. entry_price=entry_price,
  67. take_profit_price=take_profit_price,
  68. stop_loss_price=stop_loss_price,
  69. reason=reason,
  70. )