|
|
@@ -4,6 +4,7 @@ from pathlib import Path
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
+from okx_codex_trader.backtest import run_backtest
|
|
|
from okx_codex_trader.cli import main_factory
|
|
|
from okx_codex_trader.config import Config
|
|
|
from okx_codex_trader.models import Candle, OrderResult, Position, TradeSignal
|
|
|
@@ -100,22 +101,24 @@ def build_main_with_stubs():
|
|
|
|
|
|
def test_fetch_history_prints_candle_json(capsys):
|
|
|
main, client = build_main_with_stubs()
|
|
|
+ expected = [asdict(candle) for candle in sample_candles(limit=20)]
|
|
|
|
|
|
exit_code = main(["fetch-history", "--symbol", "BTC-USDT-SWAP", "--bar", "1H", "--limit", "20"])
|
|
|
|
|
|
assert exit_code == 0
|
|
|
assert client.get_candles_called_with == ("BTC-USDT-SWAP", "1H", 20)
|
|
|
- assert '"symbol": "BTC-USDT-SWAP"' in capsys.readouterr().out
|
|
|
+ assert json.loads(capsys.readouterr().out) == expected
|
|
|
|
|
|
|
|
|
def test_backtest_prints_summary_json(capsys):
|
|
|
main, client = build_main_with_stubs()
|
|
|
+ expected = run_backtest(candles=sample_candles(limit=50), leverage=2).to_dict()
|
|
|
|
|
|
exit_code = main(["backtest", "--symbol", "BTC-USDT-SWAP", "--bar", "1H", "--limit", "50", "--leverage", "2"])
|
|
|
|
|
|
assert exit_code == 0
|
|
|
assert client.get_candles_called_with == ("BTC-USDT-SWAP", "1H", 50)
|
|
|
- assert '"trade_count"' in capsys.readouterr().out
|
|
|
+ assert json.loads(capsys.readouterr().out) == expected
|
|
|
|
|
|
|
|
|
def test_analyze_writes_output_file_and_stdout(tmp_path, capsys):
|
|
|
@@ -138,15 +141,26 @@ def test_analyze_writes_output_file_and_stdout(tmp_path, capsys):
|
|
|
|
|
|
assert exit_code == 0
|
|
|
assert output_file.exists()
|
|
|
- assert json.loads(output_file.read_text()) == valid_signal()
|
|
|
assert client.get_candles_called_with == ("BTC-USDT-SWAP", "1H", 20)
|
|
|
- assert '"action"' in capsys.readouterr().out
|
|
|
+ stdout = capsys.readouterr().out.strip()
|
|
|
+ file_text = output_file.read_text()
|
|
|
+ assert stdout == file_text
|
|
|
+ assert json.loads(stdout) == valid_signal()
|
|
|
|
|
|
|
|
|
def test_paper_order_reads_signal_file_and_outputs_order_json(tmp_path, capsys):
|
|
|
main, client = build_main_with_stubs()
|
|
|
signal_file = tmp_path / "signal.json"
|
|
|
signal_file.write_text(json.dumps(valid_signal()))
|
|
|
+ expected = {
|
|
|
+ "status": "placed",
|
|
|
+ "order_id": "demo-order-1",
|
|
|
+ "symbol": "BTC-USDT-SWAP",
|
|
|
+ "side": "buy",
|
|
|
+ "pos_side": "long",
|
|
|
+ "order_type": "limit",
|
|
|
+ "size": 1.0,
|
|
|
+ }
|
|
|
|
|
|
exit_code = main(
|
|
|
[
|
|
|
@@ -166,17 +180,18 @@ def test_paper_order_reads_signal_file_and_outputs_order_json(tmp_path, capsys):
|
|
|
assert client.place_demo_order_called_with[0] == "BTC-USDT-SWAP"
|
|
|
assert asdict(client.place_demo_order_called_with[1]) == valid_signal()
|
|
|
assert client.place_demo_order_called_with[2] == 100.0
|
|
|
- assert '"status"' in capsys.readouterr().out
|
|
|
+ assert json.loads(capsys.readouterr().out) == expected
|
|
|
|
|
|
|
|
|
def test_positions_prints_position_json(capsys):
|
|
|
main, client = build_main_with_stubs()
|
|
|
+ expected = [{"symbol": "BTC-USDT-SWAP", "pos_side": "long", "size": 2.0, "avg_price": 123.5}]
|
|
|
|
|
|
exit_code = main(["positions", "--symbol", "BTC-USDT-SWAP"])
|
|
|
|
|
|
assert exit_code == 0
|
|
|
assert client.get_positions_called_with == "BTC-USDT-SWAP"
|
|
|
- assert '"symbol": "BTC-USDT-SWAP"' in capsys.readouterr().out
|
|
|
+ assert json.loads(capsys.readouterr().out) == expected
|
|
|
|
|
|
|
|
|
def test_cli_rejects_unsupported_symbol():
|