Creating Trading Rules With Intermarket Relationships

Here is an example of rules created through genetic programming to simulate trades in one asset with data from other assets.

This example uses daily close values to simulate trades entering long at the next trading day's close value of NASDAQ 100 with and exiting at the close the next trading day.

The inputs are close values for the current trading day (suffix raw000), and close values for the previous 21 trading days (suffixes raw001 through raw021) downloaded from Yahoo finance for the following:
  • irxrawNNN -- 13 Week Treasury Bill (^IRX)
  • fvxrawNNN -- Treasury Yield 5 Years (^FVX)
  • tnxrawNNN -- Treasury Yield 10 Years (^TNX)
  • tyxrawNNN -- Treasury Yield 30 Years (^TYX)
  • aordrawNNN -- ALL ORDINARIES (^AORD)
  • asx200rawNNN -- S&P/ASX 200 (^AXJO)
  • jkserawNNN -- IDX COMPOSITE (^JKSE)
  • hsirawNNN -- HANG SENG INDEX (^HSI)
  • vixrawNNN -- CBOE Volatility Index (^VIX)

The software supports the interest rates, equities, and volatility as separate types as in this post, and operations between different types result in undef values which evaluate false in the if statements.

In the following generated pseudocode rules, "return 1" means the trade would be entered on the close of the next trading day. Each block ending with a "return 1" is an individual rule from a separate run of the rule generating software.
Code:
# types_for_runtime_checking ^(?:vix)raw\d ^(?:fvx|irx|tnx|tyx)raw\d ^(?:aord|asx200|bfx|bvsp|cac40|dji|djt|dju|gdaxi|gspc|gsptse|hsi|ixic|jkse|mid|mxx|n225|nd100|ndbank|ndfin|ndind|ndinsr|ndtran|nya|rua|rui|rut|sp100|sp600|spcd|spcst|spenrg|spfin|sphe|spind|spmat|sptech|sptel|sput|ta125|uty|w5000|xau|xoi)raw\d

R0 = R1 = undef
R1  = 0.967139 * aordraw013
if  aordraw004 >= R1    R1  = 0.873757 * tyxraw013
R0  = 0.967687 * tnxraw005
if  R1 > irxraw002    if  R1 < R0    if  R0 <= tnxraw000    if  tyxraw019 >= R0    R0  = 0.925426 * tyxraw002
if  fvxraw010 > R0    if  tnxraw018 >= R0    if  irxraw017 <= R1    return  1

R1 = R0 = undef
R0  = 0.874442 * vixraw018
if  R0 > vixraw007    R0  = 0.872526 * vixraw021
if  R0 <= vixraw005    if  vixraw000 >= R0    R0  = 0.924405 * vixraw015
R1  = 0.930402 * vixraw018
if  R0 <= vixraw018    R1  = 0.936894 * vixraw011
if  R1 < vixraw016    if  vixraw010 <= R0    if  vixraw008 < R0    return  1

R0 = R1 = undef
R0  = 0.836893 * irxraw016
if  R0 < irxraw013    R1  = 0.105203 * fvxraw019
R0  = 0.978807 * irxraw021
if  irxraw010 < R0    R0  = 0.724784 * irxraw015
if  irxraw017 <= R0    R0  = 0.851472 * irxraw020
if  R1 > irxraw002    R1  = 0.946637 * asx200raw015
if  asx200raw000 > R1    if  R0 < irxraw013    if  asx200raw008 >= R1    if  asx200raw017 >= R1    return  1

R0 = R1 = undef
R1  = 0.133722 * fvxraw011
R0  = 0.123306 * fvxraw017
if  R1 >= R0    R1  = 0.983625 * asx200raw003
if  R1 < asx200raw008    R1  = 0.0825821 * fvxraw000
R0  = 0.703178 * irxraw018
if  R1 >= irxraw021    if  irxraw010 > R0    R0  = 0.123306 * fvxraw017
if  R0 >= R1    if  irxraw010 > R0    R0  = 0.123306 * fvxraw017
if  irxraw010 > R0    if  irxraw009 < R0    R0  = 0.123306 * fvxraw017
if  R0 > irxraw012    if  irxraw015 < R0    return  1

R0 = R1 = undef
R1  = 0.934906 * asx200raw018
R0  = 0.979026 * jkseraw000
if  jkseraw019 >= R0    R0  = 0.518442 * asx200raw003
if  jkseraw015 >= R0    if  R1 < asx200raw001    if  R1 < asx200raw009    R0  = 0.953588 * jkseraw001
if  jkseraw010 <= R0    if  R1 > asx200raw004    R1  = 0.975936 * jkseraw010
if  R1 > jkseraw008    if  R1 <= asx200raw009    if  jkseraw021 >= R0    if  R1 <= asx200raw007    return  1

R0 = R1 = undef
R1  = 0.993643 * asx200raw002
if  aordraw013 >= R1    if  asx200raw010 <= R1    R1  = 0.968812 * asx200raw003
if  aordraw014 < R1    R1  = 0.965842 * aordraw009
if  asx200raw020 <= R1    if  aordraw004 > R1    R0  = 0.999385 * asx200raw021
if  aordraw018 >= R0    R0  = 0.983193 * asx200raw018
R1  = 0.968812 * asx200raw003
if  R1 > asx200raw021    if  R1 >= R0    R1  = 0.967574 * aordraw001
if  R0 <= asx200raw019    if  asx200raw002 > R1    if  R0 < asx200raw005    if  aordraw000 > R1    return  1

R1 = R0 = undef
R0  = 0.970766 * aordraw007
if  aordraw015 > R0    R0  = 0.996531 * asx200raw011
if  R0 > asx200raw012    R1  = 0.995876 * asx200raw011
R0  = 0.993601 * asx200raw014
if  R1 < aordraw004    if  R1 > R0    if  aordraw016 >= R0    if  R1 <= aordraw003    return  1

The training data had 5310 simulated trades starting 1993-06-04 through 2014-07-03.

The out-of-sample evaluation data had 2275 simulated trades starting 2014-07-07 through 2023-07-28 with
total return 260.67% (not including dividends or trading costs); compound annual return 15.20%; mean return 0.0564%; median return 0.1121%; winning percent 55.47

The evaluation period with the generated rules applied had 1676 simulated, one-trading-day trades (73.67% of the total evaluation trading days) with
total return 643.1014% (not including dividends or trading costs); compound annual return 24.76%; mean return 0.1197%; median return 0.1554%; winning percent 57.88

If trades on consecutive trading days were combined (total return unchanged), there would have been 227 trades with
mean return 0.8874%; median return 0.7334%; winning percent 69.16
The combined trades would have lasted from 1 to 89 trading days with a mean 7.38 trading days and a median 3 trading days.

Each individual rule had better performance per trade on the out-of-sample data than the equivalent buy-and-hold trades. I think there is a reasonable chance the rules would have better performance than buy-and-hold trades in the future.

Comments?
 
Which software did you use for the genetic programming approach ? What (and how many different) kind of data did you use for intermarket relationships ? What was your fitness function ? If you used daily data only did you make sure that the close prices are on the same exact time ending on each of your different data sets to prevent data snooping bias ?
 
Which software did you use for the genetic programming approach ?
I wrote the software in C++ and opencl with perl and shell controlling it. It uses linear genetic programming mostly based on the ideas from this book


What (and how many different) kind of data did you use for intermarket relationships ?
The inputs are daily close prices for the current trading day and the previous 21 trading days for the assets I wrote in the post above. This data for this example has three types: U.S. Treasury interest rates, Australian and Asian equity indexes, and the VIX. The target the software is measuring against is the NASDAQ 100 which was not one of the inputs.

For each run of the software, I used various combinations of the inputs; i.e., not all of them.

The rules were allowed to use two registers to store intermediate values, and the operators allowed for these runs were if statement comparisons (< <= > >=) and multiplication of an input by a constant. The if statements have the next statement as the "then" part and no "else" part.

