Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # chart_labels.ts - CHART LABELS - Adds handy quick-reference labels to the chart
- #
- # This study provides the following functionality:
- #
- # • Implied Volatility Rank (IV Percentile)
- # • Price Rank (Price Percentile)
- # • Significant Recent Price Move
- # • Next Expiration Friday and DTE
- # • Optimal Expiration Friday (user-provided target DTE) and DTE
- # • Earnings Warning
- # • Dividend Warning
- #
- # Each label may be independently enabled or disabled. Various display
- # options are configurable for the Expiration Friday labels, such as
- # toggling display of day and/or month of expiration Friday. Labels are
- # color-coded to show proximity of the event (earnings and dividends)
- # or based on the relative position of events (expiration Fridays) or
- # depending on the actual value (IV & price ranks and significant price
- # move). Labels are decluttered automatically if there are no applicable
- # conditions or events.
- #
- # Additional details, including an explanation of the various colors,
- # are contained in the comments in each section below.
- #
- # By: Chris Baker <ChrisBaker97@gmail.com> twitter: @ChrisBaker97
- # The various studies collected here are also available in individual
- # modules at: https://bitbucket.org/ChrisBaker97/thinkscript
- #
- # This thinkScript is designed for use in the Charts tab.
- #
- # This code should be considered to be in beta status, and bugs are
- # likely. Date handing in thinkScript is notoriously lacking, and there
- # may be boundary cases that are unaccounted for or incorrectly coded.
- # I have not yet extensively tested this script against a range of potential
- # dates. Please report any bugs or anomalous behavior to me at the email
- # listed above.
- #
- # This work is licensed under the Creative Commons Attribution-ShareAlike
- # 3.0 Unported License. To view a copy of this license, visit:
- # http://creativecommons.org/licenses/by-sa/3.0/deed.en_US
- declare upper ;
- declare once_per_bar ;
- declare hide_on_intraday ; # most functions are only relevant for daily aggregation
- # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- #
- # GLOBAL CONSTRUCTS
- #
- # The user input brightness can range from 0 to 100 and controls the
- # intensity of the plot's red-green color scheme. A lower brightness will
- # show up better on a light background, and vice versa.
- input brightness = 80 ;
- # Check bounds and convert brightness input to an intensity factor between 0.2 and 1.0
- def intensity = if brightness < 0 then 0.2
- else if brightness > 100 then 1.0
- else 0.2 + (brightness * 0.008) ;
- # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- #
- # IMPLIED VOLATILITY RANK - Adds a colored label to the chart showing IV Rank
- #
- # This study simply places a label on the chart showing the current IV
- # Rank, otherwise known as IV Percentile. In addition, the label is
- # color-coded to hint at the IV Rank, where lower values appear red and
- # higher values are green, suggesting greater premium available to be
- # sold at a higher IV Rank. The period for IV calculation is the same as
- # the chart duration. This data may be nonexistant or unreliable with
- # other than a daily chart aggregation period.
- #
- # For a more complex visual presentation of IV Rank on the lower
- # subgraph, see my other thinkScript: http://pastebin.com/0Vumd8Gt
- #
- # Credit goes to Allen Everhart (http://www.smalldoginvestor.com) for the
- # original idea and implementation of IV Percentile as a chart label.
- #
- # Portions of this code are derived from the IV_percentile Scan tab
- # thinkScript included with the thinkorswim platform by TD Ameritrade.
- input ShowIvRank = yes ;
- # Clean the IV data to remove any gaps
- def ivClean = if IsNaN(ImpVolatility()) # if IV data doesn't exist for a particular period ...
- then ivClean[1] # ... set it to the previous value so it won't taint the hi/lo
- else ImpVolatility() * 100 ; # TOS expresses IV as a decimal - convert it to a percent
- def ivHi = HighestAll(ivClean) ; # highest IV over range
- def ivLo = LowestAll(ivClean) ; # lowest IV over range
- def ivRank = Round( 100 * ( (ivClean - ivLo) / (ivHi - ivLo) ), 0) ;
- # Define a color level from 0 to 255 based on current IV rank
- def ivLevel = ivRank * 2.55;
- # Calculate red and green color levels (modified by intensity) for the color function
- def ivRed = intensity * (255 - ivLevel) ;
- def ivGrn = intensity * (ivLevel) ;
- # There seems to be an error in thinkScript, where certain symbols throw a color value out of range error,
- # even when the ivRed and/or ivGrn values are verified well within range 0-255, when CreateColor() is called
- # directly from within AddLabel(). Unfortunately, in these cases, the entire study fails, and absolutely
- # nothing is displayed. As a workaround, I create a hidden plot, colored accordingly, and then steal that
- # color value to shade the label. So far, this seems to function in all cases.
- plot ivKludge = Double.NaN;
- ivKludge.AssignValueColor(CreateColor(ivRed, ivGrn, 0)) ; # color according to IV rank
- ivKludge.Hide() ;
- ivKludge.HideTitle() ;
- # Add label, colored according to current IV rank.
- AddLabel(ShowIvRank, "IV Rank: " + ivRank + "%", ivKludge.TakeValueColor()) ;
- # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- #
- # PRICE RANK - Adds a colored label to the chart showing Price Rank
- #
- # Similar to IV Rank, this displays at a glance where the current price is in
- # relation to its range over the entire chart period. The color-coding is
- # identical to IV Rank.
- input ShowPriceRank = yes ;
- # Clean the price data to remove any gaps
- def pClean = if IsNaN(close) # if price data doesn't exist for a particular period ...
- then pClean[1] # ... set it to the previous value so it won't taint the hi/lo
- else close ;
- def pHi = HighestAll(pClean) ; # highest price over range
- def pLo = LowestAll(pClean) ; # lowest price over range
- def pRank = Round( 100 * ( (pClean - pLo) / (pHi - pLo) ), 0) ;
- # Define a color level from 0 to 255 based on current price rank
- def pLevel = pRank * 2.55;
- # Calculate red and green color levels (modified by intensity) for the color function
- def pRed = intensity * (255 - pLevel) ;
- def pGrn = intensity * (pLevel) ;
- # There seems to be an error in thinkScript, where certain symbols throw a color value out of range error,
- # even when the ivRed and/or ivGrn values are verified well within range 0-255, when CreateColor() is called
- # directly from within AddLabel(). Unfortunately, in these cases, the entire study fails, and absolutely
- # nothing is displayed. As a workaround, I create a hidden plot, colored accordingly, and then steal that
- # color value to shade the label. So far, this seems to function in all cases.
- plot pKludge = Double.NaN;
- pKludge.AssignValueColor(CreateColor(pRed, pGrn, 0)) ; # color according to price Rank
- pKludge.Hide() ;
- pKludge.HideTitle() ;
- # Add label, colored according to current IV Rank.
- AddLabel(ShowPriceRank, "Price Rank: " + pRank + "%", pKludge.TakeValueColor()) ;
- # SHOW SIGNIFICANT PRICE MOVE - pricePercent
- #
- # If the underlying has moved more than a specified amount (default 5%) in the last few (default 10) bars,
- # highlight that fact with a color-coded Percent Price Move label.
- #
- # Adapted from code by Shah H. Development 2013
- input ShowPricePercent = yes ;
- input PricePercentBars = 10 ;
- input PricePercentType = ohlc4 ;
- input PricePercentTrigger = 5.0 ;
- def pricePercentChg = Round( 100 * ( PricePercentType / PricePercentType[PricePercentBars] - 1 ), 1 ) ;
- AddLabel( ShowPricePercent and ( AbsValue(pricePercentChg) > PricePercentTrigger ),
- PricePercentBars + "-Bar Price Move: " + pricePercentChg + "%",
- if pricePercentChg > 0 then Color.UPTICK else Color.DOWNTICK ) ;
- # NEXT AND OPTIMAL EXPIRATION DATES
- input ShowFrontDTE = yes ;
- input ShowOptimalDTE = yes ;
- input ShowExpDay = no ;
- input ShowExpMonth = yes ;
- input OptimalDTE = 40 ;
- # Test code. Leave commented out for normal usage.
- # # # # # # # # # # # # # # # # # # # # # # # # # # #
- # def day = 19 ;
- # def month1 = 12 ;
- # def year1 = 2013 ;
- # # # # # # # # # # # # # # # # # # # # # # # # # # #
- # Production code. Comment out to test other dates.
- def day1 = GetDayOfMonth(GetYYYYMMDD()) ; # current date (DD)
- def month1 = GetMonth() ; # current month (MM)
- def year1 = GetYear() ; # current year (YYYY)
- def month2 = ( month1 % 12 ) + 1 ; # next month
- def year2 = year1 + RoundDown(month1 / 12, 0) ; # next month's year
- def month3 = ( month2 % 12 ) + 1 ; # subsequent month
- def year3 = year2 + RoundDown(month2 / 12, 0) ; # subsequent month's year
- def month4 = ( month3 % 12 ) + 1 ;
- def year4 = year3 + RoundDown(month3 / 12, 0) ;
- # Calculate days of the week for the first days of the months.
- def firstOfMonthDayOfWeek1 = GetDayOfWeek( ( year1 * 10000 ) + ( month1 * 100 ) + 1 ) % 7 ;
- def firstOfMonthDayOfWeek2 = GetDayOfWeek( ( year2 * 10000 ) + ( month2 * 100 ) + 1 ) % 7 ;
- def firstOfMonthDayOfWeek3 = GetDayOfWeek( ( year3 * 10000 ) + ( month3 * 100 ) + 1 ) % 7 ;
- def firstOfMonthDayOfWeek4 = GetDayOfWeek( ( year4 * 10000 ) + ( month4 * 100 ) + 1 ) % 7 ;
- # Calculate days of the month for third Fridays of the months.
- def thirdFriday1 = 20 - firstOfMonthDayOfWeek1 ;
- def thirdFriday2 = 20 - firstOfMonthDayOfWeek2 ;
- def thirdFriday3 = 20 - firstOfMonthDayOfWeek3 ;
- def thirdFriday4 = 20 - firstOfMonthDayOfWeek4 ;
- # Calculate expiration Fridays.
- def expFriday1 = ( year1 * 10000 ) + ( month1 * 100 ) + thirdFriday1 ;
- def expFriday2 = ( year2 * 10000 ) + ( month2 * 100 ) + thirdFriday2 ;
- def expFriday3 = ( year3 * 10000 ) + ( month3 * 100 ) + thirdFriday3 ;
- def expFriday4 = ( year4 * 10000 ) + ( month4 * 100 ) + thirdFriday4 ;
- # Determine if we've reached expiration Friday this month already.
- def beforeExp = if thirdFriday1 > day1 then yes else no ;
- # Calculate the next three expiration Fridays.
- def firstExp = if beforeExp then expFriday1 else expFriday2 ;
- def secondExp = if beforeExp then expFriday2 else expFriday3 ;
- def thirdExp = if beforeExp then expFriday3 else expFriday4 ;
- # Calculate days to go until each expiration.
- def firstExpDist = DaysTillDate( firstExp) ;
- def secondExpDist = DaysTillDate(secondExp) ;
- def thirdExpDist = DaysTillDate( thirdExp) ;
- # Calculate absolute proximity of each expiration to the optimalDTE.
- def firstExpVariance = AbsValue(OptimalDTE - firstExpDist) ;
- def secondExpVariance = AbsValue(OptimalDTE - secondExpDist) ;
- def thirdExpVariance = AbsValue(OptimalDTE - thirdExpDist) ;
- # Pick the closest one, above or below. (Tie goes to the shorter expiration cycle.)
- def optimalExp = if firstExpVariance < secondExpVariance then firstExp
- else if secondExpVariance < thirdExpVariance then secondExp
- else thirdExp ; # Rare, but possible, depending on optimal DTE
- # Recalculate days to go until chosen expiration Friday.
- def optimalExpDist = DaysTillDate(optimalExp) ;
- # Break down expiration Fridays YYYYMMDD back into component parts.
- def firstExpYear = RoundDown(firstExp / 10000, 0);
- def firstExpMonth = RoundDown(( firstExp - firstExpYear * 10000 ) / 100, 0) ;
- def firstExpDay = firstExp - firstExpYear * 10000 - firstExpMonth * 100 ;
- def optimalExpYear = RoundDown(optimalExp / 10000, 0);
- def optimalExpMonth = RoundDown(( optimalExp - optimalExpYear * 10000 ) / 100, 0) ;
- def optimalExpDay = optimalExp - optimalExpYear * 10000 - optimalExpMonth * 100 ;
- # Label display code appears after earnings and dividend calculations, below.
- # EARNINGS
- input ShowEarnings = yes ;
- # Determine latest earnings date. If earnings occurs before market open, then the effective
- # earnings date is set to the previous trading day. For example, if XYZ has earnings on
- # Wednesday morning, its earnings date is set to Tuesday. And, if ABC has earnings Monday
- # morning, its earnings date is set to Friday, as this is the last opportunity to trade
- # the options prior to earnings release. Performing the adjustment here keeps the
- # subsequent code cleaner.
- def nextEarningsDate = HighestAll( if HasEarnings(EarningTime.AFTER_MARKET ) then GetYYYYMMDD()
- else if HasEarnings(EarningTime.BEFORE_MARKET) then GetYYYYMMDD()[1]
- else 19700101 ) ;
- # Calculate days until earnings. If earnings have passed, or there are no upcoming earnings
- # data, set to NaN. (This includes stocks whose earnings were already released this morning.)
- def daysTillEarnings = if DaysTillDate(nextEarningsDate) < 0 then Double.NaN
- else DaysTillDate(nextEarningsDate) ;
- # Generate comparison distance to earnings for color-coding. If earnings isn't a factor, we use
- # optimal expiration distnce + 1.
- def earningsDist = if isNaN(daysTillEarnings) then optimalExpDist + 1 else daysTillEarnings ;
- # Display of earnings data come after expiration data.
- # DIVIDENDS
- input ShowDividends = yes ;
- def nextDivDate = HighestAll( if !IsNaN(GetDividend()) then GetYYYYMMDD() else 19700101 ) ;
- def daysTillDiv = if DaysTillDate(nextDivDate) < 0 then Double.NaN
- else DaysTillDate(nextDivDate) ;
- # Generate comparison distance to dividend for color-coding. If dividend isn't a factor, we use
- # optimal expiration distnce + 1.
- def divDist = if isNaN(daysTillDiv ) then optimalExpDist + 1 else daysTillDiv ;
- # Display of dividend data come after expiration data.
- # DISPLAY OPTION EXPIRATION, EARNINGS, AND DIVIDEND LABELS
- # Construct the next expiration label, including friendly month names (and DTE).
- AddLabel(ShowFrontDTE and firstExp != optimalExp, "Front: " +
- ( if ShowExpDay and ShowExpMonth then Concat(firstExpDay, "") else "" ) +
- ( if ShowExpMonth then
- ( if firstExpMonth == 1 then "JAN ("
- else if firstExpMonth == 2 then "FEB ("
- else if firstExpMonth == 3 then "MAR ("
- else if firstExpMonth == 4 then "APR ("
- else if firstExpMonth == 5 then "MAY ("
- else if firstExpMonth == 6 then "JUN ("
- else if firstExpMonth == 7 then "JUL ("
- else if firstExpMonth == 8 then "AUG ("
- else if firstExpMonth == 9 then "SEP ("
- else if firstExpMonth == 10 then "OCT ("
- else if firstExpMonth == 11 then "NOV ("
- else "DEC (" )
- else "" ) +
- firstExpDist + if ShowExpMonth then ")" else "",
- if earningsDist == 0 then Color.MAGENTA
- else if earningsDist < firstExpDist then Color.LIGHT_RED
- else if divDist < firstExpDist then Color.YELLOW
- else Color.GREEN ) ;
- # Construct the optimal expiration label, including friendly month names (and DTE).
- AddLabel(ShowOptimalDTE, OptimalDTE + "-Day: " +
- ( if ShowExpDay and ShowExpMonth then Concat(optimalExpDay, "") else "" ) +
- ( if ShowExpMonth then
- ( if optimalExpMonth == 1 then "JAN ("
- else if optimalExpMonth == 2 then "FEB ("
- else if optimalExpMonth == 3 then "MAR ("
- else if optimalExpMonth == 4 then "APR ("
- else if optimalExpMonth == 5 then "MAY ("
- else if optimalExpMonth == 6 then "JUN ("
- else if optimalExpMonth == 7 then "JUL ("
- else if optimalExpMonth == 8 then "AUG ("
- else if optimalExpMonth == 9 then "SEP ("
- else if optimalExpMonth == 10 then "OCT ("
- else if optimalExpMonth == 11 then "NOV ("
- else "DEC (" )
- else "" ) +
- optimalExpDist + if ShowExpMonth then ")" else "",
- if earningsDist == 0 then Color.MAGENTA
- else if earningsDist < optimalExpDist then Color.LIGHT_RED
- else if divDist < firstExpDist and firstExpDist - divDist < 14 then Color.YELLOW
- else Color.GREEN ) ;
- # Construct the earnings label
- AddLabel(ShowEarnings and daysTillEarnings >= 0, "Earn: " + daysTillEarnings,
- if daysTillEarnings == 0 then Color.MAGENTA # today(!)
- else if daysTillEarnings < 7 then Color.LIGHT_RED # in the next week
- else if daysTillEarnings < firstExpDist then Color.DARK_ORANGE # first expiration
- else if daysTillEarnings < optimalExpDist then Color.YELLOW # optimal expiration
- else Color.GREEN ) ;
- # Construct the dividend label
- AddLabel(ShowDividends and daysTillDiv, "Div: " + daysTillDiv,
- if daysTillDiv == 0 then Color.MAGENTA # today(!)
- else if daysTillDiv < 7 then Color.LIGHT_RED # in the next week
- else if daysTillDiv < firstExpDist then Color.DARK_ORANGE # first expiration
- else if daysTillDiv < optimalExpDist then Color.YELLOW # optimal expiration
- else Color.GREEN ) ;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement