| import math |
| import pathlib |
| import sys |
|
|
| sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) |
|
|
| from app import compute_signal, futures_pnl, recommend_position_size |
|
|
|
|
| def _mock_data(n=60, start=1900.0, step=0.2): |
| t = list(range(1, n + 1)) |
| c = [start + i * step for i in range(n)] |
| o = [x - 0.2 for x in c] |
| h = [x + 0.4 for x in c] |
| l = [x - 0.4 for x in c] |
| v = [1000 + i for i in range(n)] |
| return {"t": t, "o": o, "h": h, "l": l, "c": c, "v": v} |
|
|
|
|
| def test_futures_pnl_long(): |
| out = futures_pnl(side="LONG", entry=1900, exit_price=1902, contracts=2) |
| assert out["points"] == 2 |
| assert out["gross_pnl_vnd"] > 0 |
|
|
|
|
| def test_recommend_position_size_non_negative(): |
| out = recommend_position_size(price=1900, atr=2.0, account_balance=100_000_000) |
| assert out["contracts"] >= 0 |
| assert out["risk_per_contract"] > 0 |
|
|
|
|
| def test_compute_signal_shape(): |
| out = compute_signal(_mock_data()) |
| assert out["signal"] in {"BUY", "SELL", "HOLD", "WAIT"} |
| assert isinstance(out["confidence"], float) |
| assert "reason" in out |
|
|
|
|
| def test_compute_signal_wait_when_short_history(): |
| out = compute_signal(_mock_data(n=20)) |
| assert out["signal"] == "WAIT" |
| assert "Warming up" in out["reason"] |
|
|
|
|
| def test_compute_signal_risk_when_actionable(): |
| out = compute_signal(_mock_data(n=80, start=1800, step=0.5)) |
| if out["signal"] in {"BUY", "SELL", "HOLD"}: |
| |
| assert "risk" in out |
| if out["risk"] is not None: |
| assert math.isfinite(out["risk"]["atr"]) |