Trying to create an earnings scan in TOS

magnetar513

New member
I'm attempting to create an earnings scan in TOS that finds stocks that have missed their earnings target 3 times in a row prior to a most recent earnings beat, but my code doesn't give any results even for tracking a beat after 1 miss. This is what I have so far:

# Earnings Beat
# Find Stocks that have had an earnings beat after a miss

def earningsDay = HasEarnings() ;
def earningsAtAll= if HasEarnings() then 1 else earningsAtAll[1];
def earningsBeat= (earningsDay and GetActualEarnings()>= GetEstimatedEarnings());
def earningsMiss= (earningsDay and GetActualEarnings()< GetEstimatedEarnings());
def beatTrack= if earningsBeat then 1 else if earningsMiss then 0 else beatTrack[1];
def earningsMissCount= totalSum(earningsMiss);

plot signal= beatTrack and earningsMissCount>= 1 ;
#plot signal= earningsAtAll;

I have even tested a scan that looks for stocks that have any earnings at all, with no other filters, and it only returns a list of under 5000 stocks. Maybe someone with more experience could point out the problem? Thanks in advance!
 
Solution
I'm attempting to create an earnings scan in TOS that finds stocks that have missed their earnings target 3 times in a row prior to a most recent earnings beat, but my code doesn't give any results even for tracking a beat after 1 miss. This is what I have so far:

# Earnings Beat
# Find Stocks that have had an earnings beat after a miss

def earningsDay = HasEarnings() ;
def earningsAtAll= if HasEarnings() then 1 else earningsAtAll[1];
def earningsBeat= (earningsDay and GetActualEarnings()>= GetEstimatedEarnings());
def earningsMiss= (earningsDay and GetActualEarnings()< GetEstimatedEarnings());
def beatTrack= if earningsBeat then 1 else if earningsMiss then 0 else beatTrack[1];
def earningsMissCount= totalSum(earningsMiss);

plot...
This script should be usable as a scan, though I haven't tried it. Use the "signal" line and it should return results. (well, if you have it set to be "1 within x days" where that is some length of time, otherwise it will only trigger on days they have earnings)

Code:
declare lower;

def score =
if hasEarnings() then
    if getActualEarnings() > getEstimatedEarnings() then
        if score[1] < 0 then 0
        else score[1] + 1
    else if getActualEarnings() < getEstimatedEarnings() then
        if score[1] > 0 then 0
        else score[1] - 1
    else score[1]
else score[1];

plot tally = score;
plot zero = 0;

plot signal = if tally[1] <= -3 and tally == 0 then 1 else double.nan;
signal.setPaintingStrategy(paintingStrategy.ARROW_UP);

screenshot:
777KVcO.png

Note the green up arrow in March of '21. Netflix had missed three earnings in a row (tally at -3) and then beat earnings (tally resets to 0).

-mashume

Edit Actually, they had missed 4 in a row. the first reset them to a tally of zero and the next three knocked them down to -3. If your strategy relies on exactly three misses, you'll have to edit the code to say 'if tally[1] == -2'...
 
Last edited:
I'm attempting to create an earnings scan in TOS that finds stocks that have missed their earnings target 3 times in a row prior to a most recent earnings beat, but my code doesn't give any results even for tracking a beat after 1 miss. This is what I have so far:

# Earnings Beat
# Find Stocks that have had an earnings beat after a miss

def earningsDay = HasEarnings() ;
def earningsAtAll= if HasEarnings() then 1 else earningsAtAll[1];
def earningsBeat= (earningsDay and GetActualEarnings()>= GetEstimatedEarnings());
def earningsMiss= (earningsDay and GetActualEarnings()< GetEstimatedEarnings());
def beatTrack= if earningsBeat then 1 else if earningsMiss then 0 else beatTrack[1];
def earningsMissCount= totalSum(earningsMiss);

plot signal= beatTrack and earningsMissCount>= 1 ;
#plot signal= earningsAtAll;

I have even tested a scan that looks for stocks that have any earnings at all, with no other filters, and it only returns a list of under 5000 stocks. Maybe someone with more experience could point out the problem? Thanks in advance!

been one of those days, think i was interrupted 10x today. finally finished it.


i created a system to allow 1, 2, or 3 different sequential runs.
current run can be beats or misses.
previous runs will alternate from current one.

---------------------------
it is very hard to create a scan code without errors.
i like to create it as a lower chart study first, so i can see what is going on.

read after the code to see how it works


added hints for the 3 inputs to explain what they are

if you want, current run of 1 beat, preceeded by 3 misses,
. input seq1_current = 1;
. input seq2_prev1 = -3;
. input seq3_prev2 = 0;

load the lower study and experiment with the inputs to see how it works

------------------------------

scan code

Code:
# scan_earn_4x_00_lower_scan

#---------------------------------------
#  test numbers and stocks found , day chart , 4 years
#  stocks that have a signal somewhere in past 4 years
#  TGT, MRK, MET,    +1 , -3 ,  0  ,  current earn beat, prev 3 were misses
#  SO, SBUX, BAC,    +3 , -1 , +1  ,  current run of 3 beats , prev 1 was miss , prev 1 was beat
#  WMT, MET, BBUX,   +2 , -2 ,  0  ,  current run of 2 beats, prev 2 were misses
#  BAC, BMY, COP,    -1 , +2 ,  0  ,  current 1 miss, prev 2 were beats

#  stocks that have a signal on latest earnings
#  COP,  -1 , +2 ,  0  ,  current 1 miss, prev 2 were beats
#  SO,   +3 , -1 , +1  ,  current run of 3 beats , prev 1 was miss , prev 1 was beat

#https://usethinkscript.com/threads/trying-to-create-an-earnings-scan-in-tos.19610/
#Trying to create an earnings scan in TOS

declare lower;

def na = double.nan;
def bn = barnumber();

def getearn = if !isnan(GetActualEarnings()) then round(GetActualEarnings(),2) else 0;
def estearn = if !isnan(GetEstimatedEarnings()) then round(GetEstimatedEarnings(),2) else 0;
def earningsDay = HasEarnings() ;
def earningsBeat = (earningsDay and getearn > 0 and getearn >= estearn);
def earningsMiss = (earningsDay and getearn > 0 and GetEarn < EstEarn);

# sequence1 - count seq of same states , of  beat or miss
#  cnt1 has the current state, cnt2 has the prev, cnt3 has the cnt before cnt2
#  state =  1 = beat ,  -1 = miss 
def cnt1 = if bn == 1 then 0
 else if cnt1[1] <= 0 and earningsBeat then 1
 else if cnt1[1] >= 0 and earningsmiss then -1
 else if cnt1[1] > 0 and earningsBeat then cnt1[1] + 1
 else if cnt1[1] < 0 and earningsmiss then cnt1[1] - 1
 else cnt1[1];


# sequence2 - when cnt1 has a diff state, then copy cnt1 to cnt2, and start new count in cnt1
def cnt2 = if bn == 1 then 0
 else if sign(cnt1[1]) != sign(cnt1) then cnt1[1]
 else cnt2[1];


# sequence3 - when cnt1 has a diff state, then copy cnt2 to cnt3 , and start new count in cnt2
def cnt3 = if bn == 1 then 0
 else if sign(cnt1[1]) != sign(cnt1) then cnt2[1]
 else cnt3[1];


#  input sequence counts
input seq1_current = 1;
#hint seq1_current:  sequence 1.\n positive number for a sequence of earnings that beat estimates\n negative number for a sequence of earnings that missed estimates

input seq2_prev1 = -3;
#hint seq2_prev1:  sequence before seq1, opposite sign of seq1. if seq1 is positive, then this will be a negative number\nif 0, then ignore this parameter.

input seq3_prev2 = 0;
#hint seq3_prev2:  sequence before seq2, opposite sign of seq2. if seq2 is negative, then this will be a positive  number\nif 0, then ignore this parameter.


def signsok = if seq1_current == 0 then 0
 else if (sign(seq1_current) != 0 and seq2_prev1 == 0 and sign(seq3_prev2) != 0) then 0
 else if (sign(seq1_current) != sign(seq2_prev1) and seq3_prev2 == 0) then 1
 else (sign(seq1_current) != sign(seq2_prev1) and sign(seq1_current) == sign(seq3_prev2));


def seq1;
def seq2;
def seq3;
if seq1_current > 0 then {
 seq1 = (cnt1 == seq1_current);
 seq2 = if (seq2_prev1 == 0) then 1 else (cnt2 <= seq2_prev1);
 seq3 = if (seq3_prev2 == 0) then 1 else (cnt3 >= seq3_prev2);
} else if seq1_current < 0 then {
 seq1 = (cnt1 == seq1_current);
 seq2 = if (seq2_prev1 == 0) then 1 else (cnt2 >= seq2_prev1);
 seq3 = if (seq3_prev2 == 0) then 1 else (cnt3 <= seq3_prev2);
} else {
 seq1 = 0;
 seq2 = 0;
 seq3 = 0;
}

def seqs = signsok and seq1 and seq2 and seq3;

input signal_exist_for_quarter = no;
#  no = signal exists on just 1 bar
def signal = if bn == 1 or isnan(close) then 0
 else if signal_exist_for_quarter then seqs
 else earningsDay and seqs;

# scan signal
plot z = signal;

#



----------------------------

the following is the steps i took, to make a lower code

---------------------------

i added a bubble to display the variables.
you will see that
GetActualEarnings() and GetEstimatedEarnings()
only have a number on the bar with earnings. otherwise they are NA.
those NA's will cascade in formulas and cause problems. so need to trap and convert those NA's to 0.
display values in a bubble


-----------------------------------

GetActualEarnings() and GetEstimatedEarnings(),
i would assign variables to hold those values or 0
. def getearn = if !isnan(GetActualEarnings()) then GetActualEarnings() else 0;
. def estearn = if !isnan(GetEstimatedEarnings()) then GetEstimatedEarnings() else 0;

you want 2 conditions, 2 runs,
. current run of 1 earnings beat , preceeded by a run of 3 earnings misses

---------------------------------

i created a system to allow 1, 2, or 3 different sequential runs.
current run can be beats or misses.

create 3 variables, that keep a count of sequential runs, of similar states, of beat or miss.
display values in a bubble


---------------------------------


experimental lower code

Code:
# scan_earn_4x_00_lower

#---------------------------------------
#  test numbers and stocks found , day chart , 4 years
#  stocks that have a signal somewhere in past 4 years
#  TGT, MRK, MET,    +1 , -3 ,  0  ,  current earn beat, prev 3 were misses
#  SO, SBUX, BAC,    +3 , -1 , +1  ,  current run of 3 beats , prev 1 was miss , prev 1 was beat
#  WMT, MET, BBUX,   +2 , -2 ,  0  ,  current run of 2 beats, prev 2 were misses
#  BAC, BMY, COP,    -1 , +2 ,  0  ,  current 1 miss, prev 2 were beats

#  stocks that have a signal on latest earnings
#  COP,  -1 , +2 ,  0  ,  current 1 miss, prev 2 were beats
#  SO,   +3 , -1 , +1  ,  current run of 3 beats , prev 1 was miss , prev 1 was beat


declare lower;

def na = double.nan;
def bn = barnumber();

def getearn = if !isnan(GetActualEarnings()) then round(GetActualEarnings(),2) else 0;
def estearn = if !isnan(GetEstimatedEarnings()) then round(GetEstimatedEarnings(),2) else 0;
def earningsDay = HasEarnings() ;
def earningsBeat = (earningsDay and getearn > 0 and getearn >= estearn);
def earningsMiss = (earningsDay and getearn > 0 and GetEarn < EstEarn);

# sequence1 - count seq of same states , of  beat or miss
#  cnt1 has the current state, cnt2 has the prev, cnt3 has the cnt before cnt2
#  state =  1 = beat ,  -1 = miss 
def cnt1 = if bn == 1 then 0
 else if cnt1[1] <= 0 and earningsBeat then 1
 else if cnt1[1] >= 0 and earningsmiss then -1
 else if cnt1[1] > 0 and earningsBeat then cnt1[1] + 1
 else if cnt1[1] < 0 and earningsmiss then cnt1[1] - 1
 else cnt1[1];


# sequence2 - when cnt1 has a diff state, then copy cnt1 to cnt2, and start new count in cnt1
def cnt2 = if bn == 1 then 0
 else if sign(cnt1[1]) != sign(cnt1) then cnt1[1]
 else cnt2[1];


# sequence3 - when cnt1 has a diff state, then copy cnt2 to cnt3 , and start new count in cnt2
def cnt3 = if bn == 1 then 0
 else if sign(cnt1[1]) != sign(cnt1) then cnt2[1]
 else cnt3[1];


#  input sequence counts
input seq1_current = 1;
#hint seq1_current:  sequence 1.\n positive number for a sequence of earnings that beat estimates\n negative number for a sequence of earnings that missed estimates

input seq2_prev1 = -3;
#hint seq2_prev1:  sequence before seq1, opposite sign of seq1. if seq1 is positive, then this will be a negative number\nif 0, then ignore this parameter.

