TRADERS’ TIPS

July 2024

Tips Article Thumbnail

For this month’s Traders’ Tips, the focus is Perry Kaufman’s article in the June 2024 issue, “Trading Opening Gaps And Extreme Closes In Stocks.” Here, we present the July 2024 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

Realtest: July 2024

For the type of research shown in Perry Kaufman’s article in the June 2024 issue, “Trading Opening Gaps And Extreme Closes In Stocks,” we can use the backtest engine in RealTest.

The following script, when run in the “optimize” mode, outputs a results list in RealTest that resembles the author’s table of stats for different gap ranges:

Notes:
	implements Perry Kaufman's research into intraday movement on gap days
	run Import and then Opimize to produce an entire table of pullback and runup stats
	
Import:
	DataSource:	Norgate
	IncludeList:	.S&P 500 Current & Past
	StartDate:	1/1/2010
	EndDate:	Latest
	SaveAs:	sp500.rtd
	
Settings:
	DataFile:	sp500.rtd
	StartDate:	Earliest
	EndDate:	Latest
	BarSize:	Daily
	
	TestName:	Select(border < -0.11, "gap < -11%", 
		border >= 0.11, "gap >= 11%", 
		Format("from {%} to {%}", border, border + 0.02))

Parameters:
	border:	from -0.13 to 0.11 step 0.02

Data:
	gap:	O[-1] / C - 1	// look ahead to tomorrow's open for research purposes
	gap_in_range:	Select(border < -0.11, gap < -0.11,
		border >= 0.11, gap >= 0.11,
		gap >= border and gap < border + 0.02)
	
Strategy: gap_data
	Side:	Long
	EntrySetup:	InSPX and gap_in_range
	EntryTime:	NextOpen	// buy at open of gap day
	ExitRule:	1	
	ExitTime:	NextClose	// sell at close of gap day
	Quantity:	1	// one share (position size is not relevant to this study)

Results: // override default set of results columns with ones specific to this study
	Instances:	{#}	Sum(S.Exits,S.Number)
	OpenToClose:	{%2}	Sum(S.TradePct,S.Number) / Instances
	Pullback:	{%2} 	TradeStatAvg(T.Lowest / T.PriceIn - 1)
	Runup:	{%2}	TradeStatAvg(T.Highest / T.PriceIn - 1)

Rather than a fixed universe of symbols, this uses the historical constituents of the S&P 500 index for each date since 2010 (the author’s start date in his study).

The RealTest output is shown in Figure 1. Like the author’s, these results show that the average “open to close” change is insignificant regardless of gap size.

Sample Chart

FIGURE 1: REALTEST. A script in RealTest, run in the “optimize” mode, outputs a list of metrics for different gap ranges.

Rather than only including “pullback” for gaps up and “upward pullback” (runup) for gaps down, this includes both values for all gap ranges.

It is interesting to note, though not surprising, that larger gaps in either direction have larger average intraday moves in both directions.

RealTest users can use the following script for testing to see how useful the research may be for them in practice. This script that I built backtests the following comparison for each gap range:

Notes:
	tests Perry Kaufman's idea with gap-up days in long positions:
	- sell the open, buy intraday pullback, sell on close
	- vs. always selling the close
	
Import:
	DataSource:	Norgate
	IncludeList:	.S&P 500 Current & Past
	StartDate:	1/1/2010
	EndDate:	Latest
	SaveAs:	sp500.rtd
	
Settings:
	DataFile:	sp500.rtd
	StartDate:	Earliest
	EndDate:	1/1/20
	BarSize:	Daily
	AccountSize:	1e6	// $1M account size

	TestName:	Select(border >= 0.11, Format("gap >= 11%, pullback = {~}", pullback), 
		Format("from {%} to {%}, pullback = {~}", border, border + 0.02, pullback))

Parameters:
	border:	from 0.01 to 0.11 step 0.02 def 0.07 // gap up size lower border
	pullback:	0, 1 // always hold until close if 0, sell at open then place limit order to buy again if 1

Data:
	gap:	O[-1] / C - 1	// look ahead to tomorrow's open
	gap_in_range:	gap >= border and (border = 0.11 or gap < border + 0.02)
	
Strategy: gap_sell_open_or_close
	Side:	Long
	Quantity:	10000	
	QtyType:	Value	// $10K fixed position size
	EntrySetup:	InSPX and gap_in_range
	EntryTime:	ThisClose	// enter on close when tomorrow will gap up
	ExitRule:	1	
	ExitTime:	NextClose	// sell next close when not using pullback
	ExitStop:	if(pullback, O[-1], 0)	// or sell next open when using pullback

Strategy: gap_buy_pullback_sell_close
	Side:	Long
	Quantity:	10000
	QtyType:	Value	// $10K fixed position size again
	EntrySetup:	pullback and InSPX and gap_in_range
	EntryLimit:	O[-1] * (1 - border/2)	// enter at pullback to half the min. gap of this test
	ExitRule:	1
	ExitTime:	ThisClose	// exit on the close of the entry day

This uses RealTest’s multi-strategy capability to combine two strategies: one for the initial entry, the other for the pullback reentry.

To keep this a bit simpler, I omitted a downward gap study; presumably, that would compare holding to the close versus exiting with a limit order on a runup.

The results of those tests are shown in Figure 2. Ignore the specific dollar amount—this is not a tradable strategy, just a research project. Instead, focus on the difference between each pair of tests for the same gap range.

Sample Chart

FIGURE 2: REALTEST. For comparison and for testing or research, the same script could be rerun without the downward gap study, producing results such as those shown here.

The first test of each such pair buys the close prior to the gap up and sells the following close. The second test of each pair sells the open and then reenters on pullback to half the lower gap range if touched, thus the higher trade counts.

Note that the profit differences either way are minimal and one way is not consistently better than the other, regardless of gap size.

Readers can use the scripts provided here to test for themselves some of these concepts.

—Marsten Parker
MHP Trading, Mhptrading.com
mhp@mhptrading.com

BACK TO LIST

logo

Wealth-Lab.com: July 2024

In “Trading Opening Gaps And Extreme Closes In Stocks” in the June 2024 issue, Perry Kaufman presents some research into price behavior surrounding opening gaps and large closes. Several tables of data are given with statistics.

Wealth-Lab users have access to an opensource tool they can use in Wealth-Lab to plot such data to make it easier to visualize. The tool is called ScottPlot.

Wealth-Lab 8 can display various custom plots such as line or bar charts, pie graphs, and scatter plots by throwing in some code. The code given here accomplishes a visualization for the purposes of the article with the help of ScottPlot.

Figure 3 consists of two histogram plots:

  1. the upper plot is a count of opening gaps vs. their % range, or cohort;
  2. the lower plot is the average gap size (%) vs. average pullback size (%).

For both histograms, down gaps are clustered on the left and up gaps to the right of axis X.

Sample Chart

FIGURE 3: WEALTH-LAB. In the upper histogram, you see a count of opening gaps versus their % range, or cohort. In the lower graph, you see the average gap size (%) versus average pullback size (%). For both histograms, down gaps are clustered on the left and up gaps to the right of the x axis.

If you prefer raw data, on the debug log tab, you’ll find the data behind those charts in a tabular form: the gaps themselves by symbol, cohort (the bins for gap sizes i.e. <1%, 1–3% gaps, and so on, average gap size up/down %, and average pullback % size).

using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Data;
using WealthLab.Indicators;
using System.Collections.Generic;
using System.Drawing;
using ScottPlot;
using System.Linq;
using System.IO;

namespace WealthScript8 
{
	public class Gap
	{
		public string Symbol { get; set; }
		public double Size { get; set; }
		public double PullbackSize { get; set; }
		public int Cohort { get; set; }
		public bool Up { get; set; }
		public DateTime Date { get; set; }

		public Gap() { }
		public Gap(string symbol, double size, double pullback, int cohort, bool isGapUp, DateTime dt) 
			{ Symbol = symbol; Size = size; PullbackSize = pullback; Cohort = cohort; Up = isGapUp; Date = dt; }
	}

	public class AverageGap
	{
		public string Symbol { get; set; }
		public double AverageSize { get; set; }
		public double AveragePullback { get; set; }
		public int Cohort { get; set; }
		public bool Up { get; set; }

		public AverageGap() { }
		public AverageGap(string symbol, double size, double pullback, int cohort, bool isGapUp) { Symbol = symbol; AverageSize = size; AveragePullback = pullback; Cohort = cohort; Up = isGapUp; }
	}

	public class TradersTips_Jul2024 : UserStrategyBase
	{
		public override void PreExecute(DateTime dt, List<BarHistory> participants)
		{
			foreach (BarHistory bars in participants)
			{
				int bar = GetCurrentIndex (bars);
				if (bar == bars.Count - 1)
				{
					int step = 2;
					for (int i = 1; i < bars.Count; i++)
					{
						double gap = 100.0 * (bars.Open[i] / bars.Close[i - 1] - 1);
						double _raw = Math.Abs(gap);
						if (bars. IsGapUp (i) || bars. IsGapDown (i))
						{
							double pullback = 100.0 * ( (bars. IsGapUp (i) ? bars.Low[i] : bars.High[i]) / bars.Open[i] - 1);
							int cohort = 0;
							for (int j = 0; j <= 7; j++)
							{
								if (_raw >= j && _raw < j + step)
									cohort = j;
								if ((_raw > 0 & _raw < 1) || (_raw < 0 & _raw > -1))
								{
									cohort = 0; break;
								}
							}
							Gap g = new Gap(bars.Symbol, gap, pullback, (bars. IsGapUp (i) ? cohort : -cohort), (bars. IsGapUp (i) ? true : false), bars.DateTimes[i]);
							if (gap != 0 && !double.IsNaN(gap))
								/* filter out small gaps within 0.5% */
								if (_raw >= 0.5)
									lstGaps.Add(g);
						}
					}
				}
			}
		}

		static List<Gap> lstGaps = new List<Gap>();
		static List<AverageGap> lstAvgGapsUp = new List<AverageGap>();
		static List<AverageGap> lstAvgGapsDn = new List<AverageGap>();
		Bitmap bmp = null;
		Plot plt1, plt2;
		string[] lstTicks = new string[14] { ">-11%", "-9-11%", "-7-9%", "-5-7%", "-3-5%", "-1-3%", ">-1%", "<1%", "1-3%", "3-5%", "5-7%", "7-9%", "9-11%", ">11%" };
		string path;

		public override void Initialize( BarHistory bars)
		{
			bmp = null;
			plt1 = new ScottPlot.Plot(700, 350);
			plt2 = new ScottPlot.Plot(700, 350);

			/* Plot actual chart of average gap and pullback % */
			plt1.Title(string.Format("Opening Gaps Count vs. Range"));
			plt1.YLabel(string.Format("Average % Gap Up/Pullback"));
			plt1.XLabel("Cohort");
			plt1.Grid(enable: false, lineStyle: ScottPlot.LineStyle.Dot);
			plt1.YAxis.Label("Count (#)");
			plt1.XAxis.Label("Range (%)");

			plt2.SetCulture(decimalDigits: 2);
			plt2.YAxis.Label("Avg. Pullback Size (%)");
			plt2.XAxis.Label("Avg. Gap Size (%)");

			path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
		}

		public override void Execute ( BarHistory bars, int idx)
		{
		}

		public override void BacktestComplete()
		{
			/* gap debug stats */
			lstGaps.OrderBy(g => g.Up);
			WriteToDebugLog ("Symbol" + "\t" + "Date" + "\t" + "Size" + "\t" + "Cohort" + "\t" + "Pullback Size" + "\t" + "Up?");
			foreach (var item in lstGaps)
				WriteToDebugLog (item.Symbol + "\t" + item.Date.ToShortDateString() + "\t" + Math.Round(item.Size, 2) + "\t" + item.Cohort + "\t" + Math.Round(item.PullbackSize, 2) + "\t" + item.Up, true);
				
			/* average gap size and pullback by cohort */
			lstAvgGapsUp = lstGaps.Where(i => i.Up == true).GroupBy(i => i.Cohort).
				Select(g => new AverageGap { Cohort = g.Key, AverageSize = g.Average(a => a.Size), AveragePullback = g.Average(a => a.PullbackSize) }).ToList();
			lstAvgGapsDn = lstGaps.Where(i => i.Up == false).GroupBy(i => i.Cohort).
				Select(g => new AverageGap { Cohort = g.Key, AverageSize = g.Average(a => a.Size), AveragePullback = g.Average(a => a.PullbackSize) }).ToList();
			
			/* create a histogram with a fixed number of bins */
			var hist1 = ScottPlot.Statistics.Histogram.WithFixedBinSize(min: 1, max: 11, binSize: 2);
			var hist2 = ScottPlot.Statistics.Histogram.WithFixedBinSize(min: -11, max: -1, binSize: 2);
			hist1.AddRange(lstGaps.Where(i => i.Up == true).Select(g => g.Size));
			hist2.AddRange(lstGaps.Where(i => i.Up == false).Select(g => g.Size));
			
			/* show the histogram counts as a bar plot */
			var bar1 = plt1.AddBar(values: hist1.Counts, positions: hist1.Bins);
			var bar2 = plt1.AddBar(values: hist2.Counts, positions: hist2.Bins);
			bar2.BarWidth = bar1.BarWidth = 2;

			/* average gap debug stats */
			WriteToDebugLog (Environment.NewLine + "Cohort" + "\t" + "Avg.Size %" + "\t" + "Avg.Pullback %");
			WriteToDebugLog (Environment.NewLine + "Gaps Up");
			foreach (var item in lstAvgGapsUp)
				WriteToDebugLog (item.Cohort + "\t" + Math.Round(item.AverageSize, 2) + "\t" + Math.Round(item.AveragePullback, 2), true);
			WriteToDebugLog (Environment.NewLine + "Gaps Down");
			foreach (var item in lstAvgGapsDn)
				WriteToDebugLog (item.Cohort + "\t" + Math.Round(item.AverageSize, 2) + "\t" + Math.Round(item.AveragePullback, 2), true);

			plt2.PlotBar(lstAvgGapsUp.Select(a => (double)a.Cohort).ToArray(), lstAvgGapsUp.Select(a => Math.Round(a.AverageSize, 2)).ToArray(), showValues: true);
			plt2.PlotBar(lstAvgGapsUp.Select(a => (double)a.Cohort).ToArray(), lstAvgGapsUp.Select(a => Math.Round(a.AveragePullback, 2)).ToArray(), showValues: true);
			plt2.PlotBar(lstAvgGapsDn.Select(a => (double)a.Cohort).ToArray(), lstAvgGapsDn.Select(a => Math.Round(a.AverageSize, 2)).ToArray(), showValues: true);
			plt2.PlotBar(lstAvgGapsDn.Select(a => (double)a.Cohort).ToArray(), lstAvgGapsDn.Select(a => Math.Round(a.AveragePullback, 2)).ToArray(), showValues: true);

			bmp = plt1.GetBitmap();
			DrawImageAt( bmp, 0, 0);
			bmp = plt2.GetBitmap();
			DrawImageAt( bmp, 0, 350);
		}
	}
}

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

BACK TO LIST

logo

TradingView: July 2024

The following Pine Script for TradingView creates a table showing the extreme price move frequencies (gaps or extreme closes), average pullbacks, and next closes, similar to the type of research Perry Kaufman presents in his June 2024 article, “Trading Opening Gaps And Extreme Closes In Stocks.”

//  TASC Issue: July 2024
//     Article: Trading Opening Gaps
//              And Extreme Closes In Stocks.
//  Article By: Perry J. Kaufman
//    Language: TradingView's Pine Script™ v5
// Provided By: PineCoders, for tradingview.com


//@version=5
string title = 'TASC 2024.07 Gaps and Extreme Closes'
string stitle = 'G&C'
indicator(title, stitle, false)


//   Input:
string it0 = "Opening Gaps"
string it1 = "Extreme Closes"
string it2 = "Upward"
string it3 = "Downward"
string out1 = input.string(it0, "Output",    [it0, it1])
string out2 = input.string(it2, "Direction", [it2, it3])


//   Data structure:
type Threshold
    int gap_freq_up = 0
    int gap_freq_dn = 0
    float gap_up = 0.0
    float gap_dn = 0.0
    float gap_up_pb = 0.0
    float gap_dn_pb = 0.0
    float gap_up_close = 0.0
    float gap_dn_close = 0.0
    int close_freq_up = 0
    int close_freq_dn = 0
    float close_up = 0.0
    float close_dn = 0.0
    float next_open_up = 0.0
    float next_open_dn = 0.0
    float next_close_up = 0.0
    float next_close_dn = 0.0


type Data
    float[] thresholds
    map<float, Threshold> bins


//   Functions:
get_data () =>
    // Gap bins threshold levels:
    var thresholds = array.new<float>(7)
    var bins = map.new<float, Threshold>()
    if barstate.isfirst
        float x = -0.01
        for _i = 0 to 6
            x += 0.02
            thresholds.set(_i, x)
            bins.put(x, Threshold.new())
    // Gap logic:
    float gap = open / close[1] - 1.0
    // opening gap higher:
    if gap > 0.0
        int saveindex = 0
        bool found = false
        for _i = 0 to 6
            if not found
                if gap < thresholds.get(_i)
                    found := true
                    bini = bins.get(thresholds.get(_i))
                    bini.gap_freq_up += 1
                    bini.gap_up += gap
                    saveindex := _i
        if not found
            bini = bins.get(thresholds.get(6))
            bini.gap_freq_up += 1
            bini.gap_up += gap
            saveindex := 6
        // pullback from gap higher:
        bini = bins.get(thresholds.get(saveindex))
        float gap_pb = low / open - 1.0
        bini.gap_up_pb += gap_pb
        // close relative to gap open:
        float gap_close = close / open - 1.0
        bini.gap_up_close += gap_close
    // opening gap lower:
    if gap < 0.0
        float agap = math.abs(gap)
        int saveindex = 0
        bool found = false
        for _i = 0 to 6
            if not found
                if agap < thresholds.get(_i)
                    found := true
                    bini = bins.get(thresholds.get(_i))
                    bini.gap_freq_dn += 1
                    bini.gap_dn += agap
                    saveindex := _i
        if not found
            bini = bins.get(thresholds.get(6))
            bini.gap_freq_dn += 1
            bini.gap_dn += agap
            saveindex := 6
        // upward pullback from gap lower:
        bini = bins.get(thresholds.get(saveindex))
        float gap_pb = high / open - 1.0
        bini.gap_dn_pb += gap_pb
        // close relative to gap open:
        float gap_close = close / open - 1.0
        bini.gap_dn_close += gap_close
    // Extreme close logic:
    float extreme_close = close / close[1] - 1.0
    // extreme close higher:
    if extreme_close > 0.0
        int saveindex = 0
        bool found = false
        for _i = 0 to 6
            if not found
                if extreme_close < thresholds.get(_i)
                    found := true
                    bini = bins.get(thresholds.get(_i))
                    bini.close_freq_up += 1
                    bini.close_up += extreme_close
                    saveindex := _i
                    log.info('found!')
        if not found
            bini = bins.get(thresholds.get(6))
            bini.close_freq_up += 1
            bini.close_up += extreme_close
            saveindex := 6
        // next open and close:
        bini = bins.get(thresholds.get(saveindex))
        bini.next_open_up += open / close[1] - 1.0
        bini.next_close_up += close / close[1] - 1.0
    // extreme close lower:
    if extreme_close < 0.0
        float aextreme_close = math.abs(extreme_close)
        int saveindex = 0
        bool found = false
        for _i = 0 to 6
            if not found
                if aextreme_close < thresholds.get(_i)
                    found := true
                    bini = bins.get(thresholds.get(_i))
                    bini.close_freq_dn += 1
                    bini.close_dn += aextreme_close
                    saveindex := _i
        if not found
            bini = bins.get(thresholds.get(6))
            bini.close_freq_up += 1
            bini.close_up += aextreme_close
            saveindex := 6
        // next open and close:
        bini = bins.get(thresholds.get(saveindex))
        bini.next_open_dn += open / close[1] - 1.0
        bini.next_close_dn += close / close[1] - 1.0
    Data data = Data.new(thresholds, bins)
    data


//   Data to lists:
method get_gaps_up_freq (Data data) =>
    int[] _freq = array.new<int>(7, int(na))
    for [_i, _thrs] in data.thresholds
        _freq.set(_i, data.bins.get(_thrs).gap_freq_up)
    _freq


method get_gaps_dn_freq (Data data) =>
    int[] _freq = array.new<int>(7, int(na))
    for [_i, _thrs] in data.thresholds
        _freq.set(_i, data.bins.get(_thrs).gap_freq_dn)
    _freq


method get_gaps_up_pb (Data data) =>
    float[] _pb = array.new<float>(7, float(na))
    for [_i, _thrs] in data.thresholds
        float _pbi = data.bins.get(_thrs).gap_up_pb
        int   _fqi = data.bins.get(_thrs).gap_freq_up
        if _fqi != 0 // protect agains div by 0
            _pb.set(_i, _pbi / _fqi)
    _pb


method get_gaps_dn_pb (Data data) =>
    float[] _pb = array.new<float>(7, float(na))
    for [_i, _thrs] in data.thresholds
        float _pbi = data.bins.get(_thrs).gap_dn_pb
        int   _fqi = data.bins.get(_thrs).gap_freq_dn
        if _fqi != 0 // protect agains div by 0
            _pb.set(_i, _pbi / _fqi)
    _pb


method get_gaps_up_close_vs_open (Data data) =>
    float[] _cvo = array.new<float>(7, float(na))
    for [_i, _thrs] in data.thresholds
        float _pi = data.bins.get(_thrs).gap_up_close
        int  _fqi = data.bins.get(_thrs).gap_freq_up
        if _fqi != 0 // protect agains div by 0
            _cvo.set(_i, _pi / _fqi)
    _cvo


method get_gaps_dn_close_vs_open (Data data) =>
    float[] _cvo = array.new<float>(7, float(na))
    for [_i, _thrs] in data.thresholds
        float _pi = data.bins.get(_thrs).gap_dn_close
        int  _fqi = data.bins.get(_thrs).gap_freq_dn
        if _fqi != 0 // protect agains div by 0
            _cvo.set(_i, _pi / _fqi)
    _cvo


method get_close_up_freq (Data data) =>
    int[] _freq = array.new<int>(7, int(na))
    for [_i, _thrs] in data.thresholds
        _freq.set(_i, data.bins.get(_thrs).close_freq_up)
    _freq


method get_close_dn_freq (Data data) =>
    int[] _freq = array.new<int>(7, int(na))
    for [_i, _thrs] in data.thresholds
        _freq.set(_i, data.bins.get(_thrs).close_freq_dn)
    _freq


method get_close_up_next_open (Data data) =>
    float[] _pb = array.new<float>(7, float(na))
    for [_i, _thrs] in data.thresholds
        float _pi = data.bins.get(_thrs).next_open_up
        int   _fi = data.bins.get(_thrs).close_freq_up
        if _fi != 0 // protect agains div by 0
            _pb.set(_i, _pi / _fi)
    _pb


method get_close_dn_next_open (Data data) =>
    float[] _pb = array.new<float>(7, float(na))
    for [_i, _thrs] in data.thresholds
        float _pi = data.bins.get(_thrs).next_open_dn
        int   _fi = data.bins.get(_thrs).close_freq_dn
        if _fi != 0 // protect agains div by 0
            _pb.set(_i, _pi / _fi)
    _pb


method get_close_up_next_close (Data data) =>
    float[] _cvo = array.new<float>(7, float(na))
    for [_i, _thrs] in data.thresholds
        float _pi = data.bins.get(_thrs).next_close_up
        int   _fi = data.bins.get(_thrs).close_freq_up
        if _fi != 0 // protect agains div by 0
            _cvo.set(_i, _pi / _fi)
    _cvo


method get_close_dn_next_close (Data data) =>
    float[] _cvo = array.new<float>(7, float(na))
    for [_i, _thrs] in data.thresholds
        float _pi = data.bins.get(_thrs).next_close_dn
        int   _fi = data.bins.get(_thrs).close_freq_dn
        if _fi != 0 // protect agains div by 0
            _cvo.set(_i, _pi / _fi)
    _cvo


//   Table:
method gap_table (Data data, bool gaps, bool dir) =>
    table _tbl = table.new(position.bottom_center, 8, 5)
    string _title = switch gaps
        true  => dir ? 'GAP UP' : 'GAP DOWN'
        false => dir ? 'CLOSE UP' : 'CLOSE DOWN'
    color _col_bg = color.rgb(200, 208, 231)
    color _col_bg_title = color.rgb(160, 170, 180)
    color _col_bg_dir = dir ? color.lime : color.orange
    _tbl.cell(0, 0, _title, bgcolor=_col_bg_title)
    _tbl.cell(7, 0, _title, bgcolor=_col_bg_title)
    _tbl.merge_cells(0, 0, 7, 0)
    _tbl.cell(0, 1, 'Ranges:', bgcolor=_col_bg_dir)
    _tbl.cell(0, 2, 'Frequency:', bgcolor=_col_bg_dir)
    _tbl.cell(0, 3, 'Pullback:', bgcolor=_col_bg_dir)
    _tbl.cell(0, 4, 'Close vs Open:', bgcolor=_col_bg_dir)
    int[] _freq  = switch gaps 
        true  => dir ? data.get_gaps_up_freq() :
                       data.get_gaps_dn_freq()
        false => dir ? data.get_close_up_freq() :
                       data.get_close_dn_freq()
    float[] _pb  = switch gaps 
        true  => dir ? data.get_gaps_up_pb() :
                       data.get_gaps_dn_pb()
        false => dir ? data.get_close_up_next_open() :
                       data.get_close_dn_next_open()
    float[] _cvo  = switch gaps 
        true  => dir ? data.get_gaps_up_close_vs_open() :
                       data.get_gaps_dn_close_vs_open()
        false => dir ? data.get_close_up_next_close() :
                       data.get_close_dn_next_close()
    for [_i, _thrs] in data.thresholds
        _r = str.tostring(_i*2-1)+'-'+str.tostring(_i*2+1)+'%'
        _tbl.cell(1+_i, 1, _r, bgcolor=_col_bg_dir)
        _tbl.cell(1+_i, 2, str.format('{0,number,integer}', 
                             _freq.get(_i)), bgcolor=_col_bg)
        _tbl.cell(1+_i, 3, str.format('{0,number,#.##%}', 
                             _pb.get(_i)), bgcolor=_col_bg)
        _tbl.cell(1+_i, 4, str.format('{0,number,#.##%}', 
                             _cvo.get(_i)), bgcolor=_col_bg)
    _tbl.cell(1, 1, '<1%', bgcolor=_col_bg_dir)
    _tbl.cell(7, 1, '>11%', bgcolor=_col_bg_dir)
    _tbl


//   Calculations:
bool  gaps = out1 == it0
bool  dir  = out2 == it2
data = get_data()
if barstate.islastconfirmedhistory
    table tb = data.gap_table(gaps, dir)

The script is available on TradingView from the PineCodersTASC account: https://www.tradingview.com/u/PineCodersTASC/#published-scripts.

An example output table is shown in Figure 4.

Sample Chart

FIGURE 4: TRADINGVIEW. A script creates a table showing the extreme price move frequencies in the data (gaps or extreme closes), average pullbacks, and next closes.

—PineCoders, for TradingView
www.TradingView.com

BACK TO LIST

logo

Neuroshell Trader: July 2024

The calculations performed in Perry Kaufman’s article in the June 2024 issue, “Trading Opening Gaps And Extreme Closes In Stocks,” can be easily implemented in NeuroShell Trader by combining some of NeuroShell Trader’s 800+ indicators. To implement the calculations, select “new indicator” from the insert menu and use the indicator wizard to create the following indicators:

Gap:			Subtract( Divide( Open, Lag(Close,1) ), 1 )
GapPBup:		Subtract( Divide( Low, Open), 1 )
GapPBdown:		Subtract( Divide( High, Open), 1 )
GapClose:		Subtract( Divide( Close, Open), 1 )

GapUpThreshold:	A<B<C ( LowerThreshold, Gap, UpperThreshold )
GapFreqUp:		CumSum ( IfThenElse( GapUpThreshold, 1, 0 ), 0 )
GapUp:		CumSum ( IfThenElse( GapUpThreshold, Gap, 0 ), 0 )
GapUpPB:		CumSum ( IfThenElse( GapUpThreshold, GapPBup, 0 ), 0 )
GapUpClose:		CumSum ( IfThenElse( GapUpThreshold, GapClose, 0 ), 0 )

GapDownThreshold:	A<B<C ( Mult2(UpperThreshold,-1), Gap, Mult2(LowerThreshold,-1) )
GapFreqDown:		CumSum ( IfThenElse( GapDownThreshold, 1, 0 ), 0 )
GapDown:		CumSum ( IfThenElse( GapDownThreshold, Gap, 0 ), 0 )
GapDownPB:		CumSum ( IfThenElse( GapDownThreshold, GapPBdown, 0 ), 0 )
GapDownClose:	CumSum ( IfThenElse( GapDownThreshold, GapClose, 0 ), 0 )

ExtremeClose:		Subtract( Divide( Close, Lag(Close, 1) ), 1 )
NextOpen:		Subtract( Divide( Open, Lag(Close,1) ), 1 )
NextClose:		Subtract( Divide( Close, Lag(Close,1) ), 1 )

CloseUpThreshold:	A<B<C ( LowerThreshold, ExtremeClose, UpperThreshold )
CloseFreqUp:		CumSum ( IfThenElse( CloseUpThreshold, 1, 0 ), 0 )
CloseUp:		CumSum ( IfThenElse( CloseUpThreshold, ExtremeClose, 0 ), 0 )
NextOpenUp:		CumSum ( IfThenElse( CloseUpThreshold, NextOpen, 0 ), 0 )
NextCloseUp:		CumSum ( IfThenElse( CloseUpThreshold, NextClose, 0 ), 0 )

CloseDownThreshold:	A<B<C( Mult2(UpperThreshold,-1), NextClose, Mult2(LowerThreshold,-1) )
CloseFreqDown:	CumSum ( IfThenElse( CloseDownThreshold, 1, 0 ), 0 )
CloseDown:	 	CumSum ( IfThenElse( CloseDownThreshold, ExtremeClose, 0 ), 0 )
NextOpenDown:	CumSum ( IfThenElse( CloseDownThreshold, NextOpen, 0 ), 0 )
NextCloseDown:	CumSum ( IfThenElse( CloseDownThreshold, NextClose, 0 ), 0 )

GAP UP:			GapFreqUp
GAP UP PULLBACK:  		Multiply ( 100, Divide( GapUpPB, GapFreqUp )
GAP UP CLOSE VS OPEN: 	Multiply( 100, Divide( GapUpClose, GapFreqUp )

GAP DOWN:			GapFreqUp
GAP DOWN PULLBACK:  		Multiply ( 100, Divide( GapDownPB, GapFreqDown )
GAP DOWN CLOSE VS OPEN: 	Multiply( 100, Divide( GapDownClose, GapFreqDown )

CLOSE UP:			CloseFreqUp
NEXT OPEN:			Multiply ( 100, Divide( NextOpenUp, CloseFreqUp)
NEXT CLOSE:			Multiply ( 100, Divide( NextCloseUp, CloseFreqUp)

CLOSE DOWN:			CloseFreqDown
NEXT OPEN:			Multiply ( 100, Divide( NextOpenDown, CloseFreqDown)
NEXT CLOSE:			Multiply ( 100, Divide( NextCloseDown, CloseFreqDown)

Users of NeuroShell Trader 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.

—Ward Systems Group, Inc.
sales@wardsystems.com
www.neuroshell.com

BACK TO LIST

logo

The Zorro Project: July 2024

In his article in the June 2024 issue, “Trading Opening Gaps And Extreme Closes In Stocks,” Perry Kaufman examines opening gaps and subsequent movements. The goal is not a trading system but rather a statistical analysis of gap-dependent price behavior. The premise investigated is that stocks tend to experience a strong pullback after a large upward gap, but revert to the opening price at the close of the day. This would present an opportunity to sell the stock at market open and then buy it back at a lower price during the day.

In the article, the author provides some EasyLanguage code that makes use of variables and arrays and allows the reader to create one table at a time. In the Zorro platform, users can generate a spreadsheet for the analysis of gaps, pullbacks, and relative closes of seven selected stocks. Here is code in C for use in Zorro to accomplish that:

#define STOCKS  "AAPL","AMZN","BABA","BAC","NVDA","TSLA","WMT"
#define NA	7	// number of stocks
#define NR	7	// number of ranges

var Ranges[NR] = { 1,3,5,7,9,11,9999 }; // the gap ranges
int Gaps[NA][NR];
var Pullbacks[NA][NR],Closes[NA][NR];
int i,j;

void run()
{
  BarPeriod = 1440;
  StartDate = 20100101;
  EndDate = 20240101;
  assetList("AssetsSP500"); // the S&P 500 index

if(is(INITRUN)) { // reset the arrays
  memset(Gaps,0,NA*NR*sizeof(int));
  memset(Pullbacks,0,NA*NR*sizeof(var));
  memset(Closes,0,NA*NR*sizeof(var));
}

for(j=0; 1; j++) { // calculate gaps, pullbacks, relative closes
  if(!asset(of(STOCKS))) break; // select the stock
  if(Bar < AssetFirstBar) continue;
  var Gap = 100*(priceO(0)/priceC(1)-1.);
  for(i=0; i<NR; i++)  if(Gap < Ranges[i]) {
    Gaps[j][i]++;
    Pullbacks[j][i] += priceL(0)/priceO(0)-1.;
    Closes[j][i] += priceC(0)/priceO(0)-1.;
    break;
  }
}

if(is(EXITRUN)) { // print the results to spreadsheet
  print(TO_CSV,"Range,< 1%%,1-3%%,3-5%%,5-7%%,7-9%%,9-11%%,> 11%%\n");
  for(j=0; 1; j++) {
    if(!asset(of(STOCKS))) break;
    print(TO_CSV,"%s Gaps",Asset);
    for(i=0; i<NR; i++)
      print(TO_CSV,",%i",Gaps[j][i]);
   print(TO_CSV,"\n");
  }
  print(TO_CSV,"\n");
  for(j=0; 1; j++) {
    if(!asset(of(STOCKS))) break;
    print(TO_CSV,"%s PBs",Asset);
    for(i=0; i<NR; i++)
      print(TO_CSV,",%.2f%%",100*Pullbacks[j][i]/fix0(Gaps[j][i]));
    print(TO_CSV,"\n");
  }
  print(TO_CSV,"\n");
  for(j=0; 1; j++) {
    if(!asset(of(STOCKS))) break;
      print(TO_CSV,"%s Cls",Asset);
    for(i=0; i<NR; i++)
      print(TO_CSV,",%.2f%%",100*Closes[j][i]/fix0(Gaps[j][i]));
    print(TO_CSV,"\n");
  }
  exec("Data\\Gaps.csv",0,0); // open spreadsheet in Excel
}

I’ll explain some of the code. The of function takes a list of items, such as stock names, and returns one after the other at any call. At the end of the list it returns 0. Since some of the assets did not yet exist in 2010, the AssetFirstBar variable is used to skip that empty part of the history. The memset function initializes all arrays to zero at start. At the end of the history, the EXITRUN flag becomes true and the collected data is printed to a CSV spreadsheet. I’m printing all three statistics to the same spreadsheet. The fix0 function prevents a division-by-zero error, since some ranges have zero gaps. For replicating Kaufman’s results, I have also included negative gaps in the first “< 1%” range.

The spreadsheet shown in Figure 5 yields similar results as in Kaufman’s article, with some deviations since Kaufman probably used a slightly different time range.

Sample Chart

FIGURE 5: ZORRO. In the Zorro platform, users can generate a spreadsheet for the analysis of metrics such as gaps, pullbacks, and relative closes of seven selected stocks.

The script can be downloaded from the 2024 script repository on https://financial-hacker.com. The Zorro platform for C/C++ algo trading can be downloaded from https://zorro-project.com.

—Petra Volkova
The Zorro Project by oP group Germany
https://zorro-project.com

BACK TO LIST

logo

AIQ: July 2024

The importable AIQ EDS file for Perry Kaufman’s article “Trading Opening Gaps And Extreme Closes In Stocks” can be obtained on request via rdencpa@gmail.com. The code is also shown here:

! Trading Openng Gaps And Extreme Closes In Stocks
! Author: Perry Kaufman, TASC July 2024
! Coded by: Richard Denning, 5/14/2024

gapSize is 7.
O is [open].
C is [close].
C1 is valresult(C,1).

gap is (O/C1-1)*100.

gapdn if gap < 0.

gapdn1 is iff(gap < 0 and gap > -1,gap,"").
gapdn3 is iff(gap <= -1 and gap > -3,gap,"").
gapdn5 is iff(gap <= -3 and gap > -5,gap,"").
gapdn7 is iff(gap <= -5 and gap > -7,gap,"").
gapdn9 is iff(gap <= -7 and gap > -9,gap,"").
gapdn11 is iff(gap <= -9 and gap > -11,gap,"").
gapdn11p is iff(gap <= -11 ,gap,"").

gapd1 if gap < 0 and gap > -1.
gapd3 if gap <= -1 and gap > -3.
gapd5 if gap <= -3 and gap > -5.
gapd7 if gap <= -5 and gap > -7.
gapd9 if gap <= -5 and gap > -9.
gapd11 if gap <= -7 and gap > -11.
gapd11p if gap <= -11.

HD is hasdatafor(2000).
!countgapdn1 is iff(showValues,countof(gapd1,HD),"").
countgapdn1 is countof(gapd1,HD).
countgapdn3 is countof(gapd3,HD).
countgapdn5 is countof(gapd5,HD).
countgapdn7 is countof(gapd7,HD).
countgapdn9 is countof(gapd9,HD).
countgapdn11 is countof(gapd11,HD).
countgapdn11p is countof(gapd11p,HD).

rtrnToClose is (C/O - 1)*100.
showValues if C > 10 and HD = 2000.
RetOnGapDn if showValues and gap < -gapSize.

A portion of the author’s code is set up in the AIQ EDS code file (the part of the code for the gap downs). The rule “ShowValues” will create a report counting the number of gap downs for stocks on your list that have 2,000 days of data or more and are greater than $10 in price as of the end date of the listing. This report can be exported to a CSV file for further analysis. The rule “RetOnGapDn” can be used to create a backtest in EDS that buys at the open if there is a gap down greater than the “gapSize” input and sells at the close of the same bar to determine the return from open to close. Figures 6 and 7 show the backtesting setup in EDS for the “RetOnGapDn” rule.

Sample Chart

FIGURE 6: AIQ. This shows an AIQ setup screen for the pricing with entry and exit on the current bar for a backtest of down gaps.

Sample Chart

FIGURE 7: AIQ. This shows an AIQ setup screen for "exit on same bar as entry" for a backtest of down gaps.

—Richard Denning
rdencpa@gmail.com
for AIQ Systems

BACK TO LIST

logo

NinjaTrader: July 2024

A script based on the article “Trading Opening Gaps And Extreme Closes In Stocks” in the June 2024 issue is available for download at the following link for NinjaTrader 8:

Once the file is downloaded, you can import it into NinjaTrader 8 from within the control center by selecting Tools → Import → NinjaScript Add-On and then selecting the downloaded file for NinjaTrader 8.

You can review the source code in NinjaTrader 8 by selecting the menu New → NinjaScript Editor → Indicators folder from within the control center window and selecting the ExtremeGapsAndCloses file.

Sample Chart

FIGURE 8: NINJATRADER. Users can explore price behavior in stocks surrounding the open and close.

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

—NinjaTrader, LLC
www.ninjatrader.com

BACK TO LIST

logo

TradeStation: July 2024

In “Trading Opening Gaps And Extreme Closes In Stocks,” author Perry Kaufman explores the question, “What is a stock likely to do when large opening moves and large closing moves occur?” The EasyLanguage study provided here writes data as a CSV file in a formatted table for a single symbol and allows you to specify the output file through an input.

Indicator

// TASC JULY 2024
// 
// Extreme Moves Gaps and Closes
// Copyright 2023–2024, P J Kaufman. All rights reserved.
// Show frequency of gaps within a table of ranges: 
//   gap, pullback, relative close
// Show frequency closes within a table of ranges: 
//   close, next open, next close

inputs:
	iFileName( "C:\TradeStation\Extreme_Moves.csv" );

variables: 
    double gap( 0 ), 
    double gappb( 0 ), 
    double gapclose( 0 ), 
    double extremeclose( 0 ),
    bool found( false ), 
    int ix( 0 ), 
    int saveindex( 0 ), 
    double x( 0 ),
    string FormatString( "" ),
    string RangeRowHeaderText( "" ),
    string Txt( "" );
    
arrays: 
    double thresholds[7](0), 
    double freq[7](0), 
    double gapfrequp[7](0), 
    double gapfreqdown[7](0),
    double gapup[7](0), 
    double gapdown[7](0), 
    double gapuppb[7](0), 
    double gapdownpb[7](0),
    double gapupclose[7](0), 
    double gapdownclose[7](0), 
    double closefrequp[7](0),
    double closefreqdown[7](0),
    double closeup[7](0), 
    double closedown[7](0), 
    double nextopenup[7](0), 
    double nextopendown[7](0),
    double nextCloseup[7](0), 
    double nextClosedown[7](0);

method void WriteData( string pTitle )
variables:
	string OutTxt;
begin
	OutTxt = string.Format( pTitle + "," + FormatString,
	 freq[1], 
	 freq[2], 
	 freq[3], 
	 freq[4], 
	 freq[5], 
	 freq[6], 
	 freq[7]);

	FileAppend( iFileName, OutTxt );
end;

once 
begin
    x = -0.01;
    for ix = 1 to 7 
    begin
        x = x + 0.02;
        thresholds[ix] = x;
    end;
end;

// GAP LOGIC
gap = open/Close[1] - 1;

// opening gap higher
if gap > 0 then 
begin
    found = false;
    
    for ix = 1 to 7 
    begin
        if found = false then 
        begin
            if gap < thresholds[ix] then 
            begin
                found = true;
                gapfrequp[ix] = gapfrequp[ix] + 1;
                gapup[ix] = gapup[ix] + gap;
                saveindex = ix;
            end;
        end;
    end;
 
    if found = false then 
    begin
        gapfrequp[7] = gapfrequp[7] + 1;
        gapup[7] = gapup[7] + gap;
        saveindex = 7;
    end;
    
    // pullback from gap higher
    gappb = Low / Open - 1;
    gapuppb[saveindex] = gapuppb[saveindex] + gappb;
    
    // close relative to gap open
    gapclose = Close / Open - 1;
    gapupclose[saveindex] = gapupclose[saveindex] + gapclose;
end;

// opening gap lower
if gap < 0 then 
begin
    found = false;
    for ix = 1 to 7 
    begin
        if found = false then 
        begin
            if gap < 0 and AbsValue(gap) < thresholds[ix] then 
            begin
                found = true;
                gapfreqdown[ix] = gapfreqdown[ix] + 1;
                gapdown[ix] = gapdown[ix] + gap;
                saveindex = ix;
            end;
        end;
    end;

    // upward pullback from gap lower
    gappb = High / Open - 1;
    gapdownpb[saveindex] = gapdownpb[saveindex] + gappb;
    
    // Close relative to gap open
    gapclose = Close / Open - 1;
    gapdownclose[saveindex] = gapdownclose[saveindex] 
     + gapclose;
end;

// EXTREME CLOSE LOGIC
extremeclose = Close / Close[1] - 1;

// extreme Close higher
if extremeclose > 0 then 
begin
    found = false;
    
    for ix = 1 to 7 
    begin
        if found = false then 
        begin
            if extremeclose < thresholds[ix] then 
            begin
                found = true;
                closefrequp[ix] = closefrequp[ix] + 1;
                closeup[ix] = closeup[ix] + extremeclose;
                saveindex = ix;
            end;
        end;
    end;
 
    // next open and Close
    nextopenup[saveindex] = nextopenup[saveindex] 
     + Open / Close[1] - 1;
    nextcloseup[saveindex] = nextcloseup[saveindex] 
     + Close / Close[1] - 1;
end;

// extreme Close lower
if extremeclose < 0 then 
begin
    found = false;
    
    for ix = 1 to 7 
    begin
        if found = false then 
        begin
            if absvalue(extremeclose) < thresholds[ix] then
            begin
                found = true;
                closefreqdown[ix] = closefreqdown[ix] + 1;
                closedown[ix] = closedown[ix] + extremeclose;
                saveindex = ix;
            end;
        end;
    end;
    
    // next open and close
    nextopendown[saveindex] = nextopendown[saveindex] 
     + Open / Close[1] - 1;
    nextClosedown[saveindex] = nextclosedown[saveindex] 
     + Close / Close[1] - 1;
end;

once( LastBarOnChartEx ) 
begin
	FormatString = "{0:F2},{1:F2},{2:F2},{3:F2},";
	FormatString += "{4:F2},{5:F2},{6:F2}" + NewLine;
	
	RangeRowHeaderText = "Range>>,<1%, <3%, <5%, <7%,";
	RangeRowHeaderText += "<9%, <11%, >11%" + NewLine;

    // GAPS UP
    FileAppend( iFileName, RangeRowHeaderText );
    Txt = string.Format( "GAP UP," + FormatString,
     gapfrequp[1],
     gapfrequp[2],
     gapfrequp[3],
     gapfrequp[4],
     gapfrequp[5],
     gapfrequp[6],
     gapfrequp[7]);
	
	FileAppend( iFileName, Txt );
	
    for ix = 1 to 7 
    begin
        freq[ix] = 0;
        if gapfrequp[ix] <> 0 then 
            freq[ix] = 100 * gapuppb[ix] / gapfrequp[ix];
    end;
    
    WriteData( "GAP UP PULLBACK" );
    
    for ix = 1 to 7 
    begin
        freq[ix] = 0;
        if gapfrequp[ix] <> 0 then 
            freq[ix] = 100 * gapupclose[ix] / gapfrequp[ix];
    end;
	
	WriteData( "GAP UP CLOSE VS OPEN" );
    
    // GAPS DOWN

    FileAppend( iFileName, NewLine + RangeRowHeaderText );
    
    Txt = string.Format( "GAP DOWN," + FormatString,
	 gapfreqdown[1], 
	 gapfreqdown[2],
	 gapfreqdown[3],
	 gapfreqdown[4], 
	 gapfreqdown[5],
	 gapfreqdown[6],
	 gapfreqdown[7]);
     
	FileAppend( iFileName, Txt );
    
	for ix = 1 to 7 
    begin
		freq[ix] = 0;
		if gapfreqdown[ix] <> 0 then 
            freq[ix] = 100 * gapdownpb[ix] / gapfreqdown[ix];
    end;
	
	WriteData( "GAP DOWN PULLBACK" );	

    for ix = 1 to 7 
    begin
        freq[ix] = 0;
        if gapfreqdown[ix] <> 0 then 
            freq[ix] = 100 * gapdownclose[ix] / gapfreqdown[ix];
    end;
    
	WriteData( "GAP DOWN CLOSE VS OPEN" );	
    
    // CLOSE UP
   	FileAppend( iFileName, NewLine + RangeRowHeaderText );
	Txt = string.Format( "CLOSE UP," + FormatString,
	 closefrequp[1],
	 closefrequp[2],
	 closefrequp[3], 
	 closefrequp[4], 
	 closefrequp[5], 
	 closefrequp[6], 
	 closefrequp[7]);
	
	FileAppend( iFileName, Txt );
    
    for ix = 1 to 7 
    begin
        freq[ix] = 0;
        if closefrequp[ix] <> 0 then 
            freq[ix] = 100 * nextopenup[ix] / closefrequp[ix];
    end;
    
	WriteData( "NEXT OPEN" );	

    for ix = 1 to 7 
    begin
		freq[ix] = 0;
		if Closefrequp[ix] <> 0 then 
			freq[ix] = 100 * nextcloseup[ix] / closefrequp[ix];
	end;
	
	WriteData( "NEXT CLOSE" );	
    
    // CLOSE DOWN
	FileAppend( iFileName, NewLine + RangeRowHeaderText );
	Txt = string.Format( "CLOSE DOWN," + FormatString,
     closefreqdown[1], 
     closefreqdown[2], 
     closefreqdown[3],
     closefreqdown[4], 
     closefreqdown[5], 
     closefreqdown[6],
     closefreqdown[7]);
    
    FileAppend( iFileName, Txt );
    
    for ix = 1 to 7 
    begin
        freq[ix] = 0;
		if Closefreqdown[ix] <> 0 then 
            freq[ix] = 100 * nextopendown[ix] 
             / Closefreqdown[ix];
	end;
	
	WriteData( "NEXT OPEN" );	
    
	for ix = 1 to 7 
	begin
		freq[ix] = 0;
		if Closefreqdown[ix] <> 0 then 
			freq[ix] = 100 * nextClosedown[ix] 
			 / Closefreqdown[ix];
	end;

	WriteData( "NEXT CLOSE" );	
end;
Sample Chart

FIGURE 9: TRADESTATION. This demonstrates sample output from the indicator applied to a 15-year daily chart of Boeing (BA).

—John Robinson
TradeStation Securities, Inc.
www.TradeStation.com

BACK TO LIST

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