Finding the Right Fishing Hole To Be A Better Stock Picker

justAnotherTrader

Well-known member
VIP
VIP Enthusiast
I want to share with you my style of trading. Its a sort of hybrid from many of the great traders, and it does pretty well for my account. At the micro level, I think in terms of expectations trading.

The term expectations investing was popularized in the book written by Michael Mauboussin. His theory takes a DCF and turns it on its head. Instead of trying to forecast future cashflows for a company and bringing them back to today by discounting them, he uses the current price of a stock and tries to figure out what growth assumptions are embedded into the price. This helps him to gauge the expectations of the market for the equity, and then he can decide if he agrees or disagrees.

In practice, there is one metric that anyone can get very quickly to gauge the expectations of the market on a stock, and that metric is called the FCF Yield. The higher the FCF yield, the lower the expectations the market has put on the stock. Let me give you two examples.

Example 1: ADBE
ADBE is currently trading at an 8% FCF Yield. Whats important is context, this is the highest that yield has been for the company since 2012. The market is pricing ADBE as a company that is dying, largely because of concerns that AI is destroying the moat of the company and coming after its business. I think there are some serious concerns, but I think the fear is overdone. A simple rerate to a 6% FCF Yield would imply a 40% increase in current price, and a 6% FCF yield is still relatively high for ADBE.

rGSv6yn.png


Example 2: PLTR
PLTR is currently trading at a .5% FCF Yield. Unlike ADBE, this is near the lowest yield since January of 2022. This implies the market has very high expectations for PLTR and any missteps would likely lead to a huge selloff, meaning PLTR is very vulnerable to any headwinds macro or individual

0zPJvaT.png


Putting this together:
What I have found is that certain market environments support different ranges of FCF yields better. When the market is overall euphoric, it is likely to be very optimistic about the future and will easily support FCF Yields in the sub 1.5-2% range. In markets where the traders and investors require a little more proof like today, then markets are more likely to support 2-5% ranges better. If the market was very skeptical, then it would likely desire higher yields above 5%.

Where are we today?
Today we have pockets of euphoria mixed in with pockets of higher proof required mixed in with pockets where higher yields perform better. It is quite an interesting market actually.

Stocks that are relying on AI to profit in the future, the market is requiring more proof from, like MSFT.
Stocks that are building out the infrastructure required for Data centers, updating the power grid etc, are likely to find euphoric markets and support lower FCF Yields. Stocks like PWR, MTZ, CAT etc.
Stocks that are selling off because the market thinks AI is going to drive them out of business are likely going to require higher yields such as 5% plus like GTLB and ADBE

How to trade this?
This is the most important right? First of all, your style matters and it might not match mine. I am a swing trader. I like stocks that are technically above 200SMA no matter what UNLESS I am taking a contrarian view like buying undervalued. But then I think where do I want to be positioned? Personally, I like to be in the euphoric parts of the market as much as possible because thats where the outsized returns usually can be found in my experience. So I am looking at Precious metals, Infrastructure etc. That said, I am watching the market and if it seems like its not supporting those FCF yields I will likely rotate to a new FCF yield range, and choose the stocks that are best within it.

Whats the point?
The point is that trading actually should be easy. People think its hard but when its hard, assuming you have a good foundation, it means your fishing in the wrong fishing hole. When your euphoria is high and every trade is a winner it seems it means you found the right location. If you get chopped out, it means you need to move your location, not keep trying in the same place until it works again.

This is why its so important to have a map of the terrain, so when things get hard, you know where to try next.

Hope this is a value add for someone, if so let me know and I will make another post of how I actually choose my universe of stocks
 
Last edited:

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

I want to share with you my style of trading. Its a sort of hybrid from many of the great traders, and it does pretty well for my account. At the micro level, I think in terms of expectations trading.

The term expectations investing was popularized in the book written by Michael Mauboussin. His theory takes a DCF and turns it on its head. Instead of trying to forecast future cashflows for a company and bringing them back to today by discounting them, he uses the current price of a stock and tries to figure out what growth assumptions are embedded into the price. This helps him to gauge the expectations of the market for the equity, and then he can decide if he agrees or disagrees.

In practice, there is one metric that anyone can get very quickly to gauge the expectations of the market on a stock, and that metric is called the FCF Yield. The higher the FCF yield, the lower the expectations the market has put on the stock. Let me give you two examples.

Example 1: ADBE
ADBE is currently trading at an 8% FCF Yield. Whats important is context, this is the highest that yield has been for the company since 2012. The market is pricing ADBE as a company that is dying, largely because of concerns that AI is destroying the moat of the company and coming after its business. I think there are some serious concerns, but I think the fear is overdone. A simple rerate to a 6% FCF Yield would imply a 40% increase in current price, and a 6% FCF yield is still relatively high for ADBE.

rGSv6yn.png


Example 2: PLTR
PLTR is currently trading at a .5% FCF Yield. Unlike ADBE, this is near the lowest yield since January of 2022. This implies the market has very high expectations for PLTR and any missteps would likely lead to a huge selloff, meaning PLTR is very vulnerable to any headwinds macro or individual

0zPJvaT.png


Putting this together:
What I have found is that certain market environments support different ranges of FCF yields better. When the market is overall euphoric, it is likely to be very optimistic about the future and will easily support FCF Yields in the sub 1.5-2% range. In markets where the traders and investors require a little more proof like today, then markets are more likely to support 2-5% ranges better. If the market was very skeptical, then it would likely desire higher yields above 5%.

Where are we today?
Today we have pockets of euphoria mixed in with pockets of higher proof required mixed in with pockets where higher yields perform better. It is quite an interesting market actually.

Stocks that are relying on AI to profit in the future, the market is requiring more proof from, like MSFT.
Stocks that are building out the infrastructure required for Data centers, updating the power grid etc, are likely to find euphoric markets and support lower FCF Yields. Stocks like PWR, MTZ, CAT etc.
Stocks that are selling off because the market thinks AI is going to drive them out of business are likely going to require higher yields such as 5% plus like GTLB and ADBE

How to trade this?
This is the most important right? First of all, your style matters and it might not match mine. I am a swing trader. I like stocks that are technically above 200SMA no matter what UNLESS I am taking a contrarian view like buying undervalued. But then I think where do I want to be positioned? Personally, I like to be in the euphoric parts of the market as much as possible because thats where the outsized returns usually can be found in my experience. So I am looking at Precious metals, Infrastructure etc. That said, I am watching the market and if it seems like its not supporting those FCF yields I will likely rotate to a new FCF yield range, and choose the stocks that are best within it.

Whats the point?
The point is that trading actually should be easy. People think its hard but when its hard, assuming you have a good foundation, it means your fishing in the wrong fishing hole. When your euphoria is high and every trade is a winner it seems it means you found the right location. If you get chopped out, it means you need to move your location, not keep trying in the same place until it works again.

This is why its so important to have a map of the terrain, so when things get hard, you know where to try next.

Hope this is a value add for someone, if so let me know and I will make another post of how I actually choose my universe of stocks
While you can't calculated FCF in Thinkscript studies, you can use a proxy such as EarningsPerShare divided by the share price.

DIsplay on Left Axis:

Code:
plot FCF = EarningsPerShareTTM()/close;
AddLabel(yes, "FCF: " + FCF*100 + "%", Color.GREEN);
 
While you can't calculated FCF in Thinkscript studies, you can use a proxy such as EarningsPerShare divided by the share price.

DIsplay on Left Axis:

Code:
plot FCF = EarningsPerShareTTM()/close;
AddLabel(yes, "FCF: " + FCF*100 + "%", Color.GREEN);
This caught my attention, but the issue is that EarningsPerShareTTM() doesn’t support FiscalPeriod.QUARTER. Only YEAR
 
I want to share with you my style of trading. Its a sort of hybrid from many of the great traders, and it does pretty well for my account. At the micro level, I think in terms of expectations trading.

The term expectations investing was popularized in the book written by Michael Mauboussin. His theory takes a DCF and turns it on its head. Instead of trying to forecast future cashflows for a company and bringing them back to today by discounting them, he uses the current price of a stock and tries to figure out what growth assumptions are embedded into the price. This helps him to gauge the expectations of the market for the equity, and then he can decide if he agrees or disagrees.

In practice, there is one metric that anyone can get very quickly to gauge the expectations of the market on a stock, and that metric is called the FCF Yield. The higher the FCF yield, the lower the expectations the market has put on the stock. Let me give you two examples.

Example 1: ADBE
ADBE is currently trading at an 8% FCF Yield. Whats important is context, this is the highest that yield has been for the company since 2012. The market is pricing ADBE as a company that is dying, largely because of concerns that AI is destroying the moat of the company and coming after its business. I think there are some serious concerns, but I think the fear is overdone. A simple rerate to a 6% FCF Yield would imply a 40% increase in current price, and a 6% FCF yield is still relatively high for ADBE.

rGSv6yn.png


Example 2: PLTR
PLTR is currently trading at a .5% FCF Yield. Unlike ADBE, this is near the lowest yield since January of 2022. This implies the market has very high expectations for PLTR and any missteps would likely lead to a huge selloff, meaning PLTR is very vulnerable to any headwinds macro or individual

0zPJvaT.png


Putting this together:
What I have found is that certain market environments support different ranges of FCF yields better. When the market is overall euphoric, it is likely to be very optimistic about the future and will easily support FCF Yields in the sub 1.5-2% range. In markets where the traders and investors require a little more proof like today, then markets are more likely to support 2-5% ranges better. If the market was very skeptical, then it would likely desire higher yields above 5%.

Where are we today?
Today we have pockets of euphoria mixed in with pockets of higher proof required mixed in with pockets where higher yields perform better. It is quite an interesting market actually.

Stocks that are relying on AI to profit in the future, the market is requiring more proof from, like MSFT.
Stocks that are building out the infrastructure required for Data centers, updating the power grid etc, are likely to find euphoric markets and support lower FCF Yields. Stocks like PWR, MTZ, CAT etc.
Stocks that are selling off because the market thinks AI is going to drive them out of business are likely going to require higher yields such as 5% plus like GTLB and ADBE

How to trade this?
This is the most important right? First of all, your style matters and it might not match mine. I am a swing trader. I like stocks that are technically above 200SMA no matter what UNLESS I am taking a contrarian view like buying undervalued. But then I think where do I want to be positioned? Personally, I like to be in the euphoric parts of the market as much as possible because thats where the outsized returns usually can be found in my experience. So I am looking at Precious metals, Infrastructure etc. That said, I am watching the market and if it seems like its not supporting those FCF yields I will likely rotate to a new FCF yield range, and choose the stocks that are best within it.

Whats the point?
The point is that trading actually should be easy. People think its hard but when its hard, assuming you have a good foundation, it means your fishing in the wrong fishing hole. When your euphoria is high and every trade is a winner it seems it means you found the right location. If you get chopped out, it means you need to move your location, not keep trying in the same place until it works again.

This is why its so important to have a map of the terrain, so when things get hard, you know where to try next.

Hope this is a value add for someone, if so let me know and I will make another post of how I actually choose my universe of stocks
What do you think? Only annual (yearly) data is available. This is really Merry’s kingdom and I’m just reading your comment and trying to bridge two worlds here.

1769213866810.png


Python:
# ########################################
# Ehlers Trend Strength - FCF Yield
# Uses Dominant Cycle Period -> Adaptive MA Stack Separation
# Created by @rewadiaz
# v1.0 - 01/07/2026

script nz {
    input data  = close;
    input repl  = 0;
    def ret_val = if !isNaN(data) then data else repl;
    plot return = ret_val;
}

# Dynamic Simple Moving Average (variable length)
script AvgDyn {
    input data   = close;
    input len    = 20;     # may be dynamic
    input maxLen = 200;    # MUST be constant

    def bn = BarNumber();
    def n0 = Floor(len);
    def n1 = Min(Max(n0, 1), maxLen);
    def n  = Min(n1, bn);

    def sum =
        fold i = 0 to maxLen
        with s = 0.0
        do if i < n then s + GetValue(data, i) else s;

    plot out = sum / n;
}

script EhlersCyclePeriodDP {
    input src          = close;
    input CP_Alpha     = 0.07;
    input CP_MinDelta  = 0.10;
    input CP_MaxDelta  = 0.90;
    input CP_MinPeriod = 6;
    input CP_MaxPeriod = 50;
    input AdjPeriod    = 15;

    def bn    = BarNumber();
    def TwoPi = 6.28318;

    def s = src;

    def cpSmooth =
        if bn >= 4 then (s + 2 * s[1] + 2 * s[2] + s[3]) / 6.0
        else s;

    def cpInit =
        if bn >= 3 then (s - 2 * s[1] + s[2]) / 4.0
        else 0.0;

    def cpA1 = 1 - 0.5 * CP_Alpha;
    def cpA2 = 1 - CP_Alpha;

    def cpCycle = CompoundValue(1,
        if bn <= 6 then cpInit
        else (cpA1 * cpA1) * (cpSmooth - 2 * cpSmooth[1] + cpSmooth[2])
           + 2 * cpA2 * cpCycle[1]
           - (cpA2 * cpA2) * cpCycle[2],
        0
    );

    def I1 = if bn > 6 then cpCycle[3] else 0;

    # --- Hilbert FIR kernel (same math reused twice)
    def qBase =
        (0.0962 * cpCycle
       + 0.5769 * cpCycle[2]
       - 0.5769 * cpCycle[4]
       - 0.0962 * cpCycle[6]);

    def adj1 = 0.5 + 0.08 * AdjPeriod;

    def Q1_1 = if bn > 6 then qBase * adj1 else 0;

    def dpRaw1 =
        if Q1_1 != 0 and Q1_1[1] != 0 then
            (I1 / Q1_1 - I1[1] / Q1_1[1]) /
            (1 + (I1 * I1[1]) / (Q1_1 * Q1_1[1]))
        else 0;

    def dp1  = Max(CP_MinDelta, Min(CP_MaxDelta, dpRaw1));
    def med1 = if bn < 10 then dp1 else Median(dp1, 5);
    def DC1  = if med1 == 0 then 15 else TwoPi / med1 + 0.5;

    def Inst1 = CompoundValue(1, 0.33 * DC1 + 0.67 * Inst1[1], 15);
    def Per1  = CompoundValue(1, 0.15 * Inst1 + 0.85 * Per1[1], 15);

    def adj2 = 0.5 + 0.08 * Inst1[1];
    def Q1_2 = if bn > 6 then qBase * adj2 else 0;

    def dpRaw2 =
        if Q1_2 != 0 and Q1_2[1] != 0 then
            (I1 / Q1_2 - I1[1] / Q1_2[1]) /
            (1 + (I1 * I1[1]) / (Q1_2 * Q1_2[1]))
        else 0;

    def dp2  = Max(CP_MinDelta, Min(CP_MaxDelta, dpRaw2));
    def med2 = if bn < 10 then dp2 else Median(dp2, 5);
    def DC2  = if med2 == 0 then 15 else TwoPi / med2 + 0.5;

    def Inst2 = CompoundValue(1, 0.33 * DC2 + 0.67 * Inst2[1], 15);
    def Per2  = CompoundValue(1, 0.15 * Inst2 + 0.85 * Per2[1], 15);

    def CyclePeriodC = Min(Max(Per2, CP_MinPeriod), CP_MaxPeriod);

    plot CyclePeriod = CyclePeriodC;
}

script UltimateSmoother {
    input data   = close;
    input period = 20;

    def bn = BarNumber();

    def a1 = Exp(-1.414 * Double.Pi / period);
    def c2 = 2.0 * a1 * Cos(1.414 * Double.Pi / period);
    def c3 = -Sqr(a1);
    def c1 = (1.0 + c2 - c3) / 4.0;

    def us = CompoundValue(1,
        if bn >= 4 then
            (1.0 - c1) * data
          + (2.0 * c1 - c2) * data[1]
          - (c1 + c3) * data[2]
          + c2 * us[1]
          + c3 * us[2]
        else data,
        data
    );

    plot out = us;
}


# ########################################
# Charting & Formatting
declare upper;

# ########################################
# Inputs
input CP_Price      = Close;
input CP_Alpha      = 0.07;
input CP_MinDelta   = 0.10;
input CP_MaxDelta   = 0.90;
input CP_MinPeriod  = 5;
input CP_MaxPeriod  = 50;
input maxDynLen     = 200;
input paintBars     = no;
input showLines     = no;
input showLabels    = yes;
input eupMaxYield   = 0.02;  # <2% = euphoria
input proofMaxYield = 0.05; # 2-5% = proof required, >5% skeptical
input mode          = {default Any, Euphoria, Proof, Skeptical};
input targetYield1  = 0.06;
input targetYield2  = 0.05;

# ########################################
# Chart Coloring
DefineGlobalColor("green",       CreateColor(0, 165, 0));
DefineGlobalColor("red",         CreateColor(225, 0, 0));
DefineGlobalColor("orange",      Color.DARK_ORANGE);
DefineGlobalColor("blue",        CreateColor(50, 200, 255));
DefineGlobalColor("magenta",     CreateColor(200, 125, 255));
DefineGlobalColor("light_green", CreateColor(144, 238, 144));
DefineGlobalColor("light_red",   CreateColor(255, 102, 102));

# ########################################
# Globals
def na = Double.NaN;
def sc = close;

# ########################################
# CyclePeriod (DeltaPhase)
def HP = EhlersCyclePeriodDP(
    src          = CP_Price,
    CP_Alpha     = CP_Alpha,
    CP_MinDelta  = CP_MinDelta,
    CP_MaxDelta  = CP_MaxDelta,
    CP_MinPeriod = CP_MinPeriod,
    CP_MaxPeriod = CP_MaxPeriod,
    AdjPeriod    = 15
).CyclePeriod;

# Dynamic periods
def LP = Min(Max(Floor(nz(HP,CP_MinPeriod) /2), CP_MinPeriod), CP_MaxPeriod);

# ########################################
# Ehlers Trend Stack (adaptive)
def Lfast = LP;
def Lslow = Min(Max(2 * LP, CP_MinPeriod), maxDynLen);

def maFast = AvgDyn(CP_Price, Lfast, maxDynLen).out;
def maSlow = AvgDyn(CP_Price, Lslow, maxDynLen).out;

def trendSep = maFast - maSlow;

def trendUp   = trendSep > 0;
def trendDown = trendSep < 0;

# strength baseline (RMS of separation)
def ms  = CompoundValue(1, 0.0242 * Sqr(trendSep) + 0.9758 * ms[1], Sqr(trendSep));
def rms = Sqrt(ms);

def strongTrend = AbsValue(trendSep) > rms;
def weakTrend   = !strongTrend;

# ########################################
# Fundamentals (annual only)
def fcfPS_raw = FreeCashFlowPerShare(fiscalPeriod = FiscalPeriod.YEAR);

def fcfPS_hold =
    CompoundValue(1,
        if !IsNaN(fcfPS_raw) then fcfPS_raw else fcfPS_hold[1],
        fcfPS_raw
    );

# ########################################
# FCF Yield (FCF / Market Cap) == (FCF/Share) / Price
def fcfYield_raw = if !IsNaN(fcfPS_hold) and sc != 0 then fcfPS_hold / sc else na;

def fcfYield =
    CompoundValue(1,
        if !IsNaN(fcfYield_raw) then fcfYield_raw else fcfYield[1],
        fcfYield_raw
    );

# ########################################
# Terrain bucket
def isEuphoria  = !IsNaN(fcfYield) and fcfYield < eupMaxYield;
def isProof     = !IsNaN(fcfYield) and fcfYield >= eupMaxYield and fcfYield <= proofMaxYield;
def isSkeptical = !IsNaN(fcfYield) and fcfYield > proofMaxYield;

# ########################################
# Ehler trend filter
def UpTrending =  sc > maFast or trendUp;

# ########################################
# Mode fit
def modeFit =
    if mode == mode.Any then 1
    else if mode == mode.Euphoria then (if isEuphoria then 1 else 0)
    else if mode == mode.Proof then (if isProof then 1 else 0)
    else (if isSkeptical then 1 else 0);

def setupFit = modeFit == 1 and UpTrending;

# ########################################
# Rerate math (holding FCF/share constant)
def tgtPx1 = if !IsNaN(fcfPS_hold) and targetYield1 > 0 then fcfPS_hold / targetYield1 else na;
def tgtPx2 = if !IsNaN(fcfPS_hold) and targetYield2 > 0 then fcfPS_hold / targetYield2 else na;

def up1 = if !IsNaN(tgtPx1) and sc != 0 then tgtPx1 / sc - 1 else na;
def up2 = if !IsNaN(tgtPx2) and sc != 0 then tgtPx2 / sc - 1 else na;

# ########################################
# Labels
AddLabel(showLabels,
    " FCF/Share (FY): " + (if IsNaN(fcfPS_hold) then "n/a" else AsDollars(fcfPS_hold)),
    GlobalColor("orange")
);

AddLabel(showLabels,
    " FCF Yield (FCF/MC): " + (if IsNaN(fcfYield) then "n/a" else AsPercent(fcfYield)),
    if IsNaN(fcfYield) then GlobalColor("orange") else if fcfYield >= 0 then GlobalColor("green") else GlobalColor("red")
);

AddLabel(showLabels,     
    if IsNaN(fcfYield) then " FCF Bucket: n/a"
    else if isEuphoria then " FCF Bucket: EUPHORIA (<" + AsPercent(eupMaxYield) + ") "
    else if isProof then "FCF Bucket: PROOF (" + AsPercent(eupMaxYield) + "–" + AsPercent(proofMaxYield) + ") "
    else " FCF Bucket: SKEPTICAL (>" + AsPercent(proofMaxYield) + ") ",
    if IsNaN(fcfYield) then GlobalColor("orange")
    else if isEuphoria then GlobalColor("magenta")
    else if isProof then GlobalColor("blue")
    else GlobalColor("orange"));

AddLabel(showLabels,
    " CyclePeriod(LP~" + Round(maFast,0) + "): " + (if UpTrending then "ABOVE " else "BELOW "),
    if UpTrending and strongTrend then GlobalColor("blue")
    else if sc <= maFast and strongTrend and trendDown then GlobalColor("magenta")
    else if UpTrending then GlobalColor("green")
    else if sc <= maFast and trendDown then GlobalColor("red")
    else GlobalColor("orange")
);

AddLabel(showLabels,
    " Mode: " + mode + " | Fit: " + (if setupFit then "YES " else "NO "),
    if setupFit then GlobalColor("light_green") else GlobalColor("light_red")
);

AddLabel(showLabels,
    " Rerate " + AsPercent(targetYield1) + ": " + (if IsNaN(up1) then "n/a" else AsPercent(up1)),
    if IsNaN(up1) then GlobalColor("orange") else if up1 >= 0 then GlobalColor("green") else GlobalColor("red")
);

AddLabel(showLabels,
    " Rerate " + AsPercent(targetYield2) + ": " + (if IsNaN(up2) then "n/a" else AsPercent(up2)),
    if IsNaN(up2) then GlobalColor("orange") else if up2 >= 0 then GlobalColor("green") else GlobalColor("red")
);

# Diagnostic: why n/a?
AddLabel(showLabels and IsNaN(fcfPS_hold),
    " No FCF fundamentals on loaded bars (best on DAILY equities; not on futures/forex). ",
    GlobalColor("red")
);
 
What do you think? Only annual (yearly) data is available. This is really Merry’s kingdom and I’m just reading your comment and trying to bridge two worlds here.

View attachment 26853

Python:
# ########################################
# Ehlers Trend Strength - FCF Yield
# Uses Dominant Cycle Period -> Adaptive MA Stack Separation
# Created by @rewadiaz
# v1.0 - 01/07/2026

script nz {
    input data  = close;
    input repl  = 0;
    def ret_val = if !isNaN(data) then data else repl;
    plot return = ret_val;
}

# Dynamic Simple Moving Average (variable length)
script AvgDyn {
    input data   = close;
    input len    = 20;     # may be dynamic
    input maxLen = 200;    # MUST be constant

    def bn = BarNumber();
    def n0 = Floor(len);
    def n1 = Min(Max(n0, 1), maxLen);
    def n  = Min(n1, bn);

    def sum =
        fold i = 0 to maxLen
        with s = 0.0
        do if i < n then s + GetValue(data, i) else s;

    plot out = sum / n;
}

script EhlersCyclePeriodDP {
    input src          = close;
    input CP_Alpha     = 0.07;
    input CP_MinDelta  = 0.10;
    input CP_MaxDelta  = 0.90;
    input CP_MinPeriod = 6;
    input CP_MaxPeriod = 50;
    input AdjPeriod    = 15;

    def bn    = BarNumber();
    def TwoPi = 6.28318;

    def s = src;

    def cpSmooth =
        if bn >= 4 then (s + 2 * s[1] + 2 * s[2] + s[3]) / 6.0
        else s;

    def cpInit =
        if bn >= 3 then (s - 2 * s[1] + s[2]) / 4.0
        else 0.0;

    def cpA1 = 1 - 0.5 * CP_Alpha;
    def cpA2 = 1 - CP_Alpha;

    def cpCycle = CompoundValue(1,
        if bn <= 6 then cpInit
        else (cpA1 * cpA1) * (cpSmooth - 2 * cpSmooth[1] + cpSmooth[2])
           + 2 * cpA2 * cpCycle[1]
           - (cpA2 * cpA2) * cpCycle[2],
        0
    );

    def I1 = if bn > 6 then cpCycle[3] else 0;

    # --- Hilbert FIR kernel (same math reused twice)
    def qBase =
        (0.0962 * cpCycle
       + 0.5769 * cpCycle[2]
       - 0.5769 * cpCycle[4]
       - 0.0962 * cpCycle[6]);

    def adj1 = 0.5 + 0.08 * AdjPeriod;

    def Q1_1 = if bn > 6 then qBase * adj1 else 0;

    def dpRaw1 =
        if Q1_1 != 0 and Q1_1[1] != 0 then
            (I1 / Q1_1 - I1[1] / Q1_1[1]) /
            (1 + (I1 * I1[1]) / (Q1_1 * Q1_1[1]))
        else 0;

    def dp1  = Max(CP_MinDelta, Min(CP_MaxDelta, dpRaw1));
    def med1 = if bn < 10 then dp1 else Median(dp1, 5);
    def DC1  = if med1 == 0 then 15 else TwoPi / med1 + 0.5;

    def Inst1 = CompoundValue(1, 0.33 * DC1 + 0.67 * Inst1[1], 15);
    def Per1  = CompoundValue(1, 0.15 * Inst1 + 0.85 * Per1[1], 15);

    def adj2 = 0.5 + 0.08 * Inst1[1];
    def Q1_2 = if bn > 6 then qBase * adj2 else 0;

    def dpRaw2 =
        if Q1_2 != 0 and Q1_2[1] != 0 then
            (I1 / Q1_2 - I1[1] / Q1_2[1]) /
            (1 + (I1 * I1[1]) / (Q1_2 * Q1_2[1]))
        else 0;

    def dp2  = Max(CP_MinDelta, Min(CP_MaxDelta, dpRaw2));
    def med2 = if bn < 10 then dp2 else Median(dp2, 5);
    def DC2  = if med2 == 0 then 15 else TwoPi / med2 + 0.5;

    def Inst2 = CompoundValue(1, 0.33 * DC2 + 0.67 * Inst2[1], 15);
    def Per2  = CompoundValue(1, 0.15 * Inst2 + 0.85 * Per2[1], 15);

    def CyclePeriodC = Min(Max(Per2, CP_MinPeriod), CP_MaxPeriod);

    plot CyclePeriod = CyclePeriodC;
}

