Directional Movement System
The Directional Movement System (DMS), developed by J. Welles Wilder, is a comprehensive trend-following indicator that measures both the direction and strength of price movements. It consists of three key components: the Positive Directional Indicator (+DI), Negative Directional Indicator (-DI), and the Average Directional Index (ADX) with its smoothed version ADXR.
What It Measures
The DMS measures trend direction through +DI and -DI, which track upward and downward price movements respectively. The ADX component measures trend strength on a scale of 0 to 100, with higher values indicating stronger trends. The ADXR is a smoothed version of ADX that helps confirm trend persistence.
When to Use
Use the Directional Movement System when you need to identify both trend direction and strength simultaneously. It's particularly effective for filtering out weak or choppy market conditions by only taking trades when ADX is above a certain threshold (typically 25). This helps avoid false signals during sideways or ranging markets.
Interpretation
Trading signals are generated when +DI crosses above -DI (bullish signal) or when -DI crosses above +DI (bearish signal). The ADX value confirms whether the trend is strong enough to trade: values above 25 indicate a trending market, while values below 20 suggest a weak or non-trending market. Higher ADX values (above 40) indicate very strong trends, though these may be nearing exhaustion.
Default Usage
use rust_ti::trend_indicators::bulk::directional_movement_system;
use rust_ti::ConstantModelType::SimpleMovingAverage;
pub fn main() {
// fetch the data in your preferred way
// let close = vec![...]; // closing prices
// let high = vec![...]; // high prices
// let low = vec![...]; // low prices
let dx = directional_movement_system(&high, &low, &close, 14, SimpleMovingAverage);
println!("{:?}", dx);
}
import pytechnicalindicators as pti
# fetch the data in your preferred way
# close = [...] # closing prices
# high = [...] # high prices
# low = [...] # low prices
dx = pti.trend_indicators.bulk.directional_movement_system(high, low, close, period=14, model='SimpleMovingAverage')
print(dx)
// WASM import
import init, { trend_bulk_directionalMovementSystem, ConstantModelType } from 'https://cdn.jsdelivr.net/npm/ti-engine@latest/dist/web/ti_engine.js';
await init();
// fetch the data in your preferred way
// const close = [...]; // closing prices
// const high = [...]; // high prices
// const low = [...]; // low prices
const dx = trend_bulk_directionalMovementSystem(high, low, close, 14, ConstantModelType["SimpleMovingAverage"]);
console.log(dx);
Optimization
The best way to determine what the best parameters for your indicator are is to build a simple optimization loop that tests all possible parameter combinations between a defined min and max value, and rate the output.
Below is an example of how to do this in Rust.
use rust_ti::trend_indicators::bulk::directional_movement_system;
use rust_ti::ConstantModelType::SimpleMovingAverage;
use rust_ti::chart_trends::{peaks, valleys};
fn proximity_rating(fuzzed_location: &usize, price_location: &usize) -> f64 {
1.0 / (*fuzzed_location as f64 - *price_location as f64).abs()
}
pub fn main() {
// fetch the data in your preferred way
let indicator_loop = Instant::now();
// get buy and sell points, in an ideal world we would buy at the lowest point in the dip and sell at the highest point in the peak
// In the course of a 20-day period (1 month of trading days), we want to find the highest peak and lowest valley within 5 days of each other
let sell_points = peaks(&close, 20, 5)
.into_iter()
.map(|(_, i)| i)
.collect::<Vec<usize>>();
let buy_points = valleys(&close, 20, 5)
.into_iter()
.map(|(_, i)| i)
.collect::<Vec<usize>>();
// Define the ranges for optimization
let max_period = 20;
let min_period = 5; // DMS needs sufficient data
// ADX thresholds for trend strength
let min_adx_threshold = 15; // Below this = weak trend
let max_adx_threshold = 40; // Above this = strong trend
let fuzz_parameter = 5; // Allowable distance from buy/sell points
// Store the best parameters found
let mut best_rating = 0.0;
let mut best_period = 0;
let mut best_model = ConstantModelType::SimpleMovingAverage;
let mut best_adx_threshold = 0.0;
let mut best_indicators = vec![];
let models = vec![
ConstantModelType::SimpleMovingAverage,
ConstantModelType::ExponentialMovingAverage,
ConstantModelType::SmoothedMovingAverage,
ConstantModelType::SimpleMovingMedian,
ConstantModelType::SimpleMovingMode,
];
let total_iterations = (max_period - min_period + 1)
* models.len()
* (max_adx_threshold - min_adx_threshold + 1);
let mut iteration_count = 0;
println!(
"
Running optimization loop with {} total iterations...",
total_iterations
);
for &ma_type in &models {
for period in min_period..=max_period {
for adx_threshold in min_adx_threshold..=max_adx_threshold {
iteration_count += 1;
if iteration_count % (total_iterations / 20).max(1) == 0 {
let next_log_percent = (iteration_count * 100) / total_iterations;
println!("Optimization is {}% complete...", next_log_percent);
}
// Skip if not enough data
let min_length = 3 * period;
if close.len() < min_length {
continue;
}
let indicators = directional_movement_system(
&high,
&low,
&close,
period,
ma_type,
);
let mut rating = vec![];
let mut matched_sell = vec![];
let mut matched_buy = vec![];
// DMS returns: (+DI, -DI, ADX, ADXR)
for i in 1..indicators.len() {
let price_location = i + (3 * period) - 2; // Adjust for lag
if price_location >= close.len() {
break;
}
let (plus_di, minus_di, adx, _adxr) = indicators[i];
let (prev_plus_di, prev_minus_di, _prev_adx, _prev_adxr) = indicators[i - 1];
// Only trade when ADX shows a trend exists
if adx >= adx_threshold as f64 {
// Buy signal: +DI crosses above -DI (uptrend forming)
if plus_di > minus_di && prev_plus_di <= prev_minus_di {
if buy_points.contains(&price_location) {
rating.push(1.0);
matched_buy.push(price_location);
} else if sell_points.contains(&price_location) {
rating.push(-1.0);
} else {
let mut found_buy = false;
for fuzzed_location in (price_location.saturating_sub(fuzz_parameter))
..=(price_location + fuzz_parameter)
{
if buy_points.contains(&fuzzed_location) {
rating.push(proximity_rating(&fuzzed_location, &price_location));
matched_buy.push(fuzzed_location);
found_buy = true;
}
if sell_points.contains(&fuzzed_location) {
if !matched_buy.contains(&fuzzed_location) {
rating.push(-proximity_rating(&fuzzed_location, &price_location));
}
}
}
if !found_buy {
rating.push(0.0);
}
}
}
// Sell signal: -DI crosses above +DI (downtrend forming)
else if minus_di > plus_di && prev_minus_di <= prev_plus_di {
if sell_points.contains(&price_location) {
rating.push(1.0);
matched_sell.push(price_location);
} else if buy_points.contains(&price_location) {
rating.push(-1.0);
} else {
let mut found_sell = false;
for fuzzed_location in (price_location.saturating_sub(fuzz_parameter))
..=(price_location + fuzz_parameter)
{
if sell_points.contains(&fuzzed_location) {
rating.push(proximity_rating(&fuzzed_location, &price_location));
matched_sell.push(fuzzed_location);
found_sell = true;
}
if buy_points.contains(&fuzzed_location) {
if !matched_sell.contains(&fuzzed_location) {
rating.push(-proximity_rating(&fuzzed_location, &price_location));
}
}
}
if !found_sell {
rating.push(0.0);
}
}
}
}
}
// Look for any missed buy/sell points and penalize
for missed_sell in sell_points.iter() {
if !matched_sell.contains(missed_sell) {
rating.push(-1.0);
}
}
for missed_buy in buy_points.iter() {
if !matched_buy.contains(missed_buy) {
rating.push(-1.0);
}
}
if !rating.is_empty() {
let total_rating: f64 = rating.iter().sum::<f64>() / (rating.len() as f64);
if total_rating > best_rating {
best_rating = total_rating;
best_period = period;
best_model = ma_type;
best_adx_threshold = adx_threshold as f64;
best_indicators = indicators.clone();
}
}
}
}
}
println!(
"Indicators optimization loop took {} ms to run",
indicator_loop.elapsed().as_millis()
);
println!("
Best Directional Movement System parameters found:");
println!("period = {}", best_period);
println!("model = {:?}", best_model);
println!("adx_threshold = {}", best_adx_threshold);
println!("Rating: {}", best_rating);
println!("Best Indicator values: {:?}", best_indicators);
}
Optimization Output
Below is an example output from the optimization code above run on a year of S&P data.
Best Directional Movement System parameters found:
period = 5
model = ExponentialMovingAverage
adx_threshold = 30
Rating: 0.09324324324324322
Best Indicator values: [(33.8994519250288, 41.7957913255289, 20.458357588402144, 26.497885766558245), (16.144945735043468, 49.367656292043584, 32.416657092614244, 27.7809871335861), (7.589617730179923, 40.93410679756024, 47.42070636594339, 33.29236677142793), (7.960528881781621, 36.45227973009842, 55.31128591983033, 41.39491025071076), (6.892243271536098, 28.663087157667267, 58.207367953359444, 39.332862770880794), (0.0, 35.47830701870767, 76.66618205347048, 54.54141957304236), (12.634490074253465, 22.219275647825633, 59.10402336386252, 53.26236486490295), (12.53382441370996, 33.62898376428195, 53.471358493336105, 54.39132220658322), (9.268523150463952, 40.28403090434119, 56.43237384248714, 57.319870897923295), (9.9315068493149, 36.03930911256711, 56.327897711304765, 66.49703988238763), (9.09264409182602, 35.85800752494698, 55.354717879305106, 57.22937062158381), (0.0, 39.272393412520834, 73.90156547403906, 63.68646198368758), (0.0, 44.501455708973296, 85.34620357511754, 70.88928870880234), (6.598089150770561, 32.92719464695887, 79.30524374821015, 67.81657072975746), (18.50651455240794, 27.96071822130017, 57.809688353961356, 56.58220311663323), (25.13824966998443, 19.155160726390445, 40.715154193419906, 57.30835983372948), (24.07325156308696, 35.795551607503015, 29.604629312286455, 57.475416443702), (47.38565740017098, 21.004056417699637, 29.48969053131903, 54.397467139764586), (49.71167399940191, 24.10832514629909, 29.60680892891196, 43.708248641436654), (30.252036562511925, 41.98569625578475, 24.94483447264934, 32.82999433303462), (21.79130204189867, 43.63564041368357, 28.763982414131384, 29.18430586320892), (22.159301580881102, 26.157009471803576, 21.362450876411312, 25.42607070386517), (25.51434712886825, 26.47650892217396, 13.001968996139441, 21.3043889625257), (36.313714920998656, 26.142910083212517, 13.166057756855132, 19.055446114752236), (52.36210734603019, 10.000824470277978, 34.032485220693836, 31.39823381741261), (68.29032258064525, 8.333333333333375, 51.03908186597895, 36.20076637119513), (94.73319473319492, 8.253008253008504, 65.84370283787004, 39.42283591700474), (72.15701876533392, 8.68642662953412, 73.94134819677105, 43.553702976813085), (48.39152750668664, 9.470107713439146, 74.29363225853312, 54.163058739613476), (40.69588491134119, 8.765473402475946, 70.87744614552726, 60.95826400575311), (71.11020598161272, 0.0, 81.68455755780681, 73.76413019783843), (67.71516208576348, 0.0, 88.59993723519781, 81.27064271598442), (55.78880809358158, 7.967119822952521, 83.89201430176456, 79.09282328014885), (69.7554365361033, 8.130605923726808, 82.90126565641603, 76.88935590097165), (63.73884452794736, 11.49433000067049, 78.66238930141046, 80.17347342960863), (24.400871459694624, 21.053013798111046, 50.2137740704352, 69.4068556528165), (10.27670028445821, 30.033617791569156, 47.235613544484764, 65.56381392312466), (9.704517704517695, 22.207570207569987, 42.739102762084904, 62.82018420925047), (1.9644915840443842, 20.968411344247016, 56.30458580205591, 67.48348755173319), (2.0288612658952383, 28.16116588084026, 67.25478277293975, 58.73427842168748), (2.019818880091148, 41.662320421032824, 79.30270116420041, 63.26915735434259), (1.9849960393272648, 41.568426447975654, 85.28018086622833, 64.00964181415662), (10.451954724251435, 35.807176687806155, 75.91410380093713, 66.10934480149652), (8.53147400994937, 34.94457283873244, 69.74253555819541, 68.49865916556757), (26.39611278401304, 24.301943607993362, 43.70490061627725, 61.503800890238836), (30.353585657370502, 10.82100170745592, 42.759778580259116, 64.01997972324372), (41.512149690965785, 0.0, 62.300653606989144, 69.10737870396314), (37.63196842624559, 0.0, 77.1515242775712, 73.4470299178833), (41.11687199733615, 0.0, 86.75172223188969, 65.22831142408347), (53.37017150602104, 0.0, 96.0142896625142, 69.38703412138666), (46.1127974150592, 3.240967394497167, 94.95818467569096, 78.62941914134005), (43.06634945810214, 3.4998678297646855, 90.8682924763054, 84.0099083769383), (61.69611624360903, 2.9688761323884996, 90.38722513301008, 88.56947368244988), (66.87183200579253, 3.424019861384015, 89.8517629978761, 92.93302633019515), (28.261611189628947, 10.906964277011133, 71.8549530908256, 83.40656888325827), (30.19526165061153, 10.044259307471936, 62.73607018996641, 76.8021813331359), (34.06625339015841, 9.342309182487368, 59.393425429766054, 74.89032528138807), (8.639951426836417, 12.337583485124306, 41.77113851112179, 65.81145075449894), (12.780509863772366, 11.486066361426559, 25.332357937189872, 48.593655514007736), (20.158444843884933, 3.0357499500701493, 42.988069766557956, 52.862069978262184), (32.125222702977574, 0.5243064392973996, 63.282830887305416, 61.33812815853574), (27.231759656652265, 3.015021459227386, 70.04465519129869, 55.90789685121024), (38.27954954102358, 2.171855777420329, 80.07177657613238, 52.702067256661124), (47.29143072857672, 2.1659116647792205, 88.13780034520302, 65.56293505588049), (54.36991422916185, 2.0189135693864695, 90.66629195628302, 76.97456142179422), (59.03209257253598, 2.616428204982136, 90.68138255291913, 80.36301887210891), (70.11791411816657, 0.0, 94.79541889348151, 87.43359773480694), (80.4325569490659, 0.0, 97.0731215004587, 92.60546092283086), (50.49645390070907, 5.20619910690824, 91.3156081130568, 90.99095003466991), (36.06126308991119, 4.571665820916083, 85.93408660074499, 88.30773457683206), (30.910774681913345, 3.9901755516185355, 82.27399532490055, 88.53470710919103), (27.34086671993317, 3.7704980405584685, 78.87775271170831, 87.9754371060835), (10.820480203530096, 25.488948958498973, 63.038309370196586, 77.17695874162669), (9.830973707021212, 41.97125108350201, 61.73314083869237, 73.83361371971868), (5.183307618837614, 52.156344133421975, 68.68591624562241, 75.47995578526148), (5.965180792041413, 54.32561698871293, 72.68351975749285, 75.78063623460058), (10.500113147771067, 53.54906841668582, 70.42762013866043, 66.73296475442851), (8.82352941176474, 69.31097870182562, 74.62785167804942, 68.1804962583709), (8.568262957035596, 57.158685214822015, 74.99521399955844, 71.84056512259042), (8.643815201192274, 49.661574764033595, 72.8618231276237, 72.77267144255828), (4.622600752947113, 49.35814355366265, 76.33346226365457, 73.3805412011575), (0.0, 53.70978854205285, 85.87982008829934, 80.25383588317439), (17.877722715224166, 20.678229802927092, 56.12803680260759, 65.56162540108302), (15.428415137500656, 31.34666069918932, 46.7456431380258, 59.803733132824746), (14.551381267295163, 54.98334974907356, 49.928989071646335, 63.131225667650455), (12.05439533754251, 81.0548810101989, 57.545169211963824, 71.71249465013159), (22.628579804679607, 68.61681660772844, 52.65625552571382, 54.39214616416071), (12.798703278714822, 60.10014283192364, 59.645978353007145, 53.19581074551647), (14.201762977473061, 51.84934556139253, 59.9242114875625, 54.926600279604415), (20.536910254502235, 34.00245572257771, 46.48799600013843, 52.01658260605113), (27.151440183778107, 0.0, 65.63429780361484, 59.14527666466433), (32.2192853148987, 0.0, 79.59692596934703, 69.62145216117709), (44.227434815323875, 0.0, 88.1730455637284, 74.04862852564546), (85.03838808994882, 0.0, 94.28925795660814, 70.38862697837328), (86.30555673472841, 0.0, 100.00000000000001, 82.81714890180743), (96.2647178237922, 0.0, 100.00000000000001, 89.79846298467352), (82.96586861445523, 0.0, 100.00000000000001, 94.0865227818642), (78.60290365972445, 0.0, 100.00000000000001, 97.14462897830407), (34.20240544389949, 12.114258585219083, 79.91868598889896, 89.95934299444949), (25.9344851619531, 11.232163163493535, 63.40954150201241, 81.70477075100621), (12.930806189233223, 11.58400484243173, 39.32674014826127, 69.66337007413064), (8.227848101265876, 14.78540933263436, 32.10126248239162, 66.05063124119582), (3.35627932463446, 24.661803486420784, 45.5370467446309, 62.72786636676493), (10.888042203986025, 15.163393903868815, 34.24722356897068, 48.82838253549154), (11.942073597313668, 14.478102700433856, 24.516578920974887, 31.92165953461808), (6.8340071877807995, 33.369833782569664, 41.404383059434096, 36.75282277091286), (6.617906957774858, 30.087821854862796, 50.70840267672728, 48.122724710679094), (6.623669959452508, 27.55326965466567, 53.47024604146564, 43.85873480521816), (1.0422561780796227, 42.24910394265227, 71.35738056072708, 47.93697974085099), (0.0, 44.325969174438946, 85.47496347523784, 63.43967326733597), (4.022415209482464, 29.840981105504046, 82.91540305915962, 66.81190286794345), (17.127382236605545, 22.707659115426097, 57.42220032346141, 55.44622318246353), (26.216781800589605, 17.53335883530976, 42.80494119562144, 57.08116087817426), (42.338103457876414, 0.0, 62.11337701566748, 73.79417024545266), (44.59661573653195, 0.0, 74.74225134377834, 78.82882720146898), (50.68509193113952, 0.0, 84.36247225041882, 70.89233628694012), (49.30359996955614, 0.0, 93.92210503274491, 68.36352311418318), (54.380516508662666, 0.0, 100.00000000000001, 81.05668850783374), (38.89132602193387, 4.757726819541475, 91.63131469497402, 83.18678301937618), (44.82256521196444, 4.963181761451219, 86.7668896108459, 85.56468093063236), (37.71028462307849, 5.424206601800633, 81.52310816505602, 87.72260659890046), (40.008034894398286, 6.846877869605354, 76.46266242562893, 88.23133121281447), (30.11242432977797, 6.878062842317882, 70.03237231167613, 80.83184350332508), (30.86149846372027, 0.0, 81.1236306347834, 83.94526012281464), (20.03242573077125, 11.353774259692194, 60.64996465752015, 71.08653641128808), (12.828723532001884, 18.43653684505487, 43.5349113685373, 59.99878689708312), (9.884048181920585, 20.015760441292425, 38.453629026865784, 54.243000669270955), (0.0, 20.394585914200565, 60.849062222738155, 70.98634642876078), (12.49818498620598, 19.3625671555104, 43.78155826857283, 52.21576146304649), (12.926115032287255, 17.085898783600904, 33.1106733190368, 38.32279234378705), (22.61858767975984, 9.984975316591255, 36.04232918367878, 37.24797910527228), (38.43161856963636, 6.683396068590381, 49.329951502632696, 55.089506862685425), (40.3097034567471, 7.010001754693642, 54.846087313053154, 49.31382279081299), (37.64580179859353, 7.114237378683838, 61.66041580579021, 47.3855445624135), (62.55082488199306, 0.0, 78.79488597244296, 57.41860757806087), (48.80706051264735, 10.594991765550525, 75.26562097808485, 62.297786240358775), (34.218897002822494, 11.240535818287775, 66.0238664416011, 60.43497687732713), (46.86134544667941, 10.854423534501443, 64.40778256919609, 63.03409918749315), (38.64490650425797, 11.93795498881859, 59.75881075751813, 69.27684836498054), (15.176280528366176, 22.051696284329395, 41.87368405518964, 58.56965251663725), (17.130598015553485, 13.37624027889488, 29.38816665949978, 47.706016550550444), (14.47147841058378, 37.9230664673098, 34.21940934842673, 49.313595958811405), (0.0, 38.86154703315044, 58.047738797765504, 58.90327477764181), (17.853743654822093, 33.193210659898305, 47.56505273346098, 44.719368394325315), (19.5849828163742, 27.1370774785748, 36.981491834639066, 33.18482924706942), (19.32023002317374, 34.19019826624321, 34.69997588280116, 34.459692615613946), (24.355773072313013, 10.442469037155352, 36.21942783157048, 47.13358331466799), (20.334389593061832, 53.92344896597725, 36.455325795905665, 42.01018926468332), (1.6570923552805996, 57.14980114891729, 59.009604153583496, 47.99554799411128), (1.49176545469011, 62.07335507995859, 75.10939892674658, 54.90468740477387), (17.09376167351488, 50.47814717967882, 67.63429707848518, 51.92686245502783), (65.05730228206758, 45.15018878011298, 50.00253900291025, 43.22893239940795), (92.01715760704533, 10.138931065897387, 61.81689394673143, 60.41324905015746), (108.48176760196957, 10.691270063643334, 67.94164478446204, 71.52552185560431), (117.13157559546572, 0.0, 78.86504144199212, 73.24966926023865), (104.39226889255909, 11.877873499084764, 80.62434025907298, 65.31343963099161), (41.61283707577151, 13.641956321131762, 72.26935796215538, 67.04312595444341), (15.9358186925678, 23.920392324905787, 51.818314875511724, 59.87997982998688), (2.0536657511094742, 58.952038875976754, 66.20132764840358, 72.53318454519786), (0.0, 56.208702659145615, 77.4675517656024, 79.04594601233768), (5.773126266036586, 42.35126416085215, 76.80084293700588, 74.53510044958063), (5.411202137758988, 39.69621321331869, 77.81971836697551, 64.81901662124362), (18.791077152200007, 28.902127108492188, 59.005524450568835, 62.6034260494862), (24.580152671755734, 0.0, 73.01070733100632, 75.23912954830436), (39.848845636194376, 0.0, 82.0071382206709, 79.40399057883839), (41.12681586179837, 0.0, 89.21765066610425, 83.51868451653988), (45.56725247955471, 3.2190708195582793, 88.95866111403785, 73.98209278230334), (44.384310087109164, 3.8142363795683405, 90.54682959388477, 81.77876846244554), (46.77770520357086, 4.0278684955369775, 87.61098197372684, 84.80906009719887), (26.16790327012935, 6.416597966474429, 76.62154172344266, 82.91959619477345), (50.42141794744721, 6.615199376726519, 75.50961104258705, 82.23413607831245), (59.50607604860873, 1.5209721677770263, 82.42668811958534, 86.48675885673505), (53.38612016767602, 1.8071727992543953, 86.57068111136279, 87.0908315425448), (37.1375407039854, 25.810203132267738, 60.36802955253388, 68.49478563798827), (32.85097044098513, 34.35292503943426, 38.03901088298306, 56.77431096278505), (24.968221047702098, 33.511741486585166, 27.085004571435253, 54.75584634551029), (20.87040000000025, 37.75359999999906, 24.308951631028414, 55.439816371195604), (15.18400625453764, 41.58708884793575, 29.335492086347205, 44.85176081944054), (20.45509561849456, 26.095376422173373, 23.298655561290346, 30.6688332221367), (22.20470371830254, 31.75009854158445, 22.210965370572968, 24.64798497100411), (2.0485960453727374, 66.85985247629073, 50.17486280299411, 37.24190721701126), (1.8175318961723679, 57.344368675758915, 68.02395532431122, 48.67972370532921), (11.293372713171603, 41.0237341435948, 64.81338152635509, 44.05601854382272), (8.945527236381768, 37.11029100834195, 66.0724012631777, 44.14168331687533), (19.47953920529086, 30.393760221411366, 51.55483919028811, 50.86485099664111), (29.81335084484614, 0.43442022579755146, 66.90112686370227, 67.46254109400675), (29.532149111833743, 18.618964223167318, 48.5566402821536, 56.68501090425434), (22.23466119351919, 42.97975557421382, 41.70996151695237, 53.89118139006503), (23.307363823059518, 45.151090393454254, 36.9641951140607, 44.2595171521744), (2.7285322475753127, 50.28416320298573, 57.97344748024081, 62.43728717197154), (3.8309541092023434, 47.81030728284528, 66.4318653365553, 57.49425280935445), (23.88959919951072, 28.74534437711932, 46.683631939299595, 44.19679672812598), (21.58734145422572, 27.366570388044682, 34.046252205621016, 35.50522365984086), (22.0170090680874, 31.907372303909032, 28.12538222859319, 43.049414854416995), (23.394757614523947, 41.43553172378125, 24.8980174048394, 45.66494137069735), (19.273675438832232, 50.18682293486726, 29.378150514827162, 38.03089122706338), (8.824308572639705, 49.45957401716636, 45.883446644106975, 39.964849424864), (37.784935579781944, 36.35096630327052, 30.734694205917055, 29.430038217255124), (41.509185487588205, 33.40686113123056, 23.71440504420912, 24.30621122452426), (68.43494120842922, 13.37653770033743, 40.238038468112116, 34.80809449146964), (89.82364357438232, 0.0, 62.96414474997182, 54.423795697039395), (117.25922131147568, 0.0, 76.84019466355028, 53.787444434733665), (86.00021755683682, 0.0, 89.51764034468259, 56.61602269444585), (86.35187239211956, 0.0, 97.52031224498702, 68.87917535654957), (54.679802955665046, 60.76499565343353, 63.63487123850846, 63.299507994240145), (55.31072047536514, 51.91879178014345, 38.58228026353954, 57.711237463544904), (31.674952341793393, 47.03023959929721, 28.15580209658885, 58.836721220635724), (32.06439058906805, 44.514417123650965, 19.95638700695865, 58.73834962597284), (34.6498994567664, 37.762237762237724, 9.89894314595062, 36.76690719222954), (30.611974333138885, 28.37142705626562, 7.791059511690068, 23.1866698876148), (22.483086801972124, 30.673087948629814, 10.948829703595269, 19.55231590009206), (27.851847675959036, 30.160385601939403, 7.840574810039135, 13.898480908498893), (28.898103909372185, 32.15240842573412, 6.4514808189985215, 8.17521198247457), (19.09727578440748, 41.301329713386785, 18.196335261401636, 12.993697386545852), (24.325067787951436, 10.559201477580876, 27.087598444005028, 19.01821407380015), (18.77111486486492, 11.347128378378411, 26.742099947153832, 17.291337378596484), (9.987551867219834, 27.473029045643273, 35.54555704713463, 20.998518933066574), (21.059460260972582, 24.54403914590755, 26.36085302681893, 22.278594144110283), (32.34432762014494, 18.8871285227328, 25.799163323105944, 26.443380883555484), (32.100824319816994, 22.60923428789243, 21.864548486033097, 24.303324216593467), (38.76820131002312, 22.35992648791273, 23.63470294519106, 29.590129996162844), (40.70354245002974, 17.153176330892055, 29.022759489405466, 27.691806258112198), (11.698704727332965, 45.664549129609306, 41.692845513619474, 33.74600441836271), (6.172197371614903, 49.04122889143163, 56.27318558065797, 39.06886703334553), (5.1981426860964906, 59.18289869462379, 68.82802933784869, 46.23136614151987), (4.479753704863318, 52.875182634105734, 76.92026588679686, 52.97151268810116), (3.66660972067996, 53.388998035363635, 82.67706727951358, 62.18495639656653), (3.5492806350256525, 40.32991566065831, 84.30301466757348, 70.28810012411573), (2.8948610736444693, 32.222821688697046, 84.33659456445822, 76.58231195115346), (2.6767479928287594, 31.194948943799126, 84.3066545710812, 80.61346022893903), (0.0, 29.31610407876225, 90.32746509765481, 86.5022661885842), (0.0, 24.209600518446482, 94.20138790093273, 89.2522012842531), (0.0, 28.166451552708132, 96.9520790000044, 90.6443367822313), (0.0, 42.12625566279301, 98.80149995735536, 91.55407726421828), (0.0, 36.593459222769695, 100.00000000000001, 95.16373254882741), (1.0471111111110094, 38.03377777777793, 97.94287399591218, 96.07213094842245), (1.0608980709305682, 40.467227435652866, 96.66719296287177, 96.80963598143808), (10.267556598511256, 34.49191175056278, 80.16588253857785, 89.4836912479666)]
Interactive Chart
To better illustrate how the indicator performs with different parameters, an interactive chart is provided below comparing default parameters (blue) with optimized parameters (green).
Analysis
The optimized Directional Movement System parameters (period=5, EMA, ADX threshold=30) generated more trading signals compared to the default parameters (period=14, SMA, ADX threshold=25). The trading simulation shows that the optimized strategy achieved a profit of $11.63 with 22 trades, while the default strategy resulted in a loss of $-31.07 with only 10 trades. The higher ADX threshold in the optimized parameters helps filter out weaker trends, and the shorter period with EMA provides more responsive signals, leading to better overall performance in this simulation.
Optimized trading simulation
- SideSHORT
- Shares0.033
- Entry$6051.97
- Value$6051.97
Default trading simulation
Trading simulation code
For those you want to run their own simulation to compare results
fn simulate_trading(
best_indicator: &[(f64, f64, f64, f64)],
best_period: usize,
close: &[f64],
adx_threshold: 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} | {:<8} | {:<8} | {:<8} | {:<10} | {:<12} | {:<15} | {:<10}",
"Day", "Event", "+DI", "-DI", "ADX", "Price", "Shares", "Capital", "P/L"
);
println!("{}", "-".repeat(115));
for i in 1..best_indicator.len() {
let price_location = i + (3 * best_period) - 2;
if price_location >= close.len() {
break;
}
let (plus_di, minus_di, adx, _adxr) = best_indicator[i];
let (prev_plus_di, prev_minus_di, _prev_adx, _prev_adxr) = best_indicator[i - 1];
let current_price = close[price_location];
let day = price_location;
// Only trade when trend is strong enough (ADX above threshold)
if adx < adx_threshold {
// Keep existing positions but don't open new ones
if let Some(pos) = open_long.as_ref() {
open_long = Some(Position {
entry_price: pos.entry_price,
shares: pos.shares,
});
}
if let Some(pos) = open_short.as_ref() {
open_short = Some(Position {
entry_price: pos.entry_price,
shares: pos.shares,
});
}
continue;
}
// --- Handle Long Position ---
if let Some(long_pos) = open_long.take() {
// Sell signal: -DI crosses above +DI
if minus_di > plus_di && prev_minus_di <= prev_plus_di {
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} | {:<8.2} | {:<8.2} | {:<8.2} | ${:<9.2} | {:<12.4} | ${:<14.2} | ${:<9.2}",
day,
"Sell (Close Long)",
plus_di,
minus_di,
adx,
current_price,
long_pos.shares,
capital,
profit
);
} else {
open_long = Some(long_pos); // Put it back if not selling
}
} else if plus_di > minus_di && prev_plus_di <= prev_minus_di && open_short.is_none() {
// Buy signal: +DI crosses above -DI
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} | {:<8.2} | {:<8.2} | {:<8.2} | ${:<9.2} | {:<12.4} | ${:<14.2} | {}",
day,
"Buy (Open Long)",
plus_di,
minus_di,
adx,
current_price,
shares_bought,
capital,
"-"
);
}
// --- Handle Short Position ---
if let Some(short_pos) = open_short.take() {
// Cover signal: +DI crosses above -DI
if plus_di > minus_di && prev_plus_di <= prev_minus_di {
let cost_to_cover = short_pos.shares * current_price;
let profit = (short_pos.shares * short_pos.entry_price) - cost_to_cover;
capital += profit;
println!(
"{:<5} | {:<19} | {:<8.2} | {:<8.2} | {:<8.2} | ${:<9.2} | {:<12.4} | ${:<14.2} | ${:<9.2}",
day,
"Cover (Close Short)",
plus_di,
minus_di,
adx,
current_price,
short_pos.shares,
capital,
profit
);
} else {
open_short = Some(short_pos); // Put it back if not covering
}
} else if minus_di > plus_di && prev_minus_di <= prev_plus_di && open_long.is_none() {
// Short signal: -DI crosses above +DI
let short_value = capital * investment_pct;
let shares_shorted = short_value / current_price;
open_short = Some(Position {
entry_price: current_price,
shares: shares_shorted,
});
println!(
"{:<5} | {:<19} | {:<8.2} | {:<8.2} | {:<8.2} | ${:<9.2} | {:<12.4} | ${:<14.2} | {}",
day,
"Short (Open Short)",
plus_di,
minus_di,
adx,
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
// Run trading simulation with optimized parameters
simulate_trading(&best_indicators, best_period, &close, best_adx_threshold);
// Compare with default parameters (typically period=14, SimpleMovingAverage, ADX threshold=25)
println!("
Default Indicator values for comparison:");
let default_dms = directional_movement_system(
&high,
&low,
&close,
14,
ConstantModelType::SimpleMovingAverage,
);
println!("{:?}", default_dms);
simulate_trading(&default_dms, 14, &close, 25.0);
}