Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
8f3b6d1
Publish current SpeedTrader AI project state from handoff ZIP
claude Sep 2, 2026
f5c0fc7
Reconcile cost_policy.py to the price- and direction-aware cost contract
claude Sep 2, 2026
a2ceab7
Test the untested fail-closed branches of the deterministic safety core
claude Sep 2, 2026
0b11b62
Add CI and pin the authoritative MQL5 reference against tampering
claude Sep 2, 2026
9615aed
Record verified transaction-cost research against the primary source …
claude Sep 2, 2026
eeceda9
Resolve the commission decision and correct the rates provenance date…
claude Sep 2, 2026
6edf046
Carry cost provenance onto the persisted decision (fixes a false audi…
claude Sep 2, 2026
a02dc04
Add the options domain: deterministic selection, sizing and cost
claude Sep 2, 2026
7e37cf7
Add the execution authorization boundary (§18)
claude Sep 2, 2026
27a5c35
Add the options execution adapter: the single broker boundary
claude Sep 2, 2026
d9229f4
Wire the options vertical slice end to end
claude Sep 3, 2026
e3085e6
Add the Alpaca MCP server as the broker transport (hackathon requirem…
claude Sep 3, 2026
ee839b5
Add order reconciliation: resolve UNKNOWN instead of guessing
claude Sep 3, 2026
569b64b
Add the adversarial AI layer: the model can only VETO, never authorize
claude Sep 3, 2026
b8c44c8
Add a zero-setup end-to-end demo
claude Sep 3, 2026
85301bc
Add submission README and one-page technical write-up
claude Sep 3, 2026
ab859fb
Cover the provider translation layer and sizing-policy validation
claude Sep 3, 2026
1307f9e
Add deterministic decision fingerprint and replay
claude Sep 3, 2026
f70c20e
Add a deterministic signal backtest with walk-forward and cost sensit…
claude Sep 3, 2026
74362a7
Add the command-centre dashboard, generated from real decision data
claude Sep 3, 2026
23ef243
Refine dashboard typography and title; keep the offline guarantee
claude Sep 3, 2026
6ba5332
Wire replay and the dashboard into the demo; document a fingerprint l…
claude Sep 3, 2026
47bbd1c
Fix a live-API defect: batch option quote requests at Alpaca's 100-sy…
claude Sep 3, 2026
3d1ded1
fix(mcp): unwrap the real two-level envelope the live server returns
claude Sep 3, 2026
2815a2e
docs: record the live-API validation and refresh the test count
claude Sep 3, 2026
9dfab18
docs: correct the stated reason for refusing vertical spreads
claude Sep 3, 2026
f964cae
test: make the mutation testing reproducible, and fix the gap it found
claude Sep 3, 2026
1f413b7
feat: run the real pipeline against the real broker, and fix what tha…
claude Sep 3, 2026
d0332f0
feat: bring your own strategy — the safety layer becomes the product
claude Sep 3, 2026
633c728
docs: add LICENSE, engineering log, and re-verify against lablab's ac…
claude Sep 3, 2026
2a16471
chore: ignore every .env variant, not just .env itself
claude Sep 3, 2026
6942196
feat: restart-safe execution, autonomous runtime, and a real command …
claude Sep 3, 2026
1894850
docs: record the dedicated hackathon account and state the P&L positi…
claude Sep 3, 2026
879cf30
fix(mutation): time-bound each mutant so a non-terminating one cannot…
claude Sep 3, 2026
88635b3
chore: drop four unused dependencies and split the MCP transport into…
claude Sep 3, 2026
ccc208c
feat(inspect): derive the causal chain of a decision, with authority …
claude Sep 3, 2026
f70a025
docs: document the inspector, evidence and audit; refresh counts
claude Sep 3, 2026
30f28cf
feat(inspect): derive the full options rationale from stored data
claude Sep 3, 2026
0d5dce0
feat(dashboard): options inspector, why/why-not cards, funnel and str…
claude Sep 3, 2026
b6a8279
test(mutation): prove the options max-loss figure cannot quietly beco…
claude Sep 3, 2026
702fd34
docs: document the options inspector and analytics honesty rules
claude Sep 3, 2026
16a2a00
Update team name in README
Sep 3, 2026
2dd0e8d
fix(dashboard): the pipeline strip must blame the layer that actually…
claude Sep 3, 2026
c46f57a
Merge remote-tracking branch 'origin/claude/speedtrader-ai-handoff-au…
claude Sep 3, 2026
e72a981
docs: concrete clone URL and a refreshed owner checklist
claude Sep 3, 2026
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
10 changes: 10 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Alpaca — PAPER ONLY. Never put live keys in this project.
ALPACA_API_KEY=
ALPACA_SECRET_KEY=
ALPACA_PAPER=true

# LLM provider (one is enough)
ANTHROPIC_API_KEY=
OPENAI_API_KEY=

SPEEDTRADER_CONFIG_DIR=configs
186 changes: 186 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
name: CI

# The deterministic core is the part of this system where a silent regression
# costs money rather than embarrassment, so it is verified on every push.
on:
push:
branches: ["**"]
pull_request:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
test:
name: tests (py${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.11", "3.12"]

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: pip

- name: Install
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"

- name: Verify the authoritative MQL5 reference is unmodified
# §47: any change to the ported-from source is an integrity issue, not a
# diff. Checked before the suite so the failure is unambiguous when the
# quant tests would also fail as a consequence.
run: |
echo "c799acaa797a4f23a8c9531c3b4f14599b73736af2151d9eeb7f42209332e8d9 docs/reference/SpeedTraderBot_v6.1.mq5" \
| sha256sum --check --strict

- name: Lint (errors only)
# Scoped to real defects — undefined names, redefinitions, syntax errors.
# Stylistic rules are deliberately not gating: a formatting opinion must
# never be able to block a correctness fix.
run: ruff check src tests scripts --select F821,F811,F823,E9

- name: Tests
run: pytest -q

mutation:
# A green suite proves the tests pass. It does not prove they would NOTICE a
# safety guard being removed — which, for this system, is the whole claim.
# Each guard is deliberately broken and the suite must go red.
name: mutation testing
runs-on: ubuntu-latest
# Belt and braces alongside the harness's own per-mutant ceiling: a mutant
# that removes a termination condition makes the suite run forever, and
# without a cap that burns a runner until GitHub's 6-hour job limit.
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip
- run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"

- name: Every safety guard must be load-bearing
# Non-zero if any mutant SURVIVES (a guard nothing tests), if a declared
# equivalent mutant turns out to be killable, or if an anchor is lost —
# the last meaning a guard was refactored and is silently no longer
# covered here.
run: python scripts/mutation_test.py

safety-invariants:
# These assertions are cheap and non-negotiable, so they run as their own
# job: if the suite above is ever narrowed, these still fail loudly.
name: safety invariants
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip
- run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"

- name: Paper trading only — never live
run: |
python - <<'PY'
import sys, yaml
cfg = yaml.safe_load(open("configs/execution_config.yaml"))
env = cfg.get("environment")
if env != "paper":
sys.exit(f"execution_config.environment must be 'paper', got {env!r}")
if cfg.get("replay_execution_enabled") is not False:
sys.exit("replay_execution_enabled must be false")
print("environment=paper, replay execution disabled")
PY

- name: Risk config must be fail-closed
run: |
python - <<'PY'
import sys, yaml
cfg = yaml.safe_load(open("configs/risk_config.yaml"))
for key in ("fail_closed", "kill_switch_enabled"):
if cfg.get(key) is not True:
sys.exit(f"risk_config.{key} must be true, got {cfg.get(key)!r}")
print("risk config is fail-closed with the kill switch enabled")
PY

- name: The autonomous runtime must start and stop cleanly
# A bounded loop that cannot be stopped is the failure mode that
# matters most for an unattended trader, so the kill switch and the
# cycle bound are exercised for real, not just unit-tested.
run: |
python - <<'PY'
import sys
sys.path.insert(0, "src")
from speedtrader.app.runtime import (
AutonomousRuntime, RuntimeLimits, RuntimeState, StopReason)

rt = AutonomousRuntime(
symbols=["SPY"], cycle_fn=lambda s, c: None,
limits=RuntimeLimits(interval_seconds=1, max_cycles=2,
require_market_open=False),
sleep_fn=lambda s: None)
health = rt.run()
assert health.state is RuntimeState.STOPPED, health.state
assert health.cycles_completed == 2, health.cycles_completed
assert health.stop_reason is StopReason.MAX_CYCLES
print("runtime start/stop OK:", health.to_record())
PY

- name: No credentials in any generated artefact
# The dashboard reads the paper account. It must render an account
# NUMBER and never a key, and must contain no script element at all.
run: |
python scripts/build_dashboard.py --out /tmp/dash.html
python - <<'PY'
import re, sys, pathlib
page = pathlib.Path("/tmp/dash.html").read_text()
if re.search(r"\b(?:AK|PK|SK)[A-Z0-9]{18,}\b", page):
sys.exit("credential-shaped literal in the generated dashboard")
for bad in ("<script", "http://", "https://", "onerror=", "javascript:"):
if bad in page:
sys.exit(f"generated dashboard contains {bad!r}")
print("dashboard is credential-free, script-free and self-contained")
PY

- name: No committed secrets
run: |
python - <<'PY'
import pathlib, re, sys
# Real Alpaca keys are long uppercase alphanumerics; the tests use
# short obvious dummies, so require length to avoid flagging those.
pattern = re.compile(r"\b(?:AK|PK|SK)[A-Z0-9]{18,}\b")
bad = []
for p in pathlib.Path(".").rglob("*"):
if not p.is_file() or ".git/" in str(p):
continue
if p.suffix.lower() in {".mq5", ".png", ".jpg", ".pdf"}:
continue
try:
text = p.read_text(encoding="utf-8", errors="ignore")
except OSError:
continue
if pattern.search(text):
bad.append(str(p))
if pathlib.Path(".env").exists():
bad.append(".env (must never be committed)")
if bad:
sys.exit("possible committed secrets: " + ", ".join(bad))
print("no credential-shaped literals found")
PY
25 changes: 25 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
__pycache__/
*.py[cod]
.venv/
venv/
.env
.env.*
!.env.example
.pytest_cache/
.ruff_cache/
data/decisions/*.jsonl
data/execution_intents.jsonl
data/STOP
data/snapshots/*
logs/
*.db
.DS_Store
data/dashboard.html

# editable-install build artefacts
*.egg-info/
build/
dist/
.coverage
.coverage.*
htmlcov/
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 SpeedTrader AI contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Loading
Loading