Hey everyone! I'm venturing back into the world of coding after a hiatus of about 15 years. Although not completely new to coding, my recent endeavors have been more about tweaking others' snippets rather than crafting my own from scratch. Diving into ThinkScript and the ThinkOrSwim platform has been an enlightening journey, filled with both rewarding and challenging moments. Yes, there have been plenty of all-nighters!
This is my debut post in this incredible useThinkScript community. What a fantastic resource it has turned out to be!
Lately, I've been toying with the concept of filtering simple moving average crossings and have concocted a piece of code that's left me scratching my head. I'm reaching out to you, pros, hoping to get fresh eyes on my project. Frankly, I'm a bit sheepish about the current state of my code—it feels overly brute-forced and cumbersome. I have this nagging feeling that I'm missing something obvious and could really use your insights to help streamline and enhance it.
My ThinkScript code uses two moving averages to generate trading signals, with extra filters for one-bar reversals and divergence over a specified look-back period. This divergence filter is the real head-scratcher (check out lines 36 through 120). If / when we hit the divergence threshold, I wanted signals to pop up at the crossover. But if that moment never comes before the averages cross back, this signal should NOT show, NOR the cross back signal after that. This is where I'd typically lean on a do-while loop in other languages. My attempt to mimic this in ThinkScript led me down a path of trial and error and a bit of brain overload last night. (I struggled to wrap my head around the fold operator.) I resorted to a brute-force approach, limiting my focus to just two bars ahead with a series of comparisons that felt clunky. To keep a lid on subsequent signals if the initial one is suppressed, I introduced a "carryover variable" (trackSignal), though its elegance is debatable.
Eager for your insights or a nudge in the right direction. I'm sure I'm missing something obvious. Thanks a ton in advance for diving into this with me!
This is my debut post in this incredible useThinkScript community. What a fantastic resource it has turned out to be!
Lately, I've been toying with the concept of filtering simple moving average crossings and have concocted a piece of code that's left me scratching my head. I'm reaching out to you, pros, hoping to get fresh eyes on my project. Frankly, I'm a bit sheepish about the current state of my code—it feels overly brute-forced and cumbersome. I have this nagging feeling that I'm missing something obvious and could really use your insights to help streamline and enhance it.
My ThinkScript code uses two moving averages to generate trading signals, with extra filters for one-bar reversals and divergence over a specified look-back period. This divergence filter is the real head-scratcher (check out lines 36 through 120). If / when we hit the divergence threshold, I wanted signals to pop up at the crossover. But if that moment never comes before the averages cross back, this signal should NOT show, NOR the cross back signal after that. This is where I'd typically lean on a do-while loop in other languages. My attempt to mimic this in ThinkScript led me down a path of trial and error and a bit of brain overload last night. (I struggled to wrap my head around the fold operator.) I resorted to a brute-force approach, limiting my focus to just two bars ahead with a series of comparisons that felt clunky. To keep a lid on subsequent signals if the initial one is suppressed, I introduced a "carryover variable" (trackSignal), though its elegance is debatable.
Eager for your insights or a nudge in the right direction. I'm sure I'm missing something obvious. Thanks a ton in advance for diving into this with me!
Code:
# Define input parameters for customization
input price = close; # Primary price to be used for MA calculations
input fastLength = 3; # Length for the fast moving average
input fastAverageType = AverageType.EXPONENTIAL; # Defines fast MA as exponential
input slowLength = 5; # Length for the slow moving average
input slowAverageType = AverageType.EXPONENTIAL; # Defines slow MA as exponential
input oneBarReversalFilter = no; # Enables/disables one bar reversal filter
input divergenceFilter = no; # Enables/disables divergence filter for signal refinement
input lookBackPeriod = 126; # Defines the historical period for divergence calculation
input percentageOfMaxDivergence = 5; # Threshold for divergence-based signal filtering
# Calculate moving averages based on user inputs
def fastAvg = MovingAverage(fastAverageType, price, fastLength);
def slowAvg = MovingAverage(slowAverageType, price, slowLength);
# Detect basic MA crossovers
def aboveCross = Crosses(fastAvg, slowAvg, CrossingDirection.ABOVE);
def belowCross = Crosses(fastAvg, slowAvg, CrossingDirection.BELOW);
# Apply one bar reversal filter to crossovers, if enabled
def filtered1AboveCross = if oneBarReversalFilter then !belowCross[1] and aboveCross and !belowCross[-1] else aboveCross;
def filtered1BelowCross = if oneBarReversalFilter then !aboveCross[1] and belowCross and !aboveCross[-1] else belowCross;
# Calculate divergence and apply divergence filter, if enabled
def filterDivergence = AbsValue(fastAvg - slowAvg);
def maxFilterDivergence = Highest(filterDivergence[1], lookBackPeriod);
def filterTest = filterDivergence > maxFilterDivergence * percentageOfMaxDivergence / 100;
# Initialize variables for filtered crossing signals
def filtered2AboveCross;
def filtered2BelowCross;
# Track signals to manage filtering logic
def trackSignal;
# Complex filtering logic incorporating divergence filter and tracking previous signals
# The following blocks implement the logic to filter signals based on divergence criteria
# and the historical signal tracking to manage the application of filters over time.
if divergenceFilter
then {
if filtered1AboveCross and filterTest and trackSignal[1] == 0
then {
filtered2AboveCross = 1;
} else {
if filtered1BelowCross[-1] or trackSignal[1] != 0
then {
filtered2AboveCross = 0;
} else {
if filtered1AboveCross and filterTest[-1]
then {
filtered2AboveCross = 1;
} else {
if filtered1BelowCross[-2]
then {
filtered2AboveCross = 0;
} else {
if filtered1AboveCross and filterTest[-2]
then {
filtered2AboveCross = 1;
} else {
filtered2AboveCross = 0;
}
}
}
}
}
} else {
filtered2AboveCross = filtered1AboveCross;
}
if divergenceFilter
then {
if filtered1BelowCross and filterTest and trackSignal[1] == 0
then {
filtered2BelowCross = 1;
} else {
if filtered1AboveCross[-1] or trackSignal[1] != 0
then {
filtered2BelowCross = 0;
} else {
if filtered1BelowCross and filterTest[-1]
then {
filtered2BelowCross = 1;
} else {
if filtered1AboveCross[-2]
then {
filtered2BelowCross = 0;
} else {
if filtered1BelowCross and filterTest[-2]
then {
filtered2BelowCross = 1;
} else {
filtered2BelowCross = 0;
}
}
}
}
}
} else {
filtered2BelowCross = filtered1BelowCross;
}
if filtered2AboveCross or filtered2BelowCross
then {
trackSignal = 0;
} else {
if filtered1AboveCross and !filterTest
then {
trackSignal = trackSignal[1] + 1;
} else {
if filtered1BelowCross and !filterTest
then {
trackSignal = trackSignal[1] - 1;
} else {
trackSignal = trackSignal[1];
}
}
}
# The AddChartBubble() function is commented out, but it can display debug information on the chart
# AddChartBubble(yes, close - 1000, filterDivergence / maxFilterDivergence * 100 + "\n" + percentageOfMaxDivergence + "\n" + filterTest + "\n" + filtered2BelowCross + "\n" + filterTest[-1] + "\n" + !filtered1AboveCross[-1] + "\n" + tracksignal[1], Color.WHITE);
# Final signal plotting based on the filtering and criteria checks
plot aboveSignal = filtered2AboveCross;
plot belowSignal = filtered2BelowCross;
# Customize the appearance of signals on the chart
aboveSignal.DefineColor("Above", Color.LIGHT_GREEN);
aboveSignal.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
aboveSignal.AssignValueColor(aboveSignal.Color("Above"));
aboveSignal.SetLineWeight(5);
belowSignal.DefineColor("Below", Color.PINK);
belowSignal.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
belowSignal.AssignValueColor(belowSignal.Color("Below"));
belowSignal.SetLineWeight(5);
Last edited: