Smokybot RS MK1 (Remote Signal)

stable
By smokyho in Trading Bots Published August 2023 👁 1,984 views 💬 2 comments

Description

First of all, make sure you know how to create signal from other website. For example this is a tutorial made by the excellent Haasonline Team on how to create signal from Trading View https://youtu.be/SMqRw89OnT4 BOT FEATURES - Bot has 3 signal entries. The 1st one is the primary and mandatory entry. It "read" Entry, Exit, and Custom Signal (SignalReservedA). You can use all 3 or just one. - The decision for entry is unanimous. Supertrend can be added for additional signal (personally i use this). - For Exit Position bot has 3 options. Risk Reward system (on by default), SignalExit from Signal 1 (if you set the alert for it in TradingView) or % scalping (if you set it up in the bot). - Do you love Heikin Ashi candle? . There is an option to use is as base of the chart. Just make sure that the indicators also calculated based on Heikin Ashi. BUT I USE FREE PLAN IN TRADING VIEW. I CAN ONLY HAVE 1 ALERT! Worry my young padawan do not. Advantage of this bot you still can take. Yrsssss. As described earlier the Signal 1 accept Custom Signal (SignalReservedA). Bot will read this as "open position". Long or short? This is where supertrend come in. if it happens when it's uptrend, then it's long. So if you are sending Custom Signal make sure to enable Supertrend filter or else bot will open both position when it comes. Unless if that is what you want. Fortunately there is an indicator other than alerting to open position it is also a Machine Learning classification algorithm capable of categorizing historical data from a multi-dimensional feature space (according to the desription). You can find it here How to do it: 1. Add that indicator into your TradingView chart. 2. Add new alerts in TradingView and pick "Lorentzian..." in condition. 3. Choose " Open Position" on the drawdown below it. 4. For trigger i usually use Once Per Bar Close for more confirmation. Read more the difference in their docs. 5. Set expired to maximum. 6. Click on Notifications tab 7. In the Webhook URL paste the link from Haasonline Signal Details email under Custom signal 1. 8. Create the alarm. CAN I BACKTEST Nope. You can't backtest Remote Signal BUT Haasonline log signal received from TradingView. So you can backtest it later in the future after Haasonline server collected some of the signals. Assuming you understand PineScript then you can mod the script you like to become trading strategy. Fortunately, again, someone already have done Lorentzian script trading strategy. You can find it here. So play with the setting on that strategy script, find something you like then apply it on the original script that sending alert and hope the best. May the profits be with you! More questions ? Find me in Haasonline Discord. If you would like to buy me a cup of coffee or diamond: ENS: smokyho.eth
HaasScript
-- How to create signal from TradingView https://youtu.be/SMqRw89OnT4

EnableHighSpeedUpdates(true)
HideOrderSettings()
HideTradeAmountSettings()

-- inputs
    InputGroupHeader('Trade Settings')
        local useHA = Input('Use Heikin Ashi', false, 'Heikin Ashi as price source. Match this with webhook source (i.e. TradingView).')
        local signalName1 = Input('Signal 1 Name', '')
        local signalPK1 = Input('Signal Identifier 1', '', 'Primary signal. Recognize SignalLong/Short and Custom signal 1 for entry, SignalExitLong/Short for alternative to RR.')
        local signalName2 = Input('Signal 2 Name', '')
        local signalPK2 = Input('Signal Identifier 2', '', 'Empty means it will be ignored.')
        local signalName3 = Input('Signal 3 Name', '')
        local signalPK3 = Input('Signal Identifier 3', '', 'Empty means it will be ignored.')
        local okLong = Input('Long Entry', false, 'Allow bot to open Long')
        local okProfitL = Input('Long Exit', false, 'Allow bot to exit Long')
        local okShort = Input('Short Entry', false, 'Allow bot to open Short')
        local okProfitS = Input('Short Exit', false, 'Allow bot to exit Short')
        local okReduce = Input('BalRatio Reduction', false, 'Allow Bal Ratio based size reduction')
        local okDerisk = Input('Risk Reduction', false, 'Allow Signal based size reduction')
        local oneWay = Input('Not Hedging', false, 'Only have 1 type of position per trade. Long will not open if Short is open and vice versa.')
        local okCycle = Input('Position Cycle', false, 'If true then bot only open one position per supertrend cycle.')

    InputGroupHeader('Bot Settings')
        local wtfStop = Input('Stop at no position', false, 'Deactivate bot when there is no open position.')
        local FOL = Input('Force Open Long', false, 'Force open LONG position')
        local FEL = Input('Force Exit Long', false, 'Force exit LONG position')
        local FOS = Input('Force Open Short', false, 'Force open SHORT position')
        local FES = Input('Force Exit Short', false, 'Force exit SHORT position')
        local showData = Input('Show Balance & Position Data', false, 'Show balance and position data in the logs.')
                                                                            
    InputGroupHeader('Custom Wallet')
        local wtfBal = Input('Deactivate on Over Budget', false, 'ONLY FOR BACKTEST. Disable bot when Bal. Ratio hit trigger')
        local testWallet = Input('Activate Custom Wallet', false, 'Use custom wallet amount. A must in Haaslab')
        local testBal = Input('Custom Wallet Balance', 0, 'Amount of custom wallet')

    InputGroupHeader('Budget')
        local startBal = Input('Starting Balance', 0, 'Starting trading balance which also maximum COST to limit working balance (margin cost + unrealised loss)')
        local leverage = Leverage()
        local contVal = ContractValue() 
        local strictBudget = Input('Strict Budget', false,  'When applied bot will close all position and stop if (highest bot balance - current bot balance) > starting balance.')

    InputGroupHeader('Safety')
        local slTrigger = Input('Fix SL %', 0, '0 is using Supertrend value. > 0 is fixed % and ignoring Supertrend ')
        local reduceTrigger = Input('Bal Ratio Trigger', 0.8, 'Ratio between working balance and trading balance to trigger size reduction')
        local profitOut = Input('Profit Hodl %', 100, '% of the bot profit taken out or not compounded into trading balance calculation')
        local dynamicHodl = Input('Dynamic Hodl', false, 'After in profit will only take out profit if current profit < 80% of highest profit recorded.')
        local delay = Input('SL Delay (Min)', 0, 'Stop the bot from opening new long/short order for X minutes after close at loss.')

    InputGroupHeader('Order')
        local orderCount = Input('Orders Count', 1, 'How many total filled orders are allowed for DCA.')
        local slotBudget = Input('Risk % per order', 1, '1 = risk losing ~1% of trading balance PER ORDER if SL hit from current order price. Total risk PER POSITION = order count * risk.') 
        local orderDistance = Input('Fix Orders Distance %', 0, 'Minimum % price distance from filled order to the next.')
        local dynamicDist = Input('Dynamic Order Distance %', true, 'Auto calculate order distance. If ticked will ignore Order Distance value')
        local slotSizeD = Input('Min. Order Size ', 0, 'Minimum order size if Dynamic Slot Size activated. Static amount if Dynamic Slot Size disabled.')
        local autoSlot = Input('Dynamic Order Size', true, 'Dynamically change order size. Calculated based on distance to SL price.')
                
    InputGroupHeader('Profit')
        local RR = Input('Target Reward to Risk', 1, '2 = reward 2x risk. At this level bot will start looking to close position.')
        local RRtrailing = Input('SL RR Trailing', 0, 'RR to start trailing SL. 0 is disable. 1st step is moving SL to entry.')
        local RRhalving = Input('RR Halving', false, 'IF RR Trailing enabled, halving the size at trailing point.')
        local okScalping = Input('Scalping', false, 'Take profit at min % below when price below target RR.')
        local minScalping = Input('Min. scalping profit %', 0.1, 'TP when profit (price change) above this value and ADX SMA < threshold.')

    if startBal == 0 then 
        DeactivateBot('Please enter trading balance', true)
    end

---------------------
-- DATA & PRICES
    local getMarket = PriceMarket()
    local getAccount = AccountGuid()
    local cp = CurrentPrice(getMarket)
    local mainInterval = CurrentInterval()

    local h, l, c = OptimizedForInterval(mainInterval, function()
        local o = useHA and HeikinOpenPrices(mainInterval) or OpenPrices(mainInterval)
        local c = useHA and HeikenClosePrices(mainInterval) or ClosePrices(mainInterval)
        local h = useHA and Max(HighPrices(mainInterval), o, c) or HighPrices(mainInterval)
        local l = useHA and Min(LowPrices(mainInterval), o, c) or LowPrices(mainInterval)
        
        return h, l, c
        end)


---------------------
-- Indicators
    InputGroupHeader('Supertrend')
        local stFilter = Input('Supertrend Filter', false, 'Filter order based on Supertrend. Example long signals will only executed if Supertrend is uptrend.')
        local stPeriod = Input('Period', 13)
        local stMulti = Input('ATR Multiplier', 5)
        local supertrend = CC_SuperTrendExt(h, l, c, Average(h, l), stPeriod, stMulti, true)
        Plot(0, 'Supertrend', supertrend)

---------------------
-- persistent storage

    if not inited then
    bot = Load('bot', {})

    if Count(bot) == 0 then
        -- positions
        bot.longPosId = NewGuid()
        bot.shortPosId = NewGuid()
        bot.timerL = Time()
        bot.timerS = Time()
        bot.longFilled = 0
        bot.shortFilled = 0
        bot.longTrail = 0
        bot.shortTrail = 0
        bot.longHalving = 0
        bot.shortHalving = 0
        bot.targetPriceL = cp.ask
        bot.targetPriceS = cp.bid
        bot.orderDistanceL = 0
        bot.orderDistanceS = 0
        bot.highest_delta_l = 0
        bot.highest_delta_s = 0
        bot.prevLongSLP = 0
        bot.prevShortSLP = 0
        bot.longCycle = 0
        bot.shortCycle = 0
        
        -- stat
        bot.highestP = 0
        bot.lowestP = 0
        bot.SRCounter = 0
        bot.BRCounter = 0
        
        -- signal
        bot.signal1 = SignalNone
        bot.signal2 = SignalNone
        bot.signal3 = SignalNone 
    end

    inited = true
    end


