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
9 changes: 8 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,9 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15
env:
QPK_UNIFIED_NET_REPORT_SHA: b23511cbb28b72743e35512ef4b3c73f1cd43c23
QPK_UNIFIED_NET_REPORT_SHA: b6b51d5c32594c47cc1b3ebc0c1ed226e9db887f
UES_REQUIRE_UNIFIED_NET_REPORT: "1"
UES_REQUIRE_NET_PATH_DIAGNOSTICS: "1"
steps:
- name: Checkout
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
Expand All @@ -174,11 +175,15 @@ jobs:
import inspect, json, os
from importlib.metadata import distribution
from quant_platform_kit.research_stats.unified_report import build_unified_net_report
from quant_platform_kit.research_stats.net_path_diagnostics import build_net_path_diagnostics
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
path = build_net_path_diagnostics([-.5, 0.0, 1.0])
assert path.metric("longest_recovered_drawdown_duration_observations").value == 3
assert path.no_order is True and path.live_ready is False
print("Required latest unified/outcome report capability and immutable source verified.")
PY

Expand All @@ -194,6 +199,8 @@ jobs:
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 any("net_path_actual_cli_31_books" in c.attrib["name"] for c in cases)
assert any("net_path_hand_math_and_censoring" 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
28 changes: 28 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 @@ -167,3 +167,31 @@ DSR 分别保留 `UNCOMPUTABLE/missing_gross_returns` 和
更新报告已有的真实 `implementation_sha256` 及其派生 `evidence_digest`,
不得硬编码旧身份以假称完整字节不变。未开启诊断时除此必要来源身份
之外,历史报告数值/字段/行为保持;旧 RS artifact 和 trial ledger 不重写。

### 同窗口路径观察诊断(独立显式选择)

同一历史 CLI 可额外选择 `--include-net-path-diagnostics`,要求
`--historical-account-report`;三个 Python 报告入口相应参数
`include_net_path_diagnostics=False`。不请求时不导入新能力、不增加字段。
显式请求而旧 QPK 不具备能力时,在读取私有 pack/snapshot 前返回
`PARKED/NET_PATH_DIAGNOSTICS_CAPABILITY_UNAVAILABLE`,不会仅返回旧报告。
该参数与 `--include-unified-net-report` 独立,可以同时选择;原十二项不变。

每本现有报告附 `net_path_diagnostics` 和同窗口 basis,直接使用该本已验证
ledger 的净简单收益,不再扣费用、不增加初始 NAV 收益观察、不改变
路径、参数、费用、排序或账本。四项是最长 underwater 观察段、最长
已完成回撤恢复时长、窗口末 underwater 观察段和最长严格负净收益观察段。
单位是 return observations,不能称作日历钟点、逐笔交易连败或未来恢复预测。
零收益在 underwater 时延续,同时中断严格负收益连亏;恢复时长包含
恢复观察。无已恢复样本为 null;末段未恢复显式 right-censored。

QPK 以校验后所供浮点收益的精确相对财富乘积判断峰值,不使用 epsilon
把微小回撤圆整为恢复。这里的 NAV=1 是窗口归一化起点而非账户余额;
此前峰值/连亏与完整日历均未证明,`window_only/prior_history=unknown`。
截短窗口不证明原回撤恢复,也不证明账户全历史最长连亏。原报告的
REPORT_ONLY、DEVELOPMENT_ONLY、no_order、data/oos unqualified 及晋级拒绝保持。

既有 research-unified-net-consumer CI 在自身隔离环境绑定不可变 QPK 新能力,
同时强制旧十二项和新路径报告实际 CLI/手算用例、零 skip;生产依赖 pin
与其他两个 research overlays 保留。验收只使用已有生成协议夹具,不运行
私有 cloud workflow、不新增策略试验、PIT/OOS 或生产采用声明。
18 changes: 13 additions & 5 deletions scripts/run_c3_batch_a_existing_member_baseline.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,22 +123,29 @@ def main(argv: Sequence[str] | None = None) -> int:
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("--include-net-path-diagnostics", action="store_true",
help="Opt-in window-local net-return path counts; requires historical-account-report and the research capability.")
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:
for requested, builder_name, unavailable_reason in (
(args.include_unified_net_report, "_unified_net_report_builder", "UNIFIED_NET_REPORT_CAPABILITY_UNAVAILABLE"),
(args.include_net_path_diagnostics, "_net_path_diagnostics_builder", "NET_PATH_DIAGNOSTICS_CAPABILITY_UNAVAILABLE"),
):
if not requested:
continue
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
from us_equity_strategies.research import tqqq_core_trial_journal as journal
try:
_unified_net_report_builder()
getattr(journal, builder_name)()
except ValueError:
reason = "UNIFIED_NET_REPORT_CAPABILITY_UNAVAILABLE"
reason = unavailable_reason
else:
reason = None
if reason is not None:
Expand Down Expand Up @@ -190,7 +197,8 @@ def main(argv: Sequence[str] | None = None) -> int:
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,
include_unified_net_report=args.include_unified_net_report)
include_unified_net_report=args.include_unified_net_report,
include_net_path_diagnostics=args.include_net_path_diagnostics)
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 @@ -571,6 +571,7 @@ 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,
include_net_path_diagnostics: bool = False,
) -> dict:
"""Opt-in source-bound historical control, retaining the old comparison.

Expand All @@ -583,12 +584,19 @@ 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, _unified_net_report_builder
from .tqqq_core_trial_journal import (
compute_historical_research_metrics, _unified_net_report_builder,
_net_path_diagnostics_builder,
)

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

if not all(isinstance(item, PriceSnapshotV2) for item in (soxl_snapshot, tqqq_snapshot)):
raise ValueError("HISTORICAL_SNAPSHOTS_REQUIRED")
Expand Down Expand Up @@ -645,7 +653,8 @@ def book_report(identity, weights, rebalances, fee):
"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,
include_unified_net_report=include_unified_net_report)}
include_unified_net_report=include_unified_net_report,
include_net_path_diagnostics=include_net_path_diagnostics)}

comparisons = []
by_id = {row["baseline_id"]: row for row in legacy["c3_comparison"]["baselines"]}
Expand Down
36 changes: 35 additions & 1 deletion src/us_equity_strategies/research/tqqq_core_trial_journal.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ def compute_historical_research_metrics(
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,
include_net_path_diagnostics: bool = False,
) -> dict:
"""Report historical modeled sleeves; binding is not PIT/OOS qualification.

Expand All @@ -121,7 +122,8 @@ def compute_historical_research_metrics(
annual_risk_free_rate=annual_risk_free_rate,
annual_minimum_acceptable_return=annual_minimum_acceptable_return,
start_session=start_session, end_session=end_session,
include_unified_net_report=include_unified_net_report)
include_unified_net_report=include_unified_net_report,
include_net_path_diagnostics=include_net_path_diagnostics)
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 Down Expand Up @@ -150,16 +152,35 @@ def _unified_net_report_builder():
return builder


def _net_path_diagnostics_builder():
"""Load the optional pure research capability only after explicit opt-in."""
name = "quant_platform_kit.research_stats.net_path_diagnostics"
try:
module = importlib.import_module(name)
except ModuleNotFoundError as exc:
if exc.name not in {"quant_platform_kit.research_stats", name}:
raise
raise ValueError("NET_PATH_DIAGNOSTICS_CAPABILITY_UNAVAILABLE") from None
builder = getattr(module, "build_net_path_diagnostics", None)
if not callable(builder):
raise ValueError("NET_PATH_DIAGNOSTICS_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,
include_net_path_diagnostics: 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")
if type(include_net_path_diagnostics) is not bool:
raise ValueError("NET_PATH_DIAGNOSTICS_OPT_IN_INVALID")
unified_builder = _unified_net_report_builder() if include_unified_net_report else None
path_builder = _net_path_diagnostics_builder() if include_net_path_diagnostics 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 @@ -265,6 +286,19 @@ def _compute_account_metrics(
"legacy_rf_adjusted_annualized_metrics_replaced": False,
"inference_parameters_role": "ESTIMATOR_DEFAULTS_NOT_ACCEPTANCE_THRESHOLDS",
}
if path_builder is not None:
report["net_path_diagnostics"] = path_builder(returns).to_dict()
report["net_path_diagnostics_basis"] = {
"metric_role": "REPORT_ONLY", "return_basis": "RAW_NET",
"observation_unit": "STRATEGY_ACCOUNT_SESSION_SIMPLE_NET_RETURN",
"frequency": "RETURN_OBSERVATIONS_NOT_CALENDAR_TIME_OR_TRADES",
"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,
"initial_nav": "WINDOW_NORMALIZED_NOT_ACCOUNT_BALANCE",
"costs_deducted_again": False, "prior_history": "UNKNOWN",
"legacy_metrics_replaced": False,
}
return report


Expand Down
Loading
Loading