<?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 Development &#8211; The Financial Hacker</title>
	<atom:link href="https://financial-hacker.com/category/system-development/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 Development &#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>4</slash:comments>
		
		
			</item>
		<item>
		<title>A Better Stock Rotation System</title>
		<link>https://financial-hacker.com/a-better-stock-rotation-system/</link>
					<comments>https://financial-hacker.com/a-better-stock-rotation-system/#comments</comments>
		
		<dc:creator><![CDATA[Petra Volkova]]></dc:creator>
		<pubDate>Sun, 27 Jul 2025 14:26:47 +0000</pubDate>
				<category><![CDATA[Petra on Programming]]></category>
		<category><![CDATA[System Development]]></category>
		<category><![CDATA[Amibroker]]></category>
		<category><![CDATA[Portfolio rotation]]></category>
		<guid isPermaLink="false">https://financial-hacker.com/?p=4861</guid>

					<description><![CDATA[A stock rotation system is normally a safe haven, compared to other algorithmic systems. There’s no risk of losing all capital, and you can expect small but steady gains. The catch: Most of those systems, and also the ETFs derived from them, do not fare better than the stock index. Many fare even worse. But &#8230; <a href="https://financial-hacker.com/a-better-stock-rotation-system/" class="more-link">Continue reading<span class="screen-reader-text"> "A Better Stock Rotation System"</span></a>]]></description>
										<content:encoded><![CDATA[<p><em>A stock rotation system is normally a safe haven, compared to other algorithmic systems. There’s no risk of losing all capital, and you can expect small but steady gains. The catch: Most of those systems, and also the ETFs derived from them, do not fare better than the stock index. Many fare even worse. But how can you make sure that your rotation strategy beats the index? There is a way.</em></p>
<p><span id="more-4861"></span></p>
<p>In the TASC July 2026 issue, <strong>Markos Katsanos</strong> suggests a solution for a better stock rotation system. He applies two twists: Excluding the top performers, which often experience a reversal to the mean, and filtering out bear market situations. This improves stock rotation systems a lot.</p>
<p>The code of his system is unfortunately written in Amibroker languge, which means that for using it with any other platform, one must rewrite it from scratch. Amibroker does not use buy or sell orders. Instead it has a ‘portfolio rotation mode’ that is set up with many variables. Zorro’s C language has no special rotation mode, but uses buy and sell orders for rotating, just as for any other strategy. This requires rewriting the Amibroker code, but the positive side is that the script becomes much shorter and easier to comprehend.</p>
<pre class="prettyprint">var Score[1000],Weights[1000];

void run()
{
  StartDate = 2012;
  EndDate = 2025;
  BarPeriod = 1440;
  LookBack = 252; // 1 year

  Capital = slider(1,10000,0,20000,"Capital","");
  assetList("AssetsNASDAQ");
  assetAdd("QQQ","STOOQ:QQQ"); // for the bear market detection
  asset("QQQ");
// set up variables
  int MaxOpenPositions = 15;
  int ROCBars = 100;
  int ExitBars = 20;
  int MAPeriod = 300;
  int ExcludeTopN = 2;
// bear market filter
  var MAQQQ = ZMA(seriesC(),MAPeriod);
  bool Bear = MAQQQ &lt; ref(MAQQQ,1);
  if(Day%ExitBars == 0) {
// assign a score to any asset
     for(listed_assets) {
       asset(Asset);
       if(Asset == "QQQ" || Bear)
         Score[Itor] = 0; // don't trade the index
       else
         Score[Itor] = ROC(seriesC(),ROCBars);
     }
// exclude the N top scores
     int i;
     for(i=0; i&lt;ExcludeTopN; i++)
       Score[MaxIndex(Score,NumAssetsListed)] = 0;

// rotate the positions
    distribute(Weights,Score,NumAssetsListed,MaxOpenPositions,0.5);
     rotate(0); // decrease positions
     rotate(1); // increase positions
   }
}</pre>
<p>We’re loading all NASDAQ stocks from an asset list (<strong>AssetsNASDAQ</strong>), and add the <strong>‘QQQ’</strong> index ETF because we’re needing that for the bear market filter. The <strong>MAQQQ</strong> variable holds the average index value, determined with a zero-lag moving average (<strong>ZMA</strong>). The ZMA has two advantages over a standard moving average: faster reaction (as the name says) and not needing a long data history. We assume a bear market when the average is falling.</p>
<p>Next, we check if we have reached the rotation date (<strong>Day%ExitBars</strong> is the number of days since start modulo the number of days for a rotation). If so, we loop over all assets and assign every one a score, depending on its N-day rate of return (<strong>ROC</strong>). The <strong>Itor</strong> variable is the number of the asset in the loop. The QQQ index gets no score, and in a bear market none of them gets a score.</p>
<p>Next, we remove the two top performers, since we assume they are overbought. The <strong>distribute</strong> function takes the scores and converts them to weights, while all weights sum up to 1. The function can be looked up in the Zorro manual (<a href="https://zorro-project.com/manual/en/renorm.htm">https://zorro-project.com/manual/en/renorm.htm</a>). Finally we perform the actual rotation. This is a bit tricky, because we need two steps. The first step reduces all positions that ought to be reduced. The second step increases all positions that ought to be increased. This order is important, because if we increased a position first, the total volume could exceed our capital on the broker account.</p>
<p>The <strong>rotate</strong> function is not a Zorro function, but just assigns new position sizes to any asset of the portfolio:</p>
<pre class="prettyprint">void rotate(int Buy)
{
  for(listed_assets) {
    asset(Asset);
    int NewLots = Capital*Weights[Itor]/MarginCost;
    if(NewLots &lt; LotsPool)
      exitLong(0,0,LotsPool-NewLots);
    else if(Buy &amp;&amp; NewLots &gt; LotsPool)
      enterLong(NewLots-LotsPool);
  }
}</pre>
<p>Since I see from Markos Katsanos&#8217; code that he optimized his variables, I have to do the same. For this, his variables now get optimization ranges:</p>
<pre class="prettyprint">set(PARAMETERS); // parameter optimization
setf(TrainMode,TRADES|GENETIC); // size matters
int MaxOpenPositions = optimize(15,5,30,5);
int ROCBars = optimize(100,50,250,50);
int ExitBars = optimize(20,10,50,5);
int MAPeriod = optimize(300,100,1000,100);
int ExcludeTopN = optimize(2,1,5,1);</pre>
<p>We’re using genetic optimization with considering the trade volume (<strong>TRADES|GENETIC</strong>). The optimization takes about one minute. It’s in-sample, so take the result with a grain of salt. This is the equity curve resulting from a backtest:</p>
<p><img fetchpriority="high" decoding="async" width="768" height="465" class="wp-image-4862" src="https://financial-hacker.com/wp-content/uploads/2025/07/word-image-4861-1.png" srcset="https://financial-hacker.com/wp-content/uploads/2025/07/word-image-4861-1.png 768w, https://financial-hacker.com/wp-content/uploads/2025/07/word-image-4861-1-300x182.png 300w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px" /></p>
<p>In the backtest, we’re reinvesting profits; for this replace <strong>Capital</strong> with <strong>Equity</strong> in the <strong>rotate</strong> function. The blue bars are the account equity, the black line is the QQQ index. We can see that the account has far less drawdowns than the index. The black line in the small chart below is our trade volume, which is zero when a bear market is detected. The green line is the QQQ average, with bear market situations indicated in red. In 2022, the year when the Russian attack on Ukraine began, the system did not trade at all since the bear market filter was active almost the whole year.</p>
<p>The system produces 32% CAGR, with a 14% worst drawdown. This replicates Markos Katsanos’ results, but again, keep in mind that this is from an in-sample optimization. When applying walk-forward optimization (left as an exercise to the reader :), the CAGR goes down to 22%. Still a good performance, well beyond the NASDAQ index.</p>
<p>The code can be downloaded from the 2025 script repository.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/a-better-stock-rotation-system/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title>Trading the Channel</title>
		<link>https://financial-hacker.com/trading-the-channel/</link>
					<comments>https://financial-hacker.com/trading-the-channel/#comments</comments>
		
		<dc:creator><![CDATA[Petra Volkova]]></dc:creator>
		<pubDate>Wed, 09 Apr 2025 12:14:55 +0000</pubDate>
				<category><![CDATA[Indicators]]></category>
		<category><![CDATA[Petra on Programming]]></category>
		<category><![CDATA[System Development]]></category>
		<category><![CDATA[Channels]]></category>
		<category><![CDATA[Kaufman]]></category>
		<category><![CDATA[Momentum]]></category>
		<category><![CDATA[Regression]]></category>
		<guid isPermaLink="false">https://financial-hacker.com/?p=4834</guid>

					<description><![CDATA[One of the simplest form of trend trading opens positions when the price crosses its moving average, and closes or reverses them when the price crosses back. In the latest TASC issue, Perry Kaufman suggested an alternative. He is using a linear regression line with an upper and lower band for trend trading. Such a &#8230; <a href="https://financial-hacker.com/trading-the-channel/" class="more-link">Continue reading<span class="screen-reader-text"> "Trading the Channel"</span></a>]]></description>
										<content:encoded><![CDATA[<p><em>One of the simplest form of trend trading opens positions when the price crosses its moving average, and closes or reverses them when the price crosses back. In the latest TASC issue, <strong>Perry Kaufman</strong> suggested an alternative. He is using a linear regression line with an upper and lower band for trend trading. Such a band indicator can be used to trigger long or short positions when the price crosses the upper or lower band, or when it gets close.</em><span id="more-4834"></span></p>
<p>Let’s first code the bands. They are simply a regression line moved up or down so that it crosses through the highest and lowest price peaks and valleys. Here’s the piece of code in C:</p>
<pre class="prettyprint">var Slope = LinearRegSlope(seriesC(),N);
var Intercept = LinearRegIntercept(seriesC(),N);
var LinVal, HighDev = 0, LowDev = 0;
for(i=N; i&gt;0; i--) {
  LinVal = Intercept + Slope*(N-i);
  HighDev = max(HighDev,priceC(i)-LinVal);
  LowDev = min(LowDev,priceC(i)-LinVal);
}</pre>
<p><strong>N</strong> is the number of bars for which the regression line is calculated. The line has the formula <strong>y = b + m*x</strong>, where <strong>b</strong> is the intercept and <strong>m</strong> the slope. The code generates both for the previous <strong>N</strong> bars, then calculates in the loop the maximum and minimum deviations (<strong>HighDev</strong>, <strong>LowDev</strong>). The regression value (<strong>LinVal</strong>) is calculated from the intercept and slope with the above formula. Since the bar offset <strong>i</strong> runs backwards from the current bar, the bar number that’s multiplied with the slope runs from <strong>N</strong> down to <strong>0</strong>. Here’s the code applied to a SPY chart from 2025:</p>
<p><strong><img decoding="async" width="701" height="405" class="wp-image-4835" src="https://financial-hacker.com/wp-content/uploads/2025/04/ein-bild-das-diagramm-text-reihe-screenshot-en.png" srcset="https://financial-hacker.com/wp-content/uploads/2025/04/ein-bild-das-diagramm-text-reihe-screenshot-en.png 701w, https://financial-hacker.com/wp-content/uploads/2025/04/ein-bild-das-diagramm-text-reihe-screenshot-en-300x173.png 300w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px" /></strong></p>
<p>The candles can exceed the upper and lower bands because only the close price is used for the bands. It would probably improve the system, at least in theory, when we used the high and low prices instead.</p>
<p>Kaufman suggests several methods of trading with these bands; we’re here using the ‘Inside Channel’ method since it is, according to Kaufman, the most profitable. We open a long position when the price comes within a zone around the lower band, and close the position (or open a short position) when the price comes within a zone around the upper band. Here’s the complete trading system in C for Zorro, using the above code to calculate the bands.</p>
<pre class="prettyprint">void run()
{
  BarPeriod = 1440;
  StartDate = 20100101;
  LookBack = 150;
  assetList("AssetsIB");
  asset("SPY");
  if(is(LOOKBACK)) return;

  int i, N = 40;
  var Factor = 0.2;
  var Slope = LinearRegSlope(seriesC(),N);
  var Intercept = LinearRegIntercept(seriesC(),N);
  var LinVal, HighDev = 0, LowDev = 0;
  for(i=N; i&gt;0; i--) {
    LinVal = Intercept + Slope*(N-i);
    HighDev = max(HighDev,priceC(i)-LinVal);
    LowDev = min(LowDev,priceC(i)-LinVal);
  }
  var Zone = Factor*(HighDev+LowDev);
  if(!NumOpenLong &amp;&amp; priceC(0) &lt; LinVal+LowDev+Zone)
    enterLong();
  if(!NumOpenShort &amp;&amp; priceC(0) &gt; LinVal+HighDev-Zone)
    exitLong();
}</pre>
<p>We’ve selected the <strong>AssetsIB</strong> asset list, which contains the margins, commissions and other parameters from an US broker (IBKR). So the backtest simulates trading with IBKR. The resulting equity curve with the default parameters, <strong>N = 40</strong> and <strong>Zone Factor = 20%</strong>, already shows promise with a 2.8 profit factor:</p>
<p><img decoding="async" width="701" height="405" class="wp-image-4836" src="https://financial-hacker.com/wp-content/uploads/2025/04/word-image-4834-2.png" srcset="https://financial-hacker.com/wp-content/uploads/2025/04/word-image-4834-2.png 701w, https://financial-hacker.com/wp-content/uploads/2025/04/word-image-4834-2-300x173.png 300w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px" /></p>
<p>However, Kaufman mentioned that he tested <strong>N</strong> values from 20 to 150, and zone factors from 5% to 50%. We’ll do the same by optimizing these parameters. Of course a backtest with the best optimization result would be meaningless due to bias (see <a href="https://zorro-project.com/backtest.php">https://zorro-project.com/backtest.php</a>). Therefore we’re using <strong>walk forward optimization</strong> for an out-of-sample backtest. Since anything with Zorro is done in code, we’ll insert 4 lines of C code for the optimization:</p>
<pre class="prettyprint">set(PARAMETERS); // optimize parameters
NumWFOCycles = 5;
N = optimize(40,20,150,10);
Factor = optimize(0.2,0.05,0.5,0.05);</pre>
<p>We also changed the trading from only long positions to long and short by replacing <strong>exitLong</strong> with <strong>enterShort</strong> By default, entering a short position automatically exits the long one, and vice versa. So the system is always in the market with 1 share, either long or short. The walk forward optimization takes about 3 seconds. This is the resulting equity curve:</p>
<p><img loading="lazy" decoding="async" width="701" height="405" class="wp-image-4837" src="https://financial-hacker.com/wp-content/uploads/2025/04/word-image-4834-3.png" srcset="https://financial-hacker.com/wp-content/uploads/2025/04/word-image-4834-3.png 701w, https://financial-hacker.com/wp-content/uploads/2025/04/word-image-4834-3-300x173.png 300w" sizes="auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px" /></p>
<p>The profit factor rose to 7, with a 76% win rate &#8211; not bad for such a simple system. The code can be downloaded from the 2025 script repository on <a href="https://financial-hacker.com">https://financial-hacker.com</a>. </p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/trading-the-channel/feed/</wfw:commentRss>
			<slash:comments>8</slash:comments>
		
		
			</item>
		<item>
		<title>Ehlers Loops</title>
		<link>https://financial-hacker.com/ehlers-loops/</link>
					<comments>https://financial-hacker.com/ehlers-loops/#comments</comments>
		
		<dc:creator><![CDATA[Petra Volkova]]></dc:creator>
		<pubDate>Fri, 10 Jun 2022 17:08:46 +0000</pubDate>
				<category><![CDATA[Petra on Programming]]></category>
		<category><![CDATA[System Development]]></category>
		<category><![CDATA[Ehlers]]></category>
		<category><![CDATA[Pairs rotation]]></category>
		<category><![CDATA[SPY]]></category>
		<guid isPermaLink="false">https://financial-hacker.com/?p=4448</guid>

					<description><![CDATA[Price charts normally display price over time. Or in some special cases price over ranges or momentum. In his TASC articles in June and July 2022, John Ehlers proposed a different way of charting. The relation of two parameters, like price over momentum, or price A over price B, is displayed as a 2D curve &#8230; <a href="https://financial-hacker.com/ehlers-loops/" class="more-link">Continue reading<span class="screen-reader-text"> "Ehlers Loops"</span></a>]]></description>
										<content:encoded><![CDATA[<p><em>Price charts normally display price over time. Or in some special cases price over ranges or momentum. In his TASC articles in June and July 2022, John Ehlers proposed a different way of charting. The relation of two parameters, like price over momentum, or price A over price B, is displayed as a 2D curve in a scatter plot. The resulting closed or open loop is supposed to predict the future price development. Of course only if interpreted in the right way.</em></p>
<p><span id="more-4448"></span></p>
<p>For his loop chart, Ehlers filtered the low and high frequencies out of the two data series with a roofing filter. Its code in C for Zorro:</p>
<pre class="prettyprint">var Roofing(var *Data,int HPeriod,int LPeriod)
{
  var f = 1.414*PI/HPeriod;
  var hpa1 = exp(-f);
  var hpc2 = 2*hpa1*cos(f/2);
  var hpc3 = -hpa1*hpa1;
  var hpc1 = (1 + hpc2 - hpc3) / 4;
  f = 1.414*PI/LPeriod;
  var ssa1 = exp(-f);
  var ssc2 = 2*ssa1*cos(f/2);
  var ssc3 = -ssa1*ssa1;
  var ssc1 = 1 - ssc2 - ssc3;

  vars HP = series(0,3);
  HP[0] = hpc1*(Data[0] - 2*Data[1] + Data[2]) + hpc2*HP[1] + hpc3*HP[2];
  vars SS = series(HP[0],3);
  SS[0] = ssc1*(HP[0] + HP[1])/2 + ssc2*SS[1] + ssc3*SS[2];
  var Scaled = EMA(SS[0]*SS[0],.0242);
  return SS[0]/sqrt(Scaled);
}</pre>
<p>For demonstration purposes we apply that filter to the FDX price and volume series. The filtered data points serve as XY coordinates for our curve – the &#8220;Ehlers Loop&#8221;. The following script reads 3 months stock data from an online source and displays it in a scatter plot where the XY points are connected with splines:</p>
<pre class="prettyprint">function run()
{
  StartDate = ymd(wdate(NOW)-90); // 90 days before today
  BarPeriod = 1440;
  asset("FDX");
  var PriceRMS = Roofing(seriesC(),125,20);
  var VolRMS = Roofing(series(marketVol()),125,20);

  if(is(LOOKBACK)) return; // don't plot the lookback period
  plotGraph("Loop",VolRMS,PriceRMS,DOT|GRAPH,BLUE);
  plotGraph("Loops",VolRMS,PriceRMS,SPLINE|GRAPH,TRANSP|GREY);
  if(is(EXITRUN))
    plotGraph("Last",VolRMS,PriceRMS,SQUARE|GRAPH,RED);
}</pre>
<p>The plotGraph function is used to display each coordinate with a blue dot. The last day is marked with a red square.</p>
<p><img decoding="async" src="https://financial-hacker.com/wp-content/uploads/2022/06/061022_1641_EhlersLoops1.png" alt="" /></p>
<p>Ehlers intended his loops for discretionary trading, but it can of course also be automated. For instance, the last N coordinates could be used as inputs for Zorro&#8217;s neural net, which can then be trained to predict tomorrow&#8217;s price. Or even simpler, the slope of a polynomial regression through the last points could trigger a buy order when positive, or a sell order when negative. You can use Zorro&#8217;s polyfit function and filter with the regression error. I leave this trading system as an exercise to the reader.</p>
<p>The same method can be used to compare a stock with an index for a pairs rotation strategy. We&#8217;re using RTX and SPY:</p>
<pre class="prettyprint">function run()
{
  BarPeriod = 1440;
  LookBack = 300;
  StartDate = 20210801;
  EndDate = 20211130;

  assetAdd("RTX","YAHOO:*");
  asset("RTX");
  var Y = Roofing(seriesC(),125,20);
  assetAdd("SPY","YAHOO:*");
  asset("SPY");
  var X = Roofing(seriesC(),125,20);

  if(is(LOOKBACK)) return;
  plotGraph("Loop",X,Y,DOT|GRAPH,BLUE);
  plotGraph("Loops",X,Y,SPLINE|GRAPH,TRANSP|GREY);
  if(is(EXITRUN))
    plotGraph("Last",X,Y,SQUARE|GRAPH,RED);
}</pre>
<p>The resulting chart for the period from August to November 2021:</p>
<p><img decoding="async" src="https://financial-hacker.com/wp-content/uploads/2022/06/061022_1641_EhlersLoops2.png" alt="" /></p>
<p>I found that Ehlers&#8217; roofing filter is sensitive to the lookback period. It needs some time to &#8216;swing in&#8217;. A too short lookback period, like Zorro&#8217;s default 80 bars, produces a visibly different loop. With 300 bars we&#8217;re on the safe side.</p>
<p>What can you now do with this loop chart? Ehlers proposed to switch between stock and index depending on the loop rotation and angle quadrant. Our example above, with the SPY index on the Y axis and the stock on the X axis, has a clockwise rotating loop. On clockwise rotations, buy the stock and sell the index from 5 to 10 on the clock face. Sell the stock and buy the index from 11 to 4. Be out of the market between 4 and 5, and have both positions between 10 and 11.<img loading="lazy" decoding="async" class="aligncenter" src="https://financial-hacker.com/wp-content/uploads/2022/06/Clock.png" alt="" width="211" height="198" /></p>
<p>On a counter clockwise rotation, do just the opposite. This was intended for discretionary trading, but can of course be automated with a script. Which I also gracefully leave to the reader.</p>
<p>The Roofing indicator and the Ehlers Loop scripts can be downloaded from the 2022 script repository. The Zorro software can be downloaded from <a href="https://zorro-project.com/download.php">https://zorro-project.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/ehlers-loops/feed/</wfw:commentRss>
			<slash:comments>4</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>Buy&#038;Hold? No, Buy&#038;Sell!</title>
		<link>https://financial-hacker.com/buyhold-no-buysell/</link>
					<comments>https://financial-hacker.com/buyhold-no-buysell/#comments</comments>
		
		<dc:creator><![CDATA[Petra Volkova]]></dc:creator>
		<pubDate>Sat, 19 Jun 2021 08:58:28 +0000</pubDate>
				<category><![CDATA[Indicators]]></category>
		<category><![CDATA[Petra on Programming]]></category>
		<category><![CDATA[System Development]]></category>
		<guid isPermaLink="false">https://financial-hacker.com/?p=4121</guid>

					<description><![CDATA[There&#8217;s no doubt that buying and holding index ETFs is a long-term profitable strategy. But it has two problems. It does not reinvest profits, so the capital grows only linearly, not exponentially. And it exposes the capital to the full rollercoaster market risk. A sure way to go out of the market in a downtrend, &#8230; <a href="https://financial-hacker.com/buyhold-no-buysell/" class="more-link">Continue reading<span class="screen-reader-text"> "Buy&#38;Hold? No, Buy&#38;Sell!"</span></a>]]></description>
										<content:encoded><![CDATA[<p><em>There&#8217;s no doubt that buying and holding index ETFs is a long-term profitable strategy. But it has two problems. It does not reinvest profits, so the capital grows only linearly, not exponentially. And it exposes the capital to the full rollercoaster market risk. A sure way to go out of the market in a downtrend, and invest the profits back in an uptrend would be (almost) priceless. Markos Katsanos promises no less in his Stocks&amp;Commodities July 2021 article. Does this really work?</em><span id="more-4121"></span></p>
<p>Markos&#8217; buy-and-sell system with stock index ETFs uses the VIX as a volatility indicator for predicting downtrends. The system is a veritable indicator soup, with no less than 15 separate buy and sell conditions. Fortunately his published Amibroker code can be easily converted to C:</p>
<pre class="prettyprint">function run()<br />{<br /> StartDate = 20101231;<br /> EndDate = 20201231;<br /> BarPeriod = 7*1440; // 1 week<br /> LookBack = 100;<br /> Capital = 100000;<br /><br /> set(PARAMETERS,TESTNOW,PLOTNOW);<br /> setf(PlotMode,PL_ALL);<br /> PlotScale = 8;<br /><br /> asset("VIX");<br /> var VIXdn = (price(0)/HH(15,1)-1)*100;<br /> var VIXup = (price(0)/LL(15,1)-1)*100;<br /> var VIXDnMin = -optimize(30,20,40,10);<br /> var VIXUpMin = optimize(100,80,120,10);<br /> plot("VIX",price(0),NEW,BLACK);<br /><br /> asset("SPY"); // use unadjusted data!<br /> vars SPYs = series(priceClose());<br /> vars Vols = series(marketVol());<br /> vars ATRs = series(ATR(2));<br /> var ATRDn = (ATRs[0]/MaxVal(ATRs+1,15)-1)*100;<br /> var ATRUp = (ATRs[0]/MinVal(ATRs+1,15)-1)*100;<br /> var Up = (HH(2)/LL(4)-1)*100;<br /> var Dn = (LL(2)/HH(4)-1)*100;<br /> var Cch = (LL(10)/HH(100)-1)*100;<br /> vars StochK14s = series(StochK(14,3));<br /> vars StochK40s = series(StochK(40,3));<br /> int HHb = MaxIndex(SPYs,4);<br /> int LLb = MinIndex(SPYs,4);<br /> var VOLUp = (Vols[0]/SMA(Vols+HHb+1,50)-1)*100;<br /> plot("Volume",Vols,BARS|NEW,BLUE);<br /> plot("ATR(2)",ATRs,LINE|NEW,RED);<br /><br /> int BuyInitial = once(<br />   SMA(SPYs,10) &gt;= SMA(SPYs+1,10) &amp;&amp; <br />   StochK(3,3) &lt; 40);<br /> int Buy = Up &gt; 6 &amp;&amp;<br />   (VIXdn &lt; VIXDnMin or ATRDn &lt; 2*VIXDnMin) &amp;&amp;<br />   Cch &lt; -15 &amp;&amp;<br />   MinVal(StochK40s,LLb) &lt; 25 &amp;&amp;<br />   MinVal(StochK14s,LLb) &lt; 25 &amp;&amp;<br />   StochK14s[0] &gt;= StochK40s[0];<br /> int Sell = SPYs[0] &lt; SMA(SPYs,20) &amp;&amp;<br />   Dn &lt; -6 &amp;&amp;<br />   (VIXup &gt; VIXUpMin or ATRUp &gt; 2*VIXUpMin) &amp;&amp;<br />   VOLUp &gt; 80 &amp;&amp;<br />   MaxVal(StochK40s,HHb) &gt; 85 &amp;&amp;<br />   MaxVal(StochK14s,HHb) &gt; 85 &amp;&amp;<br />   StochK14s[0] &lt;= StochK40s[0];<br /><br /> MaxLong = 1;<br /> Leverage = 1;<br /> Margin = Equity; // invest all money<br /> Fill = 3; // enter/exit at next open<br /> if(Sell)<br />   exitLong();<br /> else if(BuyInitial or Buy)<br />   enterLong();<br /><br />//plot tiny triangles for the buy and sell signals<br /> if(Buy) <br />   plot("Buy",0.9*priceLow(),MAIN|TRIANGLE,GREEN);<br /> if(Sell) <br />   plot("Sell",1.1*priceHigh(),MAIN|TRIANGLE4,RED);<br /><br />}<span style="color: #2e74b5;"><br /></span></pre>
<p>The initial buy condition is required, otherwise the system would stay out of the market for a long time. The <strong>once(..)</strong> function is used for such initializations; it returns <strong>true</strong> when its argument becomes nonzero the first time during a backtest or trading session, and <strong>false</strong> afterwards. On the chart we can see that the system is indeed only in the market when SPY rises. The blue line is the VIX, the tiny red and green triangles are the buy and sell signals. They appear well placed.</p>
<p><img decoding="async" src="https://financial-hacker.com/wp-content/uploads/2021/06/061921_0830_BuyHoldBuyS1.png" alt="" /></p>
<p>This is, of course, an in-sample result. With only 3 trades, WFO would be futile. For your experiments please be aware that the above setup, derived from the author&#8217;s Amibroker code, must be exactly reproduced for getting the same result. Tiny deviations, such as using adjusted instead of unadjusted SPY data, shifting the bars by a few hours, or using a slightly different Stoch variant, produced a quite different backtest. This hints that there&#8217;s still room for improvement with this strategy, to say the least. The above code is more an exercise in 1:1 conversion from another platform, than a real trading system. But the basic idea of buy-and-sell is certainly good, as is the idea to use the VIX for downtrend detection. Maybe someone can come up with a more simple and more serious variant of this system that would be suited for a WFO test.</p>
<p>The Buy-Sell system can be downloaded from the 2021 script repository. Since it is based on 1-week bars and uses market volume, you need Zorro S 2.38 or above to run it. Or even better, convert it to daily bars. Personally I see no reason for using 1-week bars in any trading system.</p>

]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/buyhold-no-buysell/feed/</wfw:commentRss>
			<slash:comments>8</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>The Mechanical Turk</title>
		<link>https://financial-hacker.com/the-mechanical-turk/</link>
					<comments>https://financial-hacker.com/the-mechanical-turk/#comments</comments>
		
		<dc:creator><![CDATA[jcl]]></dc:creator>
		<pubDate>Mon, 10 Dec 2018 07:17:27 +0000</pubDate>
				<category><![CDATA[Indicators]]></category>
		<category><![CDATA[System Development]]></category>
		<category><![CDATA[Options]]></category>
		<category><![CDATA[Price distribution]]></category>
		<category><![CDATA[Sentiment]]></category>
		<category><![CDATA[SPY]]></category>
		<guid isPermaLink="false">http://www.financial-hacker.com/?p=2974</guid>

					<description><![CDATA[We can see thinking machines taking over more and more human tasks, such as car driving, Go playing, or financial trading. But sometimes it&#8217;s the other way around: humans take over jobs supposedly assigned to thinking machines. Such a job is commonly referred to as a Mechanical Turk in reminiscence to Kempelen&#8217;s famous chess machine &#8230; <a href="https://financial-hacker.com/the-mechanical-turk/" class="more-link">Continue reading<span class="screen-reader-text"> "The Mechanical Turk"</span></a>]]></description>
										<content:encoded><![CDATA[<p>We can see thinking machines taking over more and more human tasks, such as car driving, Go playing, or financial trading. But sometimes it&#8217;s the other way around: humans take over jobs supposedly assigned to thinking machines. Such a job is commonly referred to as a <strong>Mechanical Turk</strong> in reminiscence to Kempelen&#8217;s famous chess machine from 1768. In our case, a Mechanical Turk is an automated trading algorithm based on human intelligence. <span id="more-2974"></span></p>
<p>Theoretically, many trend trading systems would fall in the Turk category since they are based on following the herd. But here we&#8217;re not looking into trader&#8217;s opinions of the current market situation, but into their expectations of the future. There are several methods. The most usual is evaluating the <strong>Commitment Of Traders</strong> report, for which many platforms, also Zorro, provide easy-to-use indicators. But some publications (1) found that using the COT report for predicting the markets produces mixed results at best.</p>
<p>There&#8217;s a more precise way to get the market&#8217;s expectations. It&#8217;s using <a href="https://www.financial-hacker.com/algorithmic-options-trading/">options premiums</a>. If an option expires in 6 weeks, its current premium reflects what option buyers or sellers think about the underlying price in 6 weeks.</p>
<h3>The price probability distribution</h3>
<p>For deriving the expected underlying price at expiration, we have to take all strike prices in consideration. Here&#8217;s the algorithm in short (I haven&#8217;t invented it, a longer description can be found in (2)). Assume SPY is currently trading at $200. For getting the probability that it will rise to between 210 and 220 in 6 weeks, we&#8217;re looking into the 210 call and the 220 call. Suppose they trade at $14 and $10. So we can buy the 210 call and sell the 220 call and pay $4 difference. If at expiration SPY is under 210, both contracts have no worth and we lose the 4 dollars. If it is above 220, our gain is the $10 strike difference minus $4 premium difference, so we win 6 dollars. If it is between 210 and 220, our win or loss is also inbetween, with an average of (-4+6)/2 = 1 dollar.</p>
<p>When options prices are &#8220;fair&#8221;, i.e. solely determined by probabilities where the price will end up at expiration, summing up over all possible outcomes yields zero profit or loss. So</p>
<p><em><strong>-$4×L + $1×M + $6×H = $0</strong></em></p>
<p>where <em><strong>L</strong></em> is the probability for SPY to end up below 210, <em><strong>M</strong></em> is the probability that it will be between 210 and 220, and <em><strong>H</strong></em> is the probability that it will be above 220. Since one of these three alternatives will always happen, the sum of all 3 probabilities must be 1:</p>
<p><em><strong>L + M + H = 1</strong></em></p>
<p>Now let&#8217;s assume that we know <em><strong>L</strong></em> already. Then we have two equations with two unknowns, which are easily solved:</p>
<p><em><strong>5L &#8211; 5H = 1    =&gt;  H = L &#8211; 0.2<br />
10L + 5M = 6    =&gt;  M = 1.2 -2L </strong></em></p>
<p>Assuming <em><strong>L = 50%</strong></em> yields <em><strong>H = 30%</strong></em> and <em><strong>M = 20%</strong></em>.</p>
<p>How do we now get the value <em><strong>L</strong></em>? We simply take the lowest strike offered at the market, and assume that the probability of SPY ending up even below that is zero. So our first <em><strong>L</strong></em> is 0.  We can now use the method above to calculate the <em><strong>M</strong></em> belonging to the interval between the lowest and the second-lowest strike. That <em><strong>M</strong></em> is then added to <em><strong>L</strong></em> since it&#8217;s the probability of SPY ending up at or below the second-lowest strike. We continue that process with the next interval, getting a specific <em><strong>L</strong></em> and <em><strong>M</strong></em> for any interval until we arrived at the highest strike. If the traders have been consistent with their assumptions, the final <em><strong>L</strong></em> &#8211; the sum over all <em><strong>M</strong></em>s &#8211; should be now at or close to 1.</p>
<p>Here&#8217;s a small script that displays the 6-weeks <em><strong>L</strong></em> distribution of SPY:</p>
<pre class="prettyprint">void main() 
{
	StartDate = 20170601;
	LookBack = 0;

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

// load today's contract chain
	contractUpdate(0,0,CALL|PUT);
	printf("\n%i contracts",NumContracts);
	if(!NumContracts) return;

// get underlying price
	var Price,Current = priceClose(0);
	printf("\nCurrent price %.2f",Current);

// plot CPD histogram	
	contractCPD(45);	// 6 weeks
	int N = 0;
	for(Price = 0.75*Current; Price &lt; 1.25*Current; N++, Price += 0.01*Current)
		plotBar("CPD",N,floor(Price),cpd(Price),BARS|LBL2,RED);
	printf("\nExpected price %.2f",cpdv(50));

// compare with real future price
	set(PEEK);
	N = timeOffset(UTC,-45,0,0);
	printf("\nFuture price %.2f",priceClose(N));
}</pre>
<p>The result is a histogram of expected probabilities, in percent, that the SPY price will be at or below the price at the x-axis six weeks in the future. I&#8217;ve marked the current price with a black bar.</p>
<p><figure id="attachment_3093" aria-describedby="caption-attachment-3093" style="width: 699px" class="wp-caption alignnone"><a href="https://www.financial-hacker.com/wp-content/uploads/2019/06/cpd.png"><img loading="lazy" decoding="async" class="wp-image-3093 size-full" src="https://www.financial-hacker.com/wp-content/uploads/2019/06/cpd.png" alt="" width="699" height="284" srcset="https://financial-hacker.com/wp-content/uploads/2019/06/cpd.png 699w, https://financial-hacker.com/wp-content/uploads/2019/06/cpd-300x122.png 300w" sizes="auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px" /></a><figcaption id="caption-attachment-3093" class="wp-caption-text">SPY 6 weeks price probability distribution at June 1, 2017</figcaption></figure></p>
<p>The <strong><a href="https://manual.zorro-project.com/contractcpd.htm">contractCPD</a></strong> function generates the distribution with the above described algorithm, <strong>cpd</strong> returns the accumulated probability <em><strong>L</strong></em> (in percent) at a given price, and <strong>cpdv</strong> returns the price at a given <em><strong>L</strong></em>. Therefore, <strong>cpdv(50)</strong> is the median of market expectations. In our case, at June 1 2017, a modest price increase to $245 was expected for mid-July (in fact the price ended up at $245.51, but don&#8217;t get too excited &#8211; often trader&#8217;s expectations fall short by several dollars). We can also see an unusal step at the begin of the histogram: about 10% of traders expected a strong drop to below $200. Maybe Trump twittered something that morning.</p>
<h3>The strategy</h3>
<p>We will now exploit trader&#8217;s expectations for a strategy, and this way check if they have any merit. This is our mechanical Turk:</p>
<pre class="prettyprint">#define Sentiment AssetVar[0]

void run() 
{
	StartDate = 20120102;
	EndDate = 20171231;
	BarPeriod = 1440;

	assetList("AssetsIB");
	asset("SPY");
	MaxLong = MaxShort = 1;

// load today's contract chain
	contractUpdate(0,0,CALL|PUT);
	int N = contractCPD(45);

// increase/decrease market sentiment 
	if(N) {
		var Expect = cpdv(50) - priceClose();
		if(Expect &lt; 0)
			Sentiment = min(Expect,Sentiment+Expect);
		else
			Sentiment = max(Expect,Sentiment+Expect);
	}
	if(Sentiment &gt; 5)
		enterLong();
	else if(Sentiment &lt; -5)
		enterShort();
	
	plot("Sentiment",Sentiment,NEW,RED);
}</pre>
<p>We&#8217;re checking the market expectation every day and add it to a previously defined <strong>Sentiment</strong> asset variable. If the expectation changes sign, so does <strong>Sentiment</strong>. If its amount has accumulated above 5, we buy a long or short position dependent on its sign. This way we&#8217;re only considering the market expectation when it&#8217;s either relatively strong or had the same direction for several days.</p>
<p>The result:</p>
<p><a href="https://www.financial-hacker.com/wp-content/uploads/2019/06/turkresult.png"><img loading="lazy" decoding="async" class="alignnone wp-image-3098 size-full" src="https://www.financial-hacker.com/wp-content/uploads/2019/06/turkresult.png" alt="" width="699" height="359" srcset="https://financial-hacker.com/wp-content/uploads/2019/06/turkresult.png 699w, https://financial-hacker.com/wp-content/uploads/2019/06/turkresult-300x154.png 300w" sizes="auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px" /></a></p>
<p>The system produces about 20% annual return with profit factor 3. The red line in the lower chart is the Sentiment variable &#8211; we can see that it often steadily increases, but sometimes also decreases. Since SPY normally rises, shorting it with this strategy produces less profit than going long, but is still slightly profitable with an 1.16 profit factor.</p>
<h3>Predicting the price in 6 weeks</h3>
<p>What are traders currently thinking of the SPY price in 6 weeks? For this, the first script above needs just be slightly modified so that it does not use historical options data, but connects to IB and downloads the current options chain with all prices:</p>
<pre class="prettyprint">void main() 
{
	StartDate = NOW;
	LookBack = 0;

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

// load today's contract chain
	contractUpdate(0,0,CALL|PUT);
	printf("\n%i contracts",NumContracts);
	if(!NumContracts) return;

// get underlying price
	var Price,Current = priceClose(0);
	printf("\nCurrent price %.2f",Current);

// plot CPD histogram
	printf("\nWait time approx %i minutes",1+NumContracts/200);
	contractCPD(45);
	int N = 0;
	for(Price = 0.75*Current; Price &lt; 1.25*Current; N++, Price += 0.01*Current)
		plotBar("CPD",N,floor(Price),cpd(Price),BARS|LBL2,RED);
	printf("\nExpected price %.2f",cpdv(50));
}</pre>
<p>This script must be started in Zorro&#8217;s Trade mode with the IB plugin selected. Mind the displayed &#8220;wait time&#8221;. IB sometimes needs up to ten seconds for returning the price of an option contract, so downloading the prices of all 6-weeks contracts can take half an hour or more.</p>
<p>I ran that script today (Dec 11 2018) and the option traders expect the SPY price to rise to $269 in six weeks. So I&#8217;ll check the price by the end of January and post here if they have been right.</p>
<p>I&#8217;ve added the scripts to the 218 repository. You&#8217;ll need Zorro 1.99 or above, and SPY options EOD history for the first 2 scripts. You&#8217;ll really have to buy it this time, the free <a href="https://www.financial-hacker.com/algorithmic-options-trading/">artificial options history</a> won&#8217;t do for market sentiment.</p>
<h3>Conclusions</h3>
<ul>
<li>A Turk can beat a thinking machine.</li>
<li>Option traders tend to underestimate future price changes.</li>
<li>But they are often right in the price direction.</li>
</ul>
<h3>Literature</h3>
<p>(1) <a href="http://www.farmdoc.illinois.edu/nccc134/conf_2007/pdf/confp01-07.pdf">Sanders, Irwin, Merrin: Smart Money? The Forecasting Ability of CFTC Large Traders (2007)</a></p>
<p>(2) <a href="https://www.globalcapital.com/article/k6543wh6f19l/option-prices-imply-a-probability-distribution">Pat Neal, Option Prices Imply a Probability Distribution</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/the-mechanical-turk/feed/</wfw:commentRss>
			<slash:comments>39</slash:comments>
		
		
			</item>
		<item>
		<title>Deep Learning Systems for Bitcoin 1</title>
		<link>https://financial-hacker.com/deep-learning-systems-for-bitcoins-part-1/</link>
					<comments>https://financial-hacker.com/deep-learning-systems-for-bitcoins-part-1/#comments</comments>
		
		<dc:creator><![CDATA[jcl]]></dc:creator>
		<pubDate>Wed, 27 Dec 2017 10:51:27 +0000</pubDate>
				<category><![CDATA[Machine Learning]]></category>
		<category><![CDATA[No Math]]></category>
		<category><![CDATA[System Development]]></category>
		<category><![CDATA[Autoencoder]]></category>
		<category><![CDATA[Bitcoin]]></category>
		<category><![CDATA[Blockchain]]></category>
		<category><![CDATA[Boltzmann machine]]></category>
		<category><![CDATA[Cryptocurrency]]></category>
		<category><![CDATA[Deepnet]]></category>
		<category><![CDATA[Economy]]></category>
		<category><![CDATA[H2O]]></category>
		<category><![CDATA[Keras]]></category>
		<category><![CDATA[Money]]></category>
		<category><![CDATA[MxNet]]></category>
		<category><![CDATA[R]]></category>
		<category><![CDATA[Tensorflow]]></category>
		<guid isPermaLink="false">http://www.financial-hacker.com/?p=2899</guid>

					<description><![CDATA[Since December 2017, bitcoins can not only be traded at more or less dubious exchanges, but also as futures at the CME and CBOE. And already several trading systems popped up for bitcoin and other cryptocurrencies. None of them can claim big success, with one exception. There is a very simple strategy that easily surpasses &#8230; <a href="https://financial-hacker.com/deep-learning-systems-for-bitcoins-part-1/" class="more-link">Continue reading<span class="screen-reader-text"> "Deep Learning Systems for Bitcoin 1"</span></a>]]></description>
										<content:encoded><![CDATA[<p>Since December 2017, bitcoins can not only be traded at more or less dubious exchanges, but also as futures at the CME and CBOE. And already several trading systems popped up for bitcoin and other cryptocurrencies. None of them can claim big success, with one exception. There is a very simple strategy that easily surpasses all other bitcoin systems and probably also all known historical trading systems. Its name: <strong>Buy and Hold</strong>. In the light of the extreme success of that particular bitcoin strategy, do we really need any other trading system for cryptos?<span id="more-2899"></span></p>
<h3>Bitcoin &#8211; hodl??</h3>
<p>A buy and hold strategy works extremely well when a price bubble grows, and extremely bad when it bursts. And indeed, apparently all finance and economy gurus (well, all but <a href="https://www.rt.com/news/411379-john-mcafee-bitcoin-prediction/" target="_blank" rel="noopener noreferrer">John McAfee</a>) tell you that the cryptocurrency market, and especially bitcoin, is a bubble, even a &#8220;scam with no substantial worth&#8221;, and will soon experience a crash &#8220;worse than the 17th century tulip mania&#8221; or the &#8220;18th century South Sea Company fraud&#8221;.</p>
<p><figure id="attachment_2917" aria-describedby="caption-attachment-2917" style="width: 681px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2017/12/bitcoin.png"><img loading="lazy" decoding="async" class="wp-image-2917 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2017/12/bitcoin.png" alt="" width="681" height="321" srcset="https://financial-hacker.com/wp-content/uploads/2017/12/bitcoin.png 681w, https://financial-hacker.com/wp-content/uploads/2017/12/bitcoin-300x141.png 300w" sizes="auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px" /></a><figcaption id="caption-attachment-2917" class="wp-caption-text">Bubble or not?</figcaption></figure></p>
<p>By definition, a bubble is a price largely above the &#8216;real value&#8217; or &#8216;fair value&#8217; of an asset, and it bursts when people realize that. So what is the fair value of a bitcoin? Obviously not zero, since blockchain based currencies have (aside from their disadvantages) several advantages over traditional currencies, on the economy level as well as on the private level. Such as:</p>
<ul style="list-style-type: square;">
<li>They break the <a href="http://www.financial-hacker.com/money-and-how-to-get-it/" target="_blank" rel="noopener noreferrer">link of money and debt</a>. Cryptocurrencies don&#8217;t require the bank credit mechanism for money creation.</li>
<li>They can be used where normal money would be impractical, such as fee transfers between machines or trading in multiplayer games.</li>
<li>They allow low-cost and anonymous money transactions. At least in theory.</li>
<li>They replace banks for storing and mattresses for stashing money.</li>
</ul>
<p>I&#8217;m ready to believe that blockchain is the future of money transfer and storage. But that does not mean an ever-rising bitcoin price. Hundreds of cryptocurrencies came out in the last two years, any single of them with a better blockchain technology than bitcoin, and any good programmer can add a new coin anytime. Few will survive. Countries or big companies might sooner or later issue their own crypto tokens, as Venezuela already is attempting. The release of an official blockchain Dollar, Yuan, or Euro would leave the old bitcoin with its energy hungry transaction algorithm in thin air. Thus, when investing in bitcoin, we should not hope for a rosy future, but look for its present &#8216;real value&#8217;.</p>
<p>Due to its extreme volatility, bitcoin can not replace bank tresors. But it is already used in some situations for reducing money transfer costs, since the miners get any transaction rewarded in bitcoin. And above all, anonymity can be a substantial motive to own it. When you need a hacker to delete your drunk driving record, pay her in bitcoin. But how big is the online market for illegal hacker jobs, kill contracts, money laundering, drugs, weapons, or pro-Trump facebook advertisements? No one knows, but when we compare it with cash, another form of anonymous payment, we get interesting results.</p>
<p>The current cash in circulation in the US is approximately $1.5 trillion dollars. And the current bitcoin supply, about 17 million bitcoins, represents a total value of about $250 billion. Which means that you can already replace 15% of all US cash with bitcoin! Not to mention all the other cryptos. I fear that this supply already exceeds the demand of anonymous online payment for today and also the next future.</p>
<p>For those reasons, a bitcoin &#8220;hodl&#8221; system, despite its extreme historical performance, is high risk. We don&#8217;t know when and how the bubble will burst &#8211; maybe bitcoin will go up to $100,000 before &#8211; but we have some reason to suspect that at some point sooner or later the bitcoin price might drop like a stone down to its &#8216;real value&#8217;. Which is unknown, but for practical purposes is probably not in the $15,000 area, but more like $15.</p>
<p>So we need some other method to tackle the cryptocurrency trading problem. The first question: Has the crypto market already developed price curve inefficiencies that can be exploited in a trading system? In <strong>(1)</strong> we see some tests with basic bitcoin strategies. Our own tests came to the same results. Momentum based strategies can work, and <a href="http://www.financial-hacker.com/get-rich-slowly/" target="_blank" rel="noopener noreferrer">mean-variance optimizing</a>&nbsp;portfolio systems can achieve even extreme returns with crytrocurrencies &#8211; up to 10 times higher than &#8220;hodl&#8221;. But that&#8217;s not really surprising due to the high momentums and volatilities of crypto coins. The problem is that all crypto portfolios are exposed to high risk. Other conventional model-based strategies don&#8217;t work well anyway with cryptos.</p>
<p>When we concentrate on bitcoin, our proposed system must be a fast trading, trend-agnostic strategy. That means it holds positions only a few minutes, and is not exposed to the bubble risk. I can already tell that short-term mean reversion &#8211; even with a more sophisticated system as in <strong>(1)</strong> &#8211; produces no good result with cryptos. So only a few possibilities remain. One of them is exploiting short-term price patterns. This is the strategy that we will develop. And I can already tell that it works. But for this we&#8217;ll need a deep machine learning system for detecting the patterns and determining their rules.</p>
<h3>Selecting a machine learning library</h3>
<p>The basic structure of such a machine learning system is described <a href="http://www.financial-hacker.com/build-better-strategies-part-5-developing-a-machine-learning-system/" target="_blank" rel="noopener noreferrer">here</a>. Due to the low signal-to-noise ratio and to ever-changing market conditions, analyzing price series is one of the most ambitious tasks for machine learning. Compared with other AI algorithms, deep learning systems have the highest success rate. Since we can connect any <a href="http://www.financial-hacker.com/hackers-tools-zorro-and-r/" target="_blank" rel="noopener noreferrer">Zorro</a> based trading script to the data analysis software R, we&#8217;ll use a R based deep learning package. There are meanwhile many available. Here&#8217;s the choice:</p>
<ul style="list-style-type: square;">
<li><strong>Deepnet</strong>, a lightweight and straightforward neural net library with a stacked autoencoder and a Boltzmann machine. Produces good results when the feature set is not too complex. The basic train and predict functions for using a deepnet autoencoder in a Zorro strategy:<!--?prettify linenums=true?-->
<pre class="prettyprint">library('deepnet') 

neural.train = function(model,XY) 
{
  XY &lt;- as.matrix(XY)
  X &lt;- XY[,-ncol(XY)]
  Y &lt;- XY[,ncol(XY)]
  Y &lt;- ifelse(Y &gt; 0,1,0)
  Models[[model]] &lt;&lt;- sae.dnn.train(X,Y,
      hidden = c(30), 
      learningrate = 0.5, 
      momentum = 0.5, 
      learningrate_scale = 1.0, 
      output = "sigm", 
      sae_output = "linear", 
      numepochs = 100, 
      batchsize = 100)
}

neural.predict = function(model,X) 
{
  if(is.vector(X)) X &lt;- t(X)
  return(nn.predict(Models[[model]],X))
}
</pre>
</li>
<li><strong>H2O</strong>, an open-source software package with the ability to run on distributed computer systems. Coded in Java, so the latest version of the JDK is required. Aside from deep autoencoders, many other machine learning algorithms are supported, such as random forests. Features can be preselected, and ensembles can be created. Disadvantage: While batch training is fast, predicting a single sample, as usually needed in a trading strategy, is relatively slow due to the server/client concept. The basic <strong>H2O</strong> train and predict functions for Zorro:<!--?prettify linenums=true?-->
<pre class="prettyprint">library('h2o') 
# also install the Java JDK

neural.train = function(model,XY) 
{
  XY &lt;- as.h2o(XY)
  Models[[model]] &lt;&lt;- h2o.deeplearning(
    -ncol(XY),ncol(XY),XY,
    hidden = c(30),  seed = 365)
}

neural.predict = function(model,X) 
{
  if(is.vector(X)) X &lt;- as.h2o(as.data.frame(t(X)))
  else X &lt;- as.h2o(X)
  Y &lt;- h2o.predict(Models[[model]],X)
  return(as.vector(Y))
}</pre>
</li>
<li><strong>Tensorflow</strong> in its <strong>Keras</strong> incarnation, a neural network kit by Google. Supports CPU and GPU and comes with all needed modules for tensor arithmetics, activation and loss functions, covolution kernels, and backpropagation algorithms. So you can build your own neural net structure. <strong>Keras</strong> offers a simple interface for that.
<p>Keras is available as a R library, but installing it requires also a Python environment. First install Anaconda from <a href="https://www.anaconda.com">www.anaconda.com</a>. Open the Anaconda Navigator and install the RStudio application (installing Keras outside an Anaconda environment fails on some PCs with an error message). Then open Rstudio inside the Navigator, install the Keras package, then finally execute library(&#8216;keras&#8217;) and install_keras(). These steps usually succeed.</p>
<p>The <strong>Keras</strong> train and predict functions for Zorro:<!--?prettify linenums=true?--></p>
<pre class="prettyprint">library('keras')
#needs Python 3.6 and Anaconda
#call install_keras() after installing the package

neural.train = function(model,XY) 
{
  X &lt;- data.matrix(XY[,-ncol(XY)])
  Y &lt;- XY[,ncol(XY)]
  Y &lt;- ifelse(Y &gt; 0,1,0)
  Model &lt;- keras_model_sequential() 
  Model %&gt;% 
    layer_dense(units=30,activation='relu',input_shape = c(ncol(X))) %&gt;% 
    layer_dropout(rate = 0.2) %&gt;% 
    layer_dense(units = 1, activation = 'sigmoid')
  
  Model %&gt;% compile(
    loss = 'binary_crossentropy',
    optimizer = optimizer_rmsprop(),
    metrics = c('accuracy'))
  
  Model %&gt;% fit(X, Y, 
    epochs = 20, batch_size = 20, 
    validation_split = 0, shuffle = FALSE)
  
  Models[[model]] &lt;&lt;- Model
}

neural.predict = function(model,X) 
{
  if(is.vector(X)) X &lt;- t(X)
  X &lt;- as.matrix(X)
  Y &lt;- Models[[model]] %&gt;% predict_proba(X)
  return(ifelse(Y &gt; 0.5,1,0))
}
</pre>
</li>
<li><strong>MxNet</strong>, Amazon&#8217;s answer on Google&#8217;s Tensorflow. Offers also tensor arithmetics and neural net building blocks on CPU and GPU, as well as high level network functions similar to Keras (the next Keras version will also support MxNet). Just as with Tensorflow, CUDA is supported, but not (yet) OpenCL, so you&#8217;ll need a Nvidia graphics card to enjoy GPU support. In direct comparison <strong>(2)</strong>, MxNet was reported to be less resource hungry and a bit faster than Tensorflow, but so far I could not confirm this. The standard train and predict functions:<!--?prettify linenums=true?-->
<pre class="prettyprint"># how to install the CPU version:
#cran &lt;- getOption("repos")
#cran["dmlc"] &lt;- "https://s3-us-west-2.amazonaws.com/apache-mxnet/R/CRAN/"
#options(repos = cran)
#install.packages('mxnet')
library('mxnet')

neural.train = function(model,XY) 
{
  X &lt;- data.matrix(XY[,-ncol(XY)])
  Y &lt;- XY[,ncol(XY)]
  Y &lt;- ifelse(Y &gt; 0,1,0)
  Models[[model]] &lt;&lt;- mx.mlp(X,Y,
       hidden_node = c(30), 
       out_node = 2, 
       activation = "sigmoid",
       out_activation = "softmax",
       num.round = 20,
       array.batch.size = 20,
       learning.rate = 0.05,
       momentum = 0.9,
       eval.metric = mx.metric.accuracy)
}

neural.predict = function(model,X) 
{
  if(is.vector(X)) X &lt;- t(X)
  X &lt;- data.matrix(X)
  Y &lt;- predict(Models[[model]],X)
  return(ifelse(Y[1,] &gt; Y[2,],0,1))
}
</pre>
</li>
</ul>
<p>By replacing the <strong>neural.train</strong> and <strong>neural.predict</strong> functions, and other functions for saving and loading models that are not listed here, you can run the same strategy with different deep learning packages and compare. We&#8217;re currently using Keras for most machine learning strategies, and I&#8217;ll also use it for the short-term bitcoin trading system presented in the upcoming 2nd part of this article. There is no bitcoin futures data available yet, so tick based price data from several bitcoin exchanges will have to do for the backtest.</p>
<p>I&#8217;ve uploaded the interface scripts for Deepnet, H2O, Tensorflow/Keras, and MxNet to the 2018 script repository, so you can run your own deep learning experiments and compare the packages. Here&#8217;s a Zorro script for downloading bitcoin prices from Quandl &#8211; EOD only, though, since the exchanges demand dear payment for their tick data.</p>
<pre class="prettyprint">void main()
{
  assetHistory("BITFINEX/BTCUSD",FROM_QUANDL);
}</pre>
<p>You can also get Bitcoin M1 data from Kaggle in CSV format. Here&#8217;s a Zorro script for converting it to a Zorro T6 dataset:</p>
<pre class="prettyprint">void main()
{
	string InName = "History\\bitstampUSD_1-min_data_2012-01-01_to_2019-03-13.csv";
	string Format = "+%t,f3,f1,f2,f4,f6";
	dataParse(1,Format,InName); 
	dataSave(1,"History\\BTCUSD.t6");
}</pre>
<h3>Further reading</h3>
<p>(1) Nicolas Rabener, <a href="https://www.factorresearch.com/research-quant-strategies-in-the-cryptocurrency-space" target="_blank" rel="noopener noreferrer">Quant Strategies in the Cryptocurrency Space</a></p>
<p>(2) Julien Simon, <a href="https://medium.com/@julsimon/keras-shoot-out-tensorflow-vs-mxnet-51ae2b30a9c0" target="_blank" rel="noopener noreferrer">Tensorflow vs MxNet</a></p>
<p>(3) Zachary Lipton et al, <a href="https://github.com/zackchase/mxnet-the-straight-dope" target="_blank" rel="noopener noreferrer">MxNet &#8211; The Straight Dope</a><br />
(Good introduction in deep learning with MxNet / Gluon examples)</p>
<p>(4) F.Chollet/J.J.Allaire, Deep Learning with R<br />
(Excellent introduction in Keras)</p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/deep-learning-systems-for-bitcoins-part-1/feed/</wfw:commentRss>
			<slash:comments>42</slash:comments>
		
		
			</item>
		<item>
		<title>Algorithmic Options Trading 3</title>
		<link>https://financial-hacker.com/algorithmic-options-trading-part-3/</link>
					<comments>https://financial-hacker.com/algorithmic-options-trading-part-3/#comments</comments>
		
		<dc:creator><![CDATA[jcl]]></dc:creator>
		<pubDate>Sun, 26 Nov 2017 07:59:48 +0000</pubDate>
				<category><![CDATA[Introductory]]></category>
		<category><![CDATA[No Math]]></category>
		<category><![CDATA[System Development]]></category>
		<category><![CDATA[Call]]></category>
		<category><![CDATA[Earnings]]></category>
		<category><![CDATA[ETF]]></category>
		<category><![CDATA[Options]]></category>
		<category><![CDATA[Put]]></category>
		<category><![CDATA[SPY]]></category>
		<category><![CDATA[Strangle]]></category>
		<guid isPermaLink="false">http://www.financial-hacker.com/?p=2718</guid>

					<description><![CDATA[In this article we&#8217;ll look into a real options trading strategy, like the strategies that we code for clients. This one however is based on a system from a trading book. As mentioned before, options trading books often contain systems that really work &#8211; which can not be said about day trading or forex trading &#8230; <a href="https://financial-hacker.com/algorithmic-options-trading-part-3/" class="more-link">Continue reading<span class="screen-reader-text"> "Algorithmic Options Trading 3"</span></a>]]></description>
										<content:encoded><![CDATA[<p>In this article we&#8217;ll look into a real options trading strategy, like the strategies that we code for clients. This one however is based on a system from a trading book. As mentioned before, options trading books often contain systems that <strong>really work</strong> &#8211; which can not be said about day trading or forex trading books. The system examined here is indeed able to produce profits. Which is not surprising, since it apparently <strong>never loses</strong>. But it is also obvious that its author has never backtested it. <span id="more-2718"></span></p>
<p>To clarify: I&#8217;ve selected the system not because of high profit expectancy or clever algorithm, but because it is quite simple and does not need any of the additional data normally used for option systems, such as earnings reports, open interest, implied volatility, or greeks. Which means that you don&#8217;t need to call R functions for options math, and you don&#8217;t need to pay for iVolatility options data, Zacks earnings, or any other historical data for backtesting the system. The free <a href="http://www.financial-hacker.com/hackers-tools-zorro-and-r/" target="_blank" rel="noopener">Zorro</a> version is sufficient.</p>
<p>The book cover praises the system inside:  <strong><em>To reduce your investment risk to nearly zero &#8211; Achieve consistent high annual returns in excess of 30% &#8211; It does not require you to learn fundamental and technical analyzes, deltas, thetas, gamas, vegas or other Greek goblethegooks of stocks or options  &#8211; It does not require the ability to predict market direction &#8211; It does not require stock picking skills &#8211; It does not require close monitoring</em></strong>.</p>
<p>All statements with which I, of course, highly sympathize. After all, why would we need Greek goblethegooks when we get annual 30% without them! And here are the (simplified) rules of our strategy:</p>
<ol>
<li>Sell a 6 weeks call and a 6 weeks put of an index ETF. Choose strike prices so that the premiums are in the $1..$2 range.</li>
<li>If the underlying price touches one of our strike prices, thus threatening an <a href="http://www.financial-hacker.com/algorithmic-options-trading/" target="_blank" rel="noopener">in-the-money</a> expiration, buy back that option and immediately sell a new option of the same type, but to a further expiration date, and a premium that covers the loss.</li>
<li>Wait until all options are expired, then go back to 1.</li>
</ol>
<p>If you have a bit experience with options, you&#8217;ll notice that rule 1 describes a <strong>strangle</strong> combo. And you&#8217;ll next notice something strange with rule 2. Right, such a system can never lose, since any loss would apparently be compensated by the premium from the new trade. Have we finally found the <a href="http://www.financial-hacker.com/seventeen-popular-trade-strategies-that-i-dont-really-understand/" target="_blank" rel="noopener">Holy Grail</a>, an ever-winning system? </p>
<h3>Strangle profit</h3>
<p>For getting an impression of the profit and risk, let&#8217;s first check the gain/loss diagram of the 6-week $2 premium strangle. This is the definition of a strangle in the curve plotting script from the <a href="http://www.financial-hacker.com/algorithmic-options-trading-2/" target="_blank" rel="noopener">last article</a>:</p>
<pre class="prettyprint">// Strangle
void combo()
{
	optionAdd(1,SELL|CALL,6);
	optionAdd(1,SELL|PUT,-6);
}</pre>
<p>The $6 strike-spot distances have been chosen for $2 premium from a hypothetical index ETF with $250 price, multiplier 100, and 15% annual volatility. This is the profit/loss diagram:</p>
<p><a href="http://www.financial-hacker.com/wp-content/uploads/2018/08/OptionsCurve_EURUSD_s.png"><img loading="lazy" decoding="async" class="alignnone wp-image-2841 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2018/08/OptionsCurve_EURUSD_s.png" alt="" width="742" height="481" srcset="https://financial-hacker.com/wp-content/uploads/2018/08/OptionsCurve_EURUSD_s.png 742w, https://financial-hacker.com/wp-content/uploads/2018/08/OptionsCurve_EURUSD_s-300x194.png 300w" sizes="auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px" /></a></p>
<p>Our potential gain is about $400 per combo trade, as expected (2 * 100 * $2 premium). But the price of our index ETF should better not move more than $10 in any direction until expiration. Otherwise the loss can quickly reach the thousand dollar zone. This does not really look like &#8220;reduce your investment risk to nearly zero&#8221;. But wait, we have rule 2, which will certainly save the day! Let&#8217;s put that to the backtest. </p>
<h3>The system</h3>
<pre class="prettyprint">// Quite simple options trading system 
#include &lt;contract.c&gt;

#define PREMIUM	2.00
#define WEEKS	6    // expiration

int i;
var Price;

CONTRACT* findCall(int Expiry,var Premium)
{
	for(i=0; i&lt;50; i++) {
		if(!contract(CALL,Expiry,Price+0.5*i)) return 0;
		if(between(ContractBid,0.1,Premium)) return ThisContract;
	}
	return 0;
}

CONTRACT* findPut(int Expiry,var Premium)
{
	for(i=0; i&lt;50; i++) {
		if(!contract(PUT,Expiry,Price-0.5*i)) return 0;
		if(between(ContractBid,0.1,Premium)) return ThisContract;
	}
	return 0;
}

void run() 
{
	StartDate = 20110101;
	EndDate = 20161231;
	BarPeriod = 1440;
	BarZone = ET;
	BarOffset = 15*60+20; // trade at 15:20 ET
	LookBack = 1;

	assetList("AssetsIB");
	asset("SPY"); // unadjusted!
	Multiplier = 100;

// load today's contract chain
	contractUpdate("SPY",0,CALL|PUT);
	Price = priceClose(); 

// check for in-the-money and roll 	
	for(open_trades) {
		var Loss = -TradeProfit/Multiplier;
		if(TradeIsCall &amp;&amp; Price &gt;= TradeStrike) {
			exitTrade(ThisTrade);
			printf("#\nRoll %.1f at %.2f Loss %.2f",
				TradeStrike,Price,TradeProfit);
			CONTRACT* C = findCall(NWEEKS*7,Loss*1.1);
			if(C) {
				MarginCost = 0.15*Price - (C-&gt;fStrike-Price);
				enterShort();
			}
		} else if(TradeIsPut &amp;&amp; Price &lt;= TradeStrike) {
			exitTrade(ThisTrade);
			printf("#\nRoll %.1f at %.2f Loss %.2f",
				TradeStrike,Price,TradeProfit);
			CONTRACT* C = findPut(NWEEKS*7,Loss*1.1);
			if(C) { 
				MarginCost = 0.15*Price - (Price-C-&gt;fStrike);
				enterShort();
			}
		}
	}
	
// all expired? enter new options
	if(!NumOpenShort) { 
		CONTRACT *Call = findCall(NWEEKS*7,PREMIUM); 
		CONTRACT *Put = findPut(NWEEKS*7,PREMIUM); 		
		if(Call &amp;&amp; Put) {
			MarginCost = 0.5*(0.15*Price-
				min(Call-&gt;fStrike-Price,Price-Put-&gt;fStrike));
			contract(Call); enterShort();
			contract(Put); enterShort();
		}
	}
}</pre>
<p>A brief discussion of the code (a more detailed intro in system coding can be found in the Black Book). The <strong>findCall</strong> function gets an expiration time and a premium, and looks through the current option chain for a call contract that matches these two parameters. For this it increases the strike price in 50 steps. If then still no contract is found at or below the desired premium, it returns 0. Otherwise it returns a pointer to the found contract. The <strong>findPut</strong> function does the same for a put contract.</p>
<p>The <strong>run</strong> function sets up the backtest time and other parameters for the backtest as well as for live trading. It&#8217;s a daily script, and the function runs every day at 3:20 pm Eastern Time. It uses two historical data files for the backtest. The <strong>asset</strong> function loads a file with the unadjusted SPY prices (why unadjusted? Because determining the strikes-price distances would not work with dividend adjusted prices). The <strong>contractUpdate</strong> function loads the SPY options chain of that day, either from the broker, or from a file.  Those two files must be present, plus the asset list <strong>AssetsIB.csv</strong> that contains commission, margin, and other parameters for simulating the broker or exchange where we trade.</p>
<p>The next part of the code implements the miraculous rule 2. It calculates the current loss, closes any position that is at or in the money, and immediately opens a new position, with a premium slightly above our loss (<strong>Loss*1.1</strong>). This way we&#8217;re punishing the market for going against us. The <strong>printf</strong> function just stores that event in the log, so that we can go through it and better see the fate of those trades.</p>
<p>The last part of the code is the strangle. Note the <strong>MarginCost</strong> calculation. Margin affects the required capital and thus the backtest performance, so it should reflect your broker&#8217;s margin requirement. By default, the margin of a sold option is the premium plus some fixed percentage of the underlying that&#8217;s set up in the asset list. But brokers often apply a more complex margin formula for option combos. Here we assume that the margin of a sold strangle is the premium (which is automatically added) plus 15% of the underlying price minus the minimum of the two strike differences. We multiply that by half because we have 2 positions, but the margin formula is for the whole strangle.</p>
<p>The backtest from 2011-2016 needs only about 2 seconds. This is the result (assuming we always open 1 contract):</p>
<pre class="prettyprint">Monte Carlo Analysis... Median AR 12%
Win 3699$  MI 51.38$  DD 935$  Capital 5108$
Trades 93  Win 59.1%  Avg +39.8p  Bars 24
AR 12%  PF 1.84  SR 1.08  UI 5%  R2 0.89</pre>
<p>We have won about 60% of all trades, and made 12% annual return based on Montecarlo analysis.  Not too exciting. What about the &#8220;consistent high annual returns in excess of 30%&#8221;? And how can we get a $935 drawdown when we always compensate our loss with a new trade?</p>
<h3>Is rolling over irrational?</h3>
<p>Let&#8217;s try the same strategy without the rule 2. This simplifies the script a bit:</p>
<pre class="prettyprint">// Even simpler options trading system 
#include &lt;contract.c&gt;

#define PREMIUM	2.00
#define WEEKS	6 // expiration

int i;
var Price;

CONTRACT* findCall(int Expiry,var Premium)
{
	for(i=0; i&lt;50; i++) {
		if(!contract(CALL,Expiry,Price+0.5*i)) return 0;
		if(between(ContractBid,0.1,Premium)) return ThisContract;
	}
	return 0;
}

CONTRACT* findPut(int Expiry,var Premium)
{
	for(i=0; i&lt;50; i++) {
		if(!contract(PUT,Expiry,Price-0.5*i)) return 0;
		if(between(ContractBid,0.1,Premium)) return ThisContract;
	}
	return 0;
}

void run() 
{
	StartDate = 20110101;
	EndDate = 20161231;
	BarPeriod = 1440;
	BarZone = ET;
	BarOffset = 15*60+20; // trade at 15:20 ET
	LookBack = 1;
	set(PLOTNOW);
	set(PRELOAD|LOGFILE);

	assetList("AssetsIB");
	asset("SPY"); // unadjusted!
	Multiplier = 100;

// load today's contract chain
	Price = priceClose();
	contractUpdate("SPY",0,CALL|PUT);

// all expired? enter new options
	if(!NumOpenShort) { 
		CONTRACT *Call = findCall(WEEKS*7,PREMIUM); 
		CONTRACT *Put = findPut(WEEKS*7,PREMIUM); 		
		if(Call &amp;&amp; Put) {
			MarginCost = 0.5*(0.15*Price-min(Call-&gt;fStrike-Price,Price-Put-&gt;fStrike));
			contract(Call); enterShort();
			contract(Put); enterShort();
		}
	}
}</pre>
<p>Simply removing the rolling over improved the system remarkably:</p>
<pre class="prettyprint">Monte Carlo Analysis... Median AR 25%
Win 5576$  MI 77.46$  DD 785$  Capital 3388$
Trades 78  Win 80.8%  Avg +71.5p  Bars 35
AR 27%  PF 2.00  SR 0.92  UI 5%  R2 0.92</pre>
<p>The equity curve with no rolling:</p>
<p><a href="http://www.financial-hacker.com/wp-content/uploads/2018/08/Options32_SPY.png"><img loading="lazy" decoding="async" class="alignnone wp-image-2844 size-large" src="http://www.financial-hacker.com/wp-content/uploads/2018/08/Options32_SPY-1024x329.png" alt="" width="840" height="270" srcset="https://financial-hacker.com/wp-content/uploads/2018/08/Options32_SPY-1024x329.png 1024w, https://financial-hacker.com/wp-content/uploads/2018/08/Options32_SPY-300x96.png 300w, https://financial-hacker.com/wp-content/uploads/2018/08/Options32_SPY-768x247.png 768w, https://financial-hacker.com/wp-content/uploads/2018/08/Options32_SPY-1200x386.png 1200w, https://financial-hacker.com/wp-content/uploads/2018/08/Options32_SPY.png 1497w" sizes="auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></a></p>
<p>Now the 25% annual return are somewhat closer to the promised profit. Of course at cost of higher risk, since no limiting mechanism is in place. We could now test other option combos instead of the strangle, for instance a <strong>condor</strong> for limiting the risk. We can run an optimization for finding out how the profit is affected by different premiums and expirations. I leave that to the reader. The interesting question is why rolling over options, not only with this, but with many option trading systems that we have coded so far, reduces the performance remarkably. Often to the client&#8217;s great surprise.</p>
<p>Rolling over with loss compensation establishes in fact a <a href="http://www.financial-hacker.com/seventeen-popular-trade-strategies-that-i-dont-really-understand/" target="_blank" rel="noopener">Martingale system</a>. And such a system fares no better in option trading than in the casino. In fact, even worse. In the casino you have at least the same chance with every play. In trading, a losing option combo hints that the market starts trending &#8211; and the trend is likely to continue with the rolled over contract. Quite soon you cannot anymore compensate your losses with higher premiums, since you&#8217;ll find no contracts at that value. Ok, you could then start increasing the contract volume. If you really did that, you can calculate under the link above how long your account will survive. Rolling over a losing contract is typical irrational human behavior &#8211;  but the markets tend to punish irrationality.</p>
<h3>Artificial options data</h3>
<p>Since the system does not rely on goblethegooks, we can check whether the artificial options data that we created in the <a href="http://www.financial-hacker.com/algorithmic-options-trading/" target="_blank" rel="noopener">first part</a> of this mini series can be used for testing this system. The backtest results above were with real options data. Here&#8217;s the result with the synthetic data:</p>
<pre class="prettyprint">Monte Carlo Analysis... Median AR 31%
Win 7162$  MI 99.49$  DD 1188$  Capital 3866$
Trades 88  Win 81.8%  Avg +81.4p  Bars 30
AR 31%  PF 2.36  SR 1.12  UI 4%  R2 0.88
</pre>
<p>It&#8217;s similar, but not quite identical to the real data. Artificial data represents a more efficient market situation, since its option premiums are identical to their theoretical values, and fundamentals such as earnings reports play no role. You can use it for confirming the real data backtest. Or for saving money, by backtesting a non-goblethegooks system (yes, I like this word) first with artifical data, and only if it looks good, purchasing real data for the final test.</p>
<p>I&#8217;ve added the full script to the 2017 repository. You&#8217;ll need Zorro version 1.73 or above. You can find the unadjusted SPY data in the History folder of the archive (alternatively, download it with the Zorro command <strong>assetHistory( &#8220;SPY.US&#8221;, FROM_STOOQ | UNADJUSTED)</strong>). If you don&#8217;t want to create the artificial 2011-2016 options history yourself, you can download it from the historical data archives <a href="http://zorro-project.com/download.php" target="_blank" rel="noopener">here</a>. </p>
<h3>Conclusions</h3>
<ul style="list-style-type: square;">
<li>Mind the margin cost in backtests.</li>
<li>Do not roll over losing contracts.</li>
<li>If your system has no goblethegooks, try artificial data.</li>
</ul>
<h3>Literature</h3>
<p>(1) is the book from which I pulled the system. The book is ok &#8211; not better or worse than most other options books, but at only $10, getting it is no mistake. <br />
(2) is a really good introduction into the options trading matter. Even though its author shamelessly plagiarized the title of my blog, and this even years before I started writing it!</p>
<p><strong>(1) Daniel Mollat, $tock option$, BN Publishing 2011<br />
</strong><strong>(2) Philip Z Maymin, Financial Hacking, Wspc 2012</strong></p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/algorithmic-options-trading-part-3/feed/</wfw:commentRss>
			<slash:comments>46</slash:comments>
		
		
			</item>
	</channel>
</rss>
