TRADERS’ TIPS

November 2020

Tips Article Thumbnail

For this month’s Traders’ Tips, the focus is F. Arden Thomas’ article in the August 2020 issue, “Voting With Multiple Timeframes.” Here, we present the November 2020 Traders’ Tips code with possible implementations in various software.

You can right-click on any chart to open it in a new tab or window and view it at it’s originally supplied size, often much larger than the version printed in the magazine.

The Traders’ Tips section is provided to help the reader implement a selected technique from an article in this issue or another recent issue. The entries here are contributed by software developers or programmers for software that is capable of customization.


logo

TRADESTATION: NOVEMBER 2020

In “Voting With Multiple Timeframes,” which appeared in the August 2020 issue of S&C, author F. Arden Thomas shows us a new way of using the classic stochastic oscillator by combining many timeframes into a single value by voting. By using this voting process, buy and sell signals derived from many intervals become clearly visible on the chart.

Here, we are providing TradeStation EasyLanguage code for an indicator based on the author’s work as well as a function to assist with the calculations.

Indicator: Multiple Timeframe Stochastic
// Voting With Multiple Timeframes
// TASC Nov 2020
// F. Arden Thomas
// Chart should be a daily interval

using elsystem ;
using elsystem.collections ;

inputs:
	OverBought( 80 ),
	OverSold( 20 ),
	StochasticLength( 21 ) ;
	
variables:
	vector Weekly( null ),
	vector BiMonthly( null ),
	vector Monthly( null ),
	vector QuarterQuarter( null ),
	vector HalfQuarter( null ),	
	vector Quarter( null ),
	QStoch( 0 ),
	HQStoch( 0 ),
	QQStoch( 0 ),
	MStoch( 0 ),
	BStoch( 0 ),
	WStoch( 0 ),
	DStoch( 0 ),
	BuyPressure( 0 ),
	SellPressure( 0 ),
	CB( 0 ) ;

	
// Intervals
// For intervals we'll assume trading days
// and round down to nearest whole number
// Adjust the following constants if desired
constants:
	int Q( 63 ),
	int HQ( 31 ),
	int QQ( 15 ),
	int M( 21 ),
	int B( 10 ),
	int W( 5 ) ;
	
method void InitVectors()
begin
	Weekly = new vector ;
	BiMonthly = new Vector ;
	Monthly = new Vector ;
	QuarterQuarter = new Vector ;
	HalfQuarter = new Vector ;
	Quarter = new Vector ;
end ;

method double CalcStoch( vector BarData, 
	int Length )
variables: double pctK, double Highest14, 
	double Lowest14, vector BD, int Len ;
begin
	BD = BarData ;
	Len = Length ;
	if BD.Count >= Len then
	begin
		Highest14 = Highest( BD, Len) ;
		Lowest14 = Lowest( BD, Len ) ;
		if Highest14 - Lowest14 <> 0 then
			pctK = ( Close - lowest14 ) / 
				( Highest14 - Lowest14 ) * 100
		else
			pctK = 0 ;
	end ;
	return PctK ;
end ;

method int CalcBuyPressue ()
variables: int BPVal ;
begin
	BPVal = 0 ;
	if QStoch < OverSold then	
		BPVal += 1 ;
	if HQStoch < OverSold then	
		BPVal += 1 ;
	if QQStoch < OverSold then	
		BPVal += 1 ;	
	if MStoch < OverSold then	
		BPVal += 1 ;
	if BStoch < OverSold then	
		BPVal += 1 ;
	if WStoch < OverSold then	
		BPVal += 1 ;
	if DStoch < OverSold then	
		BPVal += 1 ;
	return BPVal ;
end ;

method int CalcSellPressue ()
variables: int SPVal ;
begin
	SPVal = 0 ;
	if QStoch > OverBought then	
		SPVal += 1 ;
	if HQStoch > OverBought then	
		SPVal += 1 ;
	if QQStoch > OverBought then	
		SPVal += 1 ;	
	if MStoch > OverBought then	
		SPVal += 1 ;
	if BStoch > OverBought then	
		SPVal += 1 ;
	if WStoch > OverBought then	
		SPVal += 1 ;
	if DStoch > OverBought then	
		SPVal += 1 ;
	return SPVal ;
end ;

once
begin
	InitVectors() ;
end ;

CB = CurrentBar ;	

