Machine learning

Zorro can utilize included or external machine learning algorithms, for instance from R packages, for applying 'artificial intelligence' to algorithmic trading. Three internal algorithms for price prediction are available: a decision tree, a simple neural network, and a signal pattern learning system. They generate trading rules separately for any WFO cycle, asset, algo, and long or short trades. You can also provide your own function for training an individual model, or use functions from R or Python packages. An introduction into various machine learning methods can be found on Financial Hacker.

The advise functions are used to implement a machine learning algorithm just like a standard indicator:

adviseLong (int Method, var Objective, var Signal0, var Signal1, var Signal2, ..., var Signal19): var

adviseShort (int Method, var Objective, var Signal0, var Signal1, var Signal2, ... var Signal19): var

adviseLong (int Method, var Objective, var* Signals, int NumSignals): var

adviseShort (int Method, var Objective, var* Signals, int NumSignals): var

Call a machine learning algorithm for training or for prediction. In [Train] mode the function trains the algorithm to predict either a subsequent trade return or a training target (Objective). In [Test] or [Trade] mode the function calls the algorithm and returns the predicted value. Depending on algorithm, training can generate a binary model or a prediction function in C code that is stored in the Data folder. The only difference of the ..Long and ..Short variants is that they can train on the returns of long and short trades; otherwise they are identical. They can thus alternatively be used for generating two different models per algo/asset component.

Parameters:

Method

SIGNALS - don't train, but only export Signals and Objective in [Train] mode to a Data\*.csv file.
NEURAL - train and predict with a user-supplied or external machine learning algorithm. See below.
DTREE
- train and predict with a decision tree (20 signals max).
PERCEPTRON - train and predict with logistic regression (20 signals max).
PATTERN - train and predict with a signal pattern analyzer (20 signals max).
+FAST - fast pattern finding with large patterns (for PATTERN).
+FUZZY - fuzzy pattern finding for PATTERN; analog output for PERCEPTRON.
+2 .. +6 - number of pattern groups (for PATTERN).
+BALANCED - enforce similar numbers of positive and negative target values by replication (for SIGNALS, NEURAL, DTREE, PERCEPTRON).
+RETURNS - use the subsequent long or short trade for the training target, instead of the Objective parameter. The target is the profit or loss including trading costs of the immediately following trade with the same asset, algo, and matching long/short trade direction.
0 or omitted - use the method and signals of the last advise call. Only when signal parameters and trade returns are used.

Objective The training target. For instance an indicator value at the next bar, or a binary value like +1 / -1 for the sign of the next price change. The value should be in the same range as the signals. If persistent at 0, the next trade result is used, as if the RETURNS flag were set. The Objective parameter is only used in the training run and has no meaning in test or trade mode. Set the PEEK flag for accessing future prices in training.
Signal0,
... Signal19
3..20 parameters that are used as features to the machine learning algorithm for training or prediction (less than 3 signals would be interpreted as a Signals array). Use signals that carry information about the current market situation, for instance candle patterns, price differences, indicators, filters, or statistics functions. All signal values should be in the same range, for instance 0..1, -1..+1, or -100..+100, dependent on machine learning method (see remarks). Signals largely outside that range will generate a warning message. If the signals are omitted, the signals from the last advise call are used.
Signals Alternative input method, a var array of arbitrary length containing the features to the machine learning algorithm. Use this for less than 3 or more than 20 signals.
NumSignals Array length, the number of features in the Signals array.

Returns:

[Train] mode: 100 when trade results are to be trained, i.e. the RETURNS flag is set or Objective is zero. Otherwise 0.
[Test], [Trade] mode: Prediction value returned from the trained algorithm. Can be used as a signal for entering, exiting, or filtering trades. The DTREE, PERCEPTRON, PATTERN algorithms normally return a value in the -100 .. +100 range.

DTREE (Decision Tree)

A decision tree is a tree-like graph of decisions by comparing signals with fixed values. The values and the tree structure are generated in the training run. For this the training process iterates through the sets of signals and finds the signal values with the lowest information entropy. These values are then used to split the data space in a profitable and a non profitable part, then the process continues with iterating through the parts. Details about the decision tree algorithm can be found in books about machine learning.

The signals should be normalized roughly to the -100..100 range for best precision. They should be carefully selected so that the displayed prediction accuracy is well above 60% in all WFO cycles. Decision trees work best with signals that are independent of each other. They do not work very well when the prediction depends on a linear combination of the signals. In order to reduce overfitting, the resulting trees are pruned by removing non-predictive signals. The output of the tree is a number between -100 .. +100 dependent on the predictive quality of the current signal combination.

The decision tree functions are stored in C source code in the \Data\*.c file. The functions are automatically included in the strategy script and used by the advise function in test and trade mode. They can also be exported for using them in strategy scripts or expert advisors of other platforms. The example below is a typical Zorro-generated decision tree:

int EURUSD_S(var* sig)
{
if(sig[1] <= 12.938) {
if(sig[3] <= 0.953) return -70;
else {
if(sig[2] <= 43) return 25;
else {
if(sig[3] <= 0.962) return -67;
else return 15;
}
}
}
else {
if(sig[3] <= 0.732) return -71;
else {
if(sig[1] > 30.61) return 27;
else {
if(sig[2] > 46) return 80;
else return -62;
}
}
}
}

The advise() call used 5 signals, of which the first and the last one - sig[0] and sig[4] - had no predictive power, and thus were pruned and do not appear in the tree. Unpredictive signals are displayed in the message window.

Example of a script for generating a decision tree:

void run()
    {
      BarPeriod = 60;
      LookBack = 150;
      TradesPerBar = 2;
      if(Train) Hedge = 2;
      set(RULES|TESTNOW);
    // generate price series
      vars H = series(priceHigh()), 
        L = series(priceLow()),
        C = series(priceClose());
    
    // generate some signals from H,L,C in the -100..100 range
      var Signals[2]; 
      Signals[0] = (LowPass(H,1000)-LowPass(L,1000))/PIP;
      Signals[1] = 100*FisherN(C,100);
    
    // train and trade the signals 
      Stop = 4*ATR(100); 
      TakeProfit = 4*ATR(100);
      if(adviseLong(DTREE,0,Signals,2) > 0)
        enterLong();
      if(adviseShort(DTREE,0,Signals,2) > 0)
        enterShort();
    }

PERCEPTRON

A perceptron, also called a logistic regression function, is a simple neural net consisting of one neuron with one output and up to 20 signal inputs. It calculates its predictions from a linear combination of weighted signals. It has some similarity to the polyfit algorithm, but with arbitrary variables instead of powers of a single variable, and with a binary output. A short perceptron algorithm description can be found on the machine learning overview. The signal weights are generated in the training run for producing the best possible prediction. The signals should be in the single digit range, like -1...1.

The perceptron algorithm works best when the weighted sum of the signals has predictive power. It does not work well when the prediction requires a nonlinear signal combination, i.e., when trade successes and failures are not separated by a straight plane in the signal space. A classical example of a function that a perceptron cannot emulate is a logical XOR. Often a perceptron can be used where a decision tree fails, and vice versa.

The perceptron learning algorithm generates prediction functions in C source code in the \Data\*.c file. The functions are automatically included in the strategy script and used by the advise function in test and trade mode. They can also be exported for using them in strategy scripts or expert advisors of other platforms. The output is >0 for a positive or <0 for a negative prediction. The output magnitude is the probability associated with the prediction, in percent, e.g., 70 for 70% estimated probability. A generated perceptron function with 3 signals and binary output looks like this:

int EURUSD_S(var* sig)
{
if(-27.99*sig[0]+1.24*sig[1]-3.54*sig[2] > -21.50)
return 70;
else
return -70;
}

In FUZZY mode the output magnitude is equivalent to the prediction strength, thus allowing to ignore signals below a threshold. The scaling factor, 2.50 in the example below, is calculated so that the average perceptron return value of the training set has a magnitude of 50. Example of a fuzzy perceptron:

int EURUSD_S(var* sig)
{
return (-27.99*sig[0]+1.24*sig[1]-3.54*sig[2]-21.50)*2.50;
}

Signals that do not contain predictive market information get a weight of 0.

PATTERN (Pattern Analyzer)

The pattern analyzer is an intelligent version of classic candle pattern indicators. It does not use predefined patterns, but learns them from historical price data. It's normally fed with up to 20 open, close, high or low prices of a number of candles. It compares every signal with every other signal, and uses the comparison results - greater, smaller, or equal - for classifying the pattern.

