Trendespresso Multi-Timeframe Potato Signal
Trendespresso Multi-Timeframe Potato Signal
Trendespresso Multi-Timeframe Potato Signal
0 at
https://mozilla.org/MPL/2.0/
//
// if you want to reach me and I do not respond on TradingView, google my username
//
// I also have other private Indicators I'm always working on
// Feel free to reach out to me here or on the Baked Potato server for info and
links!
// All comments, bugfixes, and suggestions welcome :)
//
// © trendespresso
//@version=4
// WOLFPACK CREDITS
// credit to darrellfischer1 for the original wolfpack (id) script
: https://www.tradingview.com/script/9Brr8oCn-wolfpack-id-M/
// credit and special thanks to CookDog for the Taylor Series mathematical
discovery and wolfpack refinement :
https://www.tradingview.com/u/Alex_3648/
// SPECIAL THANKS
// credit and special thanks to SoupDragon for helping with the Confirmed Signal
option and general guidance : https://www.tradingview.com/u/pw31018/
// credit and special thanks to krollic for helping with the Inputs tab and
general GUI guidance :
https://www.tradingview.com/u/krollic1/
//
// Variables in Pine v4:
//
// Writing a variable in global scope without 'var' or 'varip' will force
// the variable to be continously recalcuated in realtime.
//
// Writing a variable within a function will only update the variable when
// the function is called. Using global variable names within a function
// will not work since Pine will grab the value of the global variable.
// However variables initialized and used within a function are local scope
// only meaning they cannot be accessed in global scope unless passed out
// of the function via return. Multiple variables can be returned from a
// function via tuple: [ return1, return2 ] = function ( arg1, arg2 )
// The number of returned variables and number of arguments need not match
// with a function. Also of note is that the 'security' call in Pine is
// technically a function and thus multiple variables can be returned
// via tuple from a single 'security call' – pretty cool!
//
// Writing a variable in global scope with 'var' permits the variable to
// maintain its value across bars until an update is issues (ex. := or += )
//
// Writing a variable in global scope with 'varip' forces the variable to
// maintain its value across bars. Updating the variable in any later bar
// will not affect a variable initialized with 'varip' for any future bar.
//
// Common sense thus suggests that variables should be initialized without
// 'var' or 'varip' if you desire them to be updated on every bar. Variables
// should be initialized with 'var' if you desire them to be updated
// occasionally but hold their value between changes; this setting thus
assists
// in reducing runtime and system resources since the variable is not
// typically re-calculated on every bar. Lastly, variables should only be
// initialized with 'varip' if you desire them to retain their value across
// the entire script with no re-calculating required going forward; this
// setting greatly reduces runtime since variables are only calculated once.
// I've tried to write thie script to take advantage of these runtime
// reductions, especially since every 'security' call – every additional
// timeframe – results in immense increases in runtime and system resources.
// Anything I can do to increase efficiency is good in my book!
//
// Also of note, most text and string variables are initialized with 'varip'
// since they do not change across bars. Pretty nifty especially if you want
// to change the text! Instead of using 'Replace All' which sometimes has
// unintended consequences, or trying to find each instance yourself which
// is a total pain, now the text is easily changeable from one place.
//
//
// If you find this script helpful, educational, or otherwise engaging, please
// leave a comment or drop me a line! Cheers
//
///////////////////////////////////////////////////////////////////////////////
// USER INPUTS
//
// GROUPS
//
varip groupGlobal = "Global"
varip groupIcon = "Text & Shape"
varip groupColors = "Color"
varip groupPotato = "Potato Signal"
varip groupTC = "Multiple Timeframes Label"
varip groupEMA = "Potato EMA's"
varip groupBB = "Bollinger Bands"
varip groupRibbon = "Ribbon"
varip groupTEMA = "Trendespresso"
varip groupMA = "Moving Averages"
varip groupMAL = "Moving Average Labels"
//
// STATIC TEXT
//
varip str_spc = " "
varip str_ln = "\n\n"
varip str_potato = "Potato"
varip str_signal = "Signal"
varip str_potatosig = str_potato + str_spc + str_signal
varip str_none = "None"
varip str_usr = "User"
varip str_preset1 = "The Baked Potato"
varip str_preset2 = "Trendespresso"
varip str_preset2_tt = "Overrides the Color options below."
varip str_confsig_tt = "Just like the original " + str_potatosig + ",\nnormally
this Indicator plots a signal\nwhen the conditions are met, but\nrequires the User
to wait until\nthe candle has closed for confirmation.\n\nEnabling this option will
only\ndisplay signals after they have\nbeen fully confirmed.\n\nSignals will be
printed on the same candle\nbut only upon candle close, thus eliminating\nthe risk
of false signals ('repainting')."
varip str_tc = "Triple Confirmations (Potato + Wolfpack + MLB Waves)"
varip str_orig = "Original"
varip str_orig_potato = str_orig + str_spc + str_potatosig
//
// SIGNAL METHOD
//
triple = input ( str_tc, "Signal Method", group = groupGlobal, options =
[ str_orig_potato, str_tc, str_none ], tooltip = version )
confirm = input ( true, "Confirmed Signals Only", group = groupGlobal, tooltip =
str_confsig_tt )
//
// STATIC COLORS
//
varip green = color.new ( #388e3c, 0 )
varip red = color.new ( #d32f2f, 0 )
varip orange = color.new ( #f57c00, 40 )
//
// POTATO SIGNAL APPEARANCE
//
//
varip str_potatoIcn =
"Potato" + str_spc + "Icon"
varip str_colArw =
"Colored Arrow"
varip str_arw =
"Arrow"
varip str_lngsht =
"Long / Short"
varip str_lrgIcn =
str_large + str_spc + str_icon + str_spc + str_only
varip str_smlIcn =
str_small + str_spc + str_icon + str_spc + str_only
varip str_preset1_tt =
"Overrides the '" + str_buy + "' / '" + str_sell +
"'" + str_spc + str_icon + " options below."
//varip str_icon_tt = "NOTE:\nPrettier names aren't possible in Pine v4."
varip str_icn_buy_tt = str_icon + " to draw upon a '" + str_buy + "'
signal." //\n\n //+ str_icon_tt
varip str_icn_sell_tt = str_icon + " to draw upon a '" + str_sell + "'
signal." //\n\n //+ str_icon_tt
//
// ICON PRESETS
//
presets = input ( str_usr, "Preset", group = groupIcon, options =
[ str_usr, str_preset1, str_preset2 ], tooltip = str_preset1_tt )
var int preset = presets == str_preset1 ? 1 : presets == str_preset2 ? 2
: 0
//
// COLOR INPUTS
//
varip str_bboo = "Brave Blue & Ominous Orange"
varip str_pdgr = "Green & Red (Dark)"
colorPallette = input ( str_usr, "Preset", group = groupColors, options =
[ str_usr, str_orig, str_pdgr, str_bboo ], tooltip = str_preset2_tt )
var int calPa = colorPallette == str_orig ? 1 : colorPallette ==
str_pdgr ? 2 : colorPallette == str_bboo ? 3 : 0
userText =
input ( color.white, "Text | Bull | Bear", group = groupColors, inline =
groupColors )
userGreen = calPa == 2 ? darkGreen : calPa == 3 ? braveBlue :
calPa == 1 ? color.green : input ( darkGreen, "", group =
groupColors, inline = groupColors )
userRed = calPa == 2 ? darkRed : calPa == 3 ? ominousOrange :
calPa == 1 ? color.red : input ( darkRed, "", group =
groupColors, inline = groupColors )
//
// MULTIPLE TIMEFRAMES INPUTS
//
varip num_tfs = 9
varip str_lbl_alrt_tt = "Writes a label to the right of price action\nshowing
Triple Confirmations on different timeframes.\n\nHover your mouse cursor over the
label to see Wolfpack\nand Market Liberator B wave values\n" +
"for each timeframe.\n\nNote:\nTimesframes smaller
than 1/2 the chart's\nresolution cannot be accurately calculated and thus\nwill be
automatically removed.\n(Pine v4 limitation)\n\nNote:\nIf an <error> " +
"is printed on the label,\nmost likely no Triple
Confirmation\nhas ever occurred in history for that timeframe." +
"\n\nNote:\nTimeframes measured in `Seconds` are not
supported."
varip str_lbl_tf1_tt = "With both this option and\n'Include Smallest Calculable
Timeframe' (below)\ndisabled, 'Timeframes' must be set to at least 2\nfor the label
to appear."
varip str_lbl_dyn_tt = "As of Pine v4, the 1/2 sized timeframe\nis the smallest
for which Triple Confirmations\nor the original Potato Signal\ncan be accurately be
calculated"
varip str_lbl_tf_tt = "Number of Timeframes to display.\n\nTimeframes smaller
than 2:1 to the current\n(ex. 30m is 2:1 with 1h, 4h is 2:1 with 8h)\nwill be
removed since they cannot be accurately calculated with the Pine v4 engine."
varip str_lbl_tf0_tt = "Note:\nBuy / Sell labels print at this resolution.\n\
nWill only be used if the selected\ntimeframe can be calculated accurately.\
nTimeframes smaller than 1/2 the chart's\nresolution will not be used."
varip str_lbl_4x_tt = "Potato references the 4x timeframe as the\n'Goldilocks
Trading Environment'\nsince trades that follow the\ntrend of their 4x timeframe
counterparts,\nlend themselves to be much safer\nand more successful than
otherwise."
/////////////////////////////////////////////////////////////////////////////////
// Multiple Timeframes Calculations
// HELPER FUNCTION: Split the timeframe string into an integer number and its last
letter
f_split_tf_string ( tf_str ) =>
str_num = 1
str_last_char = ""
// split the string into an array with each chracter separated
str_as_array = str.split ( tf_str, "" )
str_length = array.size ( str_as_array ) - 1
str_last_char := array.get ( str_as_array, str_length )
if na ( tonumber ( str_last_char ) )
// remove the last character of the string array if the 'str' is not an
integer only
array.pop ( str_as_array )
str_num := str_last_char != tf_str ? ceil ( tonumber ( array.join (
str_as_array, "" ) ) ) : 1 //str_length > 1 ? ceil ( tonumber ( array.join
( str_as_array, "" ) ) ) : 1
else
str_num := ceil ( tonumber ( tf_str ) )
[ str_num, str_last_char ]
// HELPER FUNCTION: Convert Months and Weeks and Days into Minutes
f_convert_to_minutes ( tf_str ) =>
[ str_num, str_last_char ] = f_split_tf_string ( tf_str )
m = 0
d = 0.0
if str_last_char == "M"
d := 30.4167 * str_num
else if str_last_char == "W"
d := 7 * str_num
else if str_last_char == "D"
d := str_num
// now figure out the days into minutes or return minutes
m := d > 0 ? ceil ( d * 24 * 60 ) : str_num
// HELPER FUNCTION: Add the native resolution at the beginning of the array.
// When 'i_lbl_inclChart' is enabled, we can safely 'push'
// the entry into the array since it will be the first.
// Otherwise, we need to 'insert' to ensure it gets put
// at the beginning of the array.
f_setup_userRes0 () =>
res0 = f_return_userRes0 ()
res0mins = f_convert_to_minutes ( res0 )
[ h0, m0 ] = f_convert_minutes_to_hours ( res0, res0mins )
// before pushing 'userRes0' into the arrays, check to see if it's already
included
return = array.includes ( dup_tfs, res0mins ) or array.includes
( userTfMins, res0mins )
if i_lbl_inclChart
f_use_timeframe ( "push", res0, res0mins, h0 + m0, int ( na ) )
else
f_use_timeframe ( "unshift", res0, res0mins, h0 + m0, int ( na ) )
return
// FUNCTION: Used on the first bar only; set up the 'userTfStr' and 'userTfMins'
arrays
// Each one with matching indexes
f_setup_userTfStr_and_userTfMins_arrays() =>
// add only unique entries if User requested
// here is where we tie each UserRes to each array index for identification
// and use throughout the entire script
f_add_unique_entry ( userRes1, 1, "push" )
f_add_unique_entry ( userRes2, 2, "push" )
f_add_unique_entry ( userRes3, 3, "push" )
f_add_unique_entry ( userRes4, 4, "push" )
f_add_unique_entry ( userRes5, 5, "push" )
f_add_unique_entry ( userRes6, 6, "push" )
f_add_unique_entry ( userRes7, 7, "push" )
f_add_unique_entry ( userRes8, 8, "push" )
f_order_timeframes_smallest_to_largest () =>
array.shift ( copied_arr )
array.sort ( copied_arr )
if end >= 0
for i = 0 to end
index = array.indexof ( userTfMins, array.get ( copied_arr, i )
)
array.push ( sorted_userTfStr, array.get ( userTfStr, index )
)
array.push ( sorted_userTfMins, array.get ( userTfMins, index )
)
array.push ( sorted_userTfhrsmins, array.get ( userTfhrsmins, index )
)
array.clear ( userTfStr )
array.clear ( userTfMins )
array.clear ( userTfhrsmins )
/////////////////////////////////////////////////////////////////////////////////
// POTATO SIGNAL INPUTS
//
// POTATO SIGNAL INPUT 1
//
// Settings for 5min chart, BTCUSDC. For Other coin, change the paremeters //
trendespresso: Huh? Potato was originally designed around 5m chart? crazy
varip per = 27 //input ( 27, "Sampling Period", minval = 1,
group = groupPotato )
//
// POTATO SIGNAL INPUT 2
//
varip mult = 1.0 //input ( 1.0, "Range Multiplier", minval = 0.1, group =
groupPotato )
/////////////////////////////////////////////////////////////////////////////////
// BOLLINGER BANDS
/////////////////////////////////////////////////////////////////////////////////
// MOVING AVERAGE CALCULATION
/////////////////////////////////////////////////////////////////////////////////
// MOVING AVERAGES
varip MA_tf_tt = "Hover your mouse cursor over\neach Moving Average label to\nsee
the exact price for that\nMoving Average."
varip MA_tt = "Enable up to 5 Moving Averages\non any other Timeframe.\n\
nDaily, Weekly, or Monthly are recommended.\n\n" + MA_tf_tt
/////////////////////////////////////////////////////////////////////////////////
// MOVING AVERAGE LABELS
/////////////////////////////////////////////////////////////////////////////////
// Potato + Wolfpack + MLB calculations
// HELPER FUNCTION: Smooth Average Range (for Potato Signal)
smoothrng ( x, t, m ) =>
wper = t * 2 - 1
avrng = ema ( abs ( x - x[1] ), t )
rtn_smooth = ema ( avrng, wper ) * m
// FUNCTION: Standard Wolfpack ema 3 - ema 8 using 'close' from 'security' as the
Source
// Wolfpack employs the Taylor Series
f_wolfpack ( src ) =>
WP = ema ( src, 3 ) - ema ( src, 8 )
WPc = WP + ( WP / 1001.00000 ) + ( WP / 1002000.000 ) + ( WP / 1003000000 ) + (
WP / 1004000000000 )
// FUNCTION: Calculate the MLB waves using 'hlc3' from 'security' and output if it
changes
f_MLBwaves ( hlc ) =>
n1 = 7 //input ( 8, "Channel Length") // 8
n2 = 10 //input ( 10, "Average Length") // 10
n3 = 4
esa = ema ( hlc, n1 )
d2 = ema ( abs ( hlc - esa ), n1 )
ci = ( hlc - esa ) / (0.015 * d2 )
tci = ema ( ci, n2 )
wt1 = tci
wt2 = sma ( wt1, n3 )
// if wt1 is greater than wt2, BUY signal is given
MLB = wt1 - wt2
/////////////////////////////////////////////////////////////////////////////////
// Fetch Prices, Wolfpack, MLB, and Potato Signal for each Timeframe
//
// this seems to work! everything must be wrapped in the 'security' function and
outputted as such
//
// EXPLANATION:
// Normally digging 'close' out of each Resolution returns good results. In our
// fringe Triple Confirmation case, the results are totally inaccurate and subject
// to fluctuations depending on the user's chart resolution. The only way to obtain
// static and accurate results is to run the wolfpack, mlb, and potato signal
functions
// directly inside the 'security' function. Think of it like wrapping the functions
// or running them inside a sandbox of each resolution, rather than simply
requesting
// the 'closing price' of each and running at the user's native chart resolution.
// For whatever reason, the results are dramatically different. Wrapping the
functions
// within 'security' is the best method for our use case. We also need the bar to
be closed
// and not currently written otherwise we risk obtaining results that are not yet
static.
// Remember, Triple Confirmations are only true and confirmed upon a bar close.
//
// many different attempts to enable lower timeframes to accurately be dispalyed on
larger views...
// unfortunately Pine doesn't do this very well in version 4 (hopefully it will in
the future?)
// thusly we are simply forced to remove inaccurate TC's as larger timeframes are
used
//
// barmerge.gaps_on makes grabbing the most recent data point much more
difficult... no benefit
// barmerge.lookahead_on grabs the latest datapoint, but this also means it
waits for additional closures before displaying... no benefit (verified no change
either)
//
// line 621: Cannot call 'security' or 'financial' inside 'if' or 'for'
// 'security' cannot have mutable variables inside it
//
//f_empty_values () => [ 0.0, 0.0, 0.0, 0.0 ]
[ c_tfA, w_tfA, m_tfA, f_tfA, BB_basis, BB_upper, BB_lower, ema1, ema2, ema3 ] =
security ( heikinashi ( syminfo.tickerid ), userRes0,
[ close,
f_wolfpack ( close ),
f_MLBwaves ( hlc3 ),
f_PotatoSignal ( f_wolfpack ( close ) ),
f_bb_basis ( close ),
f_bb_upper ( close ),
f_bb_lower ( close ),
f_ema1 ( close ),
f_ema2 ( close ),
f_ema3 ( close ) ],
barmerge.gaps_off,
barmerge.lookahead_off )
// Half-Sized timeframe's `close` will always be one candle behind native, thus we
omit `[1]`
[ c_tfH, w_tfH, m_tfH, f_tfH ] = security ( heikinashi ( syminfo.tickerid ),
userResH,
[ close,
f_wolfpack ( close ),
f_MLBwaves ( hlc3 ),
f_PotatoSignal ( f_wolfpack ( close ) ) ],
barmerge.gaps_off,
barmerge.lookahead_off )
// HELPER FUNCTION: Check the 'dup_tfs' array for the index and check that the
resolution is included
// Ensure matching indexes beteween 'userTfStr' and all its
respective data
// In Pine v4, 'array.indexof' returns -1 if the 'resolution' is
not found in 'userTfStr'
f_set_data_arrs ( resolution, c_in, w_in, m_in, f_in, is_nativeTf ) =>
if is_nativeTf
array.set ( closes, 0, c_in )
array.set ( wolfpacks, 0, w_in )
array.set ( mlbs, 0, m_in )
array.set ( filts, 0, f_in )
else if i_lbl_inclChart
index = array.indexof ( userTfStr, resolution )
if index != -1
array.set ( closes, index, c_in )
array.set ( wolfpacks, index, w_in )
array.set ( mlbs, index, m_in )
array.set ( filts, index, f_in )
else
editedArr = array.copy ( userTfStr )
array.set ( editedArr, 0, "noVal" )
index = array.indexof ( editedArr, resolution )
if index != -1
array.set ( closes, index, c_in )
array.set ( wolfpacks, index, w_in )
array.set ( mlbs, index, m_in )
array.set ( filts, index, f_in )
// for some reason 'upwards' and 'downwards' glitch out heavily when
// put into an array sooooo instead we make a bunch of int var variables
// that are guaranteed to maintain their value bar-to-bar *shrug*
var upwards_tf0 = 0
var downwards_tf0 = 0
var upwards_tf1 = 0
var downwards_tf1 = 0
var upwards_tf2 = 0
var downwards_tf2 = 0
var upwards_tf3 = 0
var downwards_tf3 = 0
var upwards_tf4 = 0
var downwards_tf4 = 0
var upwards_tf5 = 0
var downwards_tf5 = 0
var upwards_tf6 = 0
var downwards_tf6 = 0
var upwards_tf7 = 0
var downwards_tf7 = 0
var upwards_tf8 = 0
var downwards_tf8 = 0
var upwards_tf9 = 0
var downwards_tf9 = 0
/////////////////////////////////////////////////////////////////////////////////
// Confirmed Signals Only
// fancy maths to produce the desired effect of 2500 ms check when there's a clear
// break across the zero line, or 1000 ms check when the break across is very close
coeff = timeframe.isdwm ? 0.25 : array.get ( userTfMins, 0 ) > 30 ? 0.50 :
1.00
avg_MLB = max ( ( abs ( percentile_linear_interpolation ( nativeMLB, 50, 50 ) )
* coeff ), 0.10 )
avg_WP = max ( ( abs ( percentile_linear_interpolation ( nativeWP, 50, 50 ) )
* coeff ), 0.10 )
mid_MLB = avg_MLB > nativeMLB and avg_MLB * -1 < nativeMLB
mid_WP = avg_WP > nativeWP and avg_WP * -1 < nativeWP
ms = mid_MLB or mid_WP ? 1000 : 2500
// this condition checks to ensure we are within 100 milliseconds of the bar close
// if we are within 100 milliseconds of the bar close, then permit the Condition to
run
// no idea if this works as intended
// is it better to 1 ms or 1000 ms or some other value?? no clue
ms_until_bar_close = barstate.isrealtime ? time_close - timenow : -1
bar_is_confirmed = ms_until_bar_close <= ms
/////////////////////////////////////////////////////////////////////////////////
// Triple Confirmations
// these 'upwards' and 'downwards' arrays don't work properly... quite frustrating
//var upwards = array.new_int(num_tfs, 0)
//var downwards = array.new_int(num_tfs, 0)
var potatoes = array.new_int ( num_tfs, 0 )
var tcStates = array.new_int ( num_tfs, 0 )
var masterStates = array.new_int ( num_tfs, 0 )
label_text = string ( na ) // no 'var' on purpose; want a full re-draw on
each bar
label_tt_text = string ( na ) // no 'var' on purpose; want a full re-draw on
each bar
// ORIGINAL
//upward := filt > filt[1] ? nz(upward[1]) + 1 : filt < filt[1] ? 0 : nz(upward[1])
//downward := filt < filt[1] ? nz(downward[1]) + 1 : filt > filt[1] ? 0 :
nz(downward[1])
// HELPER FUNCTION: Triple Confirmations master state holder (to prevent a signal
every bar)
f_tripleConfirmation ( tcState ) => rtn_T = tcState[1] == 1 and tcState == -1 ? -
1 : tcState[1] == -1 and tcState == 1 ? 1 : 0
// HELPER FUNCTION: Add the correct negative or positive sign to the given string
(requires the original value/number obviously)
f_add_sign ( value, number_as_string ) => value < 0 ? "–" + number_as_string : "+"
+ number_as_string
// HELPER FUNCTION: Generate the Label tooltip with more verbose information
(Wolfpack and MLB values)
f_lbl_tt ( tf, potato, wolfpack, mlb ) =>
p = f_potato_signal_text ( potato )
w = f_add_sign ( wolfpack, f_osc_as_text ( wolfpack, true, i_lbl_tt_dec ) )
m = f_add_sign ( mlb, f_osc_as_text ( mlb, false, i_lbl_tt_dec ) )
tf + ": [ Potato" + p + " | Wolfpack: " + w + " | MLB: " + m + " ]"
/////////////////////////////////////////////////////////////////////////////////
// Background Label + Tooltip
// see if all TC states for the available timeframes are positive for negative
// if they are, instead of generating a uniquely colored label for each timeframe,
// just change the color of the normal label (titled 'labelBG')
// all TC's being up or down and coloring the entire label text that way will
// result in the separator being colored as well but that's acceptable considering
// the complicated trade-offs; maybe Pine v5 will be more flexible!
///////////////////////////////////////////////////////////////////////////////
// Dynamic Color Labels
/////////////////////////////////////////////////////////////////////////////////
// Alerts and Plotshapes
/////////////////////////////////////////////////////////////////////////////////
// BOLLINGER BAND plots
/////////////////////////////////////////////////////////////////////////////////
// POTATO EMAS plots
/////////////////////////////////////////////////////////////////////////////////
// MOVING AVERAGES plots
// HELPER FUNCTION: Hover mouse cursor over label to see % increase or decrease
// required for price to match or cross the line
f_ma_label_percent ( number ) =>
raw = ( ( number - close ) / close ) * 100
return = f_add_sign ( raw, f_osc_as_text ( raw, false, 2 ) ) + " %"
// FUNCTION: Return the Moving Average price, optionally appending ' K ' if >1000
f_ma_label_price ( number ) =>
return = string ( na )
if i_ma_lbl_k_val and label_tt_price >= 1000
return := f_osc_as_text ( number / 1000, false, 1 ) + "k"
else
return := f_osc_as_text ( number, false, -1 )
return
/////////////////////////////////////////////////////////////////////////////////
// end
// (c) Trendespresso, 2021