Validate reevaluation conditions against their declared timeframe

This commit is contained in:
eezd committed 2026-10-20 18:47:23 +08:00
1 parent 285b3952d7
commit 193af01913
4 files changed
+60 -5

No files matched your search

+17 -1
View File
@@ -151,10 +151,26 @@ def test_action_rules():
assert check_setup(TradePlan(action="wait", summary="s", reasons="r", risks="x"), context()) == {}
satisfied = TradePlan(action="wait", summary="s", reasons="r", risks="x",
reevaluate=[{"direction": "above", "price": "80000", "timeframe": "5m", "basis": "x"}])
assert "已经满足" in errors_of(satisfied, context())
assert "已经满足" in errors_of(satisfied, context(closed_prices={"5m": Decimal("81000")}))
assert "只能 keep 或 exit" in errors_of(setup_plan(), context(position_side="long", has_plan=True))
@pytest.mark.parametrize("direction,mark,closed,satisfied", [
("above", "110", "100", False), ("above", "100", "110", True),
("below", "100", "110", False), ("below", "110", "100", True),
])
@pytest.mark.parametrize("timeframe", ["mark", "5m"])
def test_reevaluate_uses_its_own_price(direction, mark, closed, satisfied, timeframe):
plan = TradePlan(action="wait", summary="s", reasons="r", risks="x",
reevaluate=[{"direction": direction, "price": "105", "timeframe": timeframe, "basis": "x"}])
ctx = context(mark_price=Decimal(mark), closed_prices={"5m": Decimal(closed)})
expected = satisfied if timeframe == "5m" else not satisfied
if expected:
assert "已经满足" in errors_of(plan, ctx)
else:
assert check_setup(plan, ctx) == {}
# ---------- 应有订单 ----------
LIMIT_PLAN = {"direction": "long", "setup": {}, "entry": {"price": "80500.0", "quantity": "0.0241", "stop": "80150.0"}}
+31
View File
@@ -2,6 +2,7 @@ import asyncio
from datetime import UTC, datetime, timedelta
from decimal import Decimal
from types import SimpleNamespace
from unittest.mock import AsyncMock
import pytest
@@ -288,3 +289,33 @@ def test_replacement_proposal_survives_old_plan_cancellation(trading):
finally:
await engine.market.http.aclose()
asyncio.run(check())
@pytest.mark.parametrize("closed,mark,rejected", [("100", "110", False), ("110", "100", True)])
@pytest.mark.parametrize("last_bar_closed", [False, True])
def test_submit_reevaluate_uses_latest_closed_candle(trading, monkeypatch, closed, mark, rejected, last_bar_closed):
from alphapulse.trading import analyst
from alphapulse.trading.plan import PlanContext
now_ms = int(datetime.now(UTC).timestamp() * 1000)
rows = [[0, "0", "0", "0", closed, "0", now_ms - 1000],
[1, "0", "0", "0", mark, "0", now_ms + 60_000]]
if last_bar_closed:
rows[-1][4], rows[-1][6] = closed, now_ms - 1
monkeypatch.setattr(analyst.market_client, "get", AsyncMock(return_value=rows))
monkeypatch.setattr(analyst, "mark_price", AsyncMock(return_value=Decimal(mark)))
monkeypatch.setattr(analyst, "recent_atr", AsyncMock(return_value=Decimal(1)))
monkeypatch.setattr(analyst, "read_snapshot", AsyncMock(return_value=None))
monkeypatch.setattr(analyst, "add_event", AsyncMock())
ctx = PlanContext(Decimal(mark), Decimal(1000), RULES, Decimal(1), False, None)
prepared = SimpleNamespace(exchange="bybit", context=ctx)
async def check():
result, outcome = await analyst.submit(prepared, "BTCUSDT", 1, {
"action": "wait", "summary": "s", "reasons": "r", "risks": "x",
"reevaluate": [{"direction": "above", "price": "105", "timeframe": "5m", "basis": "收盘确认"}],
})
assert ctx.closed_prices == {"5m": Decimal(closed)}
assert (outcome is None) == rejected
assert ("已经满足" in result) == rejected
asyncio.run(check())