script UltimateSmoother {
    input data   = close;
    input period = 20;

    def bn = BarNumber();

    def a1 = Exp(-1.414 * Double.Pi / period);
    def c2 = 2.0 * a1 * Cos(1.414 * Double.Pi / period);
    def c3 = -Sqr(a1);
    def c1 = (1.0 + c2 - c3) / 4.0;

    def us = CompoundValue(1,
        if bn >= 4 then
            (1.0 - c1) * data
          + (2.0 * c1 - c2) * data[1]
          - (c1 + c3) * data[2]
          + c2 * us[1]
          + c3 * us[2]
        else data,
        data
    );

    plot out = us;
}


# ########################################
# Charting & Formatting
declare upper;

# ########################################
# Inputs
input CP_Price      = Close;
input CP_Alpha      = 0.07;
input CP_MinDelta   = 0.10;
input CP_MaxDelta   = 0.90;
input CP_MinPeriod  = 5;
input CP_MaxPeriod  = 50;
input maxDynLen     = 200;
input paintBars     = no;
input showLines     = no;
input showLabels    = yes;
input eupMaxYield   = 0.02;  # <2% = euphoria
input proofMaxYield = 0.05; # 2-5% = proof required, >5% skeptical
input mode          = {default Any, Euphoria, Proof, Skeptical};
input targetYield1  = 0.06;
input targetYield2  = 0.05;

# ########################################
# Chart Coloring
DefineGlobalColor("green",       CreateColor(0, 165, 0));
DefineGlobalColor("red",         CreateColor(225, 0, 0));
DefineGlobalColor("orange",      Color.DARK_ORANGE);
DefineGlobalColor("blue",        CreateColor(50, 200, 255));
DefineGlobalColor("magenta",     CreateColor(200, 125, 255));
DefineGlobalColor("light_green", CreateColor(144, 238, 144));
DefineGlobalColor("light_red",   CreateColor(255, 102, 102));

# ########################################
# Globals
def na = Double.NaN;
def sc = close;

# ########################################
# CyclePeriod (DeltaPhase)
def HP = EhlersCyclePeriodDP(
    src          = CP_Price,
    CP_Alpha     = CP_Alpha,
    CP_MinDelta  = CP_MinDelta,
    CP_MaxDelta  = CP_MaxDelta,
    CP_MinPeriod = CP_MinPeriod,
    CP_MaxPeriod = CP_MaxPeriod,
    AdjPeriod    = 15
).CyclePeriod;

# Dynamic periods
def LP = Min(Max(Floor(nz(HP,CP_MinPeriod) /2), CP_MinPeriod), CP_MaxPeriod);

# ########################################
# Ehlers Trend Stack (adaptive)
def Lfast = LP;
def Lslow = Min(Max(2 * LP, CP_MinPeriod), maxDynLen);

def maFast = AvgDyn(CP_Price, Lfast, maxDynLen).out;
def maSlow = AvgDyn(CP_Price, Lslow, maxDynLen).out;

def trendSep = maFast - maSlow;

def trendUp   = trendSep > 0;
def trendDown = trendSep < 0;

# strength baseline (RMS of separation)
def ms  = CompoundValue(1, 0.0242 * Sqr(trendSep) + 0.9758 * ms[1], Sqr(trendSep));
def rms = Sqrt(ms);

def strongTrend = AbsValue(trendSep) > rms;
def weakTrend   = !strongTrend;

# ########################################
# Fundamentals (annual only)
def fcfPS_raw = FreeCashFlowPerShare(fiscalPeriod = FiscalPeriod.YEAR);

def fcfPS_hold =
    CompoundValue(1,
        if !IsNaN(fcfPS_raw) then fcfPS_raw else fcfPS_hold[1],
        fcfPS_raw
    );

# ########################################
# FCF Yield (FCF / Market Cap) == (FCF/Share) / Price
def fcfYield_raw = if !IsNaN(fcfPS_hold) and sc != 0 then fcfPS_hold / sc else na;

def fcfYield =
    CompoundValue(1,
        if !IsNaN(fcfYield_raw) then fcfYield_raw else fcfYield[1],
        fcfYield_raw
    );

# ########################################
# Terrain bucket
def isEuphoria  = !IsNaN(fcfYield) and fcfYield < eupMaxYield;
def isProof     = !IsNaN(fcfYield) and fcfYield >= eupMaxYield and fcfYield <= proofMaxYield;
def isSkeptical = !IsNaN(fcfYield) and fcfYield > proofMaxYield;

# ########################################
# Ehler trend filter
def UpTrending =  sc > maFast or trendUp;

# ########################################
# Mode fit
def modeFit =
    if mode == mode.Any then 1
    else if mode == mode.Euphoria then (if isEuphoria then 1 else 0)
    else if mode == mode.Proof then (if isProof then 1 else 0)
    else (if isSkeptical then 1 else 0);

def setupFit = modeFit == 1 and UpTrending;

# ########################################
# Rerate math (holding FCF/share constant)
def tgtPx1 = if !IsNaN(fcfPS_hold) and targetYield1 > 0 then fcfPS_hold / targetYield1 else na;
def tgtPx2 = if !IsNaN(fcfPS_hold) and targetYield2 > 0 then fcfPS_hold / targetYield2 else na;

def up1 = if !IsNaN(tgtPx1) and sc != 0 then tgtPx1 / sc - 1 else na;
def up2 = if !IsNaN(tgtPx2) and sc != 0 then tgtPx2 / sc - 1 else na;

# ########################################
# Labels
AddLabel(showLabels,
    " FCF/Share (FY): " + (if IsNaN(fcfPS_hold) then "n/a" else AsDollars(fcfPS_hold)),
    GlobalColor("orange")
);

AddLabel(showLabels,
    " FCF Yield (FCF/MC): " + (if IsNaN(fcfYield) then "n/a" else AsPercent(fcfYield)),
    if IsNaN(fcfYield) then GlobalColor("orange") else if fcfYield >= 0 then GlobalColor("green") else GlobalColor("red")
);

AddLabel(showLabels,    
    if IsNaN(fcfYield) then " FCF Bucket: n/a"
    else if isEuphoria then " FCF Bucket: EUPHORIA (<" + AsPercent(eupMaxYield) + ") "
    else if isProof then "FCF Bucket: PROOF (" + AsPercent(eupMaxYield) + "–" + AsPercent(proofMaxYield) + ") "
    else " FCF Bucket: SKEPTICAL (>" + AsPercent(proofMaxYield) + ") ",
    if IsNaN(fcfYield) then GlobalColor("orange")
    else if isEuphoria then GlobalColor("magenta")
    else if isProof then GlobalColor("blue")
    else GlobalColor("orange"));

AddLabel(showLabels,
    " CyclePeriod(LP~" + Round(maFast,0) + "): " + (if UpTrending then "ABOVE " else "BELOW "),
    if UpTrending and strongTrend then GlobalColor("blue")
    else if sc <= maFast and strongTrend and trendDown then GlobalColor("magenta")
    else if UpTrending then GlobalColor("green")
    else if sc <= maFast and trendDown then GlobalColor("red")
    else GlobalColor("orange")
);

AddLabel(showLabels,
    " Mode: " + mode + " | Fit: " + (if setupFit then "YES " else "NO "),
    if setupFit then GlobalColor("light_green") else GlobalColor("light_red")
);

AddLabel(showLabels,
    " Rerate " + AsPercent(targetYield1) + ": " + (if IsNaN(up1) then "n/a" else AsPercent(up1)),
    if IsNaN(up1) then GlobalColor("orange") else if up1 >= 0 then GlobalColor("green") else GlobalColor("red")
);

AddLabel(showLabels,
    " Rerate " + AsPercent(targetYield2) + ": " + (if IsNaN(up2) then "n/a" else AsPercent(up2)),
    if IsNaN(up2) then GlobalColor("orange") else if up2 >= 0 then GlobalColor("green") else GlobalColor("red")
);

# Diagnostic: why n/a?
AddLabel(showLabels and IsNaN(fcfPS_hold),
    " No FCF fundamentals on loaded bars (best on DAILY equities; not on futures/forex). ",
    GlobalColor("red")
);
I have to test it, but before I did I wanted to quickly say thanks for putting so much time in this. This seems like a really good proxy for FCF yield, something I havent tried to solve on thinkorswim before. Thanks!
 
I say retracement to midline channel/13ema than down she goes again for a double bottom maybe around $280 A trade but not a investment for me.View attachment 26856
ADBE is technically in a bearish trend, and I agree that $280 would be long term support back from the covid sell off. That puts this as a classic falling knife play if you tried to buy it.

But I would argue as someone who tries to marry the fundamentals and technicals that this is where the FCF Yield shines and possible gives a window into why the knife now has a catalyst to stop falling. The high FCF yield is a result of both a broader market SaaS sell off as well as an ADBE specific sell off due to (in my estimation overdone) fears that AI is going to bankrupt a lot of these SaaS companies. This sell off is starting to stall in some areas, note GTLB as my exhibit.

Even technically by your 5ma oscillator (which I really like btw), the stock is in extreme oversold areas. If the pattern holds according to your channel then by the time we start the next leg down we will be coming into earnings.

In my experience, which is limited by my narrow viewpoints admittedly, the strongest reversals happen when all the stars are aligned in one direction. I am a trend follower myself, and I cant stand to predict turns because I am no good at it, good chance I am wrong here. But if one was to take a risk, the reward is a 50% upside with capped downside using a $30 trail stop (based off 3xATR and also making sure we dont violate the $280 key support level you mentioned).

That gives a risk reward of 100/30 ~ 3:1

Full disclosure I have opened up a position on ADBE with an average price of ~296. I think a trader votes with his money, so we will see
 
Last edited:
ADBE is technically in a bearish trend, and I agree that $280 would be long term support back from the covid sell off. That puts this as a classic falling knife play if you tried to buy it.

But I would argue as someone who tries to marry the fundamentals and technicals that this is where the FCF Yield shines and possible gives a window into why the knife now has a catalyst to stop falling. The high FCF yield is a result of both a broader market SaaS sell off as well as an ADBE specific sell off due to (in my estimation overdone) fears that AI is going to bankrupt a lot of these SaaS companies. This sell off is starting to stall in some areas, note GTLB as my exhibit.

Even technically by your 5ma oscillator (which I really like btw), the stock is in extreme oversold areas. If the pattern holds according to your channel then by the time we start the next leg down we will be coming into earnings.

In my experience, which is limited by my narrow viewpoints admittedly, the strongest reversals happen when all the stars are aligned in one direction. I am a trend follower myself, and I cant stand to predict turns because I am no good at it, good chance I am wrong here. But if one was to take a risk, the reward is a 50% upside with capped downside using a $30 trail stop (based off 3xATR and also making sure we dont violate the $280 key support level you mentioned).

That gives a risk reward of 100/30 ~ 3:1

Full disclosure I have opened up a position on ADBE with an average price of ~296. I think a trader votes with his money, so we will see
In the picture above, I’m showing two zones: green below and red above. That’s my supply and demand. It’s a simple confirmation method that tends to work and helps avoid large losses.

If price drops to 296.53, the 24-hour candle close above 296.53 is an entry for me (the wick doesn’t matter), because on the weekly chart it’s building support and I wait for the retest to enter. If the candle closes inside the green box, then there’s no entry, I’ll wait for another signal or a catalyst.

Now, what’s the difference if you’re trading in-and-out? Yes, everything has to line up (even Jupiter has to be in the picture 😅). I know this because I’m a scalper. But what I’m saying is that ADBE has pending liquidity to resolve, and trapped buyers will participate in buying to close positions. So, in the near future, ADBE should reach the red block. When? Only they know.
I believe in playing alongside market makers, not trading the news. News brings uncertainty, and you end up playing their 50/50 game so they can survive. That’s just my view of how the market works. If this doesn’t work for you, that’s totally understandable.

Good day! 🙏
 
Last edited:
In the picture above, I’m showing two zones: green below and red above. That’s my supply and demand. It’s a simple confirmation method that tends to work and helps avoid large losses.

If price drops to 296.53, the 12-hour candle close above 296.53 is an entry for me (the wick doesn’t matter), because on the weekly chart it’s building support and I wait for the retest to enter. If the candle closes inside the green box, then there’s no entry, I’ll wait for another signal or a catalyst.

Now, what’s the difference if you’re trading in-and-out? Yes, everything has to line up (even Jupiter has to be in the picture 😅). I know this because I’m a scalper. But what I’m saying is that ADBE has pending liquidity to resolve, and trapped buyers will participate in buying to close positions. So, in the near future, ADBE should reach the red block. When? Only they know.
I believe in playing alongside market makers, not trading the news. News brings uncertainty, and you end up playing their 50/50 game so they can survive. That’s just my view of how the market works. If this doesn’t work for you, that’s totally understandable.

Good day! 🙏

The break in the positive divergence on the histograms was the first sign that sentiment changed. Price fell out of the base in mid-November, recovered, then hit its head at the 3D 55 EMA / upper trend channel and retreated.


I don’t know the future and no indicator can but I do know that around $280 buyers should be waiting. That level would likely line up as a double-bottom area, and the volume profile suggests there’s a fib confluence there too. Will it make it to $280? No idea. But if it does, I expect buyers will show up and I may pick up a few shares myself just for a trade.


That said, Adobe still has headwinds, and I think they’ll need to diversify to work their way out of the mess they’re in.

Speaking of stocks that have been beat up, CPB a dividend stock I have a target which is rather wide but between $21 to $23 which is a rough measured H&S move and the 127.2 fib with little volume not sure what to think about that issue. I think it will the next resting place for CPB. If it makes it there, that will be one I'll let ride for a few years if conditions are favorable.

By the way CPB stopped on a triple bottom for a while than jumped off a cliff. They thought it was a triple bottom, which it was, but it was also a Head & Shoulders :LOL:
 
Last edited:
What do you think? Only annual (yearly) data is available. This is really Merry’s kingdom and I’m just reading your comment and trying to bridge two worlds here.

View attachment 26853

Python:
# ########################################
# Ehlers Trend Strength - FCF Yield
# Uses Dominant Cycle Period -> Adaptive MA Stack Separation
# Created by @rewadiaz
# v1.0 - 01/07/2026

script nz {
    input data  = close;
    input repl  = 0;
    def ret_val = if !isNaN(data) then data else repl;
    plot return = ret_val;
}

# Dynamic Simple Moving Average (variable length)
script AvgDyn {
    input data   = close;
    input len    = 20;     # may be dynamic
    input maxLen = 200;    # MUST be constant

    def bn = BarNumber();
    def n0 = Floor(len);
    def n1 = Min(Max(n0, 1), maxLen);
    def n  = Min(n1, bn);

    def sum =
        fold i = 0 to maxLen
        with s = 0.0
        do if i < n then s + GetValue(data, i) else s;

    plot out = sum / n;
}

script EhlersCyclePeriodDP {
    input src          = close;
    input CP_Alpha     = 0.07;
    input CP_MinDelta  = 0.10;
    input CP_MaxDelta  = 0.90;
    input CP_MinPeriod = 6;
    input CP_MaxPeriod = 50;
    input AdjPeriod    = 15;

    def bn    = BarNumber();
    def TwoPi = 6.28318;

    def s = src;

    def cpSmooth =
        if bn >= 4 then (s + 2 * s[1] + 2 * s[2] + s[3]) / 6.0
        else s;

    def cpInit =
        if bn >= 3 then (s - 2 * s[1] + s[2]) / 4.0
        else 0.0;

    def cpA1 = 1 - 0.5 * CP_Alpha;
    def cpA2 = 1 - CP_Alpha;

    def cpCycle = CompoundValue(1,
        if bn <= 6 then cpInit
        else (cpA1 * cpA1) * (cpSmooth - 2 * cpSmooth[1] + cpSmooth[2])
           + 2 * cpA2 * cpCycle[1]
           - (cpA2 * cpA2) * cpCycle[2],
        0
    );

    def I1 = if bn > 6 then cpCycle[3] else 0;

    # --- Hilbert FIR kernel (same math reused twice)
    def qBase =
        (0.0962 * cpCycle
       + 0.5769 * cpCycle[2]
       - 0.5769 * cpCycle[4]
       - 0.0962 * cpCycle[6]);

    def adj1 = 0.5 + 0.08 * AdjPeriod;

    def Q1_1 = if bn > 6 then qBase * adj1 else 0;

    def dpRaw1 =
        if Q1_1 != 0 and Q1_1[1] != 0 then
            (I1 / Q1_1 - I1[1] / Q1_1[1]) /
            (1 + (I1 * I1[1]) / (Q1_1 * Q1_1[1]))
        else 0;

    def dp1  = Max(CP_MinDelta, Min(CP_MaxDelta, dpRaw1));
    def med1 = if bn < 10 then dp1 else Median(dp1, 5);
    def DC1  = if med1 == 0 then 15 else TwoPi / med1 + 0.5;

    def Inst1 = CompoundValue(1, 0.33 * DC1 + 0.67 * Inst1[1], 15);
    def Per1  = CompoundValue(1, 0.15 * Inst1 + 0.85 * Per1[1], 15);

    def adj2 = 0.5 + 0.08 * Inst1[1];
    def Q1_2 = if bn > 6 then qBase * adj2 else 0;

    def dpRaw2 =
        if Q1_2 != 0 and Q1_2[1] != 0 then
            (I1 / Q1_2 - I1[1] / Q1_2[1]) /
            (1 + (I1 * I1[1]) / (Q1_2 * Q1_2[1]))
        else 0;

    def dp2  = Max(CP_MinDelta, Min(CP_MaxDelta, dpRaw2));
    def med2 = if bn < 10 then dp2 else Median(dp2, 5);
    def DC2  = if med2 == 0 then 15 else TwoPi / med2 + 0.5;

    def Inst2 = CompoundValue(1, 0.33 * DC2 + 0.67 * Inst2[1], 15);
    def Per2  = CompoundValue(1, 0.15 * Inst2 + 0.85 * Per2[1], 15);

    def CyclePeriodC = Min(Max(Per2, CP_MinPeriod), CP_MaxPeriod);

    plot CyclePeriod = CyclePeriodC;
}

script UltimateSmoother {
    input data   = close;
    input period = 20;

    def bn = BarNumber();

    def a1 = Exp(-1.414 * Double.Pi / period);
    def c2 = 2.0 * a1 * Cos(1.414 * Double.Pi / period);
    def c3 = -Sqr(a1);
    def c1 = (1.0 + c2 - c3) / 4.0;

    def us = CompoundValue(1,
        if bn >= 4 then
            (1.0 - c1) * data
          + (2.0 * c1 - c2) * data[1]
          - (c1 + c3) * data[2]
          + c2 * us[1]
          + c3 * us[2]
        else data,
        data
    );

    plot out = us;
}


# ########################################
# Charting & Formatting
declare upper;

# ########################################
# Inputs
input CP_Price      = Close;
input CP_Alpha      = 0.07;
input CP_MinDelta   = 0.10;
input CP_MaxDelta   = 0.90;
input CP_MinPeriod  = 5;
input CP_MaxPeriod  = 50;
input maxDynLen     = 200;
input paintBars     = no;
input showLines     = no;
input showLabels    = yes;
input eupMaxYield   = 0.02;  # <2% = euphoria
input proofMaxYield = 0.05; # 2-5% = proof required, >5% skeptical
input mode          = {default Any, Euphoria, Proof, Skeptical};
input targetYield1  = 0.06;
input targetYield2  = 0.05;

# ########################################
# Chart Coloring
DefineGlobalColor("green",       CreateColor(0, 165, 0));
DefineGlobalColor("red",         CreateColor(225, 0, 0));
DefineGlobalColor("orange",      Color.DARK_ORANGE);
DefineGlobalColor("blue",        CreateColor(50, 200, 255));
DefineGlobalColor("magenta",     CreateColor(200, 125, 255));
DefineGlobalColor("light_green", CreateColor(144, 238, 144));
DefineGlobalColor("light_red",   CreateColor(255, 102, 102));

# ########################################
# Globals
def na = Double.NaN;
def sc = close;

# ########################################
# CyclePeriod (DeltaPhase)
def HP = EhlersCyclePeriodDP(
    src          = CP_Price,
    CP_Alpha     = CP_Alpha,
    CP_MinDelta  = CP_MinDelta,
    CP_MaxDelta  = CP_MaxDelta,
    CP_MinPeriod = CP_MinPeriod,
    CP_MaxPeriod = CP_MaxPeriod,
    AdjPeriod    = 15
).CyclePeriod;

# Dynamic periods
def LP = Min(Max(Floor(nz(HP,CP_MinPeriod) /2), CP_MinPeriod), CP_MaxPeriod);

