API Reference

class stocksimpy.core.stock_data.StockData(df: DataFrame | None = None)[source]

Bases: object

Container and validator for stock market time-series data.

Loads, validates, and exports OHLCV data with DatetimeIndex. Supports CSV, Excel, SQL, JSON, DataFrames, dictionaries, and yfinance. All loaders validate required columns (Open, High, Low, Close, Volume) and datetime indexing.

Parameters:

df (pandas.DataFrame, optional) – Stock data with DatetimeIndex and OHLCV columns. If None, creates an empty instance. Default is None.

df

Validated stock data with DatetimeIndex and columns ‘Open’, ‘High’, ‘Low’, ‘Close’, ‘Volume’. Multi-level column indexing is used for multi-ticker data.

Type:

pandas.DataFrame

Examples

>>> data = StockData.from_yfinance(["AAPL"], days_before=365)  
>>> data.df.head()  
add_indicator(indicator: str | Callable[[...], dict[str, Series]], *args: Any, base_col: str = 'Close', symbol: str = '', overwrite: bool = False, **kwargs: Any) None[source]

Add one or more technical indicators to a stock’s data.

This method applies an indicator function to a specified base column (e.g., “close”) for a given stock symbol in a MultiIndex DataFrame. The indicator function must return a dictionary mapping indicator names to pandas Series. Each resulting indicator is inserted as a new column under the corresponding stock symbol in the DataFrame.

You can use Indicators module from stocksimpy to generate these indicators quickly.

Parameters:
  • indicator (str or callable) – Either the name of a built-in indicator (e.g., “sma”, “rsi”) or a custom function that accepts a pandas Series and returns a dict of {str: pandas.Series}.

  • base_col (str, default "Close") – Base data column to apply the indicator to.

  • symbol (str, default "") – Stock symbol identifying the top-level column in the MultiIndex. Leave empty (“”) if want to apply to all the symbols

  • overwrite (bool, default False) – Whether to overwrite existing indicator columns.

  • *args – Additional arguments passed directly to the indicator function.

  • **kwargs – Additional arguments passed directly to the indicator function.

Returns:

The method mutates the StockData instance in place by adding new indicator columns.

Return type:

None

Raises:

KeyError – If the specified stock or base column does not exist in the data.

Examples

>>> def calculate_sma(series, window):
...     return {f"sma_{window}": series.rolling(window).mean()}
>>> data.add_indicator(calculate_sma, "close", 20, "AAPL")  
>>> def calculate_bands(series, window):
...     sma = series.rolling(window).mean()
...     std = series.rolling(window).std()
...     return {
...         f"bb_upper_{window}": sma + 2 * std,
...         f"bb_lower_{window}": sma - 2 * std,
...     }
>>> data.add_indicator(calculate_bands, "close", 20, "MSFT")  
add_indicator_all(symbol: str = '', base_col: str = 'Close', overwrite: bool = False) None[source]

Add all built-in technical indicators to the stock data for one or more symbols.

This method iterates over all indicator functions in the Indicators module and applies them to the specified base column for each symbol in the DataFrame. It uses the add_indicator method for each indicator. If symbol is empty, indicators are added for all symbols.

Parameters:
  • symbol (str, optional) – Stock symbol identifying the top-level column in the MultiIndex. If empty (“”), applies to all symbols.

  • base_col (str, optional) – Base data column to apply the indicators to. Default is “Close”.

  • overwrite (bool, optional) – Whether to overwrite existing indicator columns. Default is False.

Returns:

The method mutates the StockData instance in place by adding new indicator columns.

Return type:

None

Examples

>>> data.add_indicator_all()  # Adds all indicators to all symbols
>>> data.add_indicator_all(symbol="AAPL", base_col="Close", overwrite=True)  # Adds all indicators for AAPL, overwriting existing
static auto_loader(source: DataFrame | dict[str, Any] | str | tuple[Any, ...], **kwargs: Any) StockData[source]

Auto-detect input type and load data.

Supports CSV, Excel, SQLite, DataFrame, dict, JSON, and yfinance via tuple specification.

Parameters:
  • source (str, dict, tuple, or pandas.DataFrame) – Input source. File paths (str) trigger format detection by extension. Tuples trigger yfinance loading: (ticker, days_before) or (ticker, start_date, end_date).

  • **kwargs – Additional arguments for specific loaders (e.g., ‘query’ for SQLite).

Returns:

Instance with loaded and validated data.

Return type:

StockData

Raises:
  • ValueError – If file extension is unsupported or tuple format is invalid.

  • TypeError – If source type is not recognized.

Examples

>>> data = StockData.auto_loader("prices.csv")  
>>> data = StockData.auto_loader({"Open": [...], "Close": [...]})  
check_missing() Series[source]

Count missing values per column.

Returns:

Missing value count per column.

Return type:

pandas.Series

fill_missing(method: Literal['ffill', 'bfill'] = 'ffill') StockData[source]

Fill missing values in DataFrame.

Parameters:

method (str, optional) – Fill method: ‘ffill’ (forward fill) or ‘bfill’ (backward fill). Default is ‘ffill’.

Returns:

Self for method chaining.

Return type:

StockData

classmethod from_csv(file_path: str) StockData[source]

Load stock data from CSV file.

Expects ‘Date’ column or uses first column as DatetimeIndex. Removes timezone information if present.

Parameters:

file_path (str) – Path to CSV file.

Returns:

Instance with validated CSV data.

Return type:

StockData

Raises:
  • FileNotFoundError – If file does not exist.

  • ValueError – If required OHLCV columns are missing.

classmethod from_dataframe(df: DataFrame) StockData[source]

Create StockData from existing DataFrame.

Parameters:

df (pandas.DataFrame) – DataFrame with OHLCV data.

Returns:

Instance initialized with DataFrame.

Return type:

StockData

classmethod from_dict(data: dict[str, Any]) StockData[source]

Create StockData from dictionary.

Parameters:

data (dict) – Column names as keys, value lists as data.

Returns:

Instance initialized from dictionary.

Return type:

StockData

classmethod from_excel(file_path: str) StockData[source]

Load stock data from Excel file.

Parameters:

file_path (str) – Path to Excel file.

Returns:

Instance with loaded Excel data.

Return type:

StockData

classmethod from_json(json_data: dict[str, Any]) StockData[source]

Create StockData from JSON object.

Parameters:

json_data (dict) – Parsed JSON as dictionary.

Returns:

Instance initialized from JSON.

Return type:

StockData

classmethod from_sql(query: str, connection: Any) StockData[source]

Load stock data from SQL database.

Parameters:
  • query (str) – SQL SELECT query.

  • connection – Open database connection.

Returns:

Instance with query result data.

Return type:

StockData

classmethod from_sqlite(query: str, db_path: str) StockData[source]

Load stock data from SQLite database.

Parameters:
  • query (str) – SQL SELECT query.

  • db_path (str) – Path to SQLite database file.

Returns:

Instance with query result data.

Return type:

StockData

Examples

>>> data = StockData.from_sqlite("SELECT * FROM stocks", "data.db")  
classmethod from_yfinance(tickers: str | list[str], start_date: date | None = None, end_date: date | None = None, days_before: int | None = None) StockData[source]

Load stock data from Yahoo Finance.

Parameters:
  • tickers (list) – List of ticker symbols, e.g., [‘AAPL’, ‘MSFT’].

  • start_date (date, optional) – Start date (inclusive). Ignored if days_before is set. Default is None.

  • end_date (date, optional) – End date (inclusive). Ignored if days_before is set. Default is None.

  • days_before (int, optional) – Number of days before today to retrieve. Overrides start_date and end_date. Default is None.

Returns:

Instance with multi-ticker OHLCV data with DatetimeIndex.

Return type:

StockData

Raises:

ImportError – If yfinance package is not installed.

Notes

If days_before is provided, the start_date and end_date parameters are ignored. Requires internet connection. Prices are auto-adjusted for splits/dividends. Multi-ticker requests result in a MultiIndex DataFrame.

Examples

>>> data = StockData.from_yfinance(["AAPL"], days_before=365)  
>>> data.df.head()  
classmethod generate_mock_data(days: int = 100, seed: int = 42) StockData[source]

Generate synthetic OHLCV data for testing.

Parameters:
  • days (int, optional) – Number of days of data to generate. Default is 100.

  • seed (int, optional) – Random seed for reproducibility. Default is 42.

Returns:

Instance containing generated mock data.

Return type:

StockData

Examples

>>> data = StockData.generate_mock_data(50, seed=123)
>>> len(data.df)
50
get(column: str) Series[source]

Get a column from the DataFrame.

Parameters:

column (str) – Column name.

Returns:

Column data.

Return type:

pandas.Series

head(n: int = 5) DataFrame[source]

Return first n rows.

Parameters:

n (int, optional) – Number of rows. Default is 5.

Returns:

First n rows.

Return type:

pandas.DataFrame

info() None[source]

Display DataFrame information.

Return type:

None

slice(start: Any | None = None, end: Any | None = None) DataFrame[source]

Slice DataFrame by date range.

Parameters:
  • start (str or datetime, optional) – Start date (inclusive).

  • end (str or datetime, optional) – End date (inclusive).

Returns:

Sliced data.

Return type:

pandas.DataFrame

to_csv(file_path: str, **kwargs: Any) str[source]

Export DataFrame to CSV file.

Parameters:
  • file_path (str) – Output file path.

  • **kwargs – Additional arguments passed to pandas.to_csv.

Returns:

Path to exported file.

Return type:

str

to_custom(export_func: Callable[[...], Any], *args: Any, **kwargs: Any) Any[source]

Export using custom function.

Parameters:
  • export_func (callable) – Function taking DataFrame as first argument.

  • *args – Positional arguments passed to export_func.

  • **kwargs – Keyword arguments passed to export_func.

Returns:

Export result from custom function.

Return type:

result of export_func(self.df, *args, **kwargs)

Examples

>>> def save_parquet(df, path):
...     df.to_parquet(path)
...     return path
>>> data.to_custom(save_parquet, "output.parquet")  
Raises:
  • IOError – If file cannot be written.

  • IOError – If file cannot be written.

  • IOError – If database cannot be written.

to_dataframe() DataFrame[source]

Return copy of underlying DataFrame.

Returns:

DataFrame with DatetimeIndex and OHLCV columns.

Return type:

pandas.DataFrame

to_dict(orient: str = 'records') dict[str, Any][source]

Export DataFrame to dictionary.

Parameters:

orient (str, optional) – Dictionary orientation. Default is ‘records’.

Returns:

DataFrame as dictionary.

Return type:

dict

to_excel(file_path: str, **kwargs: Any) str[source]

Export DataFrame to Excel file.

Parameters:
  • file_path (str) – Output file path (.xls or .xlsx).

  • **kwargs – Additional arguments passed to pandas.to_excel.

Returns:

Path to exported file.

Return type:

str

to_json(file_path: str | None = None, orient: str = 'records', **kwargs: Any) str[source]

Export DataFrame to JSON.

Parameters:
  • file_path (str, optional) – Output file path. If None, returns JSON string. Default is None.

  • orient (str, optional) – JSON orientation (‘records’, ‘columns’, etc.). Default is ‘records’.

  • **kwargs – Additional arguments passed to pandas.to_json.

Returns:

JSON string

Return type:

str

Raises:

IOError – If file cannot be written.

to_sql(table_name: str, connection: Any, if_exists: str = 'replace', **kwargs: Any) str[source]

Export DataFrame to SQL table.

Parameters:
  • table_name (str) – Target table name.

  • connection (sqlalchemy.engine.Connection or sqlite3.Connection) – Active database connection.

  • if_exists (str, optional) – Behavior if table exists: ‘fail’, ‘replace’, ‘append’. Default is ‘replace’.

  • **kwargs – Additional arguments passed to pandas.to_sql.

Returns:

Table name in database.

Return type:

str

to_sqlite(table_name: str, db_path: str, if_exists: str = 'replace', **kwargs: Any) str[source]

Export DataFrame to SQLite database.

Parameters:
  • table_name (str) – Target table name in database.

  • db_path (str) – Path to SQLite database file.

  • if_exists (str, optional) – Behavior if table exists: ‘fail’, ‘replace’, ‘append’. Default is ‘replace’.

  • **kwargs – Additional arguments passed to pandas.to_sql.

Returns:

Path to database file.

Return type:

str

class stocksimpy.core.portfolio.Portfolio(initial_cap: float = 100000)[source]

Bases: object

Manage a trading portfolio recording cash, holdings, trades and value history.

Parameters:

initial_cap (float, optional) – Starting capital for the portfolio. Default is 100_000.

Notes

This class is used by the backtester to execute trades via exec_trade, update portfolio value with update_value, and expose value_history and trade_log for analysis and plotting (see stocksimpy.utils.visualize).

exec_trade(symbol: str, trade_type: str, price: float, shares: float, date: Timestamp, transaction_fee: float = 0.0) None[source]

Execute a buy or sell trade, updating cash, holdings, and the trade log.

Parameters:
  • symbol (str) – Stock symbol to trade.

  • trade_type (str) – ‘buy’ or ‘sell’ (case-insensitive).

  • price (float) – Price per share (must be > 0).

  • shares (float) – Number of shares to trade (non-negative). Will be converted to int.

  • date (pd.Timestamp) – Timestamp of the trade.

  • transaction_fee (float, optional) – Transaction fee applied to the trade (default 0.0).

Notes

  • For buys, if available cash is insufficient the method reduces the number of shares to the maximum affordable (after fee). If the transaction fee exceeds available cash, the trade is ignored.

  • For sells, if holdings are less than requested shares the method sells all available shares.

  • Each executed trade is logged via _log_trade and updates cash and holdings accordingly.

Raises:

ValueError – If price <= 0, shares < 0, trade_type not in {‘buy’,’sell’}, or transaction_fee < 0.

update_value(current_date: Timestamp, current_prices: dict[str, float]) None[source]

Updates the total value of the portfolio and appends it to value_history. This is a corrected version that calculates the total value (cash + holdings).

class stocksimpy.core.backtester.Backtester(symbol: str, data: StockData, strategy: Callable[[...], Any], initial_cap: float = 100000, transaction_fee: float = 0.0, trade_amount: float = 10000)[source]

Bases: object

Backtester for running simple single-symbol strategies on historical data.

This class executes either fixed-size or dynamic-size trading strategies against OHLCV time-series data. At each timestep, the strategy is invoked with historical data up to that point. The internal Portfolio tracks cash, holdings, executed trades, and value history.

Parameters:
  • symbol (str) – Ticker symbol to backtest. For multi-ticker DataFrames, only this symbol’s data will be used.

  • data (StockData) – StockData instance containing OHLCV data. The underlying DataFrame will be extracted and filtered to the selected symbol if necessary.

  • strategy (callable) – Strategy function to be executed at each timestep. In fixed mode: strategy(historic_df) -> signal In dynamic mode: strategy(historic_df, holdings) -> (signal, shares) where signal is one of {‘buy’, ‘sell’, ‘hold’}.

  • initial_cap (float, optional) – Starting cash for the portfolio. Default is 100000.

  • transaction_fee (float, optional) – Flat fee applied to every executed trade. Default is 0.0.

  • trade_amount (float, optional) – Dollar amount allocated per trade when using fixed-size backtesting. Ignored in dynamic mode. Default is 10000.

symbol

Ticker symbol used for this backtest.

Type:

str

data

Historical OHLCV data for symbol, indexed by datetime.

Type:

pandas.DataFrame

strategy

Strategy function executed at each timestep.

Type:

callable

initial_cap

Initial portfolio cash.

Type:

float

transaction_fee

Fee applied to each trade.

Type:

float

trade_amount

Dollar amount allocated per trade during fixed-size backtests.

Type:

float

portfolio

Tracks cash, holdings, executed trades, and historical total value.

Type:

Portfolio

Notes

Fixed-size mode
  • Strategy receives only historical OHLCV data: strategy(df) -> signal.

  • Number of shares is computed as: shares = int(trade_amount / close_price).

  • If close price is zero or NaN, shares will be zero and no trade occurs.

Dynamic-size mode
  • Strategy receives historical data and current holdings: strategy(df, holdings) -> (signal, shares).

  • Strategy must return (signal, shares).

  • If the signal is not ‘buy’ or ‘sell’, no trade is executed.

Multi-ticker data

If the input StockData contains multi-level columns (e.g., the output of from_yfinance), only the subcolumns for symbol are passed to the strategy. All other symbols are ignored.

State Mutation

Calling a backtest method mutates the internal portfolio state. Run generate_report() for results or inspect: - portfolio.value_history - portfolio.trade_log

Examples

Simple fixed-size strategy:

>>> def strat(df):
...     return 'buy' if df['Close'].iloc[-1] > df['Close'].rolling(10).mean().iloc[-1] else 'hold'
>>> bt = Backtester("AAPL", stock_data, strat, trade_amount=500)
>>> bt.run_backtest_fixed()   
>>> bt.generate_report()      

Dynamic-size strategy:

>>> def dyn(df, holdings):
...     if df['Close'].iloc[-1] > df['Close'].rolling(20).mean().iloc[-1]:
...         return ('buy', 5)
...     return ('sell', 5)
>>> bt = Backtester("AAPL", stock_data, dyn)
>>> bt.run_backtest_dynamic()  
>>> bt.generate_report()       
generate_report() dict[source]

Generate a summary report of the backtest execution.

Computes final portfolio value, total return percentage, and the number of trades executed. Returns an empty-portfolio default if no backtest has been run or the portfolio is empty.

Returns:

A dictionary with keys:

  • 'final_value' (float): Final portfolio value (cash + stock value).

  • 'total_return_percent' (float): Percentage return from initial capital (e.g., 15.5 for 15.5% return).

  • 'number_of_trades' (int): Total number of trades executed during the backtest.

Return type:

dict

Examples

>>> report = bt.generate_report()
>>> print(f"Final Value: ${report['final_value']:.2f}")
>>> print(f"Return: {report['total_return_percent']:.2f}%")
>>> print(f"Trades: {report['number_of_trades']}")
run_backtest_dynamic() None[source]

Execute a backtest using dynamic trade sizes.

At each timestep, the strategy is called with two arguments: (df, holdings) where:

  • df is the historical DataFrame up to the current timestamp (filtered to self.symbol for MultiIndex data).

  • holdings is the current number of shares held.

The strategy must return a tuple (signal, shares), where shares is an integer specifying the number of shares to buy or sell.

Notes

  • The portfolio is updated in place. This method does not return a value.

  • Any strategy that returns a non-tuple or a tuple of incorrect length will raise a TypeError.

  • All exceptions raised inside the strategy propagate directly to the caller, allowing debugging of strategy logic.

  • The DataFrame slice passed to the strategy includes all history up to the current timestamp, enabling rolling-window or stateful logic.

Return type:

None

Raises:
  • TypeError – If the strategy does not return a two-item tuple.

  • Exception – Any other error raised inside the strategy or during data access.

Examples

>>> def dyn(df, holdings):
...     return ('buy', 5) if df['Close'].iloc[-1] < df['Close'].rolling(20).mean().iloc[-1] else ('sell', 5)
>>> bt = Backtester('AAPL', stock_data, dyn)
>>> bt.run_backtest_dynamic()
>>> bt.generate_report()
run_backtest_fixed() None[source]

Execute a backtest using fixed trade amounts for each signal.

Iterates through historical data and calls the strategy at each timestep. The strategy receives a DataFrame containing all available historical values up to the current timestamp. The number of shares to trade is computed as:

shares = int(self.trade_amount / close_price)

For multi-ticker DataFrames (e.g., from yfinance), only the columns corresponding to self.symbol are passed to the strategy. For flat DataFrames, the entire DataFrame slice is passed unchanged.

Notes

  • If the close price is zero or missing at a timestep, the computed share count will be zero and no trade will be executed.

  • This method mutates the internal portfolio state directly and does not return anything. After execution, use generate_report() or inspect portfolio.value_history and portfolio.trade_log to access results.

Examples

>>> def strat(df):
...     return 'buy' if df['Close'].iloc[-1] > df['Close'].rolling(10).mean().iloc[-1] else 'hold'
>>> bt = Backtester('AAPL', stock_data, strat, trade_amount=500)
>>> bt.run_backtest_fixed()
>>> bt.generate_report()
Raises:

Exception – Any exception raised inside the strategy will propagate to the caller.

class stocksimpy.addons.strategy.Strategy[source]

Bases: object

Collection of ready-to-use example strategies.

This class provides simple, self-contained trading strategies in both fixed-size and dynamic-size formats. Each strategy is independent and returned as a callable function that conforms to the Backtester API.

Fixed strategies return:

strategy(data) -> signal

Dynamic strategies return:

strategy(data, holdings) -> (signal, shares)

Notes

These strategies are designed as reference implementations and examples demonstrating how to write custom strategies. They can also be used directly in Backtester instances for quick experimentation.

static atr_trend_dynamic(ma_period: int = 20, atr_period: int = 14, k: float = 1.5, allocation: float = 20000) Callable[[DataFrame, float], tuple[str, int]][source]

ATR-based dynamic trend-following strategy.

Parameters:
  • ma_period (int) – Moving average period.

  • atr_period (int) – ATR smoothing period.

  • k (float) – Volatility multiplier.

  • allocation (float) – Dollar amount converted to shares when buying.

Returns:

  • callable – Function strategy(data, holdings) -> (signal, shares).

  • Strategy Logic

  • ————–

  • - Computes ATR and SMA bands – upper = SMA + k * ATR lower = SMA - k * ATR

  • - Buy when price exceeds upper band.

  • - Sell when price falls below lower band.

  • - Otherwise hold.

Notes

Requires max(ma_period, atr_period) + 2 data points.

