[EXPERIMENTAL] [pshaiBot] Keltner Grid Bot v2 (KGB) (HEDGE-ONLY)

stable
By pshai in Trading Bots Published January 2021 👁 4,455 views 💬 15 comments ★ Staff Pick

Description

DISCLAIMER: This script is EXPERIMENTAL and might not be fully functional. Use at your own RISK! Hey! Happened to look back into this bot and.. Well. Ideas just started to fly around, OK!? So anyway. This time the approach is different. And the bot can ONLY do hedging! Currently, the bot will trigger ALL slots the price shoots through, so there will be blobs or multiple fills at times. I am not yet sure if I will leave it as it is, or make some limits to only trigger the last slot which the price breached. Ideas are welcome! New features include: - Slot size multiplier; the further the price shoots, the bigger the order size can be - Slots can reset and be re-filled after price moves back (above long slot or below short slot) - Maximum size for grid, defined with "Slot Count" - Maximum amount of fills (use this to control max. position size!) - Dynamic TP based on the amount of fills, so there is a linear curve between the new min/max Take-Profit settings - Exposed settings for the base MA and ATR, including price timeframe ~May the profits be with you~ ~pshai
HaasScript
EnableHighSpeedUpdates(true)


-- KGB:
-- use 1 active slots per side. once slot order is filled, activate next and take the placement from
-- the band value in the moment of fill. place take-profit taking the average entry price into account.
-- use either hard SL
-- or re-activate slots
-- or decrease take-profit distance as time goes on (should have setting to allow it to move to the losing side or to limit it to break-even)

InputGroupHeader('Trade Settings')
local allow_longs = Input('Allow Longs', true)
local allow_shorts = Input('Allow Shorts', true)
local deact_nopos = Input('Deactivate on NoPosition', false)
local force_close = Input('Force Position Close', false)

InputGroupHeader('General Settings')
local slot_count = Input('1. Max Slots', 10, 'Maximum count of possible price levels/slots. This does not limit how many fills you can get, but limits the range where orders can fill.')
local max_fills = Input('2. Max Fills', 100, 'Maximum count of fills allowed per position. If your trade amount is 2 and max fills set to 100, your total position size is limited to 200 (or less, if some orders dont fill 100%).')
local size_mult = Input('3. Slot Size Multiplier', 1.25, 'Exponential multiplier for slot sizes. The multiplication is applied based on the previous slot size, so if size is 1 and multiplier is 2, we got 1, 2, 4, 8, 16 and so on. Be careful with this!!')
local max_take_profit_prc = Input('4.1. Max. Take-Profit %', 1, 'Maximum TP % value that is price change based, not ROI.')
local min_take_profit_prc = Input('4.2. Min. Take-Profit %', 0, 'Minimum TP % value that is price change based, not ROI. When bot has made MAX FILLS, this TP will be used. If bot has half filled (of MAX FILLS), the take-profit will be in the middle of min. and max. TP %. In other words, the TP decreases as position size increases! Setting this to zero means BREAKEVEN at max position size.')

InputGroupHeader('Indicator Settings')
local base_ma_len = Input('1. Base MA Length', 10, 'Length of the MA that is used as an anchor-point for slot calculations.')
local base_ma_type = InputMaTypes('2. Base MA Type', EmaType, 'Type of the base MA.')
local atr_len = Input('3. ATR Length', 50, 'Length of the ATR. ATR value is used to calculate slot levels, away from the base MA.') 
local price_int = InputInterval('4. Price Data Timeframe', 1, 'Timeframe/Interval for the prices used to calculate base MA and ATR values.')

local h = HighPrices(price_int)
local l = LowPrices(price_int)
local c = ClosePrices(price_int)
local cp = CurrentPrice()

local pos_id_long = Load('poidl', NewGuid())
local pos_id_short = Load('poids', NewGuid())

local pos_long = PositionContainer(pos_id_long)
local pos_short = PositionContainer(pos_id_short)


if deact_nopos then
    if pos_long.amount > 0 and pos_short.amount <= 0 then
        if IsAnyOrderOpen(pos_id_short) then
            CancelAllOrders(pos_id_short)
        end

        allow_shorts = false
    elseif pos_long.amount <= 0 and pos_short.amount > 0 then
        if IsAnyOrderOpen(pos_id_long) then
            CancelAllOrders(pos_id_long)
        end

        allow_longs = false
    else
        DeactivateBot('Full NoPosition detected, deactivated bot.', true)
    end
end


local long_index = Load('lix', 1)
local short_index = Load('six', 1)

local base_ma = MA(c, base_ma_len, base_ma_type)
local atr = ATR(h, l, c, atr_len)

function getSlotAmount(index)
    return TradeAmount() * Pow(size_mult, index-1)
end

function updateSlot(index, is_long)
    if (is_long and long_index > max_fills)
    or (not is_long and short_index > max_fills) then
        -- print a warning ONCE. reset when TP
        if Load('fills_warning', true) then
            LogWarning('------ Maximum fills reached. No more trades allowed. ------')
            Save('fills_warning', false)
        end

        return
    end

    local prefix = is_long and 'L' or 'S'
    local mem_id = prefix..index
    local atr2 = is_long and atr * -index or atr * index
    local trigger_price = base_ma + atr2
    local oid = Load(mem_id..'oid', '')
    local is_filled = Load(mem_id..'if', false)
    local amt = getSlotAmount(index)

    if is_long and cp.close < trigger_price then
        Plot(0, mem_id, trigger_price, {c = Green, id = pos_id_long..index})
    elseif not is_long and cp.close > trigger_price then
        Plot(0, mem_id, trigger_price, {c = Red, id = pos_id_short..index})
    end

    if is_long then
        if not is_filled then
            if oid == '' and cp.close < trigger_price then
                oid = PlaceGoLongOrder(SubPerc(cp.bid, 0.01), amt, {type=MakerOrCancelOrderType, note=mem_id, positionId=pos_id_long, timeout=604800})
            elseif oid != '' and IsOrderOpen(oid) then
                CancelOrder(oid)
            end
        end

        if is_filled and cp.close > trigger_price then
            is_filled = false
        end
    else
        if not is_filled then
            if oid == '' and cp.close > trigger_price then
                oid = PlaceGoShortOrder(AddPerc(cp.ask, 0.01), amt, {type=MakerOrCancelOrderType, note=mem_id, positionId=pos_id_short, timeout=604800})
            elseif oid != '' and IsOrderOpen(oid) then
                CancelOrder(oid)
            end
        end

        if is_filled and cp.close < trigger_price then
            is_filled = false
        end
    end

    if oid != '' and IsOrderOpen(oid) == false then
        if IsOrderFilled(oid) then
            is_filled = true
            
            if is_long then
                long_index = long_index + 1
            else
                short_index = short_index + 1
            end
        end

        oid = ''
    end

    Save(mem_id..'oid', oid)
    Save(mem_id..'if', is_filled)

end

function updatePosition(pos_id)
    local position = PositionContainer(pos_id)
    local take_profit_prc = position.isLong
        and max_take_profit_prc - (long_index -2) / max_fills * (max_take_profit_prc - min_take_profit_prc)
        or max_take_profit_prc - (short_index -2) / max_fills * (max_take_profit_prc - min_take_profit_prc)
    local price = position.isLong
        and AddPerc(position.enterPrice, take_profit_prc)
        or SubPerc(position.enterPrice, take_profit_prc)
    local prefix = position.isLong and 'LX' or 'SX'
    local oid = Load(prefix..'oid', '')
    local last_amt = Load(prefix..'la', position.amount)

    if not (position.isLong or position.isShort) then
        return pos_id
    else
        if position.isLong then
            Plot(0, 'long_pos', position.enterPrice, {c = Cyan, id = position.positionId, w = 2})
        else
            Plot(0, 'short_pos', position.enterPrice, {c = Purple, id = position.positionId, w = 2})
        end
    end



    if position.amount != last_amt and oid != '' and IsOrderOpen(oid) then
        CancelOrder(oid)
        oid = ''
    elseif oid == '' then
        Log('Current TP is ' .. take_profit_prc .. ' %')

        oid = PlaceExitPositionOrder({price=price, type=MakerOrCancelOrderType, note=prefix, positionId=pos_id, timeout=604800})
    else
        if IsOrderOpen(oid) == false then
            if IsOrderFilled(oid) then
                pos_id = NewGuid()
                Save('fills_warning', true) -- reset warning switch
                
                if position.isLong then
                    long_index = 1
                elseif position.isShort then
                    short_index = 1
                end
            end

            oid = ''
        end
    end

    if oid != '' and IsOrderOpen(oid) then
        local order = OrderContainer(oid)
        Plot(0, prefix, order.price, {c=Gold(50), id=pos_id})
    end

    Save(prefix..'oid', oid)
    Save(prefix..'la', position.amount)

    return pos_id
end

for i = 1, slot_count do
    if not force_close and allow_longs then
        updateSlot(i, true)
    end

    if not force_close and allow_shorts then
        updateSlot(i, false)
    end
end

if not force_close then
    pos_id_long = updatePosition(pos_id_long)
    pos_id_short = updatePosition(pos_id_short)
else
    local long_pos = PositionContainer(pos_id_long)
    local short_pos = PositionContainer(pos_id_short)
    
    if IsAnyOrderOpen(pos_id_long) then
        CancelAllOrders(pos_id_long)
    end

    if IsAnyOrderOpen(pos_id_short) then
        CancelAllOrders(pos_id_short)
    end

    if long_pos.amount > 0 then
        PlaceExitPositionOrder(pos_id_long, {type = MarketOrderType, note = 'Forced Close'})
        pos_id_long = NewGuid()
    end

    if short_pos.amount > 0 then
        PlaceExitPositionOrder(pos_id_short, {type = MarketOrderType, note = 'Forced Close'})
        pos_id_short = NewGuid()
    end
end

Save('poidl', pos_id_long)
Save('poids', pos_id_short)
Save('lix', long_index)
Save('six', short_index)

15 Comments

Sign in to leave a comment.

V
VxLCLi over 5 years ago

Fantastic Idea, You Improve to point of Profit, Now only profit Trailer will help to The Best

H
hughoosthuizen about 4 years ago

Hi Hope you can help me .. Is there an easy way to reverse all the signals !! Sell instead of buy and buy instead of sell

Thx

Hugh

T
Trentacles about 4 years ago

This bot looks pretty useful... back testing it thoroughly. Quick 1..what does KGB refer to? and SOOO happy to see a Keltner ch bot Yew!

T
Trentacles about 4 years ago

I've got a live version running after some decent numbers BTing... is throwing a heap of cancelation of orders (is kosher and a timeout parameter me thinks?) So far seems to be operational and doing what it should do...just like a lazer beam on the scene ... it Is quite a prolific lil order fiend... trading trending market made cracks for sacks of price action Gak. I want it to get high.... sooo high.

P
pshai about 4 years ago

KGB = Keltner Grid Bot

P
pshai about 4 years ago

Swap the PlaceGoLongOrder and PlaceGoShortOrder commands around and you will get it to trade in reverse.

D
DeFiCloudsbreakout almost 4 years ago

Hey buddy. It is honestly nice work :)

But i wasnt able to make it work with, max slot=10 and max fills=1.
If I understand correctly with this settings it should open only 1 position and be able to add 10 more order (according to size multiplier value of course)

Is there something I am missing?

T
Trentacles almost 4 years ago

Not exactly ... the max slots = is the number of price points within the limts of the price range that orders can be filled ... lower slots less price diff. Max fills= the total times you can trade your trade amount. and when multiplied i.e. trade amount 10 x 100 Max fills = 1000 as your total max trade ( & what's required in your balance for the bot) .... but ergo i find I do digress because as I should have started this comment ... Why on earth are you trying only to open 1 position on a "hedged" bot .... the point is to open opposing positions hence the ( HEDGE ONLY) title.... and ok so you then should change the max fills to 2 at least Bahahhaha ... if you only open 1 fill at whatever adjusted trade amount blah blah ... you still would require at least 1 other fill to apply the take profit other wise you would just be sitting at loss once the price moves on from your entry. This is a piece of art.... read the title and the popup descriptions in HTS. Run as stock and always backtest. Start with small trade amounts and read your logs.... its alll there brother.

T
Trentacles almost 4 years ago

WTF? How is this possible... Magickal Genius? This is VERY VERY clever Lil bot! It's a piece of Art... sublime, simple, but incredibly powerful when treated with a bit of love and attention, the right settings, enough back testing to be sure of your best set up to get the most out of the indicators. Also having your trade amount balanced to about 100 fills to the take profit % which limits the spread so your max fills don't run out before taking winning positions entirely and profiting. look for small quick gains not to get rich by the end of the week. Watch this bot... Monitor it.... if it trending widely cut your loss take the hedged profit and start over... this bot can run smoothly for 12 -24hrs (set up right) and demolish a volatile price ranging with MONSTER PROFIT.... BUT BE FUCKING WARY!!!! DO NOT LET THIS BOT RUN UNATTENDED...MONITOR IT!! not like a crazy... just keep a eye on it between netflix shows or COD zombies games... watch the trends... if its broken market structure and is about really move or maybe your watching it SKYROCKET out of the upper or lower take profit zone SHUT IT OFF! let the market find its range again ... recommence. DO NOT SET AND FORGET YOU WILL LOSE any profits gained when it was playing within its set parameters. Thats important. This bot has nuances... BUT it is incredibly simple genius. Backtest, Monitor, Caress and love...don't be greedy. This is gold...a hidden gem. THANK YOU PSHAI... I get this bot.

T
Trentacles almost 4 years ago

ART... This bot is ART. Genius. Much love Pshai. This bot is off its head when set right and given some looooove.

S
Sharpnel_89 over 3 years ago

Trying this bot but its deffo not a set and forget bot. But great work on creating this. Currently i am thinking of a plan to use this bot with a 5k account cross hedge and trade amount 0.02 BTC amount. And then run it every 12 hours and force close positions every day once i go to bed.

Anyone else still using this bot and have some good sweetspot settings?

@Trentacles you still on this bot?

S
Sharpnel_89 over 3 years ago

It would mean really a lot if i could talk to someone who is also using this bot. I think it has some potential used right as well and i want to make a comeback january next year with this script with some minor tweaks

S
Sharpnel_89 about 3 years ago

This bot is indd gold, been playing around a lot with this

X
Xlwang789 almost 3 years ago

during the backtesting, the orders keep getting canceled, and I received a warning message saying: "WARNING: Order has been cancelled by script. (3be43906dcfb48638371dedb53233f44)".

I changed the numbers of the slot count and max fills, that should be ok.
did I miss any setting?

D
DB almost 3 years ago

Hi, I've been testing this bot for 3 days. Do you have any suitable setup tips for me? I am a complete beginner. Thanks David