Files

102 lines
4.0 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("TD Sequential (TD_SEQ)", "TD_SEQ", overlay=false)
//@function Calculates TD Sequential — Tom DeMark's exhaustion counting system
//@param comparePeriod Lookback for setup comparison (close vs close[comparePeriod])
//@returns [setupCount, countdownCount] signed setup (1-9) and countdown (1-13) values
//@description TD Sequential identifies trend exhaustion via two phases:
// Phase 1 — Setup (1 to 9):
// Buy Setup: consecutive bars where close < close[comparePeriod] (count negative)
// Sell Setup: consecutive bars where close > close[comparePeriod] (count positive)
// A completed setup reaches ±9. The count resets when the condition breaks.
// Phase 2 — Countdown (1 to 13):
// After a completed 9-count setup, countdown begins:
// Buy Countdown: close < low[2] (counts negative, toward 13)
// Sell Countdown: close > high[2] (counts positive, toward +13)
// Countdown is non-consecutive — only qualifying bars increment.
// Countdown resets if an opposite setup completes (±9 in other direction).
// A completed ±13 signals potential exhaustion reversal.
// Setup values: 9 to 1 (buy) or +1 to +9 (sell), 0 = no active setup bar
// Countdown values: 13 to 1 (buy) or +1 to +13 (sell), 0 = no active countdown
// The oscillator output combines both: setup during phase 1, countdown during phase 2.
// All state is O(1) via var scalars — no buffers needed.
td_seq(simple int comparePeriod) =>
if comparePeriod <= 0
runtime.error("Compare period must be greater than 0")
var int setupCount = 0
var int countdownCount = 0
var int countdownDir = 0
var bool setupComplete = false
float curClose = nz(close)
float prevClose = nz(close[comparePeriod])
// --- Phase 1: Setup counting ---
int newSetup = 0
if curClose < prevClose
newSetup := setupCount < 0 ? setupCount - 1 : -1
else if curClose > prevClose
newSetup := setupCount > 0 ? setupCount + 1 : 1
else
newSetup := 0
if newSetup > 9
newSetup := 9
if newSetup < -9
newSetup := -9
// --- Check for completed setup (reached ±9) ---
bool justCompleted = false
if math.abs(newSetup) == 9 and not setupComplete
setupComplete := true
justCompleted := true
countdownCount := 0
countdownDir := newSetup > 0 ? 1 : -1
if math.abs(newSetup) < math.abs(setupCount) or (newSetup > 0 and setupCount < 0) or (newSetup < 0 and setupCount > 0)
setupComplete := false
setupCount := newSetup
// --- Phase 2: Countdown (non-consecutive) ---
if countdownDir != 0
float low2 = nz(low[2])
float high2 = nz(high[2])
if countdownDir == -1 and curClose < low2
countdownCount -= 1
else if countdownDir == 1 and curClose > high2
countdownCount += 1
if math.abs(countdownCount) >= 13
countdownCount := countdownDir == 1 ? 13 : -13
countdownDir := 0
if (countdownDir == 1 and newSetup == -9) or (countdownDir == -1 and newSetup == 9)
countdownCount := 0
countdownDir := newSetup > 0 ? 1 : -1
// --- Output: show countdown if active, else setup ---
int result = countdownDir != 0 ? countdownCount : setupCount
[setupCount, countdownCount]
// ---------- Main loop ----------
// Inputs
i_comparePeriod = input.int(4, "Compare Period", minval=1)
// Calculation
[setup, countdown] = td_seq(i_comparePeriod)
// Plot
plot(setup, "Setup", color=color.yellow, linewidth=2, style=plot.style_histogram)
plot(countdown, "Countdown", color=color.aqua, linewidth=1)
hline(0, "Zero", color=color.gray, linestyle=hline.style_dotted)
hline(9, "Sell Setup Complete", color=color.red, linestyle=hline.style_dashed)
hline(-9, "Buy Setup Complete", color=color.green, linestyle=hline.style_dashed)
hline(13, "Sell Countdown", color=color.red, linestyle=hline.style_dotted)
hline(-13, "Buy Countdown", color=color.green, linestyle=hline.style_dotted)