if BarStatus( 1 ) = 2 then
begin
	if Mod( CB, Q ) = 0 then
		Quarter.insert( 0, Close ) ;
	if Mod( CB, HQ ) = 0 then
		HalfQuarter.insert( 0, Close ) ;
	if Mod( CB, QQ ) = 0 then
		QuarterQuarter.insert( 0, Close ) ;
	if Mod( CB, M ) = 0 then
		Monthly.insert( 0, Close ) ;	
	if Mod( CB, B ) = 0 then
		BiMonthly.insert( 0, Close ) ;
	if Mod( CB, W ) = 0 then
		Weekly.insert( 0, Close ) ;

	QStoch = CalcStoch( Quarter, 
		StochasticLength ) ;
	HQStoch = CalcStoch(HalfQuarter, 
		StochasticLength ) ;
	QQStoch = CalcStoch(QuarterQuarter, 
		StochasticLength ) ;
	MStoch = CalcStoch(Monthly, 
		StochasticLength ) ;
	BStoch = CalcStoch( BiMonthly, 
		StochasticLength ) ;
	WStoch = CalcStoch( Weekly, 
		StochasticLength ) ;

	// Calc Daily directly from bars
	DStoch = ( Close - 
		Lowest( Close, StochasticLength ) ) 
		/ ( Highest( Close, StochasticLength ) 
		- Lowest(Close, StochasticLength ) )* 100 ;
	BuyPressure = CalcBuyPressue() ;
	SellPressure = CalcSellPressue() ;
end ;

Plot1( BuyPressure, "Buy Pressure" ) ;
Plot2( SellPressure, "Sell Pressure" ) ;

To download the EasyLanguage code and a demonstration workspace for TradeStation 10, please visit our TradeStation and EasyLanguage support forum. The files for this article can be found here: https://community.tradestation.com/Discussions/Topic.aspx?Topic_ID=168100. The filename is “TASC_NOV2020.ELD.”

For more information about EasyLanguage in general, please see https://www.tradestation.com/EL-FAQ.

A sample chart is shown in Figure 1.

Sample Chart

FIGURE 1: TRADESTATION. Here, the multi-timeframe stochastic indicator is applied to a daily chart of CSCO.

This article is for informational purposes. No type of trading or investment recommendation, advice, or strategy is being made, given, or in any manner provided by TradeStation Securities or its affiliates.

—Doug McCrary
TradeStation Securities, Inc.
www.TradeStation.com

BACK TO LIST

logo

WEALTH-LAB.COM: NOVEMBER 2020

The multi-timeframe voting concept presented by author F. Arden Thomas in his August 2020 S&C article, “Voting With Multiple Timeframes,” is an interesting concept that can be applied to many common indicators such as the RSI or ADX, not just the stochastic.

First, a note regarding his “quarter-quarterly” scale. A quarter is 13 weeks, so it’s roughly a “3.25 times weekly” or “16.25 daily” scale (given there are 5 trading days in a week). Since bi-weekly and monthly scales contain 10 and 22 trading days (respectively), it doesn’t look like the new scale would be that necessary to bring much difference from either bi-weekly or monthly.

Out of the box, Wealth-Lab does not support custom chart scales like bi-weekly or half-quarterly. Although scales like bi-weekly or quarter-quarterly could be accomplished fairly easily using the “EOD Scaling Provider” (a Wealth-Lab add-on), we felt it would make more sense if the voting process considered intraday data instead. As a result, our aggregate voting indicator polls for the 21-period stochastic readings on 60-minute, daily, weekly, monthly, quarterly, and yearly timeframes.

The code we are providing here requires intraday data, which is available either for free or on a subscription basis from various providers such as Tiingo, Alpha Vantage, or DTN IQFeed. Here, we’ll use a modified version of the author’s example 2, which can take multiple positions in the same symbol.

Strategy rules:

  1. Long setup: Buying pressure >= 5
  2. Buy tomorrow at open each time when selling pressure is greater than 0
  3. Sell tomorrow at open if selling pressure = 0

As you can see from Figure 2, the trading system just marks the entries. You can make it more reactive by relaxing the exit condition—for example, require a greater selling pressure to sell (for example, >= 1).

Sample Chart

FIGURE 2: WEALTH-LAB. Here you see sample entries on a daily chart of Halliburton. Data provided by Tiingo, source: IEX exchange.

If you’re interested in more trading systems of the kind that operate in multiple timeframes, explore the classic and built-in “Elder Triple Screen” and “RSI Trifecta” strategies in Wealth-Lab. Just like this month’s strategy presented here, as well as many others contributed by the Wealth-Lab community, you can download and execute them in Wealth-Lab. To do so, choose download from the open strategy dialog (Ctrl-O).

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using WealthLab;
using WealthLab.Indicators;

