Advertisement
ChrisBaker97

Omnibus Chart Labels - thinkScript Chart Study

Oct 24th, 2013
891
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.43 KB | None | 0 0
  1. # chart_labels.ts - CHART LABELS - Adds handy quick-reference labels to the chart
  2. #
  3. # This study provides the following functionality:
  4. #
  5. # • Implied Volatility Rank (IV Percentile)
  6. # • Price Rank (Price Percentile)
  7. # • Significant Recent Price Move
  8. # • Next Expiration Friday and DTE
  9. # • Optimal Expiration Friday (user-provided target DTE) and DTE
  10. # • Earnings Warning
  11. # • Dividend Warning
  12. #
  13. # Each label may be independently enabled or disabled. Various display
  14. # options are configurable for the Expiration Friday labels, such as
  15. # toggling display of day and/or month of expiration Friday. Labels are
  16. # color-coded to show proximity of the event (earnings and dividends)
  17. # or based on the relative position of events (expiration Fridays) or
  18. # depending on the actual value (IV & price ranks and significant price
  19. # move). Labels are decluttered automatically if there are no applicable
  20. # conditions or events.
  21. #
  22. # Additional details, including an explanation of the various colors,
  23. # are contained in the comments in each section below.
  24. #
  25. # By: Chris Baker <ChrisBaker97@gmail.com> twitter: @ChrisBaker97
  26. # The various studies collected here are also available in individual
  27. # modules at: https://bitbucket.org/ChrisBaker97/thinkscript
  28. #
  29. # This thinkScript is designed for use in the Charts tab.
  30. #
  31. # This code should be considered to be in beta status, and bugs are
  32. # likely. Date handing in thinkScript is notoriously lacking, and there
  33. # may be boundary cases that are unaccounted for or incorrectly coded.
  34. # I have not yet extensively tested this script against a range of potential
  35. # dates. Please report any bugs or anomalous behavior to me at the email
  36. # listed above.
  37. #
  38. # This work is licensed under the Creative Commons Attribution-ShareAlike
  39. # 3.0 Unported License. To view a copy of this license, visit:
  40. # http://creativecommons.org/licenses/by-sa/3.0/deed.en_US
  41.  
  42. declare upper ;
  43. declare once_per_bar ;
  44. declare hide_on_intraday ; # most functions are only relevant for daily aggregation
  45.  
  46. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  47. #
  48. # GLOBAL CONSTRUCTS
  49. #
  50. # The user input brightness can range from 0 to 100 and controls the
  51. # intensity of the plot's red-green color scheme. A lower brightness will
  52. # show up better on a light background, and vice versa.
  53.  
  54. input brightness = 80 ;
  55.  
  56. # Check bounds and convert brightness input to an intensity factor between 0.2 and 1.0
  57. def intensity = if brightness < 0 then 0.2
  58. else if brightness > 100 then 1.0
  59. else 0.2 + (brightness * 0.008) ;
  60.  
  61.  
  62. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  63. #
  64. # IMPLIED VOLATILITY RANK - Adds a colored label to the chart showing IV Rank
  65. #
  66. # This study simply places a label on the chart showing the current IV
  67. # Rank, otherwise known as IV Percentile. In addition, the label is
  68. # color-coded to hint at the IV Rank, where lower values appear red and
  69. # higher values are green, suggesting greater premium available to be
  70. # sold at a higher IV Rank. The period for IV calculation is the same as
  71. # the chart duration. This data may be nonexistant or unreliable with
  72. # other than a daily chart aggregation period.
  73. #
  74. # For a more complex visual presentation of IV Rank on the lower
  75. # subgraph, see my other thinkScript: http://pastebin.com/0Vumd8Gt
  76. #
  77. # Credit goes to Allen Everhart (http://www.smalldoginvestor.com) for the
  78. # original idea and implementation of IV Percentile as a chart label.
  79. #
  80. # Portions of this code are derived from the IV_percentile Scan tab
  81. # thinkScript included with the thinkorswim platform by TD Ameritrade.
  82.  
  83. input ShowIvRank = yes ;
  84.  
  85. # Clean the IV data to remove any gaps
  86.  
  87. def ivClean = if IsNaN(ImpVolatility()) # if IV data doesn't exist for a particular period ...
  88. then ivClean[1] # ... set it to the previous value so it won't taint the hi/lo
  89. else ImpVolatility() * 100 ; # TOS expresses IV as a decimal - convert it to a percent
  90.  
  91. def ivHi = HighestAll(ivClean) ; # highest IV over range
  92. def ivLo = LowestAll(ivClean) ; # lowest IV over range
  93.  
  94. def ivRank = Round( 100 * ( (ivClean - ivLo) / (ivHi - ivLo) ), 0) ;
  95.  
  96. # Define a color level from 0 to 255 based on current IV rank
  97. def ivLevel = ivRank * 2.55;
  98.  
  99. # Calculate red and green color levels (modified by intensity) for the color function
  100. def ivRed = intensity * (255 - ivLevel) ;
  101. def ivGrn = intensity * (ivLevel) ;
  102.  
  103. # There seems to be an error in thinkScript, where certain symbols throw a color value out of range error,
  104. # even when the ivRed and/or ivGrn values are verified well within range 0-255, when CreateColor() is called
  105. # directly from within AddLabel(). Unfortunately, in these cases, the entire study fails, and absolutely
  106. # nothing is displayed. As a workaround, I create a hidden plot, colored accordingly, and then steal that
  107. # color value to shade the label. So far, this seems to function in all cases.
  108. plot ivKludge = Double.NaN;
  109. ivKludge.AssignValueColor(CreateColor(ivRed, ivGrn, 0)) ; # color according to IV rank
  110. ivKludge.Hide() ;
  111. ivKludge.HideTitle() ;
  112.  
  113. # Add label, colored according to current IV rank.
  114. AddLabel(ShowIvRank, "IV Rank: " + ivRank + "%", ivKludge.TakeValueColor()) ;
  115.  
  116.  
  117. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  118. #
  119. # PRICE RANK - Adds a colored label to the chart showing Price Rank
  120. #
  121. # Similar to IV Rank, this displays at a glance where the current price is in
  122. # relation to its range over the entire chart period. The color-coding is
  123. # identical to IV Rank.
  124.  
  125. input ShowPriceRank = yes ;
  126.  
  127. # Clean the price data to remove any gaps
  128. def pClean = if IsNaN(close) # if price data doesn't exist for a particular period ...
  129. then pClean[1] # ... set it to the previous value so it won't taint the hi/lo
  130. else close ;
  131.  
  132. def pHi = HighestAll(pClean) ; # highest price over range
  133. def pLo = LowestAll(pClean) ; # lowest price over range
  134.  
  135. def pRank = Round( 100 * ( (pClean - pLo) / (pHi - pLo) ), 0) ;
  136.  
  137. # Define a color level from 0 to 255 based on current price rank
  138. def pLevel = pRank * 2.55;
  139.  
  140. # Calculate red and green color levels (modified by intensity) for the color function
  141. def pRed = intensity * (255 - pLevel) ;
  142. def pGrn = intensity * (pLevel) ;
  143.  
  144. # There seems to be an error in thinkScript, where certain symbols throw a color value out of range error,
  145. # even when the ivRed and/or ivGrn values are verified well within range 0-255, when CreateColor() is called
  146. # directly from within AddLabel(). Unfortunately, in these cases, the entire study fails, and absolutely
  147. # nothing is displayed. As a workaround, I create a hidden plot, colored accordingly, and then steal that
  148. # color value to shade the label. So far, this seems to function in all cases.
  149. plot pKludge = Double.NaN;
  150. pKludge.AssignValueColor(CreateColor(pRed, pGrn, 0)) ; # color according to price Rank
  151. pKludge.Hide() ;
  152. pKludge.HideTitle() ;
  153.  
  154. # Add label, colored according to current IV Rank.
  155. AddLabel(ShowPriceRank, "Price Rank: " + pRank + "%", pKludge.TakeValueColor()) ;
  156.  
  157.  
  158. # SHOW SIGNIFICANT PRICE MOVE - pricePercent
  159. #
  160. # If the underlying has moved more than a specified amount (default 5%) in the last few (default 10) bars,
  161. # highlight that fact with a color-coded Percent Price Move label.
  162. #
  163. # Adapted from code by Shah H. Development 2013
  164.  
  165. input ShowPricePercent = yes ;
  166. input PricePercentBars = 10 ;
  167. input PricePercentType = ohlc4 ;
  168. input PricePercentTrigger = 5.0 ;
  169.  
  170. def pricePercentChg = Round( 100 * ( PricePercentType / PricePercentType[PricePercentBars] - 1 ), 1 ) ;
  171.  
  172. AddLabel( ShowPricePercent and ( AbsValue(pricePercentChg) > PricePercentTrigger ),
  173. PricePercentBars + "-Bar Price Move: " + pricePercentChg + "%",
  174. if pricePercentChg > 0 then Color.UPTICK else Color.DOWNTICK ) ;
  175.  
  176. # NEXT AND OPTIMAL EXPIRATION DATES
  177.  
  178. input ShowFrontDTE = yes ;
  179. input ShowOptimalDTE = yes ;
  180. input ShowExpDay = no ;
  181. input ShowExpMonth = yes ;
  182. input OptimalDTE = 40 ;
  183.  
  184. # Test code. Leave commented out for normal usage.
  185. # # # # # # # # # # # # # # # # # # # # # # # # # # #
  186. # def day = 19 ;
  187. # def month1 = 12 ;
  188. # def year1 = 2013 ;
  189. # # # # # # # # # # # # # # # # # # # # # # # # # # #
  190.  
  191. # Production code. Comment out to test other dates.
  192. def day1 = GetDayOfMonth(GetYYYYMMDD()) ; # current date (DD)
  193. def month1 = GetMonth() ; # current month (MM)
  194. def year1 = GetYear() ; # current year (YYYY)
  195.  
  196. def month2 = ( month1 % 12 ) + 1 ; # next month
  197. def year2 = year1 + RoundDown(month1 / 12, 0) ; # next month's year
  198.  
  199. def month3 = ( month2 % 12 ) + 1 ; # subsequent month
  200. def year3 = year2 + RoundDown(month2 / 12, 0) ; # subsequent month's year
  201.  
  202. def month4 = ( month3 % 12 ) + 1 ;
  203. def year4 = year3 + RoundDown(month3 / 12, 0) ;
  204.  
  205. # Calculate days of the week for the first days of the months.
  206. def firstOfMonthDayOfWeek1 = GetDayOfWeek( ( year1 * 10000 ) + ( month1 * 100 ) + 1 ) % 7 ;
  207. def firstOfMonthDayOfWeek2 = GetDayOfWeek( ( year2 * 10000 ) + ( month2 * 100 ) + 1 ) % 7 ;
  208. def firstOfMonthDayOfWeek3 = GetDayOfWeek( ( year3 * 10000 ) + ( month3 * 100 ) + 1 ) % 7 ;
  209. def firstOfMonthDayOfWeek4 = GetDayOfWeek( ( year4 * 10000 ) + ( month4 * 100 ) + 1 ) % 7 ;
  210.  
  211. # Calculate days of the month for third Fridays of the months.
  212. def thirdFriday1 = 20 - firstOfMonthDayOfWeek1 ;
  213. def thirdFriday2 = 20 - firstOfMonthDayOfWeek2 ;
  214. def thirdFriday3 = 20 - firstOfMonthDayOfWeek3 ;
  215. def thirdFriday4 = 20 - firstOfMonthDayOfWeek4 ;
  216.  
  217. # Calculate expiration Fridays.
  218. def expFriday1 = ( year1 * 10000 ) + ( month1 * 100 ) + thirdFriday1 ;
  219. def expFriday2 = ( year2 * 10000 ) + ( month2 * 100 ) + thirdFriday2 ;
  220. def expFriday3 = ( year3 * 10000 ) + ( month3 * 100 ) + thirdFriday3 ;
  221. def expFriday4 = ( year4 * 10000 ) + ( month4 * 100 ) + thirdFriday4 ;
  222.  
  223. # Determine if we've reached expiration Friday this month already.
  224. def beforeExp = if thirdFriday1 > day1 then yes else no ;
  225.  
  226. # Calculate the next three expiration Fridays.
  227. def firstExp = if beforeExp then expFriday1 else expFriday2 ;
  228. def secondExp = if beforeExp then expFriday2 else expFriday3 ;
  229. def thirdExp = if beforeExp then expFriday3 else expFriday4 ;
  230.  
  231. # Calculate days to go until each expiration.
  232. def firstExpDist = DaysTillDate( firstExp) ;
  233. def secondExpDist = DaysTillDate(secondExp) ;
  234. def thirdExpDist = DaysTillDate( thirdExp) ;
  235.  
  236. # Calculate absolute proximity of each expiration to the optimalDTE.
  237. def firstExpVariance = AbsValue(OptimalDTE - firstExpDist) ;
  238. def secondExpVariance = AbsValue(OptimalDTE - secondExpDist) ;
  239. def thirdExpVariance = AbsValue(OptimalDTE - thirdExpDist) ;
  240.  
  241. # Pick the closest one, above or below. (Tie goes to the shorter expiration cycle.)
  242. def optimalExp = if firstExpVariance < secondExpVariance then firstExp
  243. else if secondExpVariance < thirdExpVariance then secondExp
  244. else thirdExp ; # Rare, but possible, depending on optimal DTE
  245.  
  246. # Recalculate days to go until chosen expiration Friday.
  247. def optimalExpDist = DaysTillDate(optimalExp) ;
  248.  
  249. # Break down expiration Fridays YYYYMMDD back into component parts.
  250. def firstExpYear = RoundDown(firstExp / 10000, 0);
  251. def firstExpMonth = RoundDown(( firstExp - firstExpYear * 10000 ) / 100, 0) ;
  252. def firstExpDay = firstExp - firstExpYear * 10000 - firstExpMonth * 100 ;
  253.  
  254. def optimalExpYear = RoundDown(optimalExp / 10000, 0);
  255. def optimalExpMonth = RoundDown(( optimalExp - optimalExpYear * 10000 ) / 100, 0) ;
  256. def optimalExpDay = optimalExp - optimalExpYear * 10000 - optimalExpMonth * 100 ;
  257.  
  258. # Label display code appears after earnings and dividend calculations, below.
  259.  
  260.  
  261. # EARNINGS
  262.  
  263. input ShowEarnings = yes ;
  264.  
  265. # Determine latest earnings date. If earnings occurs before market open, then the effective
  266. # earnings date is set to the previous trading day. For example, if XYZ has earnings on
  267. # Wednesday morning, its earnings date is set to Tuesday. And, if ABC has earnings Monday
  268. # morning, its earnings date is set to Friday, as this is the last opportunity to trade
  269. # the options prior to earnings release. Performing the adjustment here keeps the
  270. # subsequent code cleaner.
  271. def nextEarningsDate = HighestAll( if HasEarnings(EarningTime.AFTER_MARKET ) then GetYYYYMMDD()
  272. else if HasEarnings(EarningTime.BEFORE_MARKET) then GetYYYYMMDD()[1]
  273. else 19700101 ) ;
  274.  
  275. # Calculate days until earnings. If earnings have passed, or there are no upcoming earnings
  276. # data, set to NaN. (This includes stocks whose earnings were already released this morning.)
  277. def daysTillEarnings = if DaysTillDate(nextEarningsDate) < 0 then Double.NaN
  278. else DaysTillDate(nextEarningsDate) ;
  279.  
  280. # Generate comparison distance to earnings for color-coding. If earnings isn't a factor, we use
  281. # optimal expiration distnce + 1.
  282. def earningsDist = if isNaN(daysTillEarnings) then optimalExpDist + 1 else daysTillEarnings ;
  283.  
  284.  
  285. # Display of earnings data come after expiration data.
  286.  
  287.  
  288. # DIVIDENDS
  289.  
  290. input ShowDividends = yes ;
  291.  
  292. def nextDivDate = HighestAll( if !IsNaN(GetDividend()) then GetYYYYMMDD() else 19700101 ) ;
  293.  
  294. def daysTillDiv = if DaysTillDate(nextDivDate) < 0 then Double.NaN
  295. else DaysTillDate(nextDivDate) ;
  296.  
  297. # Generate comparison distance to dividend for color-coding. If dividend isn't a factor, we use
  298. # optimal expiration distnce + 1.
  299. def divDist = if isNaN(daysTillDiv ) then optimalExpDist + 1 else daysTillDiv ;
  300.  
  301. # Display of dividend data come after expiration data.
  302.  
  303.  
  304. # DISPLAY OPTION EXPIRATION, EARNINGS, AND DIVIDEND LABELS
  305.  
  306. # Construct the next expiration label, including friendly month names (and DTE).
  307. AddLabel(ShowFrontDTE and firstExp != optimalExp, "Front: " +
  308. ( if ShowExpDay and ShowExpMonth then Concat(firstExpDay, "") else "" ) +
  309. ( if ShowExpMonth then
  310. ( if firstExpMonth == 1 then "JAN ("
  311. else if firstExpMonth == 2 then "FEB ("
  312. else if firstExpMonth == 3 then "MAR ("
  313. else if firstExpMonth == 4 then "APR ("
  314. else if firstExpMonth == 5 then "MAY ("
  315. else if firstExpMonth == 6 then "JUN ("
  316. else if firstExpMonth == 7 then "JUL ("
  317. else if firstExpMonth == 8 then "AUG ("
  318. else if firstExpMonth == 9 then "SEP ("
  319. else if firstExpMonth == 10 then "OCT ("
  320. else if firstExpMonth == 11 then "NOV ("
  321. else "DEC (" )
  322. else "" ) +
  323. firstExpDist + if ShowExpMonth then ")" else "",
  324. if earningsDist == 0 then Color.MAGENTA
  325. else if earningsDist < firstExpDist then Color.LIGHT_RED
  326. else if divDist < firstExpDist then Color.YELLOW
  327. else Color.GREEN ) ;
  328.  
  329. # Construct the optimal expiration label, including friendly month names (and DTE).
  330. AddLabel(ShowOptimalDTE, OptimalDTE + "-Day: " +
  331. ( if ShowExpDay and ShowExpMonth then Concat(optimalExpDay, "") else "" ) +
  332. ( if ShowExpMonth then
  333. ( if optimalExpMonth == 1 then "JAN ("
  334. else if optimalExpMonth == 2 then "FEB ("
  335. else if optimalExpMonth == 3 then "MAR ("
  336. else if optimalExpMonth == 4 then "APR ("
  337. else if optimalExpMonth == 5 then "MAY ("
  338. else if optimalExpMonth == 6 then "JUN ("
  339. else if optimalExpMonth == 7 then "JUL ("
  340. else if optimalExpMonth == 8 then "AUG ("
  341. else if optimalExpMonth == 9 then "SEP ("
  342. else if optimalExpMonth == 10 then "OCT ("
  343. else if optimalExpMonth == 11 then "NOV ("
  344. else "DEC (" )
  345. else "" ) +
  346. optimalExpDist + if ShowExpMonth then ")" else "",
  347. if earningsDist == 0 then Color.MAGENTA
  348. else if earningsDist < optimalExpDist then Color.LIGHT_RED
  349. else if divDist < firstExpDist and firstExpDist - divDist < 14 then Color.YELLOW
  350. else Color.GREEN ) ;
  351.  
  352. # Construct the earnings label
  353. AddLabel(ShowEarnings and daysTillEarnings >= 0, "Earn: " + daysTillEarnings,
  354. if daysTillEarnings == 0 then Color.MAGENTA # today(!)
  355. else if daysTillEarnings < 7 then Color.LIGHT_RED # in the next week
  356. else if daysTillEarnings < firstExpDist then Color.DARK_ORANGE # first expiration
  357. else if daysTillEarnings < optimalExpDist then Color.YELLOW # optimal expiration
  358. else Color.GREEN ) ;
  359.  
  360. # Construct the dividend label
  361. AddLabel(ShowDividends and daysTillDiv, "Div: " + daysTillDiv,
  362. if daysTillDiv == 0 then Color.MAGENTA # today(!)
  363. else if daysTillDiv < 7 then Color.LIGHT_RED # in the next week
  364. else if daysTillDiv < firstExpDist then Color.DARK_ORANGE # first expiration
  365. else if daysTillDiv < optimalExpDist then Color.YELLOW # optimal expiration
  366. else Color.GREEN ) ;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement