The Regime Council For ThinkOrSwim

justAnotherTrader

Active member
VIP
VIP Enthusiast
shared chart link: https://tos.mx/!J1JcPNpB MUST follow these instructions for loading shared links.
m71o4yf.png


Hope you're enjoying your Memorial Day weekend! I'm incredibly excited to share my latest creation - an indicator that I believe will become the cornerstone of every serious trader's toolkit.

Think of this as assembling a council of expert advisors, each specializing in a different aspect of market psychology. Together, they reveal not just what a stock is doing, but why it's moving and whether that movement has staying power.

The Problem with Most Indicators
Most technical indicators are just different ways of slicing the same apple. They're all looking at price and volume through slightly different lenses, often telling you the same story multiple times. It's like having five people describe the same car accident - you get repetition, not clarity.

But there are actually six distinct market forces that drive price action, and most traders only look at 2-3 of them:
  • Momentum (Where is energy building?)
  • Volatility (How much uncertainty exists?)
  • Money Flows (Where is smart money positioning?)
  • Volume (Who's participating?)
  • Strength (How sustainable is this move?)
  • Trend vs Chop (What regime are we in?)
The Z-Score Advantage
Here's where it gets interesting. Instead of trying to compare apples to oranges, I've normalized everything using Z-scores. This puts all our indicators on the same playing field - like translating different languages into a common tongue.

A Z-score tells us: "How unusual is this current reading compared to recent history?"
  • Z-Score of +2 = This is happening 2 standard deviations above normal (97.5th percentile)
  • Z-Score of -2 = This is 2 standard deviations below normal (2.5th percentile)
  • Z-Score near 0 = Nothing unusual happening

What Makes This Special
This indicator combines seven different market dimensions into one cohesive view:
  1. VWAP Distance Z - Price positioning relative to institutional levels
  2. Volume Z - Participation strength (adjusted for direction)
  3. OBV Z - Money flow momentum
  4. CMF Z - Chaikin Money Flow intensity
  5. RSI Z - Momentum oscillator extremes
  6. MACD Z - Trend momentum shifts
  7. Percentage Gain Z - Daily performance context
The magic happens when you stack these Z-scores - creating a visual representation of market confluence. When all forces align (high positive or negative histogram), you have conviction. When they're mixed, you have uncertainty.

Reading the Market's Mind
The stacked histogram shows you:
  • Height = Strength of conviction across all market forces
  • Direction = Bullish vs bearish confluence
  • Wave patterns = Sustainable trends vs unsustainable spikes
  • Regime labels = Plain English translation of what's happening
Instead of wondering "Is this a real breakout or a fake-out?", you can see whether volume, money flow, momentum, and positioning all agree. When they do, you have confidence. When they don't, you have caution.

Why This Could Be Your Last Indicator
Because it doesn't just tell you what - it tells you why, how strong, and how sustainable. It's like having a team of specialists all weighing in on the same question: "Should I pay attention to this move?"

When all your advisors agree, you act with conviction. When they disagree, you wait for clarity. Simple as that.

This may very well be the evolution from looking at individual indicators to understanding market ecosystem dynamics.


Code:
declare lower;

# === Inputs ===
input length = 14;           # For RSI/CMF lookback
input zLength = 20;          # For z-score normalization
input macdFastLen = 12;
input macdSlowLen = 26;
input macdSigLen = 9;

input emaLength = 9;             # EMA window for the moving average
input upperExtreme = 7.5;        # Threshold for highest bullish color
input upperModerate = 2.0;       # Threshold for moderate bullish color
input lowerExtreme = -7.5;       # Threshold for highest bearish color
input lowerModerate = -2.0;      # Threshold for moderate bearish color

# === Wave Smoothing Parameters ===
input TripleSmoothing = yes;     # Enable triple exponential smoothing
input SmoothingFactor = 3;       # Smoothing factor (lower = smoother)

# === VWAP Distance Z ===
def vwap = (high + low + close) / 3;
def vwapDist = close - vwap;
def vwapMean = Average(vwapDist, zLength);
def vwapStdev = StDev(vwapDist, zLength);
def vwapZ = if vwapStdev != 0 then (vwapDist - vwapMean) / vwapStdev else 0;

# === Relative Volume Z ===
def volMean = Average(volume, zLength);
def volStdev = StDev(volume, zLength);
def volumeZ = if volStdev != 0 then (volume - volMean) / volStdev else 0;
def adjVolumeZ = if close < close[1] then -volumeZ else volumeZ;

# === OBV Z ===
def obv = TotalSum(
    if close > close[1] then volume
    else if close < close[1] then -volume
    else 0
);
def obvChange = obv - obv[1];
def obvMean = Average(obvChange, zLength);
def obvStdev = StDev(obvChange, zLength);
def obvZ = if obvStdev != 0 then (obvChange - obvMean) / obvStdev else 0;

# === CMF Z ===
def mfMultiplier = if (high - low) == 0 then 0 else ((close - low) - (high - close)) / (high - low);
def mfVolume = mfMultiplier * volume;
def cmfRaw = if Sum(volume, length) == 0 then 0 else Sum(mfVolume, length) / Sum(volume, length);
def cmfMean = Average(cmfRaw, zLength);
def cmfStdev = StDev(cmfRaw, zLength);
def cmfZ = if cmfStdev != 0 then (cmfRaw - cmfMean) / cmfStdev else 0;

# === RSI Z ===
def rsi = RSI(length = length);
def rsiMean = Average(rsi, zLength);
def rsiStdev = StDev(rsi, zLength);
def rsiZ = if rsiStdev != 0 then (rsi - rsiMean) / rsiStdev else 0;

# === MACD Histogram Z ===
def macdValue = ExpAverage(close, macdFastLen) - ExpAverage(close, macdSlowLen);
def macdSignal = ExpAverage(macdValue, macdSigLen);
def macdHist = macdValue - macdSignal;
def macdMean = Average(macdHist, zLength);
def macdStdev = StDev(macdHist, zLength);
def macdZ = if macdStdev != 0 then (macdHist - macdMean) / macdStdev else 0;

# === Percentage Gain Z ===
def pctGain = (close - open) / open * 100;
def pctGainMean = Average(pctGain, zLength);
def pctGainStdev = StDev(pctGain, zLength);
def pctGainZ = if pctGainStdev != 0 then (pctGain - pctGainMean) / pctGainStdev else 0;

# === Triple Exponential Smoothing for Wave Effect ===
def vwapZSmooth = if TripleSmoothing then ExpAverage(ExpAverage(ExpAverage(vwapZ, SmoothingFactor), SmoothingFactor), SmoothingFactor) else vwapZ;
def adjVolumeZSmooth = if TripleSmoothing then ExpAverage(ExpAverage(ExpAverage(adjVolumeZ, SmoothingFactor), SmoothingFactor), SmoothingFactor) else adjVolumeZ;
def obvZSmooth = if TripleSmoothing then ExpAverage(ExpAverage(ExpAverage(obvZ, SmoothingFactor), SmoothingFactor), SmoothingFactor) else obvZ;
def cmfZSmooth = if TripleSmoothing then ExpAverage(ExpAverage(ExpAverage(cmfZ, SmoothingFactor), SmoothingFactor), SmoothingFactor) else cmfZ;
def rsiZSmooth = if TripleSmoothing then ExpAverage(ExpAverage(ExpAverage(rsiZ, SmoothingFactor), SmoothingFactor), SmoothingFactor) else rsiZ;
def macdZSmooth = if TripleSmoothing then ExpAverage(ExpAverage(ExpAverage(macdZ, SmoothingFactor), SmoothingFactor), SmoothingFactor) else macdZ;
def pctGainZSmooth = if TripleSmoothing then ExpAverage(ExpAverage(ExpAverage(pctGainZ, SmoothingFactor), SmoothingFactor), SmoothingFactor) else pctGainZ;

# === Wave-Like Stackable Histogram Bars ===
plot vwapStack = vwapZSmooth;
vwapStack.SetPaintingStrategy(PaintingStrategy.HISTOGRAM);
vwapStack.SetLineWeight(4);
vwapStack.AssignValueColor(Color.LIGHT_ORANGE);

plot volStack = vwapZSmooth + adjVolumeZSmooth;
volStack.SetPaintingStrategy(PaintingStrategy.HISTOGRAM);
volStack.SetLineWeight(4);
volStack.AssignValueColor(Color.MAGENTA);

plot obvStack = vwapZSmooth + adjVolumeZSmooth + obvZSmooth;
obvStack.SetPaintingStrategy(PaintingStrategy.HISTOGRAM);
obvStack.SetLineWeight(4);
obvStack.AssignValueColor(Color.YELLOW);

plot cmfStack = vwapZSmooth + adjVolumeZSmooth + obvZSmooth + cmfZSmooth;
cmfStack.SetPaintingStrategy(PaintingStrategy.HISTOGRAM);
cmfStack.SetLineWeight(4);
cmfStack.AssignValueColor(Color.LIGHT_GREEN);

plot rsiStack = vwapZSmooth + adjVolumeZSmooth + obvZSmooth + cmfZSmooth + rsiZSmooth;
rsiStack.SetPaintingStrategy(PaintingStrategy.HISTOGRAM);
rsiStack.SetLineWeight(4);
rsiStack.AssignValueColor(Color.CYAN);

plot macdStack = vwapZSmooth + adjVolumeZSmooth + obvZSmooth + cmfZSmooth + rsiZSmooth + macdZSmooth;
macdStack.SetPaintingStrategy(PaintingStrategy.HISTOGRAM);
macdStack.SetLineWeight(4);
macdStack.AssignValueColor(Color.VIOLET);

plot pctGainStack = vwapZSmooth + adjVolumeZSmooth + obvZSmooth + cmfZSmooth + rsiZSmooth + macdZSmooth + pctGainZSmooth;
pctGainStack.SetPaintingStrategy(PaintingStrategy.HISTOGRAM);
pctGainStack.SetLineWeight(4);
pctGainStack.AssignValueColor(Color.WHITE);

# === Threshold Lines ===
plot zeroLine = 0;
zeroLine.SetDefaultColor(Color.GRAY);
zeroLine.SetStyle(Curve.SHORT_DASH);

plot upperThresh = 1.5;
upperThresh.SetDefaultColor(Color.DARK_GRAY);
upperThresh.SetStyle(Curve.LONG_DASH);

plot lowerThresh = -1.5;
lowerThresh.SetDefaultColor(Color.DARK_GRAY);
lowerThresh.SetStyle(Curve.LONG_DASH);

# === Update sumZ calculation ===
def sumZ = vwapZSmooth + adjVolumeZSmooth + obvZSmooth + cmfZSmooth + rsiZSmooth + macdZSmooth + pctGainZSmooth;

# === EMA of sumZ ===
def sumZema = ExpAverage(sumZ, emaLength);

plot sumZemaPlot = sumZema;
sumZemaPlot.SetLineWeight(3);

sumZemaPlot.AssignValueColor(
    if sumZema >= upperExtreme then Color.ORANGE
    else if sumZema >= upperModerate then Color.GREEN
    else if sumZema <= lowerExtreme then Color.CYAN
    else if sumZema <= lowerModerate then Color.DARK_RED
    else Color.LIGHT_GRAY
);

# === Keep your existing labels but use smoothed values ===
AddLabel(yes, "VWAP_Z: " + Round(vwapZSmooth, 2), Color.LIGHT_ORANGE);
AddLabel(yes, "VOL_Z: " + Round(adjVolumeZSmooth, 2), Color.MAGENTA);
AddLabel(yes, "OBV_Z: " + Round(obvZSmooth, 2), Color.YELLOW);
AddLabel(yes, "CMF_Z: " + Round(cmfZSmooth, 2), Color.LIGHT_GREEN);
AddLabel(yes, "RSI_Z: " + Round(rsiZSmooth, 2), Color.CYAN);
AddLabel(yes, "MACD_Z: " + Round(macdZSmooth, 2), Color.VIOLET);
AddLabel(yes, "PCT_GAIN_Z: " + Round(pctGainZSmooth, 2), Color.WHITE);

# === Z-Score of the Z-Scores (using smoothed values) ===
def sumZmean = Average(sumZ, zLength);
def sumZstdev = StDev(sumZ, zLength);
def sumZZ = if sumZstdev != 0 then (sumZ - sumZmean) / sumZstdev else 0;

AddLabel(yes, "sumZZ: " + Round(sumZZ, 2),
    if sumZZ >= 2 then Color.ORANGE
    else if sumZZ <= -2 then Color.CYAN
    else Color.LIGHT_GRAY
);

# === Diagnostic Scenario Threshold Inputs ===
input cmfPos = 1.0;
input cmfNeg = -1.0;
input obvPos = 1.0;
input obvNeg = -1.0;
input volSpike = 2.0;
input volPos = 1.0;
input volNeg = -1.0;
input rsiPos = 2.0;
input rsiNeg = -2.0;
input macdPos = 1.5;
input macdNeg = -1.5;
input vwapStretch = 2.0;
input sumZZpos = 2.0;
input sumZZneg = -2.0;
input chop = 0.5;

# === Market Regime Classification ===
def regime =
    if sumZZ > sumZZpos and cmfZ > cmfPos and obvZ > obvPos and adjVolumeZ > volPos and pctGainZ > 1.0 then 1
    else if sumZZ < sumZZneg and cmfZ < cmfNeg and obvZ < obvNeg and pctGainZ < -1.0 then 2
    else if vwapZ > vwapStretch and sumZZ > sumZZpos and pctGainZ > 1.5 then 3
    else if vwapZ < -vwapStretch and sumZZ < sumZZneg and pctGainZ < -1.5 then 4
    else if adjVolumeZ > volSpike and cmfZ < cmfPos/2 and obvZ < obvPos/2 and pctGainZ > 0 then 5
    else if rsiZ > rsiPos and macdZ > macdPos and adjVolumeZ > volPos and pctGainZ > 1.0 then 6
    else if obvZ > obvPos*1.5 and cmfZ > cmfPos and adjVolumeZ < volPos/2 and pctGainZ < 0.5 then 7
    else if adjVolumeZ > volPos and obvZ < obvNeg and cmfZ < cmfNeg and pctGainZ > 0 then 8
    else if adjVolumeZ > volPos and obvZ > obvPos and cmfZ > cmfPos and pctGainZ < 0 then 9
    else if pctGainZ > 2.0 and sumZZ > sumZZpos and adjVolumeZ > volSpike then 10
    else if pctGainZ < -2.0 and sumZZ < sumZZneg and adjVolumeZ > volSpike then 11
    else 0;

# === Primary Market Regime Label ===
AddLabel(yes,
    if regime == 1 then "BREAKOUT CONFIRMED"
    else if regime == 2 then "BREAKDOWN CONFIRMED"
    else if regime == 3 then "PARABOLIC MOVE"
    else if regime == 4 then "CAPITULATION"
    else if regime == 5 then "RETAIL FOMO"
    else if regime == 6 then "MOMENTUM CLIMAX"
    else if regime == 7 then "STEALTH ACCUMULATION"
    else if regime == 8 then "BULL TRAP"
    else if regime == 9 then "BEAR TRAP"
    else if regime == 10 then "HELICOPTER MONEY"
    else if regime == 11 then "FIRE SALE"
    else if sumZZ > 1 then "UPTREND"
    else if sumZZ < -1 then "DOWNTREND"
    else "RANGE BOUND",
    if sumZZ > sumZZpos then Color.ORANGE
    else if sumZZ < sumZZneg then Color.CYAN
    else Color.LIGHT_GRAY
);

# === Action Guidance Label ===
AddLabel(yes,
    if regime == 1 then "All Systems Green"
    else if regime == 2 then "Distribution Active"
    else if regime == 3 then "Exit Zone"
    else if regime == 4 then "Reversal Watch"
    else if regime == 5 then "Fade Setup"
    else if regime == 6 then "Blowoff Risk"
    else if regime == 7 then "Smart Money Building"
    else if regime == 8 then "Price Up, Flow Down"
    else if regime == 9 then "Flow Up, Price Down"
    else if regime == 10 then "Massive Bull Move"
    else if regime == 11 then "Massive Bear Move"
    else if sumZZ > 1 then "Trend Following"
    else if sumZZ < -1 then "Trend Following"
    else "Wait for Setup",
    Color.LIGHT_GRAY
);

# === Trend Strength Label ===
AddLabel(yes,
    if AbsValue(sumZZ) > 3 then "EXTREME"
    else if AbsValue(sumZZ) > 2 then "STRONG"
    else if AbsValue(sumZZ) > 1 then "MODERATE"
    else if AbsValue(sumZZ) > 0.5 then "WEAK"
    else "NEUTRAL",
    if AbsValue(sumZZ) > 2 then Color.ORANGE
    else if AbsValue(sumZZ) > 1 then Color.YELLOW
    else Color.GRAY
);

# === Volume Participation Label ===
AddLabel(yes,
    if adjVolumeZ > volSpike then "HIGH VOL"
    else if adjVolumeZ > volPos then "ABOVE AVG VOL"
    else if adjVolumeZ < volNeg then "LOW VOL"
    else "NORMAL VOL",
    if adjVolumeZ > volSpike then Color.MAGENTA
    else if adjVolumeZ > volPos then Color.LIGHT_GRAY
    else if adjVolumeZ < volNeg then Color.DARK_GRAY
    else Color.GRAY
);

# Pseudo-code logic for ThinkScript (you'll need to implement using recursive variables)
def adaptiveMean = Average(sumZZ, zLength);
def adaptiveStdev = StDev(sumZZ, zLength);

def adaptiveUpper = adaptiveMean + adaptiveStdev;
def adaptiveLower = adaptiveMean - adaptiveStdev;

plot unusualExtreme = sumZZ > adaptiveUpper or sumZZ < adaptiveLower;
 
Last edited by a moderator:

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

Everything You Wanted To Know About This Indicator
Another excellent contribution from @justAnotherTrader.

Be aware:
This indicator is too complex for use in scans, watchlists, and conditional orders
While no script is "too complex" to plot on the chart; Schwab purposefully throttles the use of complex scripts in scans, watchlists, and conditional orders to prevent high load runs on the servers.

Many of the more complex scripts found on this forum, can only be used on charts.
They cannot be used in other widgets; such as: scans, watchlists, conditional orders, etc...

Schwab does not currently provide any workarounds.

The Regime Council Does Not Repaint: while the current bar will update until is closes; the previous bars do not repaint
 

Similar threads

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

87k+ Posts
470 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