namespace WealthLab.Strategies
{
	public class TradersTips_Nov2020 : WealthScript
	{
		protected override void Execute()
		{
			int Period = 21;
			var ls = LineStyle.Solid;
			HideVolume();

			// Access the symbol from a DataSet whose base scale is intraday.  Do not synch yet!
			Bars i60 = GetExternalSymbol("Tiingo (Minute,60)", Bars.Symbol, false);
			// Apply the indicator in the intraday scale
			DataSeries sto_60 = StochK.Series(i60, Period); sto_60.Description = "60-minute";

			SetScaleDaily();
			DataSeries sto_d = StochK.Series(Bars, Period); sto_d.Description = "Daily";

			SetScaleWeekly();
			DataSeries sto_w = StochK.Series(Bars, Period); sto_w.Description = "Weekly";

			SetScaleMonthly();
			DataSeries sto_m = StochK.Series(Bars, Period); sto_m.Description = "Monthly";
			RestoreScale();

			DataSeries sto_q = StochK.Series(BarScaleConverter.ToQuarterly(Bars), Period);
			DataSeries sto_y = StochK.Series(BarScaleConverter.ToYearly(Bars), Period);
			sto_q.Description = "Quarterly"; sto_y.Description = "Yearly";

			sto_60 = Synchronize( sto_60);
			sto_d = Synchronize( sto_d); sto_w = Synchronize( sto_w);
			sto_m = Synchronize( sto_m); sto_q = Synchronize( sto_q); sto_y = Synchronize( sto_y);

			//Determine the first valid bar
			var start = Math.Max(sto_60.FirstValidValue,
				Math.Max(sto_d.FirstValidValue,
					Math.Max(sto_w.FirstValidValue,
						Math.Max(sto_m.FirstValidValue,
							Math.Max(sto_q.FirstValidValue, sto_y.FirstValidValue)))));

			ChartPane sPane = CreatePane( 20, false, true);
			PlotSeries( sPane, sto_60, Color.Peru, ls, 1);
			PlotSeries( sPane, sto_d, Color.Chartreuse, ls, 1);
			PlotSeries( sPane, sto_w, Color.Gainsboro, ls, 1);
			PlotSeries( sPane, sto_m, Color.Sienna, ls, 1);
			PlotSeries( sPane, sto_q, Color.IndianRed, ls, 1);
			PlotSeries( sPane, sto_y, Color.NavajoWhite, ls, 1);

			DataSeries buyVote = new DataSeries(Bars, "Buy Voting");
			DataSeries sellVote = new DataSeries(Bars, "Sell Voting");

			for (int bar = 1; bar < Bars.Count; bar++)
			{
				if (sto_60[bar] < 20) buyVote[bar]++;
				if (sto_d[bar] < 20) buyVote[bar]++;
				if (sto_w[bar] < 20) buyVote[bar]++;
				if (sto_m[bar] < 20) buyVote[bar]++;
				if (sto_q[bar] < 20) buyVote[bar]++;
				if (sto_y[bar] < 20) buyVote[bar]++;

				if (sto_60[bar] > 80) sellVote[bar]++;
				if (sto_d[bar] > 80) sellVote[bar]++;
				if (sto_w[bar] > 80) sellVote[bar]++;
				if (sto_m[bar] > 80) sellVote[bar]++;
				if (sto_q[bar] > 80) sellVote[bar]++;
				if (sto_y[bar] > 80) sellVote[bar]++;
			}

			ChartPane vPane = CreatePane( 40, false, true );
			PlotSeries( vPane, buyVote, Color.Blue, ls, 1);
			PlotSeries( vPane, sellVote, Color.Red, ls, 1);
			DrawHorzLine( vPane, 20, Color.Blue, LineStyle.Dashed, 1);
			DrawHorzLine( vPane, 80, Color.Red, LineStyle.Dashed, 1);

			bool setup = false; int setupBar = -1;

			//Start trading only after all DataSeries are complete
			for (int bar = start; bar < Bars.Count; bar++)
			{
				bool buySetup = buyVote[bar] >= 5 && sellVote[bar] == 0;
				bool buyTrigger = sellVote[bar] > 0;

				for (int pos = ActivePositions.Count - 1; pos >= 0; pos--)
				{
					Position p = ActivePositions[pos];

					if (buyVote[bar] == 0)
						SellAtMarket( bar + 1, p);
				}
				
				if (!setup)
				{
					if (buySetup)
					{
						setup = true;
						setupBar = bar;
					}
				}

				if (setup)
				{
					if (buyTrigger)
						if ( BuyAtMarket( bar + 1) != null )
						setup = false;

					SetBackgroundColor( bar, Color.FromArgb(30, Color.Green));
				}
			}
		}
	}
}

—Gene (Eugene) Geren
Wealth-Lab team
www.wealth-lab.com

BACK TO LIST

logo

THINKORSWIM: NOVEMBER 2020

We have put together a study as well as a strategy based on the article “Voting With Multiple Timeframes” by F. Arden Thomas, which appeared in the August 2020 issue of S&C. To ease the loading process of the study, simply click on https://tos.mx/g7uzFGw or enter it into setup → open shared items from within thinkorswim and then choose view thinkscript and name it “StochasticVote.” Do the same with https://tos.mx/hRbYe33 and name it “StochasticVoteStrat” to add the strategy to thinkorswim.

These can be added to your chart from the study and strategies menu within thinkorswim charts.

The example chart in Figure 3 shows both the study and strategy (with a P/L graph for the strategy) added to a chart of HAL for the time period of 1/1/2008 through 1/1/2012. See the article in the August 2020 issue for how to read and interpret these.

