Skip to content

Instantly share code, notes, and snippets.

@jasonseney
Last active May 14, 2025 16:09
Show Gist options
  • Save jasonseney/d768933489ec5fad92f2aabd8ec70031 to your computer and use it in GitHub Desktop.
Save jasonseney/d768933489ec5fad92f2aabd8ec70031 to your computer and use it in GitHub Desktop.
//@version=5
// Strategy Version 0.01
strategy('Resonance Range Strategy')
IDEAL_TIMEFRAME = 15
TIME_FRAME_RATIO = timeframe.in_seconds() / 60 / IDEAL_TIMEFRAME
ATR_DIFF_THRESHOLD = 0 // The range in which we decide to take trades or not
// TODO Use this to adjust certain parameters for other timeframes
// *** Settings
timeBoxed = input(defval=false, title='Use Start Date', group = 'date')
startDate = input.time(defval = timestamp('2023-01-01'), title = 'Start Date', group = 'date')
timeMultiplier = input(defval = 2, title = 'Time Multiplier', tooltip = 'Adjusts the overall sensitivity to wave trends')
//timeMultiplier = math.min(2 / TIME_FRAME_RATIO, 1)
channelLength = input(10, 'Channel Length')
stochasticRatio = input(21, 'Stochastic Ratio Filter')
crossoverSmaLength = input(3, 'Crossover Lag (>1)')
obLevel = input(75, 'Overbought %')
osLevel = -obLevel
obExtremeLevel = input(100, 'Extreme Overbought %')
osExtremeLevel = -obExtremeLevel
atrFastBars = input(defval=5, title='ATR Fast Bars', group = 'ATR Shift')
atrSlowBars = input(defval=10, title='ATR Slow Bars')
// TODO: Adjust this with time frame ratio!
normalizedRangeBars = input(defval=96, title='ATR Normalization Bars')
// *** Wave Trends
waveTrend(priceSeries, baseLength, stochasticRatioLength, crossoverLength, timeRatio) =>
// Calculate the Exponential Moving Average of the input price series
// using the length multiplied by the time ratio
baseMA = ta.ema(priceSeries, baseLength * timeRatio)
// Calculate the absolute difference between the input price series and the previously computed EMA
// Then, compute the EMA of this absolute difference using the same period length
diffMA = ta.ema(math.abs(priceSeries - baseMA), baseLength * timeRatio)
// Compute the Composite Index by dividing the difference between the input price series and ema by
// the EMA of the absolute difference. Multiply the result by 100 to obtain the CI, stored in the variable ci.
compositeIndesx = 100 * (priceSeries - baseMA) / diffMA
// Calculate the EMA of the Composite Index
wtA = ta.ema(compositeIndesx, stochasticRatioLength * timeRatio)
// Calculate the Simple Moving Average (SMA) of wave trend A
wtB = ta.sma(wtA, crossoverLength * timeRatio)
wtDiff = wtA - wtB
[wtA, wtB, wtDiff]
// Normalized average true range over a range of bars and length of atr calculation
atrNorm(normRange, length) =>
atr = ta.atr(length)
atrCompare = ta.highest(atr, normRange) - ta.lowest(atr, normRange)
normalized = ((atr - ta.lowest(atr, normRange)) / atrCompare - 0.5)
normalized * 100
// *** ATR Trends
atrTrendNorm(normRange, lengthfast, lengthslow) =>
atrNormF = atrNorm(normRange, lengthfast)
atrNormS = atrNorm(normRange, lengthslow)
atrNormDiff = atrNormF - atrNormS
// TODO: Actually return if it's trending up or down instead of just the diff *********
// Return the ATR trend as a string
[atrNormDiff, atrNormF, atrNormS]
atrTrendLegacy(lengthfast, lengthslow) =>
atrFast = ta.atr(lengthfast) / close * 100
atrSlow = ta.atr(lengthslow) / close * 100
ma_diff = atrFast - atrSlow
[ma_diff, atrFast, atrSlow]
// *** RSI Calculations (NOT USED RN)
getRSI(length) =>
rmaUp = ta.rma(math.max(ta.change(close), 0), length)
rmaDown = ta.rma(-math.min(ta.change(close), 0), length)
rmaDown == 0 ? 100 : rmaUp == 0 ? 0 : 100 - (100 / (1 + rmaUp / rmaDown))
// Generate the wave trend
[wta, wtb, wtdiff] = waveTrend(hlc3, channelLength, stochasticRatio, crossoverSmaLength, timeMultiplier)
[wta2, wtb2, wtdiff2] = waveTrend(hlc3, channelLength, stochasticRatio, crossoverSmaLength, timeMultiplier / 2)
isCrossing = ta.cross(wta, wtb)
isBull = wtb - wta < 0
isBear = not isBull
isCrossingFast = ta.cross(wta2, wtb2)
isBullFast = wtb2 - wta2 < 0
isBearFast = not isBullFast
// Calc cross types based on levels
extreme_cross = (wta > obExtremeLevel or wta < osExtremeLevel) and isCrossing
significant_cross = (wta > obLevel or wta < osLevel) and isCrossing
// Generate the ATR trends
[atrDiff, atrFast, atrSlow] = atrTrendNorm(normalizedRangeBars, atrFastBars, atrSlowBars)
//[atrDiff, atrFast, atrSlow] = atrTrendLegacy(atrFastBars, atrSlowBars)
// *** Strategy Execution ***
// ENTER position if:
// - Crossing on wave trend
// - and ATR Norm is less than threshold (generally less than average)
// - OR ATR Diff is low
var order_count = 0
var openLong = false
var openShort = false
enteringLong = false
enteringShort = false
openLongConditions = isBull and (atrFast < 0 or atrDiff < ATR_DIFF_THRESHOLD) //and wta < 0 //Low range vs range ma, wave trend might be over sold
openShortConditions = isBear and (atrFast < 0 or atrDiff < ATR_DIFF_THRESHOLD) //and wta > 0 //Low range vs range ma, wave trend might be over bought
dateCondition = timeBoxed and time >= startDate
if isCrossing and dateCondition
// LONG
if openLongConditions
strategy.entry('Long', strategy.long)
label.new(bar_index, wta, style=label.style_diamond, color=color.green, size=size.tiny, text = 'LONG', textcolor = color.green)
order_count += 1
openLong := true
enteringLong := true
// SHORT
else if openShortConditions
strategy.entry('Short', strategy.short)
label.new(bar_index, wta, style=label.style_diamond, color=color.red, size=size.tiny, text = 'SHORT', textcolor = color.red)
openShort := true
enteringShort := true
order_count += 1
// *** Strategy Closes ***
// CLOSE position if:
// - Opening an opposite position
// OR
// - Moving in opposite direction
// - and ATR Diff is more than threshold (trending up)
closeLongConditions = isBear and (atrDiff > ATR_DIFF_THRESHOLD or atrFast > 0)
closeShortConditions = isBull and (atrDiff > ATR_DIFF_THRESHOLD or atrFast > 0)
if enteringShort or closeLongConditions
strategy.close('Long')
if openLong
openLong := false
//label.new(bar_index, wta, style=label.style_xcross, color=color.orange, size=size.tiny, text = 'CLOSE LONG', textcolor = color.orange)
if enteringLong or closeShortConditions
strategy.close('Short')
if openShort
openShort := false
//label.new(bar_index, wta, style=label.style_xcross, color=color.orange, size=size.tiny, text = 'CLOSE SHORT', textcolor = color.orange)
// **** PLOT STRATEGY *****
hasPosition = strategy.opentrades > 0
// Debug Info
plot(isCrossing ? 1 : 0, title= 'Crossing', display = display.data_window)
plot(wta, title= 'Resonance Trend', display = display.data_window)
plot(order_count, title= 'Order #', display = display.data_window)
plot(getRSI(IDEAL_TIMEFRAME), title= 'RSI', display = display.data_window)
plot(atrFast, title= 'ATR Fast', display = display.data_window)
plot(atrSlow, title= 'ATR Slow', display = display.data_window)
plot(atrDiff, title= 'ATR Diff', display = display.data_window)
plot(hasPosition ? strategy.openprofit : na, title= 'Open Profit', display = display.data_window, color = strategy.openprofit > 0 ? color.green : color.red)
plot(strategy.opentrades, title= 'Open Trades', display = display.data_window, color = color.fuchsia)
// Fill overbought
obE = hline(obExtremeLevel, color=color.silver, linestyle=hline.style_dashed, linewidth=1)
ob = hline(obLevel, color=color.silver, linestyle=hline.style_dotted, linewidth=1)
fill(ob, obE, color=color.new(color.maroon, 90))
// Fill oversold
os = hline(osLevel, color=color.silver, linestyle=hline.style_dotted, linewidth=1)
osE = hline(osExtremeLevel, color=color.silver, linestyle=hline.style_dashed, linewidth=1)
fill(os, osE, color=color.new(color.olive, 90))
// Plot out wave trend
cross_circle_color = isBull ? color.green : color.red
plot(wta, title = 'Wave Trend A', color= color.new(cross_circle_color, transp = 0.8), display = display.pane + display.data_window)
plot(wtb, title = 'Wave Trend B', color= color.new(cross_circle_color, transp = 0.8), display = display.pane + display.data_window)
// Plot ATR
plot(atrDiff, title='ATR Diff', color=color.new(color.silver,40), style=plot.style_histogram, display = display.pane)
plot(atrFast, title='ATR Norm F', color=color.new(color.purple,40), style=plot.style_line, display = display.pane)
plot(atrSlow, title='ATR Norm S', color=color.new(color.blue,40), style=plot.style_line, display = display.pane)
// Plot out crosses
plot(significant_cross ? wta : na, color=color.new(color.white, 0.5), style=plot.style_circles, linewidth=3, display = display.pane)
plot(extreme_cross ? wta : na, color=color.new(color.gray, 0.5), style=plot.style_cross, linewidth=3, display = display.pane)
// Use this to color extremes
//cross_circle_color = extreme_cross ? (isBull ? lime : red) : (isBull ? aqua : #FF9900)
// Plot cross dots
plot(isCrossing ? wta : na, color=cross_circle_color, style=plot.style_circles, linewidth=2, display = display.pane)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment