SMM 2.0 SMOD

stable
By smokyho in Trading Bots Published August 2023 👁 1,612 views 💬 0 comments

Description

Modification of Phsai's SMM 2.0 ---Camarilla Levels--- Adding Camarilla levels for dynamic range based in daily, weekly or monthly intervals. When activated bot will only trade between the Third Support and Third Resistance and take action (if selected) at Fourth Support or Fourth Resistance. More about Camarilla https://pivotboss.com/2010/05/31/the-camarilla-equation-explained/ ---Balance Management--- As usual with my script, you have to enter the amount you allocated for bot to work a.k.a Trading Balance. With this bot has new stop loss trigger which is Balance Ratio. This is the default SL type with default value 1 which means if Working Balance (Used balance for positions + position PNL ) is >= Trading Balance then all positions will be closed. If you set it to 0.8 then SL will triggered when working balance is 80% of trading balance. For backtest there is an option to stop when the Balance Ration SL triggered. The profit is not compounded into Trading Balance but if the wallet balance is < trading balance then you will get a notification in the log about it and bot will use the wallet balance for Balance Ratio. It is better to enter trading balance lower than wallet balance to give some room (loss). ---Order at New Candle Open--- Pay attention to the Order Timeout. Orders will be place on candle close (or new candle open) in this interval and will be cancelled if not filled within the forming candle. For example, if you set it to 60 then bot will be placing order at new 1H candle and will cancel it if it is not filled within 1 hour. If the order is canceled by % value then new one will be placed at the next candle. ---FIX 1.0--- 1. Fixing position ID. The mistake causing miscalculation in balance usage. 2. Fixing time interval misconfiguration for Camarilla and giving more options. 3. Adding position entry plot to better visualize the trades in back test. 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 Lightning: [email protected]
HaasScript
-- [pshaiBot] Simple Market Maker 2.0
-- Author: pshai
-- Mods by: smokyho
-- FIX 1.0

EnableHighSpeedUpdates(true)
HideOrderSettings()
HideTradeAmountSettings()

-- =======================================================================================================
-- == SMOKYHO MOD
-- =======================================================================================================
    InputGroupHeader('Bot Settings')
        local tradeBal = Input("Trading Balance", 0, "Trading balance in QUOTE currency")
        if WalletAmount() &lt; tradeBal then 
            tradeBal = WalletAmount() 
            Log("Trading balance adjusted because your wallet balance &lt; trading balance", Yellow)
        end
	    local useCam = Input('Use Camarilla for range', false, "Use Camarilla levels to define trade range. If activated it will ignore ONLY value in upper/lower Price Limit entry")
        local rangeInterval = InputInterval('Camarilla Interval', 1440, 'Time interval for Camarilla levels') --InputOptions('Camarilla Interval', 'Daily', camIntervals, 'Time interval for Camarilla levels')
        local showInfo = Input('Details in Log', false, 'Show detail information in logs about the bot condition')
    
    InputGroupHeader('Backtest Settings')
        local wtfBal = Input('Deactivate on Over Budget', false, 'Deactivate when Working Balance >= Starting Balance')

    -- price and data
        local getMarket = PriceMarket()
        local cp = CurrentPrice()
	    
        if useCam then
            s3, r3, s4, r4 = OptimizedForInterval(CurrentInterval(), function()
                local high = HighPrices(rangeInterval)
                local low = LowPrices(rangeInterval)
                local close = ClosePrices(rangeInterval)
                local range = high - low
                local r3 = close + (range * 1.1 / 4)
                local s3 = close - (range * 1.1 / 4)
                local r4 = close + (range * 1.1 / 2)
                local s4 = close - (range * 1.1 / 2)

                return s3, r3, s4, r4
            end)
        end

    
-- =======================================================================================================
-- == INIT
-- =======================================================================================================

    if not init then
        bot = Load('bot', {})
        -- id 
            bot.longPosId = NewGuid()
            bot.shortPosId = NewGuid()

        -- stat
            bot.SRCounter = 0
            bot.BRCounter = 0
            bot.SACounter = 0
            bot.slotCounter = 0

        -- create config objects
        long_config = {}
        short_config = {}

        init = true
    end

    __isSpot = MarketType() == SpotTrading

    -- budget management 
        local realizedBotProfit = GetBotProfit() 
        local longProfit = GetCurrentProfit(PositionLong)
        local shortProfit = GetCurrentProfit(PositionShort)
        local usedLong = UsedMargin(getMarket, GetPositionEnterPrice(bot.longPosId), LongAmount(getMarket), GetLeverage())
        local usedShort = UsedMargin(getMarket, GetPositionEnterPrice(bot.shortPosId), ShortAmount(getMarket), GetLeverage())
        local workingBalance = (usedLong + usedShort - longProfit - shortProfit)
        local balRatio = workingBalance / tradeBal