Sample Chart

FIGURE 3: THINKORSWIM. This example chart shows both the study and strategy (with a P/L graph for the strategy) added to a chart of HAL for the time period of 1/1/2008 through 1/1/2012.

—thinkorswim
A division of TD Ameritrade, Inc.
www.thinkorswim.com

BACK TO LIST

logo

NINJATRADER: NOVEMBER 2020

The StochasticsBuySellVote indicator, described in the August 2020 S&C article “Voting With Multiple Timeframes” by F. Arden Thomas, is available for download at the following links for NinjaTrader 8 and NinjaTrader 7:

Once the file is downloaded, you can import the indicator into NinjaTader 8 from within the control center by selecting Tools → Import → NinjaScript Add-On and then selecting the downloaded file for NinjaTrader 8. To import in NinjaTrader 7, from within the control center window, select the menu File → Utilities → Import NinjaScript and select the downloaded file.

You can review the indicator’s source code in NinjaTrader 8 by selecting the menu New → NinjaScript Editor → Indicators from within the control center window and selecting the StochasticsBuySellVote file. You can review the indicator’s source code in NinjaTrader 7 by selecting the menu Tools → Edit NinjaScript → Indicator from within the control center window and selecting the StochasticsBuySellVote file.

A sample chart with the indicator is shown in Figure 4.

Sample Chart

FIGURE 4: NINJATRADER. Here, the StochasticsBuySellVote indicator is displayed on a monthly chart of HAL from 2008 to 2019 with PlotStyle set to “square.”

NinjaScript uses compiled DLLs that run native, not interpreted, to provide you with the highest performance possible.

—Jim Dooms
NinjaTrader, LLC
www.ninjatrader.com

BACK TO LIST

logo

NEUROSHELL TRADER: NOVEMBER 2020

The stochastic buy and sell vote indicators described by F. Arden Thomas in his August 2020 S&C article “Voting With Multiple Timeframes” can be easily implemented in NeuroShell Trader by combining a few of NeuroShell Trader’s 800+ indicators. Simply select new indicator from the insert menu and use the indicator wizard to create the following indicators:

Stochastic Buy Vote
Add2( Add3( A<B( Stoch%K(60),20 ),A<B( Stoch%K(30),20 ),A<B( Stoch%K(15),20 ) ),Add4( A<B( Stoch%K(20),20 ),A<B( Stoch%K(10),20 ),A<B( Stoch%K(5),20 ),A<B( Max( Stoch%K(1),5 ),20 ) ) )

Stochastic Sell Vote
Add2( Add3( A>B( Stoch%K(60),80 ),A>B( Stoch%K(30),80 ),A>B( Stoch%K(15),80 ) ),Add4( A>B( Stoch%K(20),80 ),A>B( Stoch%K(10),80 ),A>B( Stoch%K(5),80 ),A>B( Max( Stoch%K(1),5 ),80 ) ) )
Sample Chart

FIGURE 5: NEUROSHELL TRADER. This Neuroshell Trader chart shows the stochastic buy and sell vote indicators.

NeuroShell Trader users can go to the Stocks & Commodities section of the NeuroShell Trader free technical support website to download a copy of this or any previous Traders’ Tips.

—Marge Sherald, Ward Systems Group, Inc.
301 662-7950, sales@wardsystems.com
www.neuroshell.com

BACK TO LIST

logo

QUANTACULA STUDIO: NOVEMBER 2020

In the August 2020 issue of S&C, F. Arden Thomas presents a novel use of a well-known indicator by combining it over multiple timeframes. Although the author creates a voting system by counting the number of times the indicator is in overbought/oversold range, we thought it would be interesting to create a composite indicator by averaging the stochastic value over multiple timeframes into a single indicator that moves along the standard scale.

The Quantacula code presented here illustrates how to obtain historical data for the current symbol in different timeframes, create indicators based off those data series, and combine the result into an aggregate indicator.

namespace Quantacula
{
    public class MyModel3 : UserModelBase
    {
        //create indicators and other objects here, this is executed prior to the main trading loop
        public override void Initialize(BarHistory bars)
        {
            //get daily, weekly, monthly, quarterly data series
            BarHistory daily = bars;
            BarHistory weekly = BarHistoryCompressor.ToWeekly(bars);
            BarHistory monthly = BarHistoryCompressor.ToMonthly(bars);
            BarHistory quarterly = BarHistoryCompressor.ToQuarterly(bars);

            //get stochastic oscillator for each interval
            TimeSeries skDaily = StochK.Series(daily, 21);
            TimeSeries skWeekly = StochK.Series(weekly, 21);
            TimeSeries skMonthly = StochK.Series(monthly, 21);
            TimeSeries skQuarterly = StochK.Series(quarterly, 21);

            //synchronize them back to base scale
            skWeekly = TimeSeriesSynhronizer.Synchronize(skWeekly, skDaily);
            skMonthly = TimeSeriesSynhronizer.Synchronize(skMonthly, skDaily);
            skQuarterly = TimeSeriesSynhronizer.Synchronize(skQuarterly, skDaily);

            //Plot them
            PlotTimeSeries(skDaily, "StochK Daily", "StochK", Color.FromArgb(255, 0, 0));
            PlotTimeSeries(skWeekly, "StochK Weekly", "StochK", Color.FromArgb(128, 0, 0));
            PlotTimeSeries(skMonthly, "StochK Monthly", "StochK", Color.FromArgb(64, 0, 0));
            PlotTimeSeries(skQuarterly, "StochK Quarterly", "StochK", Color.FromArgb(0, 0, 0));

            //calculate average over multiple time frames
            TimeSeries skAverage = (skDaily + skWeekly + skMonthly + skQuarterly) / 4.0;
            PlotTimeSeries(skAverage, "StochK over Multiple Time Frames", "StochKAvg", Color.Red, PlotStyles.ThickLine);
        }

        //execute the strategy rules here, this is executed once for each bar in the backtest history
        public override void Execute(BarHistory bars, int idx)
        {
        }
    }
}
Sample Chart

FIGURE 6: QUANTACULA. This example chart of IBM shows the individual stochastic %K values for multiple timeframes, as well as the combined indicator.

—Dion Kurczek
https://www.quantacula.com

BACK TO LIST

logo

OPTUMA: NOVEMBER 2020

Here are Optuma formulas for the vote method described in the August 2020 article in S&C by F. Arden Thomas, “Voting With Multiple Timeframes.”

Note these formulas are based on weekly data, so quarter-quarterly is three weeks, half-quarter is six weeks, and so on.

Buy Vote:
 
D1=STOCH(Day(PERIODAMOUNT=1), BAR1=21, BAR2=1)<20;
W1=STOCH(Week(PERIODAMOUNT=1), BAR1=21, BAR2=1)<20;
B1=STOCH(Week(PERIODAMOUNT=2), BAR1=21, BAR2=1)<20;
QQ1=STOCH(Week(PERIODAMOUNT=3), BAR1=21, BAR2=1)<20;
M1=STOCH(Week(PERIODAMOUNT=4), BAR1=21, BAR2=1)<20;
HQ1=STOCH(Week(PERIODAMOUNT=6), BAR1=21, BAR2=1)<20;
Q1=STOCH(Week(PERIODAMOUNT=12), BAR1=21, BAR2=1)<20;

D1+W1+B1+QQ1+M1+HQ1+Q1
 
Sell Vote:
 
D1=STOCH(Day(PERIODAMOUNT=1), BAR1=21, BAR2=1)>80;
W1=STOCH(Week(PERIODAMOUNT=1), BAR1=21, BAR2=1)>80;
B1=STOCH(Week(PERIODAMOUNT=2), BAR1=21, BAR2=1)>80;
QQ1=STOCH(Week(PERIODAMOUNT=3), BAR1=21, BAR2=1)>80;
M1=STOCH(Week(PERIODAMOUNT=4), BAR1=21, BAR2=1)>80;
HQ1=STOCH(Week(PERIODAMOUNT=6), BAR1=21, BAR2=1)>80;
Q1=STOCH(Week(PERIODAMOUNT=12), BAR1=21, BAR2=1)>80;

D1+W1+B1+QQ1+M1+HQ1+Q1
Sample Chart

FIGURE 7: OPTUMA. This shows the values in column format for a watchlist.

Sample Chart

FIGURE 8: OPTUMA. This sample chart displays the buy vote and sell vote lines on a chart of XLB.

support@optuma.com

BACK TO LIST

logo

AIQ: NOVEMBER 2020

The importable AIQ EDS file for F. Arden Thomas’s article from the August 2020 issue of S&C, “Voting With Multiple Timeframes,” can be obtained on request via email to info@TradersEdgeSystems.com. The code is also available below.

!Voting With Multiple Timeframes
!Author: F Arden Thomas
!Coded by: Richard Denning 9/8/2020

!INPUTS:
stoLen is 21.
bpLvl is 20.
spLvl is 80.
C is [close].
H is [high].
L is [low].
bpVot is 7.
spVot is 7.

stoLenW is 5*stoLen.
stoLenB is 10*stoLen.
stoLenQQ is 15*stoLen.
stoLenM is 20*stoLen.
stoLenHQ is 45*stoLen.
stoLenQ is 60*stoLen.

stoD is 100 * ((C-lowresult(L,stoLen)) /
	(highresult(H,stoLen) - lowresult(L,stoLen))).

stoW is 100 * ((C-lowresult(L,stoLenW)) /
	(highresult(H,stoLenW) - lowresult(L,stoLenW))).

stoB is 100 * ((C-lowresult(L,stoLenB)) /
	(highresult(H,stoLenB) - lowresult(L,stoLenB))).

