<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Data mining bias &#8211; The Financial Hacker</title>
	<atom:link href="https://financial-hacker.com/tag/data-mining-bias/feed/" rel="self" type="application/rss+xml" />
	<link>https://financial-hacker.com</link>
	<description>A new view on algorithmic trading</description>
	<lastBuildDate>Sat, 22 Oct 2022 11:08:47 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://financial-hacker.com/wp-content/uploads/2017/07/cropped-mask-32x32.jpg</url>
	<title>Data mining bias &#8211; The Financial Hacker</title>
	<link>https://financial-hacker.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Better Strategies 5: A Short-Term Machine Learning System</title>
		<link>https://financial-hacker.com/build-better-strategies-part-5-developing-a-machine-learning-system/</link>
					<comments>https://financial-hacker.com/build-better-strategies-part-5-developing-a-machine-learning-system/#comments</comments>
		
		<dc:creator><![CDATA[jcl]]></dc:creator>
		<pubDate>Fri, 12 Aug 2016 09:42:38 +0000</pubDate>
				<category><![CDATA[3 Most Clicked]]></category>
		<category><![CDATA[Machine Learning]]></category>
		<category><![CDATA[Research]]></category>
		<category><![CDATA[System Development]]></category>
		<category><![CDATA[Autoencoder]]></category>
		<category><![CDATA[Boltzmann machine]]></category>
		<category><![CDATA[Classification]]></category>
		<category><![CDATA[Confusion matrix]]></category>
		<category><![CDATA[Data mining bias]]></category>
		<category><![CDATA[Deepnet]]></category>
		<category><![CDATA[Experiment]]></category>
		<category><![CDATA[Price action]]></category>
		<category><![CDATA[R]]></category>
		<category><![CDATA[Sharpe ratio]]></category>
		<category><![CDATA[Walk forward analysis]]></category>
		<guid isPermaLink="false">http://www.financial-hacker.com/build-better-strategies-part-3-the-development-process-copy/</guid>

					<description><![CDATA[It&#8217;s time for the 5th and final part of the Build Better Strategies series. In part 3 we&#8217;ve discussed the development process of a model-based system, and consequently we&#8217;ll conclude the series with developing a data-mining system. The principles of data mining and machine learning have been the topic of part 4. For our short-term &#8230; <a href="https://financial-hacker.com/build-better-strategies-part-5-developing-a-machine-learning-system/" class="more-link">Continue reading<span class="screen-reader-text"> "Better Strategies 5: A Short-Term Machine Learning System"</span></a>]]></description>
										<content:encoded><![CDATA[<p>It&#8217;s time for the 5th and final part of the <a href="http://www.financial-hacker.com/build-better-strategies/">Build Better Strategies</a> series. In <a href="http://www.financial-hacker.com/build-better-strategies-part-3-the-development-process/" target="_blank" rel="noopener">part 3</a> we&#8217;ve discussed the development process of a model-based system, and consequently we&#8217;ll conclude the series with developing a data-mining system. The principles of data mining and machine learning have been the topic of <a href="http://www.financial-hacker.com/build-better-strategies-part-4-machine-learning/">part 4</a>. For our short-term trading example we&#8217;ll use a <strong>deep learning algorithm</strong>, a stacked autoencoder, but it will work in the same way with many other machine learning algorithms. With today&#8217;s software tools, only about <strong>20 lines of code</strong> are needed for a machine learning strategy. I&#8217;ll try to explain all steps in detail. <span id="more-1872"></span></p>
<p>Our example will be a <strong>research project</strong> &#8211; a machine learning experiment for answering two questions. Does a more complex algorithm &#8211; such as, more neurons and deeper learning &#8211; produce a better prediction? And are short-term price moves predictable by short-term price history? The last question came up due to my scepticism about <strong>price action trading</strong> in the <a href="http://www.financial-hacker.com/build-better-strategies-part-4-machine-learning/" target="_blank" rel="noopener">previous part</a> of this series. I got several emails asking about the &#8220;trading system generators&#8221; or similar price action tools that are praised on some websites. There is no hard evidence that such tools ever produced any profit (except for their vendors) &#8211; but does this mean that they all are garbage? We&#8217;ll see.</p>
<p>Our experiment is simple: We collect information from the last candles of a price curve, feed it in a deep learning neural net, and use it to predict the next candles. My hypothesis is that a few candles don&#8217;t contain any useful predictive information. Of course, a nonpredictive outcome of the experiment won&#8217;t mean that I&#8217;m right, since I could have used wrong parameters or prepared the data badly. But a predictive outcome would be a hint that I&#8217;m wrong and price action trading can indeed be profitable.</p>
<h3>Machine learning strategy development<br />
Step 1: The target variable</h3>
<p>To recap the <a href="http://www.financial-hacker.com/build-better-strategies-part-4-machine-learning/">previous part</a>: a supervised learning algorithm is trained with a set of <strong>features</strong> in order to predict a <strong>target variable</strong>. So the first thing to determine is what this target variable shall be. A popular target, used in most papers, is the sign of the price return at the next bar. Better suited for prediction, since less susceptible to randomness, is the price difference to a more distant <strong>prediction horizon</strong>, like 3 bars from now, or same day next week. Like almost anything in trading systems, the prediction horizon is a compromise between the effects of randomness (less bars are worse) and predictability (less bars are better).</p>
<p>Sometimes you&#8217;re not interested in directly predicting price, but in predicting some other parameter &#8211; such as the current leg of a Zigzag indicator &#8211; that could otherwise only be determined in hindsight. Or you want to know if a certain <strong>market inefficiency</strong> will be present in the next time, especially when you&#8217;re using machine learning not directly for trading, but for filtering trades in a <a href="http://www.financial-hacker.com/build-better-strategies-part-3-the-development-process/" target="_blank" rel="noopener">model-based system</a>. Or you want to predict something entirely different, for instance the probability of a market crash tomorrow. All this is often easier to predict than the popular tomorrow&#8217;s return.</p>
<p>In our price action experiment we&#8217;ll use the return of a short-term price action trade as target variable. Once the target is determined, next step is selecting the features.</p>
<h3>Step 2: The features</h3>
<p>A price curve is the worst case for any machine learning algorithm. Not only does it carry <strong>little signal and mostly noise</strong>, it is also nonstationary and the signal/noise ratio changes all the time. The exact ratio of signal and noise depends on what is meant with &#8220;signal&#8221;, but it is normally too low for any known machine learning algorithm to produce anything useful. So we must derive features from the price curve that contain more signal and less noise. Signal, in that context, is any information that can be used to predict the target, whatever it is. All the rest is noise.</p>
<p>Thus, <strong>selecting the features is critical for success</strong> &#8211; even more critical than deciding which machine learning algorithm you&#8217;re going to use. There are two approaches for selecting features. The first and most common is extracting as much information from the price curve as possible. Since you do not know where the information is hidden, you just generate a wild collection of indicators with a wide range of parameters, and hope that at least a few of them will contain the information that the algorithm needs. This is the approach that you normally find in the literature. The problem of this method: Any machine learning algorithm is easily confused by nonpredictive predictors. So it won&#8217;t do to just throw 150 indicators at it. You need some <strong>preselection algorithm </strong>that determines which of them carry useful information and which can be omitted. Without reducing the features this way to maybe eight or ten, even the deepest learning algorithm won&#8217;t produce anything useful.</p>
<p>The other approach, normally for experiments and research, is using only limited information from the price curve. This is the case here: Since we want to examine price action trading, we only use the last few prices as inputs, and must discard all the rest of the curve. This has the advantage that we don&#8217;t need any preselection algorithm since the number of features is limited anyway. Here are the two simple predictor functions that we use in our experiment (in C):</p>
<pre class="prettyprint">var change(int n)
{
	return scale((priceClose(0) - priceClose(n))/priceClose(0),100)/100;
}

var range(int n)
{
	return scale((HH(n) - LL(n))/priceClose(0),100)/100;
}</pre>
<p>The two functions are supposed to carry the necessary information for price action: per-bar movement and volatility. The <strong>change </strong>function is the difference of the current price to the price of <strong>n</strong> bars before, divided by the current price. The <strong>range</strong> function is the total high-low distance of the last <strong>n</strong> candles, also in divided by the current price. And the <strong>scale</strong> function centers and compresses the values to the <strong>+/-100</strong> range, so we divide them by 100 for getting them normalized to <strong>+/-1</strong>. We remember that normalizing is needed for machine learning algorithms.</p>
<h3>Step 3: Preselecting predictors</h3>
<p>When you have selected a large number of indicators or other signals as features for your algorithm, you must determine which of them is useful and which not. There are many methods for reducing the number of features, for instance:</p>
<ul style="list-style-type: square;">
<li>Determine the correlations between the signals. Remove those with a strong correlation to other signals, since they do not contribute to the information.</li>
<li>Compare the information content of signals directly, with algorithms like information entropy or decision trees.</li>
<li>Determine the information content indirectly by comparing the signals with randomized signals; there are some software libraries for this, such as the R Boruta package.</li>
<li>Use an algorithm like Principal Components Analysis (PCA) for generating a new signal set with reduced dimensionality.</li>
<li>Use genetic optimization for determining the most important signals just by the most profitable results from the prediction process. Great for curve fitting if you want to publish impressive results in a research paper.</li>
</ul>
<p>Reducing the number of features is important for most machine learning algorithms, including shallow neural nets. For deep learning it&#8217;s less important, since deep nets  with many neurons are normally able to process huge feature sets and discard redundant features. For our experiment we do not preselect or preprocess the features, but you can find useful information about this in articles (1), (2), and (3) listed at the end of the page.</p>
<h3>Step 4: Select the machine learning algorithm</h3>
<p>R offers many different ML packages, and any of them offers many different algorithms with many different parameters. Even if you already decided about the method &#8211; here, deep learning &#8211; you have still the choice among different approaches and different R packages. Most are quite new, and you can find not many empirical information that helps your decision. You have to try them all and gain experience with different methods. For our experiment we&#8217;ve choosen the <strong>Deepnet</strong> package, which is probably the simplest and easiest to use deep learning library. This keeps our code short. We&#8217;re using its <strong>Stacked Autoencoder</strong> (<strong>SAE</strong>) algorithm for pre-training the network. Deepnet also offers a <strong>Restricted Boltzmann Machine</strong> (<strong>RBM</strong>) for pre-training, but I could not get good results from it. There are other and more complex deep learning packages for R, so you can spend a lot of time checking out all of them.</p>
<p><em>How</em> pre-training works is easily explained, but <em>why</em> it works is a different matter. As to my knowledge, no one has yet come up with a solid mathematical proof that it works at all. Anyway, imagine a large neural net with many hidden layers:</p>
<p><a href="http://www.financial-hacker.com/wp-content/uploads/2016/10/deepnet.png"><img fetchpriority="high" decoding="async" class="alignnone wp-image-2026 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2016/10/deepnet.png" width="560" height="279" srcset="https://financial-hacker.com/wp-content/uploads/2016/10/deepnet.png 560w, https://financial-hacker.com/wp-content/uploads/2016/10/deepnet-300x149.png 300w" sizes="(max-width: 560px) 85vw, 560px" /></a></p>
<p>Training the net means setting up the connection weights between the neurons. The usual method is error backpropagation. But it turns out that the more hidden layers you have, the worse it works. The backpropagated error terms get smaller and smaller from layer to layer, causing the first layers of the net to learn almost nothing. Which means that the predicted result becomes more and more dependent of the random initial state of the weights. This severely limited the complexity of layer-based neural nets and therefore the tasks that they can solve. At least until 10 years ago.</p>
<p>In 2006 scientists in Toronto first published the idea to pre-train the weights with an unsupervised learning algorithm, a restricted Boltzmann machine. This turned out a revolutionary concept. It boosted the development of artificial intelligence and allowed all sorts of new applications from Go-playing machines to self-driving cars. Meanwhile, several new improvements and algorithms for deep learning have been found. A stacked autoencoder works this way:</p>
<ol>
<li>Select the hidden layer to train; begin with the first hidden layer. Connect its outputs to a temporary output layer that has the same structure as the network&#8217;s input layer.</li>
<li>Feed the network with the training samples, but without the targets. Train it so that the first hidden layer reproduces the input signal &#8211; the features &#8211; at its outputs as exactly as possible. The rest of the network is ignored. During training, apply a &#8216;weight penalty term&#8217; so that as few connection weights as possible are used for reproducing the signal.</li>
<li>Now feed the outputs of the trained hidden layer to the inputs of the next untrained hidden layer, and repeat the training process so that the input signal is now reproduced at the outputs of the next layer.</li>
<li>Repeat this process until all hidden layers are trained. We have now a &#8216;sparse network&#8217; with very few layer connections that can reproduce the input signals.</li>
<li>Now train the network with backpropagation for learning the target variable, using the pre-trained weights of the hidden layers as a starting point.</li>
</ol>
<p>The hope is that the unsupervised pre-training process produces an internal noise-reduced abstraction of the input signals that can then be used for easier learning the target. And this indeed appears to work. No one really knows why, but several theories &#8211; see paper (4) below &#8211; try to explain that phenomenon.</p>
<h3>Step 5: Generate a test data set</h3>
<p>We first need to produce a data set with features and targets so that we can test our prediction process and try out parameters. The features must be based on the same price data as in live trading, and for the target we must simulate a short-term trade. So it makes sense to generate the data not with R, but with our trading platform, which is anyway a lot faster. Here&#8217;s a small <a href="http://www.financial-hacker.com/hackers-tools-zorro-and-r/" target="_blank" rel="noopener">Zorro</a> script for this, <strong>DeepSignals.c</strong>:</p>
<pre class="prettyprint">function run()
{
	StartDate = 20140601; // start two years ago
	BarPeriod = 60; // use 1-hour bars
	LookBack = 100; // needed for scale()

	set(RULES);   // generate signals
	LifeTime = 3; // prediction horizon
	Spread = RollLong = RollShort = Commission = Slippage = 0;
	
	adviseLong(SIGNALS+BALANCED,0,
		change(1),change(2),change(3),change(4),
		range(1),range(2),range(3),range(4));
	enterLong(); 
}
</pre>
<p>We&#8217;re generating 2 years of data with features calculated by our above defined <strong>change </strong>and <strong>range</strong> functions. Our target is the result of a trade with 3 bars life time. Trading costs are set to zero, so in this case the result is equivalent to the sign of the price difference at 3 bars in the future. The <strong>adviseLong</strong> function is described in the <a href="http://manual.zorro-project.com/advisor.htm" target="_blank" rel="noopener">Zorro manual</a>; it is a mighty function that automatically handles training and predicting and allows to use any R-based machine learning algorithm just as if it were a simple indicator.</p>
<p>In our code, the function uses the next trade return as target, and the price changes and ranges of the last 4 bars as features. The <strong>SIGNALS</strong> flag tells it not to train the data, but to export it to a .csv file. The <strong>BALANCED</strong> flag makes sure that we get as many positive as negative returns; this is important for most machine learning algorithms. Run the script in [Train] mode with our usual test asset EUR/USD selected. It generates a spreadsheet file named <strong>DeepSignalsEURUSD_L.csv</strong> that contains the features in the first 8 columns, and the trade return in the last column.</p>
<h3>Step 6: Calibrate the algorithm</h3>
<p>Complex machine learning algorithms have many parameters to adjust. Some of them offer great opportunities to curve-fit the algorithm for publications. Still, we must calibrate parameters since the algorithm rarely works well with its default settings. For this, here&#8217;s an R script that reads the previously created data set and processes it with the deep learning algorithm (<strong>DeepSignal.r</strong>): </p>
<pre class="prettyprint">library('deepnet', quietly = T) 
library('caret', quietly = T)

neural.train = function(model,XY) 
{
  XY &lt;- as.matrix(XY)
  X &lt;- XY[,-ncol(XY)]
  Y &lt;- XY[,ncol(XY)]
  Y &lt;- ifelse(Y &gt; 0,1,0)
  Models[[model]] &lt;&lt;- sae.dnn.train(X,Y,
      hidden = c(50,100,50), 
      activationfun = "tanh", 
      learningrate = 0.5, 
      momentum = 0.5, 
      learningrate_scale = 1.0, 
      output = "sigm", 
      sae_output = "linear", 
      numepochs = 100, 
      batchsize = 100,
      hidden_dropout = 0, 
      visible_dropout = 0)
}

neural.predict = function(model,X) 
{
  if(is.vector(X)) X &lt;- t(X)
  return(nn.predict(Models[[model]],X))
}

neural.init = function()
{
  set.seed(365)
  Models &lt;&lt;- vector("list")
}

TestOOS = function() 
{
  neural.init()
  XY &lt;&lt;- read.csv('C:/Zorro/Data/DeepSignalsEURUSD_L.csv',header = F)
  splits &lt;- nrow(XY)*0.8
  XY.tr &lt;&lt;- head(XY,splits);
  XY.ts &lt;&lt;- tail(XY,-splits)
  neural.train(1,XY.tr)
  X &lt;&lt;- XY.ts[,-ncol(XY.ts)]
  Y &lt;&lt;- XY.ts[,ncol(XY.ts)]
  Y.ob &lt;&lt;- ifelse(Y &gt; 0,1,0)
  Y &lt;&lt;- neural.predict(1,X)
  Y.pr &lt;&lt;- ifelse(Y &gt; 0.5,1,0)
  confusionMatrix(Y.pr,Y.ob)
}</pre>
<p>We&#8217;ve defined three functions <strong>neural.train</strong>, <strong>neural.predict</strong>, and <strong>neural.init</strong> for training, predicting, and initializing the neural net. The function names are not arbitrary, but follow the convention used by Zorro&#8217;s advise(NEURAL,..) function. It doesn&#8217;t matter now, but will matter later when we use the same R script for training and trading the deep learning strategy. A fourth function, <strong>TestOOS</strong>, is used for out-of-sample testing our setup.</p>
<p>The function <strong>neural.init</strong> seeds the R random generator with a fixed value (365 is my personal lucky number). Otherwise we would get a slightly different result any time, since the neural net is initialized with random weights. It also creates a global R list named &#8220;Models&#8221;. Most R variable types don&#8217;t need to be created beforehand, some do (don&#8217;t ask me why). The &#8216;&lt;&lt;-&#8216; operator is for accessing a global variable from within a function.</p>
<p>The function <strong>neural.train</strong> takes as input a model number and the data set to be trained. The model number identifies the trained model in the &#8220;<strong>Models</strong>&#8221; list. A list is not really needed for this test, but we&#8217;ll need it for more complex strategies that train more than one model. The matrix containing the features and target is passed to the function as second parameter. If the <strong>XY</strong> data is not a proper matrix, which frequently happens in R depending on how you generated it, it is converted to one. Then it is split into the features (<strong>X</strong>) and the target (<strong>Y</strong>), and finally the target is converted to <strong>1</strong> for a positive trade outcome and <strong>0</strong> for a negative outcome. </p>
<p>The network parameters are then set up. Some are obvious, others are free to play around with:</p>
<ul style="list-style-type: square;">
<li>The network structure is given by the <strong>hidden</strong> vector:  <strong>c(50,100,50)</strong> defines 3 hidden layers, the first with 50, second with 100, and third with 50 neurons. That&#8217;s the parameter that we&#8217;ll later modify for determining whether deeper is better.</li>
<li>The <strong>activation function </strong>converts the sum of neuron input values to the neuron output; most often used are <strong>sigmoid</strong> that saturates to 0 or 1, or <strong>tanh</strong> that saturates to -1 or +1.</li>
</ul>
<p><a href="http://www.financial-hacker.com/wp-content/uploads/2016/08/sigmoid_tanh.png"><img decoding="async" class="alignnone wp-image-2111 " src="http://www.financial-hacker.com/wp-content/uploads/2016/08/sigmoid_tanh.png" width="523" height="197" srcset="https://financial-hacker.com/wp-content/uploads/2016/08/sigmoid_tanh.png 960w, https://financial-hacker.com/wp-content/uploads/2016/08/sigmoid_tanh-300x113.png 300w, https://financial-hacker.com/wp-content/uploads/2016/08/sigmoid_tanh-768x289.png 768w" sizes="(max-width: 523px) 85vw, 523px" /></a></p>
<p>We use <strong>tanh</strong> here since our signals are also in the +/-1 range. The <strong>output</strong> of the network is a sigmoid function since we want a prediction in the 0..1 range. But the <strong>SAE output</strong> must be &#8220;linear&#8221; so that the Stacked Autoencoder can reproduce the analog input signals on the outputs. Recently in fashion came RLUs, Rectified Linear Units, as activation functions for internal layers. RLUs are faster and partially overcome the above mentioned backpropagation problem, but are not supported by deepnet.</p>
<ul style="list-style-type: square;">
<li>The <strong>learning rate</strong> controls the step size for the gradient descent in training; a lower rate means finer steps and possibly more precise prediction, but longer training time.</li>
<li><strong>Momentum</strong> adds a fraction of the previous step to the current one. It prevents the gradient descent from getting stuck at a tiny local minimum or saddle point.</li>
<li>The <strong>learning rate scale</strong> is a multiplication factor for changing the learning rate after each iteration (I am not sure for what this is good, but there may be tasks where a lower learning rate on higher epochs improves the training).</li>
<li>An <strong>epoch</strong> is a training iteration over the entire data set. Training will stop once the number of epochs is reached. More epochs mean better prediction, but longer training.</li>
<li>The <strong>batch size</strong> is a number of random samples &#8211; a <strong>mini batch</strong> &#8211; taken out of the data set for a single training run. Splitting the data into mini batches speeds up training since the weight gradient is then calculated from fewer samples. The higher the batch size, the better is the training, but the more time it will take.</li>
<li>The <strong>dropout</strong> is a number of randomly selected neurons that are disabled during a mini batch. This way the net learns only with a part of its neurons. This seems a strange idea, but can effectively reduce overfitting.</li>
</ul>
<p>All these parameters are common for neural networks. Play around with them and check their effect on the result and the training time. Properly calibrating a neural net is not trivial and might be the topic of another article. The parameters are stored in the model together with the matrix of trained connection weights. So they need not to be given again in the prediction function, <strong>neural.predict</strong>. It takes the model and a vector <strong>X</strong> of features, runs it through the layers, and returns the network output, the predicted target <strong>Y</strong>. Compared with training, prediction is pretty fast since it only needs a couple thousand multiplications. If <strong>X</strong> was a row vector, it is transposed and this way converted to a column vector, otherwise the <strong>nn.predict</strong> function won&#8217;t accept it.</p>
<p>Use RStudio or some similar environment for conveniently working with R. Edit the path to the <strong>.csv</strong> data in the file above, source it, install the required R packages (deepnet, e1071, and caret), then call the <strong>TestOOS</strong> function from the command line. If everything works, it should print something like that:</p>
<pre class="prettyprint">&gt; TestOOS()
begin to train sae ......
training layer 1 autoencoder ...
####loss on step 10000 is : 0.000079
training layer 2 autoencoder ...
####loss on step 10000 is : 0.000085
training layer 3 autoencoder ...
####loss on step 10000 is : 0.000113
sae has been trained.
begin to train deep nn ......
####loss on step 10000 is : 0.123806
deep nn has been trained.
Confusion Matrix and Statistics

          Reference
Prediction    0    1
         0 1231  808
         1  512  934
                                          
               Accuracy : 0.6212          
                 95% CI : (0.6049, 0.6374)
    No Information Rate : 0.5001          
    P-Value [Acc &gt; NIR] : &lt; 2.2e-16       
                                          
                  Kappa : 0.2424          
 Mcnemar's Test P-Value : 4.677e-16       
                                          
            Sensitivity : 0.7063          
            Specificity : 0.5362          
         Pos Pred Value : 0.6037          
         Neg Pred Value : 0.6459          
             Prevalence : 0.5001          
         Detection Rate : 0.3532          
   Detection Prevalence : 0.5851          
      Balanced Accuracy : 0.6212          
                                          
       'Positive' Class : 0               
                                          
&gt; </pre>
<p><strong>TestOOS</strong> reads first our data set from Zorro&#8217;s Data folder. It splits the data in 80% for training (<strong>XY.tr</strong>) and 20% for out-of-sample testing (<strong>XY.ts</strong>). The training set is trained and the result stored in the <strong>Models</strong> list at index 1. The test set is further split in features (<strong>X</strong>) and targets (<strong>Y</strong>). <strong>Y</strong> is converted to binary 0 or 1 and stored in <strong>Y.ob</strong>, our vector of observed targets. We then predict the targets from the test set, convert them again to binary 0 or 1 and store them in <strong>Y.pr</strong>. For comparing the observation with the prediction, we use the <strong>confusionMatrix</strong> function from the caret package.</p>
<p>A confusion matrix of a binary classifier is simply a 2&#215;2 matrix that tells how many 0&#8217;s and how many 1&#8217;s had been predicted wrongly and correctly. A lot of metrics are derived from the matrix and printed in the lines above. The most important at the moment is the <strong>62% prediction accuracy</strong>. This may hint that I bashed price action trading a little prematurely. But of course the 62% might have been just luck. We&#8217;ll see that later when we run a WFO test.</p>
<p>A final advice: R packages are occasionally updated, with the possible consequence that previous R code suddenly might work differently, or not at all. This really happens, so test carefully after any update.</p>
<h3>Step 7: The strategy</h3>
<p>Now that we&#8217;ve tested our algorithm and got some prediction accuracy above 50% with a test data set, we can finally code our machine learning strategy. In fact we&#8217;ve already coded most of it, we just must add a few lines to the above Zorro script that exported the data set. This is the final script for training, testing, and (theoretically) trading the system (<strong>DeepLearn.c</strong>):</p>
<pre class="prettyprint">#include &lt;r.h&gt;

function run()
{
	StartDate = 20140601;
	BarPeriod = 60;	// 1 hour
	LookBack = 100;

	WFOPeriod = 252*24; // 1 year
	DataSplit = 90;
	NumCores = -1;  // use all CPU cores but one

	set(RULES);
	Spread = RollLong = RollShort = Commission = Slippage = 0;
	LifeTime = 3;
	if(Train) Hedge = 2;
	
	if(adviseLong(NEURAL+BALANCED,0,
		change(1),change(2),change(3),change(4),
		range(1),range(2),range(3),range(4)) &gt; 0.5) 
		enterLong();
	if(adviseShort() &gt; 0.5) 
		enterShort();
}</pre>
<p>We&#8217;re using a WFO cycle of one year, split in a 90% training and a 10% out-of-sample test period. You might ask why I have earlier used two year&#8217;s data and a different split, 80/20, for calibrating the network in step 5. This is for using differently composed data for calibrating and for walk forward testing. If we used exactly the same data, the calibration might overfit it and compromise the test. </p>
<p>The selected WFO parameters mean that the system is trained with about 225 days data, followed by a 25 days test or trade period. Thus, in live trading the system would retrain every 25 days, using the prices from the previous 225 days. In the literature you&#8217;ll sometimes find the recommendation to retrain a machine learning system after any trade, or at least any day. But this does not make much sense to me. When you used almost 1 year&#8217;s data for training a system, it can obviously not deteriorate after a single day. Or if it did, and only produced positive test results with daily retraining, I would strongly suspect that the results are artifacts by some coding mistake.</p>
<p>Training a deep network takes really a long time, in our case about 10 minutes for a network with 3 hidden layers and 200 neurons. In live trading this would be done by a second Zorro process that is automatically started by the trading Zorro. In the backtest, the system trains at any WFO cycle. Therefore using multiple cores is recommended for training many cycles in parallel. The <strong>NumCores</strong> variable at <strong>-1</strong> activates all CPU cores but one. Multiple cores are only available in Zorro S, so a complete walk forward test with all WFO cycles can take several hours with the free version.</p>
<p>In the script we now train both long and short trades. For this we have to allow hedging in Training mode, since long and short positions are open at the same time. Entering a position is now dependent on the return value from the <strong>advise</strong> function, which in turn calls either the <strong>neural.train</strong> or the <strong>neural.predict</strong> function from the R script. So we&#8217;re here entering positions when the neural net predicts a result above 0.5. </p>
<p>The R script is now controlled by the Zorro script (for this it must have the same name, <strong>DeepLearn.r</strong>, only with different extension). It is identical to our R script above since we&#8217;re using the same network parameters. Only one additional function is needed for supporting a WFO test:</p>
<pre class="prettyprint">neural.save = function(name)
{
  save(Models,file=name)  
}</pre>
<p>The <strong>neural.save</strong> function stores the <strong>Models</strong> list &#8211; it now contains 2 models for long and for short trades &#8211; after every training run in Zorro&#8217;s Data folder. Since the models are stored for later use, we do not need to train them again for repeated test runs.</p>
<p>This is the WFO equity curve generated with the script above (EUR/USD, without trading costs):</p>
<figure id="attachment_2037" aria-describedby="caption-attachment-2037" style="width: 879px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD.png"><img decoding="async" class="wp-image-2037 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD.png" width="879" height="341" srcset="https://financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD.png 879w, https://financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-300x116.png 300w, https://financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-768x298.png 768w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></a><figcaption id="caption-attachment-2037" class="wp-caption-text">EUR/USD equity curve with 50-100-50 network structure</figcaption></figure>
<p>Although not all WFO cycles get a positive result, it seems that there is some predictive effect. The curve is equivalent to an annual return of 89%, achieved with a 50-100-50 hidden layer structure. We&#8217;ll check in the next step how different network structures affect the result.</p>
<p>Since the <strong>neural.init</strong>, <strong>neural.train</strong>, <strong>neural.predict</strong>, and <strong>neural.save</strong> functions are automatically called by Zorro&#8217;s adviseLong/adviseShort functions, there are no R functions directly called in the Zorro script. Thus the script can remain unchanged when using a different machine learning method. Only the <strong>DeepLearn.r</strong> script must be modified and the neural net, for instance, replaced by a support vector machine. For trading such a machine learning system live on a VPS, make sure that R is also installed on the VPS, the needed R packages are installed, and the path to the R terminal set up in Zorro&#8217;s ini file. Otherwise you&#8217;ll get an error message when starting the strategy.</p>
<h3>Step 8: The experiment</h3>
<p>If our goal had been developing a strategy, the next steps would be the reality check, risk and money management, and preparing for live trading just as described under <a href="http://www.financial-hacker.com/build-better-strategies-part-3-the-development-process/" target="_blank" rel="noopener">model-based strategy development</a>. But for our experiment we&#8217;ll now run a series of tests, with the number of neurons per layer increased from 10 to 100 in 3 steps, and 1, 2, or 3 hidden layers (deepnet does not support more than 3). So we&#8217;re looking into the following 9 network structures: c(10), c(10,10), c(10,10,10), c(30), c(30,30), c(30,30,30), c(100), c(100,100), c(100,100,100). For this experiment you need an afternoon even with a fast PC and in multiple core mode. Here are the results (SR = Sharpe ratio, R2 = slope linearity): </p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 28px;">
<td style="width: 40px;"> </td>
<td>* 10 neurons</td>
<td>* 30 neurons</td>
<td>* 100 neurons</td>
</tr>
<tr style="height: 28.75px;">
<td style="width: 40px;">1</td>
<td>
<figure id="attachment_2047" aria-describedby="caption-attachment-2047" style="width: 300px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-1.png"><img loading="lazy" decoding="async" class="wp-image-2047 size-medium" src="http://www.financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-1-300x116.png" width="300" height="116" srcset="https://financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-1-300x116.png 300w, https://financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-1-768x298.png 768w, https://financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-1.png 879w" sizes="(max-width: 300px) 85vw, 300px" /></a><figcaption id="caption-attachment-2047" class="wp-caption-text">SR = 0.55 R2 = 0.00</figcaption></figure>
</td>
<td>
<figure id="attachment_2048" aria-describedby="caption-attachment-2048" style="width: 300px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-2.png"><img loading="lazy" decoding="async" class="wp-image-2048 size-medium" src="http://www.financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-2-300x116.png" width="300" height="116" srcset="https://financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-2-300x116.png 300w, https://financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-2-768x298.png 768w, https://financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-2.png 879w" sizes="(max-width: 300px) 85vw, 300px" /></a><figcaption id="caption-attachment-2048" class="wp-caption-text">SR = 1.02 R2 = 0.51</figcaption></figure>
</td>
<td>
<figure id="attachment_2049" aria-describedby="caption-attachment-2049" style="width: 300px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-3.png"><img loading="lazy" decoding="async" class="wp-image-2049 size-medium" src="http://www.financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-3-300x116.png" width="300" height="116" srcset="https://financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-3-300x116.png 300w, https://financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-3-768x298.png 768w, https://financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-3.png 879w" sizes="(max-width: 300px) 85vw, 300px" /></a><figcaption id="caption-attachment-2049" class="wp-caption-text">SR = 1.18 R2 = 0.84</figcaption></figure>
</td>
</tr>
<tr style="height: 28px;">
<td style="width: 40px;">2</td>
<td>
<figure id="attachment_2050" aria-describedby="caption-attachment-2050" style="width: 300px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-5.png"><img loading="lazy" decoding="async" class="wp-image-2050 size-medium" src="http://www.financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-5-300x116.png" width="300" height="116" /></a><figcaption id="caption-attachment-2050" class="wp-caption-text">SR = 0.98 R2 = 0.57</figcaption></figure>
</td>
<td>
<figure id="attachment_2052" aria-describedby="caption-attachment-2052" style="width: 300px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-6.png"><img loading="lazy" decoding="async" class="wp-image-2052 size-medium" src="http://www.financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-6-300x116.png" width="300" height="116" srcset="https://financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-6-300x116.png 300w, https://financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-6-768x298.png 768w, https://financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-6.png 879w" sizes="(max-width: 300px) 85vw, 300px" /></a><figcaption id="caption-attachment-2052" class="wp-caption-text">SR = 1.22 R2 = 0.70</figcaption></figure>
</td>
<td>
<figure id="attachment_2054" aria-describedby="caption-attachment-2054" style="width: 300px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-8.png"><img loading="lazy" decoding="async" class="wp-image-2054 size-medium" src="http://www.financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-8-300x116.png" width="300" height="116" srcset="https://financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-8-300x116.png 300w, https://financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-8-768x298.png 768w, https://financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-8.png 879w" sizes="(max-width: 300px) 85vw, 300px" /></a><figcaption id="caption-attachment-2054" class="wp-caption-text">SR = 0.84 R2 = 0.60</figcaption></figure>
</td>
</tr>
<tr style="height: 28px;">
<td style="width: 40px;">3</td>
<td>
<figure id="attachment_2051" aria-describedby="caption-attachment-2051" style="width: 300px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-4.png"><img loading="lazy" decoding="async" class="wp-image-2051 size-medium" src="http://www.financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-4-300x116.png" width="300" height="116" /></a><figcaption id="caption-attachment-2051" class="wp-caption-text">SR = 1.24 R2 = 0.79</figcaption></figure>
</td>
<td>
<figure id="attachment_2053" aria-describedby="caption-attachment-2053" style="width: 300px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-7.png"><img loading="lazy" decoding="async" class="wp-image-2053 size-medium" src="http://www.financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-7-300x116.png" width="300" height="116" srcset="https://financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-7-300x116.png 300w, https://financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-7-768x298.png 768w, https://financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-7.png 879w" sizes="(max-width: 300px) 85vw, 300px" /></a><figcaption id="caption-attachment-2053" class="wp-caption-text">SR = 1.28 R2 = 0.87</figcaption></figure>
</td>
<td>
<figure id="attachment_2060" aria-describedby="caption-attachment-2060" style="width: 300px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-9.png"><img loading="lazy" decoding="async" class="wp-image-2060 size-medium" src="http://www.financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-9-300x116.png" width="300" height="116" srcset="https://financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-9-300x116.png 300w, https://financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-9-768x298.png 768w, https://financial-hacker.com/wp-content/uploads/2016/10/DeepLearn_EURUSD-9.png 879w" sizes="(max-width: 300px) 85vw, 300px" /></a><figcaption id="caption-attachment-2060" class="wp-caption-text">SR = 1.33 R2 = 0.83</figcaption></figure>
</td>
</tr>
</tbody>
</table>
<p>We see that a simple net with only 10 neurons in a single hidden layer won&#8217;t work well for short-term prediction. Network complexity clearly improves the performance, however only up to a certain point. A good result for our system is already achieved with 3 layers x 30 neurons. Even more neurons won&#8217;t help much and sometimes even produce a worse result. This is no real surprise, since for processing only 8 inputs, 300 neurons can likely not do a better job than 100.  </p>
<h3>Conclusion</h3>
<p>Our goal was determining if a few candles can have predictive power and how the results are affected by the complexity of the algorithm. The results seem to suggest that short-term price movements can indeed be predicted sometimes by analyzing the changes and ranges of the last 4 candles. The prediction is not very accurate &#8211; it&#8217;s in the 58%..60% range, and most systems of the test series become unprofitable when trading costs are included. Still, I have to reconsider my opinion about price action trading. The fact that the prediction improves with network complexity is an especially convincing argument for short-term price predictability.</p>
<p>It would be interesting to look into the long-term stability of predictive price patterns. For this we had to run another series of experiments and modify the training period (<strong>WFOPeriod</strong> in the script above) and the 90% IS/OOS split. This takes longer time since we must use more historical data. I have done a few tests and found so far that a year seems to be indeed a good training period. The system deteriorates with periods longer than a few years. Predictive price patterns, at least of EUR/USD, have a limited lifetime.</p>
<p>Where can we go from here? There&#8217;s a plethora of possibilities, for instance:</p>
<ul style="list-style-type: square;">
<li>Use inputs from more candles and process them with far bigger networks with thousands of neurons.</li>
<li>Use <a href="http://www.financial-hacker.com/better-tests-with-oversampling/">oversampling</a> for expanding the training data. Prediction always improves with more training samples.</li>
<li>Compress time series f.i. with spectal analysis and analyze not the candles, but their frequency representation with machine learning methods.</li>
<li>Use inputs from many candles &#8211; such as, 100 &#8211; and pre-process adjacent candles with one-dimensional convolutional network layers.</li>
<li>Use recurrent networks. Especially LSTM could be very interesting for analyzing time series &#8211; and as to my knowledge, they have been rarely used for financial prediction so far.</li>
<li>Use an ensemble of neural networks for prediction, such as Aronson&#8217;s &#8220;oracles&#8221; and &#8220;comitees&#8221;.</li>
</ul>
<h3>Papers / Articles</h3>
<p>(1) <a href="http://home.iitk.ac.in/~ayushmn/mail/pre-train.pdf" target="_blank" rel="noopener">A.S.Sisodiya, Reducing Dimensionality of Data</a> <br />
(2) <a href="http://robotwealth.com/machine-learning-financial-prediction-david-aronson/" target="_blank" rel="noopener">K.Longmore, Machine Learning for Financial Prediction</a> <br />
(3) <a href="https://www.mql5.com/en/articles/2029" target="_blank" rel="noopener">V.Perervenko, Selection of Variables for Machine Learning<br />
(</a>4) <a href="http://jmlr.org/papers/volume11/erhan10a/erhan10a.pdf" target="_blank" rel="noopener">D.Erhan et al, Why Does Pre-training Help Deep Learning?</a></p>
<hr />
<p>I&#8217;ve added the C and R scripts to the 2016 script repository. You need both in Zorro&#8217;s Strategy folder. Zorro version 1.474, and R version 3.2.5 (64 bit) was used for the experiment, but it should also work with other versions. </p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/build-better-strategies-part-5-developing-a-machine-learning-system/feed/</wfw:commentRss>
			<slash:comments>114</slash:comments>
		
		
			</item>
		<item>
		<title>Better Strategies 4: Machine Learning</title>
		<link>https://financial-hacker.com/build-better-strategies-part-4-machine-learning/</link>
					<comments>https://financial-hacker.com/build-better-strategies-part-4-machine-learning/#comments</comments>
		
		<dc:creator><![CDATA[jcl]]></dc:creator>
		<pubDate>Thu, 31 Mar 2016 13:43:37 +0000</pubDate>
				<category><![CDATA[Machine Learning]]></category>
		<category><![CDATA[System Development]]></category>
		<category><![CDATA[Autoencoder]]></category>
		<category><![CDATA[Boltzmann machine]]></category>
		<category><![CDATA[Classification]]></category>
		<category><![CDATA[Data mining bias]]></category>
		<category><![CDATA[Decision tree]]></category>
		<category><![CDATA[Entropy]]></category>
		<category><![CDATA[Indicator soup]]></category>
		<category><![CDATA[K-Means]]></category>
		<category><![CDATA[K-Nearest Neighbor]]></category>
		<category><![CDATA[Naive Bayes]]></category>
		<category><![CDATA[Neural network]]></category>
		<category><![CDATA[Price action]]></category>
		<category><![CDATA[R]]></category>
		<category><![CDATA[Regression]]></category>
		<category><![CDATA[Shannon]]></category>
		<category><![CDATA[Support vector machine]]></category>
		<guid isPermaLink="false">http://www.financial-hacker.com/?p=931</guid>

					<description><![CDATA[Deep Blue was the first computer that won a chess world championship. That was 1996, and it took 20 years until another program, AlphaGo, could defeat the best human Go player. Deep Blue was a model based system with hardwired chess rules. AlphaGo is a data-mining system, a deep neural network trained with thousands of &#8230; <a href="https://financial-hacker.com/build-better-strategies-part-4-machine-learning/" class="more-link">Continue reading<span class="screen-reader-text"> "Better Strategies 4: Machine Learning"</span></a>]]></description>
										<content:encoded><![CDATA[<p><strong>Deep Blue</strong> was the first computer that won a chess world championship. That was 1996, and it took 20 years until another program, <strong>AlphaGo</strong>, could defeat the best human Go player. Deep Blue was a model based system with hardwired chess rules. AlphaGo is a data-mining system, a deep neural network trained with thousands of Go games. Not improved hardware, but a breakthrough in software was essential for the step from beating top Chess players to beating top Go players.<br />
In this 4th part of the <a href="http://www.financial-hacker.com/build-better-strategies/" target="_blank" rel="noopener noreferrer">mini-series</a> we&#8217;ll look into the <strong>data mining approach</strong> for developing trading strategies. This method does not care about market mechanisms. It just scans price curves or other data sources for predictive patterns. Machine learning or &#8220;Artificial Intelligence&#8221; is not always involved in data-mining strategies. In fact the most popular &#8211; and surprisingly profitable &#8211; data mining method works without any fancy neural networks or support vector machines.<span id="more-931"></span></p>
<h3>Machine learning principles</h3>
<p>A learning algorithm is fed with data <strong>samples</strong>, normally derived in some way from historical prices. Each sample consists of <em><strong>n</strong></em> variables <em><strong>x<sub>1</sub> .. x<sub>n</sub></strong></em>, commonly named <strong>predictors</strong>,&nbsp;<strong>features</strong>, <strong>signals</strong>, or simply <strong>input</strong>. These predictors can be the price returns of the last <em><strong>n</strong></em> bars, or a collection of classical indicators, or any other imaginable functions of the price curve (I&#8217;ve even seen the pixels of a price chart image used as predictors for a neural network!). Each sample also normally includes a <strong>target variable</strong> <em><strong>y</strong></em>, like the return of the next trade after taking the sample, or the next price movement. In the literature you can find <strong>y</strong> also named <strong>label</strong> or <strong>objective</strong>. In a <strong>training process</strong><em>,</em> the algorithm learns to predict the target&nbsp;<em><strong>y</strong></em> from the predictors <em><strong>x<sub>1</sub> .. x<sub>n</sub></strong></em>. The learned &#8216;memory&#8217; is stored in a data structure named <strong>model</strong> that is specific to the algorithm (not to be confused with a financial model for <a href="http://www.financial-hacker.com/build-better-strategies-part-2-model-based-systems/">model based strategies</a>!). A machine learning model can be a function with prediction rules in C code, generated by the training process. Or it can be a set of connection weights of a neural network.</p>
<p><strong>Training</strong>: &nbsp; &nbsp;&nbsp;<em><strong>x<sub>1</sub> .. x<sub>n</sub></strong></em>, <em><strong>y</strong></em>&nbsp; =&gt; &nbsp;model</p>
<p><strong>Prediction</strong>: &nbsp;&nbsp;<em><strong>x<sub>1</sub> .. x<sub>n</sub></strong></em>, model &nbsp;=&gt; &nbsp;<em><strong>y</strong></em></p>
<p>The predictors, features, or whatever you call them, must carry information sufficient to predict the target <em><strong>y</strong></em> with some accuracy. They m<span style="line-height: 1.75;">ust also often fulfill two formal requirements. First, all predictor values should be in the same range, like -1 .. +1 (for most R algorithms) or -100 .. +100 (for Zorro or TSSB algorithms). So you need to </span><a href="http://manual.zorro-project.com/norm.htm" target="_blank" rel="noopener noreferrer"><strong style="line-height: 1.75;">normalize</strong></a><span style="line-height: 1.75;"> them in some way before sending them to the machine. Second, the samples should be <strong>balanced</strong>, i.e. equally distributed over all values of the target variable. So there should be about as many winning as losing samples. If you do not observe these two requirements, you&#8217;ll wonder why you&#8217;re getting bad results from the machine learning algorithm.</span></p>
<p><strong>Regression</strong> algorithms predict a numeric value, like the magnitude and sign of the next price move. <strong>Classification</strong> algorithms predict a qualitative sample class, for instance whether it&#8217;s preceding a win or a loss. Some algorithms, such as neural networks, decision trees, or support vector machines, can be run in both modes.</p>
<p>A few algorithms learn to divide samples into classes without needing any target <em><strong>y</strong></em>. That&#8217;s <strong>unsupervised learning</strong>, as opposed to <strong>supervised learning</strong> using a target. Somewhere inbetween is <strong>reinforcement learning</strong>, where the system trains itself by running simulations with the given features, and using the outcome as training target. AlphaZero, the successor of AlphaGo, used reinforcement learning by playing millions of Go games against itself. In finance there are few applications for unsupervised or reinforcement learning. 99% of machine learning strategies use supervised learning.</p>
<p>Whatever signals we&#8217;re using for predictors in finance, they will most likely contain much noise and little information, and will be nonstationary on top of it. Therefore financial prediction is <strong>one of the hardest tasks</strong> in machine learning. More complex algorithms do not necessarily achieve better results. The selection of the predictors is critical to the success. It is no good idea to use lots of predictors, since this simply causes overfitting and failure in out of sample operation. Therefore data mining strategies often apply a <strong>preselection algorithm </strong>that determines a small number of predictors out of a pool of many. The preselection can be based on correlation between predictors, on significance, on information content, or simply on prediction success with a test set. Practical experiments with feature selection can be found in a recent article on the <a href="http://robotwealth.com/machine-learning-financial-prediction-david-aronson/" target="_blank" rel="noopener noreferrer">Robot Wealth</a> blog.</p>
<p>Here&#8217;s a list of the most popular data mining methods used in finance.</p>
<h3>1. Indicator soup</h3>
<p>Most trading systems we&#8217;re programming for clients are not based on a financial model. The client just wanted trade signals from certain technical indicators, filtered with other technical indicators in combination with more technical indicators. When asked how this hodgepodge of indicators could be a profitable strategy, he normally answered: &#8220;Trust me. I&#8217;m trading it manually, and it works.&#8221;</p>
<p>It did indeed. At least sometimes. Although most of those systems did not pass a WFA test (and some not even a simple backtest), a surprisingly large number did. And those were also often profitable in real trading. The client had systematically experimented with technical indicators until he found a combination that worked in live trading with certain assets. This way of trial-and-error technical analysis is a classical data mining approach, just executed by a human and not by a machine. I can not really recommend this method &#8211; and a lot of luck, not to speak of money, is probably involved &#8211; but I can testify that it sometimes leads to profitable systems.</p>
<h3>2. Candle patterns</h3>
<p>Not to be confused with those&nbsp;<a href="http://www.financial-hacker.com/seventeen-popular-trade-strategies-that-i-dont-really-understand/">Japanese Candle Patterns</a> that had their best-before date long, long&nbsp;ago. The modern equivalent is&nbsp;<strong>price action trading</strong>. You&#8217;re still looking at the open, high, low, and close of candles. You&#8217;re still hoping to find a pattern that predicts a price direction. But you&#8217;re now data mining contemporary price curves for collecting those patterns. There are software packages for that purpose. They search for patterns that are profitable by some user-defined criterion, and use them to build a specific pattern detection function. It could look like this one (from Zorro&#8217;s <a href="http://manual.zorro-project.com/advisor.htm" target="_blank" rel="noopener noreferrer">pattern analyzer</a>):</p>
<pre class="prettyprint">int detect(double* sig)
{
  if(sig[1]&lt;sig[2] &amp;&amp; sig[4]&lt;sig[0] &amp;&amp; sig[0]&lt;sig[5] &amp;&amp; sig[5]&lt;sig[3] &amp;&amp; sig[10]&lt;sig[11] &amp;&amp; sig[11]&lt;sig[7] &amp;&amp; sig[7]&lt;sig[8] &amp;&amp; sig[8]&lt;sig[9] &amp;&amp; sig[9]&lt;sig[6])
      return 1; 
  if(sig[4]&lt;sig[1] &amp;&amp; sig[1]&lt;sig[2] &amp;&amp; sig[2]&lt;sig[5] &amp;&amp; sig[5]&lt;sig[3] &amp;&amp; sig[3]&lt;sig[0] &amp;&amp; sig[7]&lt;sig[8] &amp;&amp; sig[10]&lt;sig[6] &amp;&amp; sig[6]&lt;sig[11] &amp;&amp; sig[11]&lt;sig[9])
      return 1;
  if(sig[1]&lt;sig[4] &amp;&amp; eqF(sig[4]-sig[5]) &amp;&amp; sig[5]&lt;sig[2] &amp;&amp; sig[2]&lt;sig[3] &amp;&amp; sig[3]&lt;sig[0] &amp;&amp; sig[10]&lt;sig[7] &amp;&amp; sig[8]&lt;sig[6] &amp;&amp; sig[6]&lt;sig[11] &amp;&amp; sig[11]&lt;sig[9])
      return 1;
  if(sig[1]&lt;sig[4] &amp;&amp; sig[4]&lt;sig[5] &amp;&amp; sig[5]&lt;sig[2] &amp;&amp; sig[2]&lt;sig[0] &amp;&amp; sig[0]&lt;sig[3] &amp;&amp; sig[7]&lt;sig[8] &amp;&amp; sig[10]&lt;sig[11] &amp;&amp; sig[11]&lt;sig[9] &amp;&amp; sig[9]&lt;sig[6])
      return 1;
  if(sig[1]&lt;sig[2] &amp;&amp; sig[4]&lt;sig[5] &amp;&amp; sig[5]&lt;sig[3] &amp;&amp; sig[3]&lt;sig[0] &amp;&amp; sig[10]&lt;sig[7] &amp;&amp; sig[7]&lt;sig[8] &amp;&amp; sig[8]&lt;sig[6] &amp;&amp; sig[6]&lt;sig[11] &amp;&amp; sig[11]&lt;sig[9])
      return 1;
  ....
  return 0;
}
</pre>
<p>This C function returns 1 when the signals match one of the patterns, otherwise 0. You can see from the lengthy code that this is not the fastest way to detect patterns. A better method, used by Zorro when the detection function needs not be exported, is sorting the signals by their magnitude and checking the sort order. An example of such a&nbsp;system can be found <a href="http://www.financial-hacker.com/better-tests-with-oversampling/" target="_blank" rel="noopener noreferrer">here</a>.</p>
<p>Can price action trading really work? Just like the indicator soup, it&#8217;s not based on any rational financial model. One can at best imagine that sequences of price movements cause market participants to react in a certain way, this way establishing a temporary predictive pattern. However the number of patterns is quite limited when you only look at sequences of a few adjacent candles. The next step is comparing candles that are not adjacent, but arbitrarily selected within a longer time period. This way you&#8217;re getting an almost unlimited number of patterns &#8211; but at the cost of finally leaving the realm of the rational. It is hard to imagine how a price move can be predicted by some candle patterns from weeks ago.</p>
<p>Still, a lot effort is going into that. A fellow blogger, Daniel Fernandez, runs a subscription website (<a href="https://asirikuy.com/newsite/" target="_blank" rel="noopener noreferrer">Asirikuy</a>) specialized on data mining candle patterns. He refined pattern trading down to the smallest details, and if anyone would ever achieve any profit this way, it would be him. But to his subscribers&#8217; disappointment, trading his patterns live (<a href="https://quriquant.com/pages/view/livetrading" target="_blank" rel="noopener noreferrer">QuriQuant</a>) produced very different results than his wonderful backtests. If profitable price action systems really exist, apparently no one has found them yet.</p>
<h3>3. Linear regression</h3>
<p>The simple basis of many complex machine learning algorithms: Predict the target variable <em><strong>y</strong></em> by a linear combination of the predictors &nbsp;<em><strong>x<sub>1&nbsp;</sub>.. x<sub>n</sub></strong></em>.</p>
<p style="text-align: left; padding-left: 30px;">[latex]y = a_0 + a_1 x_1 + &#8230; + a_n x_n[/latex]</p>
<p>The coefficients&nbsp;<em><strong>a<sub>n</sub></strong></em>&nbsp;are the model. They are calculated for minimizing the sum of squared differences between the true <em><strong>y</strong></em> values from the training samples and their predicted <em><strong>y</strong></em> from the above formula:</p>
<p style="padding-left: 30px;">[latex]Minimize(\sum (y_i-\hat{y_i})^2)[/latex]</p>
<p>For normal distributed samples, the minimizing is possible with some matrix arithmetic, so no iterations are required. In the case <em><strong>n = 1</strong></em>&nbsp;&#8211; with only one predictor variable <em><strong>x</strong></em> &#8211; the regression formula is reduced to</p>
<p style="padding-left: 30px;">[latex]y = a + b x[/latex]</p>
<p>which is <strong>simple linear regression</strong>, as opposed to <strong>multivariate linear regression</strong> where&nbsp;<em><strong>n &gt; 1</strong></em>. Simple linear regression is available in most trading platforms, f.i. with the <a href="http://manual.zorro-project.com/ta.htm" target="_blank" rel="noopener noreferrer">LinReg</a> indicator in the TA-Lib. With <em><strong>y</strong></em> = price and <em><strong>x</strong></em> = time it&#8217;s often used as an alternative to a moving average. Multivariate linear regression is available in the R platform through the <strong>lm(..)</strong> function that comes with the standard installation. A variant is <strong>polynomial regression</strong>. Like simple regression it uses only one predictor variable <em><strong>x</strong></em>, but also its square and higher degrees, so that&nbsp;<em><strong>x<sub>n</sub> == x<sup>n</sup></strong></em>:</p>
<p style="padding-left: 30px;">[latex]y = a_0 + a_1 x + a_2 x^2 + &#8230; + a_n x^n[/latex]</p>
<p style="text-align: left;">With <em><strong>n = 2</strong></em> or <em><strong>n = 3</strong></em>, polynomial regression is often used to predict the next average price from the smoothed prices of the last bars. The <a href="http://manual.zorro-project.com/polyfit.htm" target="_blank" rel="noopener noreferrer">polyfit</a> function of MatLab, R, Zorro, and many other platforms can be used for polynomial regression.</p>
<h3>4. Perceptron</h3>
<p>Often referred to as a neural network with only one neuron. In fact a perceptron is a regression function like above, but with a binary result, thus called <strong>logistic regression</strong>. It&#8217;s not regression though, it&#8217;s a classification algorithm. Zorro&#8217;s <strong>advise(PERCEPTRON, &#8230;)</strong> function generates C code that returns either 100 or -100, dependent on whether the predicted result is above a threshold or not:<br />
<!--?prettify linenums=true?--></p>
<pre class="prettyprint">int predict(double* sig)
{
  if(-27.99*sig[0] + 1.24*sig[1] - 3.54*sig[2] &gt; -21.50)
    return 100;
  else
    return -100;
}</pre>
<p>You can see that the <strong>sig</strong> array is equivalent to the features&nbsp;<em><strong>x<sub>n</sub></strong></em> in the regression formula, and the numeric factors are the coefficients&nbsp;<em><strong>a<sub>n</sub></strong></em>.</p>
<h3>5. N<span style="line-height: 1.75;">eural networks</span></h3>
<p>Linear or logistic regression can only solve linear problems. Many do not fall into this category &#8211; a famous example is predicting the output of a simple XOR function. And most likely also predicting prices or trade returns. An artificial neural network (ANN) can tackle nonlinear problems. It&#8217;s a bunch of perceptrons that are connected together in an array of <strong>layers</strong>. Any perceptron is a <strong>neuron</strong> of the net. Its output goes to the inputs of all neurons of the next layer, like this:</p>
<p><a href="http://www.financial-hacker.com/wp-content/uploads/2016/03/neural-network.png"><img loading="lazy" decoding="async" class="alignnone wp-image-1752 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2016/03/neural-network.png" width="500" height="309" srcset="https://financial-hacker.com/wp-content/uploads/2016/03/neural-network.png 500w, https://financial-hacker.com/wp-content/uploads/2016/03/neural-network-300x185.png 300w" sizes="(max-width: 500px) 85vw, 500px" /></a></p>
<p>Like the perceptron, a neural network also learns by determining the coefficients that minimize the error between sample prediction and sample target.&nbsp;But this requires now an approximation process, normally with <strong>backpropagating</strong> the error from the output to the inputs, optimizing the weights on its way. This process imposes two restrictions. First, the neuron outputs must now be continuously differentiable functions instead of the simple perceptron threshold. Second, the network must not be too deep &#8211; it must not have too many &#8216;hidden layers&#8217; of neurons between inputs and output.&nbsp;This second restriction limits the complexity of problems that&nbsp;a standard neural network&nbsp;can solve.</p>
<p>When using a neural network for predicting trades, you have a lot of parameters with which you can play around and, if you&#8217;re not careful, produce a lot of <strong>selection bias</strong>:</p>
<ul style="list-style-type: square;">
<li>Number of hidden layers</li>
<li>Number of neurons&nbsp;per hidden layer</li>
<li>Number of backpropagation cycles, named <strong>epochs</strong></li>
<li>Learning rate, the step width of an epoch</li>
<li>Momentum, an inertia factor for the weights adaption</li>
<li>Activation function</li>
</ul>
<p>The <strong>activation function</strong>&nbsp;emulates the perceptron threshold. For the backpropagation you need a continuously differentiable function that generates a &#8216;soft&#8217; step at a certain x value. Normally a <strong>sigmoid</strong>,&nbsp;<strong>tanh</strong>, or <strong>softmax</strong> function is used. Sometimes it&#8217;s also a <strong>linear</strong> function that just returns the weighted sum of all inputs. In this case the network can be used for regression, for predicting a numeric value instead of a binary outcome.</p>
<p>Neural networks are available in the standard <strong>R</strong> installation (<strong>nnet</strong>, a single hidden layer network) and in many packages, for instance <strong>RSNNS&nbsp;</strong>and <strong>FCNN4R</strong>.</p>
<h3>6. Deep learning</h3>
<p>Deep learning methods use neural networks with many hidden layers and thousands of neurons, which could not be effectively trained anymore by conventional backpropagation. Several methods became popular in the last years for training such huge networks. They usually pre-train the hidden neuron layers for achieving a more effective learning process. A <strong>Restricted Boltzmann Machine</strong> (<strong>RBM</strong>) is an unsupervised classification algorithm with a special network structure that has no connections between the hidden neurons. A&nbsp;<strong>Sparse Autoencoder</strong> (<strong>SAE</strong>) uses a conventional network structure, but pre-trains the hidden layers in a clever way by reproducing the input signals on the layer outputs with as few active connections as possible. Those methods allow very complex networks for tackling very complex learning tasks. Such as beating the world&#8217;s best human Go player.</p>
<p>Deep learning networks are available in the <strong>deepnet</strong> and <strong>darch</strong> R packages. Deepnet provides an autoencoder, Darch a restricted Boltzmann machine. I have not yet experimented with Darch, but here&#8217;s an example R script using the Deepnet autoencoder with 3 hidden layers for trade signals through Zorro&#8217;s <strong>neural()</strong> function:</p>
<pre class="prettyprint">library('deepnet', quietly = T) 
library('caret', quietly = T)

# called by Zorro for training
neural.train = function(model,XY) 
{
  XY &lt;- as.matrix(XY)
  X &lt;- XY[,-ncol(XY)] # predictors
  Y &lt;- XY[,ncol(XY)]  # target
  Y &lt;- ifelse(Y &gt; 0,1,0) # convert -1..1 to 0..1
  Models[[model]] &lt;&lt;- sae.dnn.train(X,Y,
      hidden = c(50,100,50), 
      activationfun = "tanh", 
      learningrate = 0.5, 
      momentum = 0.5, 
      learningrate_scale = 1.0, 
      output = "sigm", 
      sae_output = "linear", 
      numepochs = 100, 
      batchsize = 100,
      hidden_dropout = 0, 
      visible_dropout = 0)
}

# called by Zorro for prediction
neural.predict = function(model,X) 
{
  if(is.vector(X)) X &lt;- t(X) # transpose horizontal vector
  return(nn.predict(Models[[model]],X))
}

# called by Zorro for saving the models
neural.save = function(name)
{
  save(Models,file=name) # save trained models
}

# called by Zorro for initialization
neural.init = function()
{
  set.seed(365)
  Models &lt;&lt;- vector("list")
}

# quick OOS test for experimenting with the settings
Test = function() 
{
  neural.init()
  XY &lt;&lt;- read.csv('C:/Project/Zorro/Data/signals0.csv',header = F)
  splits &lt;- nrow(XY)*0.8
  XY.tr &lt;&lt;- head(XY,splits) # training set
  XY.ts &lt;&lt;- tail(XY,-splits) # test set
  neural.train(1,XY.tr)
  X &lt;&lt;- XY.ts[,-ncol(XY.ts)]
  Y &lt;&lt;- XY.ts[,ncol(XY.ts)]
  Y.ob &lt;&lt;- ifelse(Y &gt; 0,1,0)
  Y &lt;&lt;- neural.predict(1,X)
  Y.pr &lt;&lt;- ifelse(Y &gt; 0.5,1,0)
  confusionMatrix(Y.pr,Y.ob) # display prediction accuracy
}</pre>
<h3>7. Support vector machines</h3>
<p>Like a neural network, a support vector machine (SVM) is another extension of linear regression. When we look at the regression formula again,</p>
<p style="padding-left: 30px;">[latex]y = a_0 + a_1 x_1 + &#8230; + a_n x_n[/latex]</p>
<p>we can interpret the features <em><strong>x<sub>n</sub></strong></em>&nbsp;as coordinates of a <em><strong>n</strong></em>-dimensional <strong>feature space</strong>. Setting the target variable <em><strong>y</strong></em>&nbsp;to a fixed value determines a plane in that space, called a <strong>hyperplane</strong> since it has more than two (in fact,&nbsp;<em><strong>n-1</strong></em>) dimensions. The hyperplane separates the samples with <em><strong>y &gt; o</strong></em> from the samples with <em><strong>y &lt; 0</strong></em>. The <em><strong>a<sub>n</sub></strong></em> coefficients can be calculated in a way that the distances of the plane to the nearest samples &#8211; which are called the &#8216;support vectors&#8217; of the plane,&nbsp;hence the algorithm name &#8211; is maximum. This way we have a binary classifier with optimal separation of winning and losing samples.</p>
<p>The problem: normally those samples are not <strong>linearly separable</strong> &#8211; they are scattered around irregularly in the feature space. No flat plane can be squeezed between winners and losers. If it could, we had simpler methods to calculate that plane, f.i.&nbsp;<strong>linear discriminant analysis</strong>. But for the common case we need the SVM trick: Adding more dimensions to the feature space. For this the SVM algorithm produces more features with a <strong>kernel function</strong> that combines any two existing predictors to a new feature.&nbsp;This is analogous to the step above from the simple regression to polynomial regression, where also more features are added by taking the sole predictor to the n-th power. The more dimensions you add, the easier it is to separate the samples with a flat hyperplane. This plane is then transformed back to the original n-dimensional space, getting wrinkled and crumpled on the way. By clever selecting the kernel function, the process can be performed without actually computing the transformation.</p>
<p>Like neural networks, SVMs can be used not only for classification, but also for regression. They also offer some parameters for optimizing and possibly overfitting the prediction process:</p>
<ul style="list-style-type: square;">
<li>Kernel function. You normally use a RBF kernel (radial basis function, a symmetric kernel), but you also have the choice of other kernels, such as sigmoid, polynomial, and linear.</li>
<li>Gamma, the width of the RBF kernel</li>
<li>Cost parameter C, the &#8216;penalty&#8217; for wrong classifications in the training samples</li>
</ul>
<p>An&nbsp;often used SVM is the <strong>libsvm</strong> library. It&#8217;s also available in R in the <strong>e1071</strong> package. In the next and final part of this series I plan to describe a trading strategy using this SVM.</p>
<h3>8. K-Nearest neighbor</h3>
<p>Compared with the heavy ANN and SVM stuff, that&#8217;s a nice simple algorithm with a unique property: It needs no training. So the samples are the model. You could use this algorithm for a trading system that learns permanently by simply adding more and more samples. The nearest neighbor algorithm computes the distances in feature space from the current feature values to the <em><strong>k</strong></em> nearest samples. A&nbsp;distance in n-dimensional space between two feature sets <em><strong>(x<sub>1</sub> .. x<sub>n</sub>)</strong></em> and <em><strong>(y<sub>1</sub> .. y<sub>n</sub>)</strong></em> is calculated just as in 2 dimensions:</p>
<p>[latex display=&#8221;true&#8221;]D = \sqrt{(x_1-y_1)^2 + (x_2-y_2)^2 + &#8230; + (x_n-y_n)^2}[/latex]</p>
<p>The algorithm simply predicts the target from the average of the <em><strong>k</strong></em> target variables of the nearest samples, weighted by their inverse distances. It can be used for classification as well as for regression. Software tricks borrowed from computer graphics, such as an <strong>adaptive binary tree</strong> (ABT), can make the nearest neighbor search pretty fast. In my past life as computer game programmer, we used such methods in games for tasks like self-learning enemy intelligence. You can call the <strong>knn</strong> function in R for nearest neighbor prediction &#8211; or write a simple function in C for that purpose.</p>
<h3>9. K-Means</h3>
<p>This is an approximation algorithm for unsupervised classification. It has some similarity, not only its name, to k-nearest neighbor. For classifying the samples, the algorithm first places <em><strong>k</strong></em> random points in the feature space. Then it assigns to any of those points all the samples with the smallest distances to it. The point is then moved to the <strong>mean</strong> of these nearest samples. This will generate a new samples assignment, since some samples are now closer to another point. The process is repeated until&nbsp;the assignment does not change anymore by moving the points, i.e. each point lies exactly at the mean of its nearest samples. We now have <em><strong>k</strong></em> classes of samples, each in the neighborhood of one of the <em><strong>k</strong></em> points.</p>
<p>This simple algorithm can produce surprisingly good results. In R, the <strong>kmeans</strong> function does the trick. An example of the k-means algorithm for classifying candle patterns can be found here: <a href="http://robotwealth.com/unsupervised-candlestick-classification-for-fun-and-profit-part-1/" target="_blank" rel="noopener noreferrer">Unsupervised candlestick classification for fun and profit</a>.</p>
<h3>10. Naive Bayes</h3>
<p>This algorithm uses <strong>Bayes&#8217; Theorem</strong> for classifying samples of non-numeric features (i.e. <strong>events</strong>), such as the above mentioned <strong>candle patterns</strong>. Suppose that an event&nbsp;<em><strong>X</strong></em> (for instance, that the Open of the previous bar is below the Open of the current bar) appears in 80% of all winning samples. What is then the probability that a sample is winning when it contains event&nbsp;<em><strong>X</strong></em>? It&#8217;s not 0.8 as you might think. The probability can be calculated with Bayes&#8217; Theorem:</p>
<p>[latex display=&#8221;true&#8221;]P(Y \vert X) = \frac{P(X \vert Y) P(Y)}{P(X)}[/latex]</p>
<p><em><strong>P(Y|X)</strong></em> is the probability that event <em><strong>Y</strong></em> (f.i. winning) occurs in all samples containing event <em><strong>X</strong></em>&nbsp;(in our example, <em><strong>Open(1) &lt; Open(0)</strong></em>). According to the formula, it is equal to the probability of <em><strong>X</strong></em>&nbsp;occurring in all winning samples (here, 0.8), multiplied by the probability of <em><strong>Y</strong></em> in all samples (around 0.5 when you were following my above advice of balanced samples) and divided by the probability of <em><strong>X</strong></em> in all samples.</p>
<p>If we are naive and assume that all events <em><strong>X</strong></em> are independent of each other, we can calculate the overall probability that a sample is winning by simply multiplying the probabilities <em><strong>P</strong><strong>(X|winning)</strong></em> for every event <em><strong>X</strong></em>. This way we end up with this formula:</p>
<p>[latex display=&#8221;true&#8221;]P(Y | X_1 .. X_n) ~=~ s~P(Y) \prod_{i}{P(X_i | Y)}[/latex]</p>
<p>with a scaling factor <em><strong>s</strong></em>. For the formula to work, the features should be selected in a way that they are as independent as possible, which imposes an obstacle for using Naive Bayes in trading. For instance, the two events <em><strong>Close(1) &lt; Close(0)</strong></em> and <em><strong>Open(1) &lt; Open(0)</strong></em> are most likely not independent of each other. Numerical predictors can be converted to events by dividing the number into separate ranges.</p>
<p>The Naive Bayes algorithm is available in the ubiquitous <strong>e1071</strong> R package.</p>
<h3>11. Decision and regression trees</h3>
<p>Those trees predict an outcome or a numeric value based on a series of yes/no decisions, in a structure like the branches of a tree. Any decision is either the presence of an event or not (in case of non-numerical features) or a comparison of a feature value with a fixed threshold. A typical tree function, generated by Zorro&#8217;s tree builder, looks like this:</p>
<pre class="prettyprint">int tree(double* sig)
{
  if(sig[1] &lt;= 12.938) {
    if(sig[0] &lt;= 0.953) return -70;
    else {
      if(sig[2] &lt;= 43) return 25;
      else {
        if(sig[3] &lt;= 0.962) return -67;
        else return 15;
      }
    }
  }
  else {
    if(sig[3] &lt;= 0.732) return -71;
    else {
      if(sig[1] &gt; 30.61) return 27;
      else {
          if(sig[2] &gt; 46) return 80;
          else return -62;
      }
    }
  }
}</pre>
<p>How is such a tree produced from a set of samples? There are several methods; Zorro uses the <strong>Shannon i</strong><strong>nformation entropy</strong>, which already had an appearance on this blog in the <a href="http://www.financial-hacker.com/is-scalping-irrational/" target="_blank" rel="noopener noreferrer">Scalping</a> article. At first it checks one of the features, let&#8217;s say&nbsp;<em><strong>x<sub>1</sub></strong></em>. It places a hyperplane with the plane formula&nbsp;<em><strong>x<sub>1</sub>&nbsp;= t&nbsp;</strong></em>into the feature space. This hyperplane separates the samples with <em><strong>x<sub>1</sub> &gt; t</strong></em> from the samples with <em><strong>x<sub>1</sub> &lt; t</strong></em>. The dividing threshold <strong><em>t</em></strong> is selected so that the <strong>information gain</strong> &#8211; the difference of information entropy of the whole space, to the sum of information entropies of the two divided sub-spaces &#8211; is maximum. This is the case when the samples in the subspaces are more similar to each other than the samples in the whole space.</p>
<p>This process is then repeated with the next feature&nbsp;<em><strong>x<sub>2</sub></strong></em>&nbsp;and two hyperplanes splitting the two subspaces. Each split is equivalent to a comparison of a feature with a threshold. By repeated splitting, we soon get a huge tree with thousands of threshold comparisons. Then the process is run backwards by <strong>pruning</strong> the tree and removing all decisions that do not lead to substantial information gain. Finally we end up with a relatively small tree as in the code above.</p>
<p>Decision trees have a wide range of applications. They can produce excellent predictions superior to those of neural networks or support vector machines. But they are not a one-fits-all solution, since their splitting planes are always parallel to the axes of the feature space. This somewhat limits their predictions. They can be used not only for classification, but also for regression, for instance by returning the percentage of samples contributing to a certain branch of the tree. Zorro&#8217;s tree is a regression tree. The best known classification tree algorithm is <strong>C5.0</strong>, available in the <strong>C50</strong> package for R.</p>
<p>For improving the prediction even further or overcoming the parallel-axis-limitation, an ensemble of trees can be used, called a <strong>random forest</strong>. The prediction is then generated by averaging or voting the predictions from the single trees. Random forests are available in R packages <strong>randomForest</strong>, <strong>ranger</strong> and <strong>Rborist</strong>.</p>
<h3>Conclusion</h3>
<p>There are many different data mining and machine learning methods at your disposal. The critical question: what is better, a <a href="http://www.financial-hacker.com/build-better-strategies-part-2-model-based-systems/" target="_blank" rel="noopener noreferrer">model-based</a>&nbsp;or a machine learning strategy? There is no doubt that machine learning has a lot of advantages. You don&#8217;t need to care about market microstructure, economy, trader psychology, or similar soft stuff. You can concentrate on pure mathematics. Machine learning is a much more elegant, more attractive way to generate trade systems. It has all advantages on its side but one. Despite all the enthusiastic threads on trader forums, it&nbsp;tends to mysteriously fail in live trading.</p>
<p>Every second week a new paper about trading with machine learning methods is published (a few can be found below). Please take all those publications with a grain of salt. According to some papers, <strong>phantastic win rates</strong> in the range of 70%, 80%, or even 85% have been achieved. Although win rate is not the only relevant criterion &#8211; you can lose even with a high win rate &#8211; 85% accuracy in predicting trades is normally equivalent to a profit factor above 5. With such a system the involved scientists should be billionaires meanwhile. Unfortunately I never managed to reproduce those win rates with the described method, and didn&#8217;t even come close. So maybe a lot of selection bias went into the results. Or maybe I&#8217;m just too stupid.</p>
<p>Compared with model based strategies, I&#8217;ve seen not many successful machine learning systems so far. And from what one hears about the algorithmic methods by successful hedge funds, machine learning seems still rarely to be used. But maybe this will change in the future with the availability of more processing power and the upcoming of new algorithms for deep learning.</p>
<h3>Papers</h3>
<ol>
<li>Classification using deep neural networks: <a href="http://papers.ssrn.com/sol3/papers.cfm?abstract_id=2756331" target="_blank" rel="noopener noreferrer">Dixon.et.al.2016</a></li>
<li>Predicting price direction using&nbsp;ANN &amp; SVM: &nbsp;<a href="https://www.researchgate.net/profile/Melek_Boyacioglu/publication/222043783_Predicting_direction_of_stock_price_index_movement_using_artificial_neural_networks_and_support_vector_machines_The_sample_of_the_Istanbul_Stock_Exchange/links/02e7e51815672d58af000000.pdf" target="_blank" rel="noopener noreferrer">Kara.et.al.2011</a></li>
<li>Empirical comparison of learning algorithms: <a href="https://www.cs.cornell.edu/~caruana/ctp/ct.papers/caruana.icml06.pdf" target="_blank" rel="noopener noreferrer">Caruana.et.al.2006</a></li>
<li>Mining stock market tendency using GA &amp; SVM: <a href="http://ww.svms.org/finance/YuWangLai2005.pdf" target="_blank" rel="noopener noreferrer">Yu.Wang.Lai.2005</a></li>
</ol>
<p>The next part of this series will deal with the practical development of a machine learning strategy.</p>
<p style="text-align: right;"><strong>⇒&nbsp;<a href="http://www.financial-hacker.com/build-better-strategies-part-5-developing-a-machine-learning-system/">Build Better Strategies – Part 5</a></strong></p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/build-better-strategies-part-4-machine-learning/feed/</wfw:commentRss>
			<slash:comments>44</slash:comments>
		
		
			</item>
		<item>
		<title>The Cold Blood Index</title>
		<link>https://financial-hacker.com/the-cold-blood-index/</link>
					<comments>https://financial-hacker.com/the-cold-blood-index/#comments</comments>
		
		<dc:creator><![CDATA[jcl]]></dc:creator>
		<pubDate>Mon, 26 Oct 2015 12:50:51 +0000</pubDate>
				<category><![CDATA[3 Most Useful]]></category>
		<category><![CDATA[Indicators]]></category>
		<category><![CDATA[System Evaluation]]></category>
		<category><![CDATA[Cold Blood Index]]></category>
		<category><![CDATA[Data mining bias]]></category>
		<category><![CDATA[Drawdown]]></category>
		<category><![CDATA[Grid trading]]></category>
		<category><![CDATA[Zorro]]></category>
		<guid isPermaLink="false">http://www.financial-hacker.com/?p=83</guid>

					<description><![CDATA[You&#8217;ve developed a new trading system. All tests produced impressive results. So you started it live. And are down by $2000 after 2 months. Or you have a strategy that worked for 2 years, but revently went into a seemingly endless drawdown. Situations are all too familiar to any algo trader. What now? Carry on in cold blood, &#8230; <a href="https://financial-hacker.com/the-cold-blood-index/" class="more-link">Continue reading<span class="screen-reader-text"> "The Cold Blood Index"</span></a>]]></description>
										<content:encoded><![CDATA[<p>You&#8217;ve developed a new trading system. All tests produced impressive results. So you started it live. And are down by $2000 after 2 months. Or you have a strategy that worked for 2 years, but revently went into a seemingly endless drawdown. Situations are all too familiar to any algo trader. What now? <strong>Carry on in cold blood, or pull the brakes in panic?</strong> <br />     Several reasons can cause a strategy to lose money right from the start. It can be already<strong> expired</strong> since the market inefficiency disappeared. Or the system is worthless and the test falsified by some <strong>bias</strong> that survived all reality checks. Or it&#8217;s a <strong>normal drawdown</strong> that you just have to sit out. In this article I propose an algorithm for deciding very early whether or not to abandon a system in such a situation.<span id="more-83"></span></p>
<p>When you start a trading strategy, you&#8217;re almost always under water for some time. This is a normal consequence of <strong>equity curve volatility</strong>. It is the very reason why you need initial capital at all for trading (aside from covering margins and transaction costs). Here you can see the typical bumpy start of a trading system:</p>
<figure id="attachment_252" aria-describedby="caption-attachment-252" style="width: 735px" class="wp-caption alignnone"><img loading="lazy" decoding="async" class="wp-image-252 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2015/09/z5zulu3.png" alt="z5zulu3" width="735" height="323" srcset="https://financial-hacker.com/wp-content/uploads/2015/09/z5zulu3.png 735w, https://financial-hacker.com/wp-content/uploads/2015/09/z5zulu3-300x132.png 300w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px" /><figcaption id="caption-attachment-252" class="wp-caption-text">CHF grid trader, initial live equity curve</figcaption></figure>
<p>You can estimate from the live equity curve that this system was rather profitable (it was a grid trader exploiting the CHF price cap). It started in July 2013 and had earned about 750 pips in January 2014, 7 months later. Max drawdown was ~400 pips from September until November. So the raw return of that system was about 750/400 ~= 180%. Normally an excellent value for a trade system. But you can also see from the curve that you were down 200 pips about six weeks into trading, and thus had lost almost half of your minimum initial capital. And if you had started the system in September, you had even stayed under water for more than 3 months! This is a psychologically difficult situation. Many traders panic, pull out, and this way <strong>lose money even with highly profitable systems</strong>. Algo trading unaffected by emotions? Not true.</p>
<h3>Not so out of sample</h3>
<p>The basic problem: you can never fully trust your test results. No matter how out-of-sample you test it, a strategy still suffers from a certain amount of <strong>Data-Snooping Bias</strong>. The standard method of measuring bias &#8211; <strong><a href="http://www.financial-hacker.com/whites-reality-check/">White&#8217;s Reality Check</a></strong> &#8211; works well for simple mechanically generated systems, as in the <strong><a href="http://www.financial-hacker.com/trend-and-exploiting-it/">Trend Experiment</a></strong>. But all human decisions about algorithms, asset selection, filters, training targets, stop/takeprofit mechanisms, WFO windows, money management and so on add new bias, since they are normally affected by testing. The out-of-sample data is then not so out-of-sample anymore. While the bias by training or optimization can be measured and even eliminated with walk forward methods, the <strong>bias introduced by the mere development process is unknown</strong>. The strategy might still be profitable, or not anymore, or not at all. You can only find out by comparing live results permanently with test results.</p>
<p>You could do that with no risk by trading on a demo account. But if the system is really profitable, demo time is sacrificed profit and thus expensive. Often very expensive, as you must demo trade a long time for some result significancy, and many strategies have a limited lifetime anyway. So you normally demo trade a system only a few weeks for making sure that the script is bug-free, then you go live with real money.</p>
<h3>Pull-out conditions</h3>
<p>The simplest method of comparing live results is based on the <strong>maximum drawdown</strong> in the test. This is the pull-out inequality:</p>
<p style="text-align: center;"><em><strong>[pmath size=18]E ~&lt;~ C + G t/y &#8211; D[/pmath]</strong></em></p>
<p><em><strong>E</strong></em> = Current account equity<br /> <em><strong>C</strong></em> = Initial account capital<br /> <em><strong>G</strong></em> = Test profit<br /> <em><strong>t</strong></em> = Live trading period<br /> <em><strong>y</strong></em> = Test period<br /> <em><strong>D</strong></em> = Test maximum drawdown</p>
<p>This formula means simply that you should pull out when the live trading drawdown exceeds the maximum drawdown from the test. Traders often check their live results this way, but there are many problems involved with this method:</p>
<ul style="list-style-type: square;">
<li>The maximum backtest drawdown is more or less random.</li>
<li>Drawdowns grow with the test period, thus longer test periods produce worse maximum drawdowns and later pull-out signals.</li>
<li>The drawdown time is not considered.</li>
<li>The method does not work when profits are reinvested by some money management algorithm.</li>
<li>The method does not consider the unlikeliness that the maximum drawdown happens already at live trading start.</li>
</ul>
<p>For those reasons, the above pullout inequality is often modified for taking the drawdown length and growth into account. The maximum drawdown is then assumed to <strong>grow with the square root of time</strong>, leading to this modified formula:</p>
<p style="text-align: center;"><strong><em>[pmath size=18]E ~&lt;~ C + G t/y &#8211; D sqrt{{t+l}/y}[/pmath]</em></strong></p>
<p><em><strong>E</strong></em> = Current account equity<br /> <em><strong>C</strong></em> = Initial account capital<br /> <em><strong>G</strong></em> = Test profit<br /> <em><strong>t</strong></em> = Live trading period<br /> <b><i>y</i></b> = Test period<br /> <em><strong>D</strong></em> = Maximum drawdown depth<br /> <b>l</b> = Maximum drawdown length</p>
<p> This was in fact the algorithm that I often suggested to clients for supervising their live results. It puts the drawdown in relation to the test period and also considers the drawdown length, as the probability of being inside the worst drawdown right at live trading start is <em><strong>l/y</strong></em>. Still, the method does not work with a profit reinvesting system. And it is dependent on the rather random test drawdown. You could address the latter issue by taking the drawdown from a Montecarlo shuffled equity curve, but this produces new problems since trading results have often serial correlation.</p>
<p>After this lenghty introduction for motivation, here&#8217;s the proposed algorithm that overcomes the mentioned issues.</p>
<h3>Keeping cold blood</h3>
<p>For finding out if we really must immediately stop a strategy, we calculate the deviation of the current live trading situation from the strategy behavior in the test. For this we do not use the maximum drawdown, but the backtest equity or balance curve:</p>
<ol>
<li>Determine a time window of length <em><strong>l </strong></em>(in days) that you want to check. It&#8217;s normally the length of the current drawdown; if your system is not in a drawdown, you&#8217;re probably in cold blood anyway. Determine the drawdown depth <em><strong>D</strong></em>,  i.e. the net loss during that time.</li>
<li>Place a time window of same size <em><strong>l </strong></em>at the start of the test balance curve.</li>
<li>Determine the balance difference <em><strong>G</strong></em> from end to start of the window. Increase a counter <em><strong>N</strong></em> when <em><strong>G &lt;= D</strong></em>. </li>
<li>Move the window forward by 1 day.</li>
<li>Repeat steps 3 and 4 until the window arrived at the end of the balance curve. Count the steps with a counter <em><strong>M</strong></em>.</li>
</ol>
<p>Any window movement takes a sample out of the curve. We have <em><strong>N</strong></em> samples that are similar or worse, and <em><strong>M-N</strong></em> samples that are better than the current trading situation. The probability to <strong>not</strong> encounter such a drawdown in <em><strong>T</strong></em> out of <em><strong>M</strong></em> samples is a simple combinatorial equation:</p>
<p style="text-align: center;"><em><strong>[pmath size=18]1-P ~=~ {(M-N)!(M-T)! }/ {M!(M-N-T)!}[/pmath]</strong></em></p>
<p><em><strong>N</strong></em> = Number of  <em><strong>G &lt;= D</strong></em> occurrences<br /> <em><strong>M</strong></em> = Total samples = <em><strong>y-l+1</strong></em><br /> <em><strong>l </strong></em>= Window length in days<em><strong><br /> </strong></em><em><strong>y</strong></em> = Test time in days<br /> <em><strong>T</strong></em> = Samples taken = <em><strong>t-l+1<br /> </strong><strong>t</strong></em> = Live trading time in days</p>
<p><em><strong>P</strong></em> is the <strong>cold blood index</strong> &#8211; the similarity of the live situation with the backtest. As long as <em><strong>P</strong></em> stays above 0.1 or 0.2, probably all is still fine. But if <em><strong>P</strong></em> is very low or zero, either the backtest was strongly biased or the market has significantly changed. The system can still be profitable, just less profitable as in the test. But when the current loss <em><strong>D</strong></em> is large in comparison to the gains so far, we should stop.</p>
<p>Often we want to calculate <strong>P</strong> soon after the begin of live trading. The window size <strong><em>l</em> </strong>is then identical to our trading time <em><strong>t</strong></em>,<em><strong> </strong></em>hence <em><strong>T == 1</strong></em>. This simplifies the equation to: </p>
<p style="text-align: center;"><em><strong>[pmath size=18]P ~=~ N/M[/pmath]</strong></em></p>
<p>In such a situation I&#8217;d give up and pull out of a painful drawdown as soon as <em><strong>P</strong></em> drops below 5%.</p>
<p>The slight disadvantage of this method is that you must perform a backtest with the same capital allocation, and store its balance or equity curve in a file for later evaluation during live trading. However this should only take a few lines of code in a strategy script. </p>
<p>Here&#8217;s a small example script for Zorro that calculates <em><strong>P</strong></em> (in percent) from a stored balance curve when a trading time <strong>t</strong> and drawdown of length <em><strong>l</strong></em> and depth <em><strong>D</strong></em> is given:</p>
<pre>int TradeDays = 40;    <em>// t, Days since live start</em>
int DrawDownDays = 30; <em>// l, Days since you're in drawdown</em>
var DrawDown = 100;    <em>// D, Current drawdown depth in $</em>

string BalanceFile = "Log\\BalanceDaily.dbl"; <em>// stored double array</em>

var logsum(int n)
{
  if(n &lt;= 1) return 0;
  return log(n)+logsum(n-1);
}

void main()
{
  int CurveLength = file_length(BalanceFile)/sizeof(var);
  var *Balances = file_content(BalanceFile);

  int M = CurveLength - DrawDownDays + 1;
  int T = TradeDays - DrawDownDays + 1;
 
  if(T &lt; 1 || M &lt;= T) {
    printf("Not enough samples!");
    return;
  }
 
  var GMin=0., N=0.;
  int i=0;
  for(; i &lt; M-1; i++)
  {
    var G = Balances[i+DrawDownDays] - Balances[i];
    if(G &lt;= -DrawDown) N += 1.;
    if(G &lt; GMin) GMin = G;
  } 

  var P;
  if(TradeDays &gt; DrawDownDays)
    P = 1. - exp(logsum(M-N)+logsum(M-T)-logsum(M)-logsum(M-N-T));
  else
    P = N/M;

  printf("\nTest period: %i days",CurveLength);
  printf("\nWorst test drawdown: %.f",-GMin);
  printf("\nM: %i N: %i T: %i",M,(int)N,T);
  printf("\nCold Blood Index: %.1f%%",100*P);
}</pre>
<p>Since my computer is unfortunately not good enough for calculating the factorials of some thousand samples, I&#8217;ve summed up the logarithms instead &#8211; therefore the strange <strong>logsum</strong> function in the script.</p>
<h3>Conclusion</h3>
<ul style="list-style-type: square;">
<li>Finding out early whether a live trading drawdown is &#8216;normal&#8217; or not can be essential for your wallet.</li>
<li>The backtest drawdown is a late and inaccurate criteria.</li>
<li>The Cold Blood Index calculates the precise probability of such a drawdown based on the backtest balance curve.</li>
</ul>
<p>I&#8217;ve added the script above to the 2015 scripts collection. I also have suggested to the Zorro developers to implement this method for automatically analyzing drawdowns while live trading, and issue warnings when <em><strong>P</strong></em> gets dangerously low. This can also be done separately for components in a portfolio system. This feature will probably appear in a future Zorro version. </p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/the-cold-blood-index/feed/</wfw:commentRss>
			<slash:comments>27</slash:comments>
		
		
			</item>
		<item>
		<title>Boosting Strategies with MMI</title>
		<link>https://financial-hacker.com/boosting-systems-by-trade-filtering/</link>
					<comments>https://financial-hacker.com/boosting-systems-by-trade-filtering/#comments</comments>
		
		<dc:creator><![CDATA[jcl]]></dc:creator>
		<pubDate>Mon, 28 Sep 2015 15:49:35 +0000</pubDate>
				<category><![CDATA[Indicators]]></category>
		<category><![CDATA[System Development]]></category>
		<category><![CDATA[System Evaluation]]></category>
		<category><![CDATA[Data mining bias]]></category>
		<category><![CDATA[Experiment]]></category>
		<category><![CDATA[Market Meanness Index]]></category>
		<category><![CDATA[White's reality check]]></category>
		<guid isPermaLink="false">http://www.financial-hacker.com/?p=320</guid>

					<description><![CDATA[We will now repeat our experiment with the 900 trend trading strategies, but this time with trades filtered by the Market Meanness Index. In our first experiment we found many profitable strategies, some even with high profit factors, but none of them passed White&#8217;s Reality Check. So they all would probably fail in real trading in spite of &#8230; <a href="https://financial-hacker.com/boosting-systems-by-trade-filtering/" class="more-link">Continue reading<span class="screen-reader-text"> "Boosting Strategies with MMI"</span></a>]]></description>
										<content:encoded><![CDATA[<p>We will now repeat our <a href="http://www.financial-hacker.com/trend-and-exploiting-it/">experiment</a> with the 900 trend trading strategies, but this time with trades filtered by the <strong><a href="http://www.financial-hacker.com/the-market-meanness-index/">Market Meanness Index</a></strong>. In our first experiment we found many profitable strategies, some even with high profit factors, but none of them passed <strong><a href="http://www.financial-hacker.com/whites-reality-check/">White&#8217;s Reality Check</a></strong>. So they all would probably fail in real trading in spite of their great results in the backtest. This time we hope that the MMI improves most systems by filtering out trades in non-trending market situations.<span id="more-320"></span></p>
<h3>900 systems experiment revisited</h3>
<p>I have been informed by readers that I committed two mistakes, or at least inaccuracies, in the previous experiment. First, I didn&#8217;t detrend the price data. Second, I used the equity curves instead of balance curves for determining the profit factor. I didn&#8217;t detrend the prices because the systems traded long/short in a symmetric way, and I supposed that this would eliminate any trend bias. But even if this was true back then, it is now not true anymore: filtering trades by MMI or other means can introduce asymmetry. Also, calculating the profit factor from the balance curve makes indeed more sense because you&#8217;re interested in the end profit of the trades, not in their interim behavior. Therefore and for the sake of comparable results I will now and in the future use detrended trade returns and balance curves for such experiments.</p>
<p>The original test, repeated with the modifications, produced a wider profit factor distribution due to eliminating intermediate returns. But the outcome of the experiment was the same. The statistic (including trade costs) did not change much, however the profit factor distribution (without trade costs) did. This is the new WRC histogram of the original 900 systems (best system vs. bootstrap-randomized returns of all systems):</p>
<figure id="attachment_438" aria-describedby="caption-attachment-438" style="width: 729px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2015/09/trendplain.png"><img loading="lazy" decoding="async" class="wp-image-438 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2015/09/trendplain.png" alt="" width="729" height="251" srcset="https://financial-hacker.com/wp-content/uploads/2015/09/trendplain.png 729w, https://financial-hacker.com/wp-content/uploads/2015/09/trendplain-300x103.png 300w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px" /></a><figcaption id="caption-attachment-438" class="wp-caption-text">900 trend systems (no MMI)</figcaption></figure>
<p>Although the best system (black bar, a system using ALMA) is at the right side of the distribution, still 11% of random systems were better. The system does not pass the WRC at the required 95% confidence level. This turned out very different when filtering trades with the MMI.</p>
<h3>The MMI experiment</h3>
<p>This is our script <strong>TrendMMI.c</strong> for the new experiment:</p>
<pre><em><span style="color: #3366ff;">// helper function: remove systems that exceed the 4 months lookback period</span></em>
int checkLookBack(int Period) 
{
  if(Period &gt;= LookBack/TimeFrame) {
    StepNext = 0;	<em><span style="color: #3366ff;">// abort optimization</span></em>
    return LookBack/TimeFrame; <em><span style="color: #3366ff;">// reduce the period</span></em>
  } else
    return Period;
}

<em><span style="color: #3366ff;">// calculate profit factor and remove systems with not enough trades</span> </em>
var objective() 
{ 
  if(NumWinTotal &lt; 30 || NumLossTotal &lt; 30) { 
    StepNext = 0;     <em><span style="color: #3366ff;">// abort optimization</span></em>
    return 0;         <em><span style="color: #3366ff;">// don't store this system</span></em>
  } else
      return WinTotal/LossTotal; <em><span style="color: #3366ff;">// Profit factor</span></em>
}

var filter(var* Data,int Period);

void run()
{
  set(PARAMETERS|LOGFILE);
  Curves = "DailyBalance.bin";
  StartDate = 2010;
  BarPeriod = 15;
  LookBack = 80*4*24; <em><span style="color: #3366ff;">// ~ 4 months</span></em>
  Detrend = TRADES;<em><span style="color: #3366ff;">   // detrend trade results</span></em>
  while(asset(loop("EUR/USD","SPX500","XAG/USD")))
  while(algo(loop("MM15","MH1","MH4")))
  {
    TimeFrame = 1;
    if(Algo == "MH1")
      TimeFrame = 1*4;
    else if(Algo == "MH4")
      TimeFrame = 4*4;	

<em><span style="color: #3366ff;">// no trade costs</span></em>
    Spread = Commission = RollLong = RollShort = Slippage = 0;

    int Periods[10] = { 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000 };
    int Period = Periods[round(optimize(1,1,10,1),1)-1];
		
    var *Price = series(price());
    var *Smoothed = series(filter(Price,Period));

    bool DoTrade = true;
    int MMIPeriod = optimize(0,200,500,100);
    if(MMIPeriod) {
      MMIPeriod = checkLookBack(MMIPeriod);
      var *MMI_Raw = series(MMI(Price,MMIPeriod));
      var *MMI_Smooth = series(LowPass(MMI_Raw,MMIPeriod));
      DoTrade = falling(MMI_Smooth);
    }

    if(DoTrade) {
      if(valley(Smoothed))
        enterLong();
      else if(peak(Smoothed))
        enterShort();
    }
  } 
}</pre>
<p>The 10 trend trading scripts with the 10 different indicators remain unchanged, aside from now including <strong>TrendMMI.c</strong> instead of <strong>Trend.c</strong>. Trading is now dependent on a boolean variable <strong>DoTrade</strong>. The length of the MMI range is varied between 200, 300, 400, and 500 bars. As most parameters in a strategy, the MMI range is a compromise: It should be no less than 200 bars for getting any accuracy, but it should not be too long for preventing that different market regimes fall in the same MMI range. At the default range of 0, no MMI is applied and trading is not filtered. This way we&#8217;re including all the previous systems in the test. This is required for properly detecting Data Mining Bias, which must consider all systems that were discarded based on their result.</p>
<p>We&#8217;re running the MMI return value through a lowpass filter that uses the same period as the MMI range. This gives us a smooth MMI value that does not jump around. This value is now used for trade filtering: trades are opened and closed only when the smoothed MMI is falling, meaning that the market has entered trending mode within the last 200 to 500 bars. The MMI is only applied to one of the systems resulting from the prior period variation (the <strong>optimize</strong> function automatically selects the parameter of the &#8220;most robust&#8221; system before optimizing the next parameter). So now we&#8217;re testing in fact not 900, but 1260 systems: 900 without MMI and each 90 with MMI ranges of 200, 300, 400, and 500 bars. The systems with not enough trades or a too-long lookback period are again removed from the pool, so the real number of tested systems is about 1100.</p>
<p>Depending on the speed of your PC, Zorro will need about 1 hour to test all systems. At the end of every system test, Zorro produces the parameter histograms. We have now two parameters. The histogram of the first one, the price smoothing filter period, looks as before because MMI was switched off during optimization. The second histogram displays the MMI range in combination with the best value from the first histogram. &#8220;Best&#8221; is here not the highest bar from the previous histogram, but the value that Zorro deems the most robust and least sensitive to market changes. A typical MMI histograms look like this:</p>
<p><a href="http://www.financial-hacker.com/wp-content/uploads/2015/09/TrendEMA_SPX500MM15_p2.png"><img loading="lazy" decoding="async" class="wp-image-405 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2015/09/TrendEMA_SPX500MM15_p2.png" alt="" width="244" height="201" /></a></p>
<p>The first bar, marked &#8220;100&#8221;, is the best system without MMI. We can see that it is unprofitable: The profit factor (left scale) is only about 0.8. Using the MMI with a range of 200 and 300 makes the system in fact worse, and reduces the profit factor t0 0.7. However the last two MMI ranges, 400 and 500, shift the system into the profit zone. This was just a random example, but how does the MMI affect all the other systems? Here are the statistics from the MMI experiment:</p>
<table>
<tbody>
<tr>
<td>Asset, Period, Indicator</td>
<td style="text-align: right;">Success Rate</td>
<td style="text-align: right;">Winning</td>
<td style="text-align: right;">Losing</td>
</tr>
<tr>
<td><strong>EUR/USD</strong></td>
<td style="text-align: right;"><strong>46% (+8%)</strong></td>
<td style="text-align: right;"><strong>154</strong></td>
<td style="text-align: right;"><strong>185</strong></td>
</tr>
<tr>
<td><strong>S&amp;P 500</strong></td>
<td style="text-align: right;"><strong>4% (+3%)</strong></td>
<td style="text-align: right;"><strong>15</strong></td>
<td style="text-align: right;"><strong>318</strong></td>
</tr>
<tr>
<td><strong>Silver</strong></td>
<td style="text-align: right;"><strong>27% (+7%)</strong></td>
<td style="text-align: right;"><strong>87</strong></td>
<td style="text-align: right;"><strong>240</strong></td>
</tr>
<tr>
<td><strong>15 Minutes</strong></td>
<td style="text-align: right;"><strong>18% (+7%)</strong></td>
<td style="text-align: right;"><strong>71</strong></td>
<td style="text-align: right;"><strong>322</strong></td>
</tr>
<tr>
<td><strong>1 Hour</strong></td>
<td style="text-align: right;"><strong>27% (+9%)</strong></td>
<td style="text-align: right;"><strong>92</strong></td>
<td style="text-align: right;"><strong>251</strong></td>
</tr>
<tr>
<td><strong>4 Hours</strong></td>
<td style="text-align: right;"><strong>35% (+2%)</strong></td>
<td style="text-align: right;"><strong>93</strong></td>
<td style="text-align: right;"><strong>170</strong></td>
</tr>
<tr>
<td><strong>ALMA</strong></td>
<td style="text-align: right;"><strong>22% (+6)%</strong></td>
<td style="text-align: right;"><strong>22</strong></td>
<td style="text-align: right;"><strong>79</strong></td>
</tr>
<tr>
<td><strong>Decycle</strong></td>
<td style="text-align: right;"><strong>21% (+8%)</strong></td>
<td style="text-align: right;"><strong>23</strong></td>
<td style="text-align: right;"><strong>89</strong></td>
</tr>
<tr>
<td><strong>EMA</strong></td>
<td style="text-align: right;"><strong>23% (+5%)</strong></td>
<td style="text-align: right;"><strong>24</strong></td>
<td style="text-align: right;"><strong>79</strong></td>
</tr>
<tr>
<td><strong>HMA</strong></td>
<td style="text-align: right;"><strong>34% (+9%)</strong></td>
<td style="text-align: right;"><strong>33</strong></td>
<td style="text-align: right;"><strong>66</strong></td>
</tr>
<tr>
<td><strong>Laguerre</strong></td>
<td style="text-align: right;"><strong>33% (+3%)</strong></td>
<td style="text-align: right;"><strong>20</strong></td>
<td style="text-align: right;"><strong>38</strong></td>
</tr>
<tr>
<td><strong>LinearReg</strong></td>
<td style="text-align: right;"><strong>29% (+6%)</strong></td>
<td style="text-align: right;"><strong>31</strong></td>
<td style="text-align: right;"><strong>77</strong></td>
</tr>
<tr>
<td><strong>Lowpass</strong></td>
<td style="text-align: right;"><strong>24% (+5%)</strong></td>
<td style="text-align: right;"><strong>26</strong></td>
<td style="text-align: right;"><strong>82</strong></td>
</tr>
<tr>
<td><strong>SMA</strong></td>
<td style="text-align: right;"><strong>26% (+5%)</strong></td>
<td style="text-align: right;"><strong> 27</strong></td>
<td style="text-align: right;"><strong> 76</strong></td>
</tr>
<tr>
<td><strong>Smooth</strong></td>
<td style="text-align: right;"><strong>26% (+7%)</strong></td>
<td style="text-align: right;"><strong>23</strong></td>
<td style="text-align: right;"><strong>67</strong></td>
</tr>
<tr>
<td><strong>ZMA</strong></td>
<td style="text-align: right;"><strong>22% (+8)%</strong></td>
<td style="text-align: right;"><strong>27</strong></td>
<td style="text-align: right;"><strong>90</strong></td>
</tr>
</tbody>
</table>
<p>The <strong>Rate</strong> column shows the percentage of successful systems, and in parentheses the difference to the percentage without MMI. We can see that the MMI increased the number of successful systems in all markets, time frames, and indicators. However the numbers are not really representative: the MMI only affected a quarter of the tested systems, but the upper quarter, so some increase in the number of profitable systems was to be expected anyway. A more meaningful measure is the WRC. We&#8217;re using the same <strong>Bootstrap.c</strong> script as in the <a href="http://www.financial-hacker.com/whites-reality-check/">previous experiment</a>, we only need to increase the CURVES number to 1260. This is the WRC histogram of systems with MMI (again, best system vs. bootstrapped returns of all systems):</p>
<figure id="attachment_439" aria-describedby="caption-attachment-439" style="width: 833px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2015/09/trendmmi.png"><img loading="lazy" decoding="async" class="wp-image-439 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2015/09/trendmmi.png" alt="" width="833" height="251" srcset="https://financial-hacker.com/wp-content/uploads/2015/09/trendmmi.png 833w, https://financial-hacker.com/wp-content/uploads/2015/09/trendmmi-300x90.png 300w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px" /></a><figcaption id="caption-attachment-439" class="wp-caption-text">900 trend systems (with MMI)</figcaption></figure>
<p>The MMI filter now shifted the best system (black) far to the right side of the histogram. It got a <strong>p-value of 0.02</strong>, meaning that it is better than 98% of the best randomized systems, and thus well above the 95% significance level. Using the MMI for filtering trades, the method of trading on curve peaks and valleys passed White&#8217;s Reality Check. In fact two of the 1260 systems got p-values above the significance level.</p>
<p>The best systems of the experiment had some things in common: They traded with silver and used either the ALMA or the lowpass filter. This is a surprising result, because neither silver nor ALMA and  lowpass had the highest number of profitable systems. From the above table, one would assume that EUR/USD and the HMA or Laguerre filter are the most promising. They indeed produced many apparently good systems with profit factors above 2 (without trade costs), but none of them passed the WRC.</p>
<h3>Conclusion</h3>
<ul style="list-style-type: square;">
<li>The MMI improved trend following systems by 5%&#8230;10% average with all tested markets, time frames, and indicators. Best systems were improved by more than 50%.</li>
<li>Trend following systems using the MMI can pass White&#8217;s Reality Check.</li>
<li>From the 10 tested smoothing indicators, <a href="http://www.financial-hacker.com/trend-delusion-or-reality/">ALMA</a> produced the best results, although within a relatively small parameter range.</li>
<li>To do: Test more trend filters, f.i. the Hurst Exponent or Ehlers&#8217; Trend/ Cycle decomposition.</li>
<li>To do: Create a real trading system by combining the best trend systems and adding the usual system components such as stop loss, trailing algorithm, profit lock, money management, and so on.</li>
</ul>
<p>I&#8217;ve added the scripts to the 2015 scripts collection.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/boosting-systems-by-trade-filtering/feed/</wfw:commentRss>
			<slash:comments>34</slash:comments>
		
		
			</item>
		<item>
		<title>White&#8217;s Reality Check</title>
		<link>https://financial-hacker.com/whites-reality-check/</link>
					<comments>https://financial-hacker.com/whites-reality-check/#comments</comments>
		
		<dc:creator><![CDATA[jcl]]></dc:creator>
		<pubDate>Mon, 14 Sep 2015 08:48:17 +0000</pubDate>
				<category><![CDATA[Research]]></category>
		<category><![CDATA[System Evaluation]]></category>
		<category><![CDATA[Aronson]]></category>
		<category><![CDATA[Data mining bias]]></category>
		<category><![CDATA[Detrending]]></category>
		<category><![CDATA[Momentum]]></category>
		<category><![CDATA[White's reality check]]></category>
		<guid isPermaLink="false">http://www.financial-hacker.com/?p=188</guid>

					<description><![CDATA[This is the third part of the Trend Experiment article series. We now want to evaluate if the positive results from the 900 tested trend following strategies are for real, or just caused by Data Mining Bias. But what is Data Mining Bias, after all? And what is this ominous White&#8217;s Reality Check? Suppose you &#8230; <a href="https://financial-hacker.com/whites-reality-check/" class="more-link">Continue reading<span class="screen-reader-text"> "White&#8217;s Reality Check"</span></a>]]></description>
										<content:encoded><![CDATA[<p>This is the third part of the <a href="http://www.financial-hacker.com/trend-delusion-or-reality/" target="_blank" rel="noopener noreferrer">Trend Experiment</a> article series. We now want to evaluate if the positive results from the 900 tested trend following strategies are for real, or just caused by <strong>Data Mining Bias</strong>. But what is Data Mining Bias, after all? And what is this ominous <strong>White&#8217;s Reality Check</strong>?<span id="more-188"></span></p>
<p>Suppose you want to trade by moon phases. But you&#8217;re not sure if you shall buy at full moon and sell at new moon, or the other way around. So you do a series of moon phase backtests and find out that the best system, which opens positions at any first quarter moon, achieves 30% annual return. Is this finally the proof that astrology works?</p>
<p>A trade system based on a nonexisting effect has normally a profit expectancy of zero (minus trade costs). But you won&#8217;t get zero when you backtest variants of such a system. Due to statistical fluctuations, some of them will produce a positive and some a negative return. When you now pick the best performer, such as the first quarter moon trading system, you might get a high return and an impressive equity curve in the backtest. Sadly, its test result is not necessarily caused by clever trading. It might be just by clever selecting the random best performer from a pool of useless systems.</p>
<p>For finding out if the 30% return by quarter moon trading are for real or just the fool&#8217;s gold of Data Mining Bias, <strong>Halbert White</strong> (1959-2012) invented a test method in 2000.  <strong>White&#8217;s Reality Check </strong>(aka <strong>Bootstrap Reality Check</strong>) is explained in detail in the book &#8216;Evidence-Based Technical Analysis&#8217; by <strong>David Aronson</strong>. It works this way:</p>
<ol>
<li>Develop a strategy. During the development process, keep a record of all strategy variants that were tested and discarded because of their test results, including all abandoned algorithms, ideas, methods, and parameters. It does not matter if they were discarded by human decision or by a computer search or optimizing process.</li>
<li>Produce balance curves of all strategy variants, using detrended trade results and no trade costs. Note down the profit <strong>P</strong> of the best strategy.</li>
<li>Detrend all balance curves by subtracting the mean return per bar (not to be confused with detrending the trade results!). This way you get a series of curves with the same characteristics of the tested systems, but zero profit.</li>
<li>Randomize all curves by bootstrap with replacement. This produces new curves from the random returns of the old curves. Because the same bars can be selected multiple times, most new curves now produce losses or profits different from zero.</li>
<li>Select the best performer from the randomized curves, and note down its profit.</li>
<li> Repeat steps 4 and 5 a couple 1000 times.</li>
<li>You now have a list of several 1000 best profits. The median <strong>M</strong> of that list is the Data Mining Bias by your strategy development process.</li>
<li>Check where the original best profit <strong>P</strong> appears in the list. The percentage of best bootstrap profits greater than <strong>P</strong> is the so-called <strong>p-Value</strong> of the best strategy. You want the p-Value to be as low as possible. If <strong>P</strong> is better than 95% of the best bootstrap profits, the best strategy has a real edge with 95% probability.</li>
<li><strong>P</strong> minus <strong>M</strong> minus trade costs is the result to be expected in real trading the best strategy.</li>
</ol>
<p>The method is not really intuitive, but mathematically sound. However, it suffers from a couple problems that makes WRC difficult to use  in real strategy development:</p>
<ul style="list-style-type: square;">
<li>You can see the worst problem already in step 1. During strategy development you&#8217;re permanently testing ideas, adding or removing parameters, or checking out different assets and time frames. Putting aside all discarded variants and producing balance curves of all combinations of them is a cumbersome process. It gets even more diffcult with machine learning algorithms that optimize weight factors and usually do not produce discarded variants. However, the good news are that you can easily apply the WRC when your strategy variants are produced by a transparent mechanical process with no human decisions involved. That&#8217;s fortunately the case for our trend experiment.</li>
<li>WRC tends to type II errors. That means it can reject strategies although they have an edge. When more irrelevant variants &#8211; systems with random trading and zero profit expectancy &#8211; are added to the pool,  more positive results can be produced in steps 4 and 5, which reduces the probability that your selected strategy survives the test. WRC can determine rather good that a system is profitable, but can not as well determine that it is worthless.</li>
<li>It gets worse when variants have a negative expectancy.  WRC can then over-estimate Data Mining Bias (see paper 2 at the end of the article). This could theoretically also happen with our trend systems, as some variants may suffer from a phase reversal due to the delay by the smoothing indicators, and thus in fact trade against the trend instead of with it.</li>
</ul>
<h3>The Experiment</h3>
<p>First you need to collect daily return curves from all tested strategies. This requires adding a few lines to the <strong>Trend.c</strong> script from the <a href="http://www.financial-hacker.com/trend-and-exploiting-it/">previous article</a>:</p>
<pre class="prettyprint"><span style="color: #0000ff;">// some global variables</span>
int Period;
var Daily[3000];
...
<span style="color: #0000ff;">// in the run function, set all trading costs to zero</span>
 Spread = Commission = RollLong = RollShort = Slippage = 0;<span style="color: #0000ff;">
...
// store daily results in an equity curve</span>
  Daily[Day] = Equity;
}
...
<span style="color: #0000ff;">// in the objective function, save the curves in a file for later evaluation</span>
string FileName = "Log\\TrendDaily.bin";
string Name = strf("%s_%s_%s_%i",Script,Asset,Algo,Period);
int Size = Day*sizeof(var); 
file_append(FileName,Name,strlen(Name)+1);
file_append(FileName,&amp;Size,sizeof(int));
file_append(FileName,Daily,Size);</pre>
<p>The second part of the above code stores the equity at the end of any day in the <strong>Daily</strong> array. The third part stores a string with the name of the strategy, the length of the curve,  and the equity values itself in a file named <strong>TrendDaily.bin</strong> in the <strong>Log</strong> folder. After running the 10 trend scripts, all 900 resulting curves are collected together in the file.</p>
<p>The next part of our experiment is the <strong>Bootstrap.c</strong> script that applies White&#8217;s Reality Check. I&#8217;ll write it in two parts. The first part just reads the 900 curves from the <strong>TrendDaily.bin</strong> file, stores them for later evaluation, finds the best one and displays a histogram of the profit factors. Once we got that, we did already 80% of the work for the Reality Check. This is the code:</p>
<pre class="prettyprint">void _plotHistogram(string Name,var Value,var Step,int Color)
{
  var Bucket = floor(Value/Step);
  plotBar(Name,Bucket,Step*Bucket,1,SUM+BARS+LBL2,Color);
}

typedef struct curve
{
  string Name;
  int Length;
  var *Values;
} curve;

curve Curve[900];
var Daily[3000];

void main()
{
  byte *Content = file_content("Log\\TrendDaily.bin");
  int i,j,N = 0;
  int MaxN = 0;
  var MaxPerf = 0.0;
	
  while(N&lt;900 &amp;&amp; *Content)
  {
<span style="color: #0000ff;">// extract the next curve from the file</span>
    string Name = Content;
    Content += strlen(Name)+1;
    int Size = *((int*)Content);
    int Length = Size/sizeof(var); // number of values
    Content += 4;
    var *Values = Content;
    Content += Size;

<span style="color: #0000ff;">// store and plot the curve</span>		
    Curve[N].Name = Name;
    Curve[N].Length = Length;
    Curve[N].Values = Values;
    var Performance = 1.0/ProfitFactor(Values,Length);
    printf("\n%s: %.2f",Name,Performance);
    _plotHistogram("Profit",Performance,0.005,RED);

<span style="color: #0000ff;">// find the best curve</span>		
    if(MaxPerf &lt; Performance) {
      MaxN = N; MaxPerf = Performance;
    }
    N++;
  }
  printf("\n\nBenchmark: %s, %.2f",Curve[MaxN].Name,MaxPerf); 
}</pre>
<p>Most of the code is just for reading and storing all the equity curves. The indicator <strong>ProfitFactor </strong>calculates the profit factor of the curve, the sum of all daily wins divided by the sum of all daily losses. However, here we need to consider the array order. Like many platforms, Zorro stores time series in reverse chronological order,  with the most recent data at the begin. However we stored the daily equity curve in straight chronological order. So the losses are actually wins and the wins actually losses, which is why we need to inverse the profit factor. The curve with the best profit factor will be our benchmark for the test.</p>
<p>This is the resulting histogram, the profit factors of all 900 (or rather, 705 due to the trade number minimum) equity curves:</p>
<figure id="attachment_244" aria-describedby="caption-attachment-244" style="width: 833px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2015/09/trend_s.png"><img loading="lazy" decoding="async" class="wp-image-244 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2015/09/trend_s.png" alt="" width="833" height="201" srcset="https://financial-hacker.com/wp-content/uploads/2015/09/trend_s.png 833w, https://financial-hacker.com/wp-content/uploads/2015/09/trend_s-300x72.png 300w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px" /></a><figcaption id="caption-attachment-244" class="wp-caption-text">Profit factor distribution (without trade costs)</figcaption></figure>
<p>Note that the profit factors are slightly different to the parameter charts of the previous article because they were now calculated from daily returns, not from trade results. We removed trading costs, so the histogram is centered at a profit factor 1.0, aka zero profit. Only a few systems achieved a profit factor in the 1.2 range, the two best made about 1.3. Now we&#8217;ll see what White has to say to that. This is the rest of the <strong>main</strong> function in <strong>Bootstrap.c</strong> that finally applies his Reality Check:</p>
<pre>plotBar("Benchmark",MaxPerf/0.005,MaxPerf,50,BARS+LBL2,BLUE);	
printf("\nBootstrap - please wait");
int Worse = 0, Better = 0;
for(i=0; i&lt;1000; i++) {
  var MaxBootstrapPerf = 0;
  for(j=0; j&lt;N; j++) {
    randomize(BOOTSTRAP|DETREND,Daily,Curve[j].Values,Curve[j].Length);
    var Performance = 1.0/ProfitFactor(Daily,Curve[j].Length);
    MaxBootstrapPerf = max(MaxBootstrapPerf,Performance);
  }
  if(MaxPerf &gt; MaxBootstrapPerf)
    Better++;
  else
    Worse++;
  _plotHistogram("Profit",MaxBootstrapPerf,0.005,RED);
  progress(100*i/SAMPLES,0);
}
printf("\nBenchmark beats %.0f%% of samples!",
  (var)Better*100./(Better+Worse));
</pre>
<p>This code needs about 3 minutes to run; we&#8217;re sampling the 705 curves 1000 times. The <strong>randomize</strong> function will shuffle the daily returns by bootstrap with replacement; the <strong>DETREND</strong> flag tells it to subtract the mean return from all returns before. The number of random curves that are better and worse than the benchmark is stored, for printing the percentage at the end. The <strong>progress</strong> function moves the progress bar while Zorro grinds through the 705,000 curves. And this is the result:</p>
<figure id="attachment_245" aria-describedby="caption-attachment-245" style="width: 703px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2015/09/bootstrap_s.png"><img loading="lazy" decoding="async" class="wp-image-245 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2015/09/bootstrap_s.png" alt="" width="703" height="201" srcset="https://financial-hacker.com/wp-content/uploads/2015/09/bootstrap_s.png 703w, https://financial-hacker.com/wp-content/uploads/2015/09/bootstrap_s-300x86.png 300w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px" /></a><figcaption id="caption-attachment-245" class="wp-caption-text">Bootstrap results (red), benchmark system (black)</figcaption></figure>
<p>Hmm. We can see that the best system &#8211; the black bar &#8211; is at the right side of the histogram, indicating that it might be significant. But only with about 80% probability (the script gives a slightly different result every time due to the randomizing). 20% of the random curves achieve better profit factors than the best system from the experiment. The median of the randomized samples is about 1.26. Only the two best systems from the original distribution (first image) have profit factors above 1.26 &#8211; all the rest is at or below the bootstrap median.</p>
<p>So we have to conclude that this simple way of trend trading does not really work. Interestingly, one of those 900 tested systems is a system that I use for myself since 2012, although with additional filters and conditions. This system has produced good live returns and a positive result by the end of every year so far. And there&#8217;s still the fact that EUR/USD and silver in all variants produced better statistics than S&amp;P500. This hints that some trend effect exists in their price curves, but the profit factors by the simple algorithms are not high enough to pass White&#8217;s Reality Check. We need a better approach for trend exploitation. For instance, a filter that detects if trend is there or not. This will be the topic of the <a href="http://www.financial-hacker.com/the-market-meanness-index/" target="_blank" rel="noopener noreferrer">next articl</a>e of this series. We will see that a filter can have a surprising effect on reality checks. Since we now have the <strong>Bootstrap </strong>script for applying White&#8217;s Reality Check, we can quickly do further experiments.</p>
<p>The <strong>Bootstrap.c</strong> script has been added to the 2015 script collection downloadable on the sidebar.</p>
<h3>Conclusion</h3>
<ul style="list-style-type: square;">
<li>None of the 10 tested low-lag indicators, and none of the 3 tested markets shows significant positive expectancy with trend trading.</li>
<li>There is evidence of a trend effect in currencies and commodities, but it is too weak or too infrequent for being effectively exploited with simple trade signals by a filtered price curve.</li>
<li>We have now a useful code framework for comparing indicators and assets, and for further experiments with trade strategies.</li>
</ul>
<h3>Papers</h3>
<ol>
<li>Original paper by Dr. H. White:  <a href="http://www.ssc.wisc.edu/~bhansen/718/White2000.pdf" target="_blank" rel="noopener noreferrer">White2000</a></li>
<li>WRC modification by P. Hansen: <a href="http://www-siepr.stanford.edu/workp/swp05003.pdf" target="_blank" rel="noopener noreferrer">Hansen2005</a></li>
<li>Stepwise WRC Testing by J. Romano, M. Wolf: <a href="http://www.ssc.wisc.edu/~bhansen/718/RomanoWolf2005.pdf" target="_blank" rel="noopener noreferrer">RomanoWolf2005</a></li>
<li>Technical Analysis examined with WRC, by P. Hsu, C. Kuan: <a href="http://front.cc.nctu.edu.tw/Richfiles/15844-930305.pdf" target="_blank" rel="noopener noreferrer">HsuKuan2006</a></li>
<li>WRC and its Extensions by V. Corradi, N. Swanson: <a href="http://econweb.rutgers.edu/nswanson/papers/corradi_swanson_whitefest_1108_2011_09_06.pdf" target="_blank" rel="noopener noreferrer">CorradiSwanson2011</a></li>
</ol>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/whites-reality-check/feed/</wfw:commentRss>
			<slash:comments>32</slash:comments>
		
		
			</item>
	</channel>
</rss>
