Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,56 @@ jobs:
skipped = sum(int(suite.attrib.get("skipped", 0)) for suite in suites)
assert skipped == 0, f"required execution tests skipped: {skipped}"
PY

research-unified-net-consumer:
# Explicit CLI/report acceptance only. Existing production pins and both
# other research overlay environments keep their prior identities.
runs-on: ubuntu-latest
timeout-minutes: 15
env:
QPK_UNIFIED_NET_REPORT_SHA: b23511cbb28b72743e35512ef4b3c73f1cd43c23
UES_REQUIRE_UNIFIED_NET_REPORT: "1"
steps:
- name: Checkout
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0

- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: "3.11"

- name: Install isolated latest report overlay
run: |
set -euo pipefail
python -m pip install --upgrade pip
python -m pip install -e . pytest pandas_market_calendars
python -m pip install --force-reinstall \
"quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@${QPK_UNIFIED_NET_REPORT_SHA}"
python -m pip check
python - <<'PY'
import inspect, json, os
from importlib.metadata import distribution
from quant_platform_kit.research_stats.unified_report import build_unified_net_report
installed = json.loads(distribution("quant-platform-kit").read_text("direct_url.json"))
assert installed["vcs_info"]["commit_id"] == os.environ["QPK_UNIFIED_NET_REPORT_SHA"]
assert "include_outcome_diagnostics" in inspect.signature(build_unified_net_report).parameters
report = build_unified_net_report([-.1, 0.0, .2], include_outcome_diagnostics=True)
assert len(report.metrics) == 12 and report.no_order is True
print("Required latest unified/outcome report capability and immutable source verified.")
PY

- name: Run required actual CLI and report acceptance
run: |
set -euo pipefail
PYTHONPATH=src python -m pytest -q -rs \
tests/test_c3_batch_a_existing_member_baseline.py \
--junitxml=unified-net-consumer-tests.xml
python - <<'PY'
import xml.etree.ElementTree as ET
root = ET.parse("unified-net-consumer-tests.xml").getroot()
cases = list(root.iter("testcase"))
assert cases and any("unified_cli_uses_actual_selected_net_returns" in c.attrib["name"] for c in cases)
assert any("unified_hand_math" in c.attrib["name"] for c in cases)
assert not any(list(root.iter(tag)) for tag in ("skipped", "failure", "error"))
print("Actual CLI chain and positive hand-math tests executed with no skipped/failed cases.")
PY
39 changes: 39 additions & 0 deletions docs/research/c3_self_financing_combo_ledger.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,42 @@ MAR shortfall RMS 的 Sortino。rf 和 MAR 分别是显式年简单小数,除
`NOT_ESTIMATED`:这些单路径和固定预算比较没有完整 trial 集合及真实依赖/
多重试验假设。报告始终 research/synthetic、report-only、非晋级、不可实盘
或配仓。真实净成本 OOS、联合风险资格与成员资格不由合成测试授予。

## Batch A 历史入口的可选 B3 诊断

已有 `scripts/run_c3_batch_a_existing_member_baseline.py --historical-account-report`
可显式增加 `--include-unified-net-report`。对应 Python 入口
`report_historical_batch_a_baselines` 和 `compute_historical_research_metrics`
的参数为 `include_unified_net_report=False`;未请求时保留原字段、算术和
行为,不导入或要求新 B3 能力。Batch A cloud workflow 默认不增加此 flag,
生产 QPK pin、既有两个研究 overlay、冻结候选/报告/账本和排名保持。

显式请求需要 QPK `b23511cbb28b72743e35512ef4b3c73f1cd43c23` 或实际具有
相同 B3/outcome 合同的研究安装。旧 pin 缺该能力时返回明确的
`UNIFIED_NET_REPORT_CAPABILITY_UNAVAILABLE`,不会静默用八项代替十二项。
CLI 的模式/能力错误在读取私有输入前拒绝;输入绑定、来源重算与整个
历史报告的已有原子拒绝边界仍保留。新隔离 CI job 核验实际 immutable
安装来源、强制能力,并执行同一 CLI/report 链和数值验收,禁止跳过。

每本已有账户报告附独立 `unified_net_report` 及 `unified_net_report_basis`。
它使用该本已验证 ledger 的精确同窗口净简单收益,不额外扣费,不补零、
删观察或增加初始 NAV 收益。B3 的 Sharpe 是同频未年化 raw-net、ddof=1;
旧扣 rf 的年化 Sharpe/MAR Sortino 原字段不替换。四项 outcome 为
`net_win_rate_ex_zero_per_period`、`net_payoff_ratio_per_period`、
`net_profit_factor_per_period`、`mean_net_return_per_period`:周期剔零胜率、
平均正负收益比、正负收益总额比及含零观察的平均净收益。它们不表示
逐笔闭合交易成绩、未来收益或新排名/否决条件。

输入没有同路径 gross counterfactual 或完整累计 trial 证据,故成本拖累与
DSR 分别保留 `UNCOMPUTABLE/missing_gross_returns` 和
`UNCOMPUTABLE/missing_trial_evidence`、value=null;不反加费用、不拿其他
成本场景冒充 gross,也不拿旧 ledger 声明48当完整全集。无资产基准时
比较/对齐计数为 absent/null。报告估计器的既有默认样本数、概率参数不
构成晋级阈值;无定义与实际零严格区分。仅输出聚合报告,不新增原始
价格、逐日收益、持仓路径或私有位置。

输入仍为 source-seen development、data/oos unqualified,不证明 PIT、许可、
真实收费、完整插件或实盘采用;原研究/执行拒绝状态保持。修改代码会
更新报告已有的真实 `implementation_sha256` 及其派生 `evidence_digest`,
不得硬编码旧身份以假称完整字节不变。未开启诊断时除此必要来源身份
之外,历史报告数值/字段/行为保持;旧 RS artifact 和 trial ledger 不重写。
24 changes: 23 additions & 1 deletion scripts/run_c3_batch_a_existing_member_baseline.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,34 @@ def main(argv: Sequence[str] | None = None) -> int:
help="Original cloud staging root; historical report revalidates snapshots and the pack.")
parser.add_argument("--annual-risk-free-rate", type=float, default=None)
parser.add_argument("--annual-minimum-acceptable-return", type=float, default=None)
parser.add_argument("--include-unified-net-report", action="store_true",
help="Explicit research-only B3 sidecar with per-session outcome diagnostics; requires historical-account-report and latest QPK.")
parser.add_argument(
"--private-root",
type=Path,
default=None,
help=argparse.SUPPRESS, # legacy flag retained only to emit a clear park reason
)
args = parser.parse_args(argv)
if args.include_unified_net_report:
if not args.historical_account_report:
reason = "HISTORICAL_REPORT_OPT_IN_REQUIRED"
else:
from us_equity_strategies.research.tqqq_core_trial_journal import _unified_net_report_builder
try:
_unified_net_report_builder()
except ValueError:
reason = "UNIFIED_NET_REPORT_CAPABILITY_UNAVAILABLE"
else:
reason = None
if reason is not None:
# Refuse mode/capability errors before loading private inputs.
result = {"status": "PARKED", "reason_codes": [reason],
"research_only": True, "execution_authorized": False,
"promotion_authorized": False, "no_order": True,
"data_qualified": False, "oos_qualified": False, "batch_a_accepted": False}
print(json.dumps(result, sort_keys=True, separators=(",", ":"), allow_nan=False))
return 2
if args.private_root is not None and args.frozen_member_pack is None:
result = {
"schema_version": "qsl.c3-batch-a-existing-member-baseline-research.v1",
Expand Down Expand Up @@ -168,7 +189,8 @@ def main(argv: Sequence[str] | None = None) -> int:
args.snapshot_input_root / "tqqq_qqq",
identity_path=args.snapshot_input_root / "tqqq_qqq" / "object_identity.json"),
annual_risk_free_rate=args.annual_risk_free_rate,
annual_minimum_acceptable_return=args.annual_minimum_acceptable_return)
annual_minimum_acceptable_return=args.annual_minimum_acceptable_return,
include_unified_net_report=args.include_unified_net_report)
except (ValueError, TypeError, ArithmeticError):
# Never echo private paths, provider bodies or an arbitrary exception.
result = {"status": "PARKED", "reason_codes": ["HISTORICAL_ACCOUNT_REPORT_REFUSED"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,7 @@ def evaluate_batch_a_existing_member_baselines(
def report_historical_batch_a_baselines(
*, frozen_member_pack: Mapping[str, object], soxl_snapshot, tqqq_snapshot,
annual_risk_free_rate: float, annual_minimum_acceptable_return: float,
include_unified_net_report: bool = False,
) -> dict:
"""Opt-in source-bound historical control, retaining the old comparison.

Expand All @@ -582,7 +583,12 @@ def report_historical_batch_a_baselines(
from .batch_a_member_pack import materialize_batch_a_member_pack
from .c3_capital_path import scale_member_budgets_to_cash
from .c3_self_financing_combo_ledger import build_historical_self_financing_ledger
from .tqqq_core_trial_journal import compute_historical_research_metrics
from .tqqq_core_trial_journal import compute_historical_research_metrics, _unified_net_report_builder

if type(include_unified_net_report) is not bool:
raise ValueError("UNIFIED_NET_REPORT_OPT_IN_INVALID")
if include_unified_net_report:
_unified_net_report_builder()

if not all(isinstance(item, PriceSnapshotV2) for item in (soxl_snapshot, tqqq_snapshot)):
raise ValueError("HISTORICAL_SNAPSHOTS_REQUIRED")
Expand Down Expand Up @@ -638,7 +644,8 @@ def book_report(identity, weights, rebalances, fee):
"terminal_nav": ledger.days[-1].nav, "terminal_cash": ledger.days[-1].cash,
"metric_report": compute_historical_research_metrics(
ledger, input_binding=source_binding, annual_risk_free_rate=annual_risk_free_rate,
annual_minimum_acceptable_return=annual_minimum_acceptable_return)}
annual_minimum_acceptable_return=annual_minimum_acceptable_return,
include_unified_net_report=include_unified_net_report)}

comparisons = []
by_id = {row["baseline_id"]: row for row in legacy["c3_comparison"]["baselines"]}
Expand Down
48 changes: 46 additions & 2 deletions src/us_equity_strategies/research/tqqq_core_trial_journal.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from collections.abc import Mapping
from datetime import date, datetime, timezone
import hashlib
import importlib
import inspect
import json
import math
from pathlib import Path
Expand Down Expand Up @@ -98,6 +100,7 @@ def compute_historical_research_metrics(
ledger: ResearchDailyLedger, *, input_binding: Mapping,
annual_risk_free_rate: float, annual_minimum_acceptable_return: float,
start_session: date | None = None, end_session: date | None = None,
include_unified_net_report: bool = False,
) -> dict:
"""Report historical modeled sleeves; binding is not PIT/OOS qualification.

Expand All @@ -117,7 +120,8 @@ def compute_historical_research_metrics(
ledger, calendar_id="XNYS", contract_version="historical_research_account_metrics_v1",
annual_risk_free_rate=annual_risk_free_rate,
annual_minimum_acceptable_return=annual_minimum_acceptable_return,
start_session=start_session, end_session=end_session)
start_session=start_session, end_session=end_session,
include_unified_net_report=include_unified_net_report)
report.update({"data_qualified": False, "oos_qualified": False,
"evidence_scope": "HISTORICAL_SOURCE_SEEN_DEVELOPMENT_MODELED_SLEEVES",
"input_binding": json.loads(json.dumps(input_binding, allow_nan=False))})
Expand All @@ -131,12 +135,31 @@ def compute_historical_research_metrics(
return report


def _unified_net_report_builder():
"""Require the optional latest research capability without eager imports."""
name = "quant_platform_kit.research_stats.unified_report"
try:
module = importlib.import_module(name)
except ModuleNotFoundError as exc:
if exc.name not in {"quant_platform_kit.research_stats", name}:
raise
raise ValueError("UNIFIED_NET_REPORT_CAPABILITY_UNAVAILABLE") from None
builder = getattr(module, "build_unified_net_report", None)
if not callable(builder) or "include_outcome_diagnostics" not in inspect.signature(builder).parameters:
raise ValueError("UNIFIED_NET_REPORT_CAPABILITY_UNAVAILABLE")
return builder


def _compute_account_metrics(
ledger: ResearchDailyLedger, *, calendar_id: str, contract_version: str,
annual_risk_free_rate: float, annual_minimum_acceptable_return: float,
start_session: date | None, end_session: date | None,
include_unified_net_report: bool = False,
) -> dict:
"""One arithmetic body shared by the explicit report-only input contracts."""
if type(include_unified_net_report) is not bool:
raise ValueError("UNIFIED_NET_REPORT_OPT_IN_INVALID")
unified_builder = _unified_net_report_builder() if include_unified_net_report else None
if ledger.calendar_id != calendar_id or ledger.periods_per_year != 252:
raise ValueError("RESEARCH_BASIS_INVALID")
rates = (annual_risk_free_rate, annual_minimum_acceptable_return)
Expand Down Expand Up @@ -178,7 +201,7 @@ def _compute_account_metrics(
"ZERO_DOWNSIDE_DEVIATION" if all(
r >= annual_minimum_acceptable_return / 252 for r in returns)
else "NONFINITE_RESULT")
return {
report = {
"research_only": True, "synthetic": ledger.synthetic, "promotion_eligible": False,
"live_ready": False, "size_zero_required": True, "no_order": True,
"trial_id": ledger.trial_id, "run_id": ledger.run_id,
Expand Down Expand Up @@ -222,6 +245,27 @@ def _compute_account_metrics(
"trial_matrix_and_dependence_assumptions": "NOT_ESTABLISHED",
},
}
if unified_builder is not None:
# The existing ledger has already deducted costs. It has neither a
# matching gross counterfactual nor a complete cumulative trial ledger.
report["unified_net_report"] = unified_builder(
returns, include_outcome_diagnostics=True).to_dict()
report["unified_net_report_basis"] = {
"metric_role": "REPORT_ONLY", "return_basis": "RAW_NET",
"observation_unit": "STRATEGY_ACCOUNT_SESSION_SIMPLE_NET_RETURN",
"return_unit": "SIGNED_DECIMAL_FRACTION",
"frequency": "PER_SESSION_NOT_ANNUALIZED", "volatility_ddof": 1,
"calendar_id": calendar_id, "real_calendar_verified": False,
"start_session": start.isoformat(), "end_session": end.isoformat(),
"zero_return_sessions": "INCLUDED", "initial_nav_is_return_observation": False,
"costs_deducted_again": False, "gross_counterfactual": "NOT_SUPPLIED",
"cumulative_trial_evidence": "NOT_SUPPLIED",
"asset_benchmark": None, "benchmark_alignment_count": None,
"benchmark_dropped_count": None,
"legacy_rf_adjusted_annualized_metrics_replaced": False,
"inference_parameters_role": "ESTIMATOR_DEFAULTS_NOT_ACCEPTANCE_THRESHOLDS",
}
return report


def report_journaled_tqqq_core_metrics(
Expand Down
Loading
Loading