-- =======================================================================================================
-- == OPTIONS ARRAYS
-- =======================================================================================================

    local orderTypes = {
        limit = 'LIMIT',
        postLimit = 'LIMIT (Post-Only)',
        market = 'MARKET'
    }

    local marginTypes = {
        base = 'BASE',
        quote = 'QUOTE'
    }

    local tpTypes = {
        none = 'NONE',
        priceChange = 'PRICE CHANGE %',
        pnl = 'TRADING BALANCE %'
    }

    local slTypes = {
        br = 'BALANCE RATIO',
        priceChange = 'PRICE CHANGE %',
        pnl = 'OPEN PNL'
    }

    local stopActions = {
        none = 'NONE',
        exit = 'CLOSE POSITION'
    }

-- =======================================================================================================
-- == INPUTS
-- =======================================================================================================

    ---------------------------------------------------------------------------------
    -- CONFIG BUILDER
    function buildConfigs(config, prefix)
        local group = prefix .. ' Settings'

        InputButton('------ '..prefix..' ENTRY SETTINGS ------', ||nil, '', group)
        config.enabled = Input('Trade '..prefix, false, 'If true, bot will be placing long entries.', group)
        config.marginType = InputOptions(prefix..' Margin Type', marginTypes.quote, marginTypes, 'Margin type.', group)
        config.orderMargin = Input(prefix..' Order Margin', 0, 'Order size, based on the margin type. This value DOES NOT account for leverage, but is rather the notional size. 10 USDT order margin on 10x leverage would result in 1 USDT used margin per order.', group)
        config.orderMarginMult = Input(prefix..' Order Margin Multiplier', 1, 'If set to 1, order size is constant. If higher than 1, order size will INCREASE per entry. If lower than 1, order size will DECREASE per entry.', group)
        config.triggerOffset = Input(prefix..' Trigger Offset %', 0, 'Price change required (downwards for longs, up for shorts) before an entry order is placed. Negative values will allow longs traded upwards and shorts down. Set this to 0 to disable.', group)
        config.orderOffset = Input(prefix..' Order Offset %', 0.382, 'How far the new order is placed from either current prices or its trigger price.', group)
        config.orderTimeout = Input(prefix..' Order Timeout (MINUTES)', 60, 'Entry order timeout in MINUTES (for LIMIT order type). Set this to -1 to disable.', group)
        config.orderDelta = Input(prefix..' Order Delta Cancel %', 0, 'How far the prices can run before the entry order is cancelled and replaced.', group)
        config.orderType = InputOptions(prefix..' Order Type', orderTypes.limit, orderTypes, 'Entry order type.', group)
        
        InputButton('------ '..prefix..' TAKE-PROFIT SETTINGS ------', ||nil, '', group)
        config.tpType = InputOptions(prefix..' Take-profit Type', tpTypes.priceChange, tpTypes, 'Take-profit type.', group)
        config.tpValue = Input(prefix..' Take-profit Value', 0.382, 'Take-profit value for the Take-profit Type.', group)
        config.tpOrderType = InputOptions(prefix..' Order Type', orderTypes.limit, orderTypes, 'Take-profit order type.', group)

        InputButton('------ '..prefix..' STOP-LOSS SETTINGS ------', ||nil, '', group)
        config.slType = InputOptions(prefix..' Stop-loss Type', slTypes.br, slTypes, 'Stop-loss type.', group)
        config.slValue = Input(prefix..' Stop-loss Value', 1, 'Stop-loss value for the Stop-loss Type.', group)
        config.slOrderType = InputOptions(prefix..' Order Type', orderTypes.limit, orderTypes, 'Stop-loss order type.', group)
        
        InputButton('------ '..prefix..' RANGE SETTINGS ------', ||nil, '', group)
        config.upperPrice = Input(prefix..' Upper Price Limit', 0, 'Upper price level where the bot will stop trading and goes into PAUSE regardless of stop-action (no deactivation).', group)
        config.upperAction = InputOptions(prefix..' Upper Limit Action', stopActions.none, stopActions, 'Upper price limit stop-action.', group)
        config.lowerPrice = Input(prefix..' Lower Price Limit', 0, 'Lower price level where the bot will stop trading and goes into PAUSE regardless of stop-action (no deactivation).', group)
        config.lowerAction = InputOptions(prefix..' Lower Limit Action', stopActions.none, stopActions, 'Lower price limit stop-action.', group)
        config.reset = Input(prefix..' Re-init Config', false, 'If true, configs will be re-initialized in next update (allows to continue trading without having to restart bot). It is advised to let bot only update once when this is enabled.', group)

        --range mod
            config.upperPrice = useCam and r4 or config.upperPrice
            config.lowerPrice = useCam and s4 or config.lowerPrice

        --plot the range
            if config.upperPrice > 0 then 
                Plot(0, 'Upper Limit', config.upperPrice, SkyBlue)
            end 
            if config.lowerPrice > 0 then
                Plot(0, 'Lower Limit', config.lowerPrice, Orange)
            end
    end

    
    ---------------------------------------------------------------------------------
    -- BUILD CONFIGS
        buildConfigs(long_config, 'Long')
        buildConfigs(short_config, 'Short')

    
-- =======================================================================================================
-- == FUNCTIONS
-- =======================================================================================================

    ---------------------------------------------------------------------------------
    -- NEW SLOT OBJECT
    function newSlot()
        return {
            id = '',
            oid = '',
            triggerPrice = 0,
            price = 0,
            size = 0,
            timeoutTime = 0
        }
    end

    ---------------------------------------------------------------------------------
    -- CONVERT ORDER TYPE
    function getOrderType(strType)
        if strType == orderTypes.limit then
            return LimitOrderType
        elseif strType == orderTypes.postLimit then
            return MakerOrCancelOrderType
        elseif strType == orderTypes.market then
            return MarketOrderType
        end

        LogError('Order type cannot be converted: ' .. strType)
    end

    ---------------------------------------------------------------------------------
    -- CONVERT TRADE AMOUNT
    function getOrderSize(config)
        -- get order margin from config
        local margin = config.orderMargin
        local res = 0

        -- multiply the amount
        for i = 1, config.entryCount do
            if i == 1 then
                res = margin
            else
                res = res * config.orderMarginMult
            end
        end

        -- convert order size
        if config.marginType == marginTypes.quote then
            res = res / ContractValue()
        end

        return res
    end

    ---------------------------------------------------------------------------------
    -- UPDATE POSITION
    function updatePid(config, cp, isLong)
        -- dont update if position ID empty
        if not config.pid then
            return
        end

        -- get position info
        local pos = PositionContainer(config.pid)
        
        -- reset position
        if pos.amount == 0 and pos.enterPrice > 0 then
            if IsAnyOrderOpen(config.pid) then
                CancelAllOrders(config.pid)
            else
                config.pid = NewGuid()
                config.entryCount = 1
                config.startPrice = isLong and cp.bid or cp.ask
                if isLong then 
                    bot.longPosId = config.pid
                else 
                    bot.shortPosId = config.pid
                end

                LogWarning('-- New PID generated for '..(isLong and 'LONG' or 'SHORT')..' position. --')
            end
        end
    end

    ---------------------------------------------------------------------------------
    -- RUN CONFIGS
    function runConfigs(config, isLong)
        -- dont run if disabled
        if not config.enabled then
            return
        end

        -- reset
        if config.reset then
            config.run = nil
            config.pid = nil
            config.startPrice = nil
            config.entryCount = nil
            config.entrySlot = nil

            LogWarning('-- Bot was RESET. --')
        end

        -- init some variables
        if not config.run then config.run = true end
        if not config.pid then 
            config.pid = NewGuid() 
            if isLong then 
                bot.longPosId = config.pid
            else 
                bot.shortPosId = config.pid
            end
        end
        if not config.startPrice then config.startPrice = isLong and cp.bid or cp.ask end
        if not config.entryCount then config.entryCount = 1 end
        if not config.entrySlot then config.entrySlot = newSlot() end

        -- run everything
        updatePid(config, cp, isLong)
        runRange(config, cp)
        runStoploss(config, cp, isLong)
        runTakeprofit(config, cp, isLong)

        -- range 
        local rangeSet = config.lowerPrice > 0 and config.upperPrice > 0
        if useCam then
            inRange = rangeSet and cp.bid > s3 and cp.ask &lt; r3 
        else
            inRange = rangeSet and cp.bid > config.lowerPrice and cp.ask &lt; config.upperPrice 
        end
        local aboveLower = cp.bid > config.lowerPrice
        local belowUpper = cp.ask &lt; config.upperPrice

        local okLong = false 
        local okShort = false
        if rangeSet then 
            okLong = inRange 
            okShort = inRange 
        else 
            if config.lowerPrice > 0 and config.upperPrice == 0 then 
                okLong = cp.bid > config.lowerPrice 
            end

            if config.lowerPrice == 0 and config.upperPrice > 0 then
                okShort = cp.ask &lt; config.upperPrice
            end
        end

        if isLong and MinutesTillCandleClose(config.orderTimeout) == 0 and IfElse(config.lowerPrice > 0, okLong, true) then 
            runEntry(config, cp, isLong)
        elseif not isLong and MinutesTillCandleClose(config.orderTimeout) == 0 and IfElse(config.upperPrice > 0, okShort, true) then 
            runEntry(config, cp, isLong) 
        end
        
    end


    ---------------------------------------------------------------------------------
    -- RUN RANGE
    function runRange(config, cp, isLong)
        -- return if not configs running
        if not config.run then
            return
        end

        -- get position info
        local pos = PositionContainer(config.pid)

        -- if upper price set and last traded price goes above, we stop running configs
        if config.upperPrice > 0 and cp.close > config.upperPrice then
            PlotSignalBar(-2, White)
            CancelAllOrders(config.pid)

            -- stop action
            if config.upperAction == stopActions.exit and pos.amount > 0 then
                PlaceExitPositionOrder(config.pid, {type = MarketOrderType, note = 'Stop-Action Exit'})
                bot.SACounter = bot.SACounter + 1
            end

            -- inform the user
            --LogWarning((isLong and 'LONG' or 'SHORT')..' CONFIG PAUSED: Price breached UPPER price limit of '..config.upperPrice..'.')
        end

        -- if lower price set and last traded price goes below, we stop running configs
        if config.lowerPrice > 0 and cp.close &lt; config.lowerPrice then
            PlotSignalBar(-2, White)
            CancelAllOrders(config.pid)

            -- stop action
            if config.lowerAction == stopActions.exit and pos.amount > 0 then
                PlaceExitPositionOrder(config.pid, {type = MarketOrderType, note = 'Stop-Action Exit'})
                bot.SACounter = bot.SACounter + 1
            end

            -- inform the user
            --LogWarning((isLong and 'LONG' or 'SHORT')..' CONFIG PAUSED: Price breached LOWER price limit of '..config.lowerPrice..'.')
        end
    end


    ---------------------------------------------------------------------------------
    -- RUN STOP-LOSS
    function runStoploss(config, cp, isLong)
        if not config.run then
            return
        end

        if wtfBal and workingBalance != 0 then
            if workingBalance >= tradeBal then
                DeactivateBot('Over Budget', true)
            end
        end

        local direction = isLong and PositionLong or PositionShort
        local trigger = false

        ----------
        -- balance ratio
        if config.slType == slTypes.br then
            trigger = workingBalance / tradeBal >= config.slValue
            
        ----------
        -- price change % based stop-loss
        elseif config.slType == slTypes.priceChange then
            trigger = StopLoss(config.slValue, config.pid, direction)
        
        ----------
        -- unrealized PnL based stop-loss
        elseif config.slType == slTypes.pnl then
            local pos = PositionContainer(config.pid)
            trigger = pos.profit &lt;= -config.slValue
        end

        if trigger then
            -- inform user
            LogWarning('-- Stop-Loss triggered. --')
            bot.SRCounter = bot.SRCounter + 1

            -- cancel all open orders
            CancelAllOrders(config.pid)

            -- set the price
            local price = isLong
                    and cp.bid
                    or cp.ask

            -- offset price a bit of using post-only limit
            if config.slOrderType == orderTypes.postLimit then
                price = isLong
                        and AddPerc(price, 0.01)
                        or SubPerc(price, 0.01)
            end
            
            -- place exit position order
            PlaceExitPositionOrder(
                config.pid,
                price,
                getOrderType( config.slOrderType ),
                (isLong and 'Long' or 'Short')..' Stop-Loss'
            )

            config.run = false
        end
    end


    ---------------------------------------------------------------------------------
    -- RUN TAKE-PROFIT
    function runTakeprofit(config, cp, isLong)
        if not config.run then
            return
        end

        local direction = isLong and PositionLong or PositionShort
        local trigger = false

        ----------
        -- no TP used, return
        if config.tpType == tpTypes.none then
            return

        ----------
        -- price change % based take-profit
        elseif config.tpType == tpTypes.priceChange then
            trigger = TakeProfit(config.tpValue, config.pid, direction)

        ----------
        -- unrealized PnL based take-profit
        elseif config.tpType == tpTypes.pnl then
            local pos = PositionContainer(config.pid)
            trigger = pos.profit >= config.tpValue * tradeBal / 100
            
        end

        if trigger then
            -- inform user
            LogWarning('-- Take-Profit triggered. --')

            -- cancel all open orders
            CancelAllOrders(config.pid)

            -- set the price
            local price = isLong
                    and cp.ask
                    or cp.bid

            -- offset price a bit of using post-only limit
            if config.tpOrderType == orderTypes.postLimit then
                price = isLong
                        and price + PriceStep(getMarket)
                        or price - PriceStep(getMarket)
            end
            
            -- place exit position order
            PlaceExitPositionOrder(
                config.pid,
                price,
                getOrderType( config.tpOrderType ),
                (isLong and 'Long' or 'Short')..' Take-Profit'
            )

            --config.run = false
        end
    end


    ---------------------------------------------------------------------------------
    -- RUN ENTRY
    function runEntry(config, cp, isLong)
        if not config.run then
            return
        end

        -- flag-variable for if we can place new entry or not
        local canPlace = false
        local pos = PositionContainer(config.pid)
        local LLP = LastLongPrice()  
        local LSP = LastShortPrice()

        ----------
        -- limit order type logic
        if config.orderType != orderTypes.market then
            
            -- is triggerOffset used?
            if config.triggerOffset != 0 then
                -- get last recorded price (value updated when order has filled, resets when position exits)
                local triggerLevel = config.startPrice

                -- calculate delta accordingly
                local delta = isLong
                        and Delta(triggerLevel, cp.bid)
                        or Delta(cp.ask, triggerLevel)
                
                -- allow new entry
                if delta &lt;= -config.triggerOffset then
                    -- inform user
                    LogWarning('-- New '..(isLong and 'LONG' or 'SHORT')..' entry allowed. --')
                    
                    canPlace = true

                    -- set entry order price
                    if pos.amount > 0 then 
                        config.entrySlot.price = isLong
                            and SubPerc(LLP, (config.orderOffset + config.triggerOffset))
                            or AddPerc(LSP, (config.orderOffset + config.triggerOffset))
                    else
                        config.entrySlot.price = isLong
                                and SubPerc(cp.bid, config.orderOffset)
                                or AddPerc(cp.ask, config.orderOffset)
                    end

                    -- set entry order size
                    config.entrySlot.size = getOrderSize( config )
                end
            else
                canPlace = true

                -- set entry order price
                if pos.amount > 0 then 
                    config.entrySlot.price = isLong
                        and SubPerc(LLP, config.orderOffset)
                        or AddPerc(LSP, config.orderOffset)
                else
                    config.entrySlot.price = isLong
                        and SubPerc(cp.bid, config.orderOffset)
                        or AddPerc(cp.ask, config.orderOffset)
                end

                -- set entry order size
                config.entrySlot.size = getOrderSize( config )
            end

        ----------
        -- ...market order type logic
        elseif config.orderType == orderTypes.market then
            -- get last recorded price (value updated when order has filled, resets when position exits)
            local triggerLevel = config.startPrice

            -- combine triggerOffset and orderOffset to trigger market order entry
            local combinedOffset = 0

            -- add trigger offset if used
            if config.triggerOffset >= 0 then
                combinedOffset = config.triggerOffset
            end

            -- add order offset
            combinedOffset = combinedOffset + config.orderOffset

            -- calculate delta accordingly
            local delta = isLong
                    and Delta(triggerLevel, cp.bid)
                    or Delta(cp.ask, triggerLevel)

            -- allow new entry
            if delta &lt;= -combinedOffset then
                -- inform user
                LogWarning('-- New '..(isLong and 'LONG' or 'SHORT')..' entry allowed. --')

                canPlace = true

                -- set entry order price
                config.entrySlot.price = isLong
                        and cp.bid
                        or cp.ask
                
                -- set entry order size
                config.entrySlot.size = getOrderSize( config )
            end
        end

        -- continue execution in other function
        updateEntry(config, cp, canPlace, isLong)
    end


    ---------------------------------------------------------------------------------
    -- UPDATE ENTRY
    function updateEntry(config, cp, canPlace, isLong)
        -- define proper entry order command
        local cmd = isLong
                and (__isSpot and PlaceBuyOrder or PlaceGoLongOrder)
                or (__isSpot and PlaceSellOrder or PlaceGoShortOrder)
        
        -- set informative name for order
        local orderName = isLong
                and 'Long Entry '..config.entryCount
                or 'Short Entry '..config.entryCount
        
        -- order id is empty
        if config.entrySlot.oid == '' then

            -- place new order if allowed
            if canPlace then
                config.entrySlot.oid = cmd(
                    config.entrySlot.price,
                    config.entrySlot.size,
                    {
                        note = orderName,
                        timeout = config.orderTimeout * 60,
                        type = getOrderType( config.orderType ),
                        positionId = config.pid
                    }
                )
            end
        else
            -- get the order
            local order = OrderContainer(config.entrySlot.oid)

            -- order not open
            if not order.isOpen then
                config.entrySlot.oid = '' -- reset order ID

                -- order was fully filled
                if order.isFilled then
                    config.startPrice = order.price -- update last price
                    config.entryCount = config.entryCount + 1 -- increment entry count
                    if config.entryCount > bot.slotCounter then
                        bot.slotCounter = config.entryCount
                    end
                end
            else
                -- plot order as line if open
                Plot(0, orderName, order.price, {c = isLong and Green or Red, id = ''..order.price})

                -- order delta cancellation
                if config.orderDelta > 0 then
                    -- calculate delta
                    local delta = isLong
                            and Delta(order.price, cp.bid)
                            or Delta(cp.ask, order.price)
                    
                    -- cancel order
                    if delta > config.orderDelta then
                        CancelOrder(config.entrySlot.oid)
                    end
                end
            end
        end
    end


