Supertrend indicator
Trend-following indicator that provides buy and sell signals based on ATR volatility.
Export Optimization Code
Advanced Options & Examples
Optimization Output Example
Example output from running the optimization code above on a year of S&P data.
Best Indicator parameters found:
period = 3
model = ExponentialMovingAverage
constant_multiplier = 0.58
Rating: 0.10472222222222219
Best Indicator values: [5160.410314285715, 5166.1638571428575, 5207.569342857142, ...]
Analysis
The optimized Supertrend indicator (3-period EMA with 0.58 multiplier) generated significantly more trading signals compared to the default 10-period SMA with 3.0 multiplier configuration. A trading simulation was conducted with both parameter sets, starting with $1000 initial capital and investing 20% per trade. Long positions were opened when price crossed above the Supertrend line and closed when price crossed below, while short positions were opened when price crossed below and covered when price crossed above.
The optimized parameters demonstrated superior performance with a final profit of $25.19 (including a $12.60 open short position), substantially outperforming the default configuration which resulted in a loss of $20.65 (including a $15.00 open short position). The more responsive 3-period EMA with lower multiplier provided better trend-following signals that captured price movements more effectively in this dataset, generating 59 trades versus 46 trades with the default parameters.
Optimized Trading Simulation
- SideSHORT
- Shares0.03367914400016023
- Entry$6013.13
- Value$12.60
Default Trading Simulation
- SideSHORT
- Shares0.03154228882259018
- Entry$6114.63
- Value$15.00
Trading Simulation Code
For those who want to run their own simulation to compare results.
use centaur_technical_indicators::candle_indicators::bulk::supertrend;
use centaur_technical_indicators::ConstantModelType;
fn chart_simulate_trading(best_indicator: &[f64], best_period: usize, close: &[f64]) {
println!("
--- Trading Simulation ---");
let initial_capital = 1000.0;
let mut capital = initial_capital;
let investment_pct = 0.20;
struct Position {
entry_price: f64,
shares: f64,
}
let mut open_long: Option<Position> = None;
let mut open_short: Option<Position> = None;
// Print table header
println!("{:<5} | {:<19} | {:<10} | {:<10} | {:<12} | {:<15} | {:<10}",
"Day", "Event", "Indicator", "Price", "Shares", "Capital", "P/L");
println!("{}", "-".repeat(95));
for i in 0..best_indicator.len() {
let price_index = i + best_period + 1;
if price_index >= close.len() { break; }
let current_price = close[price_index];
let day = price_index;
// --- Handle Long Position ---
if let Some(long_pos) = open_long.take() {
if current_price < best_indicator[i] {
let sale_value = long_pos.shares * current_price;
let profit = sale_value - (long_pos.shares * long_pos.entry_price);
capital += sale_value;
println!("{:<5} | {:<19} | {:<10.2} | ${:<9.2} | {:<12.4} | ${:<14.2} | ${:<9.2}",
day, "Sell (Close Long)", best_indicator[i], current_price, long_pos.shares, capital, profit);
} else {
open_long = Some(long_pos); // Put it back if not selling
}
} else if current_price > best_indicator[i] && open_short.is_none() {
let investment = capital * investment_pct;
let shares_bought = investment / current_price;
open_long = Some(Position { entry_price: current_price, shares: shares_bought });
capital -= investment;
println!("{:<5} | {:<19} | {:<10.2} | ${:<9.2} | {:<12.4} | ${:<14.2} | {}",
day, "Buy (Open Long)", best_indicator[i], current_price, shares_bought, capital, "-");
}
// --- Handle Short Position ---
if let Some(short_pos) = open_short.take() {
if current_price > best_indicator[i] {
let cost_to_cover = short_pos.shares * current_price;
let profit = (short_pos.shares * short_pos.entry_price) - cost_to_cover;
capital += profit; // Add profit to capital
println!("{:<5} | {:<19} | {:<10.2} | ${:<9.2} | {:<12.4} | ${:<14.2} | ${:<9.2}",
day, "Cover (Close Short)", best_indicator[i], current_price, short_pos.shares, capital, profit);
} else {
open_short = Some(short_pos); // Put it back if not covering
}
} else if current_price < best_indicator[i] && open_long.is_none() { // Don't short if long is open
let short_value = capital * investment_pct;
let shares_shorted = short_value / current_price;
open_short = Some(Position { entry_price: current_price, shares: shares_shorted });
// Capital doesn't change when opening a short, it's held as collateral
println!("{:<5} | {:<19} | {:<10.2} | ${:<9.2} | {:<12.4} | ${:<14.2} | {}",
day, "Short (Open Short)", best_indicator[i], current_price, shares_shorted, capital, "-");
}
}
println!("
--- Final Results ---");
if let Some(pos) = open_long {
println!("Simulation ended with an OPEN LONG position:");
println!(" - Shares: {:.4}", pos.shares);
println!(" - Entry Price: ${:.2}", pos.entry_price);
let last_price = close.last().unwrap_or(&0.0);
let current_value = pos.shares * last_price;
capital += current_value;
println!(" - Position value at last price (${:.2}): ${:.2}", last_price, current_value);
println!("{{ position = "LONG", shares = {}, entry_price = "${:.2}", position_value_at_last_price = "${:.2}" }}", pos.shares, pos.entry_price, current_value);
}
if let Some(pos) = open_short {
println!("Simulation ended with an OPEN SHORT position:");
println!(" - Shares: {:.4}", pos.shares);
println!(" - Entry Price: ${:.2}", pos.entry_price);
let last_price = close.last().unwrap_or(&0.0);
let cost_to_cover = pos.shares * last_price;
let pnl = (pos.shares * pos.entry_price) - cost_to_cover;
capital += pnl;
println!(" - Unrealized P/L at last price (${:.2}): ${:.2}", last_price, pnl);
println!("{{ position = "SHORT", shares = {}, entry_price = "${:.2}", position_value_at_last_price = "${:.2}" }}", pos.shares, pos.entry_price, pnl);
}
let final_pnl = capital - initial_capital;
println!("
Initial Capital: ${:.2}", initial_capital);
println!("final_capital = "${:.2}"", capital);
println!("total_P_L = "${:.2}"", final_pnl);
}
fn main() {
// Fetch data and perform optimization as shown in the optimization code above
chart_simulate_trading(&best_indicators, best_period, &close);
let default_period = 10;
println!("
Default Indicator values for comparison:");
let default_dc = supertrend(&high, &low, &close, ConstantModelType::SimpleMovingAverage, 3.0, default_period);
println!("{:?}", default_dc);
chart_simulate_trading(&default_dc, default_period, &close);
}