stoQQ is 100 * ((C-lowresult(L,stoLenQQ)) /
	(highresult(H,stoLenQQ) - lowresult(L,stoLenQQ))).

stoM is 100 * ((C-lowresult(L,stoLenM)) /
	(highresult(H,stoLenM) - lowresult(L,stoLenM))).

stoHQ is 100 * ((C-lowresult(L,stoLenHQ)) /
	(highresult(H,stoLenHQ) - lowresult(L,stoLenHQ))).

stoQ is 100 * ((C-lowresult(L,stoLenQ)) /
	(highresult(H,stoLenQ) - lowresult(L,stoLenQ))).

BP is iff(stoD<=bpLvl,1,0) + iff(stoW<=bpLvl,1,0) + iff(stoB<=bpLvl,1,0) + iff(stoM<=bpLvl,1,0)
	+ iff(stoQQ<=bpLvl,1,0) + iff(stoHQ<=bpLvl,1,0) + iff(stoQ<=bpLvl,1,0).

SP is iff(stoD>=spLvl,1,0) + iff(stoW>=spLvl,1,0) + iff(stoB>=spLvl,1,0) + iff(stoM>=spLvl,1,0)
	+ iff(stoQQ>=spLvl,1,0) + iff(stoHQ>=spLvl,1,0) + iff(stoQ>=spLvl,1,0).

ShowValues if 1.
BP7 if BP=7.
SP7 if SP=7.
Vote7 if BP7 or SP7.

Code for multiple timeframe indicators in the article is included in the EDS file. These were implemented by approximation to the actual timeframe bars by running all in the daily mode and then just increasing the lengths as follows:

    Daily = stolen * 1
Weekly = stolen * 5
Biweekly = stolen * 10
QuarterQuarter = stolen * 15
Monthly = stolen * 20
HalfQuarter = stolen * 30
Quarter = stolen * 60

I also coded a report shows the voting of the seven indicators. In Figure 9, I show the summary report for 92 of the Nasdaq 100 stocks at a recent market high on 9/2/2020. There are 33 stocks with vote of SP=7 and only 2 stocks with a vote of BP=7. In Figure 10, I show the summary report for the same list of stocks. Now we see that no stocks have a vote of 7 on either SP or BP.

Sample Chart

FIGURE 9: AIQ. Summary EDS daily report for multi timeframe indicators run on a list of the NASDAQ 100 stocks as of 9/2/2020 (a market high).

Sample Chart

FIGURE 10: AIQ. Summary EDS daily report for multi timeframe indicators run on a list of the NASDAQ 100 stocks as of 9/18/2020 (after a drop from the high of 9/2/2020).

I do not recommend trying to run a backtest with these indicators nor try to show them as an indicator on a chart, as they run exceedingly slow and appear to freeze the computer. These indicators are best used on a single-day report to prospect for high-vote stocks.

—Richard Denning
info@TradersEdgeSystems.com
for AIQ Systems

BACK TO LIST

logo

TRADERSSTUDIO: NOVEMBER 2020

The importable TradersStudio files based on F. Arden Thomas’s article, “Voting With Multiple Timeframes,” which appeared in the August 2020 issue of S&C, can be obtained on request via email to info@TradersEdgeSystems.com. The code is also available below.

'Voting With Multiple Timeframes
'Author: F Arden Thomas
'Coded by: Richard Denning 9/8/2020

sub VOTING_sys(stoLen, bpLvl, bpVot, exitVot, spLvl, spVot, coverVot)
'stoLen=21, bpLvl=20, bpVot=1, exitVot=0, spLvl=80, spVot=2, coverVot=1 
Dim stoD As BarArray
stoD = stochFast(H,L,C,stoLen)

Dim stoW As BarArray
Dim WH As BarArray
Dim WL As BarArray
Dim WC As BarArray
WC = C Of independent1
WL = L Of independent1
WH = H Of independent1
stoW = stochFast(WH,WL,WC,stoLen)

Dim stoM As BarArray
Dim MH As BarArray
Dim ML As BarArray
Dim MC As BarArray
MC = C Of independent2
ML = L Of independent2
MH = H Of independent2
stoM = stochFast(MH,ML,MC,stoLen)

Dim BP As BarArray
BP = IIF(stoD<=bpLvl,1,0)+IIF(stoW<=bpLvl,1,0)+IIF(stoM<=bpLvl,1,0)
If BP=bpVot Then Buy("LE",1,0,Market,Day)
If BP=exitVot And BP<bpVot Then ExitLong("LX","",1,0,Market,Day)

'Dim SP As BarArray
'SP = IIF(stoD>=spLvl,1,0)+IIF(stoW>=spLvl,1,0)+IIF(stoM<=spLvl,1,0)
'If SP=spVot Then Sell("SE",1,0,Market,Day)
'If SP=coverVot And SP<spVot Then Exitshort("SX","",1,0,Market,Day)
End Sub