-- =======================================================================================================
-- == RUN
-- =======================================================================================================

    runConfigs(long_config, true)
    runConfigs(short_config, false)

    
-- =======================================================================================================
-- == BOT PERFORMANCE REPORT
-- =======================================================================================================
    
    if balRatio > bot.BRCounter then bot.BRCounter = balRatio end
    local profitPercent = realizedBotProfit / tradeBal * 100
    local profit2bal = realizedBotProfit / tradeBal / bot.BRCounter

    ChartSetOptions(1, "Bot Performance")
    Plot(1, "Working Balance", workingBalance, Red)
    Plot(1, "Bot Balance", tradeBal + realizedBotProfit, White)
    Plot(1, "Trading Balance", tradeBal, Green)--]]

    --AEP Plot
        if usedLong != 0 then
            Plot(0, 'AvgEP Long', GetPositionEnterPrice(bot.longPosId), {id=bot.longPosId, c=Green, w=2})
            --Log('GetPositionEnterPrice(bot.longPosId) '..GetPositionEnterPrice(bot.longPosId))
            --Log('Long PID '..bot.longPosId)
        end

        if usedShort != 0 then
            Plot(0, 'AvgEP Short', GetPositionEnterPrice(bot.shortPosId), {id=bot.shortPosId, c=Red, w=2})
            --Log('GetPositionEnterPrice(bot.shortPosId) '..GetPositionEnterPrice(bot.shortPosId))
            --Log('Short PID '..bot.shortPosId)
        end

    -- detailed log
    if showInfo then
        Log('Balance Ratio: '..balRatio)
        Log('Working Balance: '..workingBalance)
        Log('Bot Realized Profit: '..realizedBotProfit)
        Log('Trading Balance: '..tradeBal) 
        Log('Exchange Balance: '..WalletAmount())
        Log(' ')
    end

    -- custom reports
    Finalize(function()  
        CustomReport('Max Entry', bot.slotCounter..' entries')
        CustomReport('Stop-Action', bot.SACounter..' times')
        CustomReport('Stop Loss', bot.SRCounter..' times')
        CustomReport('Current Balance Ratio', balRatio)
        CustomReport('Highest Balance Ratio', bot.BRCounter)
        CustomReport('Profit to Highest Balance Ratio', profit2bal)
        CustomReport('Bot Profit %', profitPercent..'%')
        CustomReport('BACKTEST', 'WAS SUCCESSFUL!')
    end)

Save('bot', bot)

0 Comments

Sign in to leave a comment.

No comments yet. Be the first!