<?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>Petra on Programming &#8211; The Financial Hacker</title>
	<atom:link href="https://financial-hacker.com/category/petra-on-programming/feed/" rel="self" type="application/rss+xml" />
	<link>https://financial-hacker.com</link>
	<description>A new view on algorithmic trading</description>
	<lastBuildDate>Sat, 04 Apr 2026 13:08:55 +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>Petra on Programming &#8211; The Financial Hacker</title>
	<link>https://financial-hacker.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Coding the largest strategy ever</title>
		<link>https://financial-hacker.com/coding-the-largest-strategy-ever/</link>
					<comments>https://financial-hacker.com/coding-the-largest-strategy-ever/#comments</comments>
		
		<dc:creator><![CDATA[Petra Volkova]]></dc:creator>
		<pubDate>Mon, 30 Mar 2026 11:30:15 +0000</pubDate>
				<category><![CDATA[No Math]]></category>
		<category><![CDATA[Petra on Programming]]></category>
		<category><![CDATA[EasyLanguage]]></category>
		<category><![CDATA[TradeStation]]></category>
		<guid isPermaLink="false">https://financial-hacker.com/?p=4934</guid>

					<description><![CDATA[Recently I got a quite unusual job: Here’s an extremely profitable strategy for the TradeStation platform. It’s a bit large &#8211; about 3000 lines of EasyLanguage code. Please replicate that monster in C++ so that it runs on the Zorro platform, but still produces the same trades as on TS. While you’re at it, fix &#8230; <a href="https://financial-hacker.com/coding-the-largest-strategy-ever/" class="more-link">Continue reading<span class="screen-reader-text"> "Coding the largest strategy ever"</span></a>]]></description>
										<content:encoded><![CDATA[<p><em>Recently I got a quite unusual job: Here’s an extremely profitable strategy for the TradeStation platform. It’s a bit large &#8211; about 3000 lines of EasyLanguage code. Please replicate that monster in C++ so that it runs on the Zorro platform, but still produces the same trades as on TS. While you’re at it, fix any bugs that you encounter in the EasyLanguage code. You have 2 weeks. Good luck.</em></p>
<p><span id="more-4934"></span></p>
<p>The strategy in question is arguably the largest trading script ever written. At least it&#8217;s the largest I&#8217;ve ever seen. The system was created by a famous trader, and is said to produce immense annual returns for his customers. It had to be now migrated to Zorro. It&#8217;s not a single strategy, but a combination of multiple algorithms, similar to Zorro&#8217;s <a href="https://zorro-project.com/manual/en/zsystems.htm" target="_blank" rel="noopener">Z12 system</a>. However unlike Z12, it is not divided into separate algos that trade simultaneously. Rather, its many indicators, filters, entry and exit conditions can be combined with each other in multiple ways.  With the right combinations, you got the ultimate strategy. What are these magic combinations? I don’t know, but if you buy the system, you can maybe find out.</p>
<p>It turned out that coding the system in C++ was the easiest part of the job. The code was large, but well structured and not very complex. Using C++ reduced the code size by 50% and increased the speed by about 2000%. I guess some of its functions are now also more reliable. The challenging part was not the code conversion, but exactly replicating the trade behavior of TS. And that is the main topic of this article.</p>
<p>When you convert an EasyLanguage script to a different platform, you&#8217;ll face the problem that TS opens positions &#8211; or not &#8211; in a way you might not always expect. The usual command for entering a long position with an entry limit looks like this:</p>
<pre>TS:  Buy N Contracts Next Bar at MyEntry Limit;
Zorro:  enterLong(N,-MyEntry);</pre>
<p>Zorro would then just enter a pending trade at the given limit. But what TS does depends on the context. This is only partially documented, so I had to find out by experiment. Here are the rules by which TS appears to trade and fill positions (unless ‘pyramiding’ is enabled):</p>
<ul>
<li>If another buy or sell command was given within the current bar, the new command is ignored.</li>
<li>If a position in the same direction was already open, the command is ignored.</li>
<li>If a position in the opposite direction was already open, the command is executed and closes the opposite position when filled.</li>
<li>If a position in the same direction is currently pending, and if no other buy or sell short command was already given within the current bar, the entry limit of the pending position is set to the current entry limit.</li>
<li>If no position is open and no other buy or sell short command was given within the current bar, the command is executed.</li>
<li>If several pending positions hit their entry limits within the current bar, only the first one is filled.</li>
<li>If a position is filled, the fill price is the given entry limit with no slippage, but 1 point penalty.</li>
</ul>
<p>Similar rules apply for closing a position.</p>
<p>For replicating the TS behavior, you first need to set up Zorro’s trading parameters in this way:</p>
<pre class="prettyprint">void emulateTS()
{
  setf(TradeMode,TR_EXTEND); // update pending trades
  MaxLong = MaxShort = 1; // only 1 open position
  Hedge = 1; // close opposite positions
  Fill = 3; // next bar, optimistic mode
  Penalty = 1*PIP; // 1 point
  Slippage = 0; // don’t simulate slippage
  Interest = 0; // don’t calculate margin interest
}</pre>
<p>When the entry limit is hit, you must check whether it’s the first hit of the current bar or not. If not, don’t enter. You need a trade management function for this:</p>
<pre class="prettyprint">#define LastEntryBar AssetInt[0] 
int manage() // fill only the first pending position at any bar
{
  if(TradeIsEntry) {
    if (Bar == LastEntryBar)
      return 1; // don’t open it
    LastEntryBar = Bar;
  }
  return 16; // open it or wait for next entry
}</pre>
<p>TS adjusts the entry limits and stops of open positions only if no other buy/sell command was given before on the same bar:</p>
<pre class="prettyprint">#define LastBarLong AssetInt[1]; 
void buyNextBar(var Limit)
{
  if(Bar != LastBarLong) { // first command of the bar?
    setf(TradeMode,TR_ANYLIMIT|TR_ANYSTOP); // adjust limit/stop
    LastBarLong = Bar;
  } else // don’t adjust 
    resf(TradeMode,TR_ANYLIMIT|TR_ANYSTOP);
  enterLong(Lots,-Limit);
}

#define LastBarShort AssetInt[2]; 
void sellShortNextBar(var Limit) 
{
  if(Bar != LastBarShort) { // first command of the bar?
    setf(TradeMode,TR_ANYLIMIT|TR_ANYSTOP); // adjust limit/stop
    LastBarShort = Bar;
  } else // don’t adjust 
    resf(TradeMode,TR_ANYLIMIT|TR_ANYSTOP);
  enterShort(Lots,-Limit);
}</pre>
<p>I do not recommend using this code for other purposes than for emulating TradeStation. But the monster strategy with this trading code opened exactly the same positions at exactly the same time and entry price as the original system on TS.  Only exceptions were a few differences by rounding or by fixing bugs of the original code.</p>
<p>By the way, here are some code lines to replicate some often-used TradeStation parameters in C/C++:</p>
<pre class="prettyprint">int MarketPosition = sign(NumOpenLong-NumOpenShort); 
var PriceScale = 100; <em>// always 100 for stocks and futures</em> 
var MinMove = PIP * PriceScale; <em>// Stock: 0.01 * 100, Emini: 0.25 * 100</em> 
var PointValue = LotAmount / PriceScale; <em>// Stock: 0.01, Emini: 0.50</em> 
var BigPointValue = LotAmount; 
var EntryPrice = 0; 
for(current_trades) if(TradeIsOpen) EntryPrice = TradeFill;</pre>
<p>I hope this helps anyone who happens to get the task of replicating a large TradeStation system on a different platform. Good luck!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/coding-the-largest-strategy-ever/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>The One Euro Filter</title>
		<link>https://financial-hacker.com/the-one-euro-filter/</link>
					<comments>https://financial-hacker.com/the-one-euro-filter/#comments</comments>
		
		<dc:creator><![CDATA[Petra Volkova]]></dc:creator>
		<pubDate>Mon, 16 Mar 2026 10:07:33 +0000</pubDate>
				<category><![CDATA[Indicators]]></category>
		<category><![CDATA[Petra on Programming]]></category>
		<category><![CDATA[Ehlers]]></category>
		<category><![CDATA[Indicator]]></category>
		<category><![CDATA[Low-Lag]]></category>
		<guid isPermaLink="false">https://financial-hacker.com/?p=4925</guid>

					<description><![CDATA[Whenever John Ehlers writes about a new indicator, I crack it open and wire it straight into C for the Zorro platform. Or rather, I let ChatGPT do most of the work. The One Euro Filter is a minimalistic, yet surprisingly effective low-latency smoother that reacts instantly to volatility with less lag of the usual &#8230; <a href="https://financial-hacker.com/the-one-euro-filter/" class="more-link">Continue reading<span class="screen-reader-text"> "The One Euro Filter"</span></a>]]></description>
										<content:encoded><![CDATA[<p><em>Whenever <strong>John Ehlers</strong> writes about a new indicator, I crack it open and wire it straight into C for the Zorro platform. Or rather, I let ChatGPT do most of the work. The <strong>One Euro Filter</strong> is a minimalistic, yet surprisingly effective low-latency smoother that reacts instantly to volatility with less lag of the usual adaptive averages. This is achieved by dynamically adapting its time period.</em><span id="more-4925"></span></p>
<p>This is the <strong>OneEurFilter</strong> function in C, converted straight from Ehlers’ EasyLanguage code, the comments left in place:</p>
<pre class="prettyprint">var OneEurFilter(vars Data, int PeriodMin, var Factor)
{
  vars SmoothedDX = series(Data[0],2), 
    Smoothed = series(Data[0],2);
  var Alpha = 2*PI/(4*PI + 10);
//EMA the Delta Price
  SmoothedDX[0] = Alpha*(Data[0]-Data[1]) + (1.-Alpha)*SmoothedDX[1];
//Adjust cutoff period based on a fraction of the rate of change
  var Cutoff = PeriodMin + Factor*abs(SmoothedDX[0]);
//Compute adaptive alpha
  Alpha = 2*PI/(4*PI + Cutoff);
//Adaptive smoothing
  return Smoothed[0] = Alpha*Data[0] + (1.-Alpha)*Smoothed[1];
}</pre>
<p>This is how the OneEurFilter (blue line) looks when applied on a ES chart:</p>
<p><img fetchpriority="high" decoding="async" width="1112" height="571" class="wp-image-4926" src="https://financial-hacker.com/wp-content/uploads/2026/03/word-image-4925-1.png" srcset="https://financial-hacker.com/wp-content/uploads/2026/03/word-image-4925-1.png 1112w, https://financial-hacker.com/wp-content/uploads/2026/03/word-image-4925-1-300x154.png 300w, https://financial-hacker.com/wp-content/uploads/2026/03/word-image-4925-1-1024x526.png 1024w, https://financial-hacker.com/wp-content/uploads/2026/03/word-image-4925-1-768x394.png 768w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></p>
<p>The price curve is well reproduced with almost no lag. Cheap, efficient, low-latency — 1 Euro well spent. The code can be downloaded from the 2026 script repository.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/the-one-euro-filter/feed/</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		
			</item>
		<item>
		<title>The Points-and-Line Chart</title>
		<link>https://financial-hacker.com/the-points-and-line-chart/</link>
					<comments>https://financial-hacker.com/the-points-and-line-chart/#comments</comments>
		
		<dc:creator><![CDATA[Petra Volkova]]></dc:creator>
		<pubDate>Tue, 14 Oct 2025 13:45:25 +0000</pubDate>
				<category><![CDATA[Indicators]]></category>
		<category><![CDATA[Petra on Programming]]></category>
		<category><![CDATA[Amibroker]]></category>
		<category><![CDATA[Bars]]></category>
		<category><![CDATA[MACD]]></category>
		<guid isPermaLink="false">https://financial-hacker.com/?p=4869</guid>

					<description><![CDATA[Traders like charts with special bars, since they let the price curve appear smoother and more predictable as it really is. Some types of bars, such as Renko bars, even use fake prices for generating curves that appear to move straight upwards or downwards. In the TASC November issue, Mohamed Ashraf and Mohamed Meregy presented &#8230; <a href="https://financial-hacker.com/the-points-and-line-chart/" class="more-link">Continue reading<span class="screen-reader-text"> "The Points-and-Line Chart"</span></a>]]></description>
										<content:encoded><![CDATA[<p><em>Traders like charts with special bars, since they let the price curve appear smoother and more predictable as it really is. Some types of bars, such as <strong>Renko bars</strong>, even use fake prices for generating curves that appear to move straight upwards or downwards. In the TASC November issue, Mohamed Ashraf and Mohamed Meregy presented the <strong>Points and Line Chart</strong> that avoids this problem. At least in the standard variant, the prices in the chart are real, and can be used with indicators. In this article I&#8217;ll explain the usage of special bars with Zorro, and how ChatGPT can help with generating code.</em><span id="more-4869"></span></p>
<p>As far as I know, there is only one trading platform that supports custom bars natively. All other platforms require complicated workarounds. The author&#8217;s Amibroker code for the points-and-line chart filled six pages and looked a bit&#8230; awful. I did not want to touch it. Fortunately, my new friend Chad offered his help. The prompt:</p>
<p><strong>Dear Chad, please convert the AmiBroker code in this PDF to C for Zorro. Use the bar function for user-defined bars on </strong><a href="https://zorro-project.com/manual/en/bar.htm"><strong>https://zorro-project.com/manual/en/bar.htm</strong></a><strong>. Generate a DJIA chart with MACD and MACDSignal. For good code I’ll give you $100.</strong></p>
<p>(The $100 reward, in my experience, often improves Chad’s code. In the past, <em>“write good code or I break all your bones”</em> used to work even better, but not anymore since Chad 5.0 came out).</p>
<p>After about 3 minutes, Chad produced a lite-C script. It does still look a bit awful, but far better than the original code. I have cleaned it up at a few places, used different colors for the MACD, and added a ‘g’ to the names of global and static variables since this is my custom. This is the code of the <strong>bar</strong> function to generate Points-and-Line bars:</p>
<pre class="prettyprint">//////////////////////////////////////////////////////////
// PointsLine.c — Zorro C port of "Points &amp; Line" chart
// Ashraf &amp; Meregy, TASC Traders' Tips
// Conversion P. Volkova &amp; ChatGPT 5.0
//////////////////////////////////////////////////////////
// Modes
#define SCALE_DEFAULT 0
#define SCALE_ATR 1
#define SCALE_PERCENT 2
#define M_POINTSIZE 0
#define M_HIGHLOW 1
#define M_CLOSE 2

// User parameters
int gReverse = 3; // boxes needed to reverse
int gScale = SCALE_DEFAULT; // 0=Default, 1=ATR(14), 2=Percent
int gMethod = M_CLOSE; // 0=PointSize, 1=HighLow, 2=Close
var gPercent = 1.0; // % for SCALE_PERCENT

// Compute "box size"
var box(var Price)
{
  if(gScale == SCALE_DEFAULT) {
#define RNG(X,Y) if(Price &lt; X) return Y
    RNG(0.25,0.025);
    RNG(0.5,0.05);
    RNG(1,0.1);
    RNG(5,0.25);
    RNG(20,0.5);
    RNG(100,1);
    RNG(200,2);
    RNG(500,5);
    RNG(1000,10);
    RNG(2000,20);
    RNG(5000,50);
    RNG(10000,100);
    RNG(20000,200);
    return 500;
  }
  if(gScale = SCALE_ATR)
    return ATR(14);
  else // SCALE_PERCENT
    return Price*gPercent/100;
}
 
// User-defined bars
function bar(var *Open, var *High, var *Low, var *Close)
{
  var C = Close[0], H = High[0], L = Low[0];
  static int gDir = -1; // initially down
  static var gCF = C, gCR = C, gLF = C, gHR = C;
// box size
  var Box = fix0(box(C));
  var CF = ceil(C/Box)*Box,
  CR = floor(C/Box)*Box,
  LF = ceil(L/Box)*Box,
  HR = floor(H/Box)*Box;
   
  Switch (gMethod)
  {
  case M_POINTSIZE:
    if(CF &lt; gCF &amp;&amp; gDir &lt; 0) { // continue down, new box
      gCR = CF - Box; gCF = CF;
      Close[0] = CF; return 1;
    }
    if(gCF + Box*gReverse &lt;= CR &amp;&amp; gDir &lt; 0) {
      gCR = CR; gCF = CR + Box;
      Close[0] = CR; gDir = 1; return 1; // swap direction
    }
    if(gCR &lt; CR &amp;&amp; gDir &gt; 0) { // continue up
      gCR = CR; gCF = CR + Box; Close[0] = CR; return 1;
    }
    if(gCR - Box*gReverse &gt;= CF &amp;&amp; gDir &gt; 0) {
      gCF = CF; gCR = CF - Box; Close[0] = CF;
      gDir = -1; return 1;
    }
    break;
     
  case M_HIGHLOW:
    if(LF &lt; gLF &amp;&amp; gDir &lt; 0) {
      gHR = LF - Box; gLF = LF;
      Close[0] = L; return 1;
    }
    if(gLF + Box*gReverse &lt;= HR &amp;&amp; gDir &lt; 0) {
      gHR = HR; gLF = HR + Box;
      Close[0] = H; gDir = 1; return 1;
    }
    if(gHR &lt; HR &amp;&amp; gDir &gt; 0) {
      gHR = HR; gLF = HR + Box;
      Close[0] = H; return 1;
    }
    if(gHR - Box*gReverse &gt;= LF &amp;&amp; gDir &gt; 0) {
      gLF = LF; gHR = LF - Box;
      Close[0] = L; gDir = -1; return 1;
    }
    break;
     
  case M_CLOSE:
    if(CF &lt; gCF &amp;&amp; gDir &lt; 0) { // continue down
      gCR = CF-Box; gCF = CF;
    return 1;
    }
    if(gCF+Box*gReverse &lt;= CR &amp;&amp; gDir &lt; 0) { // go up
      gCR = CR; gCF = CR+Box;
      gDir = 1; return 1;
    }
    if(gCR &lt; CR &amp;&amp; gDir &gt; 0) {
      gCR = CR; gCF = CR+Box;
    return 1;
    }
    if(gCR-Box*gReverse &gt;= CF &amp;&amp; gDir &gt; 0) {
      gCF = CF; gCR = CF-Box;
      gDir = -1; return 1;
    }
    break;
  }
  return 4; // keep bar open, call again on next tick
}
</pre>
<p>The algorithm of the points-and-line chart in its 3 variants &#8211; POINTSIZE, HIGHLOW, and CLOSE &#8211; can be read up in the TASC article. Here I&#8217;ll only illustrate the usage of the <strong>bar()</strong> function for establishing special, event-driven bars instead of the usual time bars. The function evaluates the current and previous candle, modifies the current candle if needed, and returns <strong>1</strong> for beginning a new bar or <strong>4</strong> for continuing with the current bar. This way, all imaginable sorts of event driven bars can be generated, in the same way for backtests and for live trading. These bars are also displayed on the chart, and affect the scale of the X axis.</p>
<p>Since AmiBroker did not support standard indicators on a chart with special bars, the authors had exported the created chart and imported it as a fake asset price curve to Metastocks for using indicators with it. I wonder how this would work in live trading. Fortunately, the Zorro platform has no problems of this kind, since it treats standard bars and special bars in the same way. The <strong>run</strong> function looks just as usual:</p>
<pre class="prettyprint">function run()
{
  set(PLOTNOW,TICKS);
  BarPeriod = 1440;
  LookBack = 120;
  StartDate = 2017;
  EndDate = 2025;
  BarZone = EST;
  assetAdd("DJIA","STOOQ:^DJI");
  asset("DJIA");
  plot("MACD",MACDFix(seriesC(),14),NEW,RED);
  plot("Signal",rMACDSignal,0,GREY);
 }</pre>
<p>The function uses STOOQ as a price source and plots the MACD indicator (red) and its signal line (grey). The chart below reproduces the DJIA chart in the TASC article, and applies a standard MACD:</p>
<p><img decoding="async" width="885" height="479" class="wp-image-4870" src="https://financial-hacker.com/wp-content/uploads/2025/10/ein-bild-das-text-diagramm-reihe-zahl-enthalt-.png" alt="Ein Bild, das Text, Diagramm, Reihe, Zahl enthält.

KI-generierte Inhalte können fehlerhaft sein." srcset="https://financial-hacker.com/wp-content/uploads/2025/10/ein-bild-das-text-diagramm-reihe-zahl-enthalt-.png 885w, https://financial-hacker.com/wp-content/uploads/2025/10/ein-bild-das-text-diagramm-reihe-zahl-enthalt--300x162.png 300w, https://financial-hacker.com/wp-content/uploads/2025/10/ein-bild-das-text-diagramm-reihe-zahl-enthalt--768x416.png 768w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></p>
<p>Due to the variable length of bars, the time scale on the X axis is uneven; the DJIA moved a lot more in the years 2020 and 2022 than in the other years. The code can be downloaded from the 2025 script repository.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/the-points-and-line-chart/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>A Better Stock Rotation System</title>
		<link>https://financial-hacker.com/a-better-stock-rotation-system/</link>
					<comments>https://financial-hacker.com/a-better-stock-rotation-system/#comments</comments>
		
		<dc:creator><![CDATA[Petra Volkova]]></dc:creator>
		<pubDate>Sun, 27 Jul 2025 14:26:47 +0000</pubDate>
				<category><![CDATA[Petra on Programming]]></category>
		<category><![CDATA[System Development]]></category>
		<category><![CDATA[Amibroker]]></category>
		<category><![CDATA[Portfolio rotation]]></category>
		<guid isPermaLink="false">https://financial-hacker.com/?p=4861</guid>

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

					<description><![CDATA[Oscillator-type indicators swing around the zero line. They are often used for opening positions when oscillator exceeds a positive or negative threshold. In his article series about no-lag indicators, John Ehlers presents in the TASC June issue the Cybernetic Oscillator. It is built by applying a highpass and afterwards a lowpass filter to the price &#8230; <a href="https://financial-hacker.com/the-cybernetic-oscillator/" class="more-link">Continue reading<span class="screen-reader-text"> "The Cybernetic Oscillator"</span></a>]]></description>
										<content:encoded><![CDATA[<p><em>Oscillator-type indicators swing around the zero line. They are often used for opening positions when oscillator exceeds a positive or negative threshold. In his article series about no-lag indicators, <strong>John Ehlers</strong> presents in the TASC June issue the Cybernetic Oscillator. It is built by applying a highpass and afterwards a lowpass filter to the price curve, then normalizing the result.</em></p>
<p><span id="more-4847"></span></p>
<p>We already know Ehlers’ highpass filter from previous articles. It’s in the Zorro indicator library under the name <strong>HighPass3</strong>. The lowpass filter, Ehlers’ ‘SuperSmoother’, is also in the library, as well as the Sum of Sqares function for normalizing. This makes the Cybernetic Oscillator easy to code in C:</p>
<pre class="prettyprint">var CyberOsc(vars Data,int HPLength,int LPLength)
{
  vars HP = series(HighPass3(Data,HPLength));
  vars LP = series(Smooth(HP,LPLength));
  var RMS = sqrt(SumSq(LP,100)/100);
  return LP[0]/fix0(RMS);
}</pre>
<p>We apply two Cybernetic Oscillators, one with a short and one with a long highpass cutoff, to an S&amp;P500 chart from 2024:</p>
<pre class="prettyprint">void run()
{
  BarPeriod = 1440;
  LookBack = 250;
  StartDate = 20240301;
  EndDate = 20250407;
  asset("SPX500");
  plot("CyberOsc1",CyberOsc(seriesC(),30,20),NEW|LINE,RED);
  plot("CyberOsc2",CyberOsc(seriesC(),250,20),NEW|LINE,BLUE);
}</pre>
<p>The resulting chart replicates Ehler’s chart in the article. The red line reproduces the swings of the price curve, the blue line the long-term trend:</p>
<p><img loading="lazy" decoding="async" width="701" height="410" class="wp-image-4848" src="https://financial-hacker.com/wp-content/uploads/2025/05/word-image-4847-1.png" srcset="https://financial-hacker.com/wp-content/uploads/2025/05/word-image-4847-1.png 701w, https://financial-hacker.com/wp-content/uploads/2025/05/word-image-4847-1-300x175.png 300w" sizes="auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px" /></p>
<p>Now we test the Cybernetic Oscillator by using it for a swing trading system. We open positions in the direction of the trend. The code, replicated from Ehlers&#8217; EasyLanguage script:</p>
<pre class="prettyprint">void run()
{
  BarPeriod = 1440;
  LookBack = 250;
  StartDate = 2009;
  EndDate = 2025;
  Fill = 2; // enter at next open
  assetList("AssetsIB"); // simulate IBKR
  asset("SPY");
  vars LP = series(Smooth(seriesC(),20));
  vars BP1 = series(HighPass3(LP,55));
  var ROC1 = BP1[0] - BP1[2];
  vars BP2 = series(HighPass3(LP,156));
  var ROC2 = BP2[0] - BP2[2];

  if(!NumOpenLong &amp;&amp; ROC1 &gt; 0 &amp;&amp; ROC2 &gt; 0)
    enterLong();
  if(NumOpenLong &amp;&amp; (ROC1 &lt; 0 || ROC2 &lt; 0))
    exitLong();
}</pre>
<p>The system is opening a 1-share SPY position without reinvestment. We’re using commission, leverage and other trading parameters from a popular US broker for the simulation. Ehlers had produced his filter parameters with in-sample optimization, so take the result with a grain of salt. This is the equity curve:</p>
<p><img loading="lazy" decoding="async" width="701" height="405" class="wp-image-4849" src="https://financial-hacker.com/wp-content/uploads/2025/05/word-image-4847-2.png" srcset="https://financial-hacker.com/wp-content/uploads/2025/05/word-image-4847-2.png 701w, https://financial-hacker.com/wp-content/uploads/2025/05/word-image-4847-2-300x173.png 300w" sizes="auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px" /></p>
<p>Backtesting the same system with walk-forward optimization, for getting a more accurate result, is left as an exercise to the reader. Hint: you need to enter 4 extra lines. Ehlers’ system generates about 180 trades with 60% win rate and profit factor 2. The code can be downloaded from the 2025 script repository on <a href="https://financial-hacker.com">https://financial-hacker.com</a>.</p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/the-cybernetic-oscillator/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title>Trading the Channel</title>
		<link>https://financial-hacker.com/trading-the-channel/</link>
					<comments>https://financial-hacker.com/trading-the-channel/#comments</comments>
		
		<dc:creator><![CDATA[Petra Volkova]]></dc:creator>
		<pubDate>Wed, 09 Apr 2025 12:14:55 +0000</pubDate>
				<category><![CDATA[Indicators]]></category>
		<category><![CDATA[Petra on Programming]]></category>
		<category><![CDATA[System Development]]></category>
		<category><![CDATA[Channels]]></category>
		<category><![CDATA[Kaufman]]></category>
		<category><![CDATA[Momentum]]></category>
		<category><![CDATA[Regression]]></category>
		<guid isPermaLink="false">https://financial-hacker.com/?p=4834</guid>

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

					<description><![CDATA[In his TASC article series about no-lag indicators, John Ehlers presented last month the Ultimate Oscillator.  What&#8217;s so ultimate about it? Unlike other oscillators, it is supposed to indicate the current market direction with almost no lag. The Ultimate Oscillator is built from the difference of two highpass filters. The highpass function below is a &#8230; <a href="https://financial-hacker.com/ehlers-ultimate-oscillator/" class="more-link">Continue reading<span class="screen-reader-text"> "Ehlers&#8217; Ultimate Oscillator"</span></a>]]></description>
										<content:encoded><![CDATA[<p><em>In his TASC article series about no-lag indicators, <strong>John Ehlers</strong> presented last month the <strong>Ultimate Oscillator</strong>.  What&#8217;s so ultimate about it? Unlike other oscillators, it is supposed to indicate the current market direction with almost no lag.</em><span id="more-4823"></span></p>
<p>The Ultimate Oscillator is built from the difference of two highpass filters. The highpass function below is a straightforward conversion of Ehlers&#8217; EasyLanguage code to C:</p>
<pre class="prettyprint">var HighPass3(vars Data,int Period)
{
  var a1 = exp(-1.414*PI / Period);
  var c2 = 2*a1*cos(1.414*PI / Period);
  var c3 = -a1*a1;
  var c1 = (1.+c2-c3) / 4;
  vars HP = series(0,3);
  return HP[0] = c1*(Data[0]-2*Data[1]+Data[2])+c2*HP[1]+c3*HP[2];
}</pre>
<p>The function is named <strong>HighPass3</strong> because we have already 3 other highpass filters in the Zorro indicator library, all with different code. The Ultimate Oscillator is the difference of two highpass filters with different periods, and scaled by its root mean square for converting the output to standard deviations. Fortunately Zorro has already a sum of squares function, which makes the code shorter and simpler than Ehlers’ original:</p>
<pre class="prettyprint">var UltimateOsc(vars Data,int Edge,int Width)
{
  vars Signals = series(HighPass3(Data,Width*Edge)-HighPass3(Data,Edge));
  var RMS = sqrt(SumSq(Signals,100)/100);
  return Signals[0]/fix0(RMS);
}</pre>
<p>For checking its lag and smoothing power, we apply the Ultimate Oscillator to an S&amp;P500 chart from 2024:</p>
<pre class="prettyprint">void run()
{
  BarPeriod = 1440;
  StartDate = 20240101;
  EndDate = 20241231;
  asset("SPX500");
  plot("UltOsc", UltimateOsc(seriesC(),20,2),LINE,RED);
}</pre>
<p>The resulting chart replicates Ehler’s chart in the article. The Oscillator output is the red line:</p>
<p><img loading="lazy" decoding="async" width="701" height="408" class="wp-image-4824" src="https://financial-hacker.com/wp-content/uploads/2025/03/word-image-4823-1.png" srcset="https://financial-hacker.com/wp-content/uploads/2025/03/word-image-4823-1.png 701w, https://financial-hacker.com/wp-content/uploads/2025/03/word-image-4823-1-300x175.png 300w" sizes="auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px" /></p>
<p>We can see that the ultimate oscillator reproduces the market trend remarkably well and with no visible lag. The code can be downloaded from the 2025 script repository.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/ehlers-ultimate-oscillator/feed/</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		
			</item>
		<item>
		<title>The Ultimate Strength Index</title>
		<link>https://financial-hacker.com/the-ultimate-strength-index/</link>
					<comments>https://financial-hacker.com/the-ultimate-strength-index/#comments</comments>
		
		<dc:creator><![CDATA[Petra Volkova]]></dc:creator>
		<pubDate>Fri, 13 Dec 2024 09:47:55 +0000</pubDate>
				<category><![CDATA[Indicators]]></category>
		<category><![CDATA[Petra on Programming]]></category>
		<category><![CDATA[Ehlers]]></category>
		<category><![CDATA[RSI]]></category>
		<guid isPermaLink="false">https://financial-hacker.com/?p=4776</guid>

					<description><![CDATA[The RSI (Relative Strength Index) is a popular indicator used in many trading systems for filters or triggers. In TASC 12/2024 John Ehlers proposed a replacement for this indicator. His USI (Ultimate Strength Index) has the advantage of symmetry – the range is -1 to 1 – and, especially important, less lag. So it can &#8230; <a href="https://financial-hacker.com/the-ultimate-strength-index/" class="more-link">Continue reading<span class="screen-reader-text"> "The Ultimate Strength Index"</span></a>]]></description>
										<content:encoded><![CDATA[<p><em>The <strong>RSI</strong> (Relative Strength Index) is a popular indicator used in many trading systems for filters or triggers. In TASC 12/2024 John Ehlers proposed a replacement for this indicator. His <strong>USI</strong> (Ultimate Strength Index) has the advantage of symmetry – the range is -1 to 1 – and, especially important, less lag. So it can trigger trades earlier. Like the RSI, it enhances cycles and trends in the data, which makes it well suited for various sorts of trading systems. Let’s look how to realize it in code.</em><span id="more-4776"></span></p>
<p>The USI is based on another indicator by John Ehlers, the <strong>Ultimate Smoother</strong>. It was covered in a previous article. Here’s again its code in C:</p>
<pre class="prettyprint">var UltimateSmoother (var *Data, int Length)
{
  var f = (1.414*PI) / Length;
  var a1 = exp(-f);
  var c2 = 2*a1*cos(f);
  var c3 = -a1*a1;
  var c1 = (1+c2-c3)/4;
  vars US = series(*Data,4);
  return US[0] = (1-c1)*Data[0] + (2*c1-c2)*Data[1] - (c1+c3)*Data[2] + c2*US[1] + c3*US[2];
}</pre>
<p>Similar to the RSI, the Ultimate Strength Index calculates the normalized differences of the ups and downs in the price curve, after no-lag smoothing. Here’s Ehlers’ EasyLanguage code converted to C:</p>
<pre class="prettyprint">var UltimateStrength (int Length)
{
  vars SU = series(max(0,priceC(0) - priceC(1)));
  var USU = UltimateSmooth(series(SMA(SU,4)),Length);
  vars SD = series(max(0,priceC(1) - priceC(0)));
  var USD = UltimateSmooth(series(SMA(SD,4)),Length);
  return (USU-USD)/fix0(USU+USD);
}</pre>
<p>The <strong>fix0</strong> function is a convenience function that corrects possible divisions by zero. For checking the correctness of our conversion, we’re plotting the USI with two different time periods on a SPX chart. The code:</p>
<pre class="prettyprint">void run()
{
  BarPeriod = 1440;
  StartDate = 20230801;
  EndDate = 20240801;
  assetAdd("SPX","STOOQ:^SPX");
  asset("SPX");
  plot("USI(112)",UltimateStrength(112),NEW|LINE,BLUE);
  plot("USI(28)",UltimateStrength(28),NEW|LINE,BLUE);
}</pre>
<p>The resulting chart replicates the ES chart in Ehlers’ article:</p>
<p><img loading="lazy" decoding="async" width="774" height="537" class="wp-image-4777" src="https://financial-hacker.com/wp-content/uploads/2024/12/word-image-4776-1.png" srcset="https://financial-hacker.com/wp-content/uploads/2024/12/word-image-4776-1.png 774w, https://financial-hacker.com/wp-content/uploads/2024/12/word-image-4776-1-300x208.png 300w, https://financial-hacker.com/wp-content/uploads/2024/12/word-image-4776-1-768x533.png 768w" sizes="auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px" /></p>
<p>We can see that the long time frame USI enhances the begin and end of trends, the short time frame USI makes the cycles visible. The code can be downloaded from the 2024 script repository.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/the-ultimate-strength-index/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Ehlers’ Precision Trend Analysis</title>
		<link>https://financial-hacker.com/ehlers-precision-trend-analysis/</link>
					<comments>https://financial-hacker.com/ehlers-precision-trend-analysis/#comments</comments>
		
		<dc:creator><![CDATA[Petra Volkova]]></dc:creator>
		<pubDate>Mon, 19 Aug 2024 08:49:55 +0000</pubDate>
				<category><![CDATA[Indicators]]></category>
		<category><![CDATA[Petra on Programming]]></category>
		<category><![CDATA[Ehlers]]></category>
		<guid isPermaLink="false">https://financial-hacker.com/?p=4735</guid>

					<description><![CDATA[In TASC 8/24, John Ehlers presented a new algorithm for separating the trend line from a price curve, using spectral analysis functions. Trend lines are only useful for trading when they have little lag, so that trend changes can immediately trigger trade signals. The usual suspects like SMA, WMA, EMA are too laggy for this. &#8230; <a href="https://financial-hacker.com/ehlers-precision-trend-analysis/" class="more-link">Continue reading<span class="screen-reader-text"> "Ehlers’ Precision Trend Analysis"</span></a>]]></description>
										<content:encoded><![CDATA[<p><em>In TASC 8/24, <strong>John Ehlers</strong> presented a new algorithm for separating the trend line from a price curve, using spectral analysis functions. Trend lines are only useful for trading when they have little lag, so that trend changes can immediately trigger trade signals. The usual suspects like SMA, WMA, EMA are too laggy for this. Let&#8217;s see how good this new algorithm works. The functions below are a 1:1 conversion from Ehlers&#8217; TradeStation code to C.</em><span id="more-4735"></span></p>
<pre class="prettyprint">var HighPass3(vars Data, int Length)
{
  var f = 1.414*PI/Length;
  var a1 = exp(-f);
  var c2 = 2*a1*cos(f/2);
  var c3 = -a1*a1;
  var c1 = (1+c2-c3)/4;
  vars HP = series(0,4);
  return HP[0] = c1*(Data[0] - 2*Data[1] + Data[2]) + c2*HP[1] + c3*HP[2];
}

var TROC;
var Trend(vars Data,int Length1,int Length2)
{
  var HP1 = HighPass3(Data,Length1);
  var HP2 = HighPass3(Data,Length2);
  vars Trends = series(HP1-HP2,2);
  TROC = (Length2/6.28)*(Trends[0]-Trends[1]);
  return Trends[0];
}</pre>
<p>The filter name is <strong>HighPass3</strong> because Zorro got already threee other highpass filters in its library. We apply the <strong>trend</strong> function to an ES chart from 2022-2024, and plot also the highpass filter and the intermediate TROC variable:</p>
<pre class="prettyprint">void run()
{
  BarPeriod = 1440;
  StartDate = 20221001;
  EndDate = 20240401;
  LookBack = 250;
  assetAdd("ES","YAHOO:ES=F");
  asset("ES");
  plot("HP",HighPass3(seriesC(),250),NEW,RED);
  plot("Trend",Trend(seriesC(),250,40),NEW,RED);
  plot("ROC",TROC,0,BLUE);
}</pre>
<p>The resulting chart:</p>
<p><img loading="lazy" decoding="async" width="873" height="656" class="wp-image-4736" src="https://financial-hacker.com/wp-content/uploads/2024/08/word-image-4735-1.png" srcset="https://financial-hacker.com/wp-content/uploads/2024/08/word-image-4735-1.png 873w, https://financial-hacker.com/wp-content/uploads/2024/08/word-image-4735-1-300x225.png 300w, https://financial-hacker.com/wp-content/uploads/2024/08/word-image-4735-1-768x577.png 768w" sizes="auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></p>
<p>We can see that the red trend line in the bottom chart gives a pretty good approximation of the price curve trend with no noticable lag. The code can be downloaded from the 2024 script repository.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/ehlers-precision-trend-analysis/feed/</wfw:commentRss>
			<slash:comments>6</slash:comments>
		
		
			</item>
		<item>
		<title>Ehlers’ Ultimate Smoother</title>
		<link>https://financial-hacker.com/ehlers-ultimate-smoother/</link>
					<comments>https://financial-hacker.com/ehlers-ultimate-smoother/#comments</comments>
		
		<dc:creator><![CDATA[Petra Volkova]]></dc:creator>
		<pubDate>Fri, 17 May 2024 10:10:01 +0000</pubDate>
				<category><![CDATA[Indicators]]></category>
		<category><![CDATA[Petra on Programming]]></category>
		<category><![CDATA[Ehlers]]></category>
		<category><![CDATA[Smoothing]]></category>
		<guid isPermaLink="false">https://financial-hacker.com/?p=4717</guid>

					<description><![CDATA[In TASC 3/24, John Ehlers presented several functions for smoothing a price curve without lag, smoothing it even more, and applying a highpass and bandpass filter. No-lag smoothing, highpass, and bandpass filters are already available in the indicator library of the Zorro platform, but not Ehlers&#8217; latest invention, the Ultimate Smoother. It achieves its tremendous &#8230; <a href="https://financial-hacker.com/ehlers-ultimate-smoother/" class="more-link">Continue reading<span class="screen-reader-text"> "Ehlers’ Ultimate Smoother"</span></a>]]></description>
										<content:encoded><![CDATA[<p><em>In TASC 3/24, John Ehlers presented several functions for smoothing a price curve without lag, smoothing it even more, and applying a highpass and bandpass filter. No-lag smoothing, highpass, and bandpass filters are already available in the indicator library of the Zorro platform, but not Ehlers&#8217; latest invention, the Ultimate Smoother. It achieves its tremendous smoothing power by subtracting the high frequency components from the price curve, using a highpass filter.</em><span id="more-4717"></span></p>
<p>The function below is a straightforward conversion of Ehlers&#8217; EasyLanguage code to C:</p>
<pre class="prettyprint">var UltimateSmoother (var *Data, int Length)
{
  var f = (1.414*PI) / Length;
  var a1 = exp(-f);
  var c2 = 2*a1*cos(f);
  var c3 = -a1*a1;
  var c1 = (1+c2-c3)/4;
  vars US = series(*Data,4);
  return US[0] = (1-c1)*Data[0] + (2*c1-c2)*Data[1] - (c1+c3)*Data[2]
+ c2*US[1] + c3*US[2];
}</pre>
<p>For comparing lag and smoothing power, we apply the ultimate smoother, the super smoother from Zorro&#8217;s indicator library, and a standard EMA to an ES chart from 2023:</p>
<pre class="prettyprint">void run()
{
  BarPeriod = 1440;
  StartDate = 20230201;
  EndDate = 20231201;
  assetAdd("ES","YAHOO:ES=F");
  asset("ES");
  int Length = 30;
  plot("UltSmooth", UltimateSmoother(seriesC(),Length),LINE,MAGENTA);
  plot("Smooth",Smooth(seriesC(),Length),LINE,RED);
  plot("EMA",EMA(seriesC(),3./Length),LINE,BLUE);}
}</pre>
<p>The resulting chart replicates the ES chart in the article. The EMA is shown in blue, the super smoothing filter in red, and the ultimate smoother in magenta:</p>
<p><img loading="lazy" decoding="async" width="917" height="560" class="wp-image-4718" src="https://financial-hacker.com/wp-content/uploads/2024/05/word-image-4717-1.png" srcset="https://financial-hacker.com/wp-content/uploads/2024/05/word-image-4717-1.png 917w, https://financial-hacker.com/wp-content/uploads/2024/05/word-image-4717-1-300x183.png 300w, https://financial-hacker.com/wp-content/uploads/2024/05/word-image-4717-1-768x469.png 768w" sizes="auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></p>
<p>We can see that the ultimate smoother produces indeed the best, albeit smoothed, representation of the price curve.</p>
<p>In TASC 4/24, Ehlers also presented two band indicators based on his Ultimate Smoother. Band indicators can be used to trigger long or short positions when the price hits the upper or lower band. The first band indicator, the <strong>Ultimate Channel</strong>, is again a straightforward conversion to the C language from Ehlers’ TradeStation code:</p>
<pre class="prettyprint">var UltimateChannel(int Length,int STRLength,int NumSTRs)
{
  var TH = max(priceC(1),priceH());
  var TL = min(priceC(1),priceL());
  var STR = UltimateSmoother(series(TH-TL),STRLength);
  var Center = UltimateSmoother(seriesC(),Length);
  rRealUpperBand = Center + NumSTRs*STR;
  rRealLowerBand = Center - NumSTRs*STR;
  return Center;
}</pre>
<p><strong>rRealUpperBand</strong> and <strong>rRealLowerBand</strong> are pre-defined global variables that are used by band indicators in the indicator library of the Zorro platform. For testing the new indicator, we apply it to an ES chart:</p>
<pre class="prettyprint">void run()
{
  BarPeriod = 1440;
  StartDate = 20230301;
  EndDate = 20240201;
  assetAdd("ES","YAHOO:ES=F");
  asset("ES");
  UltimateChannel(20,20,1);
  plot("UltChannel1",rRealUpperBand,BAND1,BLUE);
  plot("UltChannel2",rRealLowerBand,BAND2,BLUE|TRANSP);
}</pre>
<p>The resulting chart replicates the ES chart in Ehlers’ article:</p>
<p><img loading="lazy" decoding="async" width="1060" height="620" class="wp-image-4721" src="https://financial-hacker.com/wp-content/uploads/2024/05/word-image-4717-1-1.png" srcset="https://financial-hacker.com/wp-content/uploads/2024/05/word-image-4717-1-1.png 1060w, https://financial-hacker.com/wp-content/uploads/2024/05/word-image-4717-1-1-300x175.png 300w, https://financial-hacker.com/wp-content/uploads/2024/05/word-image-4717-1-1-1024x599.png 1024w, https://financial-hacker.com/wp-content/uploads/2024/05/word-image-4717-1-1-768x449.png 768w" sizes="auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></p>
<p>The second band indicator, <strong>Ultimate Bands</strong>, requires less code than Ehlers’ implementation, since lite-C can apply functions to a whole data series:</p>
<pre class="prettyprint">var UltimateBands(int Length,int NumSDs)
{
  var Center = UltimateSmoother(seriesC(),Length);
  vars Diffs = series(priceC()-Center);
  var SD = sqrt(SumSq(Diffs,Length)/Length);
  rRealUpperBand = Center + NumSDs*SD;
  rRealLowerBand = Center - NumSDs*SD; return Center;
}</pre>
<p>Again applied to the ES chart:</p>
<pre class="prettyprint">void run()
{
  BarPeriod = 1440;
  StartDate = 20230301;
  EndDate = 20240201;
  assetAdd("ES","YAHOO:ES=F");
  asset("ES");
  UltimateBands(20,1);
  plot("UltBands1",rRealUpperBand,BAND1,BLUE);
  plot("UltBands2",rRealLowerBand,BAND2,BLUE|TRANSP);
}</pre>
<p><img loading="lazy" decoding="async" width="1060" height="620" class="wp-image-4722" src="https://financial-hacker.com/wp-content/uploads/2024/05/word-image-4717-2.png" srcset="https://financial-hacker.com/wp-content/uploads/2024/05/word-image-4717-2.png 1060w, https://financial-hacker.com/wp-content/uploads/2024/05/word-image-4717-2-300x175.png 300w, https://financial-hacker.com/wp-content/uploads/2024/05/word-image-4717-2-1024x599.png 1024w, https://financial-hacker.com/wp-content/uploads/2024/05/word-image-4717-2-768x449.png 768w" sizes="auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></p>
<p>We can see that both indicators produce relatively similar bands with low lag. The code of the Ultimate Smoother and the bands can be downloaded from the 2024 script repository.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/ehlers-ultimate-smoother/feed/</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		
			</item>
	</channel>
</rss>