A register assigned a value assumes the type of the expression it was assigned to. If an if statement tries to compare two operands with different types, the comparison is always interpreted as false. For example, in this rule
Code:
R0 = R1 = undef
R1  = 0.967139 * aordraw013
if  aordraw004 >= R1    R1  = 0.873757 * tyxraw013
R0  = 0.967687 * tnxraw005
if  R1 > irxraw002    if  R1 < R0    if  R0 <= tnxraw000    if  tyxraw019 >= R0    R0  = 0.925426 * tyxraw002
if  fvxraw010 > R0    if  tnxraw018 >= R0    if  irxraw017 <= R1    return  1
When the "if R1 > irxraw002" executes, R1 could be an equity type (from "R1 = 0.967139 * aordraw013") or an interest rate type (from "R1 = 0.873757 * tyxraw013). if R1 has the equity type, the if R1 > irxraw002 would not evaluate true because it's being compared to an interest rate type.

What was your fitness function ?
The fitness function is a variation of the Ulcer Performance Index multiplied by the proportion of hits (number of inputs a rule passes on divided by total number of inputs) and includes a penalty if there were too many hits. The penalty helps avoid the software thinking the fittest rule is one that takes every simulated trade.

If you used daily data only did you make sure that the close prices are on the same exact time ending on each of your different data sets to prevent data snooping bias ?
The timestamps of the data is whatever Yahoo Finance said what the closing price was for a particular day. For example, today is August 3, and the closing price for NASDAQ 100 is available for August 2 and before. One of the inputs is ASX 200, and Australian markets have already closed for August 3. The software will not use the data for ASX 200 for August 3 and would only use the data for ASX 200 for August 2 and before. This avoids data snooping.

If the data for August 2 and before was used as training data, the inputs for the history would have ended July 31, and the simulated trade would have been entered long at the close of August 1 and exited at the close of August 2.

If an exchange for one of the inputs was closed on a day U.S. equity markets are open, the previous close price for that input gets carried forward.
 
Did you check walk forward optimizing as rolling window because then you estimate better the predictive value in your insample analysis. Actually you have only one large out-of-sample test period. That is not enough to make judgement I would say. Check rolling WF analysis and optimize the IS to OOS window. Then come back with your results and let's see.
 
%%
I dont think that will help very much;
2 day trades on QQQ, even if its profitable in a bull market + most likely it IS profitable,+ in future .
MOST likely did include enough of a bull-bear cycle, to be oK,qqq.
I have traded QQQ 2 days before so I'm not saying its bad.....................
Yes i think it would outperform QQQ or QLD[3.8% t bills in it]buy + hold in a bear trend ;
but not a bull trend which we are in[2oo day moving average signal]:caution::caution:.
Thanks
 
Did you check walk forward optimizing as rolling window because then you estimate better the predictive value in your insample analysis. Actually you have only one large out-of-sample test period. That is not enough to make judgement I would say. Check rolling WF analysis and optimize the IS to OOS window. Then come back with your results and let's see.
I looked at walk-forward optimization with a rolling window.
https://ungeracademy.com/posts/how-to-use-walk-forward-analysis-you-may-be-doing-it-wrong
Another aspect to consider is that WFA can only be used on trading systems that are based on the use of optimizable parameters.

And, I found a 2020 paper (attached) describing the authors' use of genetic programming with walk-forward optimization.
upload_2023-8-5_8-36-53.png

Technical analysis combines different technical indicators( such as Relative
Strength Index, etc) and determines the timings of the investment.
Combining different indicators and tweaking their parameters
can change the trading rules and thus the profitability of the
strategy.

They combined some operators and technical indicators
Terminal Set:
Variable: Volume, Prices (Open, High, Low, Close)
Real Constants: chosen in the interval of [0 200]
Function Set:
Boolean Operators: and, or, not
Relational Operators: <, >
In the functions presented below, variable s and n stand for
any price or volume and any constant respectively.
max(s,n): Maximum of volume or price over past n days
min(s,n): Minimum of volume or price over past n days
avg(s,n): Average of volume or price over past n days
lag(s,n) Volume or price lagged by n days.
Moreover, all the indicators shown in the Table I are also part
of function set.
All the above functions have fixed arity. However, arities of
boolean operators (and, or and vot) is variable (not fixed) as
shown in the Table II.
Operator Arity
and 2,3,4,5,6,7,8
or 2,3,4,5,6,7,8
vot 3 2,3,4,5,6,7,8
upload_2023-8-5_8-39-1.png


on individual stocks
upload_2023-8-5_8-39-56.png


with a few fixed parameters
upload_2023-8-5_8-40-29.png


They don't say what they would do after walk-forward optimization is done, but
Bing Chat said:
After walk forward testing is finished, one would typically move the training window forward, retrain, and use the parameters from the untested retraining. This is because walk forward testing is a specific application of a technique known as Cross-validation. It means to take a segment of your data to optimize a system, and another segment of data to validate¹. The idea is to keep the trading model a step ahead by having multiple walk training and testing periods¹. This process helps to maintain a reasonable 'degree of freedom' and is less likely to suffer from overfitting¹.

Source: Conversation with Bing, 8/5/2023
(1) Walk forward optimization - Wikipedia. https://en.wikipedia.org/wiki/Walk_forward_optimization.
(2) Backtesting and Forward Testing: The Importance of Correlation. https://www.investopedia.com/articles/trading/10/backtesting-walkforward-important-correlation.asp.
(3) Walk-forward testing and optimization - AmiBroker. https://www.amibroker.com/guide/h_walkforward.html.

I would not use rules from an untested retraining until they pass some forward testing.

When I use my rule generator on training data, if the best rule does poorly on evaluation data, I don't use the rule. So the evaluation data is also used during training -- just not to create the rules.

I try to avoid overfitting by
  • limiting the maximum number of statements in a rule
  • allowing crossover and mutation to increase and/or decrease the number of statements in a rule (avoids having all rules use the maximum number of statements)
  • penalizing rules that hit too many of the inputs (avoids having a rule hit all inputs and being equivalent to buy-and-hold)
  • limiting the operators a rule can use (e.g., only multiplication and comparisons)
  • limiting the class of operands a rule can use (e.g., multiplication must have a constant and an input)
  • limiting the inputs for a training run (e.g., allowing 13 Week Treasury Bill and Treasury Yield 5 Years but not Treasury Yield 30 Years)
  • avoiding individual statements using two operands of different types (e.g., comparing an interest rate and an equity is always false)
  • sometimes randomly picking a fraction of the input data
  • limiting the number of generations for a run
I change settings from one run to another to see which ones tend to produce rules that work well on the evaluation data.

If I did walk-forward optimization, each moving window would have very different rules. It might show that with appropriate training and evaluation windows, the methodology I'm using can be successful in the future. But I can already tell that from the larger training and evaluation window I used. So, I don't think walk forward evaluation would be that valuable for this type of rule generation. And, all other things equal, using shorter training windows makes overfitting more likely.

Here is someone else's opinion on walk-forward optimization.
WF suffers from three major disadvantages: First, a single scenario is tested (the historical
path), which can be easily overfit (Bailey et al. [2014]). Second, WF is not
necessarily representative of future performance, as results can be biased by the particular
sequence of datapoints.
...
The third disadvantage of WF is that the initial decisions are made on a smaller
portion of the total sample. Even if a warm-up period is set, most of the information
is used by only a small portion of the decisions.
 

Attachments

Here is another example of rules created through genetic programming to simulate trades in one asset with data from other assets.

This example uses daily close values to simulate trades entering long at the next trading day's close value of Russell 2000 and exiting at the close the next trading day.

The inputs are close values for the current trading day (suffix raw000), and close values for the previous 21 trading days (suffixes raw001 through raw021) downloaded from Yahoo finance for the following:
  • ta125rawNNN -- TA-125 (^TA125.TA)
  • spenrgrawNNN -- S&P 500 Energy (Sector) (^GSPE)
  • bvsprawNNN -- IBOVESPA (^BVSP)
  • asx200rawNNN -- S&P/ASX 200 (^AXJO)
  • aordrawNNN -- ALL ORDINARIES (^AORD)
  • irxrawNNN -- 13 Week Treasury Bill (^IRX)
  • tnxrawNNN -- Treasury Yield 10 Years (^TNX)
  • vixrawNNN -- CBOE Volatility Index (^VIX)

The software supports the interest rates, equities, and volatility as separate types as before, and operations between different types result in undef values which evaluate false in the if statements.

In the following generated pseudocode rules, "return 1" means the trade would be entered on the close of the next trading day. Each block ending with a "return 1" is an individual rule from a separate run of the rule generating software.
Code:
# types_for_runtime_checking ^(?:vix)raw\d ^(?:fvx|irx|tnx|tyx)raw\d ^(?:aord|asx200|bfx|bvsp|cac40|dji|djt|dju|gdaxi|gspc|gsptse|hsi|ixic|jkse|mid|mxx|n225|nd100|ndbank|ndfin|ndind|ndinsr|ndtran|nya|rua|rui|rut|sp100|sp600|spcd|spcst|spenrg|spfin|sphe|spind|spmat|sptech|sptel|sput|ta125|uty|w5000|xau|xoi)raw\d

R1 = R0 = undef
R0  = 0.216482 * ta125raw006
R1  = 0.955771 * ta125raw019
if  R1 <= ta125raw005    R0  = 0.985709 * ta125raw009
if  ta125raw005 >= R0    R1  = 0.991367 * ta125raw009
if  ta125raw014 < R1    if  ta125raw011 < R1    if  ta125raw014 < R1    return  1

R0 = R1 = undef
R0  = 0.97479 * spenrgraw010
if  R0 <= spenrgraw009    R0  = 0.982745 * spenrgraw012
R1  = 0.99942 * spenrgraw000
if  R1 <= spenrgraw006    R0  = 0.970778 * spenrgraw006
if  R0 >= spenrgraw017    if  R0 >= R1    R0  = 0.970778 * spenrgraw006
if  R0 > spenrgraw018    if  R0 <= R1    if  spenrgraw017 <= R0    return  1

R1 = R0 = undef
R1  = 0.958637 * bvspraw013
R0  = 0.954164 * bvspraw006
if  bvspraw013 > R0    if  R0 <= bvspraw016    if  R0 <= bvspraw016    if  R1 <= R0    if  R0 < bvspraw012    if  bvspraw015 > R0    return  1

R1 = R0 = undef
if  asx200raw018 >= R1    R1  = 0.701134 * asx200raw016
R0  = 0.993568 * asx200raw001
if  R0 <= asx200raw004    R1  = 0.984193 * asx200raw012
if  R1 <= asx200raw004    if  R1 > R0    R1  = 0.984009 * asx200raw013
if  R1 >= asx200raw020    return  1

R1 = R0 = undef
R0  = 0.987216 * aordraw004
if  aordraw014 < R0    R1  = 0.989049 * aordraw021
if  aordraw019 >= R1    if  aordraw013 >= R1    if  R0 > aordraw019    if  R0 < aordraw006    if  R0 < aordraw004    if  aordraw003 > R0    if  R1 < aordraw014    return  1

R1 = R0 = undef
R1  = 0.987059 * tnxraw020
R0  = 0.988015 * tnxraw003
if  R0 < tnxraw013    if  R1 >= tnxraw016    if  R1 >= tnxraw016    return  1

R1 = undef
R1  = 0.954557 * vixraw018
if  vixraw001 < R1    R1  = 0.986823 * vixraw002
if  R1 < vixraw016    if  vixraw002 <= R1    R1  = 0.536007 * irxraw004
if  R1 > vixraw008    if  vixraw008 < R1    return  1

R0 = R1 = undef
R0  = 0.964751 * vixraw008
R1  = 0.996452 * vixraw008
if  vixraw018 > R1    R0  = 0.81551 * vixraw004
if  vixraw016 >= R0    if  R0 <= R1    R0  = 0.841603 * vixraw011
R1  = 0.787182 * vixraw020
if  R1 > R0    if  R1 < vixraw002    if  R0 < R1    return  1

R0 = R1 = undef
R1  = 0.796143 * irxraw010
if  R1 <= irxraw017    R0  = 0.823123 * irxraw004
if  irxraw012 >= R0    R0  = 0.823123 * irxraw004
R1  = 0.735272 * irxraw021
if  irxraw012 >= R0    R1  = 0.90368 * vixraw017
if  vixraw010 <= R1    R0  = 0.823123 * irxraw004
if  R0 <= irxraw004    R0  = 0.938806 * irxraw018
R1  = 0.90368 * vixraw017
if  vixraw010 <= R1    if  irxraw012 >= R0    return  1

The training data had 5310 simulated trades starting 1993-06-04 through 2014-07-03.

The out-of-sample evaluation data had 2275 simulated trades starting 2014-07-07 through 2023-07-28 with
total return 48.42% (not including dividends or trading costs); compound annual return 4.45%; mean return 0.01736%; median return 0.07983%; winning percent 52.88

The evaluation period with the generated rules applied had 1819 simulated, one-trading-day trades (79.96% of the total evaluation trading days) with
total return 271.97% (not including dividends or trading costs); compound annual return 15.59%; mean return 0.0722%; median return 0.1226%; winning percent 54.54

If trades on consecutive trading days were combined (total return unchanged), there would have been 218 trades with
mean return 0.6044%; median return 0.2834%; winning percent 56.88
The combined trades would have lasted from 1 to 47 trading days with a mean 8.34 trading days and a median 5 trading days.

Each individual rule had better performance per trade on the out-of-sample data than the equivalent buy-and-hold trades. I think there is a reasonable chance the rules would have better performance than buy-and-hold trades in the future.
 
Last edited:
Here is another example of rules created through genetic programming to simulate trades in one asset with data from other assets (also the same asset).

This example uses daily close values to simulate trades entering long at the next trading day's close value of IBOVESPA and exiting at the close the next trading day.

The inputs are close values for the current trading day (suffix raw000), and close values for the previous 21 trading days (suffixes raw001 through raw021) downloaded from Yahoo finance for the following:
  • aordrawNNN -- ALL ORDINARIES (^AORD)
  • bvsprawNNN -- IBOVESPA (^BVSP)
  • djurawNNN -- Dow Jones Utility Average (^DJU)
  • fvxrawNNN -- Treasury Yield 5 Years (^FVX)
  • gdaxirawNNN -- DAX PERFORMANCE-INDEX (^GDAXI)
  • nd100rawNNN -- NASDAQ 100 (^NDX)
  • ruirawNNN -- Russell 1000 (^RUI)
  • spcdrawNNN -- S&P 500 Consumer Discretionary (Sector) (^SP500-25)
  • spherawNNN -- S&P 500 Health Care (Sector) (^SP500-35)
  • spindrawNNN -- S&P 500 Industrials (Sector) (^SP500-20)

Unlike the earlier rules, run-time typing does not apply because each group of rules from a run has only one type of input. However, if a register (R0 or R1) has an undef value, any comparison with the register would evaluate false.

In the following generated pseudocode rules, "return 1" means the trade would be entered on the close of the next trading day. Each block ending with a "return 1" is an individual rule from a separate run of the rule generating software.
Code:
R0 = R1 = undef
R1  = 0.959715 * spindraw001
if  R1 < spindraw017    R1  = 0.988802 * spindraw005
if  R1 <= R0    if  R1 <= spindraw017    if  R1 >= R0    if  R1 < spindraw017    R1  = 0.988802 * spindraw005
if  R1 >= spindraw009    if  spindraw007 >= R1    return  1

R0 = R1 = undef
R1  = 0.990977 * spheraw006
R0  = 0.991473 * spheraw011
if  R0 > spheraw013    if  R0 <= spheraw005    if  spheraw013 > R1    if  R0 > spheraw013    R0  = 0.965347 * spheraw008
if  R1 >= spheraw001    if  R1 > spheraw011    if  R0 < spheraw018    if  R0 > spheraw021    R1  = 0.992782 * spheraw004
if  R1 < spheraw001    R0  = 0.965347 * spheraw008
if  spheraw000 <= R1    if  spheraw015 >= R0    if  R1 < spheraw005    return  1

R0 = R1 = undef
R0  = 0.97702 * spcdraw003
R1  = 0.983339 * spcdraw011
if  spcdraw006 >= R0    if  R1 < spcdraw009    R0  = 0.988494 * spcdraw001
R1  = 0.983339 * spcdraw011
if  spcdraw000 > R1    if  spcdraw004 >= R1    if  spcdraw018 >= R0    if  R1 < R0    if  spcdraw014 >= R1    if  spcdraw007 >= R1    return  1

R1 = R0 = undef
R1  = 0.987758 * ruiraw001
R0  = 0.980145 * ruiraw012
if  R1 > R0    if  R1 < ruiraw008    if  R1 < ruiraw017    if  R1 < ruiraw008    if  R1 < ruiraw004    if  R1 < ruiraw017    if  R1 < ruiraw017    if  R1 < ruiraw008    return  1

R0 = R1 = undef
R1  = 0.971681 * nd100raw000
if  nd100raw008 >= R1    if  R1 > nd100raw012    R0  = 0.824234 * nd100raw008
R1  = 0.995088 * nd100raw003
if  R1 >= nd100raw000    R0  = 0.960204 * nd100raw009
R1  = 0.982535 * nd100raw016
if  R1 < nd100raw014    if  R1 < nd100raw004    if  nd100raw001 >= R1    if  nd100raw017 >= R1    if  R1 >= R0    if  R1 > R0    if  R0 <= nd100raw013    if  nd100raw015 > R0    if  nd100raw009 > R0    return  1

R1 = R0 = undef
R0  = 0.991523 * aordraw004
R1  = 0.995105 * aordraw010
if  R1 < aordraw007    if  R1 < aordraw007    if  aordraw006 <= R0    return  1

R0 = R1 = undef
R0  = 0.975845 * gdaxiraw005
if  R0 <= gdaxiraw011    if  gdaxiraw013 <= R0    R1  = 0.555409 * gdaxiraw001
if  gdaxiraw012 >= R1    R0  = 0.987969 * gdaxiraw010
if  R0 >= gdaxiraw020    if  gdaxiraw017 >= R1    if  gdaxiraw006 >= R1    R0  = 0.987969 * gdaxiraw010
if  R0 < gdaxiraw017    if  R0 >= gdaxiraw020    R1  = 0.263305 * gdaxiraw020
if  R1 < gdaxiraw011    R1  = 0.934423 * gdaxiraw006
if  gdaxiraw017 >= R1    if  gdaxiraw020 <= R0    return  1

R1 = R0 = undef
R1  = 0.947201 * fvxraw005
R0  = 0.981228 * fvxraw012
if  R0 >= fvxraw018    if  fvxraw006 >= R0    R0  = 0.637228 * fvxraw014
if  R1 >= R0    if  R1 >= R0    if  R0 <= R1    if  fvxraw004 > R0    if  fvxraw007 >= R1    if  R0 < fvxraw015    if  fvxraw010 >= R0    if  fvxraw015 > R1    if  R0 <= R1    if  R1 < fvxraw013    if  fvxraw015 > R1    if  fvxraw015 >= R0    return  1

R1 = R0 = undef
R1  = 0.997754 * djuraw010
if  djuraw014 >= R1    R1  = 0.647859 * djuraw018
if  djuraw007 >= R1    R0  = 0.996467 * djuraw016
if  djuraw000 > R0    if  R0 <= djuraw005    R0  = 0.968677 * djuraw003
R1  = 0.950166 * djuraw013
if  R0 < djuraw001    if  R0 <= djuraw005    R0  = 0.968677 * djuraw003
if  djuraw017 > R0    if  djuraw008 < R0    if  djuraw000 > R1    return  1

R1 = R0 = undef
R0  = 0.386411 * bvspraw000
R1  = 0.967196 * bvspraw005
if  R1 < bvspraw002    if  R1 < bvspraw008    R1  = 0.683208 * bvspraw006
if  R1 >= bvspraw020    if  R0 < bvspraw016    if  bvspraw007 > R0    return  1

R1 = R0 = undef
R1  = 0.948355 * bvspraw006
if  bvspraw020 <= R1    R0  = 0.999571 * bvspraw006
if  bvspraw003 >= R0    if  bvspraw003 > R0    R0  = 0.97461 * bvspraw005
if  bvspraw003 >= R0    R1  = 0.941123 * bvspraw018
if  bvspraw020 <= R1    if  bvspraw016 < R1    R0  = 0.97461 * bvspraw005
if  bvspraw008 >= R0    R0  = 0.921583 * bvspraw013
if  bvspraw021 < R0    if  R0 >= R1    if  bvspraw021 < R0    return  1

R0 = R1 = undef
R1  = 0.992148 * aordraw012
R0  = 0.977981 * aordraw004
if  aordraw002 > R0    if  aordraw000 >= R1    if  R1 < aordraw005    if  aordraw011 < R0    R1  = 0.990964 * aordraw018
if  R1 < aordraw004    R1  = 0.98734 * aordraw003
if  R1 > aordraw013    if  R1 > aordraw006    return  1

The training data had 5310 simulated trades starting 1993-06-04 through 2014-07-03.

The out-of-sample evaluation data had 2275 simulated trades starting 2014-07-07 through 2023-07-28 with
total return 87.34% (not including dividends or trading costs); compound annual return 7.17%; mean return 0.0276%; median return 0.0000%; winning percent 49.71

The evaluation period with the generated rules applied had 1769 simulated, one-trading-day trades (77.76% of the total evaluation trading days) with
total return 962.69% (not including dividends or trading costs); compound annual return 29.78%; mean return 0.1337%; median return 0.0465%; winning percent 51.67

If trades on consecutive trading days were combined (total return unchanged), there would have been 284 trades with
mean return 0.8357%; median return 0.3711%; winning percent 55.63
The combined trades would have lasted from 1 to 48 trading days with a mean 6.23 trading days and a median 4 trading days.

Each individual rule had better performance per trade on the out-of-sample data than the equivalent buy-and-hold trades. I think there is a reasonable chance the rules would have better performance than buy-and-hold trades in the future.
 
The previous examples in this thread all had fixed-length inputs. This example has generated rules from variable-length inputs from different assets for simulating trades in another asset.

A short summary of the method is
  • Find three value swings of past data for the input assets.
  • Fit a curve with a mathematical function to the value swings.
  • Create rules for trading a target asset based on parameters of the function and its output.


Here is a more detailed explanation.

Source of Input Data

The input assets are daily close values from the following:
Code:
name                                           yahoo finance symbol    prefix
ALL ORDINARIES                                 ^AORD                   aord
BEL 20                                         ^BFX                    bfx
CAC 40                                         ^FCHI                   cac40
CBOE Volatility Index                          ^VIX                    vix
DAX PERFORMANCE-INDEX                          ^GDAXI                  gdaxi
Dow Jones Industrial Average                   ^DJI                    dji
Dow Jones Transportation Average               ^DJT                    djt
Dow Jones Utility Average                      ^DJU                    dju
HANG SENG INDEX                                ^HSI                    hsi
IBEX 35                                        ^IBEX                   ibex
IBOVESPA                                       ^BVSP                   bvsp
IDX COMPOSITE                                  ^JKSE                   jkse
IPC MEXICO                                     ^MXX                    mxx
NASDAQ 100                                     ^NDX                    nd100
NASDAQ Bank                                    ^BANK                   ndbank
NASDAQ Biotechnology                           ^NBI                    nbi
NASDAQ Composite                               ^IXIC                   ixic
NASDAQ Financial 100                           ^IXF                    ndfin
NASDAQ Industrial                              ^INDS                   ndind
NASDAQ Insurance                               ^INSR                   ndinsr
NASDAQ Transportation                          ^TRAN                   ndtran
NYSE ARCA OIL and GAS INDEX                    ^XOI                    xoi
NYSE COMPOSITE (DJ)                            ^NYA                    nya
Nikkei 225                                     ^N225                   n225
PHLX GOLD and SILVER SECTOR Index              ^XAU                    xau
PHLX Utility Sector                            ^UTY                    uty
Russell 1000                                   ^RUI                    rui
Russell 2000                                   ^RUT                    rut
Russell 3000                                   ^RUA                    rua
S&P 100                                        ^SP100                  sp100
S&P 500 Consumer Discretionary (Sector)        ^SP500-25               spcd
S&P 500 Consumer Staples (Sector)              ^SP500-30               spcst
S&P 500 Energy (Sector)                        ^GSPE                   spenrg
S&P 500 Financials (Sector)                    ^SP500-40               spfin
S&P 500 Health Care (Sector)                   ^SP500-35               sphe
S&P 500 Industrials (Sector)                   ^SP500-20               spind
S&P 500 Information Technology (Sector)        ^SP500-45               sptech
S&P 500 Materials (Sector)                     ^SP500-15               spmat
S&P 500 Telecommunication Services (Sector)    ^SP500-50               sptel
S&P 500 Utilities (Sector)                     ^SP500-55               sput
S&P 500                                        ^GSPC                   gspc
S&P 600                                        ^SP600                  sp600
S&P MID CAP 400 INDEX                          ^MID                    mid
S&P/ASX 200                                    ^AXJO                   asx200
S&P/TSX Composite index                        ^GSPTSE                 gsptse
TA-125                                         ^TA125.TA               ta125
Treasury Bill 13 Week                          ^IRX                    irx
Treasury Yield 10 Years                        ^TNX                    tnx
Treasury Yield 30 Years                        ^TYX                    tyx
Treasury Yield 5 Years                         ^FVX                    fvx
Wilshire 5000 Total Market Index               ^W5000                  w5000
I chose these assets because data for at least 30 years is available from Yahoo Finance.

For inputs to the method, the data for the local date associated with an asset is grouped together as input for that date. For example, the 2024-01-10 data is grouped with other 2024-01-10 data even though markets in Australia have already closed for January 11. When non-U.S. markets are closed, the previous close values are carried forward until their dates match the U.S. market close date.


Defining Input Value Swings

For each date in a symbol's data, the software finds data for three value swings with algorithm
  • The swing direction starts as undefined.
  • A lookback period is the past N bars or to the bar just past the previous value swing whichever is larger.
  • If no close value in the lookback period is higher than the current close value, the swing direction is up.
  • If no close value in the lookback period is lower than the current close value, the swing direction is down.
  • If the swing direction is up and down on the same bar (e.g., value hasn't changed since last swing), the swing direction does not change.
  • If the swing direction changes from not up to up, a low swing has been detected at the oldest bar with the lowest value in the lookback period.
  • If the swing direction changes from not down to down, a high swing has been detected at the oldest bar with the highest value in the lookback period.
  • The first swing found is ignored.


Modeling Input Value Swings

The software fits a curve for the data from the current bar to the bar after the value swing that came before the three value swings was detected. The fitted curve has a least squares regression line added to zero or more asymmetric triangle waves.
asymmetric_triangle_wave.png

Here is an example with the close values of the NASDAQ 100 index for 39 trading days from 2023-11-16 through 2024-01-12 with the least squares regression line and fitted curve.
nd100_20231116_20240112_39.png

The corresponding fitted curve formula is:
Code:
nd100_20240112_cly = 16881.927734375 + 551.884216308594 * -0.0502413419724309 * x  +  209.763610839844 * (
    atri(1.1723438501358, 22.5762538909912, 0.833862066268921, 0.565416514873505, x)
    + atri(0.548687338829041, 10.0181884765625, 0.412693917751312, 0.470283389091492, x)
    + atri(0.40699827671051, 7.98540878295898, 0.176088511943817, 0.500997245311737, x)
    + atri(0.368489861488342, 38.861083984375, 0.150145471096039, 0.492421269416809, x)
    + atri(0.325989842414856, 18.3074150085449, 0.756223797798157, 0.466294586658478, x) ) ;
atri(amplitude, period, phase, peakPhase, x) would be evaluated as the asymmetric triangle wave value for x bars before the current bar.

To allow more relevant comparisons of phases like 0.833862066268921, the curve start at the most recent bar and continues backwards in time.

To allow more relevant comparisons of least squares regression line slopes like -0.0502413419724309, the estimated standard deviation of the data (e.g., 551.884216308594) is factored out.

To allow more relevant comparisons of amplitudes like 1.1723438501358, the estimated standard deviation of the differences of close values to the least squares regression line (e.g., 209.763610839844) is factored out.


Creating Inputs for Rules Generator

The inputs for the rules generator are prefixed with the prefix representing a symbol with some having the index of an asymmetric triangle wave (0 has highest amplitude, 1 has the next highest, etc.). The inputs are derived from the fitted curve parameters and/or values from evaluating the fitted curve:
Code:
bars -- number of bars in the three swing pattern
example: aordbars

fitProp -- proportion of cyclic data fitted by the asymmetric triangle waves
example: aordclfitProp

dtwFitProp -- proportion of cyclic data fitted by the asymmetric triangle waves using dynamic time warping
example: aordcldtwFitProp

trendsds -- number of estimated standard deviations of distances to the least squares regression line
in the rise (+) or fall (-) of the regression line in the three swing pattern
example: aordcl_trendsds

bartrendsds -- number of estimated standard deviations of distances to the least squares regression line
in the rise (+) or fall (-) of each bar the regression line in the three swing pattern
example: aordcl_bartrendsds

wsum -- sum of all the asymmetric triangle wave values at the current bar
example: aordcl_wsum

lpr01 -- log of (extrapolated price one bar ahead divided by current price) * 100
example: aordlpr01cl

lpr12 -- log of (extrapolated price two bars ahead divided extrapolated price one bar ahead) * 100
example: aordlpr12cl

wave -- asymmetric triangle wave value at the current bar
example: aordcl_wave_0

wprop -- asymmetric triangle wave value at the current bar divided by the wave amplitude
example: aordcl_wprop_0

amp -- asymmetric triangle wave amplitude
example: aordcl_amp_0

per -- asymmetric triangle wave period
example: aordcl_per_0

ncy -- number of cycles of an asymmetric triangle wave in the three swing pattern
example: aordcl_ncy_0

vel -- absolute vertical movement per bar for an asymmetric triangle wave in the three swing pattern
example: aordcl_vel_0

lvel -- absolute vertical movement per bar from trough to peak for an asymmetric triangle wave in the three swing pattern
example: aordcl_lvel_0

rvel -- absolute vertical movement per bar from peak to trough for an asymmetric triangle wave in the three swing pattern
example: aordcl_rvel_0

phase -- phase of an asymmetric triangle wave in the three swing pattern
example: aordcl_phase_0

offsetphase -- -0.5 + phase of an asymmetric triangle wave in the three swing pattern
example: aordcl_offsetphase_0

peakphase -- peak phase of an asymmetric triangle wave in the three swing pattern
example: aordcl_peakphase_0

offsetpeakphase -- -0.5 + peak phase of an asymmetric triangle wave in the three swing pattern
example: aordcl_offsetpeakphase_0

The target for the rules generator simulates a long trade entering at the next bar's close value and exiting at the following bar's close value (no adjustments for slippage or commission).

This example uses 5253 instances of training data with simulated entries from 1994-02-23 through 2015-01-02 and 2251 instances of evaluation data with simulated entries from 2015-01-05 through 2023-12-21.


Generating Rules

The rules generator uses genetic programming to classify whether to take a trade or not. Each rule in a set of rules has one or more conditions logically ANDed together. Conditions can have inputs compared with constants or other inputs. When a rule returns 1, a long trade at the close of the next bar would be entered, and this trade would exit at the bar following entry.

After the rules generator finishes its runs using the training data, a subset is chosen as the final rules for forward testing.

Rules found for the NASDAQ 100 are:
Code:
if  gsptsebars >= 50    if  gsptselpr01cl <= 0.180333    if  gsptsecl_wprop_0 >= -0.658776    if  gsptsecl_rvel_0 > 0.0648147    if  gsptsecl_lvel_1 < 0.176615    if  gsptsecl_wsum <= 1.3634    if  gsptsecl_wave_1 >= -0.192705    if  gsptsecl_rvel_0 >= 0.0690005    if  gsptsebars > 49    return  1

if  sptechcl_ncy_0 < 1.20559    if  sptechcl_wprop_1 <= 0.95901    if  sptechlpr01cl >= -0.431104    if  sptechcl_ncy_0 < 1.20804    if  sptechlpr01cl > -0.576447    if  sptechcl_ncy_0 >= 1.09133    if  sptechcl_amp_0 < 1.24989    if  sptechcl_offsetphase_0 >= -0.350003    return  1

if  gsptsebars > 49    if  gsptsebars >= 50    if  gsptselpr01cl <= 0.134077    if  gsptsecl_ncy_0 >= 1.10754    if  gsptsecl_offsetphase_1 >= -0.286188    if  gsptsecl_phase_0 <= 0.917072    if  gsptsecl_lvel_1 < 0.174925    if  gsptsecl_wsum < 1.26999    return  1

if  ndinsrcl_phase_1 < 0.587518    if  ndinsrlpr01cl <= 0.0229777    if  ndinsrcl_wave_0 <= 0.934144    if  ndinsrlpr12cl < 0.296799    if  ndinsrcl_lvel_1 <= 0.117298    if  ndinsrcl_wprop_0 <= 0.966144    return  1

if  sptechcl_lvel_0 <= 0.187625    if  sptechcl_phase_0 >= 0.322132    if  sptechcl_ncy_0 >= 1.08671    if  sptechcl_wprop_1 <= 0.973641    if  sptechcl_wprop_0 >= -0.878743    if  sptechlpr12cl < 0.660802    if  sptechcl_ncy_0 <= 1.20559    if  sptechlpr01cl >= -0.637203    return  1

if  w5000cl_offsetphase_0 <= 0.299248    if  w5000cl_ncy_0 >= 1.61404    if  w5000cl_trendsds > -2.94143    if  w5000lpr12cl < 0.282786    if  w5000cl_lvel_0 > 0.143781    if  w5000cl_phase_0 <= 0.80247    if  w5000cl_phase_0 <= 0.799542    if  w5000cl_wave_1 > -0.0348898    return  1

if  asx200cl_wprop_1 < 0.876702    if  asx200cl_amp_1 >= 0.451709    if  asx200cl_amp_1 < 0.73843    if  asx200cl_per_1 < 30.0989    if  asx200cl_lvel_1 > 0.0462801    if  asx200cl_wave_0 >= 0.326522    if  asx200cl_amp_1 >= 0.451709    if  asx200cl_rvel_0 > 0.099337    if  asx200cl_wsum > 0.294748    return  1

if  djulpr12cl >= -0.119748    if  djucl_per_0 >= 35.1501    if  djucl_lvel_0 > djucl_rvel_0    if  djucl_phase_0 > 0.14216    if  djulpr12cl >= -0.119748    if  djucl_amp_1 < 0.66725    if  djucl_per_0 >= 35.1501    if  djucl_lvel_0 <= 0.163034    return  1

if  nbilpr12cl < 0.199289    if  nbicl_wave_0 > -0.383525    if  nbicl_trendsds <= 4.66456    if  nbicl_offsetphase_0 < -0.0186597    if  nbicl_lvel_0 < 0.135501    if  nbicl_rvel_1 <= 0.165058    if  nbilpr01cl < 0.201862    if  nbicl_offsetphase_0 <= 0.00157404    return  1

if  sp600cl_wave_0 >= -0.714745    if  sp600cl_lvel_0 >= 0.0843772    if  sp600cl_wave_0 <= 1.00219    if  sp600bars >= 37    if  sp600cl_ncy_1 < 2.46636    if  sp600lpr01cl <= 0.387207    if  sp600cl_ncy_0 >= 1.17258    if  sp600bars >= 37    return  1

if  nbicl_offsetphase_0 <= nbicl_phase_0    if  nbicl_offsetphase_0 <= -0.0201017    if  nbicl_ncy_1 <= 3.41247    if  nbilpr12cl < 0.193432    if  nbicl_trendsds <= 4.1589    if  nbicl_wprop_0 >= -0.744252    if  nbicl_lvel_0 <= 0.136218    return  1

if  bfxcl_lvel_0 > 0.12984    if  bfxcl_lvel_0 <= 0.204981    if  bfxcl_ncy_1 <= 4.39151    if  bfxcl_per_0 > 21.4993    if  bfxcl_amp_1 >= 0.538709    if  bfxcl_phase_0 > 0.301409    if  bfxcl_offsetphase_0 >= -0.27477    return  1

if  djicl_offsetphase_0 < -0.00246853    if  djicl_rvel_1 >= 0.05449    if  djicl_lvel_0 > djicl_rvel_0    if  djicl_lvel_0 >= 0.0752994    if  djicl_wprop_1 > -0.193109    if  djicl_phase_1 < 0.772738    if  djicl_rvel_1 >= 0.05449    if  djicl_ncy_1 < 3.74095    if  djicl_amp_1 <= 1.06974    return  1

if  spcdcl_offsetphase_0 < 0.0618283    if  spcdcl_bartrendsds < 0.176386    if  spcdcl_phase_0 < 0.543865    if  spcdcl_wave_1 > -0.637746    if  spcdcl_lvel_0 >= 0.128471    if  spcdcl_lvel_0 >= 0.123029    if  spcdcl_rvel_1 > 0.0738231    if  spcdcl_offsetphase_1 > 0.128175    return  1

if  sptelcl_offsetphase_1 > -0.0968448    if  sptelcl_per_1 <= 15.7406    if  sptelcl_lvel_1 < 0.370058    if  sptelcl_ncy_0 <= 2.1659    if  sptelcl_wprop_1 < 0.876375    if  sptelcl_lvel_0 > 0.142592    if  sptelcl_lvel_0 > 0.142054    if  sptelcl_lvel_0 > 0.142054    return  1

if  rutcl_ncy_1 < 3.14879    if  rutcl_rvel_0 <= rutcl_lvel_0    if  rutlpr12cl < 0.190288    if  rutcl_wave_0 > -1.0056    if  rutcl_ncy_0 > 1.17494    if  rutcl_lvel_1 <= 0.151642    if  rutlpr12cl < 0.190288    return  1

if  ruacl_ncy_0 >= 1.0421    if  ruacl_bartrendsds >= -0.0701428    if  rualpr12cl >= -1.87237    if  rualpr12cl < -0.169257    if  rualpr01cl < -0.3675    if  ruacl_wprop_0 < 0.987579    if  ruacl_ncy_0 < 3.31658    if  rualpr12cl < -0.304058    if  ruacl_wave_0 >= -0.246811    return  1

if  ruacl_wave_0 > -0.6948    if  ruacl_rvel_1 <= ruacl_lvel_1    if  ruacl_wprop_0 <= 0.999565    if  rualpr12cl <= -0.384271    if  ruacl_trendsds > -3.4272    if  ruacl_ncy_0 > 1.04294    if  rualpr12cl <= -0.384271    if  ruacl_trendsds > -3.4272    return  1

if  spfincl_per_1 <= 26.8685    if  spfincl_wave_1 > -0.189383    if  spfincl_wave_1 >= -0.161    if  spfincl_wprop_0 <= 0.983375    if  spfincl_wave_0 >= -0.58717    if  spfincl_per_0 <= 31.6072    if  spfincl_amp_0 < 1.02032    return  1

if  sp600cl_lvel_0 > 0.0835371    if  sp600cl_wprop_0 <= 0.94178    if  sp600lpr01cl <= 0.355109    if  sp600bars > 40    if  sp600cl_per_1 >= 21.0496    if  sp600cl_ncy_1 <= 2.53135    if  sp600lpr01cl <= 0.355109    if  sp600cl_lvel_0 > 0.0832733    if  sp600cl_wprop_1 >= -0.505798    return  1

if  sp100cl_wprop_0 < 0.684285    if  sp100cl_rvel_1 < sp100cl_lvel_1    if  sp100cl_wsum > -0.269601    if  sp100cl_wave_0 < 0.912238    if  sp100cl_wsum > -0.269601    if  sp100cl_offsetphase_0 > -0.354417    if  sp100cl_trendsds <= 1.95873    if  sp100cl_ncy_0 >= 1.22392    if  sp100cl_wprop_0 < 0.672633    return  1

if  n225cl_ncy_0 <= 1.37116    if  n225lpr01cl > -0.512516    if  n225cl_rvel_1 <= 0.125867    if  n225cl_lvel_1 <= 0.121976    if  n225cl_offsetphase_0 <= 0.498048    if  n225cl_wprop_0 <= 0.701871    if  n225cl_ncy_0 <= 1.37116    if  n225cl_bartrendsds < 0.0581231    if  n225lpr01cl > -0.512516    return  1

if  xoilpr01cl > -0.317378    if  xoicl_lvel_1 >= xoicl_rvel_1    if  xoicl_amp_0 < 1.19832    if  xoicl_rvel_0 <= xoicl_lvel_0    if  xoicl_amp_0 <= 1.1726    if  xoicl_phase_1 <= 0.664929    return  1

if  spfincl_offsetphase_1 < spfincl_phase_1    if  spfincl_amp_0 <= 1.01345    if  spfincl_per_1 <= 25.1463    if  spfincl_wave_0 >= -0.421303    if  spfincl_wave_1 >= -0.161709    if  spfincl_rvel_0 > 0.0965003    if  spfincl_wave_0 < 0.8804    if  spfincl_wave_0 >= -0.421303    if  spfincl_wave_0 >= -0.421303    return  1

if  ndinsrcl_wave_1 >= -0.754112    if  ndinsrcl_rvel_1 <= 0.136736    if  ndinsrcl_ncy_0 >= 1.68728    if  ndinsrcl_amp_1 > 0.390133    if  ndinsrlpr12cl < 0.280531    if  ndinsrlpr01cl < 0.440081    if  ndinsrcl_amp_1 > 0.390133    if  ndinsrcl_phase_0 > 0.0562328    return  1

if  n225cl_wprop_0 < 0.70024    if  n225cl_ncy_0 <= 1.37337    if  n225lpr01cl >= -0.51776    if  n225cl_wave_1 < 0.593395    if  n225lpr12cl <= 0.913939    if  n225cl_wprop_0 < 0.792009    if  n225lpr01cl >= -0.51776    if  n225cl_lvel_1 <= 0.122734    if  n225cl_ncy_1 < 6.07028    return  1

if  spenrgcl_rvel_0 <= 0.178898    if  spenrgcl_wave_1 > 0.160525    if  spenrgcl_rvel_0 < 0.139509    if  spenrgcl_wave_1 > 0.147221    if  spenrgcl_rvel_0 < 0.139509    if  spenrgcl_lvel_0 > 0.082349    if  spenrgcl_wsum < 1.51997    if  spenrgcl_per_1 < 33.2069    return  1

if  nyacl_amp_0 <= 1.37292    if  nyalpr01cl <= 0.308411    if  nyacl_amp_0 < 1.24746    if  nyacl_wave_0 >= 0.397509    if  nyacl_lvel_0 > nyacl_rvel_0    if  nyacl_per_1 <= 28.0804    if  nyacl_lvel_1 > 0.0514567    if  nyalpr12cl <= 0.929302    if  nyacl_wave_0 >= 0.161891    return  1

if  nyacl_rvel_0 < nyacl_lvel_0    if  nyacl_rvel_0 > 0.0554896    if  nyacl_wprop_0 >= 0.550856    if  nyacl_phase_0 < 0.461132    if  nyalpr12cl <= 0.925539    if  nyacl_per_1 <= 31.6198    if  nyacl_wave_0 >= 0.411132    if  nyacl_phase_0 < 0.461132    return  1

if  nd100cl_wsum >= 0.0434539    if  nd100cl_wave_1 <= 0.238033    if  nd100lpr12cl <= 0.106459    if  nd100cl_wave_1 < 0.233823    if  nd100cl_lvel_1 > 0.0605134    if  nd100cl_wsum < 1.29088    if  nd100cl_offsetphase_0 > -0.227007    if  nd100cl_rvel_1 > 0.0636087    if  nd100cl_ncy_1 > 2.57203    return  1


Performance

For the evaluation period, simulated buy and hold performance was
total return 260.94%, compound annual return 15.40%, maximum drawdown 35.56%, daily mean result 0.0570%, daily mean win 0.9259%, daily mean loss 1.0175%, number of trading days 2251, number of daily wins 1250, daily win rate 55.53%

The simulated performance using the rules for the evaluation period was
total return 1706.05%, compound annual return 38.10%, maximum drawdown 8.83%, mean result 0.1477%, mean win 0.8963%, mean loss 0.8530%, number of trades 1961, number of wins 1126, win rate 57.42%


Other Targets

Potential targets in addition to the NASDAQ 100 based on mapping to ETFs for actual trading and three-year, daily return correlations (QuantDare method) are in the attached corrmatrix.csv (semicolon-separated).
 

Attachments

Using the same method and data as above, the rules for IBOVESPA (Brazil)
Code:
if  djicl_ncy_1 <= 3.97268    if  djicl_amp_0 < 1.55384    if  djicl_offsetphase_0 <= -0.0132263    if  djicl_rvel_1 >= 0.0541881    if  djilpr01cl <= 0.140991    if  djilpr01cl <= 0.140991    if  djicl_lvel_0 >= 0.128096    if  djicl_lvel_0 >= 0.128096    if  djicl_rvel_0 <= 0.377881    return  1

if  nbicl_amp_0 < 1.72229    if  nbicl_amp_1 > 0.628997    if  nbicl_amp_0 <= 1.56717    if  nbicl_wsum >= -0.589339    if  nbicl_amp_1 > 0.628376    if  nbicl_rvel_0 < 0.181423    if  nbicl_rvel_0 < 0.193878    if  nbicl_wprop_0 <= 0.943634    if  nbicl_wave_0 > -0.869427    return  1

if  djicl_rvel_0 > 0.102279    if  djicl_phase_0 <= 0.501138    if  djicl_rvel_1 > 0.0512203    if  djilpr01cl <= 0.16067    if  djicl_rvel_0 <= 0.376304    if  djilpr01cl < 0.156685    if  djicl_ncy_0 > 1.34807    if  djicl_lvel_0 > 0.145273    if  djicl_ncy_1 <= 5.36803    return  1

if  bfxcl_amp_0 > 0.908939    if  bfxcl_offsetphase_1 >= -0.258078    if  bfxcl_amp_0 < 1.32304    if  bfxcl_phase_0 >= 0.248451    if  bfxcl_wave_1 <= 0.672789    if  bfxcl_phase_0 <= 0.430597    if  bfxcl_per_1 > 8.35079    return  1

if  bfxcl_phase_0 <= 0.433642    if  bfxcl_per_1 >= 8.3298    if  bfxcl_phase_0 < 0.438131    if  bfxcl_per_1 >= 7.70378    if  bfxcl_offsetphase_1 > -0.263076    if  bfxcl_amp_0 <= 1.29665    if  bfxcl_amp_0 > 0.921293    if  bfxcl_wave_0 >= 0.0982673    return  1

if  spenrgcl_lvel_0 > 0.0872609    if  spenrgcl_per_0 >= 31.4703    if  spenrgcl_lvel_0 < 0.137476    if  spenrgcl_bartrendsds > -0.0778304    if  spenrgbars <= 65    if  spenrgbars >= 41    if  spenrgcl_offsetphase_1 <= 0.207699    if  spenrgcl_per_0 > 32.9472    return  1

if  tnxcl_lvel_1 < 0.330599    if  tnxcl_lvel_1 > 0.0686704    if  tnxcl_lvel_1 > 0.0829088    if  tnxcl_per_0 < 33.2852    if  tnxcl_rvel_0 <= 0.293689    if  tnxcl_ncy_0 > 1.4499    if  tnxcl_ncy_1 <= 6.29766    if  tnxcl_bartrendsds > 0.0186923    return  1

if  bvspcl_offsetphase_0 < 0.0657059    if  bvsplpr01cl > -0.0686255    if  bvsplpr12cl > 0.314165    if  bvspcl_trendsds < 3.33793    if  bvspcl_wprop_1 >= -0.938046    if  bvspcl_offsetphase_0 <= 0.0700438    if  bvspcl_wave_0 <= 0.955152    if  bvspcl_wave_0 >= -0.998227    if  bvsplpr01cl >= -0.110323    return  1

if  bfxcl_phase_0 < 0.432821    if  bfxcl_per_0 > 30.0378    if  bfxcl_wave_1 < 0.435031    if  bfxcl_rvel_0 >= 0.0677167    if  bfxcl_offsetphase_0 > -0.495088    if  bfxcl_ncy_0 >= 1.0496    if  bfxcl_amp_0 <= 1.38842    if  bfxcl_amp_0 <= 1.38842    if  bfxcl_amp_0 >= 0.885921    return  1

if  djucl_ncy_1 < 3.80688    if  djucl_offsetphase_0 >= -0.163343    if  djubars > 48    if  djucl_wave_0 < 0.84974    if  djucl_lvel_0 <= 0.186965    if  djucl_phase_1 >= 0.361888    if  djucl_per_0 >= 20.1723    if  djubars > 49    return  1

if  spenrgcl_lvel_1 >= 0.155712    if  spenrgcl_lvel_1 >= 0.155712    if  spenrgcl_wsum <= 0.672103    if  spenrgcl_lvel_1 <= 0.244708    if  spenrgcl_rvel_0 >= 0.110098    if  spenrgcl_offsetphase_1 <= 0.435785    if  spenrgcl_lvel_1 <= 0.244708    if  spenrgcl_rvel_0 >= 0.110098    return  1

if  utycl_ncy_0 >= 1.74566    if  utycl_phase_1 > 0.0183895    if  utylpr12cl <= 0.74144    if  utycl_ncy_1 <= 3.18553    if  utylpr12cl >= -0.207678    if  utycl_per_0 <= 36.1657    if  utycl_ncy_0 >= 1.74566    if  utycl_phase_1 > 0.0158539    if  utycl_phase_1 > 0.0158539    return  1

if  spcdcl_phase_0 < 0.853743    if  spcdcl_ncy_1 <= 6.66251    if  spcdcl_wave_1 > -0.559536    if  spcdbars <= 51    if  spcdcl_wave_1 < 0.113647    if  spcdcl_amp_0 > 0.929272    if  spcdcl_rvel_1 >= 0.0843594    if  spcdbars <= 51    return  1

if  tnxcl_lvel_1 > 0.0788579    if  tnxcl_rvel_0 <= 0.310607    if  tnxcl_ncy_1 <= 5.30794    if  tnxcl_ncy_0 >= 1.45612    if  tnxcl_bartrendsds > 0.0154065    if  tnxcl_wave_0 > -0.905015    if  tnxcl_bartrendsds > 0.00511675    if  tnxcl_lvel_1 > 0.0788579    if  tnxcl_ncy_0 < 2.35492    return  1

if  cac40cl_rvel_0 <= 0.413461    if  cac40cl_amp_0 > 0.648987    if  cac40cl_amp_0 < 1.23144    if  cac40cl_amp_0 < 1.17337    if  cac40cl_phase_1 <= 0.907271    if  cac40cl_rvel_0 >= 0.192121    if  cac40cl_lvel_0 >= 0.243219    if  cac40cl_lvel_0 >= 0.243766    return  1

if  cac40cl_lvel_0 > 0.243062    if  cac40cl_rvel_0 >= 0.202346    if  cac40cl_phase_1 <= 0.738864    if  cac40bars > 21    if  cac40cl_rvel_0 < 0.400061    if  cac40cl_lvel_0 > 0.241034    if  cac40cl_amp_1 <= 0.995043    if  cac40cl_rvel_0 > 0.203703    return  1

if  ta125cl_wprop_0 >= -0.864933    if  ta125cl_amp_0 >= 0.961601    if  ta125cl_rvel_1 > 0.0660379    if  ta125lpr01cl <= 0.567576    if  ta125cl_per_1 >= 9.54008    if  ta125cl_per_1 < 25.6094    if  ta125lpr01cl < 0.684532    if  ta125cl_rvel_1 <= 0.204975    if  ta125cl_ncy_0 < 1.28982    return  1

if  sputcl_per_1 > 32.3274    if  sputcl_per_1 > 32.3274    if  sputcl_per_1 <= 64.8713    if  sputcl_per_1 <= 65.3726    if  sputcl_wave_0 <= 1.02991    if  sputcl_wave_0 < 0.98447    if  sputcl_amp_1 <= 0.713777    if  sputcl_lvel_0 <= 0.323458    return  1

if  spcdlpr01cl <= 0.484533    if  spcdbars <= 49    if  spcdcl_offsetphase_0 <= 0.292815    if  spcdcl_wave_0 >= -0.596015    if  spcdlpr12cl <= -0.0403373    if  spcdcl_per_1 >= 12.1315    if  spcdcl_wave_0 <= 1.14238    if  spcdlpr01cl <= 0.515288    return  1

if  hsilpr01cl <= -0.0750969    if  hsilpr01cl <= -0.0750969    if  hsicl_wprop_1 <= 0.986052    if  hsicl_wprop_0 < hsicl_wprop_1    if  hsicl_wprop_1 <= 0.981559    if  hsicl_wsum >= -1.67582    if  hsicl_wprop_0 < hsicl_wprop_1    if  hsicl_per_0 >= 25.1401    if  hsicl_wprop_1 >= hsicl_wprop_0    return  1

if  spcdcl_offsetphase_0 <= 0.337918    if  spcdlpr01cl < 0.429633    if  spcdcl_per_1 > 12.2083    if  spcdlpr12cl < -0.067836    if  spcdbars <= 49    if  spcdcl_bartrendsds < 0.138021    if  spcdcl_per_1 > 12.2083    if  spcdcl_wave_1 >= -0.569274    if  spcdcl_bartrendsds < 0.138021    return  1

if  djucl_phase_1 > 0.370835    if  djucl_lvel_0 <= 0.196517    if  djubars >= 49    if  djucl_ncy_1 <= 3.9283    if  djucl_ncy_1 <= 3.74913    if  djucl_offsetphase_0 >= -0.157513    if  djubars > 48    if  djucl_wave_0 <= 0.840477    return  1

if  sputcl_per_1 < 65.4399    if  sputcl_per_1 > 32.1659    if  sputcl_wave_0 < 0.990482    if  sputcl_wave_1 < 0.52619    if  sputcl_amp_1 < 0.724466    if  sputcl_amp_1 < 0.74444    if  sputcl_rvel_1 <= 0.216302    if  sputcl_wave_0 < 1.04369    if  sputcl_per_1 < 65.4399    return  1

if  spfincl_ncy_0 >= 1.41921    if  spfincl_wave_1 > -0.0124847    if  spfincl_rvel_1 >= 0.0413239    if  spfincl_ncy_0 >= 1.41921    if  spfincl_wprop_0 < 0.744351    if  spfincl_wave_1 > -0.0124847    if  spfincl_lvel_1 < 0.147589    return  1

if  asx200cl_rvel_1 < 0.186032    if  asx200cl_amp_0 > 1.0886    if  asx200cl_ncy_0 <= 2.32097    if  asx200cl_rvel_0 < asx200cl_lvel_0    if  asx200cl_wprop_0 <= 0.842555    if  asx200cl_wsum > -0.883073    if  asx200cl_ncy_0 <= 2.32097    if  asx200cl_rvel_1 < 0.186032    if  asx200cl_amp_0 > 1.0886    return  1

if  gdaxicl_wprop_0 < 0.59813    if  gdaxicl_wprop_1 <= gdaxicl_wprop_0    if  gdaxicl_amp_0 > 0.845356    if  gdaxicl_wprop_0 < 0.59813    if  gdaxicl_offsetphase_0 > -0.317979    if  gdaxicl_ncy_1 >= 1.26419    if  gdaxicl_amp_0 <= 1.15103    return  1

if  asx200cl_wave_1 > -0.378914    if  asx200cl_wave_0 > -0.988337    if  asx200cl_lvel_0 > asx200cl_rvel_0    if  asx200cl_wprop_0 < 0.834253    if  asx200cl_ncy_0 < 1.88368    if  asx200cl_rvel_0 <= 0.235947    if  asx200cl_phase_1 <= 0.925126    if  asx200cl_ncy_1 < 8.7903    if  asx200cl_amp_0 > 1.08342    return  1

As before, the simulated trades are enter long at the next day's close value, and exit at the following day's close value. The entry and exit days are always when U.S. stock markets were open. If the target market was closed those days, the previous actual trading day close was carried foward.

The simulated performance of buy and hold for the evaluation period data with simulated entries from 2015-01-05 through 2023-12-21 is
total return 123.282%, compound annual return 9.38%, maximum drawdown 47.58%, daily mean result 0.0356%, daily mean win 1.0967%, daily mean loss 1.0077%, number of trading days 2251, number of daily wins 1122, daily win rate 49.84%

The simulated performance using the rules for the evaluation period data was
total return 1096.94%, compound annual return 31.91%, maximum drawdown 11.26%, mean result 0.1340%, mean win 1.0980%, mean loss 0.9088%, number of trades 1854, number of wins 968, win rate 52.21%
 
Back
Top