The signals can be divided into groups with the PATTERN+2 .. PATTERN+6 methods. They divide the signals into up to six pattern groups and only compare signals within the same group. This is useful when, for instance, only the first two candles and the last two candles of a 3-candle pattern should be compared with each other, but not the first candle with the third candle. PATTERN+2 requires an even number of signals, of which the first half belongs to the first and the second half to the second group. PATTERN+3 likewise requires a number of signals that is divisible by 3, and so on. Pattern groups can share signals - for instance, the open, high, low, and close of the middle candle can appear in the first as well as in the second group - as long as the total number of signals does not exceed 20.

Aside from the grouping, Zorro makes no assumptions of the signals and their relations. Therefore the pattern analyzer can be also used for other signals than candle prices. All signals within a pattern group should have the same unit for being comparable, but different groups can have different units. For candle patterns, usually the high, low, and close of the last 3 bars are used for the signals - the open is not needed as it's normally identical with the close of the previous candle. More signals, such as the moving average of the price, can be added for improving the prediction (but in most cases won't).

For simple patterns with few signals, the pattern analyzer can generate direct pattern finding functions in plain C source code in the \Data\*.c file. These functions are automatically included in the strategy script and used by the advise function in test and trade mode. They can also be exported to other platforms. They find all patterns that occurred 4 or more times in the training data set and had a positive profit expectancy. They return the pattern's information ratio - the ratio of profit mean to standard deviation - multiplied with 100. The better the information ratio, the more predictive is the pattern. A typical pattern finding function with 12 signals looks like this:

int EURUSD_S(float* sig)
    {
      if(sig[1]<sig[2] && eqF(sig[2]-sig[4]) && sig[4]<sig[0] && sig[0]<sig[5] && sig[5]<sig[3]
        && sig[10]<sig[11] && sig[11]<sig[7] && sig[7]<sig[8] && sig[8]<sig[9] && sig[9]<sig[6])
          return 19;
      if(sig[4]<sig[1] && sig[1]<sig[2] && sig[2]<sig[5] && sig[5]<sig[3] && sig[3]<sig[0] && sig[7]<sig[8]
        && eqF(sig[8]-sig[10]) && sig[10]<sig[6] && sig[6]<sig[11] && sig[11]<sig[9])
          return 170;
      if(sig[1]<sig[4] && eqF(sig[4]-sig[5]) && sig[5]<sig[2] && sig[2]<sig[3] && sig[3]<sig[0]
        && sig[10]<sig[7] && eqF(sig[7]-sig[8]) && sig[8]<sig[6] && sig[6]<sig[11] && sig[11]<sig[9])
          return 74;
      if(sig[1]<sig[4] && sig[4]<sig[5] && sig[5]<sig[2] && sig[2]<sig[0] && sig[0]<sig[3] && sig[7]<sig[8]
        && eqF(sig[8]-sig[10]) && sig[10]<sig[11] && sig[11]<sig[9] && sig[9]<sig[6])
          return 143;
      if(sig[1]<sig[2] && eqF(sig[2]-sig[4]) && sig[4]<sig[5] && sig[5]<sig[3] && sig[3]<sig[0]
        && sig[10]<sig[7] && sig[7]<sig[8] && sig[8]<sig[6] && sig[6]<sig[11] && sig[11]<sig[9])
          return 168;
      ....
      return 0;
    }

The eqF function in the code above checks if two signals are equal. Signals that differ less than the FuzzyRange are considered equal.

There are two additional special methods for the pattern analyzer. The FUZZY method generates a pattern finding function that also finds patterns that can slightly deviate from the profitable patterns in the training data set. It gives patterns a higher score when they 'match better'. The deviation can be set up with FuzzyRange. A typical fuzzy pattern finding function looks like this:

int EURUSD_S(float* sig)
    {
      double result = 0.;
      result += belowF(sig[1],sig[4]) * belowF(sig[4],sig[2]) * belowF(sig[2],sig[5]) * belowF(sig[5],sig[3]) * belowF(sig[3],sig[0])
        * equF(sig[10],sig[11]) * belowF(sig[11],sig[7]) * belowF(sig[7],sig[8]) * belowF(sig[8],sig[9]) * belowF(sig[9],sig[6]) * 19;
      result += belowF(sig[4],sig[5]) * belowF(sig[5],sig[1]) * belowF(sig[1],sig[2]) * belowF(sig[2],sig[3]) * belowF(sig[3],sig[0])
        * belowF(sig[10],sig[7]) * belowF(sig[7],sig[11]) * belowF(sig[11],sig[8]) * belowF(sig[8],sig[9]) * belowF(sig[9],sig[6]) * 66;
      result += belowF(sig[4],sig[1]) * belowF(sig[1],sig[2]) * belowF(sig[2],sig[0]) * belowF(sig[0],sig[5]) * belowF(sig[5],sig[3])
        * belowF(sig[10],sig[11]) * belowF(sig[11],sig[7]) * belowF(sig[7],sig[8]) * belowF(sig[8],sig[6]) * belowF(sig[6],sig[11]) * 30;
      result += belowF(sig[1],sig[4]) * belowF(sig[4],sig[2]) * belowF(sig[2],sig[5]) * belowF(sig[5],sig[3]) * belowF(sig[3],sig[0])
        * belowF(sig[7],sig[10]) * belowF(sig[10],sig[11]) * belowF(sig[11],sig[8]) * belowF(sig[8],sig[6]) * belowF(sig[6],sig[9]) * 70;
      result += belowF(sig[4],sig[5]) * belowF(sig[5],sig[1]) * belowF(sig[1],sig[2]) * belowF(sig[2],sig[3]) * belowF(sig[3],sig[0])
        * belowF(sig[7],sig[10]) * belowF(sig[10],sig[8]) * belowF(sig[8],sig[11]) * belowF(sig[11],sig[9]) * belowF(sig[9],sig[6]) * 108;
      ...
      return result;
    }

The belowF function is described on the Fuzzy Logic page.

The FAST method does not generate C code; instead it generates a list of patterns that are classified with alphanumeric names. For finding a pattern, it is classified and its name compared with the pattern list. This is about 4 times faster than the pattern finding function in C code, and can also handle bigger and more complex patterns with up to 20 signals. It can make a remarkable difference in backtest time or when additional parameters have to be trained. A pattern name list looks like this (the numbers behind the name are the information ratios):

/* Pattern list for EURUSD_S
HIECBFGAD 61
BEFHCAIGD 152
EHBCIFGAD 73
BEFHCIGDA 69
BHFECAIGD 95
BHIFECGAD 86
HBEIFCADG 67
HEICBFGDA 108 ...*/

The FAST method cannot be used in combination with FUZZY or with FuzzyRange. But the FAST as well as the FUZZY method can be combined with pattern groups (e.g., PATTERN+FAST+2).

The find rate of the pattern analyzer can be adjusted with two variables:

PatternCount

The minimum number of occurrences of the found patterns in the analyzed price curve; default = 4.

PatternRate

The minimum win rate of the found patterns, in percent; default = 50.

An example of a pattern trading script can be found in Workshop 7.

NEURAL (General Machine Learning)

The NEURAL method uses an external machine learning library, for instance a support vector machine, random forest, or a deep learning neural network for predicting the next price or next trade return in an algo trading system. Many machine learning libraries are available in R packages or Python libraries; therefore the NEURAL method will often call R or Python functions for training and prediction. But any DLL-based machine learning library can be used as long as the neural function - see below - is adapted to it.

neural (int Status, int Model, int NumSignals, void* Data): var

This function is automatically called several times during the training, test, or trade process. It has access to all global and predefined variables. Its behavior depends on Status:

Status Parameters Called Description
NEURAL_INIT --- After the INITRUN Initialize the machine learning library (e.g., by calling Rstart); return 0 if the initialization failed, otherwise 1. The script is aborted if the system cannot be initialized.
NEURAL_EXIT --- After the EXITRUN Release the machine learning library if required.
NEURAL_TRAIN Model, NumSignals, Data After any WFOCycle in train mode

Batch training. Train the model with the given Model index with all data samples collected in the previous training run or WFO training cycle. The Model index is incremented for any asset, algo, and long/short combination. The list of samples is given in CSV format in the Data string. The columns of the CSV table are the signals, the last column is the Objective parameter or the trade result. The accuracy or loss in percent can be optionally returned by the neural function; otherwise return 1 if no accuracy or loss is calculated, or 0 for aborting the script when the training failed.

NEURAL_PREDICT Model, NumSignals, Data By any advise call in test/trade mode Return the value predicted by the model with the given Model index, using the signals contained in the Data double array.
NEURAL_SAVE Data After any WFOCycle in train mode Save all models trained in the previous training run or WFO training cycle to the file with the name given by the string Data.
NEURAL_LOAD Data Before any WFOCycle in test mode, at session begin in trade mode.  Prepare the prediction by loading all trained models from the file with the name given by the string Data. Also called in trade mode whenever the model file was updated by re-training.

