<?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>The Financial Hacker</title>
	<atom:link href="https://financial-hacker.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://financial-hacker.com</link>
	<description>A new view on algorithmic trading</description>
	<lastBuildDate>Sun, 19 Apr 2026 10:08:34 +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>The Financial Hacker</title>
	<link>https://financial-hacker.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>The AutoTune filter</title>
		<link>https://financial-hacker.com/the-autotune-filter/</link>
					<comments>https://financial-hacker.com/the-autotune-filter/#comments</comments>
		
		<dc:creator><![CDATA[Petra Volkova]]></dc:creator>
		<pubDate>Fri, 17 Apr 2026 14:53:29 +0000</pubDate>
				<category><![CDATA[Indicators]]></category>
		<category><![CDATA[Petra on Programming]]></category>
		<category><![CDATA[System Development]]></category>
		<category><![CDATA[Dominant Cycle]]></category>
		<category><![CDATA[Ehlers]]></category>
		<guid isPermaLink="false">https://financial-hacker.com/?p=4989</guid>

					<description><![CDATA[By the Fourier theorem, any price curve is a mix of many long-term and short-term cycles. Once in a while a dominant market cycle emerges and can be exploited for trading. In his TASC 5/2026 article, John Ehlers described an algorithm for detecting such dominant cycles, using them to tune a bandpass filter, and creating &#8230; <a href="https://financial-hacker.com/the-autotune-filter/" class="more-link">Continue reading<span class="screen-reader-text"> "The AutoTune filter"</span></a>]]></description>
										<content:encoded><![CDATA[<p><em>By the Fourier theorem, any price curve is a mix of many long-term and short-term cycles. Once in a while a <strong>dominant market cycle</strong> emerges and can be exploited for trading. In his TASC 5/2026 article, <strong>John Ehlers</strong> described an algorithm for detecting such dominant cycles, using them to tune a bandpass filter, and creating a profitable trading system. Here&#8217;s how to do it.</em></p>
<p><span id="more-4989"></span></p>
<p>Ehlers&#8217; Easylanguage code from the TASC article can be directly converted to C for Zorro.  ChatGPT does the job in a few seconds. First, the cycle detector:</p>
<pre class="prettyprint">var MinCorr, Filt;
var AutoTune(vars Data,int Window)
{
  Filt = HighPass3(Data,Window);
  vars HP = series(Filt);
  var Corr[256];
  int Lag, J;
  for(Lag = 1; Lag &lt;= Window; Lag++)
  {
    var Sx = 0., Sy = 0.;
    var Sxx = 0., Sxy = 0., Syy = 0.;
    for(J = 0; J &lt; Window; J++)
    {
      var X = HP[J];
      var Y = HP[Lag + J];
      Sx += X; Sy += Y;
      Sxx += X*X; Sxy += X*Y; Syy += Y*Y;
    }
    var Den1 = Window*Sxx - Sx*Sx;
    var Den2 = Window*Syy - Sy*Sy;
    Corr[Lag] = (Window*Sxy - Sx*Sy) / sqrt(fix0(Den1*Den2));
  }

  MinCorr = 1.;
  var DC = Window;
  for(Lag = 1; Lag &lt;= Window; Lag++)
    if(Corr[Lag] &lt; MinCorr) {
      MinCorr = Corr[Lag];
      DC = 2*Lag;
    }

  vars DCs = series(DC,2);
  return DCs[0] = clamp(DC,DCs[1]-2.,DCs[1]+2.);
}</pre>
<p>The output of the <strong>AutoTune</strong> function is supposed to be the dominant price cycle in units of bars. It is then used to set the center frequency of a bandpass filter. Since Zorro has already a bandpass filter in its arsenal, I named Ehlers&#8217; new version <strong>BandPass2</strong>:</p>
<pre class="prettyprint">var BandPass2(vars Data, int Period, var Bandwidth)
{
  var L1 = cos(2.*PI/Period);
  var G1 = cos(Bandwidth*2.*PI/Period);
  var S1 = 1./G1 - sqrt(1./(G1*G1) - 1.);
  vars BP = series(0,3);
  return BP[0] = 0.5*(1.-S1)*(Data[0]-Data[2]) 
    + L1*(1.+S1)*BP[1] - S1*BP[2];
}</pre>
<p>Here&#8217;s some code for reproducing Ehlers’ ES chart in the article:</p>
<pre class="prettyprint">function run()
{
  BarPeriod = 1440;
  StartDate = 2024;
  EndDate = 2025;
  asset("ES");
  var DC = AutoTune(seriesC(),20);
  var BP = BandPass2(seriesC(),DC,0.25);
  plot("Zero",0,NEW,BLACK);
  plot("BP",BP,LINE,BLUE);
}</pre>
<p>The resulting chart:</p>
<p><img fetchpriority="high" decoding="async" width="877" height="514" class="wp-image-4990" src="https://financial-hacker.com/wp-content/uploads/2026/04/word-image-4989-1.png" srcset="https://financial-hacker.com/wp-content/uploads/2026/04/word-image-4989-1.png 877w, https://financial-hacker.com/wp-content/uploads/2026/04/word-image-4989-1-300x176.png 300w, https://financial-hacker.com/wp-content/uploads/2026/04/word-image-4989-1-768x450.png 768w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></p>
<p>How can we use this bandpass output for a trade signal? Ehlers used the zero crossovers of its rate-of-change (ROC). In this way he generated an impressive equity curve in his article, unfortunately with in-sample optimization. For a more realistic result, we’re using <strong>walk-forward analysis</strong> and reinvest profits by the <strong>square root rule</strong>:</p>
<pre class="prettyprint">function run()
{
  BarPeriod = 1440;
  StartDate = 2010;
  EndDate = 2025;
  Capital = 100000;

  asset("ES");
  set(TESTNOW,PARAMETERS);
  NumWFOCycles = 10;

  int Window = optimize("Window",26,10,30,2);
  var BW = optimize("BW",0.22,0.10,0.30,0.01);
  var Thresh = -optimize("Thresh",0.22,0.1,0.3,0.01);
  var DC = AutoTune(seriesC(),Window);
  var BP = BandPass2(seriesC(),DC,BW);
  vars ROCs = series(BP-ref(BP,2));
  
  Lots = 0.5*(Capital+sqrt(1.+ProfitTotal/Capital))/MarginCost;
  MaxLong = MaxShort = 1;
  if(crossOver(ROCs,0) &amp;&amp; MinCorr &lt; Thresh)
    enterLong();
  if(crossUnder(ROCs,0) &amp;&amp; MinCorr &lt; Thresh &amp;&amp; Filt &gt; 0)
    enterShort();
}</pre>
<p>The signals are filtered by a threshold that determines whether we&#8217;re in a cyclic market condition or not. The system is not fully symmetrical in long and short positions. Training and testing produced this equity curve:</p>
<p><img decoding="async" width="701" height="405" class="wp-image-4991" src="https://financial-hacker.com/wp-content/uploads/2026/04/word-image-4989-2.png" srcset="https://financial-hacker.com/wp-content/uploads/2026/04/word-image-4989-2.png 701w, https://financial-hacker.com/wp-content/uploads/2026/04/word-image-4989-2-300x173.png 300w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px" /></p>
<p>This curve does not look as impressive as Ehler’s one, but the CAGR is in the 25% area, much better than a buy-and-hold strategy. The code can be downloaded from the 2026 script repository.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/the-autotune-filter/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<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[Pardo]]></category>
		<category><![CDATA[Ranger]]></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 &#8216;stellar returns&#8217; 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 suppose 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>7</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 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>Build Better Strategies, Part 6: Evaluation</title>
		<link>https://financial-hacker.com/build-better-strategies-part-6-evaluation/</link>
					<comments>https://financial-hacker.com/build-better-strategies-part-6-evaluation/#comments</comments>
		
		<dc:creator><![CDATA[jcl]]></dc:creator>
		<pubDate>Thu, 05 Feb 2026 16:54:41 +0000</pubDate>
				<category><![CDATA[No Math]]></category>
		<category><![CDATA[System Development]]></category>
		<category><![CDATA[System Evaluation]]></category>
		<category><![CDATA[Experiment]]></category>
		<category><![CDATA[Indicator]]></category>
		<category><![CDATA[Walk forward analysis]]></category>
		<category><![CDATA[White's reality check]]></category>
		<category><![CDATA[Zorro]]></category>
		<guid isPermaLink="false">https://financial-hacker.com/?p=4901</guid>

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

					<description><![CDATA[Not all scripts we’re hired to write are trading strategies. Some are for data analysis or event prediction – for instance: Write me a script that calculates the likeliness of a stock market crash tomorrow. Some time ago a client ordered a script for improving the performance of their company. This remarkable script was very &#8230; <a href="https://financial-hacker.com/pimp-your-performance-with-key-figures/" class="more-link">Continue reading<span class="screen-reader-text"> "Pimp your performance with key figures"</span></a>]]></description>
										<content:encoded><![CDATA[
<p>Not all scripts we’re hired to write are trading strategies. Some are for data analysis or event prediction – for instance: <em>Write me a script that calculates the likeliness of a stock market crash tomorrow</em>. Some time ago a client ordered a script for improving the performance of their company. This remarkable script was very different to a trading system. Its algorithm can in fact improve companies, but also your personal performance. How does this work?<span id="more-4782"></span></p>



<p>Just like the performance of a stock, the performance of a company is measured by numerical indicators. They are named <strong>key figures</strong>. Key figures play a major role in quality management, such as the <strong>ISO 9000</strong> standards. An ISO 9000 key figure is a detail indicator, such as the number of faults in a production line, the number of bugs found in a beta test, the number of new clients acquired per day, the total of positive minus negative online reviews, an so on. Aside from being essential for an ISO 9000 certification, these key figures have two purposes:</p>


<ul class="wp-block-list">
	<li>They give <strong>detailed insight</strong> and expose strengths and weaknesses.</li>


	<li>And they give a <strong>strong motivation</strong> for reaching a certain goal.</li>
</ul>


<p>The script below opens a user interface for entering various sorts of key figures. It calculates an <strong>overall score</strong> that reflects the current performance of a company &#8211; or of a person &#8211; and displays it in a chart. If you use it not for a company, but for yourself, it helps improving your personal life. And from the short script you can see how to create a relatively complex software with relatively few lines of code.</p>


<p>Hackers like the concept of key figures. They are plain numbers that you can work with. Anyone can define key figures for herself. If you’re a writer, an important key figure is the number of words you&#8217;ve written today; if you&#8217;re an alcolohic, it&#8217;s the number of drinks you had today. Many self-improvement books tell you precisely what you need to do for living a healthier, wealthier, happier life &#8211; but they all suffer from the same problem: <strong>long-term motivation</strong>. If you lack the iron will to keep your daily exercises, reduce smoking, stay away from fast food, and so on &#8211; all good resolutions will eventually fall into oblivion. If you had <strong>resolutions for 2025</strong>, you&#8217;ll soon know what I mean.</p>


<p>Failure is less likely when you can observe your progress any day and see its immediately effect on your overall performance score. This score is a direct measure of your success in live. Whether you’re a company or a person, you want to keep this core rising. This feedback produces a strong motivation, every day again.</p>


<figure class="wp-block-image"><img loading="lazy" decoding="async" width="701" height="405" class="wp-image-4783" src="https://financial-hacker.com/wp-content/uploads/2025/01/word-image-4782-1.png" alt="" srcset="https://financial-hacker.com/wp-content/uploads/2025/01/word-image-4782-1.png 701w, https://financial-hacker.com/wp-content/uploads/2025/01/word-image-4782-1-300x173.png 300w" sizes="auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px" /></figure>



<p><br />
The above performance chart is plotted by the key figure management script in C for the Zorro platform. The red line is the overall score, derived from all key figures in a way explained below. The blue line is the key figure for which you just entered a new value (in the example it&#8217;s the number of large or small features implemented in the last 6 months in the Zorro platform). The X axis is the date in YYMMDD format.</p>


<p>Of course, key figures can be very different. Some may have a daily goal, some not, some shall sum up over time, others (like your bank account value) are just taken as they are. The idea is that your overall score rises when you exceed the daily goals, and goes down otherwise. All key figures and their parameters can be freely defined in a CSV file, which can be edited with a text editor or with Excel. It looks like this:</p>


<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p><code>Name, Decimals, Unit, Offset, Growth, Sum<br />
Appr,1,0.1,0,0,0<br />
Praise,0,10,0,-30,1<br />
Weight,1,-0.1,-250,0.033,0<br />
Duck,1,0.5,0,-0.5,1<br />
Worth,0,1,-6000,0,0</code></p>
</div></div>
</div></div>


<p>In the first column you can assign a name. The second column is the number of decimals in the display, the third is their unit in the overall score, the fourth is an offset in the score, the fifth is the daily goal, and the last tells if the figures shall sum up over time (1) or not (0).</p>


<p>Example. Suppose you’re a president of a large country and want to pimp up your personal performance. What’s your key figures? First, of course, approval rate. Any tenth percent adds one point to your score. So the first entry is simple:</p>


<p><code>Name, Decimals, Unit, Offset, Growth, Sum </code><br />
<code>Appr, 1, 0.1, 0, 0, 0</code></p>


<p>Next, fame. Key figure is the daily number of praises on Fox News, OneAmerica, and Newsmax. You’ve ordered a White House department to count the praises; of course you’re personally counting them too, just in case. Less than 30 praises per day would be bad and reduce your score, more will improve it. So 30 praises are daily subtracted from your score. Any 10 further praises add one point. This is an accumulative key figure:</p>


<p><code>Praise, 0, 10, 0, -30, 1</code></p>


<p>Next, health. Key figure is weight. Your enemies spread rumors that you&#8217;re unfit and obese. Your doctors urge you to shed weight. So you want to lose one pound every month, which (you have your mathematicians for calculating difficult things) is about 0.033 per day. Any lost 0.1 pound adds one point to your score. The numbers are negative since you want your weight to go down, not up. The offset is your current weight.</p>


<p><code>Weight, 1, -0.1, -250, 0.033, 0</code></p>


<p>Next, literacy. Your enemies spread rumors you&#8217;re illiterate. To prove them wrong, you&#8217;ve decided to read at least half a page per day in a real book (you’ve chosen <a href="https://www.amazon.de/-/en/Doreen-Cronin/dp/0689863772">Duck for President</a> to begin with). Any further half page adds one point to your score. This is also an accumulative figure.</p>


<p><code>Duck, 1, 0.5, 0, -0.5, 1</code></p>


<p>Finally, net worth. You’ve meanwhile learned to better avoid business attempts. Let your net worth grow due to the value increase of your inherited real estate, which is currently at 6 billion. Any million further growth adds one point to your score (numbers given in millions):</p>


<p><code>Worth, 0, 1, -6000, 0, 0</code></p>


<p>For improving your personal performance, download the script from the 2025 repository. Copy the files <strong>KeyFigures.csv</strong> and <strong>KeyFigures.c</strong> in your Strategy folder. Edit <strong>KeyFigures.csv</strong> for entering your personal key figures, as in the above example (you can later add or remove key figures and use Excel to add or remove the new columns to the data file). This is the script:</p>
<pre class="prettyprint">// Pimp Your Performance with Key Figures //////////////////////

string Rules = "Strategy\\KeyFigures.csv";
string Data = "Data\\KeyData.csv"; // key figures history
string Format = "0%d.%m.%Y,f1,f,f,f,f,f,f,f,f,f,f,f,f,f,f";
int Records,Fields;

var value(int Record,int Field,int Raw)
{
	var Units = dataVar(1,Field,2);
	var Offset = dataVar(1,Field,3);
	var Growth = dataVar(1,Field,4);
	var Value = 0;
	int i;
	for(i=0; i&lt;=Record; i++) {
		if(dataVar(2,i,Field+1) &lt; 0.) continue; // ignore negative entries
		Value += Growth;
		if(i == Record || (dataInt(1,Field,5)&amp;1)) // Sum up? 
			Value += dataVar(2,i,Field+1)+Offset;
	}
	if(Raw) return Value-Offset;
	else return Value/Units;
}

var score(int Record)
{
	int i,Score = 0;
	for(i=0; i&lt;Fields; i++) 
		Score += value(Record,i,0);
	panelSet(Record+1,Fields+1,sftoa(Score,0),YELLOW,16,4);
	return Score;
}


void click(int Row,int Col)
{
	dataSet(2,Row-1,Col,atof(panelGet(Row,Col)));
	score(Row-1);
	if(dataSaveCSV(2,Format,Data)) sound("Click.wav");
	int i;
	for(i=0; i&lt;Records; i++) {
		var X = ymd(dataVar(2,i,0)) - 20000000;
		plotBar("Score",i,X,score(i),LINE,RED);
		plotBar(dataStr(1,Col-1,0),i,NIL,value(i,Col-1,1),AXIS2,BLUE);
	}
	if(Records &gt;= 2) plotChart("");
}

void main()
{
	int i = 0, j = 0;
	printf("Today is %s",strdate("%A, %d.%m.%Y",NOW));
	ignore(62);
	PlotLabels = 5;
// File 1: Rules
	Fields = dataParse(1,"ssss,f1,f,f,f,i",Rules);
// File 2: Content
	Records = dataParse(2,Format,Data);
	int LastDate = dataVar(2,Records-1,0);
	int Today = wdate(NOW);
	if(LastDate &lt; Today) { // no file or add new line
		dataAppendRow(2,16);
		for(i=1; i&lt;=Fields; i++)
			if(!(dataInt(1,i-1,5)&amp;1))
				dataSet(2,Records,i,dataVar(2,Records-1,i));
		Records++;
	}
	dataSet(2,Records-1,0,(var)Today);

// display in panel
	panel(Records+1,Fields+2,GREY,-58);
	panelFix(1,0);
	print(TO_PANEL,"Key Figures");
	for(i=0; i&lt;Fields; i++)
		panelSet(0,i+1,dataStr(1,i,0),ColorPanel[0],16,1);
	panelSet(0,i+1,"Score",ColorPanel[0],16,1);
	panelSet(0,0,"Date",ColorPanel[0],16,1);
	for(j=0; j&lt;Records; j++) {
		panelSet(j+1,0,strdate("%d.%m.%y",dataVar(2,j,0)),ColorPanel[0],0,1);
		score(j);
		for(i=0; i&lt;Fields; i++) 
			panelSet(j+1,i+1,sftoa(dataVar(2,j,i+1),-dataVar(1,i,1)),ColorPanel[2],0,2);
	}
	panelSet(-1,0,"Rules",0,0,0);
}</pre>
<p>The file locations and the CSV format of the key figures history are defined at the begin. The <strong>value</strong> function calculates the contribution of a particular key figure to the overall score. The <strong>score</strong> function updates the overall score. The <strong>click</strong> function, which is called when you enter a new value, calculates the score of that day, updates the spreadsheet, and prints the chart. The <strong>main</strong> function imports the data and key figures from their CSV files into datasets, prints the current day and displays a spreadsheet of your key figures and score history, like this:</p>


<figure class="wp-block-image"><img loading="lazy" decoding="async" width="484" height="163" class="wp-image-4784" src="https://financial-hacker.com/wp-content/uploads/2025/01/ein-bild-das-text-screenshot-schrift-zahl-enth.png" alt="Ein Bild, das Text, Screenshot, Schrift, Zahl enthält.

Automatisch generierte Beschreibung" srcset="https://financial-hacker.com/wp-content/uploads/2025/01/ein-bild-das-text-screenshot-schrift-zahl-enth.png 484w, https://financial-hacker.com/wp-content/uploads/2025/01/ein-bild-das-text-screenshot-schrift-zahl-enth-300x101.png 300w" sizes="auto, (max-width: 484px) 85vw, 484px" /> </figure>



<p> <br />
You will need <a href="https://zorro-project.com/download.php" target="_blank" rel="noopener">Zorro S</a> because the spreadsheet function is not available in the free version. Start the script any morning. It will open the spreadsheet, where you can click in any of the white fields and enter a new key figure value for today. You can anytime enter new figures for today or for past days. At any entry, the score is calculated and &#8211; if the history spans more than 2 days – a chart is plotted as in the above example.</p>


<p>Normally, personal performance depends on about 5-10 key figures (maximum is 15). For instance, miles you’ve jogged today, steps walked, exercises done, pages read, words written, words learned in a new language, value of your bank account, value of your stock portfolio, burgers eaten, cigarettes smoked, enemies killed, or number of old ladies you helped crossing the street. If you’re a president, consider the script a free present (we hope for generous tax exceptions in exchange). If you’re an ISO 9000 certified company and want to use the script for your quality management, please contact oP group to pay your fee. For personal use, the script is free. Pimp your performance and make the world a better place!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://financial-hacker.com/pimp-your-performance-with-key-figures/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
	</channel>
</rss>