---------------------
-- POSITIONS

    local dir_l = GetPositionDirection(bot.longPosId)
    local aep_l = GetPositionEnterPrice(bot.longPosId)
    local pamt_l = GetPositionAmount(bot.longPosId)
    local delta_l = pamt_l > 0 and (cp.close - aep_l) / aep_l * 100 or 0
    if delta_l > bot.highest_delta_l then 
        bot.highest_delta_l = delta_l 
    end
    --[[Plot(9, 'delta_l', delta_l)
    Plot(9, 'bot.highest_delta_l', bot.highest_delta_l, Red)--]]

    local dir_s = GetPositionDirection(bot.shortPosId)
    local aep_s = GetPositionEnterPrice(bot.shortPosId)
    local pamt_s = GetPositionAmount(bot.shortPosId)
    local delta_s = pamt_s > 0 and (aep_s - cp.close) / aep_s * 100 or 0
    if delta_s > bot.highest_delta_s then 
        bot.highest_delta_s = delta_s 
    end
    --[[Plot(8, 'delta_s', delta_s)
    Plot(8, 'bot.highest_delta_s', bot.highest_delta_s, Red)--]]

    -- manage position ids
    if pamt_l == 0 and IsPositionClosed(bot.longPosId) then
        if IsAnyOrderOpen(bot.longPosId) then
            CancelAllOrders(bot.longPosId)
        else
            bot.longPosId = NewGuid()
            dir_l = GetPositionDirection(bot.longPosId)
            aep_l = GetPositionEnterPrice(bot.longPosId)
            pamt_l = GetPositionAmount(bot.longPosId)
            bot.longTrail = 0 
            bot.longFilled = 0
            bot.longHalving = 0
            bot.highest_delta_l = 0
            if okCycle then 
                bot.longCycle = 1 
            end
        end
    end
        
    if pamt_s == 0 and IsPositionClosed(bot.shortPosId) then
        if IsAnyOrderOpen(bot.shortPosId) then
            CancelAllOrders(bot.shortPosId)
        else
            bot.shortPosId = NewGuid()
            dir_s = GetPositionDirection(bot.shortPosId)
            aep_s = GetPositionEnterPrice(bot.shortPosId)
            pamt_s = GetPositionAmount(bot.shortPosId)
            bot.shortTrail = 0
            bot.shortFilled = 0
            bot.shortHalving = 0
            bot.highest_delta_s = 0
            if okCycle then 
                bot.shortCycle = 1
            end
        end
    end

    -- get pos id
        local getPositionId = function(isLong)
            return isLong and bot.longPosId or bot.shortPosId
        end 


---------------------
-- WALLET CHECK
    local profitLabel = ProfitLabel(getMarket)
    if profitLabel == nil then profitLabel = QuoteCurrency(getMarket) end

    -- inverse or not
        if profitLabel == 'USD' or profitLabel == 'USDT' or profitLabel == 'BUSD' or profitLabel == 'USDC' then
            isInverse = false
        else
            isInverse = true
        end

    -- check balance usage
        if pamt_l > 0 then
            usedLong = isInverse and ((pamt_l * contVal) / leverage) / aep_l or (pamt_l * aep_l) / leverage
            getProfitL = isInverse and (cp.close - aep_l) * (leverage * usedLong) / cp.close or (cp.close - aep_l) * pamt_l
        else
            getProfitL = 0
            usedLong = 0
        end

        if pamt_s > 0 then
            usedShort = isInverse and ((pamt_s * contVal) / leverage) / aep_s or (pamt_s * aep_s) / leverage
            getProfitS = isInverse and (aep_s - cp.close) * (leverage * usedShort) / cp.close or (aep_s - cp.close) * pamt_s
        else
            getProfitS = 0
            usedShort = 0
        end

        local getProfit    = getProfitL + getProfitS
        local botProfit    = GetBotProfit()
        local netbotProfit = botProfit + getProfit

        if botProfit > bot.highestP then
            bot.highestP = botProfit
        elseif botProfit < bot.lowestP then
            bot.lowestP = botProfit
        end

        local balance = Balance()
        local xchangeBal = balance.total
        local walletBal  = testWallet and testBal + botProfit
                            or xchangeBal
        local workBal    = usedLong + usedShort - getProfit

        if dynamicHodl and botProfit >= bot.highestP * 0.8 then
            profitOut = 0
        end

        local botBalance = startBal + botProfit
        local netBalance = startBal + netbotProfit - usedLong - usedShort

        local maxCost = profitOut > 0 and botProfit > 0
                            and botBalance - (botProfit * profitOut / 100)
                            or botBalance

    -- BALANCE WARNING
        local balRatio = workBal / maxCost
        local okBalance = walletBal >= botBalance
        local maxBalance = startBal + bot.highestP

        if not okBalance then
            LogWarning('TRADING BALANCE: '..botBalance..' < EXCHANGE BALANCE: '..walletBal)
        end

        if maxCost <= 0 then
            DeactivateBot('OUT of TRADING BALANCE', true)
        end

        if balRatio > 0.8 then
            LogWarning('Working balance is > 80% of Budget!!!')
        end

        if balRatio > bot.BRCounter then
            bot.BRCounter = balRatio
        end

        if maxBalance - botBalance >= startBal and strictBudget then 
            okLong = false 
            FEL = true
            okShort = false 
            FES = true 
            if pamt_l == 0 and pamt_s == 0 then 
                DeactivateBot('LOSS < STARTING BALANCE', true)
            end 
        end--]]


---------------------
-- WTF
    if wtfBal then
        if balRatio > reduceTrigger then
            DeactivateBot('Deactivated because over budget', true)
        end
        --Log('STOP at over budget safety activated. Is this a backtest ?', Yellow)
    end

    if wtfStop then
        if pamt_l == 0 and pamt_s == 0 then
            DeactivateBot('Deactivated by No Position', true)
        end

        if CurrentMinute() == 00 then
            Log('Deactivate on No Position is active.', Yellow)
        end
    end
    

---------------------
-- DYNAMICS
    -- ENTRY
        local longPrice = cp.bid
        local LLP = LastLongPrice()
        local shortPrice = cp.ask
        local LSP = LastShortPrice()

    -- SL
        -- LONG
            if slTrigger == 0 then  
                longRisk = stFilter and supertrend or GetLow(l, 13)
            else 
                longRisk = SubPerc(longPrice, slTrigger)
            end
            local longSLP = longRisk

            if pamt_l != 0 then 
                longRisk = Load('longRisk')
                longSLP = longRisk 
            end 

            if RRtrailing > 0 and bot.longTrail >= 1 then 
                longSLP = aep_l + ((aep_l - longRisk) * RRtrailing * (bot.longTrail - 1))
            end

            local longRisk_range = pamt_l == 0 and longPrice - longRisk or aep_l - longRisk
            local longRisk_perc = longRisk_range / aep_l * 100
            local longSL_range = longPrice - longRisk
            --Log('longRisk '..longRisk..' longSLP '..longSLP..' longRisk_range '..longRisk_range)

        -- SHORT                
            if slTrigger == 0 then
                shortRisk = stFilter and supertrend or GetHigh(h, 13)
            else 
                shortRisk = AddPerc(shortPrice, slTrigger)
            end
            local shortSLP = shortRisk

            if pamt_s != 0 then 
                shortRisk = Load('shortRisk')
                shortSLP = shortRisk 
            end

            if RRtrailing > 0 and bot.shortTrail >= 1 then 
                shortSLP = aep_s - ((shortRisk - aep_s) * RRtrailing * (bot.shortTrail - 1))
            end

            local shortRisk_range = pamt_s == 0 and shortRisk - shortPrice or shortRisk - aep_s
            local shortRisk_perc = shortRisk_range / aep_s * 100
            local shortSL_range = shortRisk - shortPrice
            --Log('shortRisk '..shortRisk..' shortSLP '..shortSLP..' shortRisk_range '..shortRisk_range)
            --Plot(0, 'longRisk', longRisk)
            --Plot(0, 'shortRisk', shortRisk)

    -- TP RR
        -- LONG
            if pamt_l != 0 and bot.longTrail == 0 then 
                bot.targetPriceL = aep_l + (longRisk_range * RR)
            end

        -- SHORT
            if pamt_s != 0 and bot.shortTrail == 0 then 
                bot.targetPriceS = aep_s - (shortRisk_range * RR)
            end

        -- the force
            if FEL then 
                okLong = false
                okProfitL = false
                exitPriceL = cp.ask
                LET = 'Forced'
                Log('FORCE EXIT LONG POSITION ACTIVATED', Red) 
            end 

            if FES then
                okShort = false
                okProfitS = false
                exitPriceS = cp.bid
                SET = 'Forced'
                Log('FORCE EXIT SHORT POSITION ACTIVATED', Red) 
            end      


---------------------
-- SLOT SIZE
    if autoSlot then
        local risk = slotBudget / 100 * maxCost
        --Log('risk '..risk..' longRisk_range '..longRisk_range..' shortRisk_range '..shortRisk_range)  

        local sizeL = isInverse and ((risk * longRisk) / longSL_range) * longPrice / contVal
                        or risk / longSL_range
        slotSizeL = sizeL > slotSizeD and sizeL or slotSizeD
        
        local sizeS = isInverse and ((risk * shortRisk) / shortSL_range) * shortPrice / contVal
                        or risk / shortSL_range
        slotSizeS = sizeS > slotSizeD and sizeS or slotSizeD
        --Log('slotSizeL '..slotSizeL..' slotSizeS '..slotSizeS)
    else
        slotSizeL = slotSizeD 
        slotSizeS = slotSizeD 
    end

    local potentialCostL = isInverse and slotSizeL * contVal / longPrice / leverage or slotSizeL * longPrice / leverage
    local potentialLostL = isInverse and slotSizeL * contVal / longSLP * longRisk_range / longSLP or slotSizeL * longRisk_range
    local maxUsedL = longRisk_range > 0 and potentialCostL + potentialLostL or 0

    local potentialCostS = isInverse and slotSizeS * contVal / shortPrice / leverage or slotSizeS * shortPrice / leverage
    local potentialLostS = isInverse and slotSizeS * contVal / shortSLP * shortRisk_range / shortSLP or slotSizeS * shortRisk_range
    local maxUsedS = shortRisk_range > 0 and potentialCostS + potentialLostS or 0
    --Log('maxUsedL '..maxUsedL)
    --Log('maxUsedS '..maxUsedS)

    local okCostL = maxUsedL > 0 and maxUsedL < (maxCost * reduceTrigger * 0.8) - workBal 
    local okCostS = maxUsedS > 0 and maxUsedS < (maxCost * reduceTrigger * 0.8) - workBal 
    --[[if okCostL then 
        PlotSignalBar(5, Green)
    end 
    if okCostS then 
        PlotSignalBar(6, Red)
    end--]]


---------------------
-- EXECUTION
    -- Order Count
        if orderCount > 1 and bot.longFilled == 1 and bot.longTrail == 0 then 
            bot.orderDistanceL = dynamicDist and PercentageChange(longRisk, aep_l) / orderCount or orderDistance
        end

        if orderCount > 1 and bot.shortFilled == 1 and bot.shortTrail == 0 then
            bot.orderDistanceS = dynamicDist and PercentageChange(aep_s, shortRisk) / orderCount or orderDistance
        end

    -- Signals
        if signalPK1 == '' then DeactivateBot('Bot need at least one Signal Identifier', true) end

        local getSignal1 = GetRemoteSignal(signalPK1)
        if bot.signal1 != getSignal1 and (getSignal1 == SignalLong or getSignal1 == SignalShort) then 
            bot.signal1 = getSignal1
        end

        local getSignal2 = GetRemoteSignal(signalPK2)
        if bot.signal2 != getSignal2 and (getSignal2 == SignalLong or getSignal2 == SignalShort) then 
            bot.signal2 = getSignal2
        end

        local getSignal3 = GetRemoteSignal(signalPK3)
        if bot.signal3 != getSignal3 and (getSignal3 == SignalLong or getSignal3 == SignalShort) then 
            bot.signal3 = getSignal3
        end
    
    --Cycle 
        if okCycle then
            ChartSetOptions(-2, 'Position Cycle Detector')
            if bot.longCycle == 1 and pamt_l == 0 and c < supertrend then 
                bot.longCycle = 0 
                PlotSignalBar(-2, White)
            elseif bot.shortCycle == 1 and pamt_s == 0 and c > supertrend then 
                bot.shortCycle = 0 
                PlotSignalBar(-2, White)
            end
        end 

    --LONG
        local longcancelTPSL = false
        --OPEN
            local longOpen1 = pamt_l == 0
                                and IfElse(signalPK1 != '', bot.signal1 == SignalLong or getSignal1 == SignalReservedA, false)
                                and IfElse(signalPK2 != '', bot.signal2 == SignalLong, true)
                                and IfElse(signalPK3 != '', bot.signal3 == SignalLong, true) 
                                and IfElse(stFilter, l > supertrend, true)

            local longOpen2 = orderCount > 1
                                and pamt_l != 0
                                and longPrice < SubPerc(LLP, bot.orderDistanceL)
                                and longPrice > longSLP

            if longOpen1 then 
                LT = '1stOrder'
            elseif longOpen2 then
                LT = 'DCA'
            elseif FOL then 
                LT = 'ForceLong'
            end

            local longOpen = okLong
                                and okBalance
                                and okCostL
                                and bot.longFilled < orderCount
                                and IfElse(okCycle, bot.longCycle == 0, true)
                                and IfElse(oneWay, pamt_s == 0, true)
                                and (longOpen1 or longOpen2)

        --EXIT                        
            local longExit1 = pamt_l > 0
                                and cp.close > bot.targetPriceL
                                
            local longExit2 = pamt_l > 0
                                and getSignal1 == SignalExitLong

            local longExit3 = pamt_l > 0 
                                and okScalping
                                and delta_l > minScalping

            if longExit1 then 
                LET = 'RR'
            elseif longExit2 then 
                LET = 'Signal 1'
            elseif longExit3 then 
                LET = 'Scalp'
            end

            local longExit = okProfitL
                                and not FEL
                                and delta_l > 0
                                and (longExit1 or longExit2 or longExit3)

        --DERISK
            local okLongDerisk1 = pamt_l > 0 
                                    and IfElse(RRhalving, cp.close < longSLP, true)

            local okLongDerisk2 = pamt_l > 0
                                    and RRtrailing > 0
                                    and RRhalving
                                    and bot.longHalving < RR
                                    and cp.close < bot.targetPriceL

            if okLongDerisk1 then 
                LDP = IfElse(longSLP <= aep_l, longSLP, cp.ask)
                LDT = 'SL'
            elseif okLongDerisk2 then
                LDP = aep_l + (RRtrailing * longRisk_range * (bot.longHalving + 1)) 
                LDT = 'Halving'
                PlotSignalBar(3, Green)
            else 
                LDP = longSLP
                LDT = ''
            end

            local okLongDerisk = okDerisk
                                    and pamt_l > 0
                                    and not longExit 
                                    and not FEL
                                    and (okLongDerisk1 or okLongDerisk2)
        
        --RR TRAILING 
            local longRRT = pamt_l > 0
                                and RRtrailing > 0 
                                and cp.close > aep_l + (RRtrailing * longRisk_range * (bot.longTrail + 1))
            
            if longRRT and bot.longTrail < (RR / RRtrailing) then
                bot.longTrail = bot.longTrail + 1
                longcancelTPSL = true
            end

    --SHORT
        local shortcancelTPSL = false 
        --OPEN       
            local shortOpen1 = pamt_s == 0 
                                and IfElse(signalPK1 != '', bot.signal1 == SignalShort or getSignal1 == SignalReservedA, false)
                                and IfElse(signalPK2 != '', bot.signal2 == SignalShort, true)
                                and IfElse(signalPK3 != '', bot.signal3 == SignalShort, true) 
                                and IfElse(stFilter, h < supertrend, true)

            local shortOpen2 = orderCount > 1
                                and pamt_s != 0
                                and shortPrice > AddPerc(LSP, bot.orderDistanceS)
                                and shortPrice < shortSLP

            if shortOpen1 then 
                ST = '1stOrder'
            elseif shortOpen2 then 
                ST = 'DCA'
            elseif FOS then 
                LT = 'ForceShort'
            end

            local shortOpen = okShort
                                and okBalance
                                and okCostS
                                and bot.shortFilled < orderCount
                                and IfElse(okCycle, bot.shortCycle == 0, true)
                                and IfElse(oneWay, pamt_l == 0, true)
                                and (shortOpen1 or shortOpen2)
        
        -- EXIT                        
            local shortExit1 = pamt_s > 0
                                and cp.close < bot.targetPriceS

            local shortExit2 = pamt_s > 0
                                and getSignal1 == SignalExitShort
                                
            local shortExit3 = pamt_s > 0 
                                and okScalping 
                                and delta_s > minScalping

            if shortExit1 then 
                SET = 'RR'
            elseif shortExit2 then
                SET = 'Signal 1'
            elseif shortExit3 then 
                SET = 'Scalp'
            end

            local shortExit = okProfitS
                                and not FES
                                and delta_s > 0
                                and (shortExit1 or shortExit2 or shortExit3)

        -- DERISK
            local okShortDerisk1 = pamt_s > 0 
                                    and IfElse(RRhalving, cp.close > shortSLP, true)

            local okShortDerisk2 = RRtrailing > 0
                                    and RRhalving
                                    and bot.shortHalving < RR
                                    and cp.close > bot.targetPriceS

            if okShortDerisk1 then 
                SDP = IfElse(shortSLP >= aep_s, shortSLP, cp.bid)
                SDT = 'Derisking'
            elseif okShortDerisk2 then 
                SDP = aep_s - (RRtrailing * shortRisk_range * (bot.shortHalving + 1)) 
                SDT = 'Halving'
                PlotSignalBar(3, Red)
            else 
                SDP = shortSLP
                SDT = ''
            end
            local okShortDerisk = okDerisk
                                    and pamt_s > 0
                                    and not shortExit
                                    and not FES
                                    and (okShortDerisk1 or okShortDerisk2)

        -- RR Trailing 
            local shortRRT = pamt_s > 0 
                                and RRtrailing > 0 
                                and cp.close < aep_s - (RRtrailing * shortRisk_range * (bot.shortTrail + 1)) 

            if shortRRT and bot.shortTrail < (RR / RRtrailing) then
                bot.shortTrail = bot.shortTrail + 1
                shortcancelTPSL = true
            end
    
    -- SLP     
        if pamt_l > 0 and bot.prevLongSLP != longSLP then 
            bot.prevLongSLP = longSLP
            longcancelTPSL = true
            Log('longSLP changed')
        elseif pamt_s > 0 and bot.prevShortSLP != shortSLP then
            bot.prevShortSLP = shortSLP
            shortcancelTPSL = true
            Log('shortSLP changed')
        end 


