[pshaiBot] Simple Grid Bot (FUTURES)
betaDescription
Hey YOU! Having trouble modifying SGB Spot to Futures? There's no need for that!
Simple Grid Bot makes grid trading simple AF. All you need to do, is set your price range, amount of grids and the amount of your total investment and the bot will take care of the rest.
SGB also contains start and stop conditions which you can use to limit and preserve your gains and investments.
The functionality of this Futures version is exactly the same as Spot version. The only difference is that this runs on Futures markets and manages Long and Short positions only, rather than each grid with their own position.
Settings
Price Range
Lower Limit - Lower price limit.
Upper Limit - Upper price limit.
Grid mode & quantity
Mode - Grid mode. Arithmetic grid mode will place all grids evenly spaced based on a constant price difference between grids. Geometric grid mode will place all grids evenly spaced based on a constant percentage difference between grids. (not yet fully functioning)
Quantity - The amount of grids the price range is split into.
Investment amount
Asset - Select the asset type in which the investment amount is based on. Example: on BTC/USDT market the Base option would point to BTC, and Quote option would point to USDT.
Amount - Total amount to be invested into the grid range. This amount is split among grids.
Safety Settings
Max. allowed win - Disabled if zero. Set the maximum win allowed (total profits). If reached, the bot will trigger a take-profit and deactivate.
Max. allowed loss - Disabled if zero. Set the maximum loss allowed (per position). If reached, the bot will trigger a stop-loss and deactivate.
Advanced Settings
Start condition - Instant condition will start the bot immediately. Price condition activates bot once price is above or below set start value. RSI condition will activate bot once RSI is above or below set start value. (NOTE: RSI uses the main interval setting and has 14 period length)
Start type - Activation on above or below start value.
Start value - The value for Price and RSI start conditions.
Stop condition - Manual condition never deactivate the bot. Price condition deactivates bot once price is above or below set stop value. RSI condition will deactivate bot once RSI is above or below set stop value. (NOTE: RSI uses the main interval setting and has 14 period length)
Stop type - Deactivation on above or below stop value.
Stop value - The value for Price and RSI stop conditions.
Stop exits - If true, bot will sell all bought assets when using [Below value] stop condition, or buy back when using [Above value] stop condition.
NOTE: Bot is marked to be in beta test simply because the Geometric grid mode doesn't yet function as it should.
HaasScript
-- [HaasOnline] Simple Grid Bot (Futures) v1.4
-- Author: pshai
HideOrderSettings()
HideTradeAmountSettings()
EnableHighSpeedUpdates(true)
InputGroupHeader('Price Range')
local pr_lower = Input('Lower Limit', 0, 'Lower price limit.')
local pr_upper = Input('Upper Limit', 0, 'Upper price limit.')
InputGroupHeader('Grid mode & quantity')
local mode = InputOptions('Mode', 'Arithmetic', {'Arithmetic', 'Geometric'}, 'Grid mode. Arithmetic grid mode will place all grids evenly spaced based on a constant price difference between grids. Geometric grid mode will place all grids evenly spaced based on a constant percentage difference between grids.')
local grids = Input('Quantity', 0, 'The amount of grids the price range is split into.')
InputGroupHeader('Investment amount')
local asset = InputOptions('Asset', 'Base', {'Base', 'Quote'}, 'Select the asset type in which the investment amount is based on. Example: on BTC/USDT market the Base option would point to BTC, and Quote option would point to USDT.')
local amount = Input('Amount', 0, 'Total amount to be invested into the grid range. This amount is split among grids.')
InputGroupHeader('Safety Settings')
local max_win = Input('Max. allowed win', 0, 'Disabled if zero. Set the maximum win allowed (total profits). If reached, the bot will trigger a take-profit and deactivate.')
local max_loss = Input('Max. allowed loss', 0, 'Disabled if zero. Set the maximum loss allowed (per position). If reached, the bot will trigger a stop-loss and deactivate.')
InputGroupHeader('Advanced Settings')
local start_cond = InputOptions('Start condition', 'Instant', {'Instant', 'Price', 'RSI'}, 'Instant condition will start the bot immediately. Price condition activates bot once price is above or below set start value. RSI condition will activate bot once RSI is above or below set start value. (NOTE: RSI uses the main interval setting and has 14 period length)')
local start_type = InputOptions('Start type', 'Above value', {'Above value', 'Below value'}, 'Activation on above or below start value.')
local start_value = Input('Start value', 0, 'The value for Price and RSI start conditions.')
local stop_cond = InputOptions('Stop condition', 'Manual', {'Manual', 'Price', 'RSI'}, 'Manual condition never deactivate the bot. Price condition deactivates bot once price is above or below set stop value. RSI condition will deactivate bot once RSI is above or below set stop value. (NOTE: RSI uses the main interval setting and has 14 period length)')
local stop_type = InputOptions('Stop type', 'Above value', {'Above value', 'Below value'}, 'Deactivation on above or below stop value.')
local stop_value = Input('Stop value', 0, 'The value for Price and RSI stop conditions.')
local stop_exit = Input('Stop exits', false, 'If true, bot will sell all bought assets when using [Below value] stop condition, or buy back when using [Above value] stop condition.')
-- check settings
if pr_lower <= 0 then
DeactivateBot('Lower limit not set.')
return
end
if pr_upper <= 0 then
DeactivateBot('Upper limit not set.')
return
end
if pr_lower > pr_upper then
DeactivateBot('Lower Limit cannot be higher than Upper Limit.')
return
end
if grids <= 2 then
DeactivateBot('Grid quantity not set (must be 2 or more).')
return
end
if amount == 0 then
DeactivateBot('Investment amount not set.')
return
end
---------------
function createGridItem(level, amt)
return {
p = level,
amt = amt,
state = 0,
isMid = false,
isBuy = false,
pid = '',
oid = ''
}
end
function createGrid()
local cp = CurrentPrice()
local cur_pr = cp.ask
local pr_interval = grids > 0 and (pr_upper - pr_lower) / grids or 0
local min_pr_interval = MakersFee() * cur_pr * 0.02
local slot_size = amount / grids
local wallet = {
base = 0, --Balance({coin = BaseCurrency()}).available,
quote = Balance({coin = UnderlyingAsset()}).available,
}
local grid = {}
-- with geometric grid mode, calculate the % based interval instead
if mode == 'Geometric' then
pr_interval = grids > 0 and (pr_upper / pr_lower - 1) / grids * 100 or 0
min_pr_interval = MakersFee() * 2
end
Log('Grid interval: ' .. pr_interval)
Log('Breakeven interval: ' .. min_pr_interval)
if pr_interval <= min_pr_interval then
DeactivateBot('The grid will not be able to produce profits; grid interval is too small (increase total range or decrease grid quantity).')
end
-- build grid and find the middle
local mid_found = false
local closest = nil
local closest_dist = NumberMax
for i = 1, grids+1 do
local level = 0
if mode == 'Arithmetic' then
level = pr_lower + pr_interval * (i - 1)
else
if i > 1 then
level = AddPerc(grid[i-1].p, pr_interval)
else
level = pr_lower
end
end
local dist = Abs(cur_pr - level)
local amt = (asset == 'Base' and slot_size or slot_size / level)
grid[i] = createGridItem(level, amt) --{p = level, isMid = false}
if dist < closest_dist
then
if closest then
closest.isMid = false
end
closest_dist = dist
closest = grid[i]
closest.isMid = true
mid_found = true
end
end
-- second pass to set buy/sell types
min_profit, max_profit = NumberMax, NumberMin
local total_buy, total_sell = 0, 0
local prev_level = 0
for i = 1, grids+1 do
if grid[i].p <= closest.p then
grid[i].isBuy = true
total_buy = total_buy + (grid[i].amt * grid[i].p)
grid[i].pid = lpid
else
total_sell = total_sell + grid[i].amt
grid[i].pid = spid
end
if prev_level > 0 then
local p = Abs(prev_level / grid[i].p - 1)
if p < min_profit then min_profit = p end
if p > max_profit then max_profit = p end
end
prev_level = grid[i].p
end
Log('total sell: ' .. total_sell)
Log('total buy: ' .. total_buy)
Log('wallet size: ' .. wallet.quote)
--DeactivateBot()
return grid
end
function drawGrid()
local cp = CurrentPrice()
local lpos = PositionContainer(lpid)
local spos = PositionContainer(spid)
if lpos.amount > 0 then
Plot(0, 'LongAEP', lpos.enterPrice, {c = Cyan, id = ''..lpid})
Plot(1, 'LongPnL', lpos.profit, {c = Cyan, id = ''..lpid})
end
if spos.amount > 0 then
Plot(0, 'ShortAEP', spos.enterPrice, {c = Purple, id = ''..spid})
Plot(1, 'ShortPnL', spos.profit, {c = Purple, id = ''..spid})
end
for i = 1, #g_grids do
local grid = g_grids[i]
if grid.oid != '' then
local o = OrderContainer(grid.oid)
if grid.isBuy then
if grid.state == 1 then
Plot(0, i..'-Buy', o.price, {c = Green, id = grid.oid})
elseif grid.state == 3 then
Plot(0, i..'-Sell', o.price, {c = Yellow, id = grid.oid})
end
else
if grid.state == 1 then
Plot(0, i..'-Sell', o.price, {c = Red, id = grid.oid})
elseif grid.state == 3 then
Plot(0, i..'-Buy', o.price, {c = Yellow, id = grid.oid})
end
end
end
end
end
function updateGrid()
local sell = PlaceGoShortOrder
local buy = PlaceGoLongOrder
local sell2 = PlaceExitShortOrder
local buy2 = PlaceExitLongOrder
for i = 1, #g_grids do
local grid = g_grids[i]
local pos = PositionContainer(grid.pid)
local cmd = grid.isBuy and buy or sell
local cmd2 = grid.isBuy and buy2 or sell2
if grid.oid != '' then
local o = OrderContainer(grid.oid)
if not o.isOpen then
grid.oid = ''
if o.isCancelled then
grid.state = grid.state - 1
elseif o.isFilled then
grid.state = grid.state + 1
if grid.state == 4 then
grid.state = 0
end
end
end
elseif grid.state == 1 or grid.state == 3 then
grid.state = grid.state - 1
end
if not grid.isMid then
if grid.state == 0 then
if grid.oid == '' then
grid.oid = cmd(grid.p, grid.amt, {positionId = grid.pid, timeout = -1, type = LimitOrderType})
grid.state = 1
end
elseif grid.state == 2 then
if grid.oid == '' then
local p = grid.isBuy and g_grids[i+1].p or g_grids[i-1].p
grid.oid = cmd2(p, grid.amt, {positionId = grid.pid, timeout = -1, type = LimitOrderType})
grid.state = 3
end
end
end
end
end
function exitGrid()
local lpos = PositionContainer(lpid)
local spos = PositionContainer(spid)
Log('long amt: ' .. lpos.amount)
Log('short amt: ' .. spos.amount)
if lpos.amount > 0 then
PlaceExitPositionOrder(lpid, {type = MarketOrderType})
end
if spos.amount > 0 then
PlaceExitPositionOrder(spid, {type = MarketOrderType})
end
end
function updatePositions()
local lpos = PositionContainer(lpid)
local spos = PositionContainer(spid)
if lpos.amount == 0 and lpos.enterPrice > 0 then
if IsAnyOrderOpen(lpid) then
CancelAllOrders(lpid)
else
lpid = NewGuid()
end
end
if spos.amount == 0 and spos.enterPrice > 0 then
if IsAnyOrderOpen(spid) then
CancelAllOrders(spid)
else
spid = NewGuid()
end
end
end
if not init then
active = Load('active', false)
deactivated = Load('deactivated', false)
tpsl = Load('tpsl', false)
stop_count = Load('sc', 0)
min_profit = 0
max_profit = 0
lpid = Load('lpid', NewGuid())
spid = Load('spid', NewGuid())
init = Load('init', true)
end
function backupGlobals()
Save('active', active)
Save('deactivated', deactivated)
Save('tpsl', tpsl)
Save('sc', stop_count)
Save('lpid', lpid)
Save('spid', spid)
Save('init', init)
end
-- update positions
--updatePositions()
------
if not active then
if not deactivated then
local cp = CurrentPrice()
if start_cond == 'Price' then
-- check price and activate
if (start_type == 'Above value' and cp.high > start_value)
or (start_type == 'Below value' and cp.low < start_value)
then
active = true
end
elseif start_cond == 'RSI' then
-- check RSI and activate
local rsi = RSI(ClosePrices(), 14)
if (start_type == 'Above value' and rsi > start_value)
or (start_type == 'Below value' and rsi < start_value)
then
active = true
end
else
active = true -- activate instantly
end
else
-- check if any orders are open and cancel
if IsAnyOrderOpen() then
CancelAllOrders()
elseif stop_exit or tpsl then
exitGrid()
end
stop_count = stop_count + 1
if stop_count > 5 then
DeactivateBot()
end
end
else
if not g_grids then
g_grids = createGrid()
end
updateGrid()
drawGrid()
local cp = CurrentPrice()
local lpos = PositionContainer(lpid)
local spos = PositionContainer(spid)
if max_win > 0 then
if GetBotProfit({includeUnrealized = true}) >= max_win then
active = false
deactivated = true
tpsl = true
LogWarning('------------------------------------------------------------------------------')
LogWarning('-- Bot deactivated by TAKE-PROFIT: please stop and clean bot before a new run.')
LogWarning('------------------------------------------------------------------------------')
end
end
if max_loss > 0 then
if lpos.profit <= -max_loss or spos.profit <= -max_loss then
active = false
deactivated = true
tpsl = true
LogWarning('----------------------------------------------------------------------------')
LogWarning('-- Bot deactivated by STOP-LOSS: please stop and clean bot before a new run.')
LogWarning('----------------------------------------------------------------------------')
end
end
if stop_cond == 'Price' then
-- check price and deactivate
if (stop_type == 'Above value' and cp.high > stop_value)
or (stop_type == 'Below value' and cp.low < stop_value)
then
active = false
deactivated = true
LogWarning('------------------------------------------------------------------------')
LogWarning('-- Bot deactivated by PRICE: please stop and clean bot before a new run.')
LogWarning('------------------------------------------------------------------------')
end
elseif stop_cond == 'RSI' then
-- check RSI and deactivate
local rsi = RSI(ClosePrices(), 14)
if (stop_type == 'Above value' and rsi > stop_value)
or (stop_type == 'Below value' and rsi < stop_value)
then
active = false
deactivated = true
LogWarning('------------------------------------------------------------------------')
LogWarning('-- Bot deactivated by RSI: please stop and clean bot before a new run.')
LogWarning('------------------------------------------------------------------------')
end
end
CustomReport('Potential profit/grid (ROI)', Round(min_profit*100*Leverage(), 2) .. ' - ' .. Round(max_profit*100*Leverage(), 2) .. ' %')
end
------------------------------------------
backupGlobals()
PlotDoubleColor(
Plot(99, 'Bot PnL', GetBotProfit({includeUnrealized = true}), Gold),
0,
Red,
Gold(10)
)
1 Comment
Sign in to leave a comment.
Nice! Thanks for posting! KING! :)