<?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>System Evaluation &#8211; The Financial Hacker</title>
	<atom:link href="https://financial-hacker.com/category/system-evaluation/feed/" rel="self" type="application/rss+xml" />
	<link>https://financial-hacker.com</link>
	<description>A new view on algorithmic trading</description>
	<lastBuildDate>Mon, 16 Feb 2026 16:53:44 +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>System Evaluation &#8211; The Financial Hacker</title>
	<link>https://financial-hacker.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Build Better Strategies, Part 6: Evaluation</title>
		<link>https://financial-hacker.com/build-better-strategies-part-6-evaluation/</link>
					<comments>https://financial-hacker.com/build-better-strategies-part-6-evaluation/#comments</comments>
		
		<dc:creator><![CDATA[jcl]]></dc:creator>
		<pubDate>Thu, 05 Feb 2026 16:54:41 +0000</pubDate>
				<category><![CDATA[No Math]]></category>
		<category><![CDATA[System Development]]></category>
		<category><![CDATA[System Evaluation]]></category>
		<category><![CDATA[Experiment]]></category>
		<category><![CDATA[Indicator]]></category>
		<category><![CDATA[Walk forward analysis]]></category>
		<category><![CDATA[White's reality check]]></category>
		<category><![CDATA[Zorro]]></category>
		<guid isPermaLink="false">https://financial-hacker.com/?p=4901</guid>

					<description><![CDATA[Developing a successful strategy is a process with many steps, described in the Build Better Strategies article series. At some point you have coded a first, raw version of the strategy. At that stage you&#8217;re usually experimenting with different functions for market detection or trade signals. The problem: How can you determine which indicator, filter, &#8230; <a href="https://financial-hacker.com/build-better-strategies-part-6-evaluation/" class="more-link">Continue reading<span class="screen-reader-text"> "Build Better Strategies, Part 6: Evaluation"</span></a>]]></description>
										<content:encoded><![CDATA[<p>Developing a successful strategy is a process with many steps, described in the <a href="https://financial-hacker.com/build-better-strategies/">Build Better Strategies</a> article series. At some point you have coded a first, raw version of the strategy. At that stage you&#8217;re usually experimenting with different functions for market detection or trade signals. The problem: How can you determine which indicator, filter, or machine learning method  works best with which markets and which time frames? Manually testing all combinations is very time consuming, close to impossible. Here&#8217;s a way to run that process automated with a single mouse click.<span id="more-4901"></span></p>
<p>A robust trading strategy has to meet several criteria:</p>
<ul>
<li>It must exploit a real and significant market inefficiency. Random-walk markets cannot be algo traded.</li>
<li>It must work in all market situations. A trend follower must survive a mean reverting regime.</li>
<li>It must work under many different optimization settings and parameter ranges.</li>
<li>It must be unaffected by random events and price fluctuations.</li>
</ul>
<p>There are metrics and algorithms to test all this. The robustness under different market situations can be determined through the <strong>R2 coefficient</strong> or the deviations between the <strong>walk forward cycles</strong>. The parameter range robustness can be tested with a WFO profile (aka <strong>cluster analysis</strong>), the price fluctuation robustness with <strong><a href="https://financial-hacker.com/better-tests-with-oversampling/">oversampling</a></strong>. A <strong>Montecarlo analysis</strong> finds out whether the strategy is based on a real market inefficiency.</p>
<p>Some platforms, such as <strong>Zorro</strong>, have functions for all this. But they require dedicated code in the strategy, often more than for the algorithm itself. In this article I&#8217;m going to describe an <strong>evaluation framework</strong> &#8211; a &#8216;shell&#8217; &#8211; that skips the coding part. The evaluation shell is included in the latest Zorro version. It can be simply attached to any strategy script. It makes all strategy variables accessible in a panel and adds stuff that&#8217;s common to all strategies &#8211; optimization, money management, support for multiple assets and algos, cluster and montecarlo analysis. It evaluates all strategy variants in an automated process and builds the optimal portfolio of combinations from different algorithms, assets, and timeframes. </p>
<p>The process involves these steps: </p>
<p><img decoding="async" src="https://financial-hacker.com/wp-content/uploads/2026/02/eval.png" alt="" /></p>
<p>The first step of strategy evaluation is generating sets of parameter settings, named <strong><span class="tast">jobs</span></strong>. Any job is a variant of the strategy that you want to test and possibly include in the final portfolio. Parameters can be switches that select between different indicators, or variables (such as timeframes) with optimization ranges. All parameters can be edited in the user interface of the shell, then saved with a mouse click as a job. </p>
<p>The next step is an automated process that runs through all previously stored jobs, trains and tests any of them with different asset, algo, and time frame combinations, and stores their results in a <strong><span class="tast">summary</span></strong>. The summary is a CSV list with the performance metrics of all jobs. It is automatically sorted &#8211; the best performing job variants are at the top &#8211; and looks like this:</p>
<p><img decoding="async" src="https://financial-hacker.com/wp-content/uploads/2026/02/shellsummary.png" alt="" /></p>
<p>So you can see at a glance which parameter combinations work with which assets and time frames, and which are not worth to examine further. You can repeat this step with different global settings, such as bar period or optimization method, and generate multiple summaries in this way. </p>
<p>The next step in the process is <strong><span class="tast">cluster analysis</span></strong>. Every job in a selected summary is optimized multiple times with different walk-forward settings. The result with any job variant is stored in WFO profiles or heatmaps:</p>
<p><img decoding="async" src="https://financial-hacker.com/wp-content/uploads/2026/02/RangerMatrix.png" alt="" /></p>
<p>After this process, you likely ended up with a couple survivors in the top of the summary. The surviving jobs have all a positive return, a steady rising equity curve, shallow drawdowns, and robust parameter ranges since they passed the cluster analysis. But any selection process generates <strong><span class="tast">selection bias</span></strong>. Your perfect portfolio will likely produce a great backtest, but will it perform equally well in live trading? To find out, you run a <span class="tast"><strong>Montecarlo analysis</strong>, aka &#8216;Reality Check&#8217;</span>.</p>
<p><img decoding="async" src="https://financial-hacker.com/wp-content/uploads/2026/02/RealityCheck_s1.png" alt="" /></p>
<p>This is the most important test of all, since it can determine whether your strategy exploits a real market inefficiency. If the Montecarlo analysis fails with the final portfolio, it will likely also fail with any other parameter combination, so you need to run it only close to the end. If your system passes Montecarlo with a <strong><span class="tast">p-value</span></strong> below 5%, you can be relatively confident that the system will return good and steady profit in live trading. Otherwise, back to the drawing board.</p>
<h3>The use case</h3>
<p>For a real life use case,  we generated algorithms for the Z12 system that comes with Zorro. Z12 is a portfolio from several trend and counter trend algorithms that all trade simultaneously. The trading signals are generated with spectral analysis filters. The system trades a subset of Forex pairs and index CFDs on a 4-hour timeframe. The timeframe was choosen for best performance, as were the traded Forex pairs and CFDs. </p>
<p>We used the evaluation shell to create new algos, not from a selected subset, but from all major Forex pairs and major index CFDs, with 3 different time frames from 60, 120, and 240 minutes. 29 algorithms passed the cluster and montecarlo analysis, of which the least correlated were put into the final portfolio. This is the equity curve of the new Z12 system:</p>
<p><img decoding="async" src="https://financial-hacker.com/wp-content/uploads/2026/02/z12perf.png" alt="" /></p>
<p>Other performance parameters, such as Profit Factor, Sharpe ratio, Calmar Ratio, and R2 also improved by more than 30%. The annual return almost doubled, compared with the average of the previous years. Nothing in the basic Z12 algorithms has changed. Only new combinations of algos, assets, and timeframes are now traded.</p>
<p>The evaluation shell is included in Zorro version 3.01 or above. Usage and details are described under <a href="https://zorro-project.com/manual/en/shell.htm" target="_blank" rel="noopener">https://zorro-project.com/manual/en/shell.htm</a>.  Attaching the shell to a strategy is described under <a href="https://zorro-project.com/manual/en/shell2.htm" target="_blank" rel="noopener">https://zorro-project.com/manual/en/shell2.htm</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/build-better-strategies-part-6-evaluation/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Why 90% of Backtests Fail</title>
		<link>https://financial-hacker.com/why-90-of-backtests-fail/</link>
					<comments>https://financial-hacker.com/why-90-of-backtests-fail/#comments</comments>
		
		<dc:creator><![CDATA[jcl]]></dc:creator>
		<pubDate>Mon, 04 Apr 2022 15:55:47 +0000</pubDate>
				<category><![CDATA[System Development]]></category>
		<category><![CDATA[System Evaluation]]></category>
		<category><![CDATA[Backtest]]></category>
		<category><![CDATA[Montecarlo Methods]]></category>
		<category><![CDATA[SPY]]></category>
		<category><![CDATA[Walk forward analysis]]></category>
		<category><![CDATA[White's reality check]]></category>
		<guid isPermaLink="false">https://financial-hacker.com/?p=4373</guid>

					<description><![CDATA[About 9 out of 10 backtests produce wrong or misleading results. This is the number one reason why carefully developed algorithmic trading systems often fail in live trading. Even with out-of-sample data and even with cross-validation or walk-forward analysis, backtest results are often way off to the optimistic side. The majority of trading systems with &#8230; <a href="https://financial-hacker.com/why-90-of-backtests-fail/" class="more-link">Continue reading<span class="screen-reader-text"> "Why 90% of Backtests Fail"</span></a>]]></description>
										<content:encoded><![CDATA[<p>About 9 out of 10 backtests produce wrong or misleading results. This is the number one reason why carefully developed algorithmic trading systems often fail in live trading. Even with out-of-sample data and even with cross-validation or walk-forward analysis, backtest results are often way off to the optimistic side. The majority of trading systems with a positive backtest are in fact unprofitable. In this article I&#8217;ll discuss the cause of this phenomenon, and how to fix it.<span id="more-4373"></span></p>
<p>Suppose you&#8217;re developing an <a href="https://zorro-project.com/algotrading.php" target="_blank" rel="noopener">algorithmic trading strategy</a>, following all rules of proper <a href="https://financial-hacker.com/build-better-strategies-part-3-the-development-process/">system development</a>. But you are not aware that your trading algorithm has no statistical edge. The strategy is worthless, the trading rules equivalent to random trading, the profit expectancy – aside from transaction costs – is zero. The problem: you will rarely get a zero result in a backtest. A random trading strategy will in 50% of cases produce a negative backtest result, in 50% a positive result. But if the result is negative, you&#8217;re normally tempted to tweak the code or select assets and time frames until you finally got a profitable backtest. Which will happen relatively soon even when applying random modifications to the system. That&#8217;s why there are so many unprofitable strategies around, with nevertheless great backtest performances.</p>
<p>Does this mean that backtests are worthless? Not at all. But it is essential to know whether you can trust the test, or not.</p>
<h3><strong>The test-the-backtest experiment<br />
</strong></h3>
<p>There are several methods for verifying a backtest. None of them is perfect, but all give insights from different viewpoints. We&#8217;ll use the <a href="https://zorro-project.com" target="_blank" rel="noopener">Zorro algo trading software</a>, and run our experiments with the following test system that is optimized and backtested with walk-forward analysis:</p>
<pre class="prettyprint">function run()
{
  set(PARAMETERS,TESTNOW,PLOTNOW,LOGFILE);
  BarPeriod = 1440;
  LookBack = 100;
  StartDate = 2012;
  NumWFOCycles = 10;

  assetList("AssetsIB");
  asset("SPY");

  vars Signals = series(LowPass(seriesC(),optimize(10,2,20,2)));
  vars MMIFast = series(MMI(seriesC(),optimize(50,40,60,5)));
  vars MMISlow = series(LowPass(MMIFast,100));

  MaxLong = 1;
  if(falling(MMISlow)) {
    if(valley(Signals))
      enterLong();
    else if(peak(Signals))
      exitLong();
  }
}</pre>
<p>This is a classic trend following algorithm. It uses a lowpass filter for trading at the peaks and valleys of the smoothed price curve, and a MMI filter (<a href="https://financial-hacker.com/the-market-meanness-index/">Market Meanness Index</a>) for distinguishing trending from non-trending market periods. It only trades when the market has switched to rend regime, which is essential for profitable trend following systems. It opens only long positions. Lowpass and MMI filter periods are <a href="https://zorro-project.com/manual/en/optimize.htm" target="_blank" rel="noopener">optimized</a>, and the backtest is a <a href="https://zorro-project.com/manual/en/numwfocycles.htm" target="_blank" rel="noopener">walk-forward analysis</a> with 10 cycles.</p>
<h3><strong>The placebo trading system<br />
</strong></h3>
<p>It is standard for experiments to compare the real stuff with a placebo. For this we&#8217;re using a trading system that has obviously no edge, but was tweaked with the evil intention to appear profitable in a walk-forward analysis. This is our placebo system:</p>
<pre class="prettyprint">void run()
{
  set(PARAMETERS,TESTNOW,PLOTNOW,LOGFILE);
  BarPeriod = 1440;
  StartDate = 2012;
  setf(TrainMode,BRUTE);
  NumWFOCycles = 9;

  assetList("AssetsIB");
  asset("SPY");

  int Pause = optimize(5,1,15,1);
  LifeTime = optimize(5,1,15,1);

// trade after a pause...
  static int NextEntry;
  if(Init) NextEntry = 0;
  if(NextEntry-- &lt;= 0) {
    NextEntry = LifeTime+Pause;
    enterLong();
  }
}</pre>
<p>This system opens a position, keeps it a while, then closes it and pauses for a while. The trade and pause durations are walk-forward optimized between 1 day and 3 weeks. <a href="https://zorro-project.com/manual/en/timewait.ht" target="_blank" rel="noopener">LifeTime</a> is a predefined variable that closes the position after the given time. If you don&#8217;t believe in lucky trade patterns, you can rightfully assume that this system is equivalent to random trading. Let&#8217;s see how it fares in comparison to the trend trading system.</p>
<h3><strong>Trend trading vs. placebo trading<br />
</strong></h3>
<p>This is the equity curve with the trend trading system from a walk forward analysis from 2012 up to 3/2022:</p>
<p><img decoding="async" src="https://financial-hacker.com/wp-content/uploads/2022/04/040422_1441_Why90ofBack1.png" alt="" /></p>
<p>The plot begins 2015 because the preceding 3 years are used for the training and lookback periods. SPY follows the S&amp;P500 index and rises in the long term, so we could expect anyway some profit with a long-only system. But this system, with profit factor 3 and R2 coefficient 0.65 appears a lot better than random trading. Let&#8217;s compare it with the placebo system:</p>
<p><img decoding="async" src="https://financial-hacker.com/wp-content/uploads/2022/04/040422_1441_Why90ofBack2.png" alt="" /></p>
<p>The placebo system produced profit factor 2 and R2 coefficient 0.77. Slightly less than the real system, but in the same performance range. And this result was also from a walk-forward analysis, although with 9 cycles &#8211; therefore the later start of the test period. Aside from that, it seems impossible to determine solely from the equity curve and performance data which system is for real, and which is a placebo.</p>
<h3><strong>Checking the reality<br />
</strong></h3>
<p>Methods to verify backtest results are named &#8216;reality check&#8217;. They are specific to the asset and algorithm; in a multi-asset, multi-algo portfolio, you need to enable only the component you want to test. Let&#8217;s first see how the WFO split affects the backtest. In this way we can find out whether our backtest result was just due to lucky trading in a particular WFO cycle. We&#8217;re going to plot a <strong>WFO profile</strong> that displays the effect of the number of walk-forward cycles on the result. For this we outcomment the <strong>NumWFOCycles = …</strong> line in the code, and run it in training mode with the <strong>WFOProfile.c</strong> script:</p>
<pre class="prettyprint">#define run strategy
#include "trend.c" // &lt;= your script
#undef run
#define CYCLES 20 // max WFO cycles

function run()
{
  set(TESTNOW);
  NumTotalCycles = CYCLES-1;
  NumWFOCycles = TotalCycle+1;
  strategy();
}

function evaluate()
{
  var Perf = ifelse(LossTotal &gt; 0,WinTotal/LossTotal,10);
  if(Perf &gt; 1)
    plotBar("WFO+",NumWFOCycles,NumWFOCycles,Perf,BARS,BLACK);
  else
    plotBar("WFO-",NumWFOCycles,NumWFOCycles,Perf,BARS,RED);
}</pre>
<p>We&#8217;re redefining the <strong>run</strong> function to a different name. This allows us to just include the tested script and train it with WFO cycles from 2 up to the number defined by CYCLES. A backtest is executed after training. If an <strong>evaluate</strong> function is present, Zorro runs it automatically after any backtest. It plots a histogram bar of the profit factor (y axis) from each number of WFO cycles. First, the WFO profile of the trend trading system:</p>
<p><img decoding="async" src="https://financial-hacker.com/wp-content/uploads/2022/04/040422_1441_Why90ofBack3.png" alt="" /></p>
<p>We can see that the performance rises with the number of cycles. This is typical for a system that adapts to the market. All results are positive with a profit factor &gt; 1. Our arbitrary choice of 10 cycles produced a less than average result. So we can at least be sure that this backtest result was not caused by a particularly lucky number of WFO cycles.</p>
<p>The WFO profile of the placebo system:</p>
<p><img decoding="async" src="https://financial-hacker.com/wp-content/uploads/2022/04/040422_1441_Why90ofBack4.png" alt="" /></p>
<p>This time the number of WFO cycles had a strong random effect on the performance. And it is now obvious why I used 9 WFO cycles for that system. For the same reason I used brute force optimization, since it increases WFO variance and thus the chance to get lucky WFO cycle numbers. That&#8217;s the opposite of what we normally do when developing algorithmic trading strategies.</p>
<p>WFO profiles give insight into WFO cycle dependency, but not into randomness or overfitting by other means. For this, more in-depth tests are required. Zorro supports two methods, the Montecarlo Reality Check (MRC) with randomized price curves, and <a href="https://financial-hacker.com/whites-reality-check/">White&#8217;s Reality Check</a> (WRC) with detrended and bootstrapped equity curves of strategy variants. Both methods have their advantages and disadvantages. But since strategy variants from optimizing can only be created without walk-forward analysis, we&#8217;re using the MRC here.</p>
<h3><strong>The Montecarlo Reality Check<br />
</strong></h3>
<p>First we test both systems with random price curves. Randomizing removes short-term price correlations and market inefficiencies, but keeps the long-term trend. Then we compare our original backtest result with the randomized results. This yields a <strong>p-value</strong>, a metric of the probability that our test result was caused by randomness. The lower the p-Value, the more confidence we can have in the backtest result. In statistics we normally consider a result significant when its p-Value is below 5%.</p>
<p>The basic algorithm of the Montecarlo Reality Check (MRC):</p>
<ol>
<li>Train your system and run a backtest. Store the profit factor (or any other <a href="https://zorro-project.com/manual/en/performance.htm" target="_blank" rel="noopener">performance metric</a> that you want to compare).</li>
<li><a href="https://zorro-project.com/manual/en/detrend.htm" target="_blank" rel="noopener">Randomize</a> the price curve by randomly swapping price changes (shuffle without replacement).</li>
<li>Train your system again with the randomized data and run a backtest. Store the performance metric.</li>
<li>Repeat steps 2 and 3 1000 times.</li>
<li>Determine the number N of randomized tests that have a better result than the original test. The p-Value is N/1000.</li>
</ol>
<p>If our backtest result was affected by an overall upwards trending price curve, which is certainly the case for this SPY system, the randomized tests will be likewise affected. The MRC code:</p>
<pre class="prettyprint">#define run strategy
#include "trend.c" // &lt;= your script
#undef run
#define CYCLES 1000

function run()
{
  set(PRELOAD,TESTNOW);
  NumTotalCycles = CYCLES;
  if(TotalCycle == 1) // first cycle = original
    seed(12345); // always same random sequence
  else
    Detrend = SHUFFLE;
  strategy();
  set(LOGFILE|OFF); // don't export files
}

function evaluate()
{
  static var OriginalProfit, Probability;
  var PF = ifelse(LossTotal &gt; 0,WinTotal/LossTotal,10);
  if(TotalCycle == 1) {
    OriginalProfit = PF;
    Probability = 0;
  } else {
    if(PF &lt; 2*OriginalProfit) // clip image at double range
      plotHistogram("Random",PF,OriginalProfit/50,1,RED);
    if(PF &gt; OriginalProfit)
      Probability += 100./NumTotalCycles;
  }
  if(TotalCycle == NumTotalCycles) { // last cycle
    plotHistogram("Original",
     OriginalProfit,OriginalProfit/50,sqrt(NumTotalCycles),BLACK);
    printf("\n-------------------------------------------");
    printf("\nP-Value %.1f%%",Probability);
    printf("\nResult is ");
    if(Probability &lt;= 1)
      printf("highly significant") ;
    else if(Probability &lt;= 5)
      printf("significant");
    else if(Probability &lt;= 15)
      printf("maybe significant");
    else
      printf("statistically insignificant");
    printf("\n-------------------------------------------");
  }
}</pre>
<p>This code sets up the Zorro platform to train and test the system 1000 times. The <a href="https://zorro-project.com/manual/en/random.htm" target="_blank" rel="noopener"><strong>seed</strong></a> setting ensures that you get the same result on any MRC run. From the second cycle on, the historical data is shuffled without replacement. For calculating the p-value and plotting a histogram of the MRC, we use the <strong>evaluate</strong> function again. It calculates the p-value by counting the backtests resulting in higher profit factors than the original system. Depending on the system, training and testing the strategy a thousand times will take several minutes with Zorro. The resulting MRC histogram of the trend following system:</p>
<p><img decoding="async" src="https://financial-hacker.com/wp-content/uploads/2022/04/040422_1441_Why90ofBack5.png" alt="" /></p>
<p>The height of a red bar represents the number of shuffled backtests that ended at the profit factor shown on the x axis. The black bar on the right (height is irrelevant, only the x axis position matters) is the profit factor with the original price curve. We can see that most shuffled tests came out positive, due to the long-term upwards trend of the SPY price. But our test system came out even more positive. The p-Value is below 1%, meaning a high significance of our backtest. This gives us some confidence that the simple trend follower can achieve a similar result in real trading.</p>
<p>This cannot be said from the MRC histogram of the placebo system:</p>
<p><img decoding="async" src="https://financial-hacker.com/wp-content/uploads/2022/04/040422_1441_Why90ofBack6.png" alt="" /></p>
<p>The backtest profit factors now extend over a wider range, and many were more profitable than the original system. The backtest with the real price curve is indistinguishable from the randomized tests, with a p-value in the 40% area. The original backtest result of the placebo system, even though achieved with walk-forward analysis, is therefore meaningless.</p>
<p>It should be mentioned that the MRC cannot detect all invalid backtests. A system that was explicitly fitted to a particular price curve, for instance by knowing in advance its peaks and valleys, would get a low p-value by the MRC. No reality check could distinguish such a system from a system with a real edge. Therefore, neither MRC nor WRC can give absolute guarantee that a system works when it passes the check. But when it does not pass, you&#8217;re advised to better not trade it with real money.</p>
<p>I have uploaded the strategies to the 2022 script repository. The MRC and WFOProfile scripts are included in Zorro version 2.47.4 and above. You will need Zorro S for the brute force optimization of the placebo system.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/why-90-of-backtests-fail/feed/</wfw:commentRss>
			<slash:comments>22</slash:comments>
		
		
			</item>
		<item>
		<title>More Robust Strategies</title>
		<link>https://financial-hacker.com/more-robust-strategies/</link>
					<comments>https://financial-hacker.com/more-robust-strategies/#comments</comments>
		
		<dc:creator><![CDATA[Petra Volkova]]></dc:creator>
		<pubDate>Tue, 18 May 2021 16:06:35 +0000</pubDate>
				<category><![CDATA[Indicators]]></category>
		<category><![CDATA[Petra on Programming]]></category>
		<category><![CDATA[System Development]]></category>
		<category><![CDATA[System Evaluation]]></category>
		<guid isPermaLink="false">https://financial-hacker.com/?p=4085</guid>

					<description><![CDATA[The previous article dealt with John Ehlers&#8217; AM and FM demodulating technology for separating signal and noise in price curves. In the S&#38;C June issue he described a practical example. Applying his FM demodulator makes a strategy noticeably more robust &#8211; at least with parameter optimization. The simple example strategy is basically a short-term trend &#8230; <a href="https://financial-hacker.com/more-robust-strategies/" class="more-link">Continue reading<span class="screen-reader-text"> "More Robust Strategies"</span></a>]]></description>
										<content:encoded><![CDATA[<p><em>The <a href="https://financial-hacker.com/petra-on-programming-the-price-wave-radio/">previous article</a> dealt with John Ehlers&#8217; AM and FM demodulating technology for separating signal and noise in price curves. In the S&amp;C June issue he described a practical example. Applying his FM demodulator makes a strategy noticeably more robust &#8211; at least with parameter optimization.</em></p>
<p><span id="more-4085"></span>The simple example strategy is basically a short-term trend follower. The price curve is differentiated, then integrated again for removing the zero frequency part. The result is smoothed, and the system trades on zero crossings of its rate of change. Two parameters, the smoothing period and the rate-of-change period, are optimized. The code in C for Zorro:</p>
<pre class="prettyprint">void simple()
{
  int SigPeriod = optimize(8,6,14,1),
    ROCPeriod = optimize(1,1,6,1);
//Derivative of the price wave
  vars Deriv = series(priceClose(0)-priceClose(2));
//zeros at Nyquist and 2*Nyquist, i.e. Z3 = (1 + Z^-1)*(1 + Z^-2)
  vars Z3 = series(Sum(Deriv,4));
//Smooth Z3 for trading signal
  vars Signal = series(SMA(Z3,SigPeriod));
//Use Rate of Change to identify entry point
  vars Roc = series(Signal[0]-Signal[ROCPeriod]);
//If ROC Crosses Over 0 Then Buy Next Bar on Open;
//If Signal Crosses Under 0 Then Sell Next Bar on Open;
  if(crossOver(Roc,0))
    enterLong();
  else if(crossUnder(Signal,0))
    exitLong();
}</pre>
<p>For replicating Ehlers&#8217; results we use brute force in-sample optimization. This results in an impressive profit factor of almost 3 when applied to the S&amp;P500 index (SPY) from 2009 to 2021. Still, the equity curve below shows that the main profit results from the last 2 years. So this is, as Ehlers admitted, not a very robust system.</p>
<p><img decoding="async" src="https://financial-hacker.com/wp-content/uploads/2021/05/051821_1548_MoreRobustS1.png" alt="" /></p>
<p><span style="color: #44546a;font-size: 9pt"><em>Figure 1 &#8211; Simple system with no FM demodulator<br />
</em></span></p>
<p>Now we&#8217;re going to improve the system by inserting Ehler&#8217;s FM demodulator from the previous article. The code in C for Zorro:</p>
<pre class="prettyprint">void simpleFM()
{
  int i, SigPeriod = optimize(8,6,14,1),
  ROCPeriod = optimize(1,1,6,1);
//Derivative of the price wave
  vars Deriv = series(priceClose(0)-priceClose(2));
//FM filter: normalize Derive to half RMS and hard limit at +/- 1
  var RMS = 0;
  for(i=0; i&lt;50; i++) RMS += Deriv[i]*Deriv[i];
  vars Clips = series(clamp(2*Deriv[0]/sqrt(RMS/50),-1,1));
//zeros at Nyquist and 2*Nyquist, i.e. Z3 = (1 + Z^-1)*(1 + Z^-2)
  vars Z3 = series(Sum(Clips,4));
//Smooth Z3 for trading signal
  vars Signal = series(SMA(Z3,SigPeriod));
//Use Rate of Change to identify entry point
  vars Roc = series(Signal[0]-Signal[ROCPeriod]);
//If ROC Crosses Over 0 Then Buy Next Bar on Open;
//If Signal Crosses Under 0 Then Sell Next Bar on Open;
  if(crossOver(Roc,0))
    enterLong();
  else if(crossUnder(Signal,0))
    exitLong();
}</pre>
<p>The FM demodulator is basically the <strong>clamp()</strong> function, which removes the amplitude from the price curve derivative, in combination with the subsequent SMA, which acts as a lowpass filter. The result after brute force optimization:</p>
<p><img decoding="async" src="https://financial-hacker.com/wp-content/uploads/2021/05/051821_1548_MoreRobustS2.png" alt="" /></p>
<p><span style="color: #44546a;font-size: 9pt"><em>Figure 2 &#8211; Simple system with FM demodulator<br />
</em></span></p>
<p>The profit factor is still about the same, slightly below 3, but the equity curve now looks a bit better and the total profit is higher. Ehlers attributed the increased robustness to the more linear parameter surface. For verifying that, we compare the 2D parameter charts from both versions:</p>
<p><img decoding="async" src="https://financial-hacker.com/wp-content/uploads/2021/05/051821_1548_MoreRobustS3.png" alt="" /></p>
<p><span style="color: #44546a;font-size: 9pt"><em>Figure 3 &#8211; Parameter heatmap, no FM demodulator<br />
</em></span></p>
<p><img decoding="async" src="https://financial-hacker.com/wp-content/uploads/2021/05/051821_1548_MoreRobustS4.png" alt="" /></p>
<p><span style="color: #44546a;font-size: 9pt"><em>Figure 4 &#8211; Parameter heatmap, with FM demodulator<br />
</em></span></p>
<p>The charts display the performance dependent on the parameter values on the y and x asis. We can see that the FM demodulator version has indeed the smoother heatmap with a top (green) in the center of the parameter ranges. The heatmap of the other system looks crumpled with a deep valley in the center and several tops apart from each other. Clearly the optimization of the FM demodulator system was more effective.</p>
<p>Further reading: <em>John Ehlers, More Robust Strategies, Stocks &amp; Commodities June 2021.</em></p>
<p>The script can be downloaded from the 2021 script repository. You will need Zorro S 2.37 or better for the brute force optimization and the 2D parameter charts.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/more-robust-strategies/feed/</wfw:commentRss>
			<slash:comments>10</slash:comments>
		
		
			</item>
		<item>
		<title>Detecting Volume Breakouts</title>
		<link>https://financial-hacker.com/petra-on-programming-detecting-volume-breakouts/</link>
					<comments>https://financial-hacker.com/petra-on-programming-detecting-volume-breakouts/#comments</comments>
		
		<dc:creator><![CDATA[Petra Volkova]]></dc:creator>
		<pubDate>Mon, 08 Mar 2021 17:05:11 +0000</pubDate>
				<category><![CDATA[Indicators]]></category>
		<category><![CDATA[Petra on Programming]]></category>
		<category><![CDATA[System Evaluation]]></category>
		<guid isPermaLink="false">https://financial-hacker.com/?p=4012</guid>

					<description><![CDATA[It is estimated that about 6000 different technical indicators have been meanwhile published, but few of them are based on volume. In his article in Stocks &#38; Commodities April 2021, Markos Katsanos proposed a new indicator for detecting high-volume breakouts. And he tested it with a trading system that I believe is the most complex &#8230; <a href="https://financial-hacker.com/petra-on-programming-detecting-volume-breakouts/" class="more-link">Continue reading<span class="screen-reader-text"> "Detecting Volume Breakouts"</span></a>]]></description>
										<content:encoded><![CDATA[<p><em>It is estimated that about 6000 different technical indicators have been meanwhile published, but few of them are based on volume. In his article in Stocks &amp; Commodities April 2021, Markos Katsanos proposed a new indicator for detecting high-volume breakouts. And he tested it with a trading system that I believe is the most complex one ever posted on this blog.</em></p>
<p><span id="more-4012"></span></p>
<p>The VPN indicator calculates the difference of up day volume and down day volume, divided by the total volume. A day is an up day when its typical price &#8211; the average of high, low, and close &#8211; is higher than yesterday&#8217;s typical price plus 1/10 ATR. The opposite is true for down days. The result is multiplied by 100 and smoothed with a 3-day EMA. The C code of this indicator:</p>
<pre class="prettyprint">var VPN(vars Volumes,int Period)
{
  var Dist = 0.1*ATR(Period);
  vars TypPrices = series(TypPrice());
  var Vp=0,Vn=0,Vtotal=0; int i;
  for(i=0; i&lt;Period; i++) {
    if(typprices[i] &gt; TypPrices[i+1] + Dist)
      Vp += Volumes[i];
    else if(TypPrices[i] &lt; TypPrices[i+1] - Dist)
      Vn += Volumes[i];
    Vtotal += Volumes[i];
  }
  return EMA(100*(Vp-Vn)/Vtotal,3);
}</pre>
<p>The author published a chart with the VPN applied to Ideanomics Inc. (IDEX), a supplier to electric vehicles. The code for replicating his chart:</p>
<pre class="prettyprint">void run()
{
  BarPeriod = 1440;
  StartDate = 20190501;
  EndDate = 20201231;

  assetAdd("IDEX","STOOQ:IDEX.US");
  asset("IDEX");

  vars Volumes = series(marketVol());
  plot("TPR",VPN(Volumes,30),NEW,RED);
  plot("Threshold",10,0,BLACK);
}</pre>
<p>The resulting chart is displayed with a threshold line at 10%. The VPN crossing this line is supposed to be a buy signal. We can see that the signals are valid, but a bit late. They catch only the top of the price peaks. It would be possibly better to smooth the indicator not with an EMA, but with a filter with less lag, such as a 2-pole lowpass filter or one of Ehlers&#8217; zero-lag filters.</p>
<p><img decoding="async" src="https://financial-hacker.com/wp-content/uploads/2021/02/021821_1252_PetraonProg1.png" alt=""></p>
<p>How does the VPN fare in a trading system? When I test a new indicator, I normally use first a simple system with no filters, bells, or whistles for finding out if it has predictive power at all. But this is not what the author did. For testing the VPN, he invented a 2000 stocks portfolio system with the following rules:</p>
<p><em>Buy when the VPN crosses over 10% and its 50-day volume average is above its previous 50-day average, its 5-day RSI below 90, and the price above its 30-day SMA. Close open positions after 15 days, or when the VPN crosses below its 30-day SMA while the price is more than 3 ATR below its 5-day high, or when a COVID market crash is imminent. A COVID crash is predicted by a 15% Russell2000 drop and a value below the 150-day SMA. Assets are filtered out when their price was below 1 or their 5-day average volume below 100000 or the product of price and volume below 500000.</em></p>
<p>I am sure that those hand tailored rules would already produce a great backtest, no matter with which indicator. Additionally, the author optimized the VPN period, the trade lifetime, and the VPN threshold. Although his AmiBroker platform supports out-of-sample optimization, he optimized the whole backtest period and then picked the best parameters manually from the optimization chart.</p>
<p>In our replication of his test, we&#8217;ll use walk-forward optimization for getting a more serious result. As in the AmiBroker code, we invest 10,000 per stock and allow a maximum of 10 open positions. For comparison with a benchmark, we invest the same amount in IWM, the Russell2000 index ETF. As one can expect from the description, the C code is a bit lengthy:</p>
<pre class="prettyprint">#define CAPITAL 10000<br>#define MAXTRADES 10<br><br>void run()
{
  NumWFOCycles = 5;
  BarPeriod = 1440;
  StartDate = 2011;
  EndDate = 2021;
  LookBack = 150;

  setf(TrainMode,SETFACTORS); // skip OptimalF calculation<br>  UpdateDays = 5; // don't download all again every day
  assetList("AssetsRussell2000");
  assetAdd("IWM","STOOQ:IWM.US"); // Russell2000 index ETF

  set(PARAMETERS); <br>  int Period = optimize(30,30,40,10);
  var Threshold = optimize(10,10,20,10);
<br>// Sell after 15 days,
// or when a Russell2000 15% drop and a value below the 150-day SMA indicates meltdown.
  LifeTime = optimize(15,5,70,5);
  asset("IWM");
  int Meltdown = ROC(seriesC(),10) &lt; -15 &amp;&amp; priceClose() &lt; SMA(seriesC(),150);

// Use Russell2000 index (IWM) for a benchmark line
  setf(PlotMode,PL_ALL|PL_BENCHMARK); <br>  static var StartPrice;
  if(is(LOOKBACK)) StartPrice = priceClose();
  var Benchmark = MAXTRADES*CAPITAL/priceClose() * (priceClose()-StartPrice);
  plot("Russell2000",Benchmark,AXIS2,PURPLE);

  for(listed_assets)
  {
    asset(Asset);
    if(Asset == "IWM") continue; // don't trade the index
    vars Volumes = series(marketVol(0),100);
    vars Prices = series(priceClose(),30);
    vars Signals = series(VPN(Volumes,Period),2);

// Filter out when price was below 1
// or 5-day average volume below 100000
// or the 5-day average product of price and volume below 500000.

    bool Filter = Prices[0] &lt; 1
      || SMA(Volumes,5) &lt; 100000
      || SMA(series(Volumes[0]*Prices[0],5),5) &lt; 500000;

// buy when the VPN crosses over 10%
// and its 50-day volume average is above its previous 50-day average,
// its 5-day RSI is below 90, and the price is above its 30-day SMA.

    bool Buy = crossOver(Signals,Threshold)
      &amp;&amp; SMA(Volumes,50) &gt; SMA(Volumes+50,50)
      &amp;&amp; RSI(Prices,5) &lt; 90
      &amp;&amp; Prices[0] &gt; SMA(Prices,30);

// Sell when the VPN crosses below 30-day SMA while price is more than 3 ATR below 5-day high,
// or when a market meltdown is immiment

    bool Sell = (crossUnder(Signals,SMA(Signals,30))
      &amp;&amp; Prices[0] &lt; HH(5) - 3*ATR(5)) || Meltdown;

    if(!Filter &amp;&amp; Buy &amp;&amp; !Sell &amp;&amp; (Train || NumLongTotal &lt; MAXTRADES))
      enterLong(CAPITAL/Prices[0]);
    else if(Sell)
      exitLong();
  }
}</pre>
<p>I have added code comments for the various buy, sell, and filter conditions. The first start of this system will be slow because the histories of all 2000 stocks are downloaded. The Russell2000 asset list is set up to load them from Yahoo. Subsequent starts are faster. I&#8217;ve used <strong>UpdateDays = 5</strong> so that only every 5 days all the stocks are re-loaded. Setting <strong>SETFACTORS</strong> in <strong>TrainMode</strong> is a trick to prevent OptimalF calculation, which is not needed here and would take a long time with this many stocks and trades. The <strong>NUMTRADES</strong> limit is overridden in <strong>Train</strong> mode for getting more trades and thus better parameter quality.</p>
<p>The profit curves are plotted to the chart below, blue for the trading system and purple for the Benchmark.</p>
<p><img decoding="async" src="https://financial-hacker.com/wp-content/uploads/2021/02/021821_1252_PetraonProg2.png" alt=""></p>
<p>Although we used 10 years history, the backtest starts only 2017 due to the preceding WFO training periods. We can see that the VPN based trading system indeed beats the benchmark, but not really convincingly so &#8211; in the first years it actually trailed the IWM. Clearly, we could produce better backtests by in-sample optimization and hand picking parameters. Only problem is that they would be useless.</p>
<p>The VPN indicator and the trading system can be downloaded from the 2021 script repository. You need Zorro S 2.36 or newer for this system.</p>

]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/petra-on-programming-detecting-volume-breakouts/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>The Trend Persistence Indicator</title>
		<link>https://financial-hacker.com/petra-on-programming-the-trend-persistence-indicator/</link>
					<comments>https://financial-hacker.com/petra-on-programming-the-trend-persistence-indicator/#comments</comments>
		
		<dc:creator><![CDATA[Petra Volkova]]></dc:creator>
		<pubDate>Sat, 30 Jan 2021 10:08:54 +0000</pubDate>
				<category><![CDATA[Indicators]]></category>
		<category><![CDATA[Petra on Programming]]></category>
		<category><![CDATA[System Evaluation]]></category>
		<category><![CDATA[Ehlers]]></category>
		<category><![CDATA[TPR]]></category>
		<guid isPermaLink="false">https://financial-hacker.com/?p=3972</guid>

					<description><![CDATA[Financial markets are not stationary: Price curves swing all the time between trending, mean reverting, or entirely random behavior. Without a filter for detecting trend regime, any trend following strategy will bite the dust sooner or later. In Stocks &#38; Commodities February 2021, Richard Poster proposed a trend persistence indicator for avoiding unprofitable market periods. &#8230; <a href="https://financial-hacker.com/petra-on-programming-the-trend-persistence-indicator/" class="more-link">Continue reading<span class="screen-reader-text"> "The Trend Persistence Indicator"</span></a>]]></description>
										<content:encoded><![CDATA[<p><em>Financial markets are not stationary: Price curves swing all the time between trending, mean reverting, or entirely random behavior. Without a filter for detecting trend regime, any trend following strategy will bite the dust sooner or later. In Stocks &amp; Commodities February 2021, Richard Poster proposed a trend persistence indicator for avoiding unprofitable market periods.<br></em></p>
<p><span id="more-3972"></span></p>
<p>The TPR (Trend Persistence Range) indicator measures the steepness of a SMA slope and counts the bars where the slope exceeds a threshold. The more steep bars, the more trending the market. The C code of this indicator:</p>
<pre class="prettyprint">var TPR(vars Data,var Threshold,int TPRPeriod,int SMAPeriod)
{
  vars SMAs = series(SMA(Data,SMAPeriod));
  int i, CtrP=0, CtrM = 0;
  for(i=0; i&lt;TPRPeriod; i++) {
    var Slope = SMAs[i]-SMAs[i+1];
    if(Slope &gt; Threshold) CtrP++;
    else if(Slope &lt; -Threshold) CtrM++;
  }
  return 100. * abs(CtrP-CtrM)/TPRPeriod;
}
</pre>
<p>The TPR function gets threshold, counting period, and SMA period as parameters. The author published a chart with the TPR applied to EUR/USD 1-hour bars. He apparently smoothed the TPR output with some filter, but did not mention which one. For replicating his chart, I assumed that he used a simple EMA(5) and a 1 pip threshold. The resulting chart:</p>
<p><img decoding="async" src="https://financial-hacker.com/wp-content/uploads/2021/01/013021_0945_PetraonProg1.png" alt=""></p>
<p>This looks as if trends were indeed well detected. But how does the TPR fare as a trend filter in a real trading system? The author published results from a SMA crossover strategy with his TPR in comparison with several other filters &#8211; and of course the TPR won hands down. But I fear this result has to be taken with a grain of salt, because he optimized the filter parameters&nbsp; using the MT4 platform. This means the results are distorted by in-sample optimization, the only &#8220;optimization&#8221; MT4 can do.</p>
<p>For testing the TPR, here&#8217;s the C code of a basic SMA crossover system with 3 different algorithms: no filter, TPR filter, and Ehler&#8217;s Correlation Cycle filter from <a href="https://financial-hacker.com/petra-on-programming-the-correlation-cycle-indicator/">this article</a>. First, with in-sample optimization for replicating the author&#8217;s MT4 results:</p>
<pre class="prettyprint">void run() 
{
   BarPeriod = 60;
   StartDate = 2010;
   EndDate = 2020;
   LookBack = 2500; 
   set(PARAMETERS);
   asset("EUR/USD");

   while(algo(loop("None","TPR","CCY"))) 
   {
     vars Smas = series(SMA(seriesC(),2500));
     int Color = BLACK;
     bool Filter = 1;
     if(Algo == "TPR") {
       int Period = optimize(15,10,20,1);
       var Threshold = optimize(10,0,20,2);
       var Tpr = TPR(seriesC(),1*PIP,Period,5);
       Filter = Tpr &gt; Threshold;
       Color = BLUE;
     } else if(Algo == "CCY") {
       int Period = optimize(15,10,30,5);
       var Threshold = optimize(10,0,40,5);
       Filter = CCYIState(seriesC(),Period,Threshold);
       Color = PURPLE;
    }

    if(Filter &amp;&amp; crossOver(Smas,seriesC()))
      enterLong();
    else if(Filter &amp;&amp; crossUnder(Smas,seriesC()))
      enterShort();
    plot(Algo,ProfitClosed+ProfitOpen,MAIN|LINE|AXIS2,Color);
  }
} </pre>
<p>The profit curves are plotted to the chart below, black for no filter, blue for the TPR and purple for the CCY. The blue and red vertical bars are the total equity and underwater equity. On the chart, the TPR seemingly does indeed a good job – even better than the CCY. In accordance with the author&#8217;s MT4 result, filtering a trend following system with TPR seems to almost double the return:</p>
<p><img decoding="async" src="https://financial-hacker.com/wp-content/uploads/2021/01/013021_0945_PetraonProg2.png" alt=""></p>
<p>Too good to be true? The parameters were in-sample optimized, so this result is biased and would be likely different in real trading. But since the optimization was done for a 10-year period and covers many different market situations, maybe the results are not totally meaningless.</p>
<p>So let&#8217;s repeat the test, but this time with a serious optimization. We need these two additional commands in the script:</p>
<pre class="prettyprint">NumWFOCycles = 10;
DataSplit = 75;</pre>
<p>This activates walk-forward optimization with 10 cycles split into 75% training data and 25% test data. The results of the walk-forward optimization:</p>
<p><img decoding="async" src="https://financial-hacker.com/wp-content/uploads/2021/01/013021_0945_PetraonProg3.png" alt=""></p>
<p>That&#8217;s what we would likely get in real trading &#8211; and unfortunately it does not quite look as good. A SMA crossover is just not a serious trading system for Forex, no matter with which filter. Still, the equity curves are somewhat positive, and the TPR still improves the return a bit, although much less so than Ehler&#8217;s Correlation Cycle.</p>
<p>Anyway, we now have another market regime filter in our collection, for whatever it might be good. For a comprehensive comparison of trend filters, we had to repeat this out-of-sample test with many other assets, many different time periods, and possibly better trend following methods than SMA crossings.</p>
<p>The TPR indicator and the trading system can be downloaded from the 2021 script repository. You&#8217;ll need Zorro 2.35 or above.</p>

]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/petra-on-programming-the-trend-persistence-indicator/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<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>The Scholz Brake: Fixing Germany&#8217;s New 1000% Trader Tax</title>
		<link>https://financial-hacker.com/the-scholz-brake-fixing-germanys-new-1000-trader-tax/</link>
					<comments>https://financial-hacker.com/the-scholz-brake-fixing-germanys-new-1000-trader-tax/#comments</comments>
		
		<dc:creator><![CDATA[jcl]]></dc:creator>
		<pubDate>Sun, 19 Jan 2020 10:44:05 +0000</pubDate>
				<category><![CDATA[No Math]]></category>
		<category><![CDATA[System Evaluation]]></category>
		<category><![CDATA[Economy]]></category>
		<category><![CDATA[Tax]]></category>
		<guid isPermaLink="false">https://financial-hacker.com/?p=3197</guid>

					<description><![CDATA[Would you like to read &#8211; from begin to end &#8211; a 18 page pounderous law draft titled &#8220;Law for introducing a duty to report cross-border tax structuring&#8221;? The members of the German Bundestag apparently didn&#8217;t. After all, nothing seemed wrong with a duty to report cum-ex schemes. So the new law, proposed by finance &#8230; <a href="https://financial-hacker.com/the-scholz-brake-fixing-germanys-new-1000-trader-tax/" class="more-link">Continue reading<span class="screen-reader-text"> "The Scholz Brake: Fixing Germany&#8217;s New 1000% Trader Tax"</span></a>]]></description>
										<content:encoded><![CDATA[<p>Would you like to read &#8211; from begin to end &#8211; a 18 page pounderous law draft titled <em>&#8220;Law for introducing a duty to report cross-border tax structuring&#8221;</em>? The members of the German Bundestag apparently didn&#8217;t. After all, nothing seemed wrong with a duty to report cum-ex schemes. So the new law, proposed by finance minister Olaf Scholz, passed legislation on December 12, 2019 without much discussion. Only afterwards its real content, hidden on page 15, became public. It caused incredulity and turmoil among traders and investors. This article deals with the new bizarre German &#8216;trader tax&#8217;, and with ways to step around it.<span id="more-3197"></span></p>
<p>News about a &#8220;1000% tax for traders&#8221; and even a &#8220;tax on losses&#8221; spread quickly in January 2020. Hopefully, fake news? This is the relevant section of the new law (Art. 5 § 20 sentence 6 number 4):</p>
<p><em>&#8220;The Income Tax Act as published on October 8, 2009 (Federal Law Gazette I p. 3366, 3862), last amended by Article 1 of the Law of August 4, 2019 (Federal Law Gazette I p. 1122), is changed as follows. The following sentences are inserted after § 20 paragraph 6 sentence 4: Losses from financial assets within the meaning of paragraph 2 sentence 1 number 3 may only be offset in the amount of 10,000 euros with profits within the meaning of paragraph 2 sentence 1 number 3 and with income within the meaning of § 20 paragraph 1 number 11; sentences 2 and 3 apply mutatis mutandis with the proviso that losses that have not been offset each year may only be offset against profits up to the amount of 10,000 euros with profits within the meaning of paragraph 2 sentence 1 number 3 and with income within the meaning of § 20 paragraph 1 number 11. &#8220;</em></p>
<p>Translation: For the income tax, trading losses cannot anymore be offset against trading profits in excess of EUR 10,000 per year.</p>
<p>Bad enough. But surely they mean that an annual loss can only be offset in 10,000 EUR portions against annual profits in subsequent years? That was at least my interpretation. It made a sort of sense, and appeared harmless if you anyway do not plan to have an annual trading loss. But rumors said that they mean to tax not the annual net profit, but the profit of every single trade. Some said it&#8217;s even a tax on every favourable price tick of any open position, since this already constitutes a profit. In any case, this would quickly sum up to an insane tax amount &#8211; no matter if you win or lose. It&#8217;s in fact not a tax on income, but a tax on volatility, portfolio diversification, and hedging. And it was not even clear for which sorts of assets that tax is due. The relevant <em>§ 2 sentence 1 number 3</em> can be applied to options, futures, and bonds, but possibly also to all leveraged assets such as forex, CFDs, ETFs, and stocks. Only the risky and ecologically damaging speculation with cryptocurrencies seems exempt.</p>
<h6>The win/loss offset mystery</h6>
<p>For getting clarification, I wrote a letter:</p>
<p><em>Dear Federal Ministry of Finance,</em></p>
<p><em>on my blog I would like to inform readers about the current &#8220;Law for introducing a duty to report cross-border tax structuring&#8221;, in particular about Art 5 § 20 sentence 6 number 4. </em><em style="font-size: inherit;">However, I am not sure that I fully understood the new law. Therefore I kindly ask you to briefly answer my subsequent questions.</em></p>
<p><em>1) Which of the following financial products fall under the referred &#8220;financial assets&#8221;: stocks, currencies (&#8216;Forex&#8217;), options, futures, CFDs, savings contracts with a limited term, German treasury bonds?</em></p>
<p><em>2)  Which tax is due in this scenario: Bob owns EUR 20,000 that he invests in options trading. He buys about 200 options per year. In one winning year, 101 of the transactions ended with a profit of EUR 1000 each, 99 transactions with a loss of EUR 1000 each. The annual result is <strong>EUR 2000</strong>. The taxable profit is <strong>EUR 101,000</strong>, for which Bob has to pay a tax of EUR 91,000 x 25% = <strong>EUR 22,750</strong> after deducting EUR 10,000 loss. The tax rate in relation to the EUR 2000 profit is <strong>1137.5%</strong>.  Is the tax calculation correct?</em></p>
<p><em>3) Same scenario, but a losing year. 101 transactions ended with a loss of EUR 1000, 99 transactions with a profit of EUR 1000. The annual loss is <strong>EUR 2000</strong>. The taxable profit is EUR 99,000. After subtracting EUR 10,000, Bob has for his loss a tax liability of EUR 89,000 x 25% = <strong>EUR 22,250</strong>.</em></p>
<p><em>4) Same scenario, but Bob now tries to outwit the new trader tax. He buys a single long-term position for EUR 20,000 at the begin of the year. Due to leverage, the total value of the position has risen by EUR 100,000 by the middle of the year, then dropped again by EUR 100,000 by the end of the year. Bob sells at the end of the year at purchase price. The year ends profit-neutral and Bob intends to pay no tax. <br />   But the finance ministry is not as easily fooled. Since the tax also applies to the value increase in the middle of the year, the taxable profit minus loss deduction is <strong>EUR 90,000</strong>. Bob gets a tax bill of EUR 90,000 x 25% = <strong>EUR 22,500</strong>.</em></p>
<p><em>5) Same scenario as 2), but for stepping around the new trader tax, Bob now avoids closing positions. Instead he exercises all options, no matter of in the money or not, shortly before expiration. The paid premium is then not a loss, but a purchase fee. Since the broker offsets simultaneous long and short positions automatically, the finance ministry can do nothing about that. <br />   At the end of the year remains a single long position in the underlying with a value of <strong>EUR 2,000</strong>, which is then sold. The taxable profit amount is EUR 2,000 x 25% = <strong>EUR 500</strong>.</em></p>
<p><em>6) Same scenario as 2), but for stepping around the new trader tax, the broker has now offered a new structured product. Instead of closing positions, they are converted directly into another asset of the client&#8217;s choice that does not fall under § 20 sentence 6 number 4 (for instance, a nonleveraged stock). The premium for selling options is also not paid in cash, but in a position of that asset. At the end of the year, a position with a value of <strong>EUR 2,000</strong> remains in Bob&#8217;s account, which is then sold. The taxable profit amount is EUR 2,000 x 25% = <strong>EUR 500</strong>.</em></p>
<p><em>I would be pleased if you could briefly tell me which of the tax calculations in the 5 scenarios are appropriate. I would also be interested in a brief explanation of the purpose and motivation behind the new law. And I would be very interested in an explanation how Bob in scenarios 2-4 is supposed to pay his taxes, since  they exceed all his capital.</em></p>
<p><em>Sincerely yours</em></p>
<p><em>Johann Christian Lotter</em></p>
<p>I was obviously not the only one who asked the ministry about the new law. I got a long formal response with little information content. It did not answer any of my questions, but confirmed that the 1137% tax and the tax on losses in scenarios 2 and 3 is for real. They did not comment on the tick tax of scenario 4.</p>
<p>Even the experts in Scholz&#8217; finance ministry seem mystified about the implementation, motivation, or objective of this new law. It does not stand alone, but is part of a bundle of similar (although slightly less absurd) laws against retail traders and small investors. Their purpose is a mystery. They seem not motivated by populism. Except for the upcoming transaction tax, few know about them. Some say that they were originally intended against tax scams and large-scale speculation, and only designed in a wrong way.  But that is of course impossible, since it would imply a remarkable intellectual incapacity of our lawmakers. Maybe Scholz just intended to show a leftist position for his election to party chairman (which failed nevertheless). Or he&#8217;s really convinced that people who live from trading are all speculators and capitalist pigs, and must be hit whenever possible. Who knows. A personal confession at this point: I, also, am responsible for the new tax. I have always voted for Olaf Scholz&#8217; party in the past. Often just out of tradition. This was apparently not always a wise decision.</p>
<h6>Four ways to fix the tax</h6>
<p>The trader tax will be in effect from 2021. It will then be unique in the world. No other country has a tax on volatility or diversification. I think it will not last long: Traders financially ruined by it, like Bob in some of the above examples, will challenge it in court. The Federal Supreme Court might eventually annul it due to unlawful overtaxation or its blatant absurdity. But until that happens, we&#8217;ll have to live with it.</p>
<p>Large-scale tax scammers, speculators, and hedge funds can laugh at Scholz&#8217; tax constructs. They just incorporate, preferably offshore, and are exempt. That&#8217;s no solution for small private investors. Scenarios 5 and 6 are two possible ways to avoid the trader tax. A third, relatively simple method would be a trading account in a cryptocurrency. As long as the tax is not applied on open positions, converting one financial asset into another should be tax-neutral, since it does not realize any profit. The problem: You never know what bitcoin, or another account base that brokers might offer for outwitting the tax, will be worth next year. And the ministry was unable to precisely describe how the tax will be applied and which assets are affected. So there&#8217;s no guarantee that these workarounds do really work. The safe way is staying below the loss limit.  </p>
<p>I know that the <a href="https://zorro-project.com" target="_blank" rel="noopener noreferrer">Zorro platform</a>, on demand of several German users, will get a new indicator, the <strong>Scholz Brake</strong>. This indicator will be implemented in the next Zorro release. It can be set up at the session start and at the begin of any year, like this:</p>
<pre class="prettyprint">if(is(INITRUN) || year(0) != year(1)) // any new year<br />  ScholzBrake = 10000; // activate the Scholz Brake</pre>
<p>Once set, the <strong>ScholzBrake </strong>variable will be counted down by all trading losses of all Zorro instances that run on the same PC and have activated it. So the script can always check the distance to the critical EUR 10,000 total loss limit, and decide what to do. If the variable reaches zero, trading is automatically suspended until the end of the year.</p>
<p>This prevents you (mostly) from the effects of the new law. Of course at the price of not trading for the rest of the year. If you live from trading and have hit the Scholz limit early in the year, you got enough time to look for a new job. Maybe as an expert in the finance ministry.</p>
<h6>References</h6>
<p>1. <a href="https://www.bundesrat.de/SharedDocs/drucksachen/2019/0601-0700/649-19.pdf;jsessionid=994163D05E496D3D58EED7A2ECCCA424.1_cid365?__blob=publicationFile&amp;v=1" target="_blank" rel="noopener noreferrer">Gesetz zur Einführung einer Pflicht zur Mitteilung</a><br /><a href="https://www.bundesrat.de/SharedDocs/drucksachen/2019/0601-0700/649-19.pdf;jsessionid=994163D05E496D3D58EED7A2ECCCA424.1_cid365?__blob=publicationFile&amp;v=1">grenzüberschreitender Steuergestaltungen</a></p>
<p>2. <a href="https://boerse.ard.de/anlagestrategie/steuern/verlustverrechnung-fuer-termingeschaefte-wird-erschwert100.html" target="_blank" rel="noopener noreferrer">ARD Börsenmagazin</a></p>
<p>3. <a href="https://manual.zorro-project.com/lots.htm#scholz" target="_blank" rel="noopener noreferrer">ScholzBrake</a></p>
<p>4. <a href="http://chng.it/xYTnxrqxJ5" target="_blank" rel="noopener noreferrer">Petition</a></p>

]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/the-scholz-brake-fixing-germanys-new-1000-trader-tax/feed/</wfw:commentRss>
			<slash:comments>33</slash:comments>
		
		
			</item>
		<item>
		<title>Hacking a HFT system</title>
		<link>https://financial-hacker.com/hacking-hft-systems/</link>
					<comments>https://financial-hacker.com/hacking-hft-systems/#comments</comments>
		
		<dc:creator><![CDATA[jcl]]></dc:creator>
		<pubDate>Sat, 08 Jul 2017 13:26:30 +0000</pubDate>
				<category><![CDATA[No Math]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[System Evaluation]]></category>
		<category><![CDATA[Arbitrage]]></category>
		<category><![CDATA[ES]]></category>
		<category><![CDATA[ETF]]></category>
		<category><![CDATA[HFT]]></category>
		<category><![CDATA[Latency]]></category>
		<category><![CDATA[NxCore]]></category>
		<category><![CDATA[SPY]]></category>
		<guid isPermaLink="false">http://www.financial-hacker.com/?p=2359</guid>

					<description><![CDATA[Compared with machine learning or signal processing algorithms of conventional algo trading strategies, High Frequency Trading systems can be surprisingly simple. They need not attempt to predict future prices. They know the future prices already. Or rather, they know the prices that lie in the future for other, slower market participants. Recently we got some &#8230; <a href="https://financial-hacker.com/hacking-hft-systems/" class="more-link">Continue reading<span class="screen-reader-text"> "Hacking a HFT system"</span></a>]]></description>
										<content:encoded><![CDATA[<p>Compared with machine learning or signal processing algorithms of conventional algo trading strategies, <strong>High Frequency Trading</strong> systems can be surprisingly simple. They need not attempt to predict future prices. They know the future prices already. Or rather, they know the prices that lie in the future for other, slower market participants. Recently we got some contracts for simulating HFT systems in order to determine their potential profit and maximum latency. This article is about testing HFT systems the hacker&#8217;s way.<span id="more-2359"></span></p>
<p>The HFT advantage is receiving price quotes earlier and getting orders filled faster than the majority of market participants. Its profit depends on the system&#8217;s <strong>latency</strong>, the delay between price quote and subsequent order execution at the exchange. Latency is the most relevant factor of a HFT system. It can be optimized in two ways: by minimizing the distance to the exchange, and by maximizing the speed of the trading system. The former is more important than the latter.</p>
<h3>The location</h3>
<p>Ideally, a HFT server is located directly at the exchange. And indeed most exchanges are dear selling server space in their cellars &#8211; the closer to the main network hub, the dearer the space. Electrical signals in a shielded wire travel with about 0.7 .. 0.9 times the speed of light (300 km/ms). 1 m closer to the signal source means a whopping 8 nanoseconds round-trip time advantage. How many trade opportunities disappear after 8&nbsp;ns? I don&#8217;t know, but people are willing to pay for any saved nanosecond.</p>
<p>Unfortunately (or fortunately, from a cost perspective), the HFT system that we&#8217;ll examine here can not draw advantage from colocating at the exchange. For reasons that we&#8217;ll see soon, it must trade and receive quotes from the NYSE and the CME simultaneously. There&#8217;s a high speed cable and also a microwave radio link running between both cities. The theoretically ideal location of such a HFT system is <strong>Warren, Ohio</strong>, exactly halfway between New York City and Chicago. I don&#8217;t know whether there&#8217;s a high speed node in Warren, but if it is, the distance of 357 miles to both exchanges would be equivalent to about 4 ms round-trip latency.</p>
<p><figure id="attachment_2392" aria-describedby="caption-attachment-2392" style="width: 840px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2017/07/Warren-e1494606853331.jpg"><img fetchpriority="high" decoding="async" class="wp-image-2392 size-large" src="http://www.financial-hacker.com/wp-content/uploads/2017/07/Warren-e1494606853331-1024x552.jpg" alt="" width="840" height="453" srcset="https://financial-hacker.com/wp-content/uploads/2017/07/Warren-e1494606853331-1024x552.jpg 1024w, https://financial-hacker.com/wp-content/uploads/2017/07/Warren-e1494606853331-300x162.jpg 300w, https://financial-hacker.com/wp-content/uploads/2017/07/Warren-e1494606853331-768x414.jpg 768w, https://financial-hacker.com/wp-content/uploads/2017/07/Warren-e1494606853331.jpg 1065w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></a><figcaption id="caption-attachment-2392" class="wp-caption-text">Warren, Ohio &#8211; HFT Mecca of the world (image by Jack Pearce / Wikipedia Commons)</figcaption></figure></p>
<p>Without doubt, a server in this pleasant town would come at lower cost than a server in the cellar of the New York Stock Exchange. My today&#8217;s free tip for getting rich: Buy some garage space in Warren, tap into the high speed New York-Chicago cable or radio link, and rent out server racks!</p>
<h3>The software</h3>
<p>When you&#8217;ve already invested money for the optimal location and connection of the HFT system, you&#8217;ll also want an algo trading software that matches the required speed. Commercial trading platforms &#8211; even Zorro &#8211; are normally not fast enough. And you don&#8217;t know what they do behind your back. HFT systems are therefore normally not based on a universal trading platform, but directly coded. Not in R or Python, but in a fast language, usually one of the following:</p>
<ul style="list-style-type: square;">
<li><strong>C or C++.</strong> Good combination of high level and high speed. C is easy to read, but very effective and the fastest language of all &#8211; almost as fast as machine code.</li>
<li><strong>Pentium Assembler.</strong>&nbsp;Write your algorithm in machine instructions. Faster than even C when the algorithm mostly runs in loops that can be manually optimized. There are specialists for optimizing assembler code. Disadvantage: Any programmer has a hard time to understand assembler programs written by another programmer.</li>
<li><strong>CUDA, HLSL, or GPU assembler.</strong>&nbsp;Run your algorithm on the shader pipeline of the PC&#8217;s video card. This makes sense when it is heavily based on vector and matrix operations.</li>
<li><strong>VHDL.</strong> If any software would be too slow and trade success really depends on nanoseconds, the ultimate solution is coding the system in hardware. In VHDL you can define arithmetic units, digital filters, and sequencers on a FPGA (<strong>F</strong>ield <strong>P</strong>rogrammable <strong>G</strong>ate <strong>A</strong>rray) chip with a clock rate up to several 100 MHz. The chip can be directly connected to the network interface.</li>
</ul>
<p>With exception of VHDL, especially programmers of 3D computer games are normally familar with those high-speed languages. But the standard language of HFT systems is plain C/C++. It is also used in this article.</p>
<h3>The trading algorithm</h3>
<p>Many HFT systems prey on traders by using fore-running methods. They catch your quote, buy the same asset some microseconds earlier, and sell it to you with a profit. Some exchanges prevent this in the interest of fair play, while other exchanges encourage this in the interest of earning more fees. For this article we won&#8217;t use fore-running methods, but simply exploit <a href="http://www.financial-hacker.com/build-better-strategies-part-2-model-based-systems/" target="_blank" rel="noopener">arbitrage</a> between ES and SPY. We&#8217;re assuming that our server is located in Warren, Ohio, and has a high speed connection to Chicago and to New York.</p>
<p>ES is a Chicago traded S&amp;P500 future, exposed to supply and demand. SPY is a New York traded ETF, issued by State Street in Boston and following the S&amp;P500 index (minus State Street&#8217;s fees). 1 ES Point is equivalent to 10 SPY cents, so the ES price is about ten times the SPY price. Since both assets are based on the same index, we can expect that their prices are highly correlated. There have been some publications (1) that claim this correlation will break down at low time frames, causing one asset trailing the other. We&#8217;ll check if this is true. Any shortlived ES-SPY price difference that exceeds the bid-ask spreads and trading costs constitutes an arbitrage opportunity. Our arbitrage algorithm would work this way:</p>
<ul style="list-style-type: square;">
<li>Determine the SPY-ES difference.</li>
<li>Determine its deviation from the mean.&nbsp;</li>
<li>If the deviation exceeds the ask-bid spreads plus a threshold, open positions in&nbsp;ES and SPY in opposite directions.</li>
<li>If the deviation reverses its sign and exceeds the ask-bid spreads plus a smaller threshold, close the positions.</li>
</ul>
<p>This is the barebone HFT algorithm in C. If you&#8217;ve never seen HFT code before, it might look a bit strange:</p>
<pre class="prettyprint">#define THRESHOLD  0.4  // Entry/Exit threshold 

// HFT arbitrage algorithm
// returns 0 for closing all positions
// returns 1 for opening ES long, SPY short
// returns 2 for opening ES short, SPY long
// returns -1 otherwise
int tradeHFT(double AskSPY,double BidSPY,double AskES,double BidES)
{
	double SpreadSPY = AskSPY-BidSPY, SpreadES = AskES-BidES;
	double Arbitrage = 0.5*(AskSPY+BidSPY-AskES-BidES);

	static double ArbMean = Arbitrage;	
	ArbMean = 0.999*ArbMean + 0.001*Arbitrage;
	static double Deviation = 0;
	Deviation = 0.75*Deviation + 0.25*(Arbitrage - ArbMean);

	static int Position = 0;	
	if(Position == 0) {
		if(Deviation &gt; SpreadSPY+THRESHOLD)
			return Position = 1;
		if(-Deviation &gt; SpreadES+THRESHOLD)
			return Position = 2;
	} else {
		if(Position == 1 &amp;&amp; -Deviation &gt; SpreadES+THRESHOLD/2)
			return Position = 0;
		if(Position == 2 &amp;&amp; Deviation &gt; SpreadSPY+THRESHOLD/2)
			return Position = 0;
	}
	return -1;	
}
</pre>
<p>The <strong>tradeHFT</strong> function is called from some framework &#8211; not shown here &#8211; that gets the price quotes and sends the trade orders. Parameters are the current best ask and bid prices of ES and SPY from the top of the order book (we assume here that the SPY price is multiplied with 10 so that both assets are on the same scale). The function returns a code that tells the framework whether to open or close opposite positions or to do nothing. The <strong>Arbitrage</strong> variable is the mid price difference of SPY and ES. Its mean (<strong>ArbMean</strong>) is filtered by a slow EMA, and the <strong>Deviation</strong> from the mean is also slightly filtered by a fast EMA to prevent reactions on outlier quotes. The <strong>Position</strong> variable constitutes a tiny state machine with the 3 states long, short, and nothing. The entry/exit <strong>Threshold</strong> is here set to 40 cents, equivalent to a bad SPY spread multiplied with 10. This is the only adjustable parameter of the system. If we wanted to really trade it, we had to optimize the threshold using several months&#8217; ES and SPY data.</p>
<p>This minimalistic system would be quite easy to convert to Pentium assembler or even to a FPGA chip. But it is not really necessary: Even compiled with Zorro&#8217;s lite-C compiler, the <strong>tradeHFT</strong> function executes in just about 750 nanoseconds. A strongly optimizing C compiler, like Microsoft VC++, &nbsp;gets the execution time down to 650 nanoseconds. Since the time span between two ES quotes is normally 10 microseconds or more, the speed of C is largely sufficient.</p>
<p>Our HFT experiment has to answer two questions. First, are those price differences big enough for arbitrage profit? And second, at which maximum latency will the system still work?&nbsp;</p>
<h3>The data</h3>
<p>For backtesting a HFT system, normal price data that you can freely download from brokers won&#8217;t do. You have to buy high resolution order book or BBO data (<strong>B</strong>est <strong>B</strong>id and <strong>O</strong>ffer) that includes the exchange time stamps. Without knowing the exact time when the price quote was received at the exchange, it is not possible to determine the maximum latency.</p>
<p>Some companies are recording all quotes from all exchanges and are selling this data. Any one has its specific data format, so the first challenge is to convert this to lean data files that we then evaluate with our simulation software. We&#8217;re using this very simple target format for price quotes:</p>
<pre class="prettyprint">typedef struct T1    // single tick
{
	double time; // time stamp, OLE DATE format
	float fVal;  // positive = ask price, negative = bid price	
} T1;</pre>
<p>The company watching the Chicago Mercantile Exchange delivers its data in a specific CSV format with many additional fields, of which most we don&#8217;t need here (f.i. the trade volume or the quote arrival time). Every day&#8217;s quotes are stored in one CSV file. This is the Zorro script for pulling the ES December 2016 contract out of it and converting it to a dataset of T1 price quotes:</p>
<pre class="prettyprint">//////////////////////////////////////////////////////
// Convert price history from Nanotick BBO to .t1
//////////////////////////////////////////////////////

#define STARTDAY 20161004
#define ENDDAY   20161014

string InName = "History\\CME.%08d-%08d.E.BBO-C.310.ES.csv";  // name of a day file
string OutName = "History\\ES_201610.t1";
string Code = "ESZ";	// December contract symbol

string Format = "2,,%Y%m%d,%H:%M:%S,,,s,,,s,i,,";  // Nanotick csv format
void main()
{
	int N,Row,Record,Records;
	for(N = STARTDAY; N &lt;= ENDDAY; N++)
	{
		string FileName = strf(InName,N,N+1);
		if(!file_date(FileName)) continue;
		Records = dataParse(1,Format,FileName);  // read BBO data
		printf("\n%d rows read",Records);
		dataNew(2,Records,2);  // create T1 dataset
		for(Record = 0,Row = 0; Record &lt; Records; Record++)
		{
			if(!strstr(Code,dataStr(1,Record,1))) continue; // select only records with correct symbol
			T1* t1 = dataStr(2,Row,0);  // store record in T1 format
			float Price = 0.01 * dataInt(1,Record,3);  // price in cents
			if(Price &lt; 1000) continue;  // no valid price
			string AskBid = dataStr(1,Record,2);
			if(AskBid[0] == 'B')  // negative price for Bid
				Price = -Price;
			t1-&gt;fVal = Price;
			t1-&gt;time = dataVar(1,Record,0) + 1./24.;  // add 1 hour Chicago-NY time difference
			Row++;
		}
		printf(", %d stored",Row);
		dataAppend(3,2,0,Row);  // append dataset
		if(!wait(0)) return;
	}
	dataSave(3,OutName);  // store complete dataset
}</pre>
<p>We&#8217;re converting here 10 days in October 2016 for our backtest. The script first parses the CSV file into an intermediary binary dataset, which is then converted to the T1 target format. Since the time stamps are in Chicago local time, we have to add one hour to convert them to NY time.</p>
<p>The company watching the New York Stock exchange delivers data in a highly compressed specific format named &#8220;NxCore Tape&#8221;. We&#8217;re using the Zorro NxCore plugin for converting this to another T1 list:</p>
<pre class="prettyprint">//////////////////////////////////////////////////////
// Convert price history from Nanex .nx2 to .t1
//////////////////////////////////////////////////////

#define STARTDAY 20161004
#define ENDDAY 	 20161014
#define BUFFER	 10000

string InName = "History\\%8d.GS.nx2";  // name of a single day tape
string OutName = "History\\SPY_201610.t1";
string Code = "eSPY";

int Row,Rows;

typedef struct QUOTE {
	char	Name[24];
	var	Time,Price,Size;
} QUOTE;

int callback(QUOTE *Quote)
{
	if(!strstr(Quote-&gt;Name,Code)) return 1;
	T1* t1 = dataStr(1,Row,0);  // store record in T1 format
	t1-&gt;time = Quote-&gt;Time;
	t1-&gt;fVal = Quote-&gt;Price;
	Row++; Rows++;
	if(Row &gt;= BUFFER)	{   // dataset full?
		Row = 0;
		dataAppend(2,1);    // append to dataset 2
	}
	return 1;
}


void main()
{
	dataNew(1,BUFFER,2); // create a small dataset
	login(1);            // open the NxCore plugin
	
	int N;
	for(N = STARTDAY; N &lt;= ENDDAY; N++) {
		string FileName = strf(InName,N);
		if(!file_date(FileName)) continue;
		printf("\n%s..",FileName);
		Row = Rows = 0;  // initialize global variables
		brokerCommand(SET_HISTORY,FileName); // parse the tape
		dataAppend(2,1,0,Row);  // append the rest to dataset 2
		printf("\n%d rows stored",Rows);
		if(!wait(0)) return;  // abort when [Stop] was hit
	}
	dataSave(2,OutName); // store complete dataset
}
</pre>
<p>The <strong>callback</strong> function is called by any quote in the tape file. We don&#8217;t need most of the data, so we filter out only the SPY quotes (&#8220;eSPY&#8221;).</p>
<h3>Verifying the inefficiency</h3>
<p>With data from both sources, we can now compare the ES and SPY prices in high resolution. Here&#8217;s a typical 10 seconds sample from the price curves:</p>
<p><figure id="attachment_2599" aria-describedby="caption-attachment-2599" style="width: 1701px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2017/08/HFTChart_SPY.png"><img decoding="async" class="wp-image-2599 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2017/08/HFTChart_SPY.png" alt="" width="1701" height="401" srcset="https://financial-hacker.com/wp-content/uploads/2017/08/HFTChart_SPY.png 1701w, https://financial-hacker.com/wp-content/uploads/2017/08/HFTChart_SPY-300x71.png 300w, https://financial-hacker.com/wp-content/uploads/2017/08/HFTChart_SPY-768x181.png 768w, https://financial-hacker.com/wp-content/uploads/2017/08/HFTChart_SPY-1024x241.png 1024w, https://financial-hacker.com/wp-content/uploads/2017/08/HFTChart_SPY-1200x283.png 1200w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></a><figcaption id="caption-attachment-2599" class="wp-caption-text">SPY (black) vs. ES (red), October 5, 2017, 10:01:25 &#8211; 10:01.35</figcaption></figure></p>
<p>The resolution is 1 ms. ES is plotted in $ units, SPY in 10 cents units, with a small offset so that the curves lie over each other. The prices shown are ask prices. We can see that ES moves in steps of 25 cents, SPY in steps of 1 cent. The prices are still well correlated even on a millisecond scale. ES appears to trail a tiny bit.</p>
<p>We can also see an arbitrage opportunity at the steep price step in the center at about 10:01:30. ES reacted a bit slower, but stronger on some event, probably a moderate price jump of one of the stocks of the S&amp;P 500. This event also triggered a fast sequence of oscillating SPY quotes, most likely from other HFT systems, until the situation calmed down again a few 100 ms later (the scale is not linear since time periods with no quotes are skipped). For several milliseconds the ES-SPY difference exceeded the ask-bid spread of both assets (usually 25 cents for ES and 1..4 cents for SPY). We would here ideally sell ES and buy SPY immediately after the ES price step. So we have verified that the theoretized inefficiency does really exist, at least in this sample.&nbsp;</p>
<p>The script for displaying high resolution charts:<!--?prettify linenums=true?--></p>
<pre class="prettyprint">#define ES_HISTORY	"ES_201610.t1"
#define SPY_HISTORY	"SPY_201610.t1"
#define TIMEFORMAT	"%Y%m%d %H:%M:%S"
#define FACTOR		10
#define OFFSET		3.575

void main()
{
	var StartTime = wdatef(TIMEFORMAT,"20161005 10:01:25"),
		EndTime = wdatef(TIMEFORMAT,"20161005 10:01:35");
	MaxBars = 10000;
	BarPeriod = 0.001/60.;	// 1 ms plot resolution
	Outlier = 1.002;  // filter out 0.2% outliers

	assetList("HFT.csv");
	dataLoad(1,ES_HISTORY,2);
	dataLoad(2,SPY_HISTORY,2);
	int RowES=0, RowSPY=0;
	
	while(Bar &lt; MaxBars)
	{
		var TimeES = dataVar(1,RowES,0), 
			PriceES = dataVar(1,RowES,1), 
			TimeSPY = dataVar(2,RowSPY,0), 
			PriceSPY = dataVar(2,RowSPY,1);

		if(TimeES &lt; TimeSPY) RowES++;
		else RowSPY++;

		if(min(TimeES,TimeSPY) &lt; StartTime) continue;
		if(max(TimeES,TimeSPY) &gt; EndTime) break;

		if(TimeES &lt; TimeSPY) {
			asset("ES");
			priceQuote(TimeES,PriceES);
		} else {
			asset("SPY");
			priceQuote(TimeSPY,PriceSPY);
		}
		
		asset("ES");
		if(AssetBar &gt; 0) plot("ES",AskPrice+OFFSET,LINE,RED);
		asset("SPY");
		if(AssetBar &gt; 0) plot("SPY",FACTOR*AskPrice,LINE,BLACK);
	}
}
</pre>
<p>The script first reads the two historical data files that we&#8217;ve created above, and then parses them row by row. For keeping the ES and SPY quotes in sync, we always read the quote with the smaller timestamp from the datasets (the quotes are stored in ascending timestamp order). The <strong>priceQuote</strong> function checks the prices for outliers, stores the ask price in the <strong>AskPrice</strong> variable and the ask-bid difference in <strong>Spread</strong>, and&nbsp;increases the <strong>Bar</strong> count for plotting the price curves. A bar of the curve is equivalent to 1 ms. The <strong>AssetBar</strong> variable is the last bar with a price quote of that asset, and is used here to prevent plotting before the first quote arrived.&nbsp;</p>
<h3>Testing the system</h3>
<p>For backtesting our HFT system, we only need to modify the script above a bit, and call the <strong>tradeHFT</strong> function in the loop:</p>
<pre class="prettyprint">#define LATENCY		4.0	// milliseconds

function main()
{
	var StartTime = wdatef(TIMEFORMAT,"20161005 09:30:00"),
		EndTime = wdatef(TIMEFORMAT,"20161005 15:30:00");
	MaxBars = 200000;
	BarPeriod = 0.1/60.;	// 100 ms bars 
	Outlier = 1.002;

	assetList("HFT.csv");
	dataLoad(1,ES_HISTORY,2);
	dataLoad(2,SPY_HISTORY,2);
	int RowES=0, RowSPY=0;
	
	EntryDelay = LATENCY/1000.;
	Hedge = 2;
	Fill = 8; // HFT fill mode;
	Slippage = 0;
	Lots = 100;
		
	while(Bar &lt; MaxBars)
	{
		var TimeES = dataVar(1,RowES,0), 
			PriceES = dataVar(1,RowES,1),
			TimeSPY = dataVar(2,RowSPY,0),
			PriceSPY = dataVar(2,RowSPY,1);

		if(TimeES &lt; TimeSPY) RowES++;
		else RowSPY++;

		if(min(TimeES,TimeSPY) &lt; StartTime) continue;
		if(max(TimeES,TimeSPY) &gt; EndTime) break;

		if(TimeES &lt; TimeSPY) {
			asset("ES");
			priceQuote(TimeES,PriceES);
		} else {
			asset("SPY");
			priceQuote(TimeSPY,FACTOR*PriceSPY);
		}
		
		asset("ES");
		if(!AssetBar) continue;
		var AskES = AskPrice, BidES = AskPrice-Spread;
		asset("SPY");
		if(!AssetBar) continue;
		var AskSPY = AskPrice, BidSPY = AskPrice-Spread;

		int Order = tradeHFT(AskSPY,BidSPY,AskES,BidES);	
		switch(Order) {
			case 1: 
			asset("ES"); enterLong();
			asset("SPY"); enterShort();
			break;
		
			case 2: 
			asset("ES"); enterShort();
			asset("SPY"); enterLong();
			break;
		
			case 0:
			asset("ES"); exitLong(); exitShort();
			asset("SPY"); exitLong(); exitShort();
			break;
		}
	}
	printf("\nProfit %.2f at NY Time %s",
		Equity,strdate(TIMEFORMAT,dataVar(1,RowES,0)));
}
</pre>
<p>This script runs a HFT backtest of one trading day, from 9:30 until 15:30 New York time. We can see that it just calls the HFT function with the ES and SPY prices, then executes the returned code in the switch-case statement. It opens 100 units of each asset (equivalent to 2 ES and 1000 SPY contracts). The round-trip latency is set up with the <strong>EntryDelay</strong> variable. In HFT mode (<strong>Fill = 8</strong>), a trade is filled at the most recent price quote after the given delay. This ensures a realistic latency simulation when price quotes are entered with their exchange time stamps.</p>
<p>Here&#8217;s the HFT profit at the end of the day with different round-trip latency values:</p>
<table style="background-color: #e8e6e6;">
<tbody>
<tr>
<td><strong><span style="font-family: arial, helvetica, sans-serif;">LATENCY</span></strong></td>
<td><strong><span style="font-family: arial, helvetica, sans-serif;">0.5 ms</span></strong></td>
<td><strong><span style="font-family: arial, helvetica, sans-serif;">4.0 ms</span></strong></td>
<td><strong><span style="font-family: arial, helvetica, sans-serif;">6.0 ms</span></strong></td>
<td><strong><span style="font-family: arial, helvetica, sans-serif;">10&nbsp;ms</span></strong></td>
</tr>
<tr>
<td><strong><span style="font-family: arial, helvetica, sans-serif;">Profit / day</span></strong></td>
<td><strong><span style="font-family: arial, helvetica, sans-serif;">+ $793</span></strong></td>
<td><strong><span style="font-family: arial, helvetica, sans-serif;">+ $273</span></strong></td>
<td><strong><span style="font-family: arial, helvetica, sans-serif;">+ $205</span></strong></td>
<td><strong><span style="font-family: arial, helvetica, sans-serif;">&#8211; $15</span></strong></td>
</tr>
</tbody>
</table>
<p>The ES-SPY HFT arbitrage system makes about $800 each day with an unrealistic small latency of 500 microseconds. Unfortunately, due to the 700 miles between the NYSE and the CME, you&#8217;d need a time machine for that (or some faster-than-light method of quantum teleportation). A HFT server in Warren, Ohio, at 4 ms latency would generate about $300 per day. A server slightly off the direct NY-Chicago line can still grind out $200 daily. But the system deteriorates quickly when located further away, as with a server in Nashville, Tennessee, at around 10 ms latency. This is a strong hint that some high-speed systems in the proximity of both exchanges are already busy with exploiting ES-SPY arbitrage.</p>
<p>Still, $300 per day result in a decent $75,000 annual income. However, what needed you to invest for that, aside from hardware and software? With SPY at $250, the 100 units translate to 100*$2500 + 100*10*$250 = half a million dollars trade volume. So you would get only 15% annual return on your investment. But you can improve this by adding more pairs of NY ETFs and their equivalent CME futures to the arbitrage strategy. And you can improve it further when you find a broker or similar service that can receive your orders directly at the exchanges. Due to the ETF/future hedging the position is almost without risk. So you could probably negotiate a large leverage. And also a flat monthly fee, since broker commissions were not included in the test.</p>
<h3>Conclusion&nbsp;</h3>
<ul style="list-style-type: square;">
<li>When systems react fast enough, profits can be achieved with very primitive methods, such as arbitrage between highly correlated assets.</li>
<li>Location has a large impact on HFT profits.</li>
<li>ES-SPY arbitrage cannot be traded by everyone from everywhere. You&#8217;re competing with people that are doing this already. Possibly in Warren, Ohio.</li>
</ul>
<p>I&#8217;ve added the scripts and the asset list to the 2017 script archive in the &#8220;HFT&#8221; and &#8220;History&#8221; folders. Unfortunately I could not add the ES / SPY price history files, since I do not own the data. For reproducing the results, get BBO history from Nanex<img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2122.png" alt="™" class="wp-smiley" style="height: 1em; max-height: 1em;" /> or Nanotick<img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2122.png" alt="™" class="wp-smiley" style="height: 1em; max-height: 1em;" /> &#8211; their data can be read with the scripts above. You&#8217;ll also need Zorro S version 1.60 or above, which supports HFT fill mode.</p>
<h3>References</h3>
<p>(1)&nbsp;<a href="http://jaredbernsteinblog.com/trading-in-milliseconds-when-correlations-break-down/" target="_blank" rel="noopener">When Correlations Break Down</a> (Jared Bernstein 2014)</p>
<p>(2) <a href="http://www.automatedtrader.net" target="_blank" rel="noopener">Financial Programming Greatly Accelerated</a> (Cousin/Weston, Automated Trader 42/2017)</p>
<p>&nbsp;</p>
<hr>
<p><strong>Addendum.</strong> I have been asked how the profit would be affected when the server is located in New York, with 0.25 ms signal delay to the NYSE and 4.8 ms signal delay to the CME. For simulating this, modify the script:</p>
<pre class="prettyprint">var TimeES = dataVar(1,RowES,0)+4.8/(1000*60*60*24),
&nbsp; TimeSPY = dataVar(2,RowSPY,0)+0.25/(1000*60*60*24),
...
switch(Order) {
	case 1: 
		EntryDelay = 4.8/1000;
		asset("ES"); enterLong();
		EntryDelay = 0.25/1000;
		asset("SPY"); enterShort();
		break;
		
	case 2: 
		EntryDelay = 4.8/1000;
		asset("ES"); enterShort();
		EntryDelay = 0.25/1000;
		asset("SPY"); enterLong();
		break;
		
	case 0:
		EntryDelay = 4.8/1000;
		asset("ES"); exitLong(); exitShort();
		EntryDelay = 0.25/1000;
		asset("SPY"); exitLong(); exitShort();
		break;
}</pre>
<p>The first line simulates the price quote arrivals with 4.8 and 0.25 ms delay, the other lines establish the different order delays for SPY and ES. Alternatively, you could artificially delay the incoming SPY quotes by another 4.55 ms, so that their time stamps again match the ES quotes. In both cases the system returns about $240 per day, almost as much as in Warren. However a similar system located in Aurora, close to Chicago (swap the delays in the script), would produce zero profit. The asymmetry is caused by the relatively long constant periods of ES, making the SPY latency more relevant for the money at the end of the day.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/hacking-hft-systems/feed/</wfw:commentRss>
			<slash:comments>102</slash:comments>
		
		
			</item>
		<item>
		<title>Better Tests with Oversampling</title>
		<link>https://financial-hacker.com/better-tests-with-oversampling/</link>
					<comments>https://financial-hacker.com/better-tests-with-oversampling/#comments</comments>
		
		<dc:creator><![CDATA[jcl]]></dc:creator>
		<pubDate>Mon, 23 Nov 2015 14:14:52 +0000</pubDate>
				<category><![CDATA[Machine Learning]]></category>
		<category><![CDATA[System Development]]></category>
		<category><![CDATA[System Evaluation]]></category>
		<category><![CDATA[Price action]]></category>
		<category><![CDATA[Time series oversampling]]></category>
		<category><![CDATA[Walk forward analysis]]></category>
		<category><![CDATA[Zorro]]></category>
		<guid isPermaLink="false">http://www.financial-hacker.com/?p=1055</guid>

					<description><![CDATA[The more data you use for testing or training your strategy, the less bias will affect the test result and the more accurate will be the training. The problem: price data is always in short supply. Even shorter since you must put aside some part for out-of-sample tests. Extending the test or training period far &#8230; <a href="https://financial-hacker.com/better-tests-with-oversampling/" class="more-link">Continue reading<span class="screen-reader-text"> "Better Tests with Oversampling"</span></a>]]></description>
										<content:encoded><![CDATA[<p>The more data you use for testing or training your strategy, the less bias will affect the test result and the more accurate will be the training. The problem: <strong>price data is always in short supply</strong>. Even shorter since you must put aside some part for out-of-sample tests. Extending the test or training period far into the past is not always a solution. The markets of the 1990s or 1980s were very different from today, so their price data can cause misleading results.<br />
   In this article I&#8217;ll describe a simple method to <strong>produce more trades</strong> for testing and training from the same amount of price data. As a side effect, you&#8217;ll get an additional metric for the robustness of your strategy. <span id="more-1055"></span></p>
<p>The price curve is normally divided into equal sections, named <strong>bars</strong>. Any bar has an associated <strong>candle</strong> with an open, close, high, low, and average price which are used by the system for detecting patterns and generating trade signals. When the raw price data has a higher time resolution than a bar, which is normally the case, the candle prices are sampled like this:</p>
<p><span class="wp-katex-eq" data-display="false">Open ~=~ y_{t-dt} </span><br />
<span class="wp-katex-eq" data-display="false">High ~=~ max(y_{t-dt}~...~y_t)</span><br />
<span class="wp-katex-eq" data-display="false">Low ~=~ min(y_{t-dt}~...~y_t)</span><br />
<span class="wp-katex-eq" data-display="false">Close ~=~ y_t</span><br />
<span class="wp-katex-eq" data-display="false">Avg ~=~ 1/n \sum_{t-dt}^{t}{y_i}</span></p>
<p>where <em><strong>y<sub>t</sub></strong></em> is the raw price tick at time <em><strong>t</strong></em>, and <em><strong>dt</strong></em> is the bar period. If we now subdivide the bar period into <em><strong>m</strong></em> partitions and resample the bars with the time shifted by <em><strong>dt/m</strong></em>, we can produce <em><strong>m</strong></em> slightly different price curves from the same high resolution curve:</p>
<p><span class="wp-katex-eq" data-display="false">Open_j ~=~ y_{t-j/m dt-dt} </span><br />
<span class="wp-katex-eq" data-display="false">High_j ~=~ max(y_{t-j/m dt-dt}~...~y_{t-j/m dt})</span><br />
<span class="wp-katex-eq" data-display="false">Low_j ~=~ min(y_{t-j/m dt-dt}~...~y_{t-j/m dt})</span><br />
<span class="wp-katex-eq" data-display="false">Close_j ~=~ y_{t-j/m dt}</span><br />
<span class="wp-katex-eq" data-display="false">Avg_j ~=~ 1/n \sum_{t-j/m dt-dt}^{t-j/m dt}{y_i}</span></p>
<p><em><strong>m</strong></em> is the oversampling factor. The price curve index <em><strong>j</strong></em> runs from <em><strong>0</strong></em> to <em><strong>m-1</strong></em>. Any curve <em><strong>j</strong></em> has obviously the same properties as the original price curve, but has slightly different candles. Some candles can even be extremely different in volatile market situations. Testing a strategy will thus normally produce a different result on any curve.</p>
<p><strong>Time series oversampling</strong> can also can also be used with price-based bars, such as Renko Bars or Range Bars, although the above equations then change to the composition formula of the specific bar type. I found that the profit factors of strategies can differ by up to 30% between oversampled price curves. A large variance in results hints that something with the strategy may be wrong &#8211; maybe it&#8217;s too sensitive to randomness and thus subject to improvement. A strategy that produces large losses on some curves should better be discarded, even if the overall result is positive. But you can safely assume that live training results are best represented by the worst of all the oversampled price curves.</p>
<h3>Price action example</h3>
<p>Time series oversampling is supported by the <a href="http://www.financial-hacker.com/hackers-tools-zorro-and-r/">Zorro platform</a>. This allows us to quickly check its pros and cons with example strategies. We&#8217;ll look into a simple price action strategy with candle patterns. This is a strategy of the <a href="http://www.financial-hacker.com/build-better-strategies/">data mining flavor</a>. It is not based on a market model, since no good model can explain a predictive power of candle patterns (if you know one, please let me know too!). This trading method therefore has an irrational touch, although it&#8217;s said to have worked for Japanese rice traders 300 years ago, maybe due to trading habits or behavior patterns of large market participants. Whatever the reason: while trading the old rice candle patterns in today&#8217;s markets can not be really recommended, tests indeed hint at a <strong>weak and short-lived predictive power</strong> of 3-candle patterns in some currency pairs, such as EUR/USD. The emphasis is on short-lived: Trading habits change and thus predictive candle patterns expire within a few years, while new patterns emerge. </p>
<p>Here&#8217;s the Zorro script of such a strategy. In the training run it generates trading rules with 3-candle patterns that preceded profitable trades. In testing and live trading, a position is opened whenever the generated rule detects such a potentially profitable pattern. A walk forward test is used for curve fitting prevention, which is mandatory for all data mining systems:</p>
<p><!--?prettify linenums=true?--></p>
<pre class="prettyprint">function run()
{
  BarPeriod = 60; // 1-hour bars
  set(RULES+ALLCYCLES);
  NumYears = 10;
  NumWFOCycles = 10;

  if(Train) {
    Hedge = 2;	  // allow simultaneous long + short trades 
    Detrend = TRADES; // detrend on trade level
    MaxLong = MaxShort = 0; // no limit
  } else {
    Hedge = 1;	// long trade closes short and vice versa
    Detrend = 0;
    MaxLong = MaxShort = 1; // only 1 open position	
  }
	
  LifeTime = 3; // 3 hours trade time
  if(between(lhour(CET),9,13))  // European business hours
  {
    if(adviseLong(PATTERN+FAST+2,0, // train patterns with trade results
      priceHigh(2),priceLow(2),priceClose(2),
      priceHigh(1),priceLow(1),priceClose(1),
      priceHigh(1),priceLow(1),priceClose(1),
      priceHigh(0),priceLow(0),priceClose(0)) &gt; 50)
        enterLong();	
			
    if(adviseShort(PATTERN+FAST+2) &gt; 50)
      enterShort();
  }
}</pre>
<p>The core of the script is the adviseLong/adviseShort call, Zorro&#8217;s machine learning function (details are better explained in the <a href="http://manual.zorro-project.com/tutorial_pre.htm" target="_blank" rel="noopener noreferrer">Zorro tutorial</a>). The function is fed with patterns of 3 candles; the high, low, and close prices of adjacent candles are compared with each other (the open price is not used as it&#8217;s identical to the previous close in 24-hour traded assets). Training target is the return of a 3-hours trade after the appearance of a pattern. We&#8217;re using 3 hours trade time because the patterns consist of 3 bars, and it makes some sense to have a prediction horizon similar to the pattern length. Since we&#8217;re trading EUR/USD, we&#8217;re limiting the trades to European business hours. So the last trade must be entered at 13:00 for being closed at 16:00.</p>
<p>But when we train and test the above script with EUR/USD, we get no profitable strategy &#8211; at least not with realistic trading costs (an FXCM microlot account is simulated by default):</p>
<p><figure id="attachment_1139" aria-describedby="caption-attachment-1139" style="width: 879px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2015/11/priceaction2.png"><img decoding="async" class="wp-image-1139 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2015/11/priceaction2.png" alt="" width="879" height="321" srcset="https://financial-hacker.com/wp-content/uploads/2015/11/priceaction2.png 879w, https://financial-hacker.com/wp-content/uploads/2015/11/priceaction2-300x110.png 300w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></a><figcaption id="caption-attachment-1139" class="wp-caption-text">Price action without oversampling, P&amp;L curve</figcaption></figure></p>
<p>We can see that the script seems to enter trades mostly at random, so the equity drops continuously at about the rate of the trading costs. The script performs a 10-years walk-forward test in 10 cycles. The default training/test split is 85%, so the test time is about 9 months after a training time of 4 years. 4 years are roughly equivalent to 4*250*24 = 24000 patterns to check. That&#8217;s apparently not enough to significantly distinguish profitable from random patterns.</p>
<p>The problem: <strong>We can not simply extend the training time.</strong> When we do that, we&#8217;ll find that the result does not get better. The reason is the limited pattern lifetime. It makes no sense to train past the half-life of the found patterns. So this is not the solution. But what happens when we train and test the same strategy with 4-fold oversampling?<br />
<!--?prettify linenums=true?--></p>
<pre class="prettyprint">NumSampleCycles = 4;</pre>
<p>When we add this line to the script, the training process gets four times more patterns. Although many of them are similar, the amount of data is now enough to distinguish profitable from random patterns with some accuracy. We can see this in the now positive P&amp;L curve over the likewise extended test periods:</p>
<p><figure id="attachment_1117" aria-describedby="caption-attachment-1117" style="width: 879px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2015/11/priceaction.png"><img loading="lazy" decoding="async" class="wp-image-1117 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2015/11/priceaction.png" alt="" width="879" height="321" srcset="https://financial-hacker.com/wp-content/uploads/2015/11/priceaction.png 879w, https://financial-hacker.com/wp-content/uploads/2015/11/priceaction-300x110.png 300w" sizes="auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></a><figcaption id="caption-attachment-1117" class="wp-caption-text">Price action with 4-fold oversampling, P&amp;L curve</figcaption></figure></p>
<p>Also you&#8217;ll now get an additional section in the performance report, like this:</p>
<pre><code>Sample Cycles    Best    Worst    Avg  StdDev
Net Profit       5362$   4001$   4730$   4094$
Profit Factor     1.58    1.45    1.51    0.05
Num Trades        1256    1273    1240
Win Rate           41%     39%     40%</code></pre>
<p>Large deviations between the sample cycles will tell you that your strategy is unstable against random price curve fluctuations.</p>
<p>I&#8217;ve added the above script to the 2015 repository. But although it generates some profit, be aware that it&#8217;s for demonstration only and no &#8216;industrial quality&#8217;. Sharpe ratio and R2 are not good, drawdowns are long, and essential ingredients such as stops, trailing, money management, portfolio diversification, filters, and DMB measurement are not included. So better don&#8217;t trade it live.</p>
<h3>Conclusion</h3>
<p>Admittedly the price action system is a drastic and somewhat dubious example of the benefits of oversampling. But I found that 4-fold or 6-fold oversampling improves optimization and training of almost all strategies, and also increases the quality of backtests by making them less susceptible to extreme candles and outliers. </p>
<p>Oversampling is certainly not the one-fits-all solution. It will not work when the system relies on a specific time for opening and closing trades, as for gap trading or for systems based on daily bars. And it does not help either when single candles have little effect on the result, for instance when trade signals are generated from moving averages with very long time periods. But in most cases it noticeably improves testing and trading.</p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/better-tests-with-oversampling/feed/</wfw:commentRss>
			<slash:comments>13</slash:comments>
		
		
			</item>
		<item>
		<title>The Cold Blood Index</title>
		<link>https://financial-hacker.com/the-cold-blood-index/</link>
					<comments>https://financial-hacker.com/the-cold-blood-index/#comments</comments>
		
		<dc:creator><![CDATA[jcl]]></dc:creator>
		<pubDate>Mon, 26 Oct 2015 12:50:51 +0000</pubDate>
				<category><![CDATA[3 Most Useful]]></category>
		<category><![CDATA[Indicators]]></category>
		<category><![CDATA[System Evaluation]]></category>
		<category><![CDATA[Cold Blood Index]]></category>
		<category><![CDATA[Data mining bias]]></category>
		<category><![CDATA[Drawdown]]></category>
		<category><![CDATA[Grid trading]]></category>
		<category><![CDATA[Zorro]]></category>
		<guid isPermaLink="false">http://www.financial-hacker.com/?p=83</guid>

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