input seq3_prev2 = 0;
#hint seq3_prev2:  sequence before seq2, opposite sign of seq2. if seq2 is negative, then this will be a positive  number\nif 0, then ignore this parameter.


def signsok = if seq1_current == 0 then 0
 else if (sign(seq1_current) != 0 and seq2_prev1 == 0 and sign(seq3_prev2) != 0) then 0
 else if (sign(seq1_current) != sign(seq2_prev1) and seq3_prev2 == 0) then 1
 else (sign(seq1_current) != sign(seq2_prev1) and sign(seq1_current) == sign(seq3_prev2));


def seq1;
def seq2;
def seq3;
if seq1_current > 0 then {
 seq1 = (cnt1 == seq1_current);
 seq2 = if (seq2_prev1 == 0) then 1 else (cnt2 <= seq2_prev1);
 seq3 = if (seq3_prev2 == 0) then 1 else (cnt3 >= seq3_prev2);
} else if seq1_current < 0 then {
 seq1 = (cnt1 == seq1_current);
 seq2 = if (seq2_prev1 == 0) then 1 else (cnt2 >= seq2_prev1);
 seq3 = if (seq3_prev2 == 0) then 1 else (cnt3 <= seq3_prev2);
} else {
 seq1 = 0;
 seq2 = 0;
 seq3 = 0;
}

def seqs = signsok and seq1 and seq2 and seq3;

input signal_exist_for_quarter = no;
#  no = signal exists on just 1 bar
def signal = if bn == 1 or isnan(close) then 0
 else if signal_exist_for_quarter then seqs
 else earningsDay and seqs;

# scan signal
plot z = signal;


#-------------------------------


# for a scan , delete everything after this line

addlabel(!signsok, " >>>>>>>>>>>   numbers have wrongs polarity. need to be alternating   <<<<<<<<<<<", color.cyan);

addlabel(1, "  ", color.black);
addlabel(1, "Earnings", color.yellow);
addlabel(1, "  ", color.black);
addlabel(1, "Seq3: " + absvalue(seq3_prev2) + (if seq3_prev2 > 0 then " beat" else if seq3_prev2 < 0 then " miss" else ""), (if  seq3_prev2 > 0 then color.green else if seq3_prev2 < 0 then color.red else color.gray));
addlabel(1, "Seq2: " + absvalue(seq2_prev1) + (if seq2_prev1 > 0 then " beat" else if seq2_prev1 < 0 then " miss" else ""), (if  seq2_prev1 > 0 then color.green else if seq2_prev1 < 0 then color.red else color.gray));
addlabel(1, "Seq1: " + absvalue(seq1_current) + (if seq1_current > 0 then " beat" else if seq1_current < 0 then " miss" else ""), (if  seq1_current > 0 then color.green else if seq1_current < 0 then color.red else color.gray));
addlabel(1, "  ", color.black);


plot z0 = 0;

input test1 = no;
addchartbubble(test1, 0,
 earningsDay + "\n" + 
# earningsAtAll + "\n" + 
 GetEarn + "  ern\n" + 
 EstEarn + "  est\n" + 
 earningsBeat + "  beat\n" + 
 earningsMiss + "  miss\n" 
# beatTrack + "\n" 
, (if earningsDay then color.yellow else color.gray), no);


input test2 = yes;
addchartbubble(test2 and !isnan(close) and earningsDay, 0,
# earningsDay + "\n" + 
# earningsAtAll + "\n" + 
 GetEarn + "  ern\n" + 
 EstEarn + "  est\n" + 
# earningsBeat + " beat\n" + 
# earningsMiss + " miss\n" + 
# beatTrack + "\n" 
 cnt1 + "  seq1\n" +
 cnt2 + "  seq2\n" +
 cnt3 + "  seq3\n" +
 signal + "\n" 
# sign(seq1_current) + " s1\n" +
# sign(seq2_prev1)  + " s2\n" +
# sign(seq3_prev2) + " s3\n" 
, (if signal then color.yellow else color.gray), no);


#plot z3 = (seq3 * 1) + 1.5;
#plot z2 = (seq2 * 1) + 3;
#plot z1 = (seq1 * 1) + 4.5;
#
 

Attachments

  • 1beat_3misses.JPG
    1beat_3misses.JPG
    85.1 KB · Views: 31
Solution

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

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

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