[pshaiBot] Simple Market Maker (Leverage, NEW HEDGE MODE)

2 473 Views No Comments 4 weeks ago
  • Simple Market Maker
    by pshai @ 2020

    Introduction:
    This simple market maker makes the market!
    It doesn’t stop, it has no limits (other than max. pos. size)
    and it’s amazing. Get familiar with the bot before using it!
    I strongly suggest doing backtests and especially running it
    using a simulated account until you are confident that you know that
    this bot knows what it is doing! Also remember that backtests cannot
    represent the results you would see with a live bot; the difference
    is very…different.

    About trading commands….
    This bot uses exit order commands only, but dont be alarmed!
    It is merely a hack that I found while exploring, and this hack
    allowed me to not bother handling positions whatsoever!
    In other words, the bot works well with these; it just looks weird.

    –== !NEW! Hedge Mode ==–
    With this new feature you are able to trade on Binance Futures, OKEx
    and other excahnges that support hedge mode. Enable this mode ONLY
    when you are certain that hedging is supported on your chosen exchange!

    *THINGS TO NOTE:*
    – This bot will not work well on:
    –> Bybit; bot is too intense for their API, dont trade there…
    –> Bitmex; API Incompatibilities
    – BE VERY CAREFUL with fixed high leverage!

    ~~ May the profits be with you ~~
    ~pshai

    Consider donating to support my work!
    BTC: 3KBNiSWXLBdkM8s5CKizo9rUK7ViA2PuFA

    • This topic was modified 3 weeks, 5 days ago by pshai.
    • This topic was modified 3 weeks, 5 days ago by pshai.
    • This topic was modified 3 weeks, 5 days ago by pshai.
    • This topic was modified 3 weeks, 5 days ago by pshai.
    • This topic was modified 3 weeks, 3 days ago by pshai.
    • This topic was modified 1 week, 6 days ago by pshai.
    • This topic was modified 1 week, 6 days ago by pshai. Reason: UPDATE: Added Hedge Mode
    • This topic was modified 1 week, 6 days ago by pshai.
    HaasScript Code
    -- Simple Market Maker
    -- by pshai @ 2020
    -- 
    -- Introduction:
    -- This simple market maker makes the market!
    -- It doesn't stop, it has no limits (other than max. pos. size)
    -- and it's amazing. Get familiar with the bot before using it!
    -- I strongly suggest doing backtests and especially running it
    -- using a simulated account until you are confident that you know that
    -- this bot knows what it is doing! Also remember that backtests cannot
    -- represent the results you would see with a live bot; the difference
    -- is very...different.
    -- 
    -- About trading commands....
    -- This bot uses exit order commands only, but dont be alarmed!
    -- It is merely a hack that I found while exploring, and this hack
    -- allowed me to not bother handling positions whatsoever!
    -- In other words, the bot works well with these; it just looks weird.
    -- 
    -- --== !NEW! Hedge Mode ==--
    -- With this new feature you are able to trade on Binance Futures, OKEx
    -- and other excahnges that support hedge mode. Enable this mode ONLY
    -- when you are certain that hedging is supported on your chosen exchange!
    -- 
    -- *THINGS TO NOTE:*
    --  - This bot will not work well on:
    --    --> Bybit; bot is too intense for their API, dont trade there...
    --    --> Bitmex; API Incompatibilities
    --  - BE VERY CAREFUL with fixed high leverage! 
    -- 
    --
    -- ~~ May the profits be with you ~~
    -- ~pshai
    -- 
    -- 
    -- Consider donating to support my work!
    -- BTC: 3KBNiSWXLBdkM8s5CKizo9rUK7ViA2PuFA
    -- --------------------------------------------------------------------------
    
    EnableHighSpeedUpdates(true)
    HideOrderSettings()
    HideTradeAmountSettings()
    
    -- inputs
        local slotCount = Input('01. Slot Count', 5, 'How many orders are constantly kept open on both long and short side')
        local slotSize = Input('02. Slot Size', 10, 'Trade amount per slot')
        local slotSpread = Input('03. Slot Spread %', 0.1, 'Percentage based spread value between each slot')
        local slotCancel = Input('04. Cancel Distance %', 0.1, 'How much price can move to the opposite direction before orders are cancelled and replaced')
        local minSpread = Input('05. Minimum Spread %', 0.1, 'Minimum spread percentage between the first long and short entries. This setting only works when bot has no position.')
        local maxSize = Input('06. Max. Open Contracts', 12000, 'Maximum open contracts at any given time. After exceeding this value, the bot will dump a portion of position at a loss')
        local reduceSize = Input('07. Size Reduction %', 25, 'How big of a portion the bot will dump once Max. Open Contracts is exceeded')
        local reduceOrderType = InputOrderType('08. Reduction Order Type', MarketOrderType, 'The order type for size reduction dump')
        local takeProfit = Input('09. Take-Profit ROI %', 50, 'Fixed take-profit value, based on ROI % (not price change!)')
        local tpOrderType = InputOrderType('10. TP Order Type', MakerOrCancelOrderType, 'The order type for take-profit')
        local hedgeMode = Input('11. Hedge Mode', false, 'Hedge Mode works on exchanges like OKEX and Binance Futures with hedge mode enabled (need to set that via Binance Futrues website!). Changing this setting while bot is running will cause unwanted behavior!!')
        local hedgeRoi = Input('12. Hedge Minimum ROI %', 10, 'The ROI % the current open position needs to have before we enable the opposite side')
    
    -- 
        minSpread = minSpread / 2.0
    
    
    -- regular single-position logic
        if not hedgeMode then
            -- price and data
                local cp = CurrentPrice()
                local aep = GetPositionEnterPrice()
                local pamt = GetPositionAmount()
                local proi = GetPositionROI()
    
            -- not using spread if we have a position
                if pamt > 0 then
                    minSpread = 0
                end
    
            -- slot function
                local slot = function(isLong, index, amount, spread, cancelDist)
                    local prefix = isLong and 'L' or 'S'
                    local name = prefix .. index
                    local cmd = isLong and PlaceExitShortOrder or PlaceExitLongOrder -- a little hack...
                    local priceBase = isLong
                            and cp.bid
                            or cp.ask
                    local spr = minSpread + spread * index
    
                    -- if we have average entry price
                    if aep > 0 then
                        priceBase = isLong
                                and Min(aep, priceBase)
                                or Max(aep, priceBase)
                    end
    
                    -- get price
                    local price = isLong
                            and SubPerc(priceBase, spr)
                            or AddPerc(priceBase, spr)
    
                    local oid = Load(name..'oid', '') -- order id
    
                    if oid != '' then
                        local order = OrderContainer(oid)
    
                        if order.isOpen then
                            local delta = isLong
                                    and Delta(AddPerc(order.price, spr), priceBase)
                                    or Delta(priceBase, SubPerc(order.price, spr))
                            
                            if delta >= cancelDist then
                                CancelOrder(oid)
                                oid = '' -- reset id immediately, otherwise need 2 updates to get new order
                                LogWarning('Delta cancelled '..name)
                            end
                        else
                            oid = ''
                        end
                    else
                        SetFee(Abs(MakersFee())*-1)
                        oid = cmd(price, amount, {type = MakerOrCancelOrderType, note = name, timeout = 3600})
                    end
    
                    Save(name..'oid', oid)
                end
    
            -- update take-profit
                local updateTakeProfit = function(currentRoi, targetRoi, cancelDist)        
                    local name = 'Take-Profit'
                    local oid = Load('tp_oid', '')
                    local isLong = GetPositionDirection() == PositionLong
                    local timer = Load('tp_timer', Time())
    
                    if oid != '' then
                        local order = OrderContainer(oid)
    
                        if order.isOpen then
                            local delta = isLong
                                    and Delta(order.price, cp.close)
                                    or Delta(cp.close, order.price)
                            
                            if delta >= cancelDist then
                                CancelOrder(oid)
                                LogWarning('Delta cancelled '..name)
                            end
                        else
                            oid = ''
                        end
                    else
                        if currentRoi >= targetRoi and Time() >= timer then
                            SetFee(tpOrderType == MarketOrderType and TakersFee() or Abs(MakersFee())*-1)
                            oid = PlaceExitPositionOrder({type = tpOrderType, note = name, timeout = 3600})
                            timer = Time() + 60 -- 1min
                        end
                    end
    
                    Save('tp_oid', oid)
                    Save('tp_timer', timer)
                end
    
    
            -- update position size
                local updatePositionManagement = function(currentSize, sizeLimit, cancelDist)
                    local name = 'Size Reduction'
                    local oid = Load('pos_oid', '')
                    local isLong = GetPositionDirection() == PositionLong
                    local amount = SubPerc(currentSize, 100 - reduceSize) -- take X% of position
                    local price = isLong
                            and cp.ask
                            or cp.bid
                    local cmd = isLong
                            and PlaceExitLongOrder
                            or PlaceExitShortOrder
                    local timer = Load('pos_timer', Time())
    
                    if oid != '' then
                        local order = OrderContainer(oid)
    
                        if order.isOpen then
                            local delta = isLong
                                    and Delta(order.price, cp.close)
                                    or Delta(cp.close, order.price)
                            
                            if delta >= cancelDist then
                                CancelOrder(oid)
                                LogWarning('Delta cancelled '..name)
                            end
                        else
                            oid = ''
                        end
                    else
                        if currentSize > sizeLimit and Time() >= timer then
                            SetFee(reduceOrderType == MarketOrderType and TakersFee() or Abs(MakersFee())*-1)
                            oid = cmd(price, amount, {type = reduceOrderType, note = name, timeout = 6000})
                            timer = Time() + 60 -- 1min
                        end
                    end
    
                    Save('pos_oid', oid)
                    Save('pos_timer', timer)
                end
    
    
            -- da logica
    
                -- take profit
                updateTakeProfit(proi, takeProfit, slotCancel)
    
                -- risk management
                updatePositionManagement(pamt, maxSize, slotCancel)
    
    
                -- update slots
                for i = 1, slotCount do
                    slot(true, i, slotSize, slotSpread, slotCancel) -- long slot
                end
    
                for i = 1, slotCount do
                    slot(false, i, slotSize, slotSpread, slotCancel) -- short slot
                end
    
                if aep > 0 then
                    local posId = PositionContainer().positionId
                    Plot(0, 'AvgEP', aep, {c=Purple, id=posId, w=2})
                end
        
    
    
        --------------------------------------------------------------------------------------------
        --------------------------------------------------------------------------------------------
        -- hedge it!
        else
            -- price and data
                local cp = CurrentPrice()
                local c = ClosePrices()
                local rsi = RSI(c, 9) -- RSI is used to determine which side to start off with. this is to eliminate ghost positions.
                local signal = rsi > 52 and -1 or rsi < 48 and 1 or 0
    
            -- positions
                local hedge_longPosId = Load('hedge_longPosId', NewGuid())
                local hedge_shortPosId = Load('hedge_shortPosId', NewGuid())
    
                local dir_l = GetPositionDirection(hedge_longPosId)
                local aep_l = GetPositionEnterPrice(hedge_longPosId)
                local pamt_l = GetPositionAmount(hedge_longPosId)
                local proi_l = GetPositionROI(hedge_longPosId)
                local dir_s = GetPositionDirection(hedge_shortPosId)
                local aep_s = GetPositionEnterPrice(hedge_shortPosId)
                local pamt_s = GetPositionAmount(hedge_shortPosId)
                local proi_s = GetPositionROI(hedge_shortPosId)
    
            -- manage position ids
                if pamt_l == 0 and IsPositionClosed(hedge_longPosId) then
                    hedge_longPosId = NewGuid()
                end
                
                if pamt_s == 0 and IsPositionClosed(hedge_shortPosId) then
                    hedge_shortPosId = NewGuid()
                end
    
            -- not using spread if we have a position
                if pamt_l > 0 or pamt_s > 0 then
                    minSpread = 0
                end
            
            -- get pos id
                local getPositionId = function(isLong)
                    return isLong and hedge_longPosId or hedge_shortPosId
                end
    
            -- slot function
                local slot = function(isLong, index, amount, spread, cancelDist, canPlace)
                    local prefix = isLong and 'L' or 'S'
                    local name = prefix .. index
                    local cmd = isLong and PlaceGoLongOrder or PlaceGoShortOrder
                    local priceBase = isLong
                            and cp.bid
                            or cp.ask
                    local spr = minSpread + spread * index
                    local posId = getPositionId(isLong)
                    local aep = isLong and aep_l or aep_s
    
                    -- if we have average entry price
                    if aep > 0 then
                        priceBase = isLong
                                and Min(aep, priceBase)
                                or Max(aep, priceBase)
                    end
    
                    -- get price
                    local price = isLong
                            and SubPerc(priceBase, spr)
                            or AddPerc(priceBase, spr)
    
                    local oid = Load(name..'oid', '') -- order id
    
                    if oid != '' then
                        local order = OrderContainer(oid)
    
                        if order.isOpen then
                            local delta = isLong
                                    and Delta(AddPerc(order.price, spr), priceBase)
                                    or Delta(priceBase, SubPerc(order.price, spr))
                            
                            if delta >= cancelDist then
                                CancelOrder(oid)
                                LogWarning('Delta cancelled '..name)
                            elseif not canPlace then
                                CancelOrder(oid)
                                LogWarning('Not allowed right now '..name)
                            end
                        else
                            oid = ''
                        end
                    else
                        if canPlace then
                            SetFee(Abs(MakersFee())*-1)
                            oid = cmd(price, amount, {type = MakerOrCancelOrderType, note = name, timeout = 3600, positionId = posId})
                        end
                    end
    
                    Save(name..'oid', oid)
                end
    
            -- update take-profit
                local updateTakeProfit = function(isLong, currentRoi, targetRoi, cancelDist)        
                    local prefix = isLong and 'Long' or 'Short'
                    local name = prefix .. ' Take-Profit'
                    local oid = Load(prefix .. 'tp_oid', '')
                    local timer = Load(prefix .. 'tp_timer', Time())
                    local posId = getPositionId(isLong)
    
                    if oid != '' then
                        local order = OrderContainer(oid)
    
                        if order.isOpen then
                            local delta = isLong
                                    and Delta(order.price, cp.close)
                                    or Delta(cp.close, order.price)
                            
                            if delta >= cancelDist then
                                CancelOrder(oid)
                                LogWarning('Delta cancelled '..name)
                            end
                        else
                            oid = ''
                        end
                    else
                        if currentRoi >= targetRoi and Time() >= timer then
                            SetFee(tpOrderType == MarketOrderType and TakersFee() or Abs(MakersFee())*-1)
                            oid = PlaceExitPositionOrder({type = tpOrderType, note = name, timeout = 3600, positionId = posId})
                            timer = Time() + 60 -- 1min
                        end
                    end
    
                    Save(prefix .. 'tp_oid', oid)
                    Save(prefix .. 'tp_timer', timer)
                end
    
    
            -- update position size
                local updatePositionManagement = function(isLong, currentSize, sizeLimit, cancelDist)
                    local prefix = isLong and 'Long' or 'Short'
                    local name = prefix .. ' Size Reduction'
                    local oid = Load(prefix .. 'pos_oid', '')
                    local posId = getPositionId(isLong)
                    local amount = SubPerc(currentSize, 100 - reduceSize) -- take X% of position
                    local price = isLong
                            and cp.ask
                            or cp.bid
                    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
                            local delta = isLong
                                    and Delta(order.price, cp.close)
                                    or Delta(cp.close, order.price)
                            
                            if delta >= cancelDist then
                                CancelOrder(oid)
                                LogWarning('Delta cancelled '..name)
                            end
                        else
                            oid = ''
                        end
                    else
                        if currentSize > sizeLimit and Time() >= timer then
                            SetFee(reduceOrderType == MarketOrderType and TakersFee() or Abs(MakersFee())*-1)
                            oid = cmd(price, amount, {type = reduceOrderType, note = name, timeout = 6000, positionId = posId})
                            timer = Time() + 60 -- 1min
                        end
                    end
    
                    Save(prefix .. 'pos_oid', oid)
                    Save(prefix .. 'pos_timer', timer)
                end
    
    
            -- da logica
    
                -- take profit
                updateTakeProfit(true, proi_l, takeProfit, slotCancel)
                updateTakeProfit(false, proi_s, takeProfit, slotCancel)
    
                -- risk management
                updatePositionManagement(true, pamt_l, maxSize, slotCancel)
                updatePositionManagement(false, pamt_s, maxSize, slotCancel)
    
    
                -- update slots
                for i = 1, slotCount do
                    slot(true, i, slotSize, slotSpread, slotCancel, (pamt_s == 0 and signal == 1) or proi_s >= hedgeRoi) -- long slot
                end
    
                for i = 1, slotCount do
                    slot(false, i, slotSize, slotSpread, slotCancel, (pamt_l == 0 and signal == -1) or proi_l >= hedgeRoi) -- short slot
                end
    
                if aep_l > 0 then
                    local posId = getPositionId(true)
                    Plot(0, 'AvgEP Long', aep_l, {c=Teal, id=posId, w=2})
                end
    
                if aep_s > 0 then
                    local posId = getPositionId(false)
                    Plot(0, 'AvgEP Short', aep_s, {c=Purple, id=posId, w=2})
                end
    
                Save('hedge_longPosId', hedge_longPosId)
                Save('hedge_shortPosId', hedge_shortPosId)
        end
Login or Register to Comment

Unlock your crypto trading potential

Create a free account and enjoy everything we have to offer.

Join for Free