static breakout_dynamic(lookback: int = 20, allocation: float = 20000) Callable[[DataFrame, float], tuple[str, int]][source]

Breakout dynamic strategy.

Parameters:
  • lookback (int) – Window size used to compute breakout high/low.

  • allocation (float) – Dollar amount to convert into shares on a breakout.

Returns:

  • callable – Function strategy(data, holdings) -> (signal, shares).

  • Strategy Logic

  • ————–

  • - Buy when price breaks above the highest high in the lookback window.

  • - Sell when price breaks below the lowest low.

  • - Otherwise hold.

Notes

Shares are computed as int(allocation / price).

static buy_all_fixed() Callable[[DataFrame], str][source]

Always-return-buy fixed-size strategy.

Returns:

A function strategy(data) -> signal that always returns 'buy'.

Return type:

callable

Notes

Used mainly for testing, examples, or verifying the Backtester pipeline.

static multi_indicator_fixed(rsi_period: int = 14, sma_long: int = 200) Callable[[DataFrame], str][source]

Multi-indicator confirmation strategy (fixed-size).

Parameters:
  • rsi_period (int) – Period for RSI calculation.

  • sma_long (int) – Long-term SMA window for trend confirmation.

Returns:

  • callable – Function strategy(data) -> signal.

  • Strategy Logic

  • ————–

  • Buy when

    • RSI < 60

    • Price > SMA

  • Sell when

    • RSI > 70

    • OR Price < SMA

static price_action_dynamic() Callable[[DataFrame, float], tuple[str, int]][source]

Create a simple price-action dynamic strategy.

Returns:

  • callable – A function with signature strategy(data, holdings) -> (signal, shares).

  • Strategy Logic

  • ————–

  • - Compares current price to price 30 days ago.

  • - If price has dropped ≥ 14%, buys using a fixed $20,000 allocation.

    • Otherwise returns ('hold', 0).

Notes

  • Automatically handles both MultiIndex and flat OHLCV DataFrames.

  • If insufficient data (< 30 points), always returns ('hold', 0).

static rsi_momentum_fixed(rsi_period: int = 14) Callable[[DataFrame], str][source]

RSI momentum crossover fixed-size strategy.

Parameters:

rsi_period (int, default 14) – Period used in RSI computation.

Returns:

  • callable – Function strategy(data) -> signal.

  • Strategy Logic

  • ————–

  • - Computes RSI.

  • - A buy signal occurs when RSI crosses above 50.

  • - A sell signal occurs when RSI crosses below 50.

    • Otherwise returns 'hold'.

Notes

Requires at least rsi_period + 1 data points.

static rsi_reversion_fixed(rsi_period: int = 14, low_th: float = 30, high_th: float = 70) Callable[[DataFrame], str][source]

RSI mean-reversion fixed-size strategy.

Parameters:
  • rsi_period (int) – RSI period length.

  • low_th (float) – Oversold threshold.

  • high_th (float) – Overbought threshold.

Returns:

  • callable – Function strategy(data) -> signal.

  • Strategy Logic

  • ————–

  • - Buy when RSI < low_th (oversold).

  • - Sell when RSI > high_th (overbought).

  • - Otherwise hold.

static sma_ema_crossover_fixed(fast: int = 12, slow: int = 26) Callable[[DataFrame], str][source]

SMA crossover fixed-size strategy.

Parameters:
  • fast (int) – Fast moving average window.

  • slow (int) – Slow moving average window.

Returns:

  • callable – Function strategy(data) -> signal.

  • Strategy Logic

  • ————–

  • - Computes two simple moving averages.

  • - Buy when fast SMA crosses above slow SMA.

  • - Sell when fast SMA crosses below slow SMA.

  • - Otherwise hold.

Notes

Requires at least slow + 2 data points.

class stocksimpy.addons.indicators.Indicators[source]

Bases: object

This module provides basic functions for calculating various technical indicators.

Summary

Provides implementations of commonly used technical indicators such as SMA, RSI, MACD, DEMA, TEMA and related helper smoothing functions. These functions operate on pandas Series and return Series or DataFrame results suitable for usage in strategy code and performance analysis.

calculate_dema(window: int = 14) dict[str, Series][source]

Calculate the Double Exponential Moving Average (DEMA) of a data series.

Summary

DEMA reduces lag by using a double-smoothed EMA: DEMA = (2 * EMA1) - EMA2.

param data_series:

The input data series (e.g., closing prices).

type data_series:

pandas.Series

param window:

The number of periods to use for the DEMA calculation.

type window:

int

returns:

A dictionary with key dema_{window} containing a pandas Series with the DEMA values. The initial 2*window - 1 values will be NaN.

rtype:

dict

Notes

The formula for DEMA is: DEMA = (2 * EMA1) - EMA2 where EMA1 is the EMA of the original series and EMA2 is the EMA of EMA1.

calculate_ema(window: int = 14) dict[str, Series][source]

Calculates the Exponential Moving Average (EMA) of a data series.

Summary

Computes the EMA using pandas’ ewm implementation with span=window.

param data_series:

The input data series (e.g., closing prices).

type data_series:

pd.Series

param window:

The period for the EMA calculation.

type window:

int

returns:

A dictionary with key ema_{window} containing a pandas Series with the EMA values.

rtype:

dict

calculate_hma(window: int = 14) dict[str, Series][source]

Calculate the Hull Moving Average (HMA) of a data series.

Summary

The HMA minimizes lag while keeping smoothness by combining weighted averages. This implementation approximates WMA using EMA.

param data_series:

