<?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>ETF &#8211; The Financial Hacker</title>
	<atom:link href="https://financial-hacker.com/tag/etf/feed/" rel="self" type="application/rss+xml" />
	<link>https://financial-hacker.com</link>
	<description>A new view on algorithmic trading</description>
	<lastBuildDate>Sun, 06 Mar 2022 08:58:03 +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>ETF &#8211; The Financial Hacker</title>
	<link>https://financial-hacker.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<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 fetchpriority="high" 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="(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 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="(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>
		<item>
		<title>Hacking a HFT system</title>
		<link>https://financial-hacker.com/hacking-hft-systems/</link>
					<comments>https://financial-hacker.com/hacking-hft-systems/#comments</comments>
		
		<dc:creator><![CDATA[jcl]]></dc:creator>
		<pubDate>Sat, 08 Jul 2017 13:26:30 +0000</pubDate>
				<category><![CDATA[No Math]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[System Evaluation]]></category>
		<category><![CDATA[Arbitrage]]></category>
		<category><![CDATA[ES]]></category>
		<category><![CDATA[ETF]]></category>
		<category><![CDATA[HFT]]></category>
		<category><![CDATA[Latency]]></category>
		<category><![CDATA[NxCore]]></category>
		<category><![CDATA[SPY]]></category>
		<guid isPermaLink="false">http://www.financial-hacker.com/?p=2359</guid>

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

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

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

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

#define STARTDAY 20161004
#define ENDDAY   20161014

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

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

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

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

