cli.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import argparse
  2. import json
  3. from dataclasses import asdict
  4. from pathlib import Path
  5. from typing import Callable, Sequence
  6. from okx_codex_trader.backtest import run_backtest
  7. from okx_codex_trader.codex_analyzer import analyze_with_codex
  8. from okx_codex_trader.config import Config, load_config
  9. from okx_codex_trader.okx_client import OkxClient
  10. from okx_codex_trader.strategy import validate_signal
  11. SUPPORTED_SYMBOLS = ("BTC-USDT-SWAP", "ETH-USDT-SWAP")
  12. def build_parser() -> argparse.ArgumentParser:
  13. parser = argparse.ArgumentParser(prog="okx-codex-trader")
  14. subparsers = parser.add_subparsers(dest="command", required=True)
  15. fetch_history = subparsers.add_parser("fetch-history")
  16. fetch_history.add_argument("--symbol", choices=SUPPORTED_SYMBOLS, required=True)
  17. fetch_history.add_argument("--bar", required=True)
  18. fetch_history.add_argument("--limit", type=int, required=True)
  19. backtest = subparsers.add_parser("backtest")
  20. backtest.add_argument("--symbol", choices=SUPPORTED_SYMBOLS, required=True)
  21. backtest.add_argument("--bar", required=True)
  22. backtest.add_argument("--limit", type=int, required=True)
  23. backtest.add_argument("--leverage", type=int, choices=(1, 2, 3), required=True)
  24. analyze = subparsers.add_parser("analyze")
  25. analyze.add_argument("--symbol", choices=SUPPORTED_SYMBOLS, required=True)
  26. analyze.add_argument("--bar", required=True)
  27. analyze.add_argument("--limit", type=int, required=True)
  28. analyze.add_argument("--output-file", required=True)
  29. paper_order = subparsers.add_parser("paper-order")
  30. paper_order.add_argument("--symbol", choices=SUPPORTED_SYMBOLS, required=True)
  31. paper_order.add_argument("--signal-file", required=True)
  32. paper_order.add_argument("--margin-usdt", type=float, required=True)
  33. positions = subparsers.add_parser("positions")
  34. positions.add_argument("--symbol", choices=SUPPORTED_SYMBOLS, required=True)
  35. return parser
  36. def _write_text(path: str, text: str) -> None:
  37. Path(path).write_text(text)
  38. def _dump_json(payload: object) -> str:
  39. return json.dumps(payload, indent=2)
  40. def main_factory(
  41. *,
  42. load_config: Callable[[], Config] = load_config,
  43. client_factory: Callable[[Config], OkxClient] = OkxClient,
  44. analyze_fn: Callable = analyze_with_codex,
  45. write_text: Callable[[str, str], None] = _write_text,
  46. ):
  47. def main(argv: Sequence[str] | None = None) -> int:
  48. parser = build_parser()
  49. args = parser.parse_args(argv)
  50. config = load_config()
  51. client = client_factory(config)
  52. if args.command == "fetch-history":
  53. candles = client.get_candles(args.symbol, args.bar, args.limit)
  54. print(_dump_json([asdict(candle) for candle in candles]))
  55. return 0
  56. if args.command == "backtest":
  57. candles = client.get_candles(args.symbol, args.bar, args.limit)
  58. print(_dump_json(run_backtest(candles=candles, leverage=args.leverage).to_dict()))
  59. return 0
  60. if args.command == "analyze":
  61. candles = client.get_candles(args.symbol, args.bar, args.limit)
  62. signal = analyze_fn(candles=candles, symbol=args.symbol, bar=args.bar)
  63. output = _dump_json(asdict(signal))
  64. write_text(args.output_file, output)
  65. print(output)
  66. return 0
  67. if args.command == "paper-order":
  68. signal = validate_signal(json.loads(Path(args.signal_file).read_text()))
  69. print(_dump_json(asdict(client.place_demo_order(args.symbol, signal, args.margin_usdt))))
  70. return 0
  71. positions = client.get_positions(args.symbol)
  72. print(_dump_json([asdict(position) for position in positions]))
  73. return 0
  74. return main
  75. main = main_factory()
  76. if __name__ == "__main__":
  77. raise SystemExit(main())