---------------------
-- FUNCTIONS
    -- ENTRY
        function slot(isLong, price, amount, timer, trigger, canPlace) 
            local name = isLong and 'L' or 'S'
            local cmd = isLong and PlaceGoLongOrder or PlaceGoShortOrder
            local posId = getPositionId(isLong)
            local filled = isLong and bot.longFilled + 1 or bot.shortFilled + 1
            
            local oid = Load(name..'oid', '') -- order id
            if oid != '' then
                local order = OrderContainer(oid)
            
                if order.isOpen then
                    if not canPlace then
                        CancelOrder(oid)
                        LogWarning('Not allowed right now '..name)
                    end
                elseif order.isFilled then 
                    if isLong then
                        bot.longFilled = bot.longFilled + 1
                        longcancelTPSL = true
                    else
                        bot.shortFilled = bot.shortFilled + 1
                        shortcancelTPSL = true
                    end
                    oid = '' 
                else 
                    oid = ''
                end
            else
                if canPlace and TradeOncePerBar(mainInterval, posId) and Time() >= timer then
                    oid = cmd(price, amount, {timeout = mainInterval * 60, type = LimitOrderType, note = name..filled..'-'..trigger, positionId = posId})
                    if isLong then 
                        if pamt_l == 0 then
                            Save('longRisk', longRisk)
                        end
                    else 
                        if pamt_s == 0 then
                            Save('shortRisk', shortRisk)
                        end
                    end
                end
            end
            
            Save(name..'oid', oid)
        end

    -- EXIT
        function updateTakeProfit (isLong, ET, canExit)
            local prefix = isLong and 'L' or 'S'
            local name = prefix .. ' TP'
            local oid = Load(prefix .. 'tp_oid', '')
            local posId = getPositionId(isLong)
            local cancelTPSL = isLong and longcancelTPSL or shortcancelTPSL

            if oid != '' then
                local order = OrderContainer(oid)

                if order.isOpen then
                    if not canExit then
                        CancelOrder(oid)
                        LogWarning('Exit cancelled '..name)
                    end
                else
                    oid = ''
                end
            else
                if canExit then
                    local pDelta = isLong and Round(delta_l, 2) or Round(delta_s, 2)
                    oid = PlaceExitPositionOrder({timeout = mainInterval * 60, type = LimitOrderType, note = name..'-'..ET..' '..pDelta..'%', positionId = posId})
                end
            end

            Save(prefix .. 'tp_oid', oid)
        end

    -- Balance Ratio REDUCTION
        function updatePositionManagement (isLong, amount, canReduce)
            local price = isLong and cp.bid - PriceStep() or cp.ask + PriceStep()
            local prefix = isLong and 'Long' or 'Short'
            local name = prefix .. ' Size Reduction'
            local oid = Load(prefix .. 'pos_oid', '')
            local posId = getPositionId(isLong)
            local cmd = isLong
                    and PlaceExitLongOrder
                    or PlaceExitShortOrder
            local timer = Load(prefix .. 'pos_timer', Time())

            if oid != '' then
                local order = OrderContainer(oid)

                if order.isOpen then                        
                    if not canReduce then
                        CancelOrder(oid)
                        LogWarning('Reduction cancelled '..name)
                    end
                else
                    bot.SRCounter = bot.SRCounter + 1
                    oid = ''
                end
            else
                if canReduce and Time() >= timer then
                    CancelAllOrders()
                    local pDelta = isLong and Round(delta_l, 2) or Round(delta_s, 2)
                    oid = cmd(price, amount, {market = getMarket, type = MarketOrderType, note = name..' '..pDelta..'%', timeout = mainInterval * 60, positionId = posId})
                    timer = Time() + 60 -- 1min   
                end
            end

            Save(prefix .. 'pos_oid', oid)
            Save(prefix .. 'pos_timer', timer)
        end

    -- Derisking REDUCTION
        function derisking (isLong, amount, price, trigger, DT, canDerisk)
            local openDT = Load('openDT', DT)
            local prefix = isLong and 'Long ' or 'Short '
            local halvingCount = isLong and bot.longHalving + 1 or bot.shortHalving + 1
            local name = prefix..' '..DT
            local oid = Load(prefix .. 'derisk_oid', '')
            local posId = getPositionId(isLong)
            local cmd = isLong and PlaceExitLongOrder or PlaceExitShortOrder
            local cancelTPSL = isLong and longcancelTPSL or shortcancelTPSL 

            -- amount check
                if DT == 'Halving' then 
                    reduceOrderType = LimitOrderType
                    name = prefix..' '..DT..'-'..halvingCount
                    if isLong then 
                        amount = amount * 0.5
                    else
                        amount = amount * 0.5
                    end
                else 
                    if isLong then 
                        reduceOrderType = IfElse(RRhalving, MarketOrderType, StopMarketOrderType)
                    else 
                        reduceOrderType = IfElse(RRhalving, MarketOrderType, StopMarketOrderType)
                    end
                end
                
                if amount < MinimumTradeAmount(getMarket, price) then 
                    amount = amount
                end

            if oid != '' then
                local order = OrderContainer(oid)
                
                if order.isOpen then
                    if not canDerisk or cancelTPSL then
                        CancelOrder(oid)
                        Log(name..' cancelled', Yellow)
                    end
                    if openDT != DT then 
                        CancelOrder(oid)
                        Log(name..' updating', Yellow)
                    end
                elseif order.isFilled then
                    if isLong then
                        if DT == 'Halving' then 
                            bot.longHalving = bot.longHalving + 1
                        else 
                            bot.longCycle = 1
                        end
                        bot.timerL = Time() + (delay * 60)
                    else 
                        if DT == 'Halving' then 
                            bot.shortHalving = bot.shortHalving + 1
                        end
                        bot.timerS = Time() + (delay * 60)
                    end
                    oid = ''
                else
                    oid = '' 
                end
            else
                if canDerisk then
                    local pDelta = isLong and Round((price - aep_l) / price * 100, 2) or Round((aep_s - price) / price * 100, 2)
                    oid = cmd(price, amount, {market = getMarket, triggerPrice = trigger, type = reduceOrderType, note = name..' '..pDelta..'%', timeout = -1, positionId = posId})
                    Save('openDT', DT)
                end
            end
            Save(prefix .. 'derisk_oid', oid) 
        end

    -- CORE LOGIC HEDGE MODE
        slot(true, longPrice, slotSizeL, bot.timerL, LT, longOpen or FOL) -- long slot
        slot(false, shortPrice, slotSizeS, bot.timerS, ST, shortOpen or FOS) -- short slot

        updateTakeProfit(true, LET, longExit or FEL)
        updateTakeProfit(false, SET, shortExit or FES)

        updatePositionManagement(true, pamt_l, okReduce and pamt_l > 0 and balRatio > reduceTrigger and delta_l < delta_s)
        updatePositionManagement(false, pamt_s, okReduce and pamt_s > 0 and balRatio > reduceTrigger and delta_s < delta_l)

        derisking(true, pamt_l, LDP, longSLP, LDT, okLongDerisk)
        derisking(false, pamt_s, SDP, shortSLP, SDT, okShortDerisk)


--------------------- 
-- PLOT
    -- AEP Plot
        if aep_l > 0 then
            local posId = getPositionId(true)
            Plot(0, 'AvgEP Long', aep_l, {c=Green, id=posId, w=2})
            Plot(0, 'SL Long', longSLP, {c=Green, id=posId, w=1})
            --Plot(0, 'TP Long', exitPriceL, {c=Teal, id=posId, w=1})
        end

        if aep_s > 0 then
            local posId = getPositionId(false)
            Plot(0, 'AvgEP Short', aep_s, {c=Red, id=posId, w=2})
            Plot(0, 'SL Short', shortSLP, {c=Red, id=posId, w=1})
            --Plot(0, 'TP Short', exitPriceS, {c=Purple, id=posId, w=1})
        end

    --Signal Detector
        ChartSetOptions(2, 'Signals Detector. 1 = Long. -1 = Short')
        if signalPK1 != '' then 
            Plot(2, signalName1, IfElseIf(bot.signal1 == SignalLong, bot.signal1 == SignalShort, 1, -1, 0), Red)
        end

        if signalPK2 != '' then 
            Plot(2, signalName2, IfElseIf(bot.signal2 == SignalLong, bot.signal2 == SignalShort, 1, -1, 0), Green)
        end

        if signalPK3 != '' then 
            Plot(2, signalName3, IfElseIf(bot.signal3 == SignalLong, bot.signal3 == SignalShort, 1, -1, 0), SkyBlue)
        end
 
    --[[
    ChartSetOptions(1, 'Exposure')
        local lineLong = IfElse(pamt_l > 0, pamt_l, 0)
        local lineShort = IfElse(pamt_s > 0, -pamt_s, 0)
        Plot(10, 'Longs', lineLong, {c=Green, s=Step})
        Plot(10, 'Shorts', lineShort, {c=Red, s=Step})
    --]]

    --[[ChartSetOptions(1, 'Balance Monitor')
        Plot(11, 'Net Balance', netBalance, {c=Orange, s=Step})
        Plot(11, 'Trading Balance', maxCost, {c=White, s=Step})
        Plot(11, 'WorkBal', workBal, {c=Red, s=Step})
        Plot(11, 'Bot Profit', botProfit, {c=DarkGreen, s=Step})
        Plot(11, 'Bot Balance', botBalance, {c=Yellow, s=Step})
        Plot(11, 'Max Balance', maxBalance, {c=Green, s=Step})--]]
 

