Quick Start

This guide shows the minimum to load data, create strategy, run backtesting, and inspect the results.

Install

pip install stocksimpy

Minimal working example (copy & run)

from stocksimpy import *

# Load ~1 year of data
data = StockData.from_yfinance(["AAPL"], days_before=365)

# Use a built-in fixed-size strategy (RSI momentum)
strategy = Strategy.rsi_momentum_fixed()

# Create a backtester instance
bt = Backtester(symbol="AAPL", data=data, strategy=strategy)

# Run the backtest (fixed-size strategies use run_backtest_fixed)
bt.run_backtest_fixed()

# Performance summary
perf = Performance(bt)
print(perf.generate_risk_report())

# Visualization
viz = Visualize(bt)
figure = viz.visualize_backtest()
figure.show()

This gives you real trades, P/L values, risk stats, and a basic equity curve in seconds.

Writing your own strategy

Stocksimpy supports two strategy styles

Fixed strategies (simplest)

  • Same trade size on every buy/sell

  • Strategy sees past data only

  • Must return one of: “buy”, “sell”, “hold”

Minimal custom fixed strategy example:

def sma_crossover_fixed(data):
    close = data["Close"]
    short = close.rolling(20).mean()
    long = close.rolling(50).mean()

    if short.iloc[-1] > long.iloc[-1]:
        return "buy"
    elif short.iloc[-1] < long.iloc[-1]:
        return "sell"
    else:
        return "hold"

You can either calculate your own indicator results or use built-in functions to calculate them Here is an alternative to the previous code:

# This code adds the indicators
# Code this part before running your bactesting
data.add_indicator(calculate_sma, "close", 20)
data.add_indicator(calculate_sma, "close", 50)

# Then your strategy becomes

def sma_crossover_fixed(data):

    if data["sma_20"].iloc[-1] > data["sma_50"].iloc[-1]:
        return "buy"
    elif data["sma_20"].iloc[-1] < data["sma_50"].iloc[-1]:
        return "sell"
    else:
        return "hold"

Usage:

bt = Backtester("AAPL", data, sma_crossover_fixed)
bt.run_backtest_fixed()

Dynamic strategies (more control)

  • Trade size can change per trade

  • Strategy receives past data + current holdings

  • Must return (signal, trade_amount)

    • signal in “buy” | “sell” | “hold”

    • trade_amount is an integer (shares)

  • Minimal dynamic strategy example

Minimal custom dynamic strategy

# Add the following for built-in rsi calculation
data.add_indicator(calculate_rsi, "close", 14)

def rsi_dynamic(data, holdings):
    latest = data["rsi_14"].iloc[-1]

    if latest < 30:
        return "buy", 10      # accumulate
    elif latest > 70 and holdings > 0:
        return "sell", holdings  # exit position
    else:
        return "hold", 0

Usage:

bt = Backtester("AAPL", data, rsi_dynamic)
bt.run_backtest_dynamic()

Useful shortcuts

  • Built-in strategies live in Strategy

  • Built-in indicators live in Indicators

    • You can implement them directly through add_indicator() from StockData

  • Fixed: run_backtest_fixed()

  • Dynamic: run_backtest_dynamic()

  • Visuals: Visualize(bt).visualize_backtest()

  • Reports: Performance(bt).generate_risk_report()

When to use fixed vs dynamic

Feature

Fixed

Dynamic

Trade size

constant

variable

Access to current holdings

no

yes

Return value

“buy/sell/hold”

(“buy/sell/hold”, amount)

Best for

simple rules

scaling in/out, position sizing

If you are unsure start with fixed.

See the rest of the docs for deeper guide and more advanced features