Strategy Object¶
A strategy subclasses BaseStrategy (stacksats/strategy_types/__init__.py) and defines:
- identity:
strategy_id,version,description - canonical contract surface:
metadata(),params(),spec() - hooks:
transform_features,build_signals - intent path:
propose_weight(...)orbuild_target_profile(...) - optional lazy profile path:
transform_features_lazy(...),build_signal_exprs(...),build_target_profile_lazy(...) - lifecycle helpers:
validate(...),backtest(...),export(...),run(...)
Built-in strategy behavior, required columns, and tuning defaults are maintained in Strategies. This page focuses on the base API contract.
BaseStrategy.spec() is the canonical public contract for a strategy. It includes:
metadata: normalized identity (strategy_id,version,description)intent_mode: the active execution path ("propose"or"profile")params: stable, deterministic strategy configuration used for provenance/idempotencyrequired_feature_sets: framework-owned feature providers needed to materializectx.features_dfrequired_feature_columns: hard-required transformed columns
Ownership model¶
Framework vs user
User code owns transforms/signals/intent over observed inputs. Framework code owns feature sourcing, as-of materialization, iteration, clipping, and invariants.
Framework-owned behavior remains sealed:
- compute kernel (
compute_weightsinBaseStrategy) - clipping and remaining-budget enforcement
- lock semantics and final invariants
Stable Params vs Runtime State¶
Class attributes remain supported for strategy author ergonomics, but runtime caches are not part of the canonical contract.
params()includes stable public config values from class attrs and instance attrs- private attrs such as
_cache,_brk_features_cache, or_brk_overlay_cacheare excluded - unsupported public values must be normalized by overriding
params()
Use spec() when you need a durable view of strategy identity and configuration.
Minimal strategy shape¶
import polars as pl
class MyStrategy(BaseStrategy):
strategy_id = "my-strategy"
version = "1.0.0"
description = "Example strategy."
def transform_features(self, ctx: StrategyContext) -> pl.DataFrame:
...
def build_signals(self, ctx: StrategyContext, features_df: pl.DataFrame) -> dict[str, pl.Series]:
...
def build_target_profile(
self,
ctx: StrategyContext,
features_df: pl.DataFrame,
signals: dict[str, pl.Series],
) -> TargetProfile:
...
def build_target_profile_lazy(
self,
ctx: StrategyLazyContext,
features_lf: pl.LazyFrame,
) -> pl.LazyFrame:
...
Optional contract helpers:
def required_feature_sets(self) -> tuple[str, ...]:
return ("core_model_features_v1",)
def required_feature_columns(self) -> tuple[str, ...]:
return ("price_vs_ma", "mvrv_zscore", "mvrv_gradient")
ctx.features_df is always observed-only. Strategy hooks do not receive rows after current_date.
StackSats also runs best-effort static causal lint checks for patterns such as direct file/network I/O, negative shifts, and centered rolling windows. These checks are heuristic and source-dependent; they are not a security sandbox or a complete leakage proof.
build_target_profile_lazy(...) is opt-in and only used for profile-mode strategies. propose_weight(...) remains eager because it depends on per-day state.
Intent Selection¶
hook_status()reports which hooks are structurally implementedintent_mode()reports which hook path will actually execute
If a strategy implements both intent hooks:
- set
intent_preference = "propose"orintent_preference = "profile"to make the active path explicit - if unset, StackSats raises a contract error during validation/load/spec access
Lifecycle methods¶
BaseStrategy includes convenience methods for common runtime flows:
validate(config=None, **kwargs): returnsValidationResultbacktest(config=None, **kwargs): returnsBacktestResultexport(config=None, **kwargs): returnsWeightTimeSeriesBatchbacktest_and_save(config=None, output_dir=..., ...): runs backtest and writes standard artifacts underoutput/<strategy_id>/<version>/<run_id>/run(...): runs validate + backtest, with optional export and optional artifact writingmetadata(): returnsStrategyMetadataparams(): returns stable strategy configspec(): returnsStrategySpecintent_mode(): returns the active intent execution moderequired_feature_sets(): returns framework-owned provider IDsrequired_feature_columns(): returns hard-required transformed columnshook_status(): returns which intent hook path is implementedvalidate_contract(): checks framework contract compliance for the strategy instance