API Reference
- class stocksimpy.core.stock_data.StockData(df: DataFrame | None = None)[source]
Bases:
objectContainer 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
Indicatorsmodule fromstocksimpyto 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:
- 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:
- 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:
- 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:
- 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:
- 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:
- 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:
- 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:
- 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:
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:
- 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:
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
- 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:
objectManage 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 withupdate_value, and exposevalue_historyandtrade_logfor analysis and plotting (seestocksimpy.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_tradeand updatescashandholdingsaccordingly.
- Raises:
ValueError – If
price <= 0,shares < 0,trade_typenot in {‘buy’,’sell’}, ortransaction_fee < 0.
- 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:
objectBacktester 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) -> signalIn 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
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:dfis the historical DataFrame up to the current timestamp (filtered toself.symbolfor MultiIndex data).holdingsis the current number of shares held.
The strategy must return a tuple
(signal, shares), wheresharesis 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.symbolare 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 inspectportfolio.value_historyandportfolio.trade_logto 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:
objectCollection 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) + 2data 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) -> signalthat 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 + 1data 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 + 2data points.
- class stocksimpy.addons.indicators.Indicators[source]
Bases:
objectThis 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 initial2*window - 1values will be NaN.- rtype:
dict
Notes
The formula for DEMA is:
DEMA = (2 * EMA1) - EMA2whereEMA1is the EMA of the original series andEMA2is the EMA ofEMA1.
- 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
windowis 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 initialwindow - 1values 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 - 1entries 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 initialwindow - 1values 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 initial3*window - 2values will be NaN.- rtype:
dict
Notes
The formula for TEMA is:
TEMA = (3 * EMA1) - (3 * EMA2) + EMA3whereEMA1,EMA2,EMA3are 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 - 1values 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 initialwindow - 1values will be NaN.- rtype:
dict
- class stocksimpy.utils.performance.Performance(backtester: Backtester, risk_free_rate: float = 0.02)[source]
Bases:
objectCompute 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:
- 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:
objectVisualization 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']whereTypeis'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.pyplotmodule (plt) so callers can callplt.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.pyplotmodule used to create the figure. Callplt.show()to display the plot orplt.savefig(...)to save it to disk.- Return type:
matplotlib.pyplot
Notes
The method uses
backtester.symbolto 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, andTypewhereTypeis'buy'/'sell'.
Examples
>>> viz = Visualize(bt) >>> plt = viz.visualize_backtest() >>> plt.show()