Timeframes suggested by the author could only be partially implemented in TradersStudio, namely, the daily, weekly, and monthly timeframes. By adding more timeframes, if one requires them to all be voting in unison, significantly reduces the number of trades to the point that trading would occur only infrequently.

I coded a trading system that is long only and buys when any one of the three timeframes have a vote of 1. The trade is exited when the vote is less than the vote at the time of entry. I also found via optimization that a stochastic length of 7 worked better than 21.

Figure 11 shows the equity curve of trading all signals on an ETF portfolio (AGG, GLD, IWM, MDY, QQQ, DPY, VNQ, and XLU) with equal dollars put into each trade (Tradeplan=EqualEquityPlans). Figure 12 shows the drawdown percent.

Sample Chart

FIGURE 11: TRADERSSTUDIO. This displays the Tradeplan equity curve for a trading system that uses some of the article author’s timeframe indicators run on a portfolio of ETFs.

Sample Chart

FIGURE 12: TRADERSSTUDIO. This displays the Tradeplan underwater equity curve for a trading system that uses some of the article author’s timeframe indicators run on a portfolio of ETFs.

—Richard Denning
info@TradersEdgeSystems.com
for TradersStudio

BACK TO LIST

logo

THE ZORRO PROJECT: NOVEMBER 2020

In “Voting With Multiple Timeframes” by F. Arden Thomas in the August 2020 issue of S&C, the author sums up the returns by a stochastic indicator in a voting process over seven different timeframes, and uses the resulting votes for trade signals.

The article includes example code in the Smalltalk language for the stochastic indicator and counting the votes but not for the timeframe generation, which leaves some ambiguity in the timeframe generation.

Here is the Smalltalk code converted to C:

#define LEN  21 

void voteD()
{
  vars SeriesK = series((priceClose()-LL(LEN))/(HH(LEN)-LL(LEN)));
  if(MaxVal(SeriesK,5) > 0.8) SellVote++;
  if(MinVal(SeriesK,5) < 0.2) BuyVote++;
} 

The SeriesK data series is filled with the daily %K values from the “stochastic” formula over the last 21 bars. From the result, the maxima and minima of the previous 5 days are used for voting. SellVote and BuyVote are global variables for counting the votes.

For the further voting, the author applies the same indicator on weekly, monthly, bimonthly, quarterly, quarter-quarterly, and half-quarterly timeframes. Converted to multiples of a week, here I’m using timeframes of 1, 4, 8. 13, 3, and 6 weeks. There may be different ways to generate the timeframes, all resulting in slightly different votes, but we can see later that it doesn’t make much difference for the trading results.

Here is the voting function for multi-week timeframes:

void voteW(int Weeks)
{
  vars H = series(priceHigh(),-LEN),
    L = series(priceLow(),-LEN),
    C = series(priceClose(),-LEN);

  if((dow() == FRIDAY) && (week() % Weeks == 0)) { // new value every nth week
    shift(H,priceHigh(),LEN);
    shift(L,priceLow(),LEN);
    shift(C,priceClose(),LEN);
  }
  var PercentK = (C[0]-MinVal(L,LEN))/(MaxVal(H,LEN)-MinVal(L,LEN));
  if(PercentK > 0.8) SellVote++;
  if(PercentK < 0.2) BuyVote++;
} 

Since Zorro natively only supports timeframes of up to one week, the function declares data series for the high, low, and close prices, and adds new values to them on any Friday every nth week.

Here is the code to generate a voting chart for sumbol HAL:

int BuyVote,SellVote;

void run()
{
  BarPeriod = 1440;
  StartDate = 2006;
  EndDate = 2020;
  LookBack = 13*LEN;

  assetAdd("HAL","STOOQ:HAL.US");
  asset("HAL");

  BuyVote = SellVote = 0;
  voteD(); 	// daily
  voteW(1);	// weekly
  voteW(4);	// monthly (4 weeks)
  voteW(8);	// bimonthly
  voteW(13);	// quarterly (13 weeks)
  voteW(3);	// quarter-quarterly
  voteW(6);	// half-quarterly

  plot("BuyVote",BuyVote,NEW|LINE,BLUE);
  plot("SellVote",SellVote,LINE,RED);
}

The resulting chart of HAL with the votes is shown in Figure 13.

Sample Chart

FIGURE 13: ZORRO PROJECT. Here’s a sample chart of HAL showing the vote lines. The blue line is the buy vote and the red line is the sell vote.

The blue line is the buy vote and the red line the sell vote. We can see that the votes are very similar to the author’s chart in the article, but not 100% identical. The unknown functions for determining the timeframes are probably slightly different from mine.

How does the voting perform in a real trading system? This is the moment when newly invented indicators can fall apart. The author suggests three slightly different methods for generating trade signals. I coded all 3:

