vol_signal
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © voided

// USAGE
//
// This script gives signals based on a volatility forecast, e.g. for a stop
// loss. It is a simplified version of my other script "trend_vol_forecast", 
// which incorporates a trend following system and measures performance. The "X"
// labels indicate when the price touches (exceeds) a forecast. The signal 
// occurs when price crosses "fcst_up" or "fcst_down".
//
// There are only three parameters:
//
//      -   volatility window: this is the number of periods (bars) used in the
//          historical volatility calculation. smaller number = reacts more
//          quickly to changes, but is a "noisier" signal.
//      -   forecast periods: the number of periods for projecting a volatility
//          forecast. for example, "21" on a daily chart means the plots will 
//          show the forecast from 21 days ago.
//      -   forecast stdev: the number of standard deviations in the forecast.
//          for example, "2" means that price is expected to remain within
//          the forecast plot ~95% of the time. A higher number produces a
//          wider forecast.
//
// The output table shows:
//
//      -   realized vol: the volatility over the previous N periods, where N =
//          "volatility window".
//      -   forecast vol: the realized volatility from N periods ago, where N =
//          "forecast periods"
//      -   fcst_up/fcst_down (level): the price level of the forecast for the 
//          next N bars, where N = "forecast periods".
//      -   fcst_up/fcst_down (%): the difference between the current and 
//          forecast price, expressed as a whole number percentage.
//
// The plots show:
//
//      -   blue/red plot: the upper/lower forecast from "forecast periods" ago.
//      -   blue/red line: the upper/lower forecast for the next 
//          "forecast periods".
//      -   red/blue labels: an "X" where the price touched the forecast from
//          "forecast periods" ago.
//              +   NOTE: pinescript only draws a limited number of labels.
//                  They will not appear very far into the past.
//                  
//

//@version=4
study("vol_signal", overlay = true)

// INPUTS
vol_window = input(title = "volatility window", type = input.integer, defval = 30)
fcst_periods = input(title = "forecast periods", type = input.float, defval = 21)
fcst_stdev = input(title = "forecast stdev", type = input.float, defval = 2)

// PROGRAM
r = log(close / close[1])
pd = time - time[1]
ppy = (365 * 24 * 60 * 60 * 1000) / pd
hv = sqrt(ema(pow(r, 2), vol_window) * ppy)

fcst = hv * fcst_stdev * sqrt(fcst_periods / ppy)
fcst_up = close[fcst_periods] * (1 + fcst[fcst_periods])
fcst_up_pct = abs((fcst_up - close) / close) * 100
fcst_down = close[fcst_periods] * (1 - fcst[fcst_periods])
fcst_down_pct = abs((fcst_down - close) / close) * 100

// OUTPUT
if barstate.islast
    // table
    fmt = "#.###"
    
    cur_fcst_up = close * (1 + fcst)
    cur_fcst_down = close * (1 - fcst)
    
    fcst_ln_up = line.new(time, cur_fcst_up, int(time + fcst_periods * pd), cur_fcst_up, xloc = xloc.bar_time, color = color.blue)
    fcst_ln_down = line.new(time, cur_fcst_down, int(time + fcst_periods * pd), cur_fcst_down, xloc = xloc.bar_time, color = color.red)
    
    var ot = table.new(position.top_right, 2, 6)
    
    table.cell(ot, 0, 0, "realized vol", bgcolor = color.new(color.green, 60))
    table.cell(ot, 1, 0, tostring(hv * 100, fmt), bgcolor = color.new(color.green, 80))
    
    table.cell(ot, 0, 1, "forecast vol", bgcolor = color.new(color.green, 60))
    table.cell(ot, 1, 1, tostring(hv[fcst_periods] * 100, fmt), bgcolor = color.new(color.green, 80))

    table.cell(ot, 0, 2, "fcst_up", bgcolor = color.new(color.blue, 60))
    table.cell(ot, 1, 2, tostring(fcst_up, fmt), bgcolor = color.new(color.blue, 80))

    table.cell(ot, 0, 3, "fcst_up_pct", bgcolor = color.new(color.blue, 60))
    table.cell(ot, 1, 3, tostring(fcst_up_pct,fmt), bgcolor = color.new(color.blue, 80))
    
    table.cell(ot, 0, 4, "fcst_down", bgcolor = color.new(color.red, 60))
    table.cell(ot, 1, 4, tostring(fcst_down, fmt), bgcolor = color.new(color.red, 80))
    
    table.cell(ot, 0, 5, "fcst_down_pct", bgcolor = color.new(color.red, 60))
    table.cell(ot, 1, 5, tostring(fcst_down_pct, fmt), bgcolor = color.new(color.red, 80))
    
// plots and labels
plot(series = fcst_up, title = "fcst_up", color = color.blue, style = plot.style_stepline)
plot(series = fcst_down, title = "fcst_down", color = color.red, style = plot.style_stepline)
plot(fcst_up_pct, title = "fcst_up_pct", color = na, display = display.none)
plot(fcst_down_pct, title = "fcst_down_pct", color = na, display = display.none)

if low < fcst_down
    label.new(bar_index, na, "x", yloc = yloc.belowbar, style = label.style_none, textcolor = color.red)
else if high > fcst_up
    label.new(bar_index, na, "x", yloc = yloc.abovebar, style = label.style_none, textcolor = color.blue)