SMI: Stochastic Momentum Reversion¶

A mean-reversion strategy utilizing the Stochastic Momentum Index to identify and trade reversals from deep oversold territory.

In [1]:
import numpy as np
import pandas as pd
import yfinance as yf
import warnings
warnings.filterwarnings('ignore')
RANDOM_SEED = 2750
np.random.seed(RANDOM_SEED)
In [2]:
import sys
from pathlib import Path

sys.path.append("systrade")
In [3]:
config_base_path = "systrade"
In [4]:
from backtester.backtest_engine import *
from backtester.data_loader import *
from backtester.visualizations import *
from backtester.utils import *
from backtester.reports import *
In [5]:
from strategies.strategy_smi import * 
from visuals.plot_smi_signals import *
In [6]:
# dji_components = ["MMM", "AXP", "AMGN", "AMZN", "AAPL", "BA", "CAT", "CVX", "CSCO", "KO", "DIS", "GS", "HD", "HON", "IBM", "JNJ", "JPM", "MCD", "MRK", "MSFT", "NKE", "NVDA", "PG", "CRM", "SHW", "TRV", "UNH", "VZ", "V", "WMT"]
dji_halfweight = ["GS", "MSFT", "HD", "V", "SHW", "CAT", "MCD", "UNH", "AXP"] #account for 50% of weighting
In [7]:
# Download market data
tickers = dji_halfweight

Walk-Forward Analysis and Sensitivity Analysis¶

In [8]:
from backtester.walkforward_cv import *
In [9]:
set_random_seed(RANDOM_SEED)
data_dict = download_multiple(tickers,start_date='2017-04-01', end_date='2023-01-01')
Downloading data for 9 tickers...
  GS...  1448 bars
  MSFT...  1448 bars
  HD...  1448 bars
  V...  1448 bars
  SHW...  1448 bars
  CAT...  1448 bars
  MCD...  1448 bars
  UNH...  1448 bars
  AXP...  1448 bars
Successfully downloaded 9/9 tickers

In [10]:
trade_config = TradeConfig(
    initial_capital=300_000,
    position_size_pct=0.05,
    commission_pct=0.001,
    slippage_pct=0.0005,
    slippage_fixed=0.0,
    max_trade_size=30_000,        
    min_trade_size=10_000
)

wf_config = create_wf_config(
    training_days=252*2,
    testing_days=21*9,
    holdout_days=21*6,
    min_trades=30,
    n_trials=150,
    n_startup_trials = 50,
    n_jobs=-1,
    timeout=3600,
    random_seed=2570
)
In [11]:
def get_smi_param_space() -> dict:
    """
    Parameter search space for SMI Optimization.
    """
    return {
        'k_period': ('int', 8, 57),
        'd_period': ('int', 2, 7),
        'oversold_threshold': ('int', -60, -0),
        'overbought_threshold': ('int', 0, 60),
    }
In [12]:
param_space = get_smi_param_space()


results_df, sensitivity_df = run_walkforward(
    data_dict=data_dict,
    param_space=param_space,
    strategy_class=SMIStrategy,
    strategy_name="SMIStrategy",
    trade_config=trade_config,
    wf_config=wf_config,
    objective_weights=(0.35, 0.65),  # 35% win rate, 65% Sharpe
    verbose=True
)
[I 2026-01-15 06:53:56,107] A new study created in memory with name: SMIStrategy_global_cv
Global Walk-Forward: 3 windows from 2017-04-03 to 2022-12-30

Running Global Optimization on 3 windows...
Global Optimization Complete. Best Score: 1.1200
{'k_period': 41, 'd_period': 2, 'oversold_threshold': -57, 'overbought_threshold': 37}

--- Degradation Analysis ---
Avg Sharpe Degradation: -19.42% (Lower is better)
  < 10%: Very Robust
  > 50%: High Overfitting

--- Parameter Stability (Cluster Analysis) ---
              parameter     cv  range_ratio assessment
2    oversold_threshold  0.081        0.252  Excellent
0              k_period  0.090        0.293  Excellent
3  overbought_threshold  0.115        0.411       Good
1              d_period  0.213        1.000       Poor

--- Parameter Importance ---
              parameter  importance
0              k_period    0.441309
1    oversold_threshold    0.344115
2              d_period    0.156512
3  overbought_threshold    0.058064
No description has been provided for this image
Results saved to wf_method2_results

Backtest on Holdout Dataset¶

In [13]:
start_date = '2023-01-01'
raw_data = download_multiple(tickers, start_date)
Downloading data for 9 tickers...
  GS...  761 bars
  MSFT...  761 bars
  HD...  761 bars
  V...  761 bars
  SHW...  761 bars
  CAT...  761 bars
  MCD...  761 bars
  UNH...  761 bars
  AXP...  761 bars