// Example 1
if(BuyVote == BUYVOTEMAX && SellVote == 0)
  enterLong();
if(SellVote == SELLVOTEMAX && BuyVote == 0)
  exitLong();

// Example 2
if(BuyVotes[1] >= 6 && SellVotes[1] == 0 && SellVotes[0] > 0)
  enterLong();
if(SellVotes[0] == 7 && BuyVote == 0)
  exitLong();

// Exampüle 3
if(BuyVotes[1] >= 6 && SellVotes[1] == 0 && falling(BuyVotes))
  enterLong();
if(SellVote == SELLVOTEMAX && BuyVote == 0)
  exitLong();

The full code can be found in the script repository. I won’t show resulting charts from all three examples here since all three methods produce similar results, and the entry and exit points marked by the author in his chart are indeed triggered. Of the two trades triggered in seven years, one was winning. The reason for this poor performance might be not the voting concept but rather the choice of the stock or the “stochastic” indicator. So when experimenting with different timeframes, assets, indicators, and triggering methods, the trader may be able to get more trades and better results. I have seen that voting can work very well in machine learning systems.

The voting indicators and system can be downloaded from the 2020 script repository at https://financial-hacker.com. The Zorro platform can be downloaded from https://zorro-project.com.

—Petra Volkova
The Zorro Project by oP group Germany
www.zorro-project.com

BACK TO LIST

MICROSOFT EXCEL: NOVEMBER 2020

In his August 2020 article, F. Arden Thomas demonstrates a composite indicator derivation that can be applied to almost any oscillator that can have an overbought and oversold interpretation.

For his demonstration in his article, Thomas uses the stochastic %K value, as derived with a slight variation: He drops the final “times 100” of the calculation so that the resulting values range from 0.00 to 1.0.

In the article, Thomas “propagates” (maps) all of the voting data points to weekly bar periods. This propagation translates to the same monthly vote value showing up on all of the weeks that map into a given month; likewise for quarterly and the subdivisions of month and quarter used in the article.

In my Figure 14 you will also see prices and voting that correspond to the right-most third of Figure 2 in Thomas’ article. To see the middle third of Figure 2 from the article, you may add 1,030 to the current value in cell A11. This pans the data window left 3+ years into the past.

Sample Chart

FIGURE 14: EXCEL. Here is my approximation of the stochastics chart from Figure 1 of Thomas’ article with the corresponding vote values.

The calculations for this spreadsheet are relatively straightforward if a bit repetitive. For seven timeframes, the column count became a bit bulky for the charting tab so I moved the bulk of the repetitive work to a helper tab named PeriodBarBuilder. The CalculationsAndCharts tab pulls the finished values back in preparation for charting.

As downloaded, this spreadsheet cannot duplicate the entire width (number of years) of Figure 2 from the article. I did not set up anywhere near enough “available calculation rows” (as shown in cell A7). You would need something like 6,000 calculation rows to duplicate Figure 2 of the article.

The current calculation row count represents a tradeoff between full equivalence to the charts in the article and physical size of the spreadsheet. As it is, this spreadsheet is a bit bulky at over 4 megabytes and I always try to keep these Traders’ Tips spreadsheets under 3.5 megabytes. For those comfortable with editing spreadsheets, you can extend both the CalculationsAndCharts tab and the PeriodBarBuilder tabs towards the 6,000 calculation rows number. (Both tabs must have the same number of calculation rows.)

However, there are three columns on the PeriodBarBuilder tab that require special handling in such an extension. These are the columns that calculate bars-per-interval numbers for the Bi-Month, Bi-Quarter and Qtr-Quarter intervals (columns AQ, BR, and CE). These three columns each subdivide the count of bars in the bars-per-interval columns of the next larger interval. Bi-Month splits Month, Bi-Quarter splits Quarter, and Qtr-Quarter splits Bi-Quarter.

For example, AQ subdivides the values in AD. The goal is two new rows each containing half of the value of each cell in AD, thus doubling the number of rows in the new column.

To accomplish this, the cells in AQ come in pairs, the first of the pair divides the value from AD by 2 and rounds up for any odd half value. The second of the pair takes the difference of the source AD cell minus the computed AQ value. So for an AD value of 3, the first AQ would be 2, and the second AQ would be 1.

So any attempt to extend the calculation rows on the PeriodBarBuilder tab must use a pairwise drag-fill for each of these three columns.

Additional column formula and use commentary can be found as cell notes in the column headers row. Hover over any cell tagged with a small red flag in the upper right corner.

To download this spreadsheet: The spreadsheet file for this Traders’ Tip can be downloaded here. To successfully download a working version of this spreadsheet, follow these steps:

—Ron McAllister
Excel and VBA programmer
rpmac_xltt@sprynet.com

BACK TO LIST

Originally published in the November 2020 issue of
Technical Analysis of STOCKS & COMMODITIES magazine.
All rights reserved. © Copyright 2020, Technical Analysis, Inc.