# ########################################
# Ehlers Trend Stack (adaptive)
def Lfast = LP;
def Lslow = Min(Max(2 * LP, CP_MinPeriod), maxDynLen);

def maFast = AvgDyn(CP_Price, Lfast, maxDynLen).out;
def maSlow = AvgDyn(CP_Price, Lslow, maxDynLen).out;

def trendSep = maFast - maSlow;

def trendUp   = trendSep > 0;
def trendDown = trendSep < 0;

# strength baseline (RMS of separation)
def ms  = CompoundValue(1, 0.0242 * Sqr(trendSep) + 0.9758 * ms[1], Sqr(trendSep));
def rms = Sqrt(ms);

def strongTrend = AbsValue(trendSep) > rms;
def weakTrend   = !strongTrend;

# ########################################
# Fundamentals (annual only)
def fcfPS_raw = FreeCashFlowPerShare(fiscalPeriod = FiscalPeriod.YEAR);

def fcfPS_hold =
    CompoundValue(1,
        if !IsNaN(fcfPS_raw) then fcfPS_raw else fcfPS_hold[1],
        fcfPS_raw
    );

# ########################################
# FCF Yield (FCF / Market Cap) == (FCF/Share) / Price
def fcfYield_raw = if !IsNaN(fcfPS_hold) and sc != 0 then fcfPS_hold / sc else na;

def fcfYield =
    CompoundValue(1,
        if !IsNaN(fcfYield_raw) then fcfYield_raw else fcfYield[1],
        fcfYield_raw
    );

# ########################################
# Terrain bucket
def isEuphoria  = !IsNaN(fcfYield) and fcfYield < eupMaxYield;
def isProof     = !IsNaN(fcfYield) and fcfYield >= eupMaxYield and fcfYield <= proofMaxYield;
def isSkeptical = !IsNaN(fcfYield) and fcfYield > proofMaxYield;

# ########################################
# Ehler trend filter
def UpTrending =  sc > maFast or trendUp;

# ########################################
# Mode fit
def modeFit =
    if mode == mode.Any then 1
    else if mode == mode.Euphoria then (if isEuphoria then 1 else 0)
    else if mode == mode.Proof then (if isProof then 1 else 0)
    else (if isSkeptical then 1 else 0);

def setupFit = modeFit == 1 and UpTrending;

# ########################################
# Rerate math (holding FCF/share constant)
def tgtPx1 = if !IsNaN(fcfPS_hold) and targetYield1 > 0 then fcfPS_hold / targetYield1 else na;
def tgtPx2 = if !IsNaN(fcfPS_hold) and targetYield2 > 0 then fcfPS_hold / targetYield2 else na;

def up1 = if !IsNaN(tgtPx1) and sc != 0 then tgtPx1 / sc - 1 else na;
def up2 = if !IsNaN(tgtPx2) and sc != 0 then tgtPx2 / sc - 1 else na;

# ########################################
# Labels
AddLabel(showLabels,
    " FCF/Share (FY): " + (if IsNaN(fcfPS_hold) then "n/a" else AsDollars(fcfPS_hold)),
    GlobalColor("orange")
);

AddLabel(showLabels,
    " FCF Yield (FCF/MC): " + (if IsNaN(fcfYield) then "n/a" else AsPercent(fcfYield)),
    if IsNaN(fcfYield) then GlobalColor("orange") else if fcfYield >= 0 then GlobalColor("green") else GlobalColor("red")
);

AddLabel(showLabels,    
    if IsNaN(fcfYield) then " FCF Bucket: n/a"
    else if isEuphoria then " FCF Bucket: EUPHORIA (<" + AsPercent(eupMaxYield) + ") "
    else if isProof then "FCF Bucket: PROOF (" + AsPercent(eupMaxYield) + "–" + AsPercent(proofMaxYield) + ") "
    else " FCF Bucket: SKEPTICAL (>" + AsPercent(proofMaxYield) + ") ",
    if IsNaN(fcfYield) then GlobalColor("orange")
    else if isEuphoria then GlobalColor("magenta")
    else if isProof then GlobalColor("blue")
    else GlobalColor("orange"));

AddLabel(showLabels,
    " CyclePeriod(LP~" + Round(maFast,0) + "): " + (if UpTrending then "ABOVE " else "BELOW "),
    if UpTrending and strongTrend then GlobalColor("blue")
    else if sc <= maFast and strongTrend and trendDown then GlobalColor("magenta")
    else if UpTrending then GlobalColor("green")
    else if sc <= maFast and trendDown then GlobalColor("red")
    else GlobalColor("orange")
);

AddLabel(showLabels,
    " Mode: " + mode + " | Fit: " + (if setupFit then "YES " else "NO "),
    if setupFit then GlobalColor("light_green") else GlobalColor("light_red")
);

AddLabel(showLabels,
    " Rerate " + AsPercent(targetYield1) + ": " + (if IsNaN(up1) then "n/a" else AsPercent(up1)),
    if IsNaN(up1) then GlobalColor("orange") else if up1 >= 0 then GlobalColor("green") else GlobalColor("red")
);

AddLabel(showLabels,
    " Rerate " + AsPercent(targetYield2) + ": " + (if IsNaN(up2) then "n/a" else AsPercent(up2)),
    if IsNaN(up2) then GlobalColor("orange") else if up2 >= 0 then GlobalColor("green") else GlobalColor("red")
);

# Diagnostic: why n/a?
AddLabel(showLabels and IsNaN(fcfPS_hold),
    " No FCF fundamentals on loaded bars (best on DAILY equities; not on futures/forex). ",
    GlobalColor("red")
);
daily scanner
Code:
# === Ehlers + FCF Yield SCAN ===
# Timeframe: DAILY
# Conditions:
# 1. Adaptive MA separation indicates trend up
# 2. FCF Yield between 2% and 5% (proof zone)
# antwerks 01/24/2026

def maFast = Average(close, 20);
def maSlow = Average(close, 40);

def trendUp = maFast > maSlow;

def fcfPerShare = FreeCashFlowPerShare(fiscalPeriod = FiscalPeriod.YEAR);
def fcfYield = if close != 0 then fcfPerShare / close else Double.NaN;

def fcfInProofZone = fcfYield >= 0.02 and fcfYield <= 0.05;

plot scan = trendUp and fcfInProofZone;
 
While you can't calculated FCF in Thinkscript studies, you can use a proxy such as EarningsPerShare divided by the share price.

DIsplay on Left Axis:

Code:
plot FCF = EarningsPerShareTTM()/close;
AddLabel(yes, "FCF: " + FCF*100 + "%", Color.GREEN);
Ehhh thats the earnings yield mainly popularized by Joel Greenblatt, but the number is quite different from free cash flow. One of the reasons people were confused about amazons share price insanity for years was they were looking at the earnings and seeing it was garbage and couldn't justify the share price. What they missed was that beceause of amazon web services, the free cash flow was gushing $ and thats what was bringing in investment dollars. I say that just to show that using that as a proxy would have you looking in the exact wrong place that led people to miss out on earlier investment in amazon. Not ideal
 

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

87k+ Posts
1209 Online
Create Post

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