In this example, I create trading rules by comparing values from fitted price curves. Software finds three swings in price data as above. The software creates curves to fit the price swings that sum a least squares regression line with zero or more cosine waves using the Goertzel algorithm. To help curve values be comparable to curve values from different times, the software standardizes them by removing: the y intercept from the least squares regression line the estimated standard deviation of the price data from the least squares regression line the estimated standard deviation of the differences if price values to the least squares regression line For example, using open prices of QQQ for 37 trading days from 20170615 to 20170807, the fitted curve found before the above removals is Code: QQQ_20170807_37_opy = 137.947982788086 + 3.77645492553711 * -0.0476661579206547 * x + 1.5986567735672 * ( 0.541859686374664 * cos(twopi / 22.9808105990718 * x + 3.64079618453979) + 0.210554629564285 * cos(twopi / 13.5704574901945 * x + 5.54141235351562) + 0.13944485783577 * cos(twopi / 106.626021858345 * x + 1.72074127197266) + 0.0950047299265862 * cos(twopi / 2.38496891108685 * x + 6.05813837051392) + 0.0915462970733643 * cos(twopi / 5.50749497583084 * x + 4.27757024765015) ) ; And for 48 trading days from 20231002 to 20231208, the fitted curve found before removals is Code: QQQ_20231208_48_opy = 389.808898925781 + 20.9928169250488 * -0.0383396329932307 * x + 7.348717212677 * ( 0.502204477787018 * cos(twopi / 30.906657620687 * x + 3.87648510932922) + 0.194004282355309 * cos(twopi / 17.810513676591 * x + 5.62850379943848) + 0.111993163824081 * cos(twopi / 8.01638992087503 * x + 0.216300472617149) + 0.0793934985995293 * cos(twopi / 12.8295016279416 * x + 0.930293023586273) + 0.0728166997432709 * cos(twopi / 4.91025367585738 * x + 4.41841173171997) ) ; The curves point backwards in time, so x=0 refers to the current bar, x=1 is for the previous bar, etc. Graphs of these show similar structures But the values of the curves are very different and not very comparable. After the removals, the curve formulas are Code: QQQ_20170807_37_opy = -0.0476661579206547 * x + ( 0.541859686374664 * cos(twopi / 22.9808105990718 * x + 3.64079618453979) + 0.210554629564285 * cos(twopi / 13.5704574901945 * x + 5.54141235351562) + 0.13944485783577 * cos(twopi / 106.626021858345 * x + 1.72074127197266) + 0.0950047299265862 * cos(twopi / 2.38496891108685 * x + 6.05813837051392) + 0.0915462970733643 * cos(twopi / 5.50749497583084 * x + 4.27757024765015) ) ; QQQ_20231208_48_opy = -0.0383396329932307 * x + ( 0.502204477787018 * cos(twopi / 30.906657620687 * x + 3.87648510932922) + 0.194004282355309 * cos(twopi / 17.810513676591 * x + 5.62850379943848) + 0.111993163824081 * cos(twopi / 8.01638992087503 * x + 0.216300472617149) + 0.0793934985995293 * cos(twopi / 12.8295016279416 * x + 0.930293023586273) + 0.0728166997432709 * cos(twopi / 4.91025367585738 * x + 4.41841173171997) ) ; Now the curves have comparable values. The functions for the curves run on multiple ETFs to create values five trading days before the current bar through five trading days after the current bar. Input price data uses values adjusted for splits and dividends on assorted ETFs in the attached tab-separated etf_inputs.csv Training data was for simulated trades entered from 2001-03-30 through 2017-03-22. Evaluation data was for simulated trades entered from 2017-03-23 through 2024-01-25. The simulated trades entered long at the open and exited at the next trading day's open. The simulated trades did not account for slippage and commission. A genetic program rules generator created rules such as for QQQ: Code: if IOOop_AfterStdz2 >= IOOop_AfterStdz3 if IOOop_BeforeStdz0 > IOOop_AfterStdz4 if IOOop_AfterStdz4 < IOOop_BeforeStdz1 if IOOop_AfterStdz1 > IOOop_AfterStdz3 if IOOop_BeforeStdz5 >= IOOop_BeforeStdz3 return 1 if IOOop_BeforeStdz0 > IOOop_AfterStdz4 if IOOop_BeforeStdz0 >= IOOop_AfterStdz4 if IOOop_BeforeStdz5 >= IOOop_BeforeStdz3 if IOOop_BeforeStdz0 > IOOop_BeforeStdz1 return 1 if EWYop_BeforeStdz4 >= EWYop_BeforeStdz3 if EWYop_AfterStdz2 > -0.124356 if EWYop_AfterStdz1 >= -0.598665 if EWYop_BeforeStdz4 < EWYop_AfterStdz3 return 1 if EWYop_BeforeStdz3 <= 0.728926 if EWYop_BeforeStdz3 <= -0.330632 if EWYop_BeforeStdz4 < 1.59147 if EWYop_AfterStdz1 >= -0.728304 if EWYop_AfterStdz2 <= 0.301724 if EWYop_BeforeStdz0 >= EWYop_BeforeStdz1 return 1 if IWNop_BeforeStdz1 >= -1.28908 if IWNop_BeforeStdz3 >= IWNop_AfterStdz4 if IWNop_AfterStdz2 <= IWNop_BeforeStdz4 if IWNop_BeforeStdz1 > 0.563882 return 1 if IWNop_BeforeStdz4 >= IWNop_AfterStdz3 if IWNop_AfterStdz1 < IWNop_BeforeStdz0 if IWNop_BeforeStdz1 > IWNop_AfterStdz4 if IWNop_AfterStdz3 < IWNop_AfterStdz1 if IWNop_BeforeStdz4 > 0.357067 return 1 if XLEop_BeforeStdz2 <= XLEop_BeforeStdz0 if XLEop_BeforeStdz2 < XLEop_BeforeStdz0 if XLEop_BeforeStdz1 < 0.658045 if XLEop_AfterStdz2 >= XLEop_AfterStdz4 if XLEop_AfterStdz4 < 0.123062 return 1 if EWYop_AfterStdz2 < 0.293288 if EWYop_AfterStdz1 >= EWYop_BeforeStdz2 if EWYop_BeforeStdz5 <= EWYop_AfterStdz2 if EWYop_BeforeStdz1 < EWYop_BeforeStdz0 if EWYop_AfterStdz1 > EWYop_BeforeStdz5 return 1 if XLEop_BeforeStdz1 > XLEop_BeforeStdz3 if XLEop_AfterStdz1 >= -0.429701 if XLEop_BeforeStdz2 <= XLEop_AfterStdz2 if XLEop_AfterStdz4 < 0.237824 if XLEop_BeforeStdz2 <= XLEop_AfterStdz2 return 1 For the evaluation period, simulated buy and hold performance was total return 241.72%, compound annual return 19.66%, maximum drawdown 35.12%, daily mean result 0.0714%, daily mean win 0.9900%, daily mean loss 1.1012%, number of trading days 1722, number of daily wins 970, daily win rate 56.33% The simulated performance using the rules for the evaluation period was total return 254.12%, compound annual return 20.28%, maximum drawdown 21.32%, mean result 0.1365%, mean win 1.0127%, mean loss 0.9623%, number of trades 927, number of wins 518, win rate 55.88%