Successfully downloaded 9/9 tickers

In [14]:
# Load configurations
strategy_config, trade_config, full_config = load_configs(
    f'{config_base_path}/configs/smi_strategy.yaml',
    f'{config_base_path}/configs/default_settings.yaml'
)
In [15]:
strategy_config
Out[15]:
StrategyConfig(name='SMI', parameters={'k_period': 40, 'd_period': 3, 'oversold_threshold': -50, 'overbought_threshold': 50})
In [16]:
trade_config
Out[16]:
TradeConfig(initial_capital=100000, commission_pct=0.001, slippage_pct=0.0005, slippage_fixed=0.01, position_size_pct=0.95, max_trade_size=50000, min_trade_size=100)
In [17]:
trade_config.initial_capital = 300_000
trade_config.max_trade_size = 30_000
trade_config.min_trade_size = 10_000
In [18]:
trade_config
Out[18]:
TradeConfig(initial_capital=300000, commission_pct=0.001, slippage_pct=0.0005, slippage_fixed=0.01, position_size_pct=0.95, max_trade_size=30000, min_trade_size=10000)
In [19]:
strategy_config.parameters = {'k_period': 49, 'd_period': 2, 'oversold_threshold': -57, 'overbought_threshold': 59}
In [20]:
aligned_data = align_data(raw_data)

print(f" Downloaded data for {len(tickers)} tickers")
if len(aligned_data) > 0:
    first_ticker = list(aligned_data.keys())[0]
    date_range = f"{aligned_data[first_ticker].index[0]} to {aligned_data[first_ticker].index[-1]}"
    print(f"   Date range: {date_range}")
    print(f"   Total bars per ticker: {len(aligned_data[first_ticker])}")
 Downloaded data for 9 tickers
   Date range: 2023-01-03 00:00:00 to 2026-01-14 00:00:00
   Total bars per ticker: 761
In [21]:
# Initialize strategy
print("\n Initializing SMI strategy...")
strategy = SMIStrategy(strategy_config)

# Validate configuration
if not strategy.validate_config():
    print(" Invalid configuration!")
 Initializing SMI strategy...
In [22]:
# Run backtest
print("\n Running backtest...")
engine = BacktestEngine(strategy, trade_config)
results = engine.run(aligned_data)

# Print results
print_performance_summary(results.metrics)
 Running backtest...

================================================================================
PERFORMANCE SUMMARY
================================================================================

*** RETURNS ***
  Initial Capital:    $  300,000.00
  Final Value:        $  368,356.84
  Total P&L:          $   68,356.84
  Total Return:              22.79%

*** RISK METRICS ***
  Sharpe Ratio:               1.06
  Sortino Ratio:              1.41
  Max Drawdown:              -7.62%
  Calmar Ratio:               2.99
  Annual Volatility:          6.61%

*** TRADE STATISTICS ***
  Total Trades:                 39
  Win Rate:                  84.62%
  Profit Factor:              7.89
  Expectancy:         $     1776.94
  Best Trade:         $     7929.28
  Worst Trade:        $    -4016.17
  Average Trade:      $     1776.94

*** COSTS ***
  Total Commission:   $        0.00
  Total Slippage:     $        0.00
  Total Costs:        $        0.00

================================================================================

In [23]:
plot_performance_dashboard(results.portfolio_state, trade_config.initial_capital)
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
In [24]:
# Prepare data with indicators (SAVE THIS!)
prepared_data = {}
for ticker, df in aligned_data.items():
    prepared = strategy.prepare_data(df)
    prepared['ticker'] = ticker
    prepared_data[ticker] = prepared
In [25]:
plot_smi_signals(
    data=prepared_data['UNH'],
    ticker='UNH',
    results=results,
    start_date='2023-01-01',
    end_date='2025-12-31',
    strategy_config=strategy_config
)
================================================================================
SMI STRATEGY SIGNALS FOR UNH
Period: 2023-01-01 to 2025-12-31
================================================================================
No description has been provided for this image
SMI Strategy Statistics for UNH:
--------------------------------------------------------------------------------
  Total Entry Signals: 5
  Total Exit Signals:  4

  Trade Performance:
    Total P&L: $4,514.92
    Wins: 2 | Losses: 2
    Win Rate: 50.0%
    Avg Win:  $4,339.14
    Avg Loss: $-2,081.68
================================================================================
In [26]:
for ticker in prepared_data.keys():
    plot_smi_signals(
        data=prepared_data[ticker],
        ticker=ticker,
        results=results,
        start_date='2023-01-01',
        end_date='2025-12-31',
        strategy_config=strategy_config
    )
================================================================================
SMI STRATEGY SIGNALS FOR GS
Period: 2023-01-01 to 2025-12-31
================================================================================
No description has been provided for this image
SMI Strategy Statistics for GS:
--------------------------------------------------------------------------------
  Total Entry Signals: 3
  Total Exit Signals:  3

  Trade Performance:
    Total P&L: $9,490.08
    Wins: 3 | Losses: 0
    Win Rate: 100.0%
    Avg Win:  $3,163.36
================================================================================

================================================================================
SMI STRATEGY SIGNALS FOR MSFT
Period: 2023-01-01 to 2025-12-31
================================================================================
No description has been provided for this image
SMI Strategy Statistics for MSFT:
--------------------------------------------------------------------------------
  Total Entry Signals: 5
  Total Exit Signals:  4

  Trade Performance:
    Total P&L: $10,309.95
    Wins: 4 | Losses: 0
    Win Rate: 100.0%
    Avg Win:  $2,577.49
================================================================================

================================================================================
SMI STRATEGY SIGNALS FOR HD
Period: 2023-01-01 to 2025-12-31
================================================================================
No description has been provided for this image
SMI Strategy Statistics for HD:
--------------------------------------------------------------------------------
  Total Entry Signals: 5
  Total Exit Signals:  4

  Trade Performance:
    Total P&L: $4,798.24
    Wins: 3 | Losses: 1
    Win Rate: 75.0%
    Avg Win:  $1,864.41
    Avg Loss: $-794.98
================================================================================

================================================================================
SMI STRATEGY SIGNALS FOR V
Period: 2023-01-01 to 2025-12-31
================================================================================
No description has been provided for this image
SMI Strategy Statistics for V:
--------------------------------------------------------------------------------
  Total Entry Signals: 3
  Total Exit Signals:  3

  Trade Performance:
    Total P&L: $5,583.10
    Wins: 3 | Losses: 0
    Win Rate: 100.0%
    Avg Win:  $1,861.03
================================================================================

================================================================================
SMI STRATEGY SIGNALS FOR SHW
Period: 2023-01-01 to 2025-12-31
================================================================================
No description has been provided for this image
SMI Strategy Statistics for SHW:
--------------------------------------------------------------------------------
  Total Entry Signals: 5
  Total Exit Signals:  4

  Trade Performance:
    Total P&L: $5,855.76
    Wins: 4 | Losses: 0
    Win Rate: 100.0%
    Avg Win:  $1,463.94
================================================================================

================================================================================
SMI STRATEGY SIGNALS FOR CAT
Period: 2023-01-01 to 2025-12-31
================================================================================
No description has been provided for this image
SMI Strategy Statistics for CAT:
--------------------------------------------------------------------------------
  Total Entry Signals: 5
  Total Exit Signals:  5

  Trade Performance:
    Total P&L: $18,848.40
    Wins: 5 | Losses: 0
    Win Rate: 100.0%
    Avg Win:  $3,769.68
================================================================================

================================================================================
SMI STRATEGY SIGNALS FOR MCD
Period: 2023-01-01 to 2025-12-31
================================================================================
No description has been provided for this image
SMI Strategy Statistics for MCD:
--------------------------------------------------------------------------------
  Total Entry Signals: 5
  Total Exit Signals:  5

  Trade Performance:
    Total P&L: $3,281.61
    Wins: 4 | Losses: 1
    Win Rate: 80.0%
    Avg Win:  $1,148.10
    Avg Loss: $-1,310.81
================================================================================

================================================================================
SMI STRATEGY SIGNALS FOR UNH
Period: 2023-01-01 to 2025-12-31
================================================================================
No description has been provided for this image
SMI Strategy Statistics for UNH:
--------------------------------------------------------------------------------
  Total Entry Signals: 5
  Total Exit Signals:  4

  Trade Performance:
    Total P&L: $4,514.92
    Wins: 2 | Losses: 2
    Win Rate: 50.0%
    Avg Win:  $4,339.14
    Avg Loss: $-2,081.68
================================================================================

================================================================================
SMI STRATEGY SIGNALS FOR AXP
Period: 2023-01-01 to 2025-12-31
================================================================================
No description has been provided for this image
SMI Strategy Statistics for AXP:
--------------------------------------------------------------------------------
  Total Entry Signals: 3
  Total Exit Signals:  3

  Trade Performance:
    Total P&L: $7,898.03
    Wins: 3 | Losses: 0
    Win Rate: 100.0%
    Avg Win:  $2,632.68
================================================================================