int Row,Rows;

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

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


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

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

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

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

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

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

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

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

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

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

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

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

					<description><![CDATA[Most trading systems are of the get-rich-quick type. They exploit temporary market inefficiencies and aim for annual returns in the 100% area. They require regular supervision and adaption to market conditions, and still have a limited lifetime. Their expiration is often accompanied by large losses. But what if you&#8217;ve nevertheless collected some handsome gains, and &#8230; <a href="https://financial-hacker.com/get-rich-slowly/" class="more-link">Continue reading<span class="screen-reader-text"> "Get Rich Slowly"</span></a>]]></description>
										<content:encoded><![CDATA[<p>Most trading systems are of the get-rich-quick type. They exploit temporary market inefficiencies and aim for annual returns in the 100% area. They require regular supervision and adaption to market conditions, and still have a limited lifetime. Their expiration is often accompanied by large losses. But what if you&#8217;ve nevertheless collected some handsome gains, and now want to park them in a more safe haven? Put the money under the pillow? Take it into the bank? Give it to a hedge funds? Obviously, all that goes against an algo trader&#8217;s honor code. Here&#8217;s an alternative.<span id="more-1702"></span></p>
<p>The old-fashioned investing method is buying some low-risk stocks and then waiting a long time. Any portfolio of stocks has a certain mean return and a certain fluctuation in value; you normally want to minimize the latter and maximize the former. Since the mean return and the fluctuation changes all the time, this task requires rebalancing the portfolio in regular intervals. The <strong>optimal capital allocation</strong> among the portfolio components produces either maximum mean return for a given allowed risk, or minimum risk &#8211; respectively, minimum variance &#8211; for a given mean return. This optimal allocation is often very different to investing the same amount in all N components of the portfolio. An easy way to solve this mean / variance optimization problem was published 60 years ago by <a href="https://en.wikipedia.org/wiki/Harry_Markowitz" target="_blank" rel="noopener noreferrer"><strong>Harry Markowitz</strong></a>. It won him later the Nobel prize.</p>
<h3>The unfashionable Markowitz</h3>
<p>Unfortunately, Markowitz got largely out of fashion since then. The problem is the same as with all trading algorithms: You can only calculate the optimal capital allocation in hindsight. Optimized portfolios mysteriously failed in live trading. They were said to often return less than a simple 1/N capital distribution. But this was challenged recently in <a href="http://papers.ssrn.com/sol3/papers.cfm?abstract_id=2606884">an interesting paper</a> (1) by Keller, Butler, and Kipnis, of which I quote the first paragraph:</p>
<p><em>Mean-Variance Optimization (MVO) as introduced by Markowitz (1952) is often presented as an elegant but impractical theory. MVO is an<strong> &#8220;unstable and error-maximizing</strong>&#8221; procedure (Michaud 1989), and is &#8220;<strong>nearly always beaten by simple 1/N portfolios</strong>&#8221; (DeMiguel, 2007). And to quote Ang (2014): &#8220;Mean-variance weights <strong>perform horribly</strong>… The optimal mean-variance portfolio is a complex function of estimated means, volatilities, and correlations of asset returns. There are many parameters to estimate. Optimized mean-variance portfolios can <strong>blow up</strong> when there are tiny errors in any of these inputs&#8230;&#8221;.</em></p>
<p>The optimized portfolios of the quoted authors indeed blew up. But Markowitz is not to blame. They just did not understand what &#8216;optimal capital allocation&#8217; means. Suppose you have a portfolio of very similar assets, all with almost identical mean return and variance, only one of them is a tiny bit better. The Markowitz algorithm will then tend to assign all capital to that single asset. That&#8217;s just logical, as it is the optimal capital allocation. But it&#8217;s not the optimal portfolio. You don&#8217;t want to expose all capital to a single stock. If that company goes belly up, your portfolio will too. This is the mentioned &#8216;stability problem&#8217;. However there is a simple and obvious solution: a per-asset weight limit.</p>
<p>Aside from that, the Markowitz vituperators used too long, mean-reverting time periods for sampling the returns and covariances, and they applied the MVO algorithm wrongly to mixed long/short portfolios. When correctly applied to a momentum-governed time period and long-only, well diversified portfolios with a weight limit, MVO produced out of sample results <strong>far superior to 1/N</strong>. This was proven by testing a number of example portfolios in (1) with a R MVO implementation by fellow blogger <a href="http://quantstrattrader.wordpress.com/">Ilya Kipnis</a>.</p>
<p>However, a R implementation is not very practical for live trading. For this we have to implement MVO in a real trade platform. Then we can park our money in an optimized portfolio of stocks and ETFs, let the platform rebalance the capital allocation in regular intervals, lean back, wait, and get rich slowly.</p>
<h3>Implementing MVO</h3>
<p>The Zorro implementation is based on Markowitz&#8217; 1959 publication (2). In chapter 8, he described the MVO algorithm in a clear and easy to follow way. For simple minded programmers like me, he even included a brief introduction to linear algebra! I only modified his original algorithm by adding the mentioned weight constraint. This constraint stabilizes the algorithm and keeps the portfolio diversified.</p>
<p>In wise anticipation of future computing machines, Markowitz also included an example portfolio for checking if you programmed his algorithm correctly. The proof:</p>
<pre class="prettyprint">function main()
{
	var Means[3] = { .062,.146,.128 };
	var Covariances[3][3] = {.0146,.0187,.0145,.0187,.0854,.0104,.0145,.0104,.0289};
	var Weights[3];
	
	var BestVariance = markowitz(Covariances,Means,3,0);

	markowitzReturn(Weights,1);
	printf("\nMax:  %.2f %.2f %.2f",Weights[0],Weights[1],Weights[2]);
	markowitzReturn(Weights,BestVariance);
	printf("\nBest: %.2f %.2f %.2f",Weights[0],Weights[1],Weights[2]);
	markowitzReturn(Weights,0);
	printf("\nMin:  %.2f %.2f %.2f",Weights[0],Weights[1],Weights[2]);
}</pre>
<p>The means and covariances arrays in the script are from Markowitz&#8217; example portfolio. The <strong>markowitz</strong> function runs the algorithm and returns the variance value associated with the best Sharpe ratio. The <strong>markowitzReturn</strong> function then calculates the capital allocation weights with the maximum mean return for a given variance. The weights for maximum, best, and minimum variance are printed. If I did it right, they should be exactly the same as in Markowitz&#8217; publication:</p>
<pre class="prettyprint">Max:  0.00 1.00 0.00
Best: 0.00 0.22 0.78
Min:  0.99 0.00 0.01
</pre>
<h3>Selecting the assets</h3>
<p>For long-term portfolios you can&#8217;t use the same high-leverage Forex or CFD instruments that you preferred for your short-term strategies. Instead you normally invest in stocks, ETFs, or similar instruments. They offer several advantages for algo trading:</p>
<ul style="list-style-type: square;">
<li><strong>No zero-sum game.</strong> In the long run, stocks and index ETFs have positive mean returns due to dividends and accumulated value, while Forex pairs and index CFDs have negative mean returns due to swap/rollover fees.<br />
  </li>
<li><strong>Serious brokers.</strong> Stock/ETF brokers are all regulated, what can not be said of all Forex/CFD brokers.<br />
  </li>
<li><strong>More data</strong> for your algorithms, such as volume and market depth information.<br />
  </li>
<li><strong>Bigger choice of assets</strong> from many different market sectors.<br />
   </li>
<li><strong>More trading methods,</strong> such as pairs trading (&#8220;stat arb&#8221;), trading risk-free assets such as T-bills, or trading volatility.</li>
</ul>
<p>The obvious disadvantage is low leverage, like 1:2 compared with 1:100 or more for Forex instruments. Low leverage is ok for a long-term system, but not for getting rich quick. More restrictions apply to long-term portfolios. MVO obviously won&#8217;t work well with components that have no positive mean return. And it won&#8217;t work well either when the returns are strongly correlated. So when selecting assets for your long-term portfolio, you have to look not only for returns, but also for correlation. Here&#8217;s the main part of a Zorro script for that:</p>
<pre class="prettyprint">#define NN  30  // max number of assets

function run()
{
	BarPeriod = 1440;
	NumYears = 7;
	LookBack = 6*252; // 6 years

	string	Names[NN];
	vars	Returns[NN];
	var	Correlations[NN][NN];

	int N = 0;
	while(Names[N] = loop( 
		"TLT","LQD","SPY","GLD","VGLT","AOK"))
	{
		if(is(INITRUN))
			assetHistory(Names[N],FROM_YAHOO);
		asset(Names[N]);
		Returns[N] = series((priceClose(0)-priceClose(1))/priceClose(1));
		if(N++ &gt;= NN) break;
	}
	if(is(EXITRUN)) {
		int i,j;
		for(i=0; i&lt;N; i++)
		for(j=0; j&lt;N; j++)
			Correlations[N*i+j] = 
				Correlation(Returns[i],Returns[j],LookBack);
		plotHeatmap("Correlation",Correlations,N,N);
		for(i=0; i&lt;N; i++)
			printf("\n%i - %s: Mean %.2f%%  Variance %.2f%%",
				i+1,Names[i],
				100*annual(Moment(Returns[i],LookBack,1)),
				252*100*Moment(Returns[i],LookBack,2));
	}
}
</pre>
<p>The script first sets up some parameters, then goes into a loop over <strong>N</strong> assets. Here I&#8217;ve just entered some popular ETFs; for replacing them, websites such as <a href="http://etfdb.com/etfs/">etfdb.com</a> give an overview and help searching for the optimal ETF combination.</p>
<p>In the initial run, the asset prices are downloaded from Yahoo. They are corrected for splits and dividends. The <strong>assetHistory</strong> function stores them as historical price data files. Then the assets are selected and their returns are calculated and stored in the <strong>Returns</strong> data series. This is repeated with all 1-day bars of a 7 years test period (obviously the period depends on since when the selected ETFs are available). In the final run the script prints the annual mean returns and variances of all assets, which are the first and second <strong>moments</strong> of the return series. The <strong>annual</strong> function and the 252 multiplication factor convert daily values to annual values. The results for the selected ETFs:</p>
<pre class="prettyprint">1 - TLT: Mean 10.75% Variance 2.29%
2 - LQD: Mean 6.46% Variance 0.31%
3 - SPY: Mean 13.51% Variance 2.51%
4 - GLD: Mean 3.25% Variance 3.04%
5 - VGLT: Mean 9.83% Variance 1.65%
6 - AOK: Mean 4.70% Variance 0.23%</pre>
<p>The ideal ETF has high mean return, low variance, and low correlation to all other assets of the portfolio. The correlation can be seen in the <strong>correlation matrix</strong> that is computed from all collected returns in the above code, then plotted in a <strong>N*N</strong> heatmap:</p>
<p><a href="http://www.financial-hacker.com/wp-content/uploads/2016/12/Heatmap_s-1.png"><img loading="lazy" decoding="async" class="alignnone wp-image-1777 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2016/12/Heatmap_s-1.png" width="379" height="351" srcset="https://financial-hacker.com/wp-content/uploads/2016/12/Heatmap_s-1.png 379w, https://financial-hacker.com/wp-content/uploads/2016/12/Heatmap_s-1-300x278.png 300w" sizes="auto, (max-width: 379px) 85vw, 379px" /></a></p>
<p>The correlation matrix contains the correlation coefficients of every asset with every other asset. The rows and columns of the heatmap are the 6 assets. The colors go from blue for low correlation between the row and column asset, to red for high correlation. Since any asset correlates perfectly with itself, we always have a red diagonal. But you can see from the other red squares that some of my 6 popular ETFs were no good choice. Finding the perfect ETF combination, with the heatmap as blue as possible, is left as an exercise to the reader.</p>
<h3>The efficient frontier</h3>
<p>After selecting the assets for our portfolio, we now have to calculate the optimal capital allocation, using the MVO algorithm. However, &#8220;optimal&#8221; depends on the desired risk, i.e. volatility of the portfolio. For every risk value there&#8217;s a optimal allocation that generates the maximum return. So the optimal allocation is not a point, but a curve in the return / variance plane, named the <strong>Efficient Frontier</strong>. We can calculate and plot it with this script:</p>
<pre class="prettyprint">function run()
{
	... // similar to Heatmap script
 
	if(is(EXITRUN)) {
		int i,j;
		for(i=0; i&lt;N; i++) {
			Means[i] = Moment(Returns[i],LookBack,1);
			for(j=0; j&lt;N; j++)
				Covariances[N*i+j] =
					Covariance(Returns[i],Returns[j],LookBack);	
		}

		var BestV = markowitz(Covariances,Means,N,0);	
		var MinV = markowitzVariance(0,0);
		var MaxV = markowitzVariance(0,1);

		int Steps = 50;
		for(i=0; i&lt;Steps; i++) {
			var V = MinV + i*(MaxV-MinV)/Steps;
			var R = markowitzReturn(0,V);
			plotBar("Frontier",i,V,100*R,LINE|LBL2,BLACK);
		}
		plotGraph("Max Sharpe",(BestV-MinV)*Steps/(MaxV-MinV),
			100*markowitzReturn(0,BestV),SQUARE,GREEN);
	}
}</pre>
<p>I&#8217;ve omitted the first part since it&#8217;s identical to the heatmap script. Only the covariance matrix is now calculated instead of the correlation matrix. Covariances and mean returns are fed to the <strong>markowitz</strong> function that again returns the variance with the best Sharpe ratio. The subsequent calls to <strong>markowitzVariance</strong> also return the highest and the lowest variance of the efficient frontier and establish the borders of the plot. Finally the script plots 50 points of the annual mean return from the lowest to the highest variance:</p>
<p><a href="http://www.financial-hacker.com/wp-content/uploads/2016/12/EFrontier_s.png"><img loading="lazy" decoding="async" class="alignnone wp-image-1780 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2016/12/EFrontier_s.png" width="629" height="301" srcset="https://financial-hacker.com/wp-content/uploads/2016/12/EFrontier_s.png 629w, https://financial-hacker.com/wp-content/uploads/2016/12/EFrontier_s-300x144.png 300w" sizes="auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px" /></a></p>
<p>At the right side we can see that the portfolio reaches a maximum annual return of about 12.9%, which is simply all capital allocated to SPY. On the left side we achieve only 5.4% return, but with less than a tenth of the daily variance. The green dot is the point on the frontier with the best Sharpe ratio (= return divided by square root of variance) at 10% annual return and 0.025 variance. This is the optimal portfolio &#8211; at least in hindsight. </p>
<h3>Experiments</h3>
<p>How will a mean / variance optimized portfolio fare in an out of sample test, compared with with 1/N? Here&#8217;s a script for experiments with different portfolio compositions, lookback periods, weight constraints, and variances:</p>
<pre class="prettyprint">#define DAYS	252 // 1 year lookback period
#define NN	30  // max number of assets

function run()
{
	... // similar to Heatmap script

	int i,j;
	static var BestVariance = 0;
	if(tdm() == 1 &amp;&amp; !is(LOOKBACK)) {
		for(i=0; i&lt;N; i++) {
			Means[i] = Moment(Returns[i],LookBack,1);
			for(j=0; j&lt;N; j++)
				Covariances[N*i+j] = Covariance(Returns[i],Returns[j],LookBack);	
		}
		BestVariance = markowitz(Covariances,Means,N,0.5);
	}
	
	var Weights[NN]; 
	static var Return, ReturnN, ReturnMax, ReturnBest, ReturnMin;
	if(is(LOOKBACK)) {
		Month = 0;
		ReturnN = ReturnMax = ReturnBest = ReturnMin = 0;
	}

	if(BestVariance &gt; 0) {
		for(Return=0,i=0; i&lt;N; i++) Return += (Returns[i])[0]/N; // 1/N 
		ReturnN = (ReturnN+1)*(Return+1)-1;
		
		markowitzReturn(Weights,0);	// min variance
		for(Return=0,i=0; i&lt;N; i++) Return += Weights[i]*(Returns[i])[0];
		ReturnMin = (ReturnMin+1)*(Return+1)-1;
		
		markowitzReturn(Weights,1);	// max return
		for(Return=0,i=0; i&lt;N; i++) Return += Weights[i]*(Returns[i])[0];
		ReturnMax = (ReturnMax+1)*(Return+1)-1;

		markowitzReturn(Weights,BestVariance); // max Sharpe
		for(Return=0,i=0; i&lt;N; i++) Return += Weights[i]*(Returns[i])[0];
		ReturnBest = (ReturnBest+1)*(Return+1)-1;

		plot("1/N",100*ReturnN,AXIS2,BLACK);
		plot("Max Sharpe",100*ReturnBest,AXIS2,GREEN);
		plot("Max Return",100*ReturnMax,AXIS2,RED);
		plot("Min Variance",100*ReturnMin,AXIS2,BLUE);
	}
}</pre>
<p>The script goes through 7 years of historical data, and stores the daily returns in the <strong>Returns</strong> data series. At the first trading day of every month (<strong>tdm() == 1</strong>) it computes the means and the covariance matrix of the last 252 days, then calculates the efficient frontier. This time we also apply a 0.5 weight constraint to the minimum variance point. Based on this efficient frontier, we compute the daily total return with equal weights (<strong>ReturnN</strong>), best Sharpe ratio (<strong>ReturnBest</strong>), minimum variance (<strong>ReturnMin</strong>) and maximum Return (<strong>ReturnMax</strong>). The weights remain unchanged until the next rebalancing, this way establishing an out of sample test. The four daily returns are added up to 4 different equity curves :</p>
<p> <a href="http://www.financial-hacker.com/wp-content/uploads/2016/12/MVO_AOK-1.png"><img loading="lazy" decoding="async" class="alignnone wp-image-1788 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2016/12/MVO_AOK-1.png" width="1079" height="301" srcset="https://financial-hacker.com/wp-content/uploads/2016/12/MVO_AOK-1.png 1079w, https://financial-hacker.com/wp-content/uploads/2016/12/MVO_AOK-1-300x84.png 300w, https://financial-hacker.com/wp-content/uploads/2016/12/MVO_AOK-1-768x214.png 768w, https://financial-hacker.com/wp-content/uploads/2016/12/MVO_AOK-1-1024x286.png 1024w" sizes="auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></a></p>
<p>We can see that MVO improves the portfolio in all three variants, in spite of its bad reputation. The black line is the 1/N portfolio with equal weights for all asset. The blue line is the minimum variance portfolio &#8211; we can see that it produces slightly better profits than 1/N, but with much lower volatility. The red line is the maximum return portfolio with the best profit, but high volatility and sharp drawdowns. The green line, the maximum Sharpe portfolio, is somewhere inbetween. Different portfolio compositions can produce a different order of lines, but the blue and green lines have almost always a much better Sharpe ratio than the black line. Since the minimum variance portfolio can be traded with higher leverage due to the smaller drawdowns, it often produces the highest profits.</p>
<p>For checking the monthly rebalancing of the capital allocation weights, we can display the weights in a heatmap:</p>
<p><a href="http://www.financial-hacker.com/wp-content/uploads/2016/12/MVO_s.png"><img loading="lazy" decoding="async" class="alignnone wp-image-1790 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2016/12/MVO_s.png" width="1551" height="162" srcset="https://financial-hacker.com/wp-content/uploads/2016/12/MVO_s.png 1551w, https://financial-hacker.com/wp-content/uploads/2016/12/MVO_s-300x31.png 300w, https://financial-hacker.com/wp-content/uploads/2016/12/MVO_s-768x80.png 768w, https://financial-hacker.com/wp-content/uploads/2016/12/MVO_s-1024x107.png 1024w, https://financial-hacker.com/wp-content/uploads/2016/12/MVO_s-1200x125.png 1200w" sizes="auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></a></p>
<p>The horizontal axis is the month of the simulation, the vertical axis the asset number. High weights are red and low weights are blue. The weight distribution above is for the maximum Sharpe portfolio of the 6 ETFs.</p>
<h3>The final money parking system</h3>
<p>After all those experiments we can now code our long-term system. It shall work in the following way:</p>
<ul style="list-style-type: square;">
<li>The efficient frontier is calculated from daily returns of the last 252 trading days, i.e. one year. That&#8217;s a good time period for MVO according to (1), since most ETFs show 1-year momentum.<br />
  </li>
<li>The system rebalances the portfolio once per month. Shorter time periods, such as daily or weekly rebalancing, showed no advantage in my tests, but reduced the profit due to higher trading costs. Longer time periods, such as 3 months, let the system deteriorate.<br />
  </li>
<li> The point on the efficient frontier can be set up with a slider between minimum variance and maximum Sharpe. This way you can control the risk of the system. <br />
  </li>
<li>We use a 50% weight constraint at minimum variance. It&#8217;s then not anymore the optimal portfolio, but according to (1) &#8211; and my tests have confirmed this &#8211; it often improves the out of sample balance due to better diversification.</li>
</ul>
<p>Here&#8217;s the script:</p>
<pre class="prettyprint">#define LEVERAGE 4	// 1:4 leverage
#define DAYS	252 	// 1 year
#define NN	30	// max number of assets

function run()
{
	BarPeriod = 1440;
	LookBack = DAYS;

	string Names[NN];
	vars	Returns[NN];
	var	Means[NN];
	var	Covariances[NN][NN];
	var	Weights[NN];

	var TotalCapital = slider(1,1000,0,10000,"Capital","Total capital to distribute");
	var VFactor = slider(2,10,0,100,"Risk","Variance factor");
	
	int N = 0;
	while(Names[N] = loop( 
		"TLT","LQD","SPY","GLD","VGLT","AOK"))
	{
		if(is(INITRUN))
			assetHistory(Names[N],FROM_YAHOO);
		asset(Names[N]);
		Returns[N] = series((priceClose(0)-priceClose(1))/priceClose(1));
		if(N++ &gt;= NN) break;
	}

	if(is(EXITRUN)) {
		int i,j;
		for(i=0; i&lt;N; i++) {
			Means[i] = Moment(Returns[i],LookBack,1);
			for(j=0; j&lt;N; j++)
				Covariances[N*i+j] = Covariance(Returns[i],Returns[j],LookBack);	
		}
		var BestVariance = markowitz(Covariances,Means,N,0.5);
		var MinVariance = markowitzVariance(0,0);
		markowitzReturn(Weights,MinVariance+VFactor/100.*(BestVariance-MinVariance));

		for(i=0; i&lt;N; i++) {
			asset(Names[i]);
			MarginCost = priceClose()/LEVERAGE;
			int Position = TotalCapital*Weights[i]/MarginCost;
			printf("\n%s:  %d Contracts at %.0f$",Names[i],Position,priceClose());
		}
	}
}</pre>
<p>On Zorro&#8217;s panel you can set up the invested capital with a slider (<strong>TotalCapital</strong>) between 0 and 10,000$. A second slider (<strong>VFactor</strong>) is for setting up the desired risk from 0 to 100%: At 0 you&#8217;re trading with minimum variance, at 100 with maximum Sharpe ratio.</p>
<p><a href="http://www.financial-hacker.com/wp-content/uploads/2016/05/sliders.png"><img loading="lazy" decoding="async" class="alignnone wp-image-1808 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2016/05/sliders.png" width="289" height="85" /></a></p>
<p>This script advises only, but does not trade: For automated trading it, you would need an <a href="http://www.financial-hacker.com/dear-brokers/" target="_blank" rel="noopener noreferrer">API plugin</a> to a ETF broker, such as IB. But the free Zorro version only has plugins for Forex/CFD brokers; the IB plugin is not free. However, since positions are only opened or closed once per month and price data is free from Yahoo, you do not really need an API connection for trading a MVO portfolio. Just fire up the above script once every month, and check what it prints out:</p>
<pre class="prettyprint">TLT:  0 Contracts at 129$
LQD:  0 Contracts at 120$
SPY:  3 Contracts at 206$
GLD:  16 Contracts at 124$
VGLT:  15 Contracts at 80$
AOK:  0 Contracts at 32$
</pre>
<p>Apparently, the optimal portfolio for this month consists of 3 contracts SPY, 16 contracts GLD, and 15 VGLT contracts. You can now manually open or close those positions in your broker&#8217;s trading platform until your portfolio matches the printed advice. Leverage is 4 by default, but you can change this to your broker&#8217;s leverage in the #define at the begin of the script. For a script that trades, simply replace the <strong>printf</strong> statement with a trade command that opens or closes the difference to the current position of the asset. This, too, is left as an exercise to the reader&#8230;</p>
<h3>MVO vs. OptimalF</h3>
<p>It seems natural to use MVO not only for a portfolio of many assets, but also for a portfolio of many trading systems. I&#8217;ve tested this with the <a href="http://manual.zorro-project.com/zsystems.htm" target="_blank" rel="noopener noreferrer">Z12 system</a> that comes with Zorro and contains about 100 different system/asset combinations. It turned out that MVO did not produce better results than Ralph Vince&#8217;s <strong>OptimalF</strong> factors that are originally used by the system. OptimalF factors do not consider correlations between components, but they do consider the drawdown depths, while MVO is only based on means and covariances. The ultimate solution for such a portfolio of many trading systems might be a combination of MVO for the capital distribution and OptimalF for weight constraints. I have not tested this yet, but it&#8217;s on my to do list.</p>
<p>I&#8217;ve added all scripts to the 2016 script repository. You&#8217;ll need Zorro 1.44 or above for running them. And after you made your first million with the MVO script, don&#8217;t forget to <a href="http://manual.zorro-project.com/restrictions.htm" target="_blank" rel="noopener noreferrer">sponsor</a> Zorro generously! <img src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<hr />
<h3>Papers</h3>
<ol>
<li>Momentum and Markowitz &#8211; A Golden Combination: <a href="http://papers.ssrn.com/sol3/papers.cfm?abstract_id=2606884" target="_blank" rel="noopener noreferrer">Keller.Butler.Kipnis.2015</a><br />
  </li>
<li>Harry M. Markowitz, Portfolio Selection, Wiley 1959<br />
  </li>
<li>MVO overview at <a href="https://www.guidedchoice.com/video/dr-harry-markowitz-father-of-modern-portfolio-theory/" target="_blank" rel="noopener noreferrer">guidedchoice.com</a></li>
</ol>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/get-rich-slowly/feed/</wfw:commentRss>
			<slash:comments>89</slash:comments>
		
		
			</item>
	</channel>
</rss>
