CME 선물 예측과 백테스트 계보 검증
코드 Machine Learning for Trading
요약
이 검증 스크립트는 모델 예측부터 포트폴리오 비중과 백테스팅까지 축소된 CME 선물 리서치 워크플로를 실행합니다. 예측값이 예상한 상품, 타임스탬프, 폴드 키를 정확히 포함하는지, 적합된 상태와 예측 파일이 기록된 다이제스트와 일치하는지, 요청을 다시 실행했을 때 같은 아티팩트가 재현되는지 확인합니다. 미리보기 출력이 공식 모집단과 후보 집합에서 분리되어 있는지도 점검합니다.
예시에서는 순위가 가장 높은 상품을 선택해 롱숏 신호를 구성하고 변동성에 반비례해 포지션 규모를 정합니다. 상품별 롤 및 만기 메타데이터가 포함된 검증 가격을 불러온 뒤, 타입이 지정된 백테스트 경로의 결과를 직접 전략 실행 결과와 비교합니다. 체결 내역과 유한한 성과 지표가 기록되었는지도 확인합니다. 이러한 점검은 재현성, 데이터 계보, 선물별 처리를 보여주지만, 전략의 수익성이나 축소된 미리보기가 전체 프로덕션 평가를 대표한다는 점을 입증하지는 않습니다.
핵심 아이디어
- 예상 집합과 상품, 타임스탬프, 폴드 키가 정확히 일치하는지 비교해 예측 적격성을 확인할 수 있습니다.
- 아티팩트 다이제스트와 재실행으로 저장된 모델 상태와 재현성을 점검할 수 있습니다.
- 미리보기 예측과 백테스트가 공식 모집단이나 후보 집합에 들어가지 않도록 차단합니다.
- 예시에서는 순위 상위 종목으로 롱숏 신호를 구성하고 역변동성 방식으로 배분합니다.
- 선물 백테스트에는 상품, 롤, 만기, 계약 포지션 메타데이터를 유지해야 합니다.
태그
전문
# prove_cme_futures_interface.py
```py
"""Run the reduced real-data proof for the CME futures research interface."""
from __future__ import annotations
import argparse
import hashlib
import json
import math
import sqlite3
from pathlib import Path
import polars as pl
from case_studies.cme_futures.research_workflow import (
load_futures_price_path,
model_request_catalog,
open_study,
publish_product_weights,
resolve_model_requests,
run_resolved_model_requests,
)
from case_studies.research import CandidateSet, OfficialPopulation
from case_studies.research.execution import run_backtests
from case_studies.utils.artifact_digest import value_digest
CASE_STUDY = "cme_futures"
def _product_keys(frame: pl.DataFrame) -> pl.DataFrame:
entity_columns = [column for column in ("symbol", "product") if column in frame.columns]
fold_columns = [column for column in ("fold", "fold_id") if column in frame.columns]
if len(entity_columns) != 1 or len(fold_columns) != 1:
raise ValueError("prediction eligibility requires one entity key and one fold key")
result = frame.select(entity_columns[0], "timestamp", fold_columns[0])
return result.rename({entity_columns[0]: "product", fold_columns[0]: "fold"})
def _sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def _fitted_state_rows(study, training_hash: str) -> list[tuple]:
root = study.storage_root("preview")
with sqlite3.connect(root / "run_log" / "registry.db") as db:
rows = db.execute(
"SELECT fold_id, fitted_state_path, fitted_state_digest, "
"prediction_shard_path, prediction_shard_digest "
"FROM candidate_fold_completions WHERE training_hash = ? ORDER BY fold_id",
(training_hash,),
).fetchall()
if not rows:
raise AssertionError("reduced model run persisted no fitted-state rows")
for _, fitted_path, fitted_digest, shard_path, shard_digest in rows:
fitted = root / fitted_path
shard = root / shard_path
assert fitted.is_file() and _sha256(fitted) == fitted_digest
assert shard.is_file() and _sha256(shard) == shard_digest
return rows
def _returns(result) -> pl.DataFrame:
path = result.root / "run_log" / "backtest" / result.hash / "daily_returns.parquet"
return pl.read_parquet(path)
def _reject_preview_population(study, *, member_kind: str, member_hash: str) -> None:
try:
OfficialPopulation.create(
study,
name=f"preview-{member_kind}-must-not-enter-official",
member_kind=member_kind,
members=[member_hash],
)
except ValueError as error:
assert "preview" in str(error) or "exploratory" in str(error)
else:
raise AssertionError(f"preview {member_kind} entered an official population")
def prove(workspace: Path) -> dict[str, object]:
study = open_study(execution_tier="preview", workspace=workspace)
request_catalog = model_request_catalog(
"linear",
labels=("fwd_ret_5d",),
config_names=("ols",),
)
preview_reductions = {"folds": [0], "max_symbols": 6}
resolved = resolve_model_requests(
study,
request_catalog,
execution_tier="preview",
preview_reductions=preview_reductions,
)[0]
assert resolved.spec["computation"]["preview_reductions"] == preview_reductions
execution = run_resolved_model_requests(study, [resolved])
run = execution.runs[0]
prediction = run.predictions[-1]
prediction_frame = prediction.load()
expected = _product_keys(resolved._context.expected_keys)
actual = _product_keys(prediction_frame)
key_columns = ["product", "timestamp", "fold"]
assert actual.height == actual.n_unique(key_columns)
assert actual.join(expected, on=key_columns, how="anti").is_empty()
assert expected.join(actual, on=key_columns, how="anti").is_empty()
assert actual.get_column("fold").unique().to_list() == [0]
assert 1 < actual.get_column("product").n_unique() <= 6
coverage = prediction.coverage()
assert coverage is not None and coverage["status"] == "complete"
assert coverage["n_expected"] == coverage["n_actual"] == actual.height
state_rows = _fitted_state_rows(study, run.training.hash)
restarted = run_resolved_model_requests(study, [resolved]).runs[0]
assert restarted.training.hash == run.training.hash
assert [item.hash for item in restarted.predictions] == [item.hash for item in run.predictions]
assert value_digest(restarted.predictions[-1].load()) == value_digest(prediction_frame)
assert _fitted_state_rows(study, restarted.training.hash) == state_rows
preview_catalog = study.predictions.table(include_preview=True)
canonical_catalog = study.predictions.table(include_preview=False)
selected = preview_catalog.filter(pl.col("prediction_hash") == prediction.hash)
assert selected.height == 1 and selected.item(0, "complete") is True
if not canonical_catalog.is_empty():
assert prediction.hash not in canonical_catalog.get_column("prediction_hash").to_list()
_reject_preview_population(study, member_kind="prediction", member_hash=prediction.hash)
products = set(actual.get_column("product"))
price_path = load_futures_price_path(
"fwd_ret_5d",
split="validation",
products=sorted(products),
)
assert set(price_path.prices.get_column("product")) == products
assert "symbol" not in price_path.prices.columns
assert price_path.audit.get_column("position").unique().to_list() == [0]
assert price_path.roll_transitions.height > 0
assert price_path.roll_transitions.get_column("roll_adjustment_factor").is_finite().all()
assert set(price_path.expiry_rules.get_column("product")) == products
signal = {"method": "equal_weight_top_k", "top_k": 2}
allocation = {"method": "inverse_vol", "vol_window": 20}
decision = publish_product_weights(
prediction,
prices=price_path.prices,
signal=signal,
allocation=allocation,
)
assert decision.spec["decision_keys"] == ["product", "timestamp"]
resolved_signal = decision.spec["parameters"]["signal"]
resolved_allocation = decision.spec["parameters"]["allocation"]
assert resolved_signal["long_short"] is True
assert resolved_allocation["long_short"] is True
assert decision.load().filter(pl.col("weight") < 0).height > 0
assert decision.load().filter(pl.col("weight") > 0).height > 0
typed = run_backtests(
study,
predictions=selected,
signal=resolved_signal,
allocation=resolved_allocation,
decision=decision,
prices=price_path.prices,
).results[0]
direct = study.strategy(
prediction=prediction,
signal=resolved_signal,
allocation=resolved_allocation,
).run(prices=price_path.prices)
assert _returns(typed).equals(_returns(direct))
typed_spec = typed.spec()
assert typed_spec["entity_contract"]["reader_key"] == "product"
assert typed_spec["decision_artifact"]["hash"] == decision.hash
assert typed_spec["decision_artifact"]["decision_keys"] == ["product", "timestamp"]
assert typed_spec["futures_market"]["roll"]["type"] == "volume"
assert typed_spec["futures_market"]["contract_position"] == 0
assert set(typed_spec["futures_market"]["expiry"]["products"]) == products
assert typed.lineage()["prediction_hash"] == prediction.hash
backtest_dir = typed.root / "run_log" / "backtest" / typed.hash
fills = pl.read_parquet(backtest_dir / "fills.parquet")
assert fills.height > 0
with sqlite3.connect(typed.root / "run_log" / "registry.db") as db:
metrics = db.execute(
"SELECT sharpe, num_trades FROM backtest_metrics WHERE backtest_hash = ?",
(typed.hash,),
).fetchone()
assert metrics is not None
sharpe, num_trades = metrics
assert math.isfinite(sharpe) and num_trades > 0
try:
CandidateSet.create(study, "preview-backtest-must-not-rank", [typed])
except ValueError as error:
assert "preview" in str(error) or "exploratory" in str(error)
else:
raise AssertionError("preview backtest entered a candidate set")
_reject_preview_population(study, member_kind="backtest", member_hash=typed.hash)
return {
"backtest_hash": typed.hash,
"eligible_rows": actual.height,
"fitted_state_rows": len(state_rows),
"num_fills": fills.height,
"num_products": len(products),
"num_trades": int(num_trades),
"prediction_hash": prediction.hash,
"roll_transitions": price_path.roll_transitions.height,
"sharpe": float(sharpe),
"training_hash": run.training.hash,
"workspace": str(study.storage_root("preview")),
}
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("workspace", type=Path)
args = parser.parse_args()
print(json.dumps(prove(args.workspace), indent=2, sort_keys=True))
if __name__ == "__main__":
main()
```출처의 라이선스에 따라 출처를 표시하고 전문을 공개합니다. 라이선스: MIT
이 요약은 원문을 바탕으로 Stratmill의 리서치 에이전트가 작성했으며, 원문을 복사한 것이 아닙니다.