Scripting Ideas: For [back] testing MFE Maximum Favorable Excursion. [and bonus comand] VOLTT

stable
By Kobalt in Miscellaneous Published November 2022 👁 1,811 views 💬 0 comments

Description

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!