Profit Trailer with Fallback Protection and Break Even Safety

stable
By tedo in Safeties Published June 2024 👁 1,066 views 💬 0 comments

Description

This script implements a comprehensive trading safety mechanism that includes trailing stops, fallback protection, and break-even safety features. The logic is designed to maximize profits while minimizing risks through well-defined parameters and conditions. Key Features: Trailing Stops: Begins trailing when a specified profit percentage (trailStartPrc) is reached. Adjusts the stop level based on the highest recorded profit and a trailing distance (trailDistPrc). Supports different trailing modes (default, grow, shrink). Fallback Protection: Optional feature to prevent bagging up on a weak trend. Activates after the trailing stop is stopped a specified number of times (maxFallbackProtect). Includes its own rebound mechanism to allow for market corrections before triggering an exit. Break Even Safety: Activates when a specified percentage price change (breakEvenMinPrc) is reached. Sets the entry price as a stop-loss level to exit the position near break-even if the market moves against the position. Includes a rebound mechanism to allow for market corrections before triggering an exit.
HaasScript
-- Author: pshai
-- modification: tedo

DefineCommand("ProfitTrailerFallbackProtectBE", "ProfitTrailerFallbackProtect (Quiet, Inputs, Start 1.5, Distance 0.5)")

-- Define parameters
local trailStartPrc = DefineParameter(NumberType, "trailStartPrc", "Profit level (as percentage) where the trailing starts.", false, 1.5, "Input")
local trailDistPrc = DefineParameter(NumberType, "trailDistPrc", "Trailing distance (as percentage) from the highest recorded profit.", false, 0.5, "Input")
local trailMode = DefineParameter(StringType, "trailMode", "Trailing mode. Use one of the following: 'default', 'grow', or 'shrink'.", false, "default", "Input")
local maxRebounds = DefineParameter(NumberType, "maxRebounds", "Maximum rebounds allowed. If not set, then rebound is not used. Default is 0 (disabled).", false, 0, "Input")
local maxFallbackProtect = DefineParameter(NumberType, "maxFallbackProtect", "Optional reduced trailStart, trailDist or fixed TP after trailing stopped x times to prevent bagging up on a weak trend. Default is 0 (disabled).", false, 0.6, "Input")
local min_prc = DefineParameter(NumberType, "min_prc", "Minimum required percentage price change for safety to activate", false, 1, "Input")
local enableFallbackProtect = DefineParameter(BooleanType, "enableFallbackProtect", "Enable or disable fallback protection", false, true, "Input")
local enableBreakEvenSafety = DefineParameter(BooleanType, "enableBreakEvenSafety", "Enable or disable break even safety", false, true, "Input")
local breakEvenMinPrc = DefineParameter(NumberType, 'breakEvenMinPrc', 'Minimum required percentage price change for break even safety to activate', true, 1, 'Number, Input')
local breakEvenMaxRebounds = DefineParameter(NumberType, 'breakEvenMaxRebounds', 'Number of rebounds allowed before triggering the break even safety', false, 0, 'Number, Input')
local fallbackMaxRebounds = DefineParameter(NumberType, 'fallbackMaxRebounds', 'Number of rebounds allowed before triggering the fallback protection', false, 0, 'Number, Input')
local positionId = DefineParameter(StringType, "positionId", "Unique position ID.", false, "", "Load")
local ptiqname = DefineParameter(StringType, "name", "Name to distinguish it from others if used multiples in one script.", false, "", "Text")
local isLogging = DefineParameter(BooleanType, "logging", "Turn logging on or off.", false, true, "Input")
local isVerbose = DefineParameter(BooleanType, "verbose", "Log extra detail.", false, false, "Input")

-- Input fields

-- Profit Trailer Settings
local trailStartPrc = Input("Trailing Start %", 1, "Profit level (as percentage) where the trailing starts.", ptiqname.."Profit Trailer Settings")
local trailDistPrc = Input("Trailing Distance %", 0.5, "Trailing distance (as percentage) from the highest recorded profit.", ptiqname.."Profit Trailer Settings")
local trailMode = Input("Trailing Mode", 'default', "Trailing mode. Use one of the following: 'default', 'grow', or 'shrink'.", ptiqname.."Profit Trailer Settings")
local maxRebounds = Input("Max. Rebounds", 0, "Maximum rebounds allowed. If not set, then rebound is not used. Default is 0 (disabled).", ptiqname.."Profit Trailer Settings")
local isLogging = Input("Logging", true, "", ptiqname.."Profit Trailer Settings")
local isVerbose = Input("Verbose Logging", false, "", ptiqname.."Profit Trailer Settings")

-- Fallback Protection Settings
local maxFallbackProtect = Input("Max Fallback Protect", 2, "Optional reduced trailStart, trailDist as a multiplier and fixed TP. Example: 0.5 reduces both and if it goes above, will use 0.5% TP if it falls back after trailing stopped x times (use rebounds?) to prevent bagging up on a weak trend.", ptiqname.."Fallback Protection Settings")
local min_prc = Input("Minimum Required Percentage Price Change", 0.5, "Minimum required percentage price change for safety to activate", ptiqname.."Fallback Protection Settings")
local enableFallbackProtect = Input("Enable Fallback Protection", true, "Enable or disable fallback protection", ptiqname.."Fallback Protection Settings")
local fallbackMaxRebounds = Input("Fallback Max Rebounds", 0, "Number of rebounds allowed before triggering the fallback protection", ptiqname.."Fallback Protection Settings")

-- Break Even Safety Settings
local enableBreakEvenSafety = Input("Enable Break Even Safety", true, "Enable or disable break even safety", ptiqname.."Break Even Safety Settings")
local breakEvenMinPrc = Input("Break Even Min %", 1, "Minimum required percentage price change for break even safety to activate", ptiqname.."Break Even Safety Settings")
local breakEvenMaxRebounds = Input("Break Even Max Rebounds", 0, "Number of rebounds allowed before triggering the break even safety", ptiqname.."Break Even Safety Settings")

local position = PositionContainer(positionId)
if positionId == "" then positionId = position[1] end -- Override empty position id

local market = position[2]
local isLong = position[3]
local direction = GetPositionDirection(positionId)
local roi = GetPositionROI(positionId)
local enterPrice = position[6]

local isTrailing = Load('isTrailing'..positionId, false)
local highestRoi = Load('highestRoi'..positionId, roi)
local rebounds = Load('rebounds'..positionId, 0)
local reboundLevel = Load('reboundLevel'..positionId, 0)
local fallbackTriggered = Load('fallbackTriggered'..positionId, false)
local fallbackCount = Load('fallbackCount'..positionId, 0)
local fallbackReboundCount = Load('fallbackReboundCount'..positionId, 0)
local fallbackReboundLevel = Load('fallbackReboundLevel'..positionId, 0)
local breakEvenActive = Load('breakEvenActive'..positionId, false)
local breakEvenReboundCount = Load('breakEvenReboundCount'..positionId, 0)
local breakEvenReboundLevel = Load('breakEvenReboundLevel'..positionId, 0)

maxRebounds = Abs(maxRebounds)
local result = false

-- Helper function to calculate trail stop
local function calculateTrailStop(highestRoi, trailDistPrc, trailStartPrc, trailMode)
    if trailMode == "default" then
        return highestRoi - trailDistPrc
    elseif trailMode == "grow" then
        local multiplier = trailDistPrc / trailStartPrc
        local newDist = multiplier * highestRoi
        return highestRoi - newDist
    elseif trailMode == "shrink" then
        local multiplier = trailDistPrc * trailStartPrc
        local newDist = multiplier / highestRoi
        return highestRoi - newDist
    end
end

-- Helper function to reset trailing state
local function resetTrailingState()
    isTrailing = false
    rebounds = 0
    highestRoi = 0
    reboundLevel = 0
    fallbackTriggered = false
    fallbackCount = 0
end

-- Break Even Safety Logic
local function breakEvenSafetyLogic()
    if not enableBreakEvenSafety then return end

    local cp = CurrentPrice(market)
    if PercentagePriceChange(breakEvenMinPrc, {positionId = positionId}) then
        breakEvenActive = true
    elseif breakEvenActive then
        if (isLong and cp.bid <= enterPrice) or (not isLong and cp.ask >= enterPrice) then
            if breakEvenMaxRebounds > 0 and breakEvenReboundCount < breakEvenMaxRebounds and breakEvenReboundLevel <= 0 then
                breakEvenReboundLevel = cp.close
                if isLogging then
                    LogWarning('[BE] '..market..': Break Even Rebound target set at '..breakEvenReboundLevel..'.')
                end
            end
            if breakEvenReboundCount >= breakEvenMaxRebounds then
                result = true
                if isLogging then
                    LogWarning('[BE] '..market..': Break Even Safety triggered. Exiting position.')
                end
            end
        end
    end