Model is the number of the trained model, for instance a set of decision rules, or a set of weights of a neural network, starting with 0. When several models are trained for long and short predictions and for different assets or algos, the index selects the suited model. The number of models is therefore normally equal to the number of advise calls per run cycle. All trained models are saved to a *.ml file at the end of every WFO cycle. In R, the models are normally stored in a list of lists and accessed through their index (e.g., Models[[model+1]]). Any additional parameter set generated in the training process - for instance, a set of normalization factors, or selection masks for the signals - can be saved as part of the model.

The NumSignals parameter is the number of signals passed to the advise function. It is normally identical to the number of trained features.

The Data parameter provides data to the function. The data can be of different types. For NEURAL_LEARN/NEURAL_PREDICT it's a pointer to a double array of length NumSignals+1, containing the signal values plus the prediction objective or trade result at the end. Note that a plain data array has no "dim names" or other R gimmicks - if they are needed in the R training or predicting function, add them there. For NEURAL_TRAIN the Data parameter is a text string containing all samples in CSV format. The string can be stored in a temporary CSV file and then read by the machine learning algorithm for training the model. For NEURAL_SAVE/NEURAL_LOAD the Data parameter is the suggested file name for saving or loading all trained models of the current WFO cycle in the Data folder. Use the slash(string) function for converting backslashes to slashes when required for R file paths.

The code below is the default neural function in the r.h file for using an R machine learning algorithm. A 64-bit variant for Python can be found in pynet.cpp. If required for special purposes, the default neural function can be replaced by a user-supplied function.

var neural(int Status, int model, int NumSignals, void* Data)
{
if(!wait(0)) return 0;
// open an R script with the same name as the strategy script
if(Status == NEURAL_INIT) {
if(!Rstart(strf("%s.r",Script),2)) return 0;
Rx("neural.init()");
return 1;
}
// export batch training samples and call the R training function
if(Status == NEURAL_TRAIN) {
string name = strf("Data\\signals%i.csv",Core);
file_write(name,Data,0);
Rx(strf("XY <- read.csv('%s%s',header = F)",slash(ZorroFolder),slash(name)));
Rset("AlgoVar",AlgoVar,8);
if(!Rx(strf("neural.train(%i,XY)",model+1),2))
return 0;
return 1;
}
// predict the target with the R predict function
if(Status == NEURAL_PREDICT) {
Rset("AlgoVar",AlgoVar,8);
Rset("X",(double*)Data,NumSignals);
Rx(strf("Y <- neural.predict(%i,X)",model+1));
return Rd("Y[1]");
}
// save all trained models
if(Status == NEURAL_SAVE) {
print(TO_ANY,"\nStore %s",strrchr(Data,'\\')+1);
return Rx(strf("neural.save('%s')",slash(Data)),2);
}
// load all trained models
if(Status == NEURAL_LOAD) {
printf("\nLoad %s",strrchr(Data,'\\')+1);
return Rx(strf("load('%s')",slash(Data)),2);
}
return 1;
}

The default neural function requires an R script with the same name as the strategy script, but extension .r instead of .c. There is also a Python version available. The R or Python script must contain the following functions:

neural.init() initializes the machine learning package, defines a model list, sets the initial seed, number of cores, GPU/CPU context, etc.

neural.train(Model, XY) trains the algorithm and stores the trained model in the model list. XY is a data frame with signals in the first NumSignals columns and the objective in the last (NumSignals+1) column. The samples are the rows of the data frame. Model is the number in the model list where the trained model is to be stored. The model can be any R object combined from the machine learning model and any additional data, e.g., a set of normalization factors. In the Python version, XY is the file name of a .csv file with the data.

neural.save(FileName) stores all models from the model list to a file with the given name and path. This function is called once per WFO cycle, so any cycle generates a file of all models of this cycle.

neural.load(FileName) loads back the previously stored model list for the current WFO cycle. If this optional function is missing, the model list is loaded with the R load() function. Otherwise, make sure to load the list into the global R environment so that it is accessible to neural.predict, e.g., with load(FileName, envir=.GlobalEnv).

neural.predict(Model, X) predicts the objective from the signals. X is a vector of size NumSignals, containing the signal values for a single prediction. Model is the number of the model in the model list. The neural.predict return value is returned by the advise function. The neural.predict function could also support batch predictions where X is a data frame with several samples and a vector Y is returned. Batch predictions are not used by Zorro, but useful for calibrating the algorithm with a training/test data set generated in SIGNALS mode.

 An example of such a deep learning system can be found on Financial Hacker. A simple machine learning strategy DeepLearn.c is included, together with R scripts for running it with the Deepnet, H2O, MxNet, or Keras deep learning packages. A .cpp version is available for PyTorch. The neural function is compatible with all Zorro trading, test, and training methods, including walk forward analysis, multi-core parallel training, and multiple assets and algorithms. A short description of installation and usage of popular deep learning packages can be found here.

Remarks:

  • The RULES flag must be set for generating rules or training a machine learning algorithm. See the remarks under Training for special cases when RULES and PARAMETERS are generated at the same time.
  • All advise calls for a given asset/algo combination must use the same Method, the same signals, and the same training target type (either Objective or the next trade return). However, different methods and signals can be used for different assets and algorithm identifiers, so call algo() for combining multiple advise methods in the same script. Ensemble or hybrid methods can also be implemented this way, using different algo identifiers.
  • The generated rules by DTREE, PERCEPTRON, PATTERN are stored in plain C code in *.c files in the Data folder. This allows exporting the rules to other platforms, and to examine the prediction process to some degree for checking if the rules make sense. Models by external machine learning algorithms are stored in *.ml files in the Data folder. If several models are trained, they are numbered in the order of advise calls and WFO cycles. E.g., for two advise calls, two assets, and 7 WFO cycles, 28 models are generated, 4 for any cycle.
  • For considering the effects of stops, profit targets, and transaction costs in training, call the advise function with Objective = 0 before entering a trade. Zorro will then use the result of the next trade for training the rules. When training long and short trades at the same time, set Hedge to make sure that they can be opened simultaneously in training mode; otherwise, any trade would close an opposite trade just entered before. Define an exit condition for the trade, such as a timed exit after a number of bars, or a stop/trailing mechanism for predicting a positive or negative excursion. Do not train rules with hedging explicitly disabled or with special modes such as NFA or Virtual Hedging.
  • Predictions are normally better with fewer signals, so use as few signals as possible, or pre-process the signals with an algorithm to reduce dimensionality. For more than 20 signals, use a Signals array. The signals for the DTREE, PATTERN, and PERCEPTRON methods are always limited to 20.
  • Most machine learning algorithms require balanced training data. That means the trades used for training should result in about 50% wins and 50% losses, and negative and positive Objective values should be equally distributed. The +BALANCED flag will duplicate samples until a 50/50 balance is reached. The used algorithm balances the samples also locally, so arbitrary subsets ('batches') of the samples are still balanced.
  • The SIGNALS method exports data for testing and calibrating external machine learning algorithms. It is normally recommended to also use BALANCED data for that. The data is stored in a CSV file with the signals in the first columns and the objective in the last column.
  • During inactive time periods determined by LookBack, BarMode, a SKIP flag, or TimeFrame, the advise functions won't train or predict and return 0.
  • Functions like scale or Normalize can be used to convert the signals to the same range. Some R machine learning algorithms require that signals and targets are in the 0..1 range; in that case, negative values or values greater than 1 lead to wrong results. If a signal value is outside the +/-1000 range, a warning is issued.
  • When using a future value for prediction, such as the price change of the next bars (e.g., Objective = priceClose(-5) - priceClose(0)), make sure to set the PEEK flag in train mode. Alternatively, use the price change of a past bar to the current bar, and pass past signals - e.g., from a series - to the advise function in train mode, e.g.:
      Objective = priceClose(0) - priceClose(5);
      int Offset = ifelse(Train,5,0);
      var Prediction = adviseLong(Method,Objective,Signal0[Offset],Signal1[Offset],...);
  • All machine learning functions have a tendency to overfit the strategy. For this reason, strategies that use the advise function should always be tested with unseen data, e.g., by using Out-Of-Sample testing or Walk Forward Optimization. Use the OPENEND flag to prevent trades from prematurely closing at the end of a WFO training period and thus reducing the training quality. When WFO training, make sure to set DataHorizon accordingly to prevent peeking bias in the subsequent test cycle.
  • A message like "NEURAL_INIT: failed" hints that the required R packages are not installed. Make sure that you can source the R script and that the train/predict functions run with no error message.
  • When WFO training a machine learning algorithm with multiple cores, check if the used R libraries are compatible with multiple processes. This is normally not the case when hardware resources, such as the GPU, or when multiple CPU cores are used. The R neural.init function is called at the begin of any process, which can lead to different results in single core and multi core when a random seed is set in the neural.init function.
  • For training multiple objectives with the NEURAL method, pass the further objectives as Signal parameters to the advise function in training mode. In test and trade mode, use a separate prediction function that loads the current model and returns the predictions from R in a vector.
  • For R debugging, use the R sink function for printing R output to a file. Or use the save.image / load functions for storing the current variable and function state and examining it later.
  • For internal machine learning algorithms, the estimated prediction accuracy or the number of found patterns is printed in the message window. The signals should be selected in a way that the prediction accuracy is well above 50% or that as many as possible profitable patterns are found. For external machine learning functions, use a confusion matrix or the built-in metrics for determining the prediction accuracy.
  • When rules are stored in C code, the internal lite-C compiler compiles in 32-bit mode unless FAST is set. Since the compiler does not support freeing parts of the code – only the complete code – the code is growing on any run cycle, thus increasing the memory footprint. The memory is only freed when the script is compiled again.

See also:

optimize, frechet, polyfit, predict, Workshop 7, R Bridge, deep learning

► latest version online

Machine Learning

Zorro kann integrierte oder externe Machine-Learning-Algorithmen nutzen, zum Beispiel aus R-Paketen, um 'künstliche Intelligenz' auf algorithmisches Trading anzuwenden. Drei interne Algorithmen zur Preisprognose sind verfügbar: ein Entscheidungsbaum, ein einfaches neuronales Netzwerk und ein Signal-Muster-Lernsystem. Sie generieren Handelsregeln separat für jeden WFO-Zyklus, Asset, Algo und Long- oder Short-Trades. Sie können auch Ihre eigene Funktion zum Trainieren eines individuellen Modells bereitstellen oder Funktionen aus R- oder Python-Paketen verwenden. Eine Einführung in verschiedene Machine-Learning-Methoden finden Sie auf Financial Hacker.

Die advise-Funktionen werden verwendet, um einen Machine-Learning-Algorithmus genauso wie einen Standardindikator zu implementieren:

adviseLong (int Method, var Objective, var Signal0, var Signal1, var Signal2, ..., var Signal19): var

adviseShort (int Method, var Objective, var Signal0, var Signal1, var Signal2, ... var Signal19): var

adviseLong (int Method, var Objective, var* Signals, int NumSignals): var

adviseShort (int Method, var Objective, var* Signals, int NumSignals): var

Rufen Sie einen Machine-Learning-Algorithmus zum Trainieren oder zur Vorhersage auf. Im [Train]-Modus trainiert die Funktion den Algorithmus, um entweder einen nachfolgenden Trade-Rendite oder ein Trainingsziel (Objective) vorherzusagen. Im [Test] oder [Trade]-Modus ruft die Funktion den Algorithmus auf und gibt den vorhergesagten Wert zurück. Abhängig vom Algorithmus kann das Training ein binäres Modell oder eine Vorhersagefunktion im C-Code generieren, die im Data-Ordner gespeichert wird. Der einzige Unterschied der ..Long und ..Short Varianten besteht darin, dass sie auf die Renditen von Long- und Short-Trades trainieren können; ansonsten sind sie identisch. Sie können somit alternativ verwendet werden, um zwei verschiedene Modelle pro Algo/Asset-Komponente zu generieren.

Parameter:

Methode

SIGNALS - nicht trainieren, sondern nur Signals und Objective im [Train]-Modus in eine Data\*.csv-Datei exportieren.
NEURAL - trainieren und vorhersagen mit einem benutzerdefinierten oder externen Machine-Learning-Algorithmus. Siehe unten.
DTREE
- trainieren und vorhersagen mit einem Entscheidungsbaum (max. 20 Signale).
PERCEPTRON - trainieren und vorhersagen mit logistischer Regression (max. 20 Signale).
PATTERN - trainieren und vorhersagen mit einem Signal-Muster-Analysator (max. 20 Signale).
+FAST - schnelles Musterfinden mit großen Mustern (für PATTERN).
+FUZZY - unscharfes Musterfinden für PATTERN; analoger Output für PERCEPTRON.
+2 .. +6 - Anzahl der Mustergruppen (für PATTERN).
+BALANCED - erzwingt ähnliche Zahlen von positiven und negativen Zielwerten durch Replikation (für SIGNALS, NEURAL, DTREE, PERCEPTRON).
+RETURNS - verwendet den nachfolgenden Long- oder Short-Trade als Trainingsziel anstelle des Objective Parameters. Das Ziel ist der Gewinn oder Verlust einschließlich Handelskosten des unmittelbar folgenden Trades mit demselben Asset, Algo und passender Long/Short-Traderrichtung.
0 oder ausgelassen - verwendet die Methode und Signale des letzten advise-Aufrufs. Nur wenn Signaleparameter und Trade-Renditen verwendet werden.

Ziel Das Trainingsziel. Zum Beispiel ein Indikatorwert beim nächsten Balken oder ein binärer Wert wie +1 / -1 für das Vorzeichen der nächsten Preisänderung. Der Wert sollte im gleichen Bereich wie die Signale liegen. Wenn auf 0 gesetzt, wird das Ergebnis des nächsten Trades verwendet, als ob das RETURNS-Flag gesetzt wäre. Der Ziel-Parameter wird nur im Trainingslauf verwendet und hat keine Bedeutung im Test- oder Trade-Modus. Setzen Sie das PEEK-Flag, um im Training auf zukünftige Preise zuzugreifen.
Signal0,
... Signal19
3..20 Parameter, die als Merkmale für den Machine-Learning-Algorithmus zum Trainieren oder Vorhersagen verwendet werden (weniger als 3 Signale werden als Signals-Array interpretiert). Verwenden Sie Signale, die Informationen über die aktuelle Marktsituation tragen, zum Beispiel Kerzenmuster, Preisunterschiede, Indikatoren, Filter oder Statistikfunktionen. Alle Signalwerte sollten im gleichen Bereich liegen, zum Beispiel 0..1, -1..+1 oder -100..+100, abhängig von der Machine-Learning-Methode (siehe Anmerkungen). Signale, die weit außerhalb dieses Bereichs liegen, erzeugen eine Warnmeldung. Wenn die Signale weggelassen werden, werden die Signale des letzten advise-Aufrufs verwendet.
Signals Alternative Eingabemethode, ein var-Array beliebiger Länge, das die Merkmale für den Machine-Learning-Algorithmus enthält. Verwenden Sie dies für weniger als 3 oder mehr als 20 Signale.
AnzahlSignale Länge des Arrays, die Anzahl der Merkmale im Signals-Array.

Rückgaben:

[Train]-Modus: 100, wenn Trade-Ergebnisse trainiert werden sollen, d.h. das RETURNS-Flag gesetzt ist oder Ziel null ist. Andernfalls 0.
[Test], [Trade]-Modus: Vorhersagewert, der vom trainierten Algorithmus zurückgegeben wird. Kann als Signal zum Ein- und Aussteigen oder Filtern von Trades verwendet werden. Die Algorithmen DTREE, PERCEPTRON, PATTERN geben normalerweise einen Wert im Bereich -100 .. +100 zurück.

DTREE (Entscheidungsbaum)

Ein Entscheidungsbaum ist ein baumartiges Entscheidungsdiagramm, bei dem Signale mit festen Werten verglichen werden. Die Werte und die Baumstruktur werden im Trainingslauf generiert. Dabei durchläuft der Trainingsprozess die Signalsets und findet die Signalwerte mit der niedrigsten Informationsentropie. Diese Werte werden dann verwendet, um den Datenraum in einen profitablen und einen nicht profitablen Teil zu spalten, dann wird der Prozess mit der Iteration durch die Teile fortgesetzt. Details zum Entscheidungsbaum-Algorithmus finden Sie in Büchern über Machine Learning.

Die Signale sollten grob auf den Bereich -100..100 normalisiert werden, um die beste Präzision zu erzielen. Sie sollten sorgfältig ausgewählt werden, sodass die angezeigte Vorhersagegenauigkeit in allen WFO-Zyklen deutlich über 60% liegt. Entscheidungsbäume funktionieren am besten mit Signalen, die unabhängig voneinander sind. Sie funktionieren nicht sehr gut, wenn die Vorhersage von einer linearen Kombination der Signale abhängt. Um Overfitting zu reduzieren, werden die resultierenden Bäume beschnitten, indem unvorhersagende Signale entfernt werden. Die Ausgabe des Baumes ist eine Zahl zwischen -100 .. +100, abhängig von der Vorhersagequalität der aktuellen Signalkombination.

Die Entscheidungsbaum-Funktionen werden im C-Quellcode in der \Data\*.c-Datei gespeichert. Die Funktionen werden automatisch in das Strategieskript eingebunden und von der advise-Funktion im Test- und Trade-Modus verwendet. Sie können auch exportiert werden, um sie in Strategieskripten oder Expert Advisors anderer Plattformen zu verwenden. Das untenstehende Beispiel ist ein typischer von Zorro generierter Entscheidungsbaum:

int EURUSD_S(var* sig)
{
if(sig[1] <= 12.938) {
if(sig[3] <= 0.953) return -70;
else {
if(sig[2] <= 43) return 25;
else {
if(sig[3] <= 0.962) return -67;
else return 15;
}
}
}
else {
if(sig[3] <= 0.732) return -71;
else {
if(sig[1] > 30.61) return 27;
else {
if(sig[2] > 46) return 80;
else return -62;
}
}
}
}

Der advise()-Aufruf verwendete 5 Signale, von denen das erste und das letzte - sig[0] und sig[4] - keine Vorhersagekraft hatten und daher beschnitten wurden und nicht im Baum erscheinen. Unvorhersagende Signale werden im Nachrichtenfenster angezeigt.

Beispiel für ein Skript zur Generierung eines Entscheidungsbaums:

void run()
    {
      BarPeriod = 60;
      LookBack = 150;
      TradesPerBar = 2;
      if(Train) Hedge = 2;
      set(RULES|TESTNOW);
    // generiere Preisreihen
      vars H = series(priceHigh()), 
        L = series(priceLow()),
        C = series(priceClose());
    
    // generiere einige Signale aus H, L, C im Bereich -100..100
      var Signals[2]; 
      Signals[0] = (LowPass(H,1000)-LowPass(L,1000))/PIP;
      Signals[1] = 100*FisherN(C,100);
    
    // trainiere und handle die Signale 
      Stop = 4*ATR(100); 
      TakeProfit = 4*ATR(100);
      if(adviseLong(DTREE,0,Signals,2) > 0)
        enterLong();
      if(adviseShort(DTREE,0,Signals,2) > 0)
        enterShort();
    }

PERCEPTRON

Ein Perzeptron, auch als logistische Regression Funktion bezeichnet, ist ein einfaches neuronales Netzwerk, bestehend aus einem Neuron mit einem Ausgang und bis zu 20 Signaleingängen. Es berechnet seine Vorhersagen aus einer linearen Kombination gewichteter Signale. Es weist einige Ähnlichkeiten mit dem polyfit-Algorithmus auf, jedoch mit beliebigen Variablen anstelle von Potenzen einer einzigen Variable und mit einem binären Ausgang. Eine kurze Beschreibung des Perzeptron-Algorithmus finden Sie auf der Machine Learning Übersicht. Die Signalgewichte werden im Trainingslauf generiert, um die bestmögliche Vorhersage zu erzielen. Die Signale sollten sich im einstelligen Bereich befinden, wie -1...1.

Der Perzeptron-Algorithmus funktioniert am besten, wenn die gewichtete Summe der Signale Vorhersagekraft hat. Er funktioniert nicht gut, wenn die Vorhersage eine nichtlineare Signalkombination erfordert, d.h., wenn Trades nicht durch eine gerade Ebene im Signalspeicher getrennt werden. Ein klassisches Beispiel für eine Funktion, die ein Perzeptron nicht emulieren kann, ist ein logisches XOR. Oft kann ein Perzeptron verwendet werden, wo ein Entscheidungsbaum scheitert, und umgekehrt.

Der Perzeptron-Lernalgorithmus generiert Vorhersagefunktionen im C-Quellcode in der \Data\*.c-Datei. Die Funktionen werden automatisch in das Strategieskript eingebunden und von der advise-Funktion im Test- und Trade-Modus verwendet. Sie können auch exportiert werden, um sie in Strategieskripten oder Expert Advisors anderer Plattformen zu verwenden. Die Ausgabe ist >0 für eine positive oder <0 für eine negative Vorhersage. Die Ausgabemagnitude ist die mit der Vorhersage verbundene Wahrscheinlichkeit in Prozent, z.B. 70 für eine geschätzte Wahrscheinlichkeit von 70%. Eine generierte Perzeptron-Funktion mit 3 Signalen und binärem Ausgang sieht wie folgt aus:

int EURUSD_S(var* sig)
{
if(-27.99*sig[0]+1.24*sig[1]-3.54*sig[2] > -21.50)
return 70;
else
return -70;
}

Im FUZZY-Modus ist die Ausgabemagnitude äquivalent zur Vorhersagekraft, was es ermöglicht, Signale unterhalb eines Schwellenwerts zu ignorieren. Der Skalierungsfaktor, 2.50 im untenstehenden Beispiel, wird so berechnet, dass der durchschnittliche Perzeptron-Rückgabewert des Trainingssatzes eine Magnitude von 50 hat. Beispiel für ein unscharfes Perzeptron:

int EURUSD_S(var* sig)
{
return (-27.99*sig[0]+1.24*sig[1]-3.54*sig[2]-21.50)*2.50;
}

Signale, die keine vorhersagenden Marktinformationen enthalten, erhalten ein Gewicht von 0.

PATTERN (Muster-Analysator)

Der Muster-Analysator ist eine intelligente Version klassischer Kerzenmuster-Indikatoren. Er verwendet keine vordefinierten Muster, sondern lernt diese aus historischen Preisdaten. Er wird normalerweise mit bis zu 20 Open-, Close-, High- oder Low-Preisen einer Anzahl von Kerzen gefüttert. Er vergleicht jedes Signal mit jedem anderen Signal und verwendet die Vergleichsergebnisse – größer, kleiner oder gleich – zur Klassifizierung des Musters.

Die Signale können mit den Methoden PATTERN+2 .. PATTERN+6 in Gruppen unterteilt werden. Sie teilen die Signale in bis zu sechs Mustergruppen und vergleichen nur Signale innerhalb derselben Gruppe. Dies ist nützlich, wenn zum Beispiel nur die ersten beiden Kerzen und die letzten beiden Kerzen eines 3-Kerzen-Musters miteinander verglichen werden sollen, aber nicht die erste Kerze mit der dritten Kerze. PATTERN+2 erfordert eine gerade Anzahl von Signalen, von denen die erste Hälfte zur ersten und die zweite Hälfte zur zweiten Gruppe gehört. PATTERN+3 erfordert ebenfalls eine durch 3 teilbare Anzahl von Signalen und so weiter. Mustergruppen können Signale gemeinsam nutzen – zum Beispiel können Open, High, Low und Close der mittleren Kerze sowohl in der ersten als auch in der zweiten Gruppe erscheinen – solange die Gesamtzahl der Signale 20 nicht überschreitet.

Abgesehen von der Gruppierung trifft Zorro keine Annahmen über die Signale und ihre Beziehungen. Daher kann der Muster-Analysator auch für andere Signale als Kerzenpreise verwendet werden. Alle Signale innerhalb einer Mustergruppe sollten die gleiche Einheit haben, um vergleichbar zu sein, aber verschiedene Gruppen können unterschiedliche Einheiten haben. Für Kerzenmuster werden normalerweise die High-, Low- und Close-Preise der letzten 3 Bars für die Signale verwendet – der Open ist nicht notwendig, da er normalerweise mit dem Close der vorherigen Kerze identisch ist. Weitere Signale, wie der gleitende Durchschnitt des Preises, können zur Verbesserung der Vorhersage hinzugefügt werden (aber in den meisten Fällen nicht).

Für einfache Muster mit wenigen Signalen kann der Muster-Analysator direkte Musterfindungsfunktionen im einfachen C-Quellcode in der \Data\*.c-Datei generieren. Diese Funktionen werden automatisch in das Strategieskript eingebunden und von der advise-Funktion im Test- und Trade-Modus verwendet. Sie können auch exportiert werden, um sie in anderen Plattformen zu verwenden. Sie finden alle Muster, die 4 oder mehr Male im Trainingsdatensatz aufgetreten sind und eine positive Gewinnerwartung hatten. Sie geben das Informationsverhältnis des Musters zurück – das Verhältnis des Gewinnmittels zur Standardabweichung – multipliziert mit 100. Je besser das Informationsverhältnis, desto vorhersagender ist das Muster. Eine typische Musterfindungsfunktion mit 12 Signalen sieht wie folgt aus:

int EURUSD_S(float* sig)
    {
      if(sig[1]<sig[2] && eqF(sig[2]-sig[4]) && sig[4]<sig[0] && sig[0]<sig[5] && sig[5]<sig[3]
        && sig[10]<sig[11] && sig[11]<sig[7] && sig[7]<sig[8] && sig[8]<sig[9] && sig[9]<sig[6])
          return 19;
      if(sig[4]<sig[1] && sig[1]<sig[2] && sig[2]<sig[5] && sig[5]<sig[3] && sig[3]<sig[0] && sig[7]<sig[8]
        && eqF(sig[8]-sig[10]) && sig[10]<sig[6] && sig[6]<sig[11] && sig[11]<sig[9])
          return 170;
      if(sig[1]<sig[4] && eqF(sig[4]-sig[5]) && sig[5]<sig[2] && sig[2]<sig[3] && sig[3]<sig[0]
        && sig[10]<sig[7] && eqF(sig[7]-sig[8]) && sig[8]<sig[6] && sig[6]<sig[11] && sig[11]<sig[9])
          return 74;
      if(sig[1]<sig[4] && sig[4]<sig[5] && sig[5]<sig[2] && sig[2]<sig[0] && sig[0]<sig[3] && sig[7]<sig[8]
        && eqF(sig[8]-sig[10]) && sig[10]<sig[11] && sig[11]<sig[9] && sig[9]<sig[6])
          return 143;
      if(sig[1]<sig[2] && eqF(sig[2]-sig[4]) && sig[4]<sig[5] && sig[5]<sig[3] && sig[3]<sig[0]
        && sig[10]<sig[7] && sig[7]<sig[8] && sig[8]<sig[6] && sig[6]<sig[11] && sig[11]<sig[9])
          return 168;
      ....
      return 0;
    }

Die eqF-Funktion im obigen Code überprüft, ob zwei Signale gleich sind. Signale, die sich weniger als der FuzzyRange unterscheiden, werden als gleich betrachtet.

Es gibt zwei zusätzliche spezielle Methoden für den Muster-Analysator. Die FUZZY-Methode generiert eine Musterfindungsfunktion, die auch Muster findet, die leicht von den profitablen Mustern im Trainingsdatensatz abweichen können. Sie gibt Mustern eine höhere Punktzahl, wenn sie 'besser passen'. Die Abweichung kann mit FuzzyRange eingestellt werden. Eine typische unscharfe Musterfindungsfunktion sieht wie folgt aus:

int EURUSD_S(float* sig)
    {
      double result = 0.;
      result += belowF(sig[1],sig[4]) * belowF(sig[4],sig[2]) * belowF(sig[2],sig[5]) * belowF(sig[5],sig[3]) * belowF(sig[3],sig[0])
        * equF(sig[10],sig[11]) * belowF(sig[11],sig[7]) * belowF(sig[7],sig[8]) * belowF(sig[8],sig[9]) * belowF(sig[9],sig[6]) * 19;
      result += belowF(sig[4],sig[5]) * belowF(sig[5],sig[1]) * belowF(sig[1],sig[2]) * belowF(sig[2],sig[3]) * belowF(sig[3],sig[0])
        * belowF(sig[10],sig[7]) * belowF(sig[7],sig[11]) * belowF(sig[11],sig[8]) * belowF(sig[8],sig[9]) * belowF(sig[9],sig[6]) * 66;
      result += belowF(sig[4],sig[1]) * belowF(sig[1],sig[2]) * belowF(sig[2],sig[0]) * belowF(sig[0],sig[5]) * belowF(sig[5],sig[3])
        * belowF(sig[10],sig[11]) * belowF(sig[11],sig[7]) * belowF(sig[7],sig[8]) * belowF(sig[8],sig[6]) * belowF(sig[6],sig[11]) * 30;
      result += belowF(sig[1],sig[4]) * belowF(sig[4],sig[2]) * belowF(sig[2],sig[5]) * belowF(sig[5],sig[3]) * belowF(sig[3],sig[0])
        * belowF(sig[7],sig[10]) * belowF(sig[10],sig[11]) * belowF(sig[11],sig[8]) * belowF(sig[8],sig[6]) * belowF(sig[6],sig[9]) * 70;
      result += belowF(sig[4],sig[5]) * belowF(sig[5],sig[1]) * belowF(sig[1],sig[2]) * belowF(sig[2],sig[3]) * belowF(sig[3],sig[0])
        * belowF(sig[7],sig[10]) * belowF(sig[10],sig[8]) * belowF(sig[8],sig[11]) * belowF(sig[11],sig[9]) * belowF(sig[9],sig[6]) * 108;
      ...
      return result;
    }

Die belowF-Funktion wird auf der Fuzzy Logic-Seite beschrieben.

Die FAST-Methode generiert keinen C-Code; stattdessen generiert sie eine Liste von Mustern, die mit alphanumerischen Namen klassifiziert werden. Um ein Muster zu finden, wird es klassifiziert und sein Name mit der Musterliste verglichen. Dies ist etwa 4-mal schneller als die Musterfindungsfunktion im C-Code und kann auch größere und komplexere Muster mit bis zu 20 Signalen verarbeiten. Dies kann einen bemerkenswerten Unterschied in der Backtest-Zeit machen oder wenn zusätzliche Parameter trainiert werden müssen. Eine Muster-Namensliste sieht wie folgt aus (die Zahlen hinter dem Namen sind die Informationsverhältnisse):

/* Pattern list for EURUSD_S
HIECBFGAD 61
BEFHCAIGD 152
EHBCIFGAD 73
BEFHCIGDA 69
BHFECAIGD 95
BHIFECGAD 86
HBEIFCADG 67
HEICBFGDA 108 ...*/

Die FAST-Methode kann nicht in Kombination mit FUZZY oder mit FuzzyRange verwendet werden. Aber die FAST sowie die FUZZY-Methode können mit Mustergruppen kombiniert werden (z.B. PATTERN+FAST+2).

Die Findequote des Muster-Analysators kann mit zwei Variablen angepasst werden:

MusterAnzahl

Die minimale Anzahl von Vorkommnissen der gefundenen Muster in der analysierten Preisreihe; Standardwert = 4.

MusterRate

Die minimale Gewinnrate der gefundenen Muster, in Prozent; Standardwert = 50.

Ein Beispiel für ein Musterhandels-Skript finden Sie in Workshop 7.

NEURAL (Allgemeines Machine Learning)

Die NEURAL-Methode verwendet eine externe Machine-Learning-Bibliothek, zum Beispiel eine Support Vector Machine, Random Forest oder ein Deep Learning neuronales Netzwerk zur Vorhersage des nächsten Preises oder der nächsten Trade-Rendite in einem Algo-Trading-System. Viele Machine-Learning-Bibliotheken sind in R-Paketen oder Python-Bibliotheken verfügbar; daher ruft die NEURAL-Methode oft R oder Python-Funktionen zum Trainieren und Vorhersagen auf. Aber jede DLL-basierte Machine-Learning-Bibliothek kann verwendet werden, solange die neural-Funktion – siehe unten – darauf angepasst ist.

neural (int Status, int Model, int NumSignals, void* Data): var

Diese Funktion wird automatisch mehrmals während des Trainings-, Test- oder Trade-Prozesses aufgerufen. Sie hat Zugriff auf alle globalen und vordefinierten Variablen. Ihr Verhalten hängt von Status ab:

Status Parameter Aufgerufen von Beschreibung
NEURAL_INIT --- Nach dem INITRUN Initialisiert die Machine-Learning-Bibliothek (z.B. durch Aufruf von Rstart); gibt 0 zurück, wenn die Initialisierung fehlgeschlagen ist, andernfalls 1. Das Skript wird abgebrochen, wenn das System nicht initialisiert werden kann.
NEURAL_EXIT --- Nach dem EXITRUN Freigabe der Machine-Learning-Bibliothek, falls erforderlich.
NEURAL_TRAIN Model, NumSignals, Data Nach jedem WFOCycle im Trainingsmodus

Batch-Training. Trainieren Sie das Modell mit dem gegebenen Model-Index mit allen Datensätzen, die im vorherigen Trainingslauf oder WFO-Trainingszyklus gesammelt wurden. Der Model-Index wird für jede Kombination aus Asset, Algo und Long/Short erhöht. Die Liste der Datensätze wird im CSV-Format im Data-String bereitgestellt. Die Spalten der CSV-Tabelle sind die Signale, die letzte Spalte ist der Objective-Parameter oder das Trade-Ergebnis. Die Genauigkeit oder der Verlust in Prozent kann optional von der neural-Funktion zurückgegeben werden; andernfalls wird 1 zurückgegeben, wenn keine Genauigkeit oder kein Verlust berechnet wird, oder 0, um das Skript abzubrechen, wenn das Training fehlgeschlagen ist.

NEURAL_PREDICT Model, NumSignals, Data Durch jeden advise-Aufruf im Test-/Trade-Modus Gibt den vom Modell mit dem gegebenen Model-Index vorhergesagten Wert zurück, unter Verwendung der Signale im Data-Double-Array.
NEURAL_SAVE Data Nach jedem WFOCycle im Trainingsmodus Speichert alle in der vorherigen Trainingsrunde oder im WFO-Trainingszyklus trainierten Modelle in der Datei mit dem Namen, der durch den String Data angegeben ist.
NEURAL_LOAD Data Vor jedem WFOCycle im Testmodus, zu Beginn der Session im Trade-Modus.  Bereitet die Vorhersage vor, indem alle trainierten Modelle aus der Datei mit dem Namen, der durch den String Data angegeben ist, geladen werden. Wird auch im Trade-Modus aufgerufen, wann immer die Modell-Datei durch erneutes Trainieren aktualisiert wurde.

Model ist die Nummer des trainierten Modells, zum Beispiel eine Reihe von Entscheidungsregeln oder eine Reihe von Gewichten eines neuronalen Netzwerks, beginnend mit 0. Wenn mehrere Modelle für Long- und Short-Vorhersagen und für verschiedene Assets oder Algos trainiert werden, wählt der Index das geeignete Modell aus. Die Anzahl der Modelle ist daher normalerweise gleich der Anzahl der advise-Aufrufe pro run-Zyklus. Alle trainierten Modelle werden am Ende jedes WFO-Zyklus in einer *.ml-Datei gespeichert. In R werden die Modelle normalerweise in einer Liste von Listen gespeichert und über ihren Index (z.B. Models[[model+1]]) zugegriffen. Jeder zusätzliche Parametersatz, der im Trainingsprozess generiert wird – zum Beispiel eine Reihe von Normalisierungsfaktoren oder Auswahlmasken für die Signale – kann als Teil des Modells gespeichert werden.

Der NumSignals-Parameter ist die Anzahl der Signale, die an die advise-Funktion übergeben werden. Er ist normalerweise identisch mit der Anzahl der trainierten Merkmale.

Der Data-Parameter liefert Daten an die Funktion. Die Daten können verschiedener Typen sein. Für NEURAL_LEARN/NEURAL_PREDICT ist es ein Zeiger auf ein double-Array der Länge NumSignals+1, das die Signalwerte plus das Vorhersageziel oder das Trade-Ergebnis am Ende enthält. Beachten Sie, dass ein einfaches Datenarray keine "dim names" oder andere R-Kleinigkeiten hat – wenn diese im R-Trainings- oder Vorhersagefunktion benötigt werden, fügen Sie sie dort hinzu. Für NEURAL_TRAIN ist der Data-Parameter ein Textstring, der alle Samples im CSV-Format enthält. Der String kann in einer temporären CSV-Datei gespeichert und dann von dem Machine-Learning-Algorithmus zum Trainieren des Modells gelesen werden. Für NEURAL_SAVE/NEURAL_LOAD ist der Data-Parameter der vorgeschlagene Dateiname zum Speichern oder Laden aller trainierten Modelle des aktuellen WFO-Zyklus im Data-Ordner. Verwenden Sie die slash(string)-Funktion, um Backslashes in Slashes umzuwandeln, wenn dies für R-Dateipfade erforderlich ist.

Der untenstehende Code ist die Standard-neural-Funktion in der r.h-Datei zur Verwendung eines R-Machine-Learning-Algorithmus. Eine 64-Bit-Variante für Python ist in pynet.cpp verfügbar. Wenn erforderlich für spezielle Zwecke, kann die Standard-neural-Funktion durch eine benutzerdefinierte Funktion ersetzt werden.

var neural(int Status, int model, int NumSignals, void* Data)
{
if(!wait(0)) return 0;
// öffne ein R-Skript mit demselben Namen wie das Strategieskript
if(Status == NEURAL_INIT) {
if(!Rstart(strf("%s.r",Script),2)) return 0;
Rx("neural.init()");
return 1;
}
// exportiere Batch-Trainingssamples und rufe die R-Trainingsfunktion auf
if(Status == NEURAL_TRAIN) {
string name = strf("Data\\signals%i.csv",Core);
file_write(name,Data,0);
Rx(strf("XY <- read.csv('%s%s',header = F)",slash(ZorroFolder),slash(name)));
Rset("AlgoVar",AlgoVar,8);
if(!Rx(strf("neural.train(%i,XY)",model+1),2))
return 0;
return 1;
}
// vorhersage das Ziel mit der R-predict-Funktion
if(Status == NEURAL_PREDICT) {
Rset("AlgoVar",AlgoVar,8);
Rset("X",(double*)Data,NumSignals);
Rx(strf("Y <- neural.predict(%i,X)",model+1));
return Rd("Y[1]");
}
// speichere alle trainierten Modelle
if(Status == NEURAL_SAVE) {
print(TO_ANY,"\nStore %s",strrchr(Data,'\\')+1);
return Rx(strf("neural.save('%s')",slash(Data)),2);
}
// lade alle trainierten Modelle
if(Status == NEURAL_LOAD) {
printf("\nLoad %s",strrchr(Data,'\\')+1);
return Rx(strf("load('%s')",slash(Data)),2);
}
return 1;
}

Die Standard-neural-Funktion erfordert ein R-Skript mit demselben Namen wie das Strategieskript, aber der Erweiterung .r statt .c. Es gibt auch eine Python-Version. Das R- oder Python-Skript muss die folgenden Funktionen enthalten:

neural.init() initialisiert das Machine-Learning-Paket, definiert eine Modellliste, setzt den initialen Seed, die Anzahl der Kerne, GPU/CPU-Kontext usw.

neural.train(Model, XY) trainiert den Algorithmus und speichert das trainierte Modell in der Modellliste. XY ist ein Data Frame mit Signalen in den ersten NumSignals Spalten und dem Ziel in der letzten (NumSignals+1) Spalte. Die Samples sind die Zeilen des Data Frames. Model ist die Nummer in der Modellliste, in der das trainierte Modell gespeichert werden soll. Das Modell kann jedes R-Objekt sein, das aus dem Machine-Learning-Modell und zusätzlichen Daten zusammengesetzt ist, z.B. eine Reihe von Normalisierungsfaktoren. In der Python-Version ist XY der Dateiname einer .csv-Datei mit den Daten.

neural.save(FileName) speichert alle Modelle aus der Modellliste in einer Datei mit dem angegebenen Namen und Pfad. Diese Funktion wird einmal pro WFO-Zyklus aufgerufen, sodass jeder Zyklus eine Datei aller Modelle dieses Zyklus generiert.

neural.load(FileName) lädt die zuvor gespeicherte Modellliste für den aktuellen WFO-Zyklus zurück. Wenn diese optionale Funktion fehlt, wird die Modellliste mit der R-Funktion load() geladen. Andernfalls stellen Sie sicher, dass die Liste in die globale R-Umgebung geladen wird, damit sie für neural.predict zugänglich ist, z.B. mit load(FileName, envir=.GlobalEnv).

neural.predict(Model, X) sagt das Ziel basierend auf den Signalen voraus. X ist ein Vektor der Größe NumSignals, der die Signalwerte für eine einzelne Vorhersage enthält. Model ist die Nummer des Modells in der Modellliste. Der Rückgabewert der neural.predict-Funktion wird von der advise-Funktion zurückgegeben. Die neural.predict-Funktion könnte auch Batch-Vorhersagen unterstützen, bei denen X ein Data Frame mit mehreren Samples ist und ein Vektor Y zurückgegeben wird. Batch-Vorhersagen werden von Zorro nicht verwendet, sind aber nützlich, um den Algorithmus mit einem Trainings/Test-Datensatz zu kalibrieren, der im SIGNALS-Modus generiert wurde.

 Ein Beispiel für ein solches Deep-Learning-System finden Sie auf Financial Hacker. Eine einfache Machine-Learning-Strategie DeepLearn.c ist enthalten, zusammen mit R-Skripten zur Ausführung mit den Deep-Learning-Paketen Deepnet, H2O, MxNet oder Keras. Eine .cpp-Version ist für PyTorch verfügbar. Die neural-Funktion ist mit allen Zorro-Trading-, Test- und Trainingsmethoden kompatibel, einschließlich Walk Forward Analysis, Multi-Core-Parallelen Training und mehreren Assets und Algorithmen. Eine kurze Beschreibung der Installation und Verwendung beliebter Deep-Learning-Pakete finden Sie hier.

