crucible-stack
A framework for defining, searching, sizing and deploying trading strategies.
It contains no strategies, and that is the point.
crucible answers one question: is this edge real,
or did I find it by looking hard enough? crucible-stack is the machinery around that
verdict, the parts every systematic strategy needs and nobody wants to write twice:
| Package | What it does | Seam |
|---|---|---|
framework |
how you define a strategy: the RulesStrategy interface, the params/config schema, the strategy registry, the Monte Carlo engine |
n/a |
engine |
the rules simulator and a pluggable exit/stop registry | n/a |
optimize |
how you search a parameter space honestly: the trial matrix, the sweep, and a selection step that prices in how many variants you actually tried | 1 |
capital |
how you size an edge into an account: R-multiples to a currency equity curve, with bootstrap bands | 3 |
orchestrate |
how you deploy it: a gate, a drift monitor, a ledger, and triggers that make re-optimization a standing process rather than a memory | 4 |
Why the strategies are missing
The split is deliberate. A strategy is somebody's edge and may be worth keeping private. The framework around it is not an edge, it is plumbing, and plumbing kept private is just plumbing you maintain alone.
So every place a strategy would go is a registry that ships empty:
from crucible_stack.framework import register_strategy, RulesStrategy
from crucible_stack.engine.exits import register_exit, ExitRule
@register_strategy("my_gold_trend") # lives in YOUR repo, public or not
class GoldTrend(RulesStrategy):
...
crucible-stack never imports your code. Your code imports it. That direction is enforced
by a test (tests/test_boundaries.py), not by good intentions, and the test is
mutation-checked so that it fails when the boundary actually breaks.
The idea worth stealing
Most backtesting stacks stop at a backtest. This one is shaped around two ideas that are easy to state and are usually skipped:
1. The search is part of the result. If you try 400 variants and report the best one's
Sharpe, you have reported the maximum of 400 draws, not an edge. optimize keeps a
SearchSpaceLog of how many variants were genuinely tried and feeds that count into the
correction (deflated Sharpe, PBO, White's Reality Check, Hansen's SPA). The honest
denominator is the whole point, and it is the number everybody rounds down.
2. A deployed strategy is not a finished one. orchestrate treats deployment as a loop
that has to keep re-earning its place: a gate that fails closed (no verdict, no
promotion), a drift monitor that compares the live book against the envelope frozen at
promotion rather than one recomputed today, and a ledger that remembers what was promoted
and when. See ADR-0003 for why each of those is shaped the way it is, and ADR-0004 for why
this repo exists at all.
Install
pip install -e . # requires Python 3.11+, and crucible >= 0.3.0
pytest -q
If you install this editable, read this first
The natural way to work on a strategy is to install both this package and your own
repo editable, side by side. That is a good setup and it has one property worth
stating plainly, because it causes bugs that look like anything but their cause: an
editable install is the working tree. There is no copy. import crucible_stack
reads the checkout directly, in whatever state it is in at that moment.
Three consequences:
A git operation here changes your strategy's behaviour, with no change to your
strategy. Switch branches, stash, or rebase in this repo and your next import
picks it up. A test that passes, then fails against an untouched strategy tree, is
usually this rather than a flake.
Version metadata is written at install time and never refreshed. pip list will
report whatever pyproject.toml said when you installed, not what the code is now. A
constraint like crucible-stack>=0.1.0 is satisfied by a checkout that could be
anywhere from that release to unreleased main. Do not read a version pin as evidence
that an API is present. Check the signature, or write a test that does. This package
does exactly that for its own dependency in tests/test_crucible_compat.py, because
the crucible API it needs shipped after the tagged release it nominally requires.
Do not declare a dependency by URL to work around a stale release. It is tempting
to pin crucible-stack @ git+https://... when you need something newer than the last
tag. It breaks any consumer that installs the same package from a local editable
checkout (pip cannot reconcile a URL requirement with a local one) and it blocks PyPI
upload. Pin normally and enforce the API with a test.
Status
Early. Extracted from a working private strategy repo rather than designed in the abstract, which shows in both directions: the seams are load-bearing and have been used in anger, and the naming still carries some of its origin.
While 0.x, breaking changes may land in a minor release, but never silently, and
removals go through deprecation. What counts as public, what is deliberately excluded, and
what changes at 1.0 are all in docs/api-stability.md. The surface
it describes is pinned by tests/test_public_api.py, so the policy is checked rather than
promised.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file crucible_stack-0.2.0.tar.gz.
File metadata
- Download URL: crucible_stack-0.2.0.tar.gz
- Upload date:
- Size: 100.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2035464e022d4be6fce56b6a545555d77425a26fd257fb9cdf15e723a73533de
|
|
| MD5 |
f6722c14a28324d82db7ee660f9119e1
|
|
| BLAKE2b-256 |
2a72f92fbe10cdd04704a07a2a390cd9dec5153fb99ea4bf08b7907bcd1f87b3
|
Provenance
The following attestation bundles were made for crucible_stack-0.2.0.tar.gz:
Publisher:
release.yml on mspinola/crucible-stack
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
crucible_stack-0.2.0.tar.gz -
Subject digest:
2035464e022d4be6fce56b6a545555d77425a26fd257fb9cdf15e723a73533de - Sigstore transparency entry: 2332892189
- Sigstore integration time:
-
Permalink:
mspinola/crucible-stack@e08d80a7e44821e22a8ad5087b04771cb8efdcc4 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/mspinola
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e08d80a7e44821e22a8ad5087b04771cb8efdcc4 -
Trigger Event:
push
-
Statement type:
File details
Details for the file crucible_stack-0.2.0-py3-none-any.whl.
File metadata
- Download URL: crucible_stack-0.2.0-py3-none-any.whl
- Upload date:
- Size: 72.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
39d2939018f75ad42be15d2457ebc9dc77cb21cab4ee7be5f2ee197c60bb2749
|
|
| MD5 |
91c1c00de3734f70f5979386b4cb8367
|
|
| BLAKE2b-256 |
c2baaaee4dcd84b9bba8da8cc29963fa310fca97686f64ba7b911931e10352e8
|
Provenance
The following attestation bundles were made for crucible_stack-0.2.0-py3-none-any.whl:
Publisher:
release.yml on mspinola/crucible-stack
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
crucible_stack-0.2.0-py3-none-any.whl -
Subject digest:
39d2939018f75ad42be15d2457ebc9dc77cb21cab4ee7be5f2ee197c60bb2749 - Sigstore transparency entry: 2332892195
- Sigstore integration time:
-
Permalink:
mspinola/crucible-stack@e08d80a7e44821e22a8ad5087b04771cb8efdcc4 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/mspinola
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e08d80a7e44821e22a8ad5087b04771cb8efdcc4 -
Trigger Event:
push
-
Statement type: