<?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>Research &#8211; The Financial Hacker</title>
	<atom:link href="https://financial-hacker.com/category/research/feed/" rel="self" type="application/rss+xml" />
	<link>https://financial-hacker.com</link>
	<description>A new view on algorithmic trading</description>
	<lastBuildDate>Sun, 11 Jan 2026 12:48:05 +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>Research &#8211; The Financial Hacker</title>
	<link>https://financial-hacker.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>&#8220;Please Send Me a Trading System!&#8221;</title>
		<link>https://financial-hacker.com/please-send-me-a-trading-system/</link>
					<comments>https://financial-hacker.com/please-send-me-a-trading-system/#comments</comments>
		
		<dc:creator><![CDATA[jcl]]></dc:creator>
		<pubDate>Thu, 08 Oct 2020 09:26:00 +0000</pubDate>
				<category><![CDATA[3 Most Useful]]></category>
		<category><![CDATA[Introductory]]></category>
		<category><![CDATA[No Math]]></category>
		<category><![CDATA[Research]]></category>
		<category><![CDATA[System Evaluation]]></category>
		<guid isPermaLink="false">https://financial-hacker.com/?p=3565</guid>

					<description><![CDATA[&#8220;It should produce 150 pips per week. With the best EAs and indicators that you know. How much does it cost? Please also send live histories of your top systems.&#8221;  Although we often get such requests, we still don&#8217;t know the best indicators, don&#8217;t believe in best EAs, and don&#8217;t sell top systems. We do &#8230; <a href="https://financial-hacker.com/please-send-me-a-trading-system/" class="more-link">Continue reading<span class="screen-reader-text"> "&#8220;Please Send Me a Trading System!&#8221;"</span></a>]]></description>
										<content:encoded><![CDATA[
<p><em>&#8220;It should produce 150 pips per week. With the best EAs and indicators that you know. How much does it cost? Please also send live histories of your top systems.&#8221;</em> <br />
Although we often get such requests, we still don&#8217;t know the best indicators, don&#8217;t believe in best EAs, and don&#8217;t sell top systems. We do not sell algo trading systems at all, but only program them for clients after their specifications. We do not trade them, except for testing. But after programming almost 1000 systems, we can see <strong>a pattern emerging</strong>. Which trading <span style="font-size: inherit;">strategies do usually work? Which will fall apart already in the backtest? Here&#8217;s a ranking of all systems we did so far, with a surprising winner.</span><span id="more-3565"></span></p>
<p>One should think that most clients come up with very similar trading systems, so we could meanwhile just click them together from ready code. But it is not so. There&#8217;s apparently no limit of trading ideas. Almost any other system uses some new trading method, unusual data source, or exotic indicator. Still, the systems can be classified in several simple categories. Any of them has its specific success and failure rate.</p>
<h3>Trading systems categorized</h3>
<p>We classify the systems by their market and by their trading rules, both specified by the client. The 4 main markets are Forex/ CFDs, cryptocurrencies, stocks/ETFs/futures, and options. And the 4 <a href="https://zorro-project.com/algotrading.php" target="_blank" rel="noopener">main algorithmic trading methods</a> are risk premia, market models, data mining, and indicator soups.  To recap:</p>
<p><strong>Risk premium systems</strong> gain higher profits by accepting higher risks. In that category fall many stock portfolio rotation and options trading systems.</p>
<p><strong>Market model systems</strong> exploit a particular market inefficiency by detecting anomalies in price curves. Mean reversion, market cycles, market events, or statistical arbitrage are typical model based trade methods. </p>
<p><strong>Data mining systems</strong> predict a price trend by evaluating signals with a machine learning algorithm (aka &#8216;artificial intelligence&#8217;). Those signals are usually derived from the order book or the price curve, but sometimes also from fundamental data or exotic data sources.</p>
<p><strong>Indicator soups</strong> are the most often demanded systems. They do not target a particular market inefficiency, except maybe for going with the trend. They generate trade signals from a combination of traditional or new invented &#8216;technical indicators&#8217;. </p>
<p>Some algo trading systems can fall in more than one category. For instance, a <a href="https://financial-hacker.com/build-better-strategies/">grid trader</a> can be considered a risk premium system (high probability of small wins against low probability of high losses), but also a model based system (exploitation of volatility anomalies). If a system cannot be clearly assigned, it is split among several categories in the table below. We can see that clients favor some of the 16 possible combinations, while others are rare:</p>
<table style="background-color: #cadbe6; width: 100%; height: 230px;">
<tbody>
<tr style="height: 55px;">
<td style="height: 55px; width: 23.0159%;"> </td>
<td style="height: 55px; width: 18.254%;">Risk<br />
premium</td>
<td style="height: 55px; width: 14.7619%;">Market<br />
model</td>
<td style="height: 55px; width: 14.9206%;">Data<br />
mining</td>
<td style="height: 55px; width: 18.254%;">Indicator<br />
soup</td>
<td style="height: 55px; width: 10.6349%;">Sum</td>
</tr>
<tr style="height: 35px;">
<td style="height: 35px; width: 23.0159%;">Forex/CFDs</td>
<td style="height: 35px; width: 18.254%; text-align: center;">21</td>
<td style="height: 35px; width: 14.7619%; text-align: center;">74</td>
<td style="height: 35px; width: 14.9206%; text-align: center;">121</td>
<td style="height: 35px; width: 18.254%; text-align: center;">235</td>
<td style="height: 35px; width: 10.6349%; text-align: center;">451</td>
</tr>
<tr style="height: 35px;">
<td style="height: 35px; width: 23.0159%;">Crypto</td>
<td style="height: 35px; width: 18.254%; text-align: center;">0</td>
<td style="height: 35px; width: 14.7619%; text-align: center;">4</td>
<td style="height: 35px; width: 14.9206%; text-align: center;">55</td>
<td style="height: 35px; width: 18.254%; text-align: center;">40</td>
<td style="height: 35px; width: 10.6349%; text-align: center;">99</td>
</tr>
<tr style="height: 35px;">
<td style="height: 35px; width: 23.0159%;">Stocks/ETFs</td>
<td style="height: 35px; width: 18.254%; text-align: center;">68</td>
<td style="height: 35px; width: 14.7619%; text-align: center;">95</td>
<td style="height: 35px; width: 14.9206%; text-align: center;">34</td>
<td style="height: 35px; width: 18.254%; text-align: center;">14</td>
<td style="height: 35px; width: 10.6349%; text-align: center;">211</td>
</tr>
<tr style="height: 35px;">
<td style="height: 35px; width: 23.0159%;">Options</td>
<td style="height: 35px; width: 18.254%; text-align: center;">48</td>
<td style="height: 35px; width: 14.7619%; text-align: center;">159</td>
<td style="height: 35px; width: 14.9206%; text-align: center;">16</td>
<td style="height: 35px; width: 18.254%; text-align: center;">12</td>
<td style="height: 35px; width: 10.6349%; text-align: center;">235</td>
</tr>
<tr style="height: 35px;">
<td style="height: 35px; width: 23.0159%;">Sum</td>
<td style="height: 35px; width: 18.254%; text-align: center;">137</td>
<td style="height: 35px; width: 14.7619%; text-align: center;">332</td>
<td style="height: 35px; width: 14.9206%; text-align: center;">226</td>
<td style="height: 35px; width: 18.254%; text-align: center;">301</td>
<td style="height: 35px; width: 10.6349%; text-align: center;">996</td>
</tr>
</tbody>
</table>
<p>For determining the success or failure rate, we used 8-years backtests for unoptimized systems, and a walk forward analysis for optimized systems. A successful system had to return at least 12% CAGR for stocks, futures, or options, or 30% annual profit for Forex, CFDs, or cryptocurrencies. The R2 parameter had to be above 0.7. If clients ordered a <a href="https://zorro-project.com/backtest.php" target="_blank" rel="noopener">Montecarlo analysis</a>, the system had to pass it at 95% confidence. If one of those conditions was not fulfilled, the system was classified as failure. </p>
<p>The percentages of successful systems:</p>
<table class=" alignleft" style="height: 230px; background-color: #cadbe6; width: 100%;" border="0" width="100%" cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 15.0pt;">
<td class="xl65" style="height: 50px; width: 23.0159%;" width="80" height="40"> </td>
<td class="xl65" style="width: 14.7619%; height: 35px;" width="80">Risk<br />
premium</td>
<td class="xl65" style="width: 15.0794%; height: 35px;" width="80">Market<br />
model</td>
<td class="xl65" style="width: 15.0794%; height: 35px;" width="80">Data<br />
mining</td>
<td class="xl65" style="width: 16.6667%; height: 35px;" width="80">Indicator<br />
soup</td>
<td class="xl66" style="width: 15.2381%; height: 50px;" width="80">Success<br />
rate</td>
</tr>
<tr style="height: 15.0pt;">
<td class="xl65" style="height: 15px; width: 23.0159%;" width="80" height="20">Forex/CFDs</td>
<td class="xl65" style="width: 14.7619%; height: 15px; text-align: center;" align="right" width="80">88 %</td>
<td class="xl65" style="width: 15.0794%; height: 15px; text-align: center;" align="right" width="80">81 %</td>
<td class="xl65" style="width: 15.0794%; height: 15px; text-align: center;" align="right" width="80">69 %</td>
<td class="xl65" style="width: 16.6667%; height: 15px; background-color: #ff0000; text-align: center;" align="right" width="80">31 %</td>
<td class="xl66" style="width: 15.2381%; height: 15px; text-align: center;" align="right" width="80">52 %</td>
</tr>
<tr style="height: 15.0pt;">
<td class="xl65" style="height: 15px; width: 23.0159%;" width="80" height="20">Crypto</td>
<td class="xl65" style="width: 14.7619%; height: 15px; text-align: center;" align="right" width="80">0 %</td>
<td class="xl65" style="width: 15.0794%; height: 15px; text-align: center;" align="right" width="80">75 %</td>
<td class="xl65" style="width: 15.0794%; height: 15px; text-align: center;" align="right" width="80">62 %</td>
<td class="xl65" style="width: 16.6667%; height: 15px; background-color: #ff0000; text-align: center;" align="right" width="80">25 %</td>
<td class="xl66" style="width: 15.2381%; height: 15px; text-align: center;" align="right" width="80">49 %</td>
</tr>
<tr style="height: 15.0pt;">
<td class="xl65" style="height: 15px; width: 23.0159%;" width="80" height="20">Stocks/ETFs</td>
<td class="xl65" style="width: 14.7619%; height: 15px; background-color: #00ff00; text-align: center;" align="right" width="80">92 %</td>
<td class="xl65" style="width: 15.0794%; height: 15px; text-align: center;" align="right" width="80">85 %</td>
<td class="xl65" style="width: 15.0794%; height: 15px; text-align: center;" align="right" width="80">61 %</td>
<td class="xl65" style="width: 16.6667%; height: 15px; text-align: center;" align="right" width="80">35 %</td>
<td class="xl66" style="width: 15.2381%; height: 15px; text-align: center;" align="right" width="80">80 %</td>
</tr>
<tr style="height: 15.0pt;">
<td class="xl65" style="height: 15px; width: 23.0159%;" width="80" height="20">Options</td>
<td class="xl65" style="width: 14.7619%; height: 15px; background-color: #00ff00; text-align: center;" align="right" width="80">96 %</td>
<td class="xl65" style="width: 15.0794%; height: 15px; background-color: #00ff00; text-align: center;" align="right" width="80">91 %</td>
<td class="xl65" style="width: 15.0794%; height: 15px; text-align: center;" align="right" width="80">75 %</td>
<td class="xl65" style="width: 16.6667%; height: 15px; text-align: center;" align="right" width="80">58 %</td>
<td class="xl66" style="width: 15.2381%; height: 15px; text-align: center;" align="right" width="80">89 %</td>
</tr>
<tr style="height: 15.0pt;">
<td class="xl66" style="height: 15px; width: 23.0159%;" width="80" height="20">Success rate</td>
<td class="xl66" style="width: 14.7619%; height: 15px; text-align: center;" align="right" width="80">93 %</td>
<td class="xl66" style="width: 15.0794%; height: 15px; text-align: center;" align="right" width="80">87 %</td>
<td class="xl66" style="width: 15.0794%; height: 15px; text-align: center;" align="right" width="80">67 %</td>
<td class="xl66" style="width: 16.6667%; height: 15px; text-align: center;" align="right" width="80">32 %</td>
<td class="xl66" style="width: 15.2381%; height: 15px; text-align: center;" align="right" width="80">66 %</td>
</tr>
</tbody>
</table>
<p> The average success rates at the end of the columns and rows are weighted by the number of systems. We can see that the overall success rate was only 66%. In 34% of cases we had to break the bad news to the client that it&#8217;s not advised to trade this system live. It produced no, or too little profit in the tests. Sometimes we could see what the problem was, and suggest ways to improve the system. But even total failures were no wasted money. When you know that your favorite manually traded system won&#8217;t work in the long run, you&#8217;ll save a lot more money than spent for programming and testing. </p>
<h3>And the winner is&#8230;</h3>
<p>The statistics are spoiled by the forex and crypto systems, half of which were losers. This is at least better than most such systems from trading books or trader forums, of which 90% fail already in a simple out-of-sample test, or at least in a cluster or Montecarlo analysis. We got a surprising result in the &#8216;Indicator soup&#8217; systems. You would normally expect that they all fail big time, since they are not based on a market model. But in fact almost every third indicator hodgepodge was successful, even in live trading on a test server. Maybe the clients knew more than we did. </p>
<p>It is also a bit surprising that the most complex systems of all, the data mining systems that usually employ deep learning algorithms, did not fare much better. They have an acceptable success rate, but are easily surpassed by a certain sort of much simpler systems. </p>
<p>Of all systems we tested so far, the big winners were the long-term trading systems for <strong>stocks</strong>, <strong>ETFs</strong>, or <strong>options</strong>. Of the option traders, the simpler systems had often better performance. It&#8217;s relatively hard to specify a losing option system, but some still managed it by using short expiration dates, complex entries, fancy rollovers, or intraday buying and selling. One of the very simple, but successful option traders was included in the Zorro scripts.</p>
<p>We found another trend that is not visible in the table: With a few exceptions like HFT or arbitrage, there was an almost linear correlation between <strong>time frames</strong> and performances. Systems on one, five, or ten minute bars were rarely profitable. The good systems traded mostly on 1-hour, 4-hour, or 24-hour time frames. Faster is not always better.</p>
<p>This does not mean that we all should now abandon forex and cryptos and trade only long-term options or ETF portfolios. Diversification is a key to success. All markets still have long periods of ineffectivity and plenty opportunities of trading profits. Maybe the statistics above help to look for them. </p>
<p><strong>Update (2025):</strong> This article was posted 5 years ago, and we have meanwhile more than 2000 systems programmed for clients. Due to technical progress especially with machine learning systems the overall success rate is now slightly better at 71%. But the success relations between the various trading methods are still the same.</p>
<h3>Related articles</h3>
<p><strong>⇒ </strong><a href="https://financial-hacker.com/i-hired-a-contract-coder/">I Hired a Contract Coder</a></p>
<p><strong>⇒ </strong><a href="https://financial-hacker.com/build-better-strategies/">Build Better Strategies!</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/please-send-me-a-trading-system/feed/</wfw:commentRss>
			<slash:comments>32</slash:comments>
		
		
			</item>
		<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>
<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>
<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="auto, (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="auto, (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="auto, (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="auto, (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="auto, (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="auto, (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="auto, (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>Is &#8220;Scalping&#8221; Irrational?</title>
		<link>https://financial-hacker.com/is-scalping-irrational/</link>
					<comments>https://financial-hacker.com/is-scalping-irrational/#comments</comments>
		
		<dc:creator><![CDATA[jcl]]></dc:creator>
		<pubDate>Fri, 09 Oct 2015 16:45:41 +0000</pubDate>
				<category><![CDATA[Indicators]]></category>
		<category><![CDATA[Research]]></category>
		<category><![CDATA[System Evaluation]]></category>
		<category><![CDATA[Entropy]]></category>
		<category><![CDATA[Experiment]]></category>
		<category><![CDATA[HFT]]></category>
		<category><![CDATA[Information]]></category>
		<category><![CDATA[Shannon]]></category>
		<category><![CDATA[Zorro]]></category>
		<guid isPermaLink="false">http://www.financial-hacker.com/?p=483</guid>

					<description><![CDATA[Clients often ask for strategies that trade on very short time frames. Some are possibly inspired by &#8220;I just made $2000 in 5 minutes&#8221; stories on trader forums. Others have heard of High Frequency Trading: the higher the frequency, the better must be the trading! The Zorro developers had been pestered for years until they &#8230; <a href="https://financial-hacker.com/is-scalping-irrational/" class="more-link">Continue reading<span class="screen-reader-text"> "Is &#8220;Scalping&#8221; Irrational?"</span></a>]]></description>
										<content:encoded><![CDATA[<p>Clients often ask for strategies that trade on <strong>very short time frames</strong>. Some are possibly inspired by &#8220;I just made $2000 in 5 minutes&#8221; stories on trader forums. Others have heard of <a href="http://www.financial-hacker.com/hacking-hft-systems/" target="_blank" rel="noopener"><strong>High Frequency Trading</strong></a>: the higher the frequency, the better must be the trading! The <strong><a href="http://www.financial-hacker.com/hackers-tools-zorro-and-r/">Zorro</a></strong> developers had been pestered for years until they finally implemented tick histories and millisecond time frames. <strong>Totally useless features?</strong> Or has short term algo trading indeed some quantifiable advantages? An experiment for looking into that matter produced a <strong>surprising result</strong>.<span id="more-483"></span></p>
<p>It is certainly tempting to earn profits within minutes. Additionally, short time frames produce more bars and trades &#8211; a great advantage for strategy development. The quality of test and training depends on the amount of data, and timely price data is always in short supply. Still, scalping &#8211; opening and closing trades in minutes or seconds &#8211; is largely considered nonsense and irrational by algo traders. Four main reasons are given:</p>
<ol>
<li>Short time frames cause high <strong>trading costs</strong> &#8211; slippage, spread, commission &#8211; in relation to the expected profit.</li>
<li>Short time frames expose more <strong>&#8216;noise&#8217;,</strong> <strong>&#8216;randomness&#8217;</strong> and <strong>&#8216;artifacts&#8217;</strong> in the price curve, which reduces profit and increases risk.</li>
<li>Any algorithms had to be individually adapted to the broker or price data provider due to <strong>price feed dependency</strong> in short time frames.</li>
<li>Algorithmic strategies usually <strong>cease working</strong> below a certain time frame.</li>
</ol>
<p>Higher costs, less profit, more risk, feed dependency, no working strategies &#8211; seemingly good arguments against scalping (HFT is a very different matter). But never trust common wisdom, especially not in trading. That&#8217;s why I had not yet added scalping to my <a href="http://www.financial-hacker.com/seventeen-popular-trade-strategies-that-i-dont-really-understand/">list of irrational trade methods</a>. I can confirm reasons number 3 and 4 from my own experiences: Below bar periods of about 10 minutes, backtests with price histories from different brokers began to produce noticeably different results. And I never managed to develop a strategy with a significantly positive walk-forward test on bar periods less than 30 minutes. But this does not mean that such a strategy does not exist. Maybe short time frames just need special trade methods?</p>
<p>So I&#8217;ve programmed an experiment for finding out once and for all if scalping is really as bad as it&#8217;s rumored to be. Then I can at least give some reasoned advice to the next client who desires a tick-triggered short-term trading strategy.</p>
<h3>Trading costs examined</h3>
<p>The first part of the experiment is easily done: a statistic of the impact of trading costs. Higher costs obviously require more profits for compensation. How many trades must you win for overcoming the trading costs at different time frames? Here&#8217;s a short script (in C, for Zorro) for answering this question:</p>
<pre class="prettyprint">function run()
{
  BarPeriod = 1;
  LookBack = 1440;
  Commission = 0.60;
  Spread = 0.5*PIP;

  int duration = 1, i = 0;
  if(!is(LOOKBACK))
    while(duration &lt;= 1440)
  { 
    var Return = abs(priceClose(0)-priceClose(duration))*PIPCost/PIP;
    var Cost = Commission*LotAmount/10000. + Spread*PIPCost/PIP;
    var Rate = ifelse(Return &gt; Cost, Cost/(2*Return) + 0.5, 1.);

    plotBar("Min Rate",i++,duration,100*Rate,AVG+BARS,RED); 
 
    if(duration &lt; 10) duration += 1;
    else if(duration &lt; 60) duration += 5;
    else if(duration &lt; 180) duration += 30;
    else duration += 60;
  }
  Bar += 100; // hack!
}</pre>
<p>This script calculates the minimum win rate to compensate the trade costs for different trade durations. We assumed here a spread of <strong>0.5 pips</strong> and a round turn commission of <strong>60 cents</strong> per 10,000 contracts &#8211; that&#8217;s average costs of a Forex trade. <strong>PIPCost/PIP</strong> in the above script is the conversion factor from a price difference to a win or loss on the account. We&#8217;re also assuming no win/loss bias: Trades shall win or lose on average the same amount. This allows us to split the <strong>Return</strong> of any trade in a win and a loss, determined by <strong>WinRate</strong>. The win is <strong>WinRate * Return</strong> and the loss is <strong>(1-WinRate) * Return</strong>. For breaking even, the win minus the loss must cover the cost. The required win rate for this is</p>
<p style="padding-left: 30px; text-align: center;"><em><strong>WinRate = Cost/(2*Return) + 0.5</strong></em></p>
<p>The win rate is averaged over all bars and plotted in a histogram of trade durations from 1 minute up to 1 day. The duration is varied in steps of 1, 5, 30, and 60 minutes. We&#8217;re entering a trade for any duration every 101 minutes (<strong>Bar += 100</strong> in the script is a hack for running the simulation in steps of 101 minutes, while still maintaining the 1-minute bar period).</p>
<p>The script needs a few seconds to run, then produces this histogram (for EUR/USD and 2015):</p>
<p><figure id="attachment_524" aria-describedby="caption-attachment-524" style="width: 889px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2015/10/scalp11.png"><img loading="lazy" decoding="async" class="wp-image-524 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2015/10/scalp11.png" alt="" width="889" height="513" srcset="https://financial-hacker.com/wp-content/uploads/2015/10/scalp11.png 889w, https://financial-hacker.com/wp-content/uploads/2015/10/scalp11-300x173.png 300w" sizes="auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></a><figcaption id="caption-attachment-524" class="wp-caption-text">Required win rate in percent vs. trade duration in minutes</figcaption></figure></p>
<p>You need about <strong>53% win rate</strong> for covering the costs of 1-day trades (rightmost bar), but <strong>90% win rate </strong>for 1-minute trades! Or alternatively, a 9:1 reward to risk ratio at 50% win rate. This exceeds the best performances of real trading systems by a large amount, and seems to confirm convincingly the first reason why you better take tales by scalping heroes on trader forums with a grain of salt.</p>
<p>But what about reason number two &#8211; that short time frames are plagued with &#8216;noise&#8217; and &#8216;randomness&#8217;? Or is it maybe the other way around and some effect makes short time frames even more predictable? That&#8217;s a little harder to test.</p>
<h3>Measuring randomness</h3>
<p>&#8216;Noise&#8217; is often identified with the high-frequency components of a signal. Naturally, short time frames produce more high-frequency components than long time frames. They could be detected with a highpass filter, or eliminated with a lowpass filter. Only problem: <strong>Price curve noise</strong> is not always related to high frequencies. Noise is just the part of the curve that does not carry information about the trading signal. For cycle trading, high frequencies are the signal and low-frequency trend is the noise. So the jaggies and ripples of a short time frame price curve might be just the very inefficiencies that you want to exploit. It depends on the strategy what noise is; there is no &#8216;general price noise&#8217;.</p>
<p>Thus we need a better criteria for determining the tradeability of a price curve. That criteria is <strong>randomness</strong>. You can not trade a random market, but you can potentially trade anything that deviates from randomness. Randomness can be measured through the <strong>information content</strong> of the price curve. A good measure of information content is the <strong>Shannon Entropy</strong>. It is defined this way:</p>
<p><a href="http://www.financial-hacker.com/wp-content/uploads/2015/10/shannon.png"><img loading="lazy" decoding="async" class="wp-image-529 size-full aligncenter" src="http://www.financial-hacker.com/wp-content/uploads/2015/10/shannon.png" alt="" width="260" height="47" /></a></p>
<p>This formula basically measures disorder. A very ordered, predictable signal has low entropy. A random, unpredictable signal has high entropy. In the formula, <em><strong>P(s<sub>i</sub>)</strong></em> is the relative frequency of a certain pattern <em><strong>s<sub>i </sub></strong></em>in the signal <em><strong>S</strong></em>. The entropy is at maximum when all patterns are evenly distributed and all <em><strong>P(s<sub>i</sub>)</strong></em> have about the same value. If some patterns appear more frequently than other patterns, the entropy goes down. The signal is then less random and more predictable. The Shannon Entropy is measured in <strong>bit</strong>.</p>
<p>The problem: Zorro has tons of indicators, even the Shannon Gain, but not the Shannon Entropy! So I have no choice but to write a new indicator, which fortunately is my job anyway. This is the source code of the Shannon Entropy of a char string:</p>
<pre class="prettyprint">var ShannonEntropy(char *S,int Length)
{
  static var Hist[256];
  memset(Hist,0,256*sizeof(var));
  var Step = 1./Length;
  int i;
  for(i=0; i&lt;Length; i++) 
    Hist[S[i]] += Step;
  var H = 0;
  for(i=0;i&lt;256;i++) {
    if(Hist[i] &gt; 0.)
      H -= Hist[i]*log2(Hist[i]);
  }
  return H;
}</pre>
<p>A char has 8 bit, so 2<sup>8</sup> = 256 different chars can appear in a string. The frequency of each char is counted and stored in the <strong>Hist</strong> array. So this array contains the <em><strong>P(s<sub>i</sub>)</strong> </em>of the above entropy formula. They are multiplied with their binary logarithm and summed up; the result is <em><strong>H(S)</strong></em>, the Shannon Entropy.</p>
<p>In the above code, a char is a pattern of the signal. So we need to convert our price curve into char patterns. This is done by a second <strong>ShannonEntropy</strong> function that calls the first one:</p>
<pre class="prettyprint">var ShannonEntropy(var *Data,int Length,int PatternSize)
{
  static char S[1024]; // hack!
  int i,j;
  int Size = min(Length-PatternSize-1,1024);
  for(i=0; i&lt;Size; i++) {
    int C = 0;
    for(j=0; j&lt;PatternSize; j++) {
    if(Data[i+j] &gt; Data[i+j+1])
      C += 1&lt;&lt;j;
    }
    S[i] = C;
  }
  return ShannonEntropy(S,Size);
}</pre>
<p><strong>PatternSize</strong> determines the partitioning of the price curve. A pattern is defined by a number of price changes. Each price is either higher than the previous price, or it is not; this is a binary information and constitutes one bit of the pattern. A pattern can consist of up to 8 bits, equivalent to 256 combinations of price changes. The patterns are stored in a char string. Their entropy is then determined by calling the first <strong>ShannonEntropy</strong> function with that string (both functions have the same name, but the compiler can distinguish them from their different parameters). Patterns are generated from any price and the subsequent <strong>PatternSize</strong> prices; then the procedure is repeated with the next price. So the patterns overlap.</p>
<h3>An unexpected result</h3>
<p>Now we only need to produce a histogram of the Shannon Entropy, similar to the win rate in our first script:</p>
<pre class="prettyprint">function run()
{
  BarPeriod = 1;
  LookBack = 1440*300;
  StartWeek = 10000;
 
  int Duration = 1, i = 0;
  while(Duration &lt;= 1440)
  { 
    TimeFrame = frameSync(Duration);
    var *Prices = series(price(),300);

    if(!is(LOOKBACK) &amp;&amp; 0 == (Bar%101)) {
      var H = ShannonEntropy(Prices,300,3);
      plotBar("Randomness",i++,Duration,H,AVG+BARS,BLUE);	
    }
    if(Duration &lt; 10) Duration += 1;
    else if(Duration &lt; 60) Duration += 5;
    else if(Duration &lt; 240) Duration += 30;
    else if(Duration &lt; 720) Duration += 120;
    else Duration += 720;
  }
}</pre>
<p>The entropy is calculated for all time frames at every 101th bar, determined with the modulo function. (Why 101? In such cases I&#8217;m using odd numbers for preventing synchronization effects). I cannot use here the hack with skipping the next 100 bars as in the previous script, as skipping bars would prevent proper shifting of the price series. That&#8217;s why this script must really grind through any minute of 3 years, and needs several minutes to complete.</p>
<p>Two code lines should be explained because they are critical for measuring the entropy of daily candles using less-than-a-day bar periods:</p>
<p><strong>StartWeek = 10000;</strong></p>
<p>This starts the week at Monday midnight (<strong>1</strong> = Monday, <strong>00 00</strong> = midnight) instead of Sunday 11 pm. This line was missing at first and I wondered why the entropy of daily candles was higher than I expected. Reason:  The single Sunday hour at 11 pm counted as a full day and noticeably increased the randomness of daily candles.</p>
<p><strong>TimeFrame = frameSync(Duration);</strong></p>
<p>This synchronizes the time frame to full hours respectively days. If this is missing, the Shannon Entropy of daily candles gets again a too high value since the candles are not in sync with a day anymore. A day has often less than 1440 one-minute bars due to weekends and irregularities in the historical data.</p>
<p>The Shannon Entropy is calculated with a pattern size of 3 price changes, resulting in 8 different patterns. 3 bit is the maximum entropy for 8 patterns. As price changes are not completely random, I expected an entropy value slightly smaller than 3, steadily increasing when time frames are decreasing. However I got this interesting histogram (EUR/USD, 2013-2015, FXCM price data):</p>
<p><figure id="attachment_569" aria-describedby="caption-attachment-569" style="width: 637px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2015/10/scalp2_32.png"><img loading="lazy" decoding="async" class="wp-image-569 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2015/10/scalp2_32.png" alt="" width="637" height="513" srcset="https://financial-hacker.com/wp-content/uploads/2015/10/scalp2_32.png 637w, https://financial-hacker.com/wp-content/uploads/2015/10/scalp2_32-300x242.png 300w" sizes="auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px" /></a><figcaption id="caption-attachment-569" class="wp-caption-text">Entropy vs. time frame (minutes)</figcaption></figure></p>
<p>The entropy is almost, but not quite 3 bit. This confirms that price patterns are not absolutely random. We can see that the 1440 minutes time frame has the lowest Shannon Entropy at about 2.9 bit. This was expected, as the daily cycle has a strong effect on the price curve, and daily candles are thus more regular than candles of other time frames. For this reason price action or price pattern algorithms often use daily candles. The entropy increases with decreasing time frames, but only down to time frames  of about ten minutes. Even lower time frames are actually less random!</p>
<p>This is an unexpected result. The lower the time frame, the less price quotes does it contain, so the impact of chance should be in fact higher. But the opposite is the case. I could produce similar results with other patterns of 4 and 5 bit, and also with other assets. For making sure I continued the experiment with a different, tick-based price history and even shorter time frames of 2, 5, 10, 15, 30, 45, and 60 seconds (Zorro&#8217;s &#8220;useless&#8221; micro time frames now came in handy, after all):</p>
<p><figure id="attachment_601" aria-describedby="caption-attachment-601" style="width: 205px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2015/10/scalp2_41.png"><img loading="lazy" decoding="async" class="wp-image-601 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2015/10/scalp2_41.png" alt="" width="205" height="513" srcset="https://financial-hacker.com/wp-content/uploads/2015/10/scalp2_41.png 205w, https://financial-hacker.com/wp-content/uploads/2015/10/scalp2_41-120x300.png 120w" sizes="auto, (max-width: 205px) 85vw, 205px" /></a><figcaption id="caption-attachment-601" class="wp-caption-text">Entropy vs. time frame (seconds)</figcaption></figure></p>
<p>The x axis is now in second units instead of minutes. We see that price randomness continues to drop with the time frame.</p>
<p>There are several possible explanations. Price granularity is higher at low time frames due to the smaller number of ticks. High-volume trades are often split into many small parts (&#8216;<strong>iceberg trades</strong>&#8216;) and may cause a sequence of similar price quotes in short intervals. All this reduces the price entropy of short time frames. But it does not necessarily increase trade opportunities:  A series of identical quotes has zero entropy and is 100% predictable, but can not be traded. Of course, iceberg trades are still an interesting inefficiency that could theoretically be exploited &#8211; if it weren&#8217;t for the high trading costs. So that&#8217;s something to look further into only when you have direct market access and no broker fees.</p>
<p>I have again uploaded the scripts to the 2015 scripts collection. You&#8217;ll need Zorro 1.36 or above for reproducing the results. Zorro S and tick based data are needed for the second time frames.</p>
<h3>Conclusions</h3>
<ul style="list-style-type: square;">
<li>Scalping is not completely nuts. Very low time frames expose some regularity.</li>
<li>Whatever the reason, this regularity can not be exploited by retail traders due to the high costs of short term trades.</li>
<li>On time frames above 60 minutes prices become less random and more regular. This recommends long time frames for algo trading.</li>
<li>The most regular price patterns appear with 1-day bars. They also cause the least trading costs.</li>
</ul>
<h3>Papers</h3>
<p>Shannon Entropy: <a href="http://www.ueltschi.org/teaching/chapShannon.pdf" target="_blank" rel="noopener noreferrer">Lecture</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/is-scalping-irrational/feed/</wfw:commentRss>
			<slash:comments>24</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>
<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="auto, (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>
<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>
<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="auto, (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>
<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>
		<item>
		<title>The Trend Experiment</title>
		<link>https://financial-hacker.com/trend-and-exploiting-it/</link>
					<comments>https://financial-hacker.com/trend-and-exploiting-it/#comments</comments>
		
		<dc:creator><![CDATA[jcl]]></dc:creator>
		<pubDate>Mon, 07 Sep 2015 06:44:42 +0000</pubDate>
				<category><![CDATA[Research]]></category>
		<category><![CDATA[System Development]]></category>
		<category><![CDATA[System Evaluation]]></category>
		<category><![CDATA[Decycler]]></category>
		<category><![CDATA[Experiment]]></category>
		<category><![CDATA[Low-Lag]]></category>
		<category><![CDATA[Momentum]]></category>
		<guid isPermaLink="false">http://www.financial-hacker.com/?p=53</guid>

					<description><![CDATA[This is the second part of the trend experiment article series, involving 900 systems and 10 different &#8220;smoothing&#8221; or &#8220;low-lag&#8221; indicators for finding out if trend really exists and can be exploited by a simple algorithmic system. When you do such an experiment, you have normally some expectations about the outcome, such as: Trend exists, but is difficult &#8230; <a href="https://financial-hacker.com/trend-and-exploiting-it/" class="more-link">Continue reading<span class="screen-reader-text"> "The Trend Experiment"</span></a>]]></description>
										<content:encoded><![CDATA[<p>This is the second part of the <a href="http://www.financial-hacker.com/trend-delusion-or-reality/">trend experiment</a> article series, involving 900 systems and 10 different &#8220;smoothing&#8221; or &#8220;low-lag&#8221; indicators for finding out if trend really exists and can be exploited by a <strong>simple algorithmic system</strong>. When you do such an experiment, you have normally some expectations about the outcome, such as:<span id="more-53"></span></p>
<ul style="list-style-type: square;">
<li>Trend exists, but is difficult to detect and to exploit. The experiment will find only a few profitable and a lot more unprofitable systems (with realistic trade costs).</li>
<li>More sophisticated indicators, such as the Decycler or the Lowpass filter, will produce better results than the old SMA, EMA, or HMA. Modern indicators have less lag and thus react faster on the begin and end of a trend.</li>
<li>White&#8217;s Reality Check will reveal a data mining bias rather close to the returns of the best systems from the experiment. If algorithmic trend trading were easy, anyone would be doing it.</li>
<li>EUR/USD will return better results because according to trader&#8217;s wisdom, currencies are &#8220;more trending&#8221;.</li>
</ul>
<p>However the results were quite a surprise.</p>
<h3>The Trend Test System</h3>
<p>This is the script <strong>Trend.c</strong> used for the trend experiment:</p>
<pre class="prettyprint">var objective() 
{ 
  return WinTotal/max(1,LossTotal); <span style="color: #0000ff;">// Profit factor</span>
}

var filter(var* Data,int Period);

void run()
{
  set(PARAMETERS|LOGFILE);
  PlotHeight1 = 200;
  PlotScale = 8;
  ColorBars[1] = BLACK; <span style="color: #0000ff;">// winning trades</span>
  ColorBars[2] = GREY; <span style="color: #0000ff;">// losing trades</span>

  StartDate = 2010;
  BarPeriod = 15;
  LookBack = 80*4*24; <span style="color: #0000ff;">// 80 trading days ~ 4 months</span>
	
  while(asset(loop("EUR/USD","SPX500","XAG/USD")))
  while(algo(loop("M15","H1","H4")))
  {
    if(Algo == "M15")
      TimeFrame = 1; // 15 minutes
    else if(Algo == "H1")
      TimeFrame = 4; // 1 hour
    else if(Algo == "H4")
      TimeFrame = 16;	// 4 hours

    int Periods[10] = { 10,20,50,100,200,500,1000,2000,5000,10000 };
    int Period = Periods[optimize(0,0,9,1)];
		
    vars Prices = series(price());
    vars Smoothed = series(filter(Prices,Period));

    if(valley(Smoothed))
      enterLong();
    else if(peak(Smoothed))
      enterShort();
  } 
}</pre>
<p>This script is supposed to run in Zorro&#8217;s &#8220;Train&#8221; mode. We&#8217;re not really training the systems, but Train mode produces parameter histograms, and that&#8217;s what we want. The script is basically the same as described in the <a href="http://www.financial-hacker.com/trend-delusion-or-reality/">previous article</a>, but two asset and algo loops are now used for cycling through three different assets (EUR/USD, S&amp;P 500, and Silver) and three time frames (15 minutes, 1 hour, 4 hours). The <strong>optimize</strong> function selects the time period of the smoothing indicator, in 10 steps from 10 to 10,000 time frames. The meaning of the Zorro-specific functions like <strong>asset</strong>, <strong>algo</strong>, <strong>loop</strong>, <strong>optimize</strong> can be found in the <a href="http://manual.zorro-trader.com" target="_blank" rel="noopener noreferrer">Zorro manual</a>.</p>
<p>6 years of price history from 2010 to 2015 are used for the simulation. The default trade costs &#8211; commission, bid/ask spread, and rollover &#8211; are taken from the average 2015 parameters of a FXCM microlot account. FXCM is not the cheapest broker, so the expectation value of a random-trading simulation is not zero, but a loss, dependent on the number of trades. For comparing results we&#8217;re using the profit factor, calculated in the <strong>objective</strong> function. The script calls a <strong>filter</strong> function that is defined as a prototype, and contained in a main script that includes <strong>Trend.c</strong>. So any of the indicators is represented by a very short main script like this (<strong>TrendLowpass.c</strong>):</p>
<pre class="prettyprint">#include "Strategy\Trend.c"

var filter(var* Data,int Period)
{
  return LowPass(Data,Period);
}</pre>
<p>All the scripts can be downloaded from the <strong>scripts2015</strong> archive at the sidebar. There&#8217;s also a batch file included that starts them all and lets them run in parallel.</p>
<h3>The Results</h3>
<p>When you select one of the scripts and click Zorro&#8217;s  <strong>[Train]</strong> button, it will grind its wheels about 10 minutes and then spit out a sheet of histograms like this (for the ZMA):</p>
<p><a href="http://www.financial-hacker.com/wp-content/uploads/2015/09/ZMA.png"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-159" src="http://www.financial-hacker.com/wp-content/uploads/2015/09/ZMA.png" alt="ZMA" width="1253" height="641" srcset="https://financial-hacker.com/wp-content/uploads/2015/09/ZMA.png 1253w, https://financial-hacker.com/wp-content/uploads/2015/09/ZMA-300x153.png 300w, https://financial-hacker.com/wp-content/uploads/2015/09/ZMA-1024x524.png 1024w, https://financial-hacker.com/wp-content/uploads/2015/09/ZMA-1200x614.png 1200w" sizes="auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></a></p>
<p>We got 9 histograms from the combinations of EUR/USD, S&amp;P 500, and Silver (horizontally) with bar sizes of 15 minutes, 1 hour, and 4 hours. The red bars are the <strong>objective</strong> return &#8211; in this case, the <strong>profit factor</strong>,  total win divided by total loss. If they are above <strong>1.0</strong> (left scale), the system was profitable. The black and grey bars are the number of winning and losing trades (right scale). The steps on the X axis are the indicator time period; they normally don&#8217;t go all the way to 10 because systems are discarded when the time period exceeded 4 months or the system entered less than 30 trades. That&#8217;s why the experiment didn&#8217;t produce 900 systems, but only 705.</p>
<p>We can see that the profit generally increases, and the number of trades decreases with the time frame. As typical for trend trading systems we got far more losing trades (grey) than winning trades (black). 13 of the 82 systems in the histograms above have been profitable, 69 were losers. Most of the profitable systems traded EUR/USD. Only one S&amp;P 500 system, and only three silver trading systems produced any profit. Here&#8217;s a statistic of all winners and losers from the 705 tested systems:</p>
<table>
<tbody>
<tr>
<td>Asset, Period, Indicator</td>
<td style="text-align: right;">Rate</td>
<td style="text-align: right;">Total</td>
<td style="text-align: right;">Won</td>
<td style="text-align: right;">Lost</td>
</tr>
<tr>
<td><strong>EUR/USD</strong></td>
<td style="text-align: right;"><strong>38%</strong></td>
<td style="text-align: right;"><strong>237</strong></td>
<td style="text-align: right;"><strong>89</strong></td>
<td style="text-align: right;"><strong>148</strong></td>
</tr>
<tr>
<td><strong>S&amp;P 500</strong></td>
<td style="text-align: right;"><strong>3%</strong></td>
<td style="text-align: right;"><strong>237</strong></td>
<td style="text-align: right;"><strong>6</strong></td>
<td style="text-align: right;"><strong>231</strong></td>
</tr>
<tr>
<td><strong>Silver</strong></td>
<td style="text-align: right;"><strong>21%</strong></td>
<td style="text-align: right;"><strong>231</strong></td>
<td style="text-align: right;"><strong>48</strong></td>
<td style="text-align: right;"><strong>183</strong></td>
</tr>
<tr>
<td><strong>15 Minutes</strong></td>
<td style="text-align: right;"><strong>14%</strong></td>
<td style="text-align: right;"><strong>297</strong></td>
<td style="text-align: right;"><strong>41</strong></td>
<td style="text-align: right;"><strong>256</strong></td>
</tr>
<tr>
<td><strong>1 Hour</strong></td>
<td style="text-align: right;"><strong>20%</strong></td>
<td style="text-align: right;"><strong>239</strong></td>
<td style="text-align: right;"><strong>48</strong></td>
<td style="text-align: right;"><strong>191</strong></td>
</tr>
<tr>
<td><strong>4 Hours</strong></td>
<td style="text-align: right;"><strong>32%</strong></td>
<td style="text-align: right;"><strong>169</strong></td>
<td style="text-align: right;"><strong>54</strong></td>
<td style="text-align: right;"><strong>115</strong></td>
</tr>
<tr>
<td><strong>ALMA</strong></td>
<td style="text-align: right;"><strong>16%</strong></td>
<td style="text-align: right;"><strong>64</strong></td>
<td style="text-align: right;"><strong>10</strong></td>
<td style="text-align: right;"><strong>54</strong></td>
</tr>
<tr>
<td><strong>Decycle</strong></td>
<td style="text-align: right;"><strong>15%</strong></td>
<td style="text-align: right;"><strong>74</strong></td>
<td style="text-align: right;"><strong>11</strong></td>
<td style="text-align: right;"><strong>63</strong></td>
</tr>
<tr>
<td><strong>EMA</strong></td>
<td style="text-align: right;"><strong>20%</strong></td>
<td style="text-align: right;"><strong>69</strong></td>
<td style="text-align: right;"><strong>14</strong></td>
<td style="text-align: right;"><strong>55</strong></td>
</tr>
<tr>
<td><strong>HMA</strong></td>
<td style="text-align: right;"><strong>25%</strong></td>
<td style="text-align: right;"><strong>63</strong></td>
<td style="text-align: right;"><strong>16</strong></td>
<td style="text-align: right;"><strong>47</strong></td>
</tr>
<tr>
<td><strong>Laguerre</strong></td>
<td style="text-align: right;"><strong>31%</strong></td>
<td style="text-align: right;"><strong>32</strong></td>
<td style="text-align: right;"><strong>10</strong></td>
<td style="text-align: right;"><strong>22</strong></td>
</tr>
<tr>
<td><strong>LinearReg</strong></td>
<td style="text-align: right;"><strong>26%</strong></td>
<td style="text-align: right;"><strong>74</strong></td>
<td style="text-align: right;"><strong>19</strong></td>
<td style="text-align: right;"><strong>53</strong></td>
</tr>
<tr>
<td><strong>Lowpass</strong></td>
<td style="text-align: right;"><strong>20%</strong></td>
<td style="text-align: right;"><strong>71</strong></td>
<td style="text-align: right;"><strong>14</strong></td>
<td style="text-align: right;"><strong>57</strong></td>
</tr>
<tr>
<td><strong>SMA</strong></td>
<td style="text-align: right;"><strong>23%</strong></td>
<td style="text-align: right;"><strong> 70</strong></td>
<td style="text-align: right;"><strong> 16</strong></td>
<td style="text-align: right;"><strong> 54</strong></td>
</tr>
<tr>
<td><strong>Smooth</strong></td>
<td style="text-align: right;"><strong>19%</strong></td>
<td style="text-align: right;"><strong>108</strong></td>
<td style="text-align: right;"><strong>20</strong></td>
<td style="text-align: right;"><strong>88</strong></td>
</tr>
<tr>
<td><strong>ZMA</strong></td>
<td style="text-align: right;"><strong>16%</strong></td>
<td style="text-align: right;"><strong>82</strong></td>
<td style="text-align: right;"><strong>13</strong></td>
<td style="text-align: right;"><strong>69</strong></td>
</tr>
</tbody>
</table>
<p>Since the results are for 2010-2015, you might get different figures when testing different time periods. The <strong>Rate</strong> column shows the percentage of successful systems. We got a mix of expected and unexpected results. Almost 40% of the EUR/USD systems are winners,  which seems to confirm that currencies tend to trend. The S&amp;P 500 index however is not trending at all &#8211;  and this despite the fact that it shows the most significant trend in the long run, as visible in the title image. But here we&#8217;re looking for shorter term trends that can be exploited by day trading. Silver, with 20% winners, has at least some trend. Longer time frames produce dramatically better results, mostly due to less trade costs, but also partly because trend seems to prefer long cycles and low frequencies. (You can see that the difference comes not from trade costs alone when you test the systems with spread, commission, slippage, and rollover set to zero). 1-day bar periods would be even more profitable. But I haven&#8217;t included them in the test because they produce not enough trades for statistical significance.</p>
<p>The indicators show a surprising behavior. The old traditional indicators put up a good fight. Ehler&#8217;s smoothing, decycling, and zero-lag indicators, produce less profitable systems than their classic counterparts. However we&#8217;ll see later that the modern indicators are in fact more profitable, although within a smaller parameter range. But they all occupy the top of the profit list. The best systems, with modern indicators and long time frames, have profit factors up to <strong>2</strong>.</p>
<p>Does this mean that we can put them together in a compound system and will get rich? I don&#8217;t know, because I haven&#8217;t determined yet the Data Mining Bias produced by this experiment. For this we need to store and randomize the balance curves, and apply <strong>White&#8217;s Reality Check</strong>. This will be the topic of the <a href="http://www.financial-hacker.com/whites-reality-check/">next article</a> of the Trend Experiment series.</p>
<h3>Conclusion</h3>
<ul style="list-style-type: square;">
<li>Different markets show very different behavior. EUR/USD and silver are significantly more profitable with short-term trend following than S&amp;P500.</li>
<li>Low-lag smoothing indicators have a smaller profitable parameter range than traditional indicators, but produce higher profits.</li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/trend-and-exploiting-it/feed/</wfw:commentRss>
			<slash:comments>12</slash:comments>
		
		
			</item>
	</channel>
</rss>
