Williams %R
Momentum indicator measuring overbought/oversold levels from -100 to 0 scale.
Export Optimization Code
Advanced Options & Examples
Optimization Output Example
Example output from running the optimization code above on a year of S&P data.
Period: 13
Oversold threshold: -58.0
Overbought threshold: -9.0
Rating: 0.3703933747412007
Best Williams %R values: [-36.785046728971935, -33.246105919003476, -88.27855320426245, ...]
Analysis
The optimized Williams %R parameters generate more nuanced trading signals compared to the default settings. A trading simulation was conducted to evaluate the effectiveness of both parameter sets. Both strategies started with an initial capital of $1000 and invested 20% of the remaining capital on each trade.
Long positions were opened when Williams %R fell below the oversold level and closed when it rose above the overbought level. Short positions were opened when Williams %R rose above the overbought level and closed when it fell below the oversold level.
The results are shown in the tables below. The optimized Williams %R strategy yielded a profit of $4.03, with a $192.18 open position. This outperformed the default Williams %R strategy which resulted in a profit of $0.64 (with a $191.53 open position).
Optimized Trading Simulation
- SideLONG
- Shares0.0341
- Entry$5955.25
- Value$192.18
Default Trading Simulation
- SideLONG
- Shares0.034
- Entry$5955.25
- Value$191.53
Trading Simulation Code
For those who want to run their own simulation to compare results.
use centaur_technical_indicators::momentum_indicators::bulk::{williams_percent_r};
fn simulate_trading(best_indicator: &[f64], best_period: usize, close: &[f64], best_oversold: usize, best_overbought: usize) {
// --- TRADING SIMULATION CODE ---
println!("
--- Trading Simulation ---");
let best_oversold = best_oversold as f64 * -1.0; // Convert back to negative
let best_overbought = best_overbought as f64 * -1.0; // Convert back to negative
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", "WR", "Price", "Shares", "Capital", "P/L");
println!("{}", "-".repeat(95));
for i in 0..best_indicator.len() {
let price_index = i + best_period;
if price_index >= close.len() { break; }
let wr_val = best_indicator[i];
let current_price = close[price_index];
let day = price_index;
// --- Handle Long Position ---
if let Some(long_pos) = open_long.take() {
if wr_val > best_overbought as f64 {
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)", wr_val, current_price, long_pos.shares, capital, profit);
} else {
open_long = Some(long_pos); // Put it back if not selling
}
} else if wr_val < best_oversold as f64 && open_short.is_none() { // Don't buy if short is open
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)", wr_val, current_price, shares_bought, capital, "-");
}
// --- Handle Short Position ---
if let Some(short_pos) = open_short.take() {
if wr_val < best_oversold as f64 {
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)", wr_val, current_price, short_pos.shares, capital, profit);
} else {
open_short = Some(short_pos); // Put it back if not covering
}
} else if wr_val > best_overbought as f64 && 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)", wr_val, 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);
}
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);
}
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
simulate_trading(&best_williams_percent_r, best_period, &close, best_oversold, best_overbought);
println!("
Default Williams %R values for comparison:");
let default_wr = williams_percent_r(&high, &low, &close, 10);
println!("{:?}", default_wr);
simulate_trading(&default_wr, 10, &close, 80, 20);
}