[EXPERIMENTAL] [pshaiBot] Keltner Grid Bot v2 (KGB) (HEDGE-ONLY)
stableDescription
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.
Fantastic Idea, You Improve to point of Profit, Now only profit Trailer will help to The Best
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
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!
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.
KGB = Keltner Grid Bot
Swap the PlaceGoLongOrder and PlaceGoShortOrder commands around and you will get it to trade in reverse.
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?
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.
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.
ART... This bot is ART. Genius. Much love Pshai. This bot is off its head when set right and given some looooove.
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?
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
This bot is indd gold, been playing around a lot with this
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?
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