Guest User

Untitled

a guest
Nov 3rd, 2026
193
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.57 KB | None | 0 0
  1. //@version=6
  2. indicator("SMT+", shorttitle = "SSMT+", overlay = true, max_labels_count = 500, max_lines_count = 500, max_boxes_count = 200)
  3.  
  4. const string TZ = "America/New_York"
  5.  
  6. const string G_PAIR = "Correlated Symbol"
  7. const string G_CYC = "Cycles"
  8. const string G_DISP = "Display"
  9. const string G_BOX = "Boxes"
  10.  
  11. pairMode = input.string("Auto", "Pair", options = ["Auto", "Manual"], inline = "pair", group = G_PAIR)
  12. pairInput = input.symbol("CME_MINI:ES1!", "", inline = "pair", tooltip = "Auto: NQ-ES, MNQ-MES, YM-ES, MYM-MES, same contract as the chart. Manual: the symbol picked here.", group = G_PAIR)
  13.  
  14. showMicro = input.bool(false, "Micro (22.5m)", inline = "c1", tooltip = "1m to 5m charts.", group = G_CYC)
  15. show90 = input.bool(true, "90-Minute", inline = "c1", tooltip = "1m to 15m charts.", group = G_CYC)
  16. showDaily = input.bool(true, "Daily", inline = "c2", tooltip = "The four 6-hour quarters: Q1 18:00, Q2 00:00, Q3 06:00, Q4 12:00. 1m to 1H charts.", group = G_CYC)
  17. showQ1 = input.bool(true, "Q1", inline = "c3", group = G_CYC)
  18. showQ2 = input.bool(true, "Q2", inline = "c3", group = G_CYC)
  19. showQ3 = input.bool(true, "Q3", inline = "c3", group = G_CYC)
  20. showQ4 = input.bool(true, "Q4", inline = "c3", tooltip = "Daily: only SSMTs that form in the ticked quarters.", group = G_CYC)
  21. showWeek = input.bool(true, "Weekly", inline = "c4", group = G_CYC)
  22. showMonth = input.bool(true, "Monthly", inline = "c4", tooltip = "Weekly and Monthly work up to a daily chart.", group = G_CYC)
  23.  
  24. lineCol = input.color(#000000, "Color", inline = "line", group = G_DISP)
  25. lineStyle = input.string("Dotted", "", options = ["Solid", "Dashed", "Dotted"], inline = "line", group = G_DISP)
  26. maxShown = input.int(20, "Max visible", minval = 1, maxval = 100, tooltip = "Only this many of the most recent SSMTs stay on the chart.", group = G_DISP)
  27. lookback = input.string("1 Week", "Lookback Period", options = ["1 Day", "1 Week", "1 Month", "Max"], group = G_DISP)
  28. labelSize = input.string("Small", "Label size", options = ["Tiny", "Small", "Normal", "Large"], group = G_DISP)
  29.  
  30. boxDaily = input.bool(false, "Daily quarter boxes", inline = "box", group = G_BOX)
  31. box90m = input.bool(false, "90m boxes", inline = "box", group = G_BOX)
  32.  
  33. type Side
  34. bool on
  35. bool dead
  36. int id
  37. line ln
  38. label lb
  39.  
  40. type Cycle
  41. int rank
  42. int key
  43. float hi
  44. float lo
  45. int hiBar
  46. int loBar
  47. float prevHi
  48. float prevLo
  49. int prevHiBar
  50. int prevLoBar
  51. float pairHi
  52. float pairLo
  53. float prevPairHi
  54. float prevPairLo
  55. Side up
  56. Side dn
  57. box bx
  58. int firedBar
  59. int firedDir
  60.  
  61. type Counter
  62. int seq
  63. bool changed
  64.  
  65. color clear = color.new(color.black, 100)
  66.  
  67. labelSz() =>
  68. switch labelSize
  69. "Tiny" => size.tiny
  70. "Normal" => size.normal
  71. "Large" => size.large
  72. => size.small
  73.  
  74. priceText(float p) => str.tostring(p, format.mintick)
  75.  
  76. tfSec = timeframe.in_seconds()
  77. canMicro = timeframe.isintraday and tfSec <= 300
  78. can90 = timeframe.isintraday and tfSec <= 900 and 5400 % tfSec == 0
  79. canDaily = timeframe.isintraday and tfSec <= 3600 and 21600 % tfSec == 0
  80. canWeek = tfSec <= 86400
  81.  
  82. lookbackMs = switch lookback
  83. "1 Day" => 86400000
  84. "1 Week" => 7 * 86400000
  85. "1 Month" => 31 * 86400000
  86. => na
  87. inView = na(lookbackMs) or time >= last_bar_time - lookbackMs
  88.  
  89. tradingDay = time_tradingday
  90. minuteOfDay = hour(time, TZ) * 60 + minute(time, TZ)
  91. sinceOpen = (minuteOfDay - 1080 + 1440) % 1440
  92. quarter = int(sinceOpen / 360)
  93. paused = timeframe.isintraday and minuteOfDay >= 1020 and minuteOfDay < 1080
  94.  
  95. microKey = tradingDay * 64 + int(sinceOpen * 2 / 45)
  96. key90 = tradingDay * 16 + int(sinceOpen / 90)
  97. dailyKey = tradingDay * 4 + quarter
  98. weekKey = int((int(math.floor(tradingDay / 86400000.0)) + 3) / 7)
  99. monthKey = year(tradingDay, "UTC") * 12 + month(tradingDay, "UTC")
  100.  
  101. autoPair = switch syminfo.root
  102. "NQ" => "ES"
  103. "ES" => "NQ"
  104. "MNQ" => "MES"
  105. "MES" => "MNQ"
  106. "YM" => "ES"
  107. "MYM" => "MES"
  108. => ""
  109. manual = pairMode == "Manual"
  110. pairSym = manual ? pairInput : autoPair == "" ? "" : (syminfo.root == "YM" or syminfo.root == "MYM") ? "CME_MINI:" + autoPair + "1!" : str.replace(syminfo.tickerid, syminfo.root, autoPair)
  111. pairName = manual ? array.last(str.split(pairInput, ":")) : autoPair
  112. hasPair = pairSym != ""
  113. [pairHigh, pairLow] = request.security(hasPair ? pairSym : syminfo.tickerid, timeframe.period, [high, low], ignore_invalid_symbol = true)
  114.  
  115. newCycle(int rank) => Cycle.new(rank = rank, up = Side.new(), dn = Side.new())
  116.  
  117. var micro = newCycle(0)
  118. var min90 = newCycle(1)
  119. var daily = newCycle(2)
  120. var weekly = newCycle(3)
  121. var monthly = newCycle(4)
  122. var counter = Counter.new(0, false)
  123.  
  124. var array<line> lines = array.new<line>()
  125. var array<label> labels = array.new<label>()
  126. var array<int> ids = array.new<int>()
  127. var array<int> ranks = array.new<int>()
  128. var array<string> tags = array.new<string>()
  129. var array<string> tips = array.new<string>()
  130. var array<box> boxes = array.new<box>()
  131.  
  132. rollCycle(Cycle c, int key, bool drawBox) =>
  133. if na(c.key) or key != c.key
  134. c.prevHi := c.hi
  135. c.prevLo := c.lo
  136. c.prevHiBar := c.hiBar
  137. c.prevLoBar := c.loBar
  138. c.prevPairHi := c.pairHi
  139. c.prevPairLo := c.pairLo
  140. c.hi := high
  141. c.lo := low
  142. c.hiBar := bar_index
  143. c.loBar := bar_index
  144. c.pairHi := pairHigh
  145. c.pairLo := pairLow
  146. c.key := key
  147. c.up.on := false
  148. c.up.dead := false
  149. c.dn.on := false
  150. c.dn.dead := false
  151. if drawBox and inView
  152. c.bx := box.new(bar_index, high, bar_index, low, border_color = lineCol, border_style = line.style_dotted, bgcolor = clear)
  153. boxes.push(c.bx)
  154. if boxes.size() > 150
  155. boxes.shift().delete()
  156. true
  157.  
  158. updateCycle(Cycle c) =>
  159. if high > c.hi
  160. c.hi := high
  161. c.hiBar := bar_index
  162. if low < c.lo
  163. c.lo := low
  164. c.loBar := bar_index
  165. c.pairHi := na(c.pairHi) ? pairHigh : math.max(c.pairHi, nz(pairHigh, c.pairHi))
  166. c.pairLo := na(c.pairLo) ? pairLow : math.min(c.pairLo, nz(pairLow, c.pairLo))
  167. if not na(c.bx)
  168. c.bx.set_right(bar_index)
  169. c.bx.set_top(c.hi)
  170. c.bx.set_bottom(c.lo)
  171. true
  172.  
  173. addMark(line ln, label lb, int id, int rank, string tag, string tip) =>
  174. lines.push(ln)
  175. labels.push(lb)
  176. ids.push(id)
  177. ranks.push(rank)
  178. tags.push(tag)
  179. tips.push(tip)
  180. if lines.size() > maxShown
  181. lines.shift().delete()
  182. labels.shift().delete()
  183. ids.shift()
  184. ranks.shift()
  185. tags.shift()
  186. tips.shift()
  187. counter.changed := true
  188. true
  189.  
  190. removeMark(int id) =>
  191. i = ids.indexof(id)
  192. if i >= 0
  193. lines.remove(i).delete()
  194. labels.remove(i).delete()
  195. ids.remove(i)
  196. ranks.remove(i)
  197. tags.remove(i)
  198. tips.remove(i)
  199. counter.changed := true
  200. true
  201.  
  202. mergeLabels() =>
  203. n = labels.size()
  204. if n > 0
  205. for i = 0 to n - 1
  206. lb = labels.get(i)
  207. x = lb.get_x()
  208. y = lb.get_y()
  209. first = true
  210. if i > 0
  211. for j = 0 to i - 1
  212. if labels.get(j).get_x() == x and labels.get(j).get_y() == y
  213. first := false
  214. if first
  215. names = ""
  216. tip = ""
  217. for r = 0 to 4
  218. for k = 0 to n - 1
  219. if ranks.get(k) == r and labels.get(k).get_x() == x and labels.get(k).get_y() == y
  220. names := names + (names == "" ? "" : " | ") + tags.get(k)
  221. tip := tip + (tip == "" ? "" : "\n") + tips.get(k)
  222. lb.set_text("SSMT [" + names + "]")
  223. lb.set_tooltip(tip)
  224. else
  225. lb.set_text("")
  226. true
  227.  
  228. checkSSMT(Cycle c, int dir, string tag, string name, bool enabled) =>
  229. atHigh = dir == -1
  230. s = atHigh ? c.up : c.dn
  231. prev = atHigh ? c.prevHi : c.prevLo
  232. prevPair = atHigh ? c.prevPairHi : c.prevPairLo
  233. if enabled and hasPair and not na(prev) and not na(prevPair) and not s.dead
  234. ext = atHigh ? c.hi : c.lo
  235. extBar = atHigh ? c.hiBar : c.loBar
  236. pairExt = atHigh ? c.pairHi : c.pairLo
  237. chartTook = atHigh ? ext > prev : ext < prev
  238. pairTook = atHigh ? pairExt > prevPair : pairExt < prevPair
  239. diverged = chartTook != pairTook
  240. who = (chartTook ? syminfo.ticker : pairName) + " took it, " + (chartTook ? pairName : syminfo.ticker) + " did not"
  241. tip = (atHigh ? "Bearish" : "Bullish") + " SSMT [" + name + "] w/" + pairName + ": previous " + (atHigh ? "high " : "low ") + priceText(prev) + ". " + who + "."
  242. if s.on and not diverged
  243. removeMark(s.id)
  244. s.on := false
  245. s.dead := true
  246. if s.on and diverged and not na(s.ln)
  247. if s.lb.get_x() != extBar or s.lb.get_y() != ext
  248. counter.changed := true
  249. s.ln.set_xy2(extBar, ext)
  250. s.lb.set_xy(extBar, ext)
  251. i = ids.indexof(s.id)
  252. if i >= 0
  253. tips.set(i, tip)
  254. if not s.on and diverged and barstate.isconfirmed
  255. counter.seq := counter.seq + 1
  256. s.id := counter.seq
  257. s.ln := na
  258. s.lb := na
  259. if inView
  260. prevBar = atHigh ? c.prevHiBar : c.prevLoBar
  261. s.ln := line.new(prevBar, prev, extBar, ext, color = lineCol, style = lineStyle == "Dashed" ? line.style_dashed : lineStyle == "Solid" ? line.style_solid : line.style_dotted)
  262. s.lb := label.new(extBar, ext, "SSMT [" + tag + "]", color = clear, style = atHigh ? label.style_label_down : label.style_label_up, textcolor = lineCol, size = labelSz(), tooltip = tip, text_font_family = font.family_monospace)
  263. addMark(s.ln, s.lb, s.id, c.rank, tag, tip)
  264. s.on := true
  265. c.firedBar := bar_index
  266. c.firedDir := dir
  267. true
  268.  
  269. dailyQuarterOn = (quarter == 0 and showQ1) or (quarter == 1 and showQ2) or (quarter == 2 and showQ3) or (quarter == 3 and showQ4)
  270. dailyName = "Daily Q" + str.tostring(quarter + 1)
  271.  
  272. if not paused
  273. if canMicro
  274. rollCycle(micro, microKey, false)
  275. updateCycle(micro)
  276. checkSSMT(micro, -1, "Micro", "Micro", showMicro)
  277. checkSSMT(micro, 1, "Micro", "Micro", showMicro)
  278. if can90
  279. rollCycle(min90, key90, box90m)
  280. updateCycle(min90)
  281. checkSSMT(min90, -1, "90m", "90m", show90)
  282. checkSSMT(min90, 1, "90m", "90m", show90)
  283. if canDaily
  284. rollCycle(daily, dailyKey, boxDaily)
  285. updateCycle(daily)
  286. checkSSMT(daily, -1, "D", dailyName, showDaily and dailyQuarterOn)
  287. checkSSMT(daily, 1, "D", dailyName, showDaily and dailyQuarterOn)
  288. if canWeek
  289. rollCycle(weekly, weekKey, false)
  290. updateCycle(weekly)
  291. rollCycle(monthly, monthKey, false)
  292. updateCycle(monthly)
  293. checkSSMT(weekly, -1, "W", "Weekly", showWeek)
  294. checkSSMT(weekly, 1, "W", "Weekly", showWeek)
  295. checkSSMT(monthly, -1, "M", "Monthly", showMonth)
  296. checkSSMT(monthly, 1, "M", "Monthly", showMonth)
  297.  
  298. if counter.changed
  299. mergeLabels()
  300. counter.changed := false
  301.  
  302. fired(Cycle c, int dir) => c.firedBar == bar_index and c.firedDir == dir
  303. alertcondition(fired(micro, -1), "SSMT Micro - High side", "SSMT+: bearish Micro SSMT on {{ticker}} at {{close}}")
  304. alertcondition(fired(micro, 1), "SSMT Micro - Low side", "SSMT+: bullish Micro SSMT on {{ticker}} at {{close}}")
  305. alertcondition(fired(min90, -1), "SSMT 90m - High side", "SSMT+: bearish 90m SSMT on {{ticker}} at {{close}}")
  306. alertcondition(fired(min90, 1), "SSMT 90m - Low side", "SSMT+: bullish 90m SSMT on {{ticker}} at {{close}}")
  307. alertcondition(fired(daily, -1), "SSMT Daily - High side", "SSMT+: bearish Daily SSMT on {{ticker}} at {{close}}")
  308. alertcondition(fired(daily, 1), "SSMT Daily - Low side", "SSMT+: bullish Daily SSMT on {{ticker}} at {{close}}")
  309. alertcondition(fired(weekly, -1), "SSMT Weekly - High side", "SSMT+: bearish Weekly SSMT on {{ticker}} at {{close}}")
  310. alertcondition(fired(weekly, 1), "SSMT Weekly - Low side", "SSMT+: bullish Weekly SSMT on {{ticker}} at {{close}}")
  311. alertcondition(fired(monthly, -1), "SSMT Monthly - High side", "SSMT+: bearish Monthly SSMT on {{ticker}} at {{close}}")
  312. alertcondition(fired(monthly, 1), "SSMT Monthly - Low side", "SSMT+: bullish Monthly SSMT on {{ticker}} at {{close}}")
  313.  
  314.  
Advertisement
Add Comment
Please, Sign In to add comment