Add doctrine liquidity kernel
This commit is contained in:
@@ -93,6 +93,8 @@ class ApiTests(unittest.TestCase):
|
||||
self.assertIn("backtest_evidence", result[0]["ict_rulebook_mapping"]["present_concepts"])
|
||||
self.assertTrue(result[0]["doctrine_research_status"]["research_only"])
|
||||
self.assertEqual(result[0]["doctrine_research_status"]["research_only_reason"], "missing_doctrine_setup_playbook")
|
||||
self.assertEqual(result[0]["liquidity_kernel_status"]["status"], "study_only")
|
||||
self.assertIn("missing_liquidity_target", result[0]["liquidity_kernel_status"]["reasons"])
|
||||
|
||||
def test_normalize_backtest_summary_row_preserves_existing_quality_gate(self) -> None:
|
||||
row = {"id": 10, "summary": {"total_signals": 3, "quality_gate": {"passed": True}, "by_symbol": {"BTC-USDT": {"count": 3}}}}
|
||||
@@ -122,6 +124,8 @@ class ApiTests(unittest.TestCase):
|
||||
self.assertIn("sample_size_too_small", result["cockpit_promotion_gate"]["reasons"])
|
||||
self.assertEqual(result["doctrine_rule_gap_summary"]["item_count"], 1)
|
||||
self.assertEqual(result["doctrine_rule_gap_summary"]["research_only_count"], 1)
|
||||
self.assertEqual(result["liquidity_kernel_summary"]["item_count"], 1)
|
||||
self.assertIn("missing_liquidity_target", result["liquidity_kernel_summary"]["reason_counts"])
|
||||
|
||||
def test_route_request_trading_doctrine_rules_returns_d1_matrix(self) -> None:
|
||||
status, payload = route_request(
|
||||
@@ -150,6 +154,22 @@ class ApiTests(unittest.TestCase):
|
||||
self.assertEqual(payload["phase"], "D2_PLAYBOOK_BACKTEST_COHORT")
|
||||
self.assertEqual(payload["playbooks"][0]["setup_playbook_id"], "ICT-CH1-6-LONDON-DISCOUNT-REVERSAL")
|
||||
|
||||
def test_route_request_trading_liquidity_kernel_returns_d3_registry(self) -> None:
|
||||
status, payload = route_request(
|
||||
"/trading/liquidity-kernel",
|
||||
{},
|
||||
dependencies={
|
||||
"trading_liquidity_kernel": lambda: {
|
||||
"phase": "D3_LIQUIDITY_KERNEL",
|
||||
"rules": [{"rule_id": "stop_run_sweep_raid_validated"}],
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
self.assertEqual(status, 200)
|
||||
self.assertEqual(payload["phase"], "D3_LIQUIDITY_KERNEL")
|
||||
self.assertEqual(payload["rules"][0]["rule_id"], "stop_run_sweep_raid_validated")
|
||||
|
||||
def test_route_request_trading_candidates_returns_items_payload(self) -> None:
|
||||
status, payload = route_request(
|
||||
"/trading/candidates",
|
||||
|
||||
@@ -118,6 +118,22 @@ class ApiServerRoutingTests(unittest.TestCase):
|
||||
self.assertEqual(payload["phase"], "D2_PLAYBOOK_BACKTEST_COHORT")
|
||||
self.assertEqual(payload["playbook_count"], 1)
|
||||
|
||||
def test_routes_trading_liquidity_kernel(self) -> None:
|
||||
status, payload = route_request(
|
||||
"/trading/liquidity-kernel",
|
||||
{},
|
||||
dependencies={
|
||||
"trading_liquidity_kernel": lambda: {
|
||||
"phase": "D3_LIQUIDITY_KERNEL",
|
||||
"rule_count": 6,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
self.assertEqual(status, 200)
|
||||
self.assertEqual(payload["phase"], "D3_LIQUIDITY_KERNEL")
|
||||
self.assertEqual(payload["rule_count"], 6)
|
||||
|
||||
def test_routes_trading_alert_quality_summary(self) -> None:
|
||||
status, payload = route_request(
|
||||
"/trading/alerts/quality-summary",
|
||||
|
||||
@@ -427,6 +427,14 @@ class BacktestOutcomeTests(unittest.TestCase):
|
||||
"expires_ts": datetime(2026, 5, 6, 4, 0, tzinfo=timezone.utc),
|
||||
"confirmation_state": {"executable": True},
|
||||
"missing_confirmations": [],
|
||||
"liquidity_target_side": "buy_side_liquidity",
|
||||
"working_draw": "upper_external_pool",
|
||||
"liquidity_pool_id": "bsl-old-high",
|
||||
"liquidity_pool_rank": "primary",
|
||||
"liquidity_event": "sweep",
|
||||
"swept_level": 103.0,
|
||||
"sweep_confirmed": True,
|
||||
"post_take_reaction": "acceptance_displacement",
|
||||
"entry_low": 100.0,
|
||||
"entry_high": 100.2,
|
||||
"stop_loss": 99.0,
|
||||
@@ -458,6 +466,8 @@ class BacktestOutcomeTests(unittest.TestCase):
|
||||
self.assertEqual(units[0]["confirmed_ts"], rows[1]["created_ts"])
|
||||
self.assertAlmostEqual(units[0]["rr_tp1"], 0.8181818181)
|
||||
self.assertEqual(units[0]["target_plan"]["tp2"]["objective_layer"], "ERL")
|
||||
self.assertEqual(units[0]["liquidity_backtest_features"]["liquidity_target_side"], "buy_side_liquidity")
|
||||
self.assertEqual(units[0]["liquidity_kernel_status"]["status"], "liquidity_mapped")
|
||||
|
||||
|
||||
class BacktestServiceRunTests(unittest.TestCase):
|
||||
|
||||
@@ -20,6 +20,7 @@ NONEXISTENT_VISUAL_PLAN_NAME = "AI_ICT_" + "视觉盯盘与主动告警增强方
|
||||
ACTIVE_TOP_LEVEL_DOCS = {
|
||||
"AI_ICT_DoctrineKernel市场语言规则内核_D1_2026-05-21_v1.md",
|
||||
"AI_ICT_DoctrineKernel_PlaybookBacktestCohort_D2_2026-05-21_v1.md",
|
||||
"AI_ICT_DoctrineKernel_LiquidityKernel_D3_2026-05-21_v1.md",
|
||||
"AI_ICT_硬性主线约束_必须遵循ICT交易全书_2026-05-21_v1.md",
|
||||
"AI_ICT_实盘辅助分析决策助手ICT主线开发方案_2026-05-21_v1.md",
|
||||
"AI_ICT_主线裁剪审计_S1_2026-05-21_v1.md",
|
||||
|
||||
@@ -2,12 +2,17 @@ import unittest
|
||||
|
||||
from src.services.ict_mainline import (
|
||||
DOCTRINE_KERNEL_PHASE,
|
||||
DOCTRINE_LIQUIDITY_PHASE,
|
||||
DOCTRINE_PLAYBOOK_PHASE,
|
||||
build_doctrine_backtest_cohort,
|
||||
build_liquidity_backtest_features,
|
||||
build_doctrine_playbook_gate,
|
||||
build_doctrine_playbook_registry,
|
||||
build_doctrine_rule_matrix,
|
||||
build_liquidity_kernel_registry,
|
||||
evaluate_doctrine_research_status,
|
||||
evaluate_liquidity_kernel_status,
|
||||
summarize_liquidity_kernel_statuses,
|
||||
summarize_doctrine_rule_gaps,
|
||||
)
|
||||
|
||||
@@ -34,8 +39,79 @@ class IctDoctrineKernelTests(unittest.TestCase):
|
||||
self.assertEqual(playbook["setup_playbook_id"], "ICT-CH1-6-LONDON-DISCOUNT-REVERSAL")
|
||||
self.assertIn("range_definition", playbook)
|
||||
self.assertIn("target_rule", playbook)
|
||||
self.assertIn("liquidity_target_side", playbook["liquidity_event_rule"]["required_fields"])
|
||||
self.assertIn("post_take_reaction", playbook["liquidity_event_rule"]["required_fields"])
|
||||
self.assertTrue(playbook["promotion_gate"]["requires_backtest_quality_gate"])
|
||||
|
||||
def test_d3_liquidity_kernel_registry_exposes_target_ranking_rules(self) -> None:
|
||||
registry = build_liquidity_kernel_registry()
|
||||
|
||||
self.assertEqual(registry["phase"], DOCTRINE_LIQUIDITY_PHASE)
|
||||
self.assertEqual(registry["status"], "active")
|
||||
self.assertGreaterEqual(registry["rule_count"], 6)
|
||||
self.assertIn("liquidity_target_side", registry["required_backtest_feature_fields"])
|
||||
self.assertIn("Stop Run / Sweep / Raid is an evidence component only, never a setup by itself.", registry["hard_rules"])
|
||||
|
||||
def test_sweep_without_ranked_target_stays_study_only(self) -> None:
|
||||
status = evaluate_liquidity_kernel_status(
|
||||
{
|
||||
"liquidity_event": "sweep",
|
||||
"swept_level": 100.5,
|
||||
"sweep_confirmed": True,
|
||||
}
|
||||
)
|
||||
|
||||
self.assertFalse(status["passed"])
|
||||
self.assertEqual(status["phase"], DOCTRINE_LIQUIDITY_PHASE)
|
||||
self.assertEqual(status["status"], "study_only")
|
||||
self.assertIn("missing_liquidity_target", status["reasons"])
|
||||
self.assertIn("missing_liquidity_pool", status["reasons"])
|
||||
self.assertIn("sweep_is_not_setup", status["reasons"])
|
||||
|
||||
def test_complete_liquidity_kernel_payload_maps_without_execution_permission(self) -> None:
|
||||
status = evaluate_liquidity_kernel_status(
|
||||
{
|
||||
"liquidity_target_side": "sell_side_liquidity",
|
||||
"working_draw": "lower_external_pool",
|
||||
"secondary_draw": "range_midpoint",
|
||||
"observation_draw": "upper_bsl",
|
||||
"liquidity_pool_id": "ssl-ny-low",
|
||||
"liquidity_pool_rank": "primary",
|
||||
"liquidity_event": "liquidity_sweep",
|
||||
"swept_level": 99.5,
|
||||
"sweep_confirmed": True,
|
||||
"raid_stop_run_kind": "sweep",
|
||||
"post_take_reaction": "reclaim_displacement",
|
||||
"manipulation_window_state": "confirmed",
|
||||
"reorder_trigger_type": "",
|
||||
}
|
||||
)
|
||||
|
||||
self.assertTrue(status["passed"])
|
||||
self.assertEqual(status["status"], "liquidity_mapped")
|
||||
self.assertEqual(status["execution_boundary"], "read_only_analysis_no_order_submission")
|
||||
|
||||
def test_liquidity_summary_counts_blockers(self) -> None:
|
||||
summary = summarize_liquidity_kernel_statuses(
|
||||
[
|
||||
{"liquidity_event": "sweep", "sweep_confirmed": True},
|
||||
{
|
||||
"liquidity_target_side": "buy_side_liquidity",
|
||||
"working_draw": "upper_external_pool",
|
||||
"liquidity_pool_id": "bsl-old-high",
|
||||
"liquidity_pool_rank": "primary",
|
||||
"liquidity_event": "raid",
|
||||
"swept_level": 105.0,
|
||||
"post_take_reaction": "acceptance_displacement",
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
self.assertEqual(summary["item_count"], 2)
|
||||
self.assertEqual(summary["passed_count"], 1)
|
||||
self.assertGreater(summary["reason_counts"]["sweep_is_not_setup"], 0)
|
||||
self.assertEqual(summary["target_side_counts"]["buy_side_liquidity"], 1)
|
||||
|
||||
def test_playbook_gate_blocks_missing_and_passes_registered_schema(self) -> None:
|
||||
missing = build_doctrine_playbook_gate({"model_code": "SWEEP_MSS_FVG"})
|
||||
|
||||
@@ -115,6 +191,14 @@ class IctDoctrineKernelTests(unittest.TestCase):
|
||||
cohort = build_doctrine_backtest_cohort(
|
||||
{
|
||||
"setup_playbook_id": "ICT-CH1-6-LONDON-DISCOUNT-REVERSAL",
|
||||
"liquidity_target_side": "sell_side_liquidity",
|
||||
"working_draw": "lower_external_pool",
|
||||
"liquidity_pool_id": "ssl-ny-low",
|
||||
"liquidity_pool_rank": "primary",
|
||||
"liquidity_event": "sweep",
|
||||
"swept_level": 99.5,
|
||||
"sweep_confirmed": True,
|
||||
"post_take_reaction": "reclaim_displacement",
|
||||
"summary": {"quality_gate": {"passed": True, "reasons": []}},
|
||||
}
|
||||
)
|
||||
@@ -122,11 +206,33 @@ class IctDoctrineKernelTests(unittest.TestCase):
|
||||
self.assertEqual(cohort["cohort_key"], "ICT-CH1-6-LONDON-DISCOUNT-REVERSAL::complete")
|
||||
self.assertEqual(cohort["doctrine_completeness"], "complete")
|
||||
self.assertTrue(cohort["promotion_gate"]["passed"])
|
||||
self.assertEqual(cohort["liquidity_kernel_status"]["status"], "liquidity_mapped")
|
||||
|
||||
missing = build_doctrine_backtest_cohort({"model_code": "SWEEP_MSS_FVG"})
|
||||
|
||||
self.assertEqual(missing["cohort_key"], "missing_playbook::incomplete")
|
||||
self.assertIn("missing_doctrine_setup_playbook", missing["promotion_gate"]["reasons"])
|
||||
self.assertIn("missing_liquidity_target", missing["promotion_gate"]["reasons"])
|
||||
|
||||
def test_liquidity_backtest_features_extract_nested_candidate_fields(self) -> None:
|
||||
features = build_liquidity_backtest_features(
|
||||
{
|
||||
"candidate": {
|
||||
"entry": {
|
||||
"liquidity_target_side": "buy_side_liquidity",
|
||||
"liquidity_pool_id": "bsl-old-high",
|
||||
},
|
||||
"confirmation_state": {"post_take_reaction": "acceptance_displacement"},
|
||||
},
|
||||
"sweep_confirmed": False,
|
||||
"liquidity_pool_rank": "primary",
|
||||
}
|
||||
)
|
||||
|
||||
self.assertEqual(features["liquidity_target_side"], "buy_side_liquidity")
|
||||
self.assertEqual(features["liquidity_pool_id"], "bsl-old-high")
|
||||
self.assertEqual(features["liquidity_pool_rank"], "primary")
|
||||
self.assertFalse(features["sweep_confirmed"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user