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
13 changes: 6 additions & 7 deletions .github/workflows/private-comparisons.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Private Comparison Evidence
on:
workflow_call:
secrets:
VECTORBT_PRO_DEPLOY_KEY:
VECTORBT_PRO_GH_TOKEN:
required: true
workflow_dispatch:

Expand All @@ -30,14 +30,12 @@ jobs:

- name: Configure licensed source access
env:
VECTORBT_PRO_DEPLOY_KEY: ${{ secrets.VECTORBT_PRO_DEPLOY_KEY }}
run: |
install -m 700 -d "$HOME/.ssh"
printf '%s\n' "$VECTORBT_PRO_DEPLOY_KEY" > "$HOME/.ssh/id_ed25519"
chmod 600 "$HOME/.ssh/id_ed25519"
ssh-keyscan github.com >> "$HOME/.ssh/known_hosts"
GH_TOKEN: ${{ secrets.VECTORBT_PRO_GH_TOKEN }}
run: gh auth setup-git --hostname github.com

- name: Build the locked VectorBT Pro environment
env:
GH_TOKEN: ${{ secrets.VECTORBT_PRO_GH_TOKEN }}
run: >-
python validation/build_framework_env.py
--framework vectorbt_pro
Expand Down Expand Up @@ -102,6 +100,7 @@ jobs:
validation/run_lean_case_studies.py
--lean-command .venv-lean/bin/lean
--output lean-case-studies.json
--promote

- name: Retain LEAN provenance
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
Expand Down
52 changes: 50 additions & 2 deletions tests/contracts/test_framework_environment_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def test_definition_rejects_wrong_private_commit(
project.mkdir()
(project / "uv.lock").write_text("wrong-commit", encoding="utf-8")
(project / "pyproject.toml").write_text(
'[project]\ndependencies = ["vectorbtpro @ git+ssh://example.invalid/repo@wrong"]\n',
'[project]\ndependencies = ["vectorbtpro @ git+https://example.invalid/repo@wrong"]\n',
encoding="utf-8",
)
monkeypatch.setattr(build_framework_env, "ENVIRONMENTS_DIR", tmp_path)
Expand All @@ -97,6 +97,28 @@ def test_definition_rejects_wrong_private_commit(
assert any("artifact identity" in failure for failure in failures)


def test_definition_rejects_private_ssh_source(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
targets: dict[str, FrameworkTarget],
) -> None:
target = targets["vectorbt_pro"]
project = tmp_path / "vectorbt_pro"
project.mkdir()
(project / "uv.lock").write_text(target.immutable_id.removeprefix("git:"), encoding="utf-8")
(project / "pyproject.toml").write_text(
"[project]\ndependencies = ["
f'"vectorbtpro @ git+ssh://git@github.com/polakowo/vectorbt.pro.git@'
f'{target.source_commit}"\n]\n',
encoding="utf-8",
)
monkeypatch.setattr(build_framework_env, "ENVIRONMENTS_DIR", tmp_path)

failures = build_framework_env.definition_failures("vectorbt_pro", target)

assert "VectorBT Pro environment must use GitHub CLI authenticated HTTPS" in failures


def test_private_build_reports_missing_licensed_access(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
Expand All @@ -109,7 +131,7 @@ def test_private_build_reports_missing_licensed_access(
lambda *args, **_kwargs: (_ for _ in ()).throw(subprocess.CalledProcessError(1, args[0])),
)

with pytest.raises(RuntimeError, match="Authorized SSH access"):
with pytest.raises(RuntimeError, match="GitHub CLI access"):
build_framework_env.build_environment(
"vectorbt_pro", targets["vectorbt_pro"], root=tmp_path
)
Expand Down Expand Up @@ -149,6 +171,32 @@ def run(command: list[str], **_kwargs):
assert commands == [["docker", "buildx", "imagetools", "inspect", target.artifact]]


def test_lean_build_pulls_the_verified_engine_image(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
targets: dict[str, FrameworkTarget],
) -> None:
target = targets["lean"]
commands: list[list[str]] = []

def run(command: list[str], **_kwargs):
commands.append(command)
return subprocess.CompletedProcess(command, 0)

monkeypatch.setattr(build_framework_env.subprocess, "run", run)
monkeypatch.setattr(build_framework_env, "definition_failures", lambda *_: [])
monkeypatch.setattr(
build_framework_env,
"verify_environment",
lambda *_args, **_kwargs: {"framework": "lean"},
)

evidence = build_framework_env.build_environment("lean", target, root=tmp_path)

assert evidence == {"framework": "lean"}
assert commands[-1] == ["docker", "pull", target.artifact]


def test_private_environment_rejects_wrong_installed_commit(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
Expand Down
10 changes: 9 additions & 1 deletion tests/contracts/test_release_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,16 @@ def test_private_comparison_workflow_pins_and_retains_evidence() -> None:
workflow = _workflow("private-comparisons.yml")
jobs = workflow["jobs"]

assert workflow["on"]["workflow_call"]["secrets"]["VECTORBT_PRO_DEPLOY_KEY"]["required"]
assert workflow["on"]["workflow_call"]["secrets"]["VECTORBT_PRO_GH_TOKEN"]["required"]
pro_commands = "\n".join(step.get("run", "") for step in jobs["vectorbt-pro"]["steps"])
assert "gh auth setup-git" in pro_commands
assert "id_ed25519" not in pro_commands
authenticated_steps = [
step
for step in jobs["vectorbt-pro"]["steps"]
if step.get("env", {}).get("GH_TOKEN") == "${{ secrets.VECTORBT_PRO_GH_TOKEN }}"
]
assert len(authenticated_steps) == 2
assert "validation/build_framework_env.py" in pro_commands
assert "validation/native/vectorbt_behavior.py" in pro_commands
assert "validation/run_all_correctness.py" in pro_commands
Expand Down
2 changes: 1 addition & 1 deletion validation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ not used for the published real-strategy ratios above.
# Build and verify all public targets from their committed uv locks
python validation/build_framework_env.py --all-public

# Licensed VectorBT Pro requires authorized SSH access to the frozen source commit
# Licensed VectorBT Pro requires GitHub CLI access to the frozen source commit
python validation/build_framework_env.py --framework vectorbt_pro

# Verify the locked LEAN CLI and immutable engine image
Expand Down
15 changes: 12 additions & 3 deletions validation/build_framework_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ def definition_failures(framework: str, target: FrameworkTarget) -> list[str]:
if len(matching) != 1:
failures.append(f"Environment must define one {target.package} dependency: {framework}")
elif framework == "vectorbt_pro":
if not matching[0].startswith(
"vectorbtpro @ git+https://github.com/polakowo/vectorbt.pro.git@"
):
failures.append("VectorBT Pro environment must use GitHub CLI authenticated HTTPS")
if target.source_commit is None or f"@{target.source_commit}" not in matching[0]:
failures.append("VectorBT Pro environment does not pin the manifest commit")
else:
Expand Down Expand Up @@ -198,11 +202,16 @@ def build_environment(
except subprocess.CalledProcessError as error:
if framework == "vectorbt_pro":
raise RuntimeError(
"VectorBT Pro is unavailable. Authorized SSH access to the licensed source is "
"required; no credentials are stored in this repository."
"VectorBT Pro is unavailable. Authorized GitHub CLI access to the licensed "
"source is required; no credentials are stored in this repository."
) from error
raise
return verify_environment(framework, target, root=root)
evidence = verify_environment(framework, target, root=root)
if framework == "lean":
if target.artifact is None:
raise ValueError("LEAN target lacks an immutable engine image")
subprocess.run(["docker", "pull", target.artifact], check=True)
return evidence


def main() -> int:
Expand Down
2 changes: 1 addition & 1 deletion validation/environments/vectorbt_pro/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version = "0.0.0"
requires-python = ">=3.12,<3.13"
dependencies = [
"ml4t-backtest",
"vectorbtpro @ git+ssh://git@github.com/polakowo/vectorbt.pro.git@6e18cf0aa37849cfc20848f40f1d26ecfdc771b4",
"vectorbtpro @ git+https://github.com/polakowo/vectorbt.pro.git@6e18cf0aa37849cfc20848f40f1d26ecfdc771b4",
]

[tool.uv]
Expand Down
4 changes: 2 additions & 2 deletions validation/environments/vectorbt_pro/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 7 additions & 7 deletions validation/lean/case_study_evidence.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"case_study_support_files": {
"project-config.json": "6f53654afabb96e23944db61235b83848ab24fcac2ecaf9597382e0671fef01f"
"project-config.json": "2c71309922dc0305269869776ba945432379a2c45d5327550708953e6901b0c6"
},
"cases": [
{
Expand Down Expand Up @@ -57,8 +57,8 @@
"validation/lean/workspace/chapter16_etfs/ml4t_daily_equity.csv": "20bc4a28ad04fdc458bd8f5031fbf976623caddd819da70f2e51ddb45dea6609",
"validation/lean/workspace/chapter16_etfs/ml4t_order_events.csv.xz": "63535bec501d36e4f1eae501917e0000e636c03a19d637b05cdda1428bc80421"
},
"runtime_seconds": 12.663519160356373,
"summary_sha256": "8a50b99a15d26baa87845c8ed2a517f40747b5e352cc6e739d208cba4323b777"
"runtime_seconds": 21.884960932000013,
"summary_sha256": "677166e5de08ea5de1795d1b4d4ac440b5e2cf1e5c4f26f5139bbd0ee2682240"
},
{
"case": "chapter16_sp500_equity_option_analytics",
Expand Down Expand Up @@ -115,8 +115,8 @@
"validation/lean/workspace/chapter16_sp500_equity_option_analytics/ml4t_daily_equity.csv": "83a98f50053b12dd4baa37484b0b268cb8ec83ebdf633ee2e6ee790db0bb73ad",
"validation/lean/workspace/chapter16_sp500_equity_option_analytics/ml4t_order_events.csv": "334cd463affb3d23f074882fe1d8131861ec67ca5203fbdfb86cae4d8da64917"
},
"runtime_seconds": 8.238332036882639,
"summary_sha256": "df58a18960ff30d5cc5cf51ee7072dd2b557f46ad7e0ced3d74aaab061eb3e13"
"runtime_seconds": 14.92322724799999,
"summary_sha256": "035984926246be0c41275ba43df96e2e04d06ef105bfb6055d35909f525e43dd"
},
{
"case": "chapter16_us_equities_panel",
Expand Down Expand Up @@ -201,8 +201,8 @@
"validation/lean/workspace/chapter16_us_equities_panel/ml4t_order_events.csv.part01.xz": "9aea6ff30791dc06cd9b917e6f0c639d1b6f17883012b9c5190d7be6a899d980",
"validation/lean/workspace/chapter16_us_equities_panel/ml4t_order_events.csv.part02.xz": "915bef2036b7d49535eb81a458a1af891f8f15c249bb413831771ea52e982a26"
},
"runtime_seconds": 23.13902745116502,
"summary_sha256": "4d10636f183790250e6feb7018554c09140c1603dd8c09c7e93029679543397b"
"runtime_seconds": 42.17692052699999,
"summary_sha256": "bd8582a9316684d73a0ed55d4ed16ed2e7a05be5b94881607f022a5867f3ecbd"
}
],
"cli_observed": "lean 1.0.228",
Expand Down
9 changes: 7 additions & 2 deletions validation/lean/case_study_support/project-config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
{
"algorithm-language": "Python",
"parameters": {},
"description": "ml4t case-study lean parity",
"local-id": 0
"docker": {
"environment": {
"PYTHONPYCACHEPREFIX": "/tmp/lean-pycache"
}
},
"local-id": 0,
"parameters": {}
}
2 changes: 1 addition & 1 deletion validation/native/evidence/lean-18001.json
Original file line number Diff line number Diff line change
Expand Up @@ -2254,7 +2254,7 @@
"security": "Equity USA, daily, adjusted normalization, leverage 2"
},
"oracle_files": {
"config.json": "5b7ec7bf073127d026e6086ec18771dcb700375570ba51def97739d85009351c",
"config.json": "272e56b7a924ac9fd1dfe01e70be10e2a7c58b1b9328576e7a9c0b6e3144dbb3",
"main.py": "04aab940df029bf37e7aa1a9d3d8ab0996fce0bb3a10fa4dd4e15ca685e83e19"
},
"oracle_sha256": "1675f062ce54f29727d2bf7d17afc5c9db4e439e9f921e270feec49cde065d66",
Expand Down
5 changes: 5 additions & 0 deletions validation/native/lean_project/config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
{
"algorithm-language": "Python",
"description": "ML4T native LEAN behavior oracle",
"docker": {
"environment": {
"PYTHONPYCACHEPREFIX": "/tmp/lean-pycache"
}
},
"local-id": 771804201,
"parameters": {}
}