end

-- Fallback Protection Logic
local function fallbackProtectionLogic()
    if not enableFallbackProtect then return end

    local cp = CurrentPrice(market)
    if roi < trailStartPrc and trailStartPrc > min_prc then
        if fallbackMaxRebounds > 0 and fallbackReboundLevel > 0 and 
           ((isLong and cp.bid > fallbackReboundLevel) or (not isLong and cp.ask < fallbackReboundLevel)) then
            fallbackReboundCount = fallbackReboundCount + 1
            fallbackReboundLevel = 0
            if isLogging then
                LogWarning(market..': Fallback Rebound of '..fallbackReboundCount..'/'..fallbackMaxRebounds..'.')
            end
        end

        fallbackCount = fallbackCount + 1
        if fallbackCount >= maxFallbackProtect then
            fallbackTriggered = true
            result = true
            if isLogging then
                LogWarning(market..': Fallback Protection triggered. Signaling exit.')
            end
        else
            if fallbackMaxRebounds > 0 and fallbackReboundCount < fallbackMaxRebounds and fallbackReboundLevel <= 0 then
                fallbackReboundLevel = cp.close
                if isLogging then
                    LogWarning(market..': Fallback Rebound target set at '..fallbackReboundLevel..'.')
                end
            end
        end
    end
end

-- Main logic
if direction != NoPosition then
    if isVerbose then
        Log(market..': Current profit: '..roi..' %')
        Log(market..': Position: '..IfElse(isLong, 'Long', 'Short'))
    end

    -- Check if we need to start trailing
    if not isTrailing and trailStartPrc < roi then
        isTrailing = true
        if isLogging then
            LogWarning(market..': Started trailing.')
        end
    end

    -- Update trailing
    if isTrailing then
        highestRoi = Max(roi, highestRoi)
        local trailStop = calculateTrailStop(highestRoi, trailDistPrc, trailStartPrc, trailMode)

        -- Check if we need to stop trailing
        if roi < trailStartPrc then
            resetTrailingState()
            if isLogging then
                LogWarning(market..': Profits have dropped below start value. Trailing is stopped.')
            end
        end

        -- Update rebound count if used
        if maxRebounds > 0 and reboundLevel > 0 and roi > reboundLevel then
            rebounds = rebounds + 1
            reboundLevel = 0
            if isLogging then
                LogWarning(market..': Rebound of '..rebounds..'/'..maxRebounds..'.')
            end
        end

        -- Check if we need or can exit
        if trailStop > roi then
            -- Prepare for a rebound if used
            if maxRebounds > 0 and rebounds < maxRebounds and roi >= 0 and reboundLevel <= 0 then
                reboundLevel = roi
                highestRoi = roi
                if isLogging then
                    LogWarning(market..': Rebound target of '..reboundLevel..' % is set.')
                end
            end

            -- Signal for exit if we have enough profits
            if roi > trailStartPrc then
                if rebounds >= maxRebounds then
                    result = true
                    if isLogging then
                        LogWarning(market..': Signaling exit.')
                    end
                end
            else
                if isLogging then
                    LogWarning(market..': Not enough profits to exit position, still watching it.')
                end
            end
        end
    end

    -- Execute Fallback Protection Logic
    fallbackProtectionLogic()

    -- Execute Break Even Safety Logic
    breakEvenSafetyLogic()
end

-- Store values for next update
Save('isTrailing'..positionId, isTrailing)
Save('highestRoi'..positionId, highestRoi)
Save('rebounds'..positionId, rebounds)
Save('reboundLevel'..positionId, reboundLevel)
Save('fallbackTriggered'..positionId, fallbackTriggered)
Save('fallbackCount'..positionId, fallbackCount)
Save('fallbackReboundCount'..positionId, fallbackReboundCount)
Save('fallbackReboundLevel'..positionId, fallbackReboundLevel)
Save('breakEvenActive'..positionId, breakEvenActive)
Save('breakEvenReboundCount'..positionId, breakEvenReboundCount)
Save('breakEvenReboundLevel'..positionId, breakEvenReboundLevel)

-- Define output signal
DefineOutput(BooleanType, result, "True if safety has triggered, otherwise false", "SafetyContainer, And, Or, IfElse, IfElseIf")

0 Comments

Sign in to leave a comment.

No comments yet. Be the first!