Skip to content

hypertrial/stacksats

StackSats

Stacking Sats Logo

PyPI version Python versions Package Check License: MIT

StackSats, developed by Hypertrial, is a Python package for strategy-first Bitcoin DCA ("stacking sats") research and execution.

Learn more at www.stackingsats.org.

Start Here

Start with the docs at docs/framework.md. It is the canonical framework contract and the best first read before using StackSats.

For the full docs index, see docs/index.md. Hosted docs site: https://hypertrial.github.io/stacksats/.

Framework Principles

  • The framework owns budget math, iteration, feasibility clipping, and lock semantics.
  • Users own features, signals, hyperparameters, and daily intent.
  • Strategy hooks support either day-level intent (propose_weight(state)) or batch intent (build_target_profile(...)).
  • The same sealed allocation kernel runs in local, backtest, and production.

See docs/framework.md for the canonical contract.

Installation

pip install stacksats

For local development:

pip install -e .
pip install -r requirements-dev.txt

Optional deploy extras:

pip install "stacksats[deploy]"

Quick Start

Create my_strategy.py:

import pandas as pd

from stacksats import BaseStrategy, StrategyContext, TargetProfile


class MyStrategy(BaseStrategy):
    strategy_id = "my-strategy"
    version = "1.0.0"
    description = "Example user strategy."

    def transform_features(self, ctx: StrategyContext) -> pd.DataFrame:
        return ctx.features_df.loc[ctx.start_date : ctx.end_date].copy()

    def build_signals(
        self, ctx: StrategyContext, features_df: pd.DataFrame
    ) -> dict[str, pd.Series]:
        del ctx
        value_signal = -features_df["mvrv_zscore"].clip(-4, 4)
        trend_signal = -features_df["price_vs_ma"].clip(-1, 1)
        return {"value": value_signal, "trend": trend_signal}

    def build_target_profile(
        self,
        ctx: StrategyContext,
        features_df: pd.DataFrame,
        signals: dict[str, pd.Series],
    ) -> TargetProfile:
        del ctx, features_df
        preference = (0.7 * signals["value"]) + (0.3 * signals["trend"])
        return TargetProfile(values=preference, mode="preference")

    # Optional alternative hook:
    # def propose_weight(self, state) -> float:
    #     return state.uniform_weight


if __name__ == "__main__":
    strategy = MyStrategy()
    validation = strategy.validate()
    print(validation.summary())
    result = strategy.backtest()
    print(result.summary())
    result.plot(output_dir="output")
    result.to_json("output/backtest_result.json")

Run it:

python my_strategy.py

Strategy Lifecycle CLI

stacksats strategy validate --strategy my_strategy.py:MyStrategy
stacksats strategy backtest --strategy my_strategy.py:MyStrategy --output-dir output
stacksats strategy export --strategy my_strategy.py:MyStrategy --output-dir output

Artifacts are written to:

output/<strategy_id>/<version>/<run_id>/

Public API

Top-level exports:

  • BaseStrategy, StrategyContext, DayState, TargetProfile
  • BacktestConfig, ValidationConfig, ExportConfig
  • StrategyArtifactSet
  • StrategyTimeSeries, StrategyTimeSeriesBatch
  • BacktestResult, ValidationResult
  • load_strategy(), load_data(), precompute_features()
  • MVRVStrategy

Development

pytest tests/ -v
ruff check .
bash scripts/check_docs_refs.sh

For command examples using the packaged strategy template, see docs/commands.md.