Scripting Ideas: For [back] testing MFE Maximum Favorable Excursion. [and bonus comand] VOLTT
stableDescription
Add MFA to Max Draw Down/MAE for back testing/reports
What does MFE mean?
MFE stands for Maximum Favorable Excursion.
MFE marks the highest price during a long trade and the lowest price during a short trade. This shows you what the highest profit was during a trade.
What does MAE mean?
MAE stands for Maximum Adverse Excursion (Max Draw Down)
MAE marks the lowest price during a long trade and the highest price during a short trade. This helps you to identify what the maximum loss was during a trade. This is also known as maximum drawdown of the position.
Maximum Adverse Excursion ( Price MAE )
Defines the price of the asset that would have led to maximum losses during the trade.
Maximum Favorable Excursion ( Price MFE )
Defines the price of the asset that would have led to maximizing profits during the trade.
Maximum Adverse Excursion ( Position MAE )
Defines the maximum possible losses that could have taken place during the trade.
Maximum Adverse Excursion ( Position MFE)
Defines the maximum possible profits that could have taken place during the trade.
Why are these metrics important?
These metrics are key are they can answer the following key questions while trading:
How much profit did I leave on the table?
What could have been the best possible exit price?
What are the maximum possible losses I could have taken during the trade?
What could have been the worst possible exit price?
Answering the questions above can help you better understand whether the trade you took respected your trading rules and whether your exit could have been better or not.
https://analyzingalpha.com/maximum-favorable-excursion
HaasScript
DefineCommand('Volatility_Trader_Tools', '[Kobalt] Volatility_Trader_Tools (VOLTT) Keltner Channel based ATR Bands, StdDeviation BBands VWAP bands (the big 3) added ATR as oscillator to confirm the exhaustion points(reversal best dip, peak)')
local pIsBt = DefineParameter(BooleanType, 'isBacktestt', '', true, true)
local pPlotIndex = DefineParameter(NumberType, 'index to plot', 'For ATR sub chart combo', false, 8) --testing 1 2
local pPlotIndexHight = DefineParameter(NumberType, 'plotIndexHight', 'For subChartPlots', false, 0.2)
local isBt = Load('pIsBt', false)
local useCurrentRes = Load('pIntern', true)
local resCustom = Load('resCustom', 0)
local plotBB = Load('plotBB', false)
local plotATR = Load('plotATR', false)
local plotKC = Load('plotKC', false)
local plotMult = Load('plotMult', false)
local internInt = CurrentInterval()
local intInput = 0
local int1 = 0
--local DefineIntervalOptimization((cInterval)
InputGroupHeader(' VOLTT: Selection')
local isBt = Input('isBt', true)
local useCurrentRes = Input(' Use custom timeframe?', false, 'Use Current Chart Resolution or custom input?')
local resCustom = InputInterval(' Higher Tf or keep current ', 240, 'default 30', '')
local plotATR = Input(' plot ATR', false)
local plotKC = Input(' plotKC', false)
local plotBB = Input(' Plot BB', false)
local plotMult = Input('plotMult', false)
--local plotADX = Input('plot ADX', false)
if useCurrentRes then
local int1 = internInt
else
local int1 = resCustom
end
Save('int1', int1)
Save('isBt', isBt)
Save('plotATR', plotATR)
Save('plotKC', plotKC)
Save('plotBB', plotBB)
Save('plotMult', plotMult)
--Save('plotADX', plotADX)
InputGroupHeader(' VOLTT: Keltner Channel + ATR Bands')
local atr_len = Input(' ATR Length', 14, 'common is 14', '')
local emaPeriod = Input(' EMA Length', 10, 'common = 10, standard 10 can use longer EMA', '')
local atr_mult = Input(' ATR Mult', 2, 'how many (2) Average True Ranges, similar to StdDev vut different, but that is the point ', '')
local kc_offset = Input(' KC Offset', 0, 'offsets the bands, not sure if / how this is useful yet, number of candles to remove', '')
InputGroupHeader(' VOLTT: 2 ATR Bands')
local atr_len2 = Input(' ATR Length2', 14, 'common is 14', '')
--local kc_len2 = Input(' EMA Length2', 10, 'common is 10', '')
local atr_mult2 = Input(' ATR Mult2', 3, 'default 2, standard 1.5', '')
local kc_offset3 = Input(' KC Offset2', 0, 'offsets the bands, not sure if / how this is useful yet, number of candles to remove', '')
local atr_len3 = Input(' ATR Length3', 28, 'common is 14', '')
--local kc_len3 = Input(' EMA Length3', 10, 'common is 10', '')
local atr_mult3 = Input(' ATR Mult3', 4, 'default 2, standard 1.5', '')
local kc_offset3 = Input(' KC Offset3', 0, 'offsets the bands, not sure if / how this is useful yet, number of candles to remove', '')
InputGroupHeader(' VOLTT: Average Directional indeX')
local adxPeriod = Input('adxPeriod slow_SpookyTrigger', 50, 'default 50 Period to calculate SpookyTriggerSlow', '')
local adx2Period = Input('adx2Period fast_SpookyTrigger', 14, 'default 14 Period to calculate spookyTriggerFast', '')
local minMultiplier = Input('Min. Dynamic Multiplier', 3, 'default 1 for no effect', '')
local data = CC_GetPrices()
local kc = {
lower = Load('kcl', {}),
upper = Load('kcu', {})
}
OptimizedForInterval(int1, function()
local h = Offset(data.h(int1), kc_offset)
local l = Offset(data.l(int1), kc_offset)
local c = Offset(data.c(int1), kc_offset)
local kc1 = KELTNER(h,l,c, emaPeriod, atr_len, atr_mult)
local kc2 = KELTNER(h,l,c, emaPeriod, atr_len2, atr_mult2)
local kc3 = KELTNER(h,l,c, emaPeriod, atr_len3, atr_mult3)
local kcmid = kc.middle
if isBt and plotKC then
local kh1 = Plot(0, 'Upper 1 ATR', kc1.upper, LineOptions(Blue(90), {style=Smooth}))
local km1 = Plot(0, 'Middle 1', kc1.middle, LineOptions(DarkGray, {style=Smooth}))
local kl1 = Plot(0, 'Lower 1 ATR', kc1.lower, LineOptions(Blue(90), {style=Smooth}))
local kh2 = Plot(0, 'Upper 2 ATR', kc2.upper, LineOptions(Orange(33), {style=Smooth}))
local km2 = Plot(0, 'Middle 2', kc2.middle, LineOptions(Gray, {style=Smooth}))
local kl2 = Plot(0, 'Lower 2 ATR', kc2.lower, LineOptions(Orange(33), {style=Smooth}))
local kh3 = Plot(0, 'Upper 3 ATR', kc3.upper, LineOptions(Aqua(33), {style=Smooth}))
local km3 = Plot(0, 'Middle 3', kc3.middle, LineOptions(Orange, {style=Smooth}))
local kl3 = Plot(0, 'Lower 3 ATR', kc3.lower, LineOptions(Aqua(33), {style=Smooth}))
PlotBands(kh1, kl1, Olive(8))
PlotBands(kh2, kh1, Orange(8))
PlotBands(kh3, kh2, Red(8))
PlotBands(kl1, kl2, Blue(8))
PlotBands(kl2, kl3, Purple(8))
-- PlotBands(kl3, kl2, Red(8))
ChartSetOptions(0, 'VOLTT')
end
Save('kcl', kc1.lower)
Save('kcu', kc1.upper)
Save('kcl2', kc2.lower)
Save('kcu2', kc2.upper)
Save('kcl3', kc3.lower)
Save('kcu3', kc3.upper)
local atrosc = ATR(data.h(internInt), data.l(internInt), data.c(internInt), atr_len)
if plotATR and isBt then
Plot(6, 'atr', atrosc, Red)
ChartSetOptions(6, 'atr', pPlotIndexHight)
end
local bbMaType = SmaType
InputGroupHeader(' VOLTT: Standard Deviation: Bollinger Bands')
local bbMaType = InputMaTypes('MA to calculate BBands', SmaType, 'for contrarians')
local bbPeriod = Input(' MA period for BB', 33, 'common is 20', '')
local devUp = Input(' deviation Factor Up', 1, ' To adjust upper or lower bands to market conditions StdDev for UPPER Bollinger Band(s), Before stdDev Bands Multiplier(devFactor), value 1 would be neutral (symetrical if used for both bands) Can be modified to add a directional bias/Skew', '')
local devDown = Input(' deviation Factor Down', 1, 'Number or factor StdDev for LOWER Bollinger Band(s), Before stdDev Bands Multiplier, value 1 would be neutral (symetrical if used for both bands)', '')
local devBasis = Input(' baseDev', 2.1, 'your preferred BBand and deviations to take the trade (write puts...) "Your standard", Standard Deviation we use before adding outer bands.(contingency, blackswans, another Gamma squeeze from WallstBets...', '')
local devFactor = Input(' devFactor', 0.95, 'to get additional bands')
--local devMult = Input(' Number of extra Bands', 2, 'Anything more than 1 adds a Band outside the Bollinger rangeto get the standard, Standard Deviation we use 2', '')
local upperBB = devUp * devBasis
local lowerBB = devDown * devBasis
local upperBB2 = (devUp * devBasis) + (devUp * devFactor)
local lowerBB2 = (devDown * devBasis) + (devDown * devFactor)
local bb = BBANDS(data.c(internInt), bbPeriod, upperBB, lowerBB, bbMaType)
local bb2 = BBANDS(data.c(internInt), bbPeriod, upperBB2, lowerBB2, bbMaType)
--PlotBBandsChart(0, 'bb', bb.upper, bb.middle, bb.lower)
if isBt and plotBB then
Plot(0, 'upperBB', bb.upper, LineOptions(Aqua(33), {style=Smooth, width=1}))
--Plot(0, 'Median', bb.middle, LineOptions(Orange, {style=Smooth}))
Plot(0, 'lowerBB', bb.lower, LineOptions(Aqua(33), {style=Smooth, width=1}))
Plot(0, 'upperBB2', bb2.upper, LineOptions(Aqua(33), {style=Smooth, width=2}))
--local bbStandard = Plot(0, 'Middle 3', kc3.middle, LineOptions(Orange, {style=Smooth}))
Plot(0, 'lowerBB2', bb2.lower, LineOptions(Aqua(33), {style=Smooth, width=2}))
--local bbMA = MA(data.c(0), bbPeriod, bbMaType)
--local bb = BBANDS(data.c(int1), 20, 2, 3, SmaType)
--PlotBBandsChart(0, 'bb', bb.upper, bb.middle, bb.lower)
--local upperBB2 = (devUp + (devUp * devFactor))
--PlotBBandsChart(0, 'bb2', bb2.upper, bb2.middle, bb2.lower)
-- STF (Short Timeframe)
--Plot(0, 'upperBB2', upperBB2, LineOptions(Aqua(33), {style=Smooth}))
--local maBB = Plot(0, '(non)Standard MA', bbMA, LineOptions(Orange, {style=Smooth}))
--Plot(0, 'lowerBB2', lowerBB2, LineOptions(Aqua(33), {style=Smooth}))
end
end)
-- These are the output results.
-- These indicate what the Trend Control thinks
-- is the current market condition.
local doLongs = Load('dl', false)
local doShorts = Load('ds', false)
local doRange = Load('dr', false)
local sellPut = Load('dl', false)
local sellCall = Load('ds', false)
-- 5min update frequency for final results
--OptimizedForInterval(5, function()
local startTimeLong = Load('stl', 0)
local endTimeLong = Load('etl', 0)
local startTimeShort = Load('sts', 0)
local endTimeShort = Load('ets', 0)
local startTimeRanging = Load('str', 0)
local endTimeRanging = Load('etr', 0)
local totalTimeLong = Load('ttl', 0)
local totalTimeShort = Load('tts', 0)
local totalTimeRange = Load('ttr', 0)
local ch_price = data.c(1, false)
local volume = data.v(1, false)
if #kc.upper > 1 then
if (ch_price > kc.lower and ch_price < kc.upper) then
if startTimeRanging == 0 and endTimeRanging == 0 then
startTimeRanging = Time()
doRange = true
if isBt and doRange then
PlotSignalBar(-6, Gold)
end
end
if startTimeLong > 0 and endTimeLong == 0 then
endTimeLong = Time()
doLongs = false
elseif startTimeShort > 0 and endTimeShort == 0 then
endTimeShort = Time()
doShorts = false
end
elseif (ch_price < kc.lower or ch_price > kc.upper) then
if startTimeRanging > 0 and endTimeRanging == 0 then
endTimeRanging = Time()
doRange = false
end
if ch_price < kc.lower and startTimeLong == 0 and endTimeLong == 0 then
startTimeLong = Time()
doLongs = true
if isBt and doLongs then
PlotSignalBar(-6, Green)
end
if startTimeShort > 0 and endTimeShort == 0 then
endTimeShort = Time()
doShorts = false
end
elseif ch_price > kc.upper and startTimeShort == 0 and endTimeShort == 0 then
startTimeShort = Time()
doShorts = true
if isBt and doShorts then
PlotSignalBar(-6, Red)
end
-- if isBt then
-- ChartSetOptions(-6, 'doStuff', 0.09)
--end
if startTimeLong > 0 and endTimeLong == 0 then
endTimeLong = Time()
doLongs = false
end
end
end
end
if startTimeLong > 0 and startTimeLong < endTimeLong then
if isBt then
PlotVerticalZone(0, '', Green(10), startTimeLong, endTimeLong)
end
totalTimeLong = totalTimeLong + (endTimeLong - startTimeLong)
startTimeLong = 0
endTimeLong = 0
end
if startTimeShort > 0 and startTimeShort < endTimeShort then
if isBt then
PlotVerticalZone(0, '', Red(10), startTimeShort, endTimeShort)
end
totalTimeShort = totalTimeShort + (endTimeShort - startTimeShort)
startTimeShort = 0
endTimeShort = 0
end
if startTimeRanging > 0 and startTimeRanging < endTimeRanging then
if isBt then
PlotVerticalZone(0, '', Yellow(10), startTimeRanging, endTimeRanging)
end
totalTimeRange = totalTimeRange + (endTimeRanging - startTimeRanging)
startTimeRanging = 0
endTimeRanging = 0
end
local h1 = data.h(1, false)
local l1 = data.l(1, false)
local hldiff = (h1[1] - l1[1]) / (h1[2] - l1[2])
local adx = ADX(data.h(1), data.l(1), data.c(1), adxPeriod)
local adxDiff = adx[1] - adx[2]
local spookyTriggerSlow = hldiff > 1 and adxDiff > 0.75
local adx2 = ADX(data.h(1), data.l(1), data.c(1), adx2Period)
local adxDiff2 = adx2[1] - adx2[2]
local spookyTriggerFast = hldiff > 1 and adxDiff2 > 0.75
local adx3 = ArrayGet(adx, 1)
local adx4 = ArrayGet(adx2, 1)
local multiplier = Max(1.0, (adx3*0.4+adx4*0.6)/20)
--local adxMixMultiplier = Max(1.0, (multiplier*0.4 + adxmix*0.6)/20)
if isBt and plotMult then
Plot(pPlotIndex, 'Multiplier', multiplier, Red)
-- Plot(3, 'adxMixMultiplier', adxMixMultiplier, Orange)
ChartSetOptions(pPlotIndex, 'Multiplier', pPlotIndexHight)
end
--[[if isBt and plotADX then
Plot(9, 'adx', adx3, Yellow())
Plot(9, 'adx2', adx4, Blue())
--Plot(2, 'adx adx2/2', adxmix, Blue)
ChartSetOptions(9, 'ADX', pPlotIndexHight)
end]]
Save('stl', startTimeLong)
Save('etl', endTimeLong)
Save('sts', startTimeShort)
Save('ets', endTimeShort)
Save('str', startTimeRanging)
Save('etr', endTimeRanging)
Save('ttl', totalTimeLong)
Save('tts', totalTimeShort)
Save('ttr', totalTimeRange)
Save('dl', doLongs)
Save('ds', doShorts)
Save('dr', doRange)
Finalize(function() --- check what to do or dont here
local current = ''
if doLongs then current = current..'No-trade zone, Longs' end
if doShorts then current = current..' No-trade zone, Shorts' end
if doRange then current = current..' Full steam ahead!' end
totalTimeLong = totalTimeLong / 60
totalTimeShort = totalTimeShort / 60
totalTimeRange = totalTimeRange / 60
CustomReport('GTFO Longs', totalTimeLong..' minutes', 'Trend Control')
CustomReport('GTFO Shorts', totalTimeShort..' minutes', 'Trend Control')
CustomReport('Ranging zones', totalTimeRange..' minutes', 'Trend Control')
CustomReport('Current zone', current, 'Trend Control')
CustomReport('hldiff', hldiff, 'InterZoneCalcs')
CustomReport('adxDiff', adxDiff, 'InterZoneCalcs')
CustomReport('adxDiff2', adxDiff2, 'InterZoneCalcs')
CustomReport('adx3', adx3, 'InterZoneCalcs')
CustomReport('adx4', adx4, 'InterZoneCalcs')
CustomReport('multiplier', multiplier, 'result')
CustomReport('spookyTriggerSlow', spookyTriggerSlow, 'result')
CustomReport('spookyTriggerFast', spookyTriggerFast, 'result')
CustomReport('doLongs', doLongs, 'result')
CustomReport('doShorts', doShorts, 'result')
CustomReport('doRange', doRange, 'result')
end)
local result = {
doLongs = doLongs,
doShorts = doShorts,
doRange = doRange,
spookyFast = spookyTriggerFast,
spookySlow = spookyTriggerSlow,
multiplier = multiplier
}
DefineOutput(ListDynamicType, result, 'Trend Control output array-object.')
0 Comments
Sign in to leave a comment.
No comments yet. Be the first!