The input data series (e.g., closing prices).

type data_series:

pandas.Series

param window:

The number of periods to use for the HMA calculation.

type window:

int

returns:

A dictionary with key hma_{window} containing a pandas Series with the HMA values. The initial values will be NaN due to the nested EMA calculations.

rtype:

dict

raises ValueError:

If the window is less than 2, as HMA calculation requires at least two periods.

Notes

This implementation uses the following equation to calculate HMA: HMA(n) = WMA(2 * WMA(n/2) - WMA(n)), sqrt(n) and approximates WMA via EMA.

calculate_hma_macd(fast_period: int = 12, slow_period: int = 26, signal_period: int = 9) dict[str, Series][source]

Calculate the Hull Moving Average (HMA) MACD indicator.

Summary

Variant of MACD that smooths the signal line using HMA for reduced lag and improved responsiveness. Returns MACD line, HMA-smoothed signal, and histogram.

param data_series:

The input data series (e.g., closing prices).

type data_series:

pandas.Series

param fast_period:

The period for the fast EMA used in the MACD line (default 12).

type fast_period:

int, optional

param slow_period:

The period for the slow EMA used in the MACD line (default 26).

type slow_period:

int, optional

param signal_period:

The period for the HMA used to smooth the signal line (default 9).

type signal_period:

int, optional

returns:

A dictionary containing three Series: ‘hma_macd_line’, ‘hma_macd_signal’, and ‘hma_macd_histogram’.

rtype:

dict

Notes

Calculation steps: 1. Calculate fast EMA and slow EMA. 2. MACD Line = fast EMA - slow EMA. 3. Signal Line = HMA(MACD Line, signal_period). 4. Histogram = MACD Line - Signal Line.

calculate_macd(fast_period: int = 12, slow_period: int = 26, signal_period: int = 9) dict[str, Series][source]

Calculates the Moving Average Convergence Divergence (MACD) indicator.

Summary

MACD is a trend-following momentum indicator showing the relationship between two EMAs. This function returns the MACD line, its signal line, and the histogram.

param data_series:

The input data series (e.g., closing prices).

type data_series:

pandas.Series

param fast_period:

The period for the fast EMA (default is 12).

type fast_period:

int, optional

param slow_period:

The period for the slow EMA (default is 26).

type slow_period:

int, optional

param signal_period:

The period for the signal line EMA (default is 9).

type signal_period:

int, optional

returns:

A dictionary containing three Series: ‘macd_line’, ‘macd_signal’, and ‘macd_histogram’.

rtype:

dict

calculate_rsi(window: int = 14) dict[str, Series][source]

Calculate the Relative Strength Index (RSI) of a given data series.

Summary

RSI is a momentum oscillator ranging 0-100 used to identify overbought or oversold conditions. This implementation uses Wilder’s smoothing.

param data_series:

The input data series for which to calculate the RSI.

type data_series:

pandas.Series

param window:

The number of periods to use for the RSI calculation (default is 14).

type window:

int, optional

returns:

A dictionary with key rsi_{window} containing a pandas Series with the RSI values. The initial window - 1 values will be NaN.

rtype:

dict

calculate_sma(window: int = 14) dict[str, Series][source]

Calculate the Simple Moving Average (SMA) of a given data series.

Summary

Computes the rolling mean over the specified window. The first window - 1 entries will be NaN.

param data_series:

The input data series for which to calculate the SMA.

type data_series:

pandas.Series

param window:

The window size (number of periods) to use for the SMA calculation.

type window:

int

returns:

A dictionary with key sma_{window} containing a pandas Series with the SMA values. The initial window - 1 values will be NaN.

rtype:

dict

calculate_tema(window: int = 14) dict[str, Series][source]

Calculate the Triple Exponential Moving Average (TEMA) of a data series.

Summary

TEMA reduces lag by applying triple EMA smoothing: TEMA = 3*EMA1 - 3*EMA2 + EMA3.

param data_series:

The input data series (e.g., closing prices).

type data_series:

pandas.Series

param window:

The number of periods to use for the TEMA calculation.

type window:

int

returns:

A dictionary with key tema_{window} containing a pandas Series with the TEMA values. The initial 3*window - 2 values will be NaN.

rtype:

dict

Notes

The formula for TEMA is: TEMA = (3 * EMA1) - (3 * EMA2) + EMA3 where EMA1, EMA2, EMA3 are successive EMAs of the series.

calculate_tema_macd(fast_period: int = 12, slow_period: int = 26, signal_period: int = 9) dict[str, Series][source]

Calculate the Triple Exponential Moving Average (TEMA) MACD indicator.

Summary

Variant of MACD that smooths the signal line with a TEMA for increased responsiveness. Returns the MACD line, TEMA-smoothed signal, and histogram.

param data_series:

The input data series (e.g., closing prices).

type data_series:

pandas.Series

param fast_period:

The period for the fast EMA used in the MACD line (default 12).

type fast_period:

int, optional

param slow_period:

The period for the slow EMA used in the MACD line (default 26).

type slow_period:

int, optional

param signal_period:

The period for the TEMA used to smooth the signal line (default 9).

type signal_period:

int, optional

returns:

A dictionary containing three Series: ‘tema_macd_line’, ‘tema_macd_signal’, and ‘tema_macd_histogram’.

rtype:

dict

Notes

Calculation steps: 1. Calculate fast EMA and slow EMA. 2. MACD Line = fast EMA - slow EMA. 3. Signal Line = TEMA(MACD Line, signal_period). 4. Histogram = MACD Line - Signal Line.

calculate_wilders_macd(fast_period: int = 12, slow_period: int = 26, signal_period: int = 9) dict[str, Series][source]

Calculates the Moving Average Convergence Divergence (MACD) indicator using Wilder’s smoothing.

Summary

Variant of MACD where the signal line is smoothed with Wilder’s method instead of a standard EMA. Returns MACD line, Wilder-smoothed signal, and the histogram.

param data_series:

The input data series (e.g., closing prices).

type data_series:

pandas.Series

param fast_period:

The period for the fast EMA (default is 12).

type fast_period:

int, optional

param slow_period:

The period for the slow EMA (default is 26).

type slow_period:

int, optional

param signal_period:

The period for the signal line (Wilder’s smoothing) (default is 9).

type signal_period:

int, optional

returns:

A dictionary containing three Series: ‘wilders_macd_line’, ‘wilders_macd_signal’, and ‘wilders_macd_histogram’.

rtype:

dict

calculate_wma(window: int = 14) dict[str, Series][source]

Calculates the Weighted Moving Average (WMA) for a pandas Series.

Summary

Computes a linearly-weighted average over the specified window where more recent observations receive greater weight.

param data_series:

The input pandas Series for which to calculate the WMA.

type data_series:

pd.Series

param window:

The size of the moving window. This must be a positive integer.

type window:

int

returns:
  • dict

  • A dictionary with key wma_{window} containing a pandas Series with the Weighted Moving Average.

  • The first window - 1 values will be NaN.

static get_name_func() dict[str, Callable[[...], dict[str, Any]]][source]

Get a dictionary mapping indicator names to their corresponding calculation functions.

Returns:

A dictionary where keys are indicator names (e.g., ‘sma’, ‘ema’, ‘rsi’) and values are the corresponding static methods of the Indicators class that perform the calculations. All lowercased for consistency.

Return type:

dict

wilders_smoothing(window: int = 14) dict[str, Series][source]

Calculate Wilder’s Smoothing for a given data series.

Summary

Implements Wilder’s smoothing (an EMA variant) commonly used for RSI/ATR.

param data_series:

The input data series to smooth.

type data_series:

pandas.Series

param window:

The window size (number of periods) for the smoothing calculation.

type window:

int

returns:

A dictionary with key wilders_smoothing_{window} containing a pandas Series with the smoothed values. The initial window - 1 values will be NaN.

rtype:

dict

class stocksimpy.utils.performance.Performance(backtester: Backtester, risk_free_rate: float = 0.02)[source]

Bases: object

Compute performance and risk metrics for a backtest execution.

This class wraps a completed Backtester instance to calculate risk-adjusted returns, volatility, drawdown, and other common performance statistics. All metrics are computed from the portfolio’s value history and trade log.

Parameters:
  • backtester (Backtester) – A completed Backtester instance with an executed portfolio. The portfolio’s value_history and trade_log will be used to compute metrics.

  • risk_free_rate (float, optional) – Annual risk-free rate used for Sharpe ratio and other risk-adjusted calculations. Expressed as a decimal (e.g., 0.02 for 2%). Default is 0.02.

backtester

Reference to the underlying Backtester instance.

Type:

Backtester

portfolio

The portfolio object from the backtester.

Type:

Portfolio

symbol

Ticker symbol being analyzed.

Type:

str

risk_free_rate

Annual risk-free rate for calculations.

Type:

float

Notes

  • All return metrics are expressed as decimals (e.g., 0.15 for 15%).

  • Volatility and Sharpe ratio are annualized using the observed trading days per year in the portfolio’s value history.

  • Drawdown is expressed as a negative decimal (e.g., -0.25 for -25%).

  • Metrics assume that the backtester has already been executed; results may be incorrect or zero if the backtest produced no trades or value history.

Examples

>>> bt = Backtester('AAPL', stock_data, strategy)
>>> bt.run_backtest_fixed()
>>> perf = Performance(bt, risk_free_rate=0.02)
>>> report = perf.generate_risk_report()
>>> print(f"Sharpe Ratio: {report['Sharpe Ratio']:.2f}")
calc_daily_returns() Series[source]

Calculate daily percentage returns from portfolio value history.

Returns:

Daily returns as decimals (e.g., 0.01 for 1% daily return). Index is aligned with the portfolio value history dates. Missing values are dropped.

Return type:

pandas.Series

calc_max_drawdown() float[source]

Calculate the maximum drawdown during the backtest period.

Maximum drawdown is the peak-to-trough decline from the highest portfolio value to the lowest subsequent value. It measures the worst cumulative loss experienced by the strategy.

Returns:

Maximum drawdown as a negative decimal. For example, -0.25 represents a 25% drawdown from peak to trough. Returns 0.0 if the portfolio value history is empty.

Return type:

float

Notes

  • Formula: min((value - cummax(value)) / cummax(value))

  • A drawdown of -0.2 means the portfolio fell 20% from its peak.

  • All drawdown values are negative or zero.

calc_sharpe_ratio() float[source]

Calculate the annualized Sharpe ratio.

The Sharpe ratio measures risk-adjusted return by comparing the excess daily return (above the risk-free rate) to the standard deviation of daily returns. Higher Sharpe ratios indicate better risk-adjusted performance.

Returns:

Annualized Sharpe ratio. A typical range is 0 to 3, with values above 1.0 generally considered good and above 2.0 excellent. Returns 0.0 if daily returns are empty or have zero volatility.

Return type:

float

Notes

  • The calculation uses the dynamic annualization factor from _get_annualized_trading_days() to adapt to the backtest period.

  • Formula:

    (mean_daily_excess_return / daily_volatility) * sqrt(annualization_factor)

  • The risk-free rate is converted to a daily rate before computation.

  • Sharpe ratios above 1.0 typically indicate strong risk-adjusted returns.

Examples

>>> perf = Performance(bt)
>>> sharpe = perf.calc_sharpe_ratio()
>>> if sharpe > 1.0:
...     print("Good risk-adjusted performance")
calc_total_return() float[source]

Calculate total return over the backtest period.

Computes the percentage return from the initial capital to the final portfolio value, expressed as a decimal.

Returns:

Total return as a decimal. For example, 0.15 represents a 15% return. Returns 0.0 if the portfolio value history is empty.

Return type:

float

Notes

Total return is calculated as:

(final_value - initial_capital) / initial_capital

calc_volatility() float[source]

Calculate annualized portfolio volatility.

Volatility is the standard deviation of daily returns, annualized using the observed trading days per year. Higher volatility indicates more erratic price swings; lower volatility indicates more stable returns.

Returns:

Annualized volatility as a decimal. For example, 0.20 represents 20% annualized volatility. Returns 0.0 if daily returns are empty.

Return type:

float

Notes

  • Formula: daily_returns.std() * sqrt(annualization_factor)

  • Annualization factor is dynamically computed from trading days observed in the portfolio history.

  • Volatility is a key input to the Sharpe ratio calculation.

Examples

>>> vol = perf.calc_volatility()
>>> print(f"Annualized volatility: {vol*100:.1f}%")
Annualized volatility: 18.5%
generate_risk_report() Series[source]

Generate a comprehensive performance and risk report.

Computes and aggregates the most important performance metrics into a single pandas Series for easy inspection and comparison. All metrics are computed lazily at the time of the call.

Returns:

A labeled one-dimensional array with keys:

  • 'Total Return' (float): Decimal total return from initial capital to final portfolio value.

  • 'Annualized Return' (float): Geometric annualized return over the backtest period.

  • 'Max Drawdown' (float): Maximum peak-to-trough decline as a negative decimal (e.g., -0.25 for -25%).

  • 'Sharpe Ratio' (float): Risk-adjusted return metric annualized using dynamic trading days. Generally, values > 1.0 are good.

  • 'Volatility' (float): Annualized standard deviation of daily returns, expressed as a decimal (e.g., 0.15 for 15%).

Return type:

pandas.Series

Notes

  • All return metrics are expressed as decimals (0.15 = 15%).

  • All metrics return 0.0 if insufficient data is available.

  • Sortino Ratio is deliberately omitted pending implementation verification; uncomment the corresponding line in the code once ready.

  • This is the recommended entry point for quick portfolio evaluation.

Examples

>>> report = perf.generate_risk_report()
>>> print(report)
Total Return           0.150000
Annualized Return      0.120000
Max Drawdown          -0.250000
Sharpe Ratio           1.450000
Volatility             0.180000
dtype: float64
get_annualized_return() float[source]

Calculate annualized return over the backtest period.

Converts the total return to a compound annual growth rate (CAGR) using the date span of the portfolio (start to end). This gives a consistent annual rate for comparisons across different time horizons.

Returns:

Annualized return as a decimal. For example, 0.12 represents a 12% annualized return. Returns 0.0 if date span is zero.

Return type:

float

Notes

  • The calculation assumes a 365.25-day year to account for leap years.

  • The date span includes all calendar days (weekends, holidays, etc.), not just trading days.

  • Formula: (1 + total_return) ** (365.25 / days) - 1

class stocksimpy.utils.visualize.Visualize(backtester: Any)[source]

Bases: object

Visualization utilities for backtest results.

This helper provides convenience plotting for single-symbol backtests. It expects the provided backtester to expose the following attributes:

  • backtester.data: a DataFrame with OHLCV columns. For MultiIndex data the implementation selects ('Close', symbol) for the symbol’s close prices.

  • backtester.portfolio.value_history: a pandas Series of portfolio total values indexed by timestamps.

  • backtester.portfolio.trade_log: a pandas DataFrame with executed trades and at minimum the columns ['Date', 'Price', 'Type'] where Type is 'buy' or 'sell'.

Parameters:

backtester (Backtester) – Backtester instance containing historical data, the portfolio, and the trade log. The instance is stored on the Visualize object and used by the plotting methods.

Notes

  • The plotting functions return the matplotlib.pyplot module (plt) so callers can call plt.show(), plt.savefig(...), or continue customizing the figure.

  • The helper intentionally performs minimal validation on the input data to stay lightweight; callers should ensure the backtester and portfolio conform to the expected structure.

visualize_backtest() Any[source]

Plot the backtest price, portfolio value, and executed trades.

This method renders a two-axis plot: the symbol’s close price on the left y-axis and the portfolio’s total value on the right y-axis. Buy and sell executions are annotated on the price axis as upward and downward markers respectively.

Returns:

The matplotlib.pyplot module used to create the figure. Call plt.show() to display the plot or plt.savefig(...) to save it to disk.

Return type:

matplotlib.pyplot

Notes

  • The method uses backtester.symbol to choose the symbol to plot.

  • For MultiIndex DataFrames produced by e.g. yfinance this method selects the close series using ('Close', symbol). If your data is flat (no MultiIndex), ensure the close prices are available at the column 'Close'.

  • The trade log is expected to contain at least the columns Date, Price, and Type where Type is 'buy'/'sell'.

Examples

>>> viz = Visualize(bt)
>>> plt = viz.visualize_backtest()
>>> plt.show()