<?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>Arbitrage &#8211; The Financial Hacker</title>
	<atom:link href="https://financial-hacker.com/tag/statistical-arbitrage/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>Arbitrage &#8211; The Financial Hacker</title>
	<link>https://financial-hacker.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<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 fetchpriority="high" decoding="async" class="wp-image-2392 size-large" src="http://www.financial-hacker.com/wp-content/uploads/2017/07/Warren-e1494606853331-1024x552.jpg" alt="" width="840" height="453" srcset="https://financial-hacker.com/wp-content/uploads/2017/07/Warren-e1494606853331-1024x552.jpg 1024w, https://financial-hacker.com/wp-content/uploads/2017/07/Warren-e1494606853331-300x162.jpg 300w, https://financial-hacker.com/wp-content/uploads/2017/07/Warren-e1494606853331-768x414.jpg 768w, https://financial-hacker.com/wp-content/uploads/2017/07/Warren-e1494606853331.jpg 1065w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></a><figcaption id="caption-attachment-2392" class="wp-caption-text">Warren, Ohio &#8211; HFT Mecca of the world (image by Jack Pearce / Wikipedia Commons)</figcaption></figure>
<p>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 decoding="async" class="wp-image-2599 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2017/08/HFTChart_SPY.png" alt="" width="1701" height="401" srcset="https://financial-hacker.com/wp-content/uploads/2017/08/HFTChart_SPY.png 1701w, https://financial-hacker.com/wp-content/uploads/2017/08/HFTChart_SPY-300x71.png 300w, https://financial-hacker.com/wp-content/uploads/2017/08/HFTChart_SPY-768x181.png 768w, https://financial-hacker.com/wp-content/uploads/2017/08/HFTChart_SPY-1024x241.png 1024w, https://financial-hacker.com/wp-content/uploads/2017/08/HFTChart_SPY-1200x283.png 1200w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></a><figcaption id="caption-attachment-2599" class="wp-caption-text">SPY (black) vs. ES (red), October 5, 2017, 10:01:25 &#8211; 10:01.35</figcaption></figure>
<p>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/15.0.3/72x72/2122.png" alt="™" class="wp-smiley" style="height: 1em; max-height: 1em;" /> or Nanotick<img src="https://s.w.org/images/core/emoji/15.0.3/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>Build Better Strategies! Part 2: Model-Based Systems</title>
		<link>https://financial-hacker.com/build-better-strategies-part-2-model-based-systems/</link>
					<comments>https://financial-hacker.com/build-better-strategies-part-2-model-based-systems/#comments</comments>
		
		<dc:creator><![CDATA[jcl]]></dc:creator>
		<pubDate>Fri, 25 Dec 2015 12:34:08 +0000</pubDate>
				<category><![CDATA[Indicators]]></category>
		<category><![CDATA[System Development]]></category>
		<category><![CDATA[Arbitrage]]></category>
		<category><![CDATA[Bandpass filter]]></category>
		<category><![CDATA[Brexit]]></category>
		<category><![CDATA[CHF]]></category>
		<category><![CDATA[Currency strength]]></category>
		<category><![CDATA[Curve patterns]]></category>
		<category><![CDATA[Cycles]]></category>
		<category><![CDATA[Earnings]]></category>
		<category><![CDATA[Ehlers]]></category>
		<category><![CDATA[Fisher transformation]]></category>
		<category><![CDATA[Frechet algorithm]]></category>
		<category><![CDATA[Gap]]></category>
		<category><![CDATA[Heteroskedasticity]]></category>
		<category><![CDATA[Hurst exponent]]></category>
		<category><![CDATA[Market Meanness Index]]></category>
		<category><![CDATA[Mean Reversion]]></category>
		<category><![CDATA[Momentum]]></category>
		<category><![CDATA[Price shock]]></category>
		<category><![CDATA[Seasonality]]></category>
		<category><![CDATA[Spectral filter]]></category>
		<category><![CDATA[Support and resistance]]></category>
		<guid isPermaLink="false">http://www.financial-hacker.com/?p=318</guid>

					<description><![CDATA[Trading systems come in two flavors: model-based and data-mining. This article deals with model based strategies. Even when the basic algorithms are not complex, properly developing them has its difficulties and pitfalls (otherwise anyone would be doing it). A significant market inefficiency gives a system only a relatively small edge. Any little mistake can turn &#8230; <a href="https://financial-hacker.com/build-better-strategies-part-2-model-based-systems/" class="more-link">Continue reading<span class="screen-reader-text"> "Build Better Strategies! Part 2: Model-Based Systems"</span></a>]]></description>
										<content:encoded><![CDATA[<p>Trading systems come in two flavors: <strong>model-based</strong> and <strong>data-mining</strong>. This article deals with model based strategies. Even when the basic algorithms are not complex, properly developing them has its difficulties and pitfalls (otherwise anyone would be doing it). A significant market inefficiency gives a system only a <strong>relatively small edge</strong>. Any little mistake can turn a winning strategy into a losing one. And you will not necessarily notice this in the backtest. <span id="more-318"></span></p>
<p>Developing a model-based strategy begins with the <strong>market inefficiency</strong> that you want to exploit. The inefficiency produces a <strong>price anomaly</strong> or <strong>price pattern</strong> that you can describe with a qualitative or quantitative <strong>model</strong>. Such a model predicts the current price <em><strong>y<sub>t</sub></strong></em> from the previous price <em><strong>y<sub>t-1</sub></strong></em> plus some function <em><strong>f</strong></em> of a limited number of previous prices plus some noise term <strong>ε</strong>:</p>
<p style="text-align: center;">[pmath size=16]y_t ~=~ y_{t-1} + f(y_{t-1},~&#8230;, ~y_{t-n}) + epsilon[/pmath]</p>
<p>The time distance between the prices <em><strong>y<sub>t</sub></strong></em> is the <strong>time frame</strong> of the model; the number <em><strong>n</strong></em> of prices used in the function <em><strong>f</strong></em> is the <strong>lookback period</strong> of the model. The higher the predictive <em><strong>f</strong></em> term in relation to the nonpredictive <strong>ε</strong> term, the better is the strategy. Some traders claim that their favorite method does not predict, but &#8216;reacts on the market&#8217; or achieves a positive return by some other means. On a certain trader forum you can even encounter a math professor who re-invented the grid trading system, and praised it as non-predictive and even able to trade a random walk curve. But systems that do not predict <em><strong>y<sub>t</sub></strong></em> in some way must rely on luck; they only can redistribute risk, for instance exchange a high risk of a small loss for a low risk of a high loss. The profit expectancy stays negative. As far as I know, the professor is still trying to sell his grid trader, still advertising it as non-predictive, and still regularly blowing his demo account with it. </p>
<p>Trading by throwing a coin loses the transaction costs. But trading by applying the wrong model &#8211; for instance, trend following to a mean reverting price series &#8211; can cause much higher losses. The average trader indeed loses more than by random trading (about 13 pips per trade according to FXCM statistics). So it&#8217;s not sufficient to have a model; you must also prove that it is valid for the market you trade, at the time you trade, and with the used time frame and lookback period.</p>
<p>Not all price anomalies can be exploited. Limiting stock prices to 1/16 fractions of a dollar is clearly an inefficiency, but it&#8217;s probably difficult to use it for prediction or make money from it. The working model-based strategies that I know, either from theory or because we&#8217;ve been contracted to code some of them, can be classified in several categories. The most frequent are:</p>
<h3>1. Trend </h3>
<p>Momentum in the price curve is probably the most significant and most exploited anomaly. No need to elaborate here, as trend following was the topic of a whole <a href="http://www.financial-hacker.com/trend-delusion-or-reality/">article series</a> on this blog. There are many methods of trend following, the classic being a <strong>moving average crossover</strong>. This &#8216;hello world&#8217; of strategies (<a href="http://www.financial-hacker.com/hackers-tools-zorro-and-r/">here</a> the scripts in R and in C) routinely fails, as it does not distinguish between real momentum and random peaks or valleys in the price curve.</p>
<p>The problem: momentum does not exist in all markets all the time. Any asset can have long non-trending periods. And contrary to popular belief this is not necessarily a &#8216;sidewards market&#8217;. A random walk curve can go up and down and still has zero momentum. Therefore, some good filter that detects the real market regime is essential for trend following systems. Here&#8217;s a minimal Zorro strategy that uses a lowpass filter for detecting trend reversal, and the <a href="http://www.financial-hacker.com/the-market-meanness-index/">MMI</a> indicator for determining when we&#8217;re entering trend regime:</p>
<pre class="prettyprint">function run()
{
  vars Price = series(price());
  vars Trend = series(LowPass(Price,500));
	
  vars MMI_Raw = series(MMI(Price,300));
  vars MMI_Smooth = series(LowPass(MMI_Raw,500));
	
  if(falling(MMI_Smooth)) {
    if(valley(Trend))
      reverseLong(1);
    else if(peak(Trend))
      reverseShort(1);
  }
}</pre>
<p>The profit curve of this strategy:</p>
<figure id="attachment_1224" aria-describedby="caption-attachment-1224" style="width: 879px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2015/12/momentum.png"><img decoding="async" class="wp-image-1224 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2015/12/momentum.png" alt="" width="879" height="321" srcset="https://financial-hacker.com/wp-content/uploads/2015/12/momentum.png 879w, https://financial-hacker.com/wp-content/uploads/2015/12/momentum-300x110.png 300w, https://financial-hacker.com/wp-content/uploads/2015/12/momentum-768x280.png 768w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></a><figcaption id="caption-attachment-1224" class="wp-caption-text">Momentum strategy profit curve</figcaption></figure>
<p>(For the sake of simplicity all strategy snippets on this page are barebone systems with no exit mechanism other than reversal, and no stops, trailing, parameter training, money management, or other gimmicks. Of course the backtests mean in no way that those are profitable systems. The P&amp;L curves are all from EUR/USD, an asset good for demonstrations since it seems to contain a little bit of every possible inefficiency). </p>
<h3><strong>2. Mean reversion</strong></h3>
<p>A mean reverting market believes in a &#8216;real value&#8217; or &#8216;fair price&#8217; of an asset. Traders buy when the actual price is cheaper than it ought to be in their opinion, and sell when it is more expensive. This causes the price curve to revert back to the mean more often than in a random walk. Random data are mean reverting 75% of the time (proof <a href="http://www.financial-hacker.com/the-market-meanness-index/">here</a>), so anything above 75% is caused by a market inefficiency. A model:</p>
<p>[pmath size=16]y_t ~=~ y_{t-1} ~-~ 1/{1+lambda}(y_{t-1}- hat{y}) ~+~ epsilon[/pmath]</p>
<p>[pmath size=16]y_t[/pmath] = price at bar <em><strong>t<br />
 </strong></em>[pmath size=16]hat{y}[/pmath] = fair price<br />
 [pmath size=16]lambda[/pmath] = half-life factor<br />
 [pmath size=16]epsilon[/pmath] = some random noise term</p>
<p>The higher the half-life factor, the weaker is the mean reversion. The half-life of mean reversion in price series ist normally in the range of  50-200 bars. You can calculate <em><strong>λ</strong></em> by linear regression between <em><strong>y<sub>t-1</sub></strong></em> and <em><strong>(y<sub>t-1</sub>-y<sub>t</sub>)</strong></em>. The price series need not be stationary for experiencing mean reversion, since the fair price is allowed to drift. It just must drift less as in a random walk. Mean reversion is usually exploited by removing the trend from the price curve and normalizing the result. This produces an oscillating signal that can trigger trades when it approaches a top or bottom. Here&#8217;s the script of a simple mean reversion system:</p>
<pre class="prettyprint">function run()
{
  vars Price = series(price());
  vars Filtered = series(HighPass(Price,30));
  vars Signal = series(FisherN(Filtered,500));
  var Threshold = 1.0;

  if(Hurst(Price,500) &lt; 0.5) { // do we have mean reversion?
    if(crossUnder(Signal,-Threshold))
      reverseLong(1); 
    else if(crossOver(Signal,Threshold))
      reverseShort(1);
  }
} </pre>
<p>The highpass filter dampens all cycles above 30 bars and thus removes the trend from the price curve. The result is normalized by the <strong>Fisher transformation </strong>which produces a Gaussian distribution of the data. This allows us to determine fixed thresholds at <strong>1</strong> and <strong>-1</strong> for separating the tails from the resulting bell curve. If the price enters a tail in any direction, a trade is triggered in anticipation that it will soon return into the bell&#8217;s belly. For detecting mean reverting regime, the script uses the <strong>Hurst Exponent</strong>. The exponent is 0.5 for a random walk. Above 0.5 begins momentum regime and below 0.5 mean reversion regime.</p>
<figure id="attachment_1226" aria-describedby="caption-attachment-1226" style="width: 879px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2015/12/meanreversion.png"><img loading="lazy" decoding="async" class="wp-image-1226 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2015/12/meanreversion.png" alt="" width="879" height="301" srcset="https://financial-hacker.com/wp-content/uploads/2015/12/meanreversion.png 879w, https://financial-hacker.com/wp-content/uploads/2015/12/meanreversion-300x103.png 300w, https://financial-hacker.com/wp-content/uploads/2015/12/meanreversion-768x263.png 768w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></a><figcaption id="caption-attachment-1226" class="wp-caption-text">Mean reversion profit curve</figcaption></figure>
<h3>3. Statistical Arbitrage</h3>
<p>Strategies can exploit the similarity between two or more assets.  This allows to hedge the first asset by a reverse position in the second asset, and this way derive profit from mean reversion of their price difference:</p>
<p>[pmath size=16]y ~=~ h_1 y_1 &#8211; h_2 y_2[/pmath]</p>
<p>where <em><strong>y<sub>1</sub></strong></em> and <em><strong>y<sub>2</sub></strong></em> are the prices of the two assets and the multiplication factors <em><strong>h<sub>1</sub></strong></em> and <em><strong>h<sub>2</sub></strong></em> their <strong>hedge ratios</strong>. The hedge ratios are calculated in a way that the mean of the difference <em><strong>y</strong></em> is zero or a constant value. The simplest method for calculating the hedge ratios is linear regression between <em><strong>y<sub>1</sub></strong></em> and <em><strong>y<sub>2</sub></strong></em>. A mean reversion strategy as above can then be applied to <em><strong>y.</strong></em>  </p>
<p>The assets need not be of the same type; a typical arbitrage system can be based on the price difference between an index ETF and its major stock. When <em><strong>y</strong></em> is not <strong>stationary</strong> &#8211; meaning that its mean tends to wander off slowly &#8211; the hedge ratios must be adapted in real time for compensating. <a href="https://mktstk.wordpress.com/2015/08/18/the-kalman-filter-and-pairs-trading/" target="_blank" rel="noopener">Here</a> is a proposal using a <strong>Kalman Filter</strong> by a fellow blogger.</p>
<p>The simple arbitrage system from the <a href="http://manual.zorro-project.com/Lecture4.htm" target="_blank" rel="noopener">R tutorial</a>:<!--?prettify linenums=true?--></p>
<pre class="prettyprint">require(quantmod)

symbols &lt;- c("AAPL", "QQQ")
getSymbols(symbols)

#define training set
startT  &lt;- "2007-01-01"
endT    &lt;- "2009-01-01"
rangeT  &lt;- paste(startT,"::",endT,sep ="")
tAAPL   &lt;- AAPL[,6][rangeT]
tQQQ   &lt;- QQQ[,6][rangeT]
 
#compute price differences on in-sample data
pdtAAPL &lt;- diff(tAAPL)[-1]
pdtQQQ &lt;- diff(tQQQ)[-1]
 
#build the model
model  &lt;- lm(pdtAAPL ~ pdtQQQ - 1)
 
#extract the hedge ratio (h1 is assumed 1)
h2 &lt;- as.numeric(model$coefficients[1])

#spread price (in-sample)
spreadT &lt;- tAAPL - h2 * tQQQ
 
#compute statistics of the spread
meanT    &lt;- as.numeric(mean(spreadT,na.rm=TRUE))
sdT      &lt;- as.numeric(sd(spreadT,na.rm=TRUE))
upperThr &lt;- meanT + 1 * sdT
lowerThr &lt;- meanT - 1 * sdT
 
#run in-sample test
spreadL  &lt;- length(spreadT)
pricesB  &lt;- c(rep(NA,spreadL))
pricesS  &lt;- c(rep(NA,spreadL))
sp       &lt;- as.numeric(spreadT)
tradeQty &lt;- 100
totalP   &lt;- 0

for(i in 1:spreadL) {
     spTemp &lt;- sp[i]
     if(spTemp &lt; lowerThr) {
        if(totalP &lt;= 0){
           totalP     &lt;- totalP + tradeQty
           pricesB[i] &lt;- spTemp
        }
     } else if(spTemp &gt; upperThr) {
       if(totalP &gt;= 0){
          totalP &lt;- totalP - tradeQty
          pricesS[i] &lt;- spTemp
       }
    }
}
</pre>
<h3>4. Price constraints</h3>
<p>A price constraint is an artificial force that causes a constant price drift or establishes a price range, floor, or ceiling. The most famous example was the <a href="http://www.financial-hacker.com/build-better-strategies/" target="_blank" rel="noopener">EUR/CHF price cap</a> mentioned in the first part of this series. But even after removal of the cap, the EUR/CHF price has still a constraint, this time not enforced by the national bank, but by the current strong asymmetry in EUR and CHF buying power. An extreme example of a ranging price is the EUR/DKK pair (see below). All such constraints can be used in strategies to the trader&#8217;s advantage. </p>
<figure id="attachment_1983" aria-describedby="caption-attachment-1983" style="width: 979px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2015/12/PlotCurve_EURDKK.png"><img loading="lazy" decoding="async" class="wp-image-1983 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2015/12/PlotCurve_EURDKK.png" width="979" height="321" srcset="https://financial-hacker.com/wp-content/uploads/2015/12/PlotCurve_EURDKK.png 979w, https://financial-hacker.com/wp-content/uploads/2015/12/PlotCurve_EURDKK-300x98.png 300w, https://financial-hacker.com/wp-content/uploads/2015/12/PlotCurve_EURDKK-768x252.png 768w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></a><figcaption id="caption-attachment-1983" class="wp-caption-text">EUR/DKK price range 2006-2016</figcaption></figure>
<h3>5. Cycles</h3>
<p>Non-seasonal cycles are caused by feedback from the price curve. When traders believe in a &#8216;fair price&#8217; of an asset, they often sell or buy a position when the price reaches a certain distance from that value, in hope of a reversal. Or they close winning positions when the favorite price movement begins to decelerate. Such effects can synchronize entries and exits among a large number of traders, and cause the price curve to oscillate with a period that is stable over several cycles. Often many such cycles are superposed on the curve, like this:</p>
<p>[pmath size=16]y_t ~=~ hat{y}  ~+~ sum{i}{}{a_i sin(2 pi t/C_i+D_i)} ~+~ epsilon[/pmath]</p>
<p>When you know the period <em><strong>C<sub>i</sub></strong></em> and the phase <em><strong>D<sub>i</sub></strong></em> of the dominant cycle, you can calculate the optimal trade entry and exit points as long as the cycle persists. Cycles in the price curve can be detected with spectral analysis functions &#8211; for instance, fast Fourier transformation (FFT) or simply a bank of narrow bandpass filters. Here is the frequency spectrum of the EUR/USD in October 2015:</p>
<figure id="attachment_1160" aria-describedby="caption-attachment-1160" style="width: 1599px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2015/11/spectrum.png"><img loading="lazy" decoding="async" class="wp-image-1160 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2015/11/spectrum.png" alt="" width="1599" height="321" srcset="https://financial-hacker.com/wp-content/uploads/2015/11/spectrum.png 1599w, https://financial-hacker.com/wp-content/uploads/2015/11/spectrum-300x60.png 300w, https://financial-hacker.com/wp-content/uploads/2015/11/spectrum-1024x206.png 1024w, https://financial-hacker.com/wp-content/uploads/2015/11/spectrum-1200x241.png 1200w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></a><figcaption id="caption-attachment-1160" class="wp-caption-text">EUR/USD spectrum, cycle length in bars</figcaption></figure>
<p>Exploiting cycles is a little more tricky than trend following or mean reversion. You need not only the cycle length of the dominant cycle of the spectrum, but also its phase (for triggering trades at the right moment) and its amplitude (for determining if there is a cycle worth trading at all). This is a barebone script:</p>
<pre class="prettyprint">function run()
{
  vars Price = series(price());
  var Phase = DominantPhase(Price,10);
  vars Signal = series(sin(Phase+PI/4));
  vars Dominant = series(BandPass(Price,rDominantPeriod,1));
  ExitTime = 10*rDominantPeriod;
  var Threshold = 1*PIP;
	
  if(Amplitude(Dominant,100) &gt; Threshold) {
    if(valley(Signal))
      reverseLong(1); 
    else if(peak(Signal))
      reverseShort(1);
  }
}</pre>
<p>The <strong>DominantPhase</strong> function determines both the phase and the cycle length of the dominant peak in the spectrum; the latter is stored in the <strong>rDominantPeriod</strong> variable. The phase is converted to a sine curve that is shifted ahead by <em><strong>π/4</strong></em>. With this trick we&#8217;ll get a sine curve that runs ahead of the price curve. Thus we do real price prediction here, only question is if the price will follow our prediction. This is determined by applying a bandpass filter centered at the dominant cycle to the price curve, and measuring its amplitude (the <em><strong>a<sub>i</sub></strong></em> in the formula). If the amplitude is above a threshold,  we conclude that we have a strong enough cycle. The script then enters long on a valley of the run-ahead sine curve, and short on a peak. Since cycles are shortlived, the duration of a trade is limited by <strong>ExitTime</strong> to a maximum of 10 cycles. </p>
<p><a href="http://www.financial-hacker.com/wp-content/uploads/2015/12/cycles2.png"><img loading="lazy" decoding="async" class="alignnone wp-image-1252 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2015/12/cycles2.png" alt="" width="879" height="321" srcset="https://financial-hacker.com/wp-content/uploads/2015/12/cycles2.png 879w, https://financial-hacker.com/wp-content/uploads/2015/12/cycles2-300x110.png 300w, https://financial-hacker.com/wp-content/uploads/2015/12/cycles2-768x280.png 768w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></a></p>
<p>We can see from the P&amp;L curve that there were long periods in 2012 and 2014 with no strong cycles in the EUR/USD price curve. </p>
<h3>6. Clusters</h3>
<p>The same effect that causes prices to oscillate can also let them cluster at certain levels. Extreme clustering can even produce &#8220;supply&#8221; and &#8220;demand&#8221; lines (also known as &#8220;<strong>support and resistance</strong>&#8220;), the favorite subjects in trading seminars. Expert seminar lecturers can draw support and resistance lines on any chart, no matter if it&#8217;s pork belly prices or last year&#8217;s baseball scores. However the mere existence of those lines remains debatable: There are few strategies that really identify and exploit them, and even less that really produce profits. Still, clusters in price curves are real and can be easily identified in a histogram similar to the cycles spectogram.</p>
<h3>7. Curve patterns</h3>
<p>They arise from repetitive behavior of traders. Traders not only produce, but also believe in many curve patterns; most &#8211; such as the famous &#8216;head and shoulders&#8217; pattern that is said to predict trend reversal &#8211; are myths (at least I have not found any statistical evidence of it, and heard of no other any research that ever confirmed the existence of predictive heads and shoulders in price curves). But some patterns, for instance &#8220;cups&#8221; or &#8220;half-cups&#8221;, really exist and can indeed precede an upwards or downwards movement. Curve patterns &#8211; not to be confused with <strong>candle patterns</strong> &#8211; can be exploited by pattern-detecting methods such as the <a href="http://zorro-project.com/manual/en/detect.htm" target="_blank" rel="noopener">Fréchet algorithm</a>. </p>
<p>A special variant of a curve pattern is the <strong>Breakout</strong> &#8211; a sudden momentum after a long sidewards movement. Is can be caused, for instance, by trader&#8217;s tendency to place their stop losses as a short distance below or above the current plateau. Triggering the first stops then accelerates the price movement until more and more stops are triggered. Such an effect can be exploited by a system that detects a sidewards period and then lies in wait for the first move in any direction.</p>
<h3>8. Seasonality</h3>
<p>&#8220;Season&#8221; does not necessarily mean a season of a year. Supply and demand can also follow monthly, weekly, or daily patterns that can be detected and exploited by strategies. For instance, the S&amp;P500 index is said to often move upwards in the first days of a month, or to show an upwards trend in the early morning hours before the main trading session of the day. Since seasonal effects are easy to exploit, they are often shortlived, weak, and therefore hard to detect by just eyeballing price curves. But they can be found by plotting a <a href="http://zorro-project.com/manual/en/profile.htm" target="_blank" rel="noopener">day, week, or month profile</a> of average price curve differences.</p>
<h3>9. Gaps</h3>
<p>When market participants contemplate whether to enter or close a position, they seem to come to rather similar conclusions when they have time to think it over at night or during the weekend. This can cause the price to start at a different level when the market opens again. Overnight or weekend price gaps are often more predictable than price changes during trading hours. And of course they can be exploited in a strategy. On the Zorro forum was recently a discussion about the &#8220;<a href="http://www.opserver.de/ubb7/ubbthreads.php?ubb=showflat&amp;Number=452807&amp;page=1" target="_blank" rel="noopener">One Night Stand System</a>&#8220;, a simple currency weekend-gap trader with mysterious profits.</p>
<h3>10. Autoregression and heteroskedasticity</h3>
<p>The latter is a fancy word for: &#8220;Prices jitter a lot and the jittering varies over time&#8221;. The ARIMA and GARCH models are the first models that you encounter in financial math. They assume that future returns or future volatility can be determined with a linear combination of past returns or past volatility. Those models are often considered purely theoretical and of no practical use. Not true: You can use them for predicting tomorrow&#8217;s price just as any other model. You can examine a <strong>correlogram</strong> &#8211; a statistics of the correlation of the current return with the returns of the previous bars &#8211; for finding out if an ARIMA model fits to a certain price series. Here are two excellent articles by fellow bloggers for using those models in trading strategies:  <a href="https://www.quantstart.com/articles/ARIMA-GARCH-Trading-Strategy-on-the-SP500-Stock-Market-Index-Using-R" target="_blank" rel="noopener">ARIMA+GARCH Trading Strategy on the S&amp;P500</a> and <a href="http://robotwealth.com/fitting-time-series-models-to-the-forex-market-are-arimagarch-predictions-profitable" target="_blank" rel="noopener">Are ARIMA/GARCH Predictions Profitable?</a></p>
<h3>11. Price shocks</h3>
<p>Price shocks often happen on Monday or Friday morning when companies or organizations publish good or bad news that affect the market. Even without knowing the news, a strategy can detect the first price reactions and quickly jump onto the bandwagon. This is especially easy when a large shock is shaking the markets. Here&#8217;s a simple Forex portfolio strategy that evaluates the relative strengths of currencies for detecting price shocks:</p>
<pre class="prettyprint">function run()
{
  BarPeriod = 60;
  ccyReset();
  string Name;
  while(Name = (loop(Assets)))
  {
    if(assetType(Name) != FOREX) 
      continue; // Currency pairs only
    asset(Name);
    vars Prices = series(priceClose());
    ccySet(ROC(Prices,1)); // store price change as strength
  }
  
// get currency pairs with highest and lowest strength difference
  string Best = ccyMax(), Worst = ccyMin();
  var Threshold = 1.0; // The shock level

  static char OldBest[8], OldWorst[8];	// static for keeping contents between runs
  if(*OldBest &amp;&amp; !strstr(Best,OldBest)) { // new strongest asset?
    asset(OldBest);
    exitLong();
    if(ccyStrength(Best) &gt; Threshold) {
      asset(Best);
      enterLong();
    }
  } 
  if(*OldWorst &amp;&amp; !strstr(Worst,OldWorst)) { // new weakest asset?
    asset(OldWorst);
    exitShort();
    if(ccyStrength(Worst) &lt; -Threshold) {
      asset(Worst);
      enterShort();
    }
  }

// store previous strongest and weakest asset names  
  strcpy(OldBest,Best);
  strcpy(OldWorst,Worst);
}</pre>
<p>The equity curve of the currency strength system (you&#8217;ll need Zorro 1.48 or above):</p>
<figure id="attachment_2004" aria-describedby="caption-attachment-2004" style="width: 879px" class="wp-caption alignnone"><a href="http://www.financial-hacker.com/wp-content/uploads/2015/12/CurrencyStrength_EURCHF.png"><img loading="lazy" decoding="async" class="wp-image-2004 size-full" src="http://www.financial-hacker.com/wp-content/uploads/2015/12/CurrencyStrength_EURCHF.png" width="879" height="341" srcset="https://financial-hacker.com/wp-content/uploads/2015/12/CurrencyStrength_EURCHF.png 879w, https://financial-hacker.com/wp-content/uploads/2015/12/CurrencyStrength_EURCHF-300x116.png 300w, https://financial-hacker.com/wp-content/uploads/2015/12/CurrencyStrength_EURCHF-768x298.png 768w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></a><figcaption id="caption-attachment-2004" class="wp-caption-text">Price shock exploiting system</figcaption></figure>
<p>The blue equity curve above reflects profits from small and large jumps of currency prices. You can clearly identify the <strong>Brexit</strong> and the <strong>CHF price cap shock</strong>. Of course such strategies would work even better if the news could be early detected and interpreted in some way. Some data services provide news events with a binary valuation, like &#8220;good&#8221; or &#8220;bad&#8221;. Especially of interest are <strong>earnings reports</strong>, as provided by data services such as Zacks or Xignite. Depending on which surprises the earnings report contains, stock prices and implied volatilities can rise or drop sharply at the report day, and generate quick profits.</p>
<p>For learning what can happen when news are used in more creative ways, I recommend the excellent <a href="http://www.amazon.de/gp/product/0099553279/ref=as_li_tl?ie=UTF8&amp;camp=1638&amp;creative=6742&amp;creativeASIN=0099553279&amp;linkCode=as2&amp;tag=worterbuchdes-21&quot;&gt;The Fear Index&lt;/a&gt;&lt;img src=&quot;http://ir-de.amazon-adsystem.com/e/ir?t=worterbuchdes-21&amp;l=as2&amp;o=3&amp;a=0099553279&quot; width=&quot;1&quot; height=&quot;1&quot; border=&quot;0&quot; alt=&quot;&quot; style=&quot;border:none !important; margin:0px !important;" target="_blank" rel="noopener">Fear Index</a> by Robert Harris &#8211; a mandatory book in any financial hacker&#8217;s library.</p>
<hr />
<p>This was the second part of the <a href="http://www.financial-hacker.com/build-better-strategies/">Build Better Strategies</a> series. The third part will deal with the process to develop a model-based strategy, from inital research up to building the user interface. In case someone wants to experiment with the code snippets posted here, I&#8217;ve added them to the 2015 scripts repository. They are no real strategies, though. The missing elements &#8211; parameter optimization, exit algorithms, money management etc. &#8211; will be the topic of the next part of the series.</p>
<p style="text-align: right;"><strong>⇒ <a href="http://www.financial-hacker.com/build-better-strategies-part-3-the-development-process/">Build Better Strategies &#8211; Part 3</a></strong></p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/build-better-strategies-part-2-model-based-systems/feed/</wfw:commentRss>
			<slash:comments>46</slash:comments>
		
		
			</item>
	</channel>
</rss>