---------------------
-- FINAL REPORT
    Finalize(function()
        CustomReport('Current Balance Ratio', Round(100 * balRatio, 2)..'%')
        CustomReport('Highest Balance Ratio', Round(100 * bot.BRCounter, 2)..'%')
        CustomReport('Size Reduction', bot.SRCounter..' times')
        CustomReport('Final Wallet Balance', Round(walletBal, 5)..' '..profitLabel)
        CustomReport('Final Net Balance', Round(netBalance, 5)..' '..profitLabel)
        CustomReport('Highest Profit', Round(bot.highestP, 5)..' '..profitLabel)
        CustomReport('Lowest Profit', Round(bot.lowestP, 5)..' '..profitLabel)
        if testWallet then
            local realprofitPercent = Round(botProfit / startBal * 100, 2) 
            local runprofitPercent = Round(netbotProfit / startBal * 100, 2) 
            CustomReport('Running Profit %: ', runprofitPercent..'%')
            CustomReport('Realized Profit %: ', realprofitPercent..'%')
            CustomReport('Profit to Highest Bal Ratio', Round(realprofitPercent / (100 * bot.BRCounter), 2))
        end    
    end)
 

---------------------
-- INFO 
    --positions data
        if showData then
            if signalPK3 != '' then Log(signalName3..': '..getSignal3) end
            if signalPK2 != '' then Log(signalName2..': '..getSignal2) end
            if signalPK1 != '' then Log(signalName1..': '..getSignal1) end

            if pamt_l > 0 then
                if RRtrailing > 0 then 
                    Log('Next LONG halving is at '..LDP)
                end
                Log('LONG Delta: '..Round(delta_l, 2)..
                    '% | Amount: '..getProfitL)
            end

            if pamt_s > 0 then 
                if RRtrailing > 0 then 
                    Log('Next SHORT halving is at '..SDP)
                end
                Log('SHORT Delta: '..Round(delta_s, 2)..
                    '% | Amount: '..getProfitS)
            end

    -- balance data
            Log('Balance Monitor -> Wallet: '..Round(walletBal, 5)..' '..profitLabel..
                ' | Highet: '..Round(maxBalance, 5)..' '..profitLabel..
                ' | Bot: '..Round(botBalance, 5)..' '..profitLabel..
                ' | Trading: '..Round(maxCost, 5)..' '..profitLabel..
                ' | Net: '..Round(netBalance, 5)..' '..profitLabel..
                ' | Working: '..Round(workBal, 5)..' '..profitLabel..
                ' | Ratio: '..balRatio
                )
            Log(' ')
        end--]]     

---------------------

Save('bot', bot)

2 Comments

Sign in to leave a comment.

C
Costa over 2 years ago

Hey ! Thank you so much for these customization diamonds!
I am a young padawan who recently boarded the boat and I am truly honored to use your tool
This may give me the independent power to learn Python and Lua code and perhaps customize the MK3 with my own full indicator, but for now I'm really happy to be able to export any signal from the bot and test the good things I have I would like to use for the rest of the journey ! glad to meet you

S
smokyho over 2 years ago

Thank you very much for your kind words and good luck!