Anmerkungen:

  • Das RULES-Flag muss gesetzt sein, um Regeln zu generieren oder einen Machine-Learning-Algorithmus zu trainieren. Siehe die Anmerkungen unter Training für spezielle Fälle, wenn RULES und PARAMETERS gleichzeitig generiert werden.
  • Alle advise-Aufrufe für eine bestimmte Asset/Algo-Kombination müssen dieselbe Methode, dieselben Signale und denselben Trainingszieltyp verwenden (entweder Ziel oder die nächste Trade-Rendite). Unterschiedliche Methoden und Signale können jedoch für verschiedene Assets und Algorithmus-IDs verwendet werden, daher rufen Sie algo() auf, um mehrere advise-Methoden im selben Skript zu kombinieren. Ensemble- oder Hybrid-Methoden können ebenfalls auf diese Weise implementiert werden, indem verschiedene algo-IDs verwendet werden.
  • Die generierten Regeln durch DTREE, PERCEPTRON, PATTERN werden im einfachen C-Code in *.c-Dateien im Data-Ordner gespeichert. Dies ermöglicht das Exportieren der Regeln zu anderen Plattformen und das Untersuchen des Vorhersageprozesses bis zu einem gewissen Grad, um zu überprüfen, ob die Regeln sinnvoll sind. Modelle von externen Machine-Learning-Algorithmen werden in *.ml-Dateien im Data-Ordner gespeichert. Wenn mehrere Modelle trainiert werden, werden sie in der Reihenfolge der advise-Aufrufe und WFO-Zyklen nummeriert. Zum Beispiel werden für zwei advise-Aufrufe, zwei Assets und 7 WFO-Zyklen 28 Modelle generiert, 4 für jeden Zyklus.
  • Um die Auswirkungen von Stops, Gewinnzielen und Transaktionskosten im Training zu berücksichtigen, rufen Sie die advise-Funktion mit Objective = 0 vor dem Einstieg in einen Trade auf. Zorro wird dann das Ergebnis des nächsten Trades zur Schulung der Regeln verwenden. Beim gleichzeitigen Trainieren von Long- und Short-Trades setzen Sie Hedge, um sicherzustellen, dass sie im Trainingsmodus gleichzeitig geöffnet werden können; andernfalls würde jeder Trade einen entgegengesetzten Trade, der gerade vorher eröffnet wurde, schließen. Definieren Sie eine Ausstiegsbedingung für den Trade, wie einen zeitgesteuerten Ausstieg nach einer bestimmten Anzahl von Balken oder einen Stop/Trailing-Mechanismus zur Vorhersage einer positiven oder negativen Bewegung. Trainieren Sie keine Regeln mit explizit deaktiviertem Hedging oder mit speziellen Modi wie NFA oder Virtual Hedging.
  • Vorhersagen sind normalerweise besser mit weniger Signalen, also verwenden Sie so wenige Signale wie möglich oder verarbeiten Sie die Signale mit einem Algorithmus, um die Dimensionalität zu reduzieren. Für mehr als 20 Signale verwenden Sie ein Signals-Array. Die Signale für die Methoden DTREE, PATTERN und PERCEPTRON sind immer auf 20 beschränkt.
  • Die meisten Machine-Learning-Algorithmen erfordern ausgewogene Trainingsdaten. Das bedeutet, dass die für das Training verwendeten Trades etwa 50% Gewinne und 50% Verluste ergeben sollten und negative und positive Objective-Werte gleich verteilt sein sollten. Das +BALANCED-Flag dupliziert Samples, bis ein 50/50-Gleichgewicht erreicht ist. Der verwendete Algorithmus balanciert die Samples auch lokal, sodass beliebige Untergruppen ('Batches') der Samples immer noch ausgeglichen sind.
  • Die SIGNALS-Methode exportiert Daten zum Testen und Kalibrieren externer Machine-Learning-Algorithmen. Es wird normalerweise empfohlen, auch BALANCED-Daten dafür zu verwenden. Die Daten werden in einer CSV-Datei gespeichert, wobei die Signale in den ersten Spalten und das Ziel in der letzten Spalte stehen.
  • Während inaktiver Zeiträume, die durch LookBack, BarMode, ein SKIP-Flag oder TimeFrame bestimmt werden, werden die advise-Funktionen nicht trainiert oder vorhersagen und geben 0 zurück.
  • Funktionen wie scale oder Normalize können verwendet werden, um die Signale in den gleichen Bereich zu konvertieren. Einige R-Machine-Learning-Algorithmen erfordern, dass Signale und Ziele im 0..1-Bereich liegen; in diesem Fall führen negative Werte oder Werte größer als 1 zu falschen Ergebnissen. Wenn ein Signalwert außerhalb des Bereichs +/-1000 liegt, wird eine Warnung ausgegeben.
  • Beim Verwenden eines zukünftigen Werts zur Vorhersage, wie der Preisänderung der nächsten Balken (z.B. Objective = priceClose(-5) - priceClose(0)), stellen Sie sicher, dass Sie das PEEK-Flag im Trainingsmodus setzen. Alternativ verwenden Sie die Preisänderung eines vergangenen Balkens zum aktuellen Balken und übergeben vergangene Signale – z.B. aus einer series – an die advise-Funktion im Trainingsmodus, z.B.:
      Objective = priceClose(0) - priceClose(5);
      int Offset = ifelse(Train,5,0);
      var Prediction = adviseLong(Method,Objective,Signal0[Offset],Signal1[Offset],...);
  • Alle Machine-Learning-Funktionen neigen dazu, die Strategie zu overfitten. Aus diesem Grund sollten Strategien, die die advise-Funktion verwenden, immer mit ungesehenen Daten getestet werden, z.B. durch Verwendung von Out-Of-Sample Testing oder Walk Forward Optimization. Verwenden Sie das OPENEND-Flag, um zu verhindern, dass Trades am Ende eines WFO-Trainingszeitraums vorzeitig geschlossen werden und somit die Trainingsqualität verringern. Beim WFO-Training stellen Sie sicher, dass Sie DataHorizon entsprechend einstellen, um peeking bias im nachfolgenden Testzyklus zu vermeiden.
  • Eine Nachricht wie "NEURAL_INIT: failed" deutet darauf hin, dass die erforderlichen R-Pakete nicht installiert sind. Stellen Sie sicher, dass Sie das R-Skript ausführen können und dass die Train/Predict-Funktionen ohne Fehlermeldung laufen.
  • Beim WFO-Training eines Machine-Learning-Algorithmus mit mehreren Kernen, prüfen Sie, ob die verwendeten R-Bibliotheken mit mehreren Prozessen kompatibel sind. Dies ist normalerweise nicht der Fall, wenn Hardware-Ressourcen wie die GPU oder mehrere CPU-Kerne verwendet werden. Die R neural.init-Funktion wird am Beginn jedes Prozesses aufgerufen, was zu unterschiedlichen Ergebnissen in Single-Core- und Multi-Core-Umgebungen führen kann, wenn in der neural.init-Funktion ein Zufallsseed gesetzt wird.
  • Für das Trainieren von mehreren Zielen mit der NEURAL-Methode übergeben Sie die weiteren Ziele als Signal-Parameter an die advise-Funktion im Trainingsmodus. Im Test- und Trade-Modus verwenden Sie eine separate Vorhersagefunktion, die das aktuelle Modell lädt und die Vorhersagen von R in einem Vektor zurückgibt.
  • Für R-Debugging verwenden Sie die R sink-Funktion, um R-Ausgaben in eine Datei zu drucken. Oder verwenden Sie die save.image / load-Funktionen, um den aktuellen Variablen- und Funktionsstatus zu speichern und später zu untersuchen.
  • Für interne Machine-Learning-Algorithmen wird die geschätzte Vorhersagegenauigkeit oder die Anzahl der gefundenen Muster im Nachrichtenfenster angezeigt. Die Signale sollten so ausgewählt werden, dass die Vorhersagegenauigkeit deutlich über 50% liegt oder so viele profitable Muster wie möglich gefunden werden. Für externe Machine-Learning-Funktionen verwenden Sie eine Konfusionsmatrix oder die integrierten Metriken zur Bestimmung der Vorhersagegenauigkeit.
  • Wenn Regeln im C-Code gespeichert werden, kompiliert der interne lite-C-Compiler standardmäßig im 32-Bit-Modus, es sei denn, FAST ist gesetzt. Da der Compiler das Freigeben von Teilen des Codes – nur den kompletten Code – nicht unterstützt, wächst der Code bei jedem Laufzyklus weiter, wodurch der Speicherbedarf erhöht wird. Der Speicher wird nur freigegeben, wenn das Skript erneut kompiliert wird.

Siehe auch:

optimize, frechet, polyfit, predict, Workshop 7, R Bridge, deep learning

► Neueste Version online