AGAIG Overnight Trading Indicator For ThinkOrSwim

csricksdds

Trader Educator
VIP
AGAIG OVERNIGHT TRADING
LAST 30 Minutes Indicator Monday-Thursday
Final Hour Donchian Options Signal​

NOTE: This indicator only fires (shows) when the "stars align" and the stock (ETF) is suitable for an overnight trade using these parameters. The BUBBLE will state "{Trade Overnight"? The "Strike Premium" is the amount you should be looking to pay that is closest to the option strike that can (or should) be selected. On the ALAB I’m showing you, the option price is steep, and so it could be traded as a SPREAD to reduce cost and risk? It’s best to place your trade for expirations at least 5 days out but not necessary. On this chart I have added my Price Pointer, the Mobius Super Trend, and the Overnight Indicator. The upper labels and diagnostics can be toggled on/off (my picture has the Labels on and the diagnostics off). This indicator can be applied to all charts and for all time frames.
Chart Look:
SXVHyvh.png

Here's a rundown on using the indicator as it stands today:

Setup

Apply indicator to whatever intraday chart you're actively watching (1-min, 5-min, etc.) — the core Donchian/SMA/ATR math always pulls Daily-period data via period = AggregationPeriod.DAY, so the signal itself is daily-based regardless of your chart's timeframe. The intraday chart just gives you the 30 minutes timing gate.

Applying it to a Daily chart works too, but skips the 30 minute gate entirely (final30min = yes on daily+ aggregations) — useful for backtesting/eyeballing history, not for live entry timing.

What it's telling you

Green up arrow
= bullish pullback entry (call side): upper-band test N days back (testLookbackDays, default 3), two lower closes, close below the 10 SMA.

Red down arrow = bearish failed-breakout entry (put side): same upper-band test, plus an inverted hammer and a red close.

"Trade Overnight?" bubble (once per day, on the first Final 30 minute bar) — gives you the approximate strike premium target (close ± ATR(20)×0.5) to scan the next-week chain against.

Yellow square = Day 5 exit reminder, fires once in the exitTime window (~1:30 PM ET) for whatever contracts you're still holding into the 5th session.

Tuning knobs you already have

testLookbackDays — you're shortening this now; expect looser/more frequent testedUpper hits.

testTolerancePct — widen if you want "near touches" of the upper band to count, not just exact new highs.

testDefinition — WithinLastNDays (any test in the window) vs ExactDayOffset (test must be exactly N days back) — worth testing both once you've picked your new lookback value.

sessionStartTime/sessionEndTime — adjust if your "final 30 minute" isn't literally 3:30–4 PM ET.

Diagnostics, when you need them again

Flip showDiagnostics to yes any time you want to sanity-check how often testedUpper, twoLowerCloses, belowSMA, isInvHammer, redBar are firing individually vs. how often they combine into a full signal — the magenta label plus T/H bubbles do that. Flip back to no once you're done, since it's just scaffolding, not part of the live logic.

Known limitation to keep in mind

One open trade tracked at a time (recEntryDay gets overwritten if a new signal fires before the prior trade's Day 5 exit) — fine if you're trading one position at a time, but worth knowing if you ever run this across multiple symbols simultaneously and treat them as independent.

That's the whole loop: arrow fires in the Final 30 minute bar → check the strike premium bubble → hold through Day 5 → exit reminder fires

Indicator Link: http://tos.mx/!R6djxmjF
Chart Link: http://tos.mx/!be2a17u3

Code:
#
# FinalHour_DonchianOptionsSignal
#
# AGAIG Overnight Trading Indicator - Final 30 Minute Trading Overnight Signal
#
# Rules implemented:
#  - Entries only Mon-Thu, Final 30 minutes of session (default 3:30-4:00 PM ET)
#  - Uses Daily chart data (20-bar Donchian Channel, 10-bar SMA)
#  - Bullish (call) signal: pullback after upper-band test 3 days prior +
#       two consecutive lower closes + close below 10 SMA
#  - Bearish (put) signal: failed breakout -> upper-band test 3 days prior +
#       inverted hammer on signal day + red bar (close < open)
#  - Day 5 exit reminder for remaining runner contracts (~1:30 PM ET)
#  - ATR(20)*0.5 label to help select next-week option strike/price
#
# NOTE: Works on any chart (daily or intraday) since all core calcs pull
# Daily-period data explicitly. Apply to your active intraday chart to get
# the Final Hour timing gate; apply to a Daily chart to just see the signal
# day highlighted.
#

declare upper;

# ---------------- Inputs ----------------
input donchianLength      = 20;
input smaLength           = 10;
input atrLength           = 20;
input testLookbackDays    = 3;
input testDefinition      = {default WithinLastNDays, ExactDayOffset};
input testTolerancePct    = 0.0;     # e.g. 0.25 = allow high within 0.25% of band to count as a "test"
input sessionStartTime    = 1530;    # 3:00 PM ET - start of Final Hour
input sessionEndTime      = 1600;    # 4:00 PM ET - session close
input exitTime            = 1330;    # 1:30 PM ET - Day 5 runner exit time
input exitWindowMinutes   = 5;       # width of the Day-5 exit trigger window
input showLabels          = yes;
input showDiagnostics     = no;

# ---------------- Daily source data (works regardless of chart agg) ----------------
def dOpen  = open(period = AggregationPeriod.DAY);
def dHigh  = high(period = AggregationPeriod.DAY);
def dLow   = low(period = AggregationPeriod.DAY);
def dClose = close(period = AggregationPeriod.DAY);

# ---------------- Core studies ----------------
def upperBand = Highest(dHigh, donchianLength);
def lowerBand = Lowest(dLow, donchianLength);
def sma10     = Average(dClose, smaLength);
def tr        = TrueRange(dHigh, dClose, dLow);
def atr20     = WildersAverage(tr, atrLength);

# ---------------- "Test of the upper band" 3 days prior to signal/entry day ----------------
# readyForSignal guards against NaN on the earliest bars of your loaded
# history (before 20+ daily bars exist for Donchian/SMA/ATR to compute).
# Without this, a single NaN early in the chart poisons the recursive
# diagnostic counters below and prints N/A forever after.
def readyForSignal = !IsNaN(upperBand) and !IsNaN(sma10) and !IsNaN(atr20);

# isNewHigh[0] = 1 if today made a new N-bar high (with optional tolerance)
def bandTolerance0 = upperBand[0] * (testTolerancePct / 100);
def isNewHigh = if dHigh[0] >= upperBand[0] - bandTolerance0 then 1 else 0;

# WithinLastNDays: any new-high day in the last testLookbackDays bars (excl. today)
# ExactDayOffset:  new-high must have happened exactly testLookbackDays bars ago
def testedUpperWindow = Highest(isNewHigh[1], testLookbackDays) == 1;
def testedUpperExact  = isNewHigh[testLookbackDays] == 1;
def testedUpper = readyForSignal and
    (if testDefinition == testDefinition.WithinLastNDays then testedUpperWindow else testedUpperExact);

# ---------------- Bullish pullback signal (calls) ----------------
def twoLowerCloses = readyForSignal and dClose[1] < dClose[2] and dClose[0] < dClose[1];
def belowSMA       = readyForSignal and dClose[0] < sma10[0];
def bullishSignal  = testedUpper and twoLowerCloses and belowSMA;

# ---------------- Bearish failed-breakout signal (puts) ----------------
def rng            = dHigh[0] - dLow[0];
def bodySize       = AbsValue(dClose[0] - dOpen[0]);
def upperWick      = dHigh[0] - Max(dOpen[0], dClose[0]);
def lowerWick      = Min(dOpen[0], dClose[0]) - dLow[0];
def isInvHammer    = readyForSignal and rng > 0
                      and upperWick >= rng * (2.0 / 3.0)
                      and bodySize  <= rng * 0.25
                      and lowerWick <= rng * 0.15;
def redBar         = readyForSignal and dClose[0] < dOpen[0];
def bearishSignal  = testedUpper and isInvHammer and redBar;

# ---------------- Time / Day-of-week gate (Final Hour, Mon-Thu) ----------------
# Auto-detect chart timeframe so this works whether you're on a 1-min chart,
# an hourly chart, or the Daily chart itself.
def aggPeriod  = GetAggregationPeriod();
def isIntraday = aggPeriod < AggregationPeriod.DAY;

def dow            = GetDayOfWeek(GetYYYYMMDD());   # 0=Sun ... 6=Sat
def validDay       = dow >= 1 and dow <= 4;         # Mon-Thu

# On intraday charts, require the bar to fall inside the Final Hour window.
# On Daily (or higher) charts there's no intraday clock to check against, so
# the time gate is skipped and the daily signal alone drives the plot.
def finalHour = if isIntraday
                 then SecondsFromTime(sessionStartTime) >= 0 and SecondsTillTime(sessionEndTime) > 0
                 else yes;

def validEntryWindow = validDay and finalHour;

def bullishEntry = bullishSignal and validEntryWindow;
def bearishEntry = bearishSignal and validEntryWindow;

# First bar of the Final Hour window each day - used to fire the alert only
# once per day instead of on every intraday bar within the hour.
def firstBarInWindow = finalHour and !finalHour[1];
def alertGate         = !isIntraday or firstBarInWindow;

# ---------------- Entry plots ----------------
plot BullSignal = if bullishEntry then dLow[0] else Double.NaN;
BullSignal.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
BullSignal.SetDefaultColor(Color.GREEN);
BullSignal.SetLineWeight(3);

plot BearSignal = if bearishEntry then dHigh[0] else Double.NaN;
BearSignal.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN);
BearSignal.SetDefaultColor(Color.RED);
BearSignal.SetLineWeight(3);

Alert(bullishEntry and alertGate, "BULLISH Final Hour Entry (Call side)", Alert.BAR, Sound.Ring);
Alert(bearishEntry and alertGate, "BEARISH Final Hour Entry (Put side)", Alert.BAR, Sound.Ring);

# ---------------- Reference plots (optional visual context) ----------------
plot UpperBandPlot = upperBand;
UpperBandPlot.SetDefaultColor(Color.LIGHT_GRAY);
UpperBandPlot.SetStyle(Curve.SHORT_DASH);

plot LowerBandPlot = lowerBand;
LowerBandPlot.SetDefaultColor(Color.LIGHT_GRAY);
LowerBandPlot.SetStyle(Curve.SHORT_DASH);

plot SmaPlot = sma10;
SmaPlot.SetDefaultColor(Color.CYAN);

# ---------------- Day counter (drives the 5-session hold / exit) ----------------
def today       = GetYYYYMMDD();
def rawNewDay   = today != today[1];
def isNewDay    = !IsNaN(rawNewDay) and rawNewDay;   # NaN-safe: bar 0 has no today[1]
def dayCounter  = CompoundValue(1, dayCounter[1] + (if isNewDay then 1 else 0), 1);

# ---------------- Diagnostics: how often does each sub-condition fire? ----------------
# Counts are cumulative over your chart's loaded history (one count per
# calendar day, not per intraday bar). Every increment is explicitly checked
# for NaN before being added - without this, a single bad bar anywhere in
# history (e.g. a still-forming daily bar during pre/post-market with no
# trades yet) permanently corrupts the running total to N/A from that point
# forward, since NaN + anything = NaN in a recursive sum.
def rawIncTestedUpper = if isNewDay and testedUpper then 1 else 0;
def rawIncTwoLower    = if isNewDay and twoLowerCloses then 1 else 0;
def rawIncBelowSMA    = if isNewDay and belowSMA then 1 else 0;
def rawIncInvHammer   = if isNewDay and isInvHammer then 1 else 0;
def rawIncRedBar      = if isNewDay and redBar then 1 else 0;
def rawIncBullish     = if isNewDay and bullishSignal then 1 else 0;
def rawIncBearish     = if isNewDay and bearishSignal then 1 else 0;
def rawIncTotalDays   = if isNewDay then 1 else 0;

def incTestedUpper = if IsNaN(rawIncTestedUpper) then 0 else rawIncTestedUpper;
def incTwoLower    = if IsNaN(rawIncTwoLower)    then 0 else rawIncTwoLower;
def incBelowSMA    = if IsNaN(rawIncBelowSMA)    then 0 else rawIncBelowSMA;
def incInvHammer   = if IsNaN(rawIncInvHammer)   then 0 else rawIncInvHammer;
def incRedBar      = if IsNaN(rawIncRedBar)      then 0 else rawIncRedBar;
def incBullish     = if IsNaN(rawIncBullish)     then 0 else rawIncBullish;
def incBearish     = if IsNaN(rawIncBearish)     then 0 else rawIncBearish;
def incTotalDays   = if IsNaN(rawIncTotalDays)   then 0 else rawIncTotalDays;

def cntTestedUpper = CompoundValue(1, cntTestedUpper[1] + incTestedUpper, 0);
def cntTwoLower    = CompoundValue(1, cntTwoLower[1]    + incTwoLower, 0);
def cntBelowSMA    = CompoundValue(1, cntBelowSMA[1]    + incBelowSMA, 0);
def cntInvHammer   = CompoundValue(1, cntInvHammer[1]   + incInvHammer, 0);
def cntRedBar      = CompoundValue(1, cntRedBar[1]      + incRedBar, 0);
def cntBullish     = CompoundValue(1, cntBullish[1]     + incBullish, 0);
def cntBearish     = CompoundValue(1, cntBearish[1]     + incBearish, 0);
def cntTotalDays   = CompoundValue(1, cntTotalDays[1]   + incTotalDays, 1);

AddLabel(showDiagnostics, "Diag (" + cntTotalDays + " days loaded) - TestedUpper: " + cntTestedUpper +
    "  2LowerCloses: " + cntTwoLower + "  BelowSMA: " + cntBelowSMA +
    "  InvHammer: " + cntInvHammer + "  RedBar: " + cntRedBar +
    "  -> BullSignal: " + cntBullish + "  BearSignal: " + cntBearish, Color.MAGENTA);

# Last-fired dates for TestedUpper / InvHammer, plus per-day "T"/"H" chart
# bubbles so you can visually see WHERE each sub-condition fired historically
# (independent of whether a full signal ever completed).
def lastTestedDate = CompoundValue(1, if isNewDay and testedUpper then today else lastTestedDate[1], Double.NaN);
def lastHammerDate = CompoundValue(1, if isNewDay and isInvHammer then today else lastHammerDate[1], Double.NaN);

AddLabel(showDiagnostics, "Last TestedUpper date: " + AsText(lastTestedDate) +
    "   Last InvHammer date: " + AsText(lastHammerDate), Color.LIGHT_GRAY);

AddChartBubble(showDiagnostics and isNewDay and testedUpper, dHigh[0] * 1.01, "T", Color.CYAN, yes);
AddChartBubble(showDiagnostics and isNewDay and isInvHammer, dHigh[0] * 1.02, "H", Color.ORANGE, yes);

# Track the day counter value of the most recent entry (bullish or bearish).
# NOTE: only tracks ONE open trade at a time - a new entry before Day 5
# overwrites the prior trade's tracked entry day.
def recEntryDay = CompoundValue(1,
                       if bullishEntry or bearishEntry then dayCounter else recEntryDay[1],
                       Double.NaN);

def daysSinceEntry = dayCounter - recEntryDay;

def exitWindowSecs = exitWindowMinutes * 60;
def exitTimeHit = SecondsFromTime(exitTime) >= 0
                   and SecondsFromTime(exitTime) < exitWindowSecs;

def day5Exit = daysSinceEntry == 5 and exitTimeHit;

plot ExitSignal = if day5Exit then dClose[0] else Double.NaN;
ExitSignal.SetPaintingStrategy(PaintingStrategy.SQUARES);
ExitSignal.SetDefaultColor(Color.YELLOW);
ExitSignal.SetLineWeight(3);

Alert(day5Exit, "DAY 5 EXIT WINDOW - Close remaining runner contracts", Alert.BAR, Sound.Chimes);

# ---------------- Option selection helper: ATR(20) x 0.5 ----------------
def optionPriceTarget = atr20 * 0.5;

AddLabel(showLabels, "ATR(20) x 0.5 (target option price): " +
    AsText(optionPriceTarget, NumberFormat.TWO_DECIMAL_PLACES), Color.CYAN);

AddLabel(showLabels, "10 SMA: " + AsText(sma10, NumberFormat.TWO_DECIMAL_PLACES) +
    "   Upper Band: " + AsText(upperBand, NumberFormat.TWO_DECIMAL_PLACES) +
    "   Lower Band: " + AsText(lowerBand, NumberFormat.TWO_DECIMAL_PLACES), Color.GRAY);

AddLabel(showLabels and bullishEntry, "BULLISH ENTRY - Pullback after upper band test", Color.GREEN);
AddLabel(showLabels and bearishEntry, "BEARISH ENTRY - Failed breakout / inverted hammer", Color.RED);
AddLabel(showLabels and day5Exit, "DAY 5 EXIT - Close remaining contracts", Color.YELLOW);
AddLabel(showLabels and !validDay, "Outside trading days (Mon-Thu only)", Color.DARK_GRAY);
AddLabel(showLabels, if isIntraday then "Mode: Intraday (Final 30 minute gate active)" else "Mode: Daily/Higher (Final Hour gate skipped)", Color.GRAY);

# ---------------- Overnight trade / strike reminder bubbles ----------------
# NOTE: ATR(20)*0.5 was defined as your target OPTION PREMIUM, not a strike
# offset. Since a study can't query the live option chain, this bubble shows
# an approximate strike distance (close +/- ATR*0.5) as a starting point for
# scanning the next-week chain - confirm the actual strike/premium in the
# chain before entering.
def approxCallStrike = dClose[0] + atr20 * 0.5;
def approxPutStrike  = dClose[0] - atr20 * 0.5;

AddChartBubble(bullishEntry and alertGate, dLow[0],
    "Trade Overnight?\nStrike Premium ~ " + AsText(approxCallStrike, NumberFormat.TWO_DECIMAL_PLACES),
    Color.GREEN, no);

AddChartBubble(bearishEntry and alertGate, dHigh[0],
    "Trade Overnight?\nStrike Premium ~ " + AsText(approxPutStrike, NumberFormat.TWO_DECIMAL_PLACES),
    Color.RED, yes);
 
Last edited by a moderator:

Join useThinkScript to post your question to a community of 21,000+ developers and traders.

Similar threads

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

87k+ Posts
1098 Online
Create Post

Similar threads

Similar threads

The Market Trading Game Changer

Join 2,500+ subscribers inside the useThinkScript VIP Membership Club
  • Exclusive indicators
  • Proven strategies & setups
  • Private Discord community
  • ‘Buy The Dip’ signal alerts
  • Exclusive members-only content
  • Add-ons and resources
  • 1 full year of unlimited support

Frequently Asked Questions

What is useThinkScript?

useThinkScript is the #1 community of stock market investors using indicators and other tools to power their trading strategies. Traders of all skill levels use our forums to learn about scripting and indicators, help each other, and discover new ways to gain an edge in the markets.

How do I get started?

We get it. Our forum can be intimidating, if not overwhelming. With thousands of topics, tens of thousands of posts, our community has created an incredibly deep knowledge base for stock traders. No one can ever exhaust every resource provided on our site.

If you are new, or just looking for guidance, here are some helpful links to get you started.

What are the benefits of VIP Membership?
VIP members get exclusive access to these proven and tested premium indicators: Buy the Dip, Advanced Market Moves 2.0, Take Profit, and Volatility Trading Range. In addition, VIP members get access to over 50 VIP-only custom indicators, add-ons, and strategies, private VIP-only forums, private Discord channel to discuss trades and strategies in real-time, customer support, trade alerts, and much more. Learn all about VIP membership here.
How can I access the premium indicators?
To access the premium indicators, which are plug and play ready, sign up for VIP membership here.
Back
Top