[ShoeBot] Shoe's Money Maker a.k.a yet another SMM version
stableDescription
Yet another SMM version. Same same but different.
HaasScript
-- Modified version of Phsai's amazing Simple Market Maker
-- combined with Smokyho's awesome dynamic budget.
--
-- ONLY FOR BINANCE FUTURES USDT/COIN HEDGE MODE ENABLED
-- If you do not have a Binance account yet feel free to use my referral
-- and save 10% on fees: https://www.binance.com/en/register?ref=XLP49I1W
--
-- CC_VPM3() by Kobalt needs installation of http://bit.ly/3sftz2G
--
-- Check this discord post for some additional infos: https://bit.ly/3tCkzod
--
-- Consider donating to support our work!
-- Phsai
-- BTC : 1MTEdma4LgdN2hSadRppeZ6PxsyXQNuxS2
-- USDT: 0x2f052efde92ded10e05e00277f4a5cdfd9c280ca
-- Smokyho
-- BTC : 35KY1GPFtxKoJ6Bzri6sLYQPcmGZhHfRac
-- USDT: 0x7720A90d0D1973eFcc258b91450c51c9967e110A
-- Kobalt
-- BTC: 1PWedYjnVGvRmphbkM69J91PjeMM66AYbS
-- BNB: 0x05221390ae191a880880924f15bf515480d7a12b BEP20 (BSC)
-- USDT: 0x05221390ae191a880880924f15bf515480d7a12b ERC20
-- Shoe
-- BTC: 1BCAGne4JDFMPptRkeLhQxtBm6qpLuBfd8
-- IOTA: iota1qrzsyghryhev0g057tqspnkz7jkelfy8l46jttcv0jn7kdt59g97ghclvym
-- --------------------------------------------------------------------------
EnableHighSpeedUpdates(true)
HideOrderSettings()
HideTradeAmountSettings()
-- Check BYBIT or BINANCE
local getMarket = PriceMarket()
local isBybit = StringContains(getMarket, 'BYBIT')
-- custom log with switch because one-liner works for variable assignements only (https://bit.ly/3nt9frB)
local sLog = function(msg, dbg, warning)
local dbg = Not(dbg == false or dbg == true) and true or dbg
if dbg then
if warning != true then
Log(msg)
else
LogWarning(msg)
end
end
end
-- saves values to compare if they have changed (mostly used to reinitialize orders on relevant changes)
local hasChanged = function(key, value, tolerance)
local tolerance = IsNull(tolerance) and 0 or tolerance
local val2comp = Load(key, value)
if tolerance == 0 and val2comp != value then
sLog("Relevant change in "..key.." (previous: "..val2comp.." / new: "..value..")", true, true)
Save(key, value)
return true
elseif Abs(val2comp - value) > val2comp * tolerance then
sLog("Relevant change out of tolerance in "..key.." (previous: "..val2comp.." / new: "..value..")", true, true)
Save(key, value)
return true
else
--sLog("NO relevant change in "..key.." (previous: "..val2comp.." / new: "..value..")", true, false)
Save(key, value)
return false
end
end
-- inputs
local relevantChange = false
InputGroupHeader('General Settings')
local isBacktest = Input('01. Is Backtest', false, 'Activate if you\'re backtesting and relevant settings are taken care of.')
InputGroupHeader('Trade Settings')
local okLong = Input('01. Allow Long', false, 'Allow bot to open Long')
local okShort = Input('02. Allow Short', false, 'Allow bot to open Short')
local wtfStop = Input('03. Stop at no position', false, 'Deactivate bot when there is no open position')
InputGroupHeader('Backtest Settings')
local wtfBal = Input('01. Deactivate on Over Budget', false)
local wtfAmount = Input('02. Deactivate on Over Size', false)
--local compound = Input('03. Add profit into balance', false, 'ONLY FOR BACKTEST. DO NOT USE WHEN ON RUNNING BOT') -- this is managed by isBacktest
local compound = isBacktest
--local testWallet = Input('04. Use Custom Wallet', false, 'ONLY FOR BACKTEST. DO NOT USE WHEN ON RUNNING BOT') -- this is managed by isBacktest
local testWallet = isBacktest
local testBal = Input('03. Backtest Wallet Balance', 1000, 'ONLY FOR BACKTEST. Is ignored if you did not activate backtest above.')
InputGroupHeader('Budget & Safety')
local maxSizeD = Input('01. Max. Open '..AmountLabel(), 23, 'Maximum open contracts at any given time also as minimum for Dynamic Max Open. After exceeding this value, the bot will dump a portion of position at a loss.')
local autoMax = Input('01A. Dynamic Max Open', true, 'Dynamically change max open contracts based on available balance')
local slotBudget = Input('01B. Max. Open Divider', 777, 'DSSize feature - Divide max open position with this value to get new Slot Size. Example Max Open is 1000 and devider is 200 then slot size is 5')
local leverage = Input('01C. Leverage', 20, 'MUST be filled even if using Cross Margin. Important for trading budget.')
local contVal = Input('01D. COIN-M Value', 10, 'ONLY if bot trading on INVERSE Futures then enter the Contract Value. Ignore if trade on USDT')
local maxBudget = Input('01E. Balance Budget', 0.3, 'How much from wallet balance allocated for this bot. 0.5 is 50% of wallet balance.')
local maxOpen = Input('01F. Position Budget', 0.5, 'How much from the Balance Budget allocated for opening positions. 0.1 is 10% of Balance Budget')
local reduceSize = Input('02. Size Reduction %', 21, 'How big of a portion the bot will dump once Max. Open Contracts is exceeded')
local reduceOrderType = InputOrderType('03. Reduction Order Type', MarketOrderType, 'The order type for size reduction dump')
local noReduce = Input('04. Disable Size Reduction', false, 'Are you sure?')
InputGroupHeader('Slot Size')
local slotCountTotal = Input('01. Slot Count Total', 1, 'How many orders are constantly kept open on both long and short side')
relevantChange = hasChanged("slotCountTotal", slotCountTotal) and true or relevantChange
local slotCountLow = Input('02. Slot Count Low', 2, 'How many orders are constantly kept open on both long and short side')
relevantChange = hasChanged("slotCountLow", slotCountLow) and true or relevantChange
local slotSizeD = Input('03. Slot Size', 0.1, 'Default trade amount per slot')
local autoSlot = Input('03A. Dynamic Slot Size', true, 'DSSize - Dynamically change slot size favoring trending side.')
InputGroupHeader('Slot Spread')
local slotSpreadMin = Input('05. Min Spread %', 0.07, 'Percentage based spread value between each slot')
relevantChange = hasChanged("slotSpreadMin", slotSpreadMin) and true or relevantChange
local slotSpreadLow = Input('06. Low Spread %', 0.40, 'Percentage based spread value between each slot')
relevantChange = hasChanged("slotSpreadLow", slotSpreadLow) and true or relevantChange
local slotSpreadHigh = Input('07. High Spread %', 0.56, 'Percentage based spread value between each slot')
relevantChange = hasChanged("slotSpreadHigh", slotSpreadHigh) and true or relevantChange
local slotSpreadHighFactor = Input('08. High Spread Increase %', 0.0, 'Percentage based spread value between each slot')
relevantChange = hasChanged("slotSpreadHighFactor", slotSpreadHighFactor) and true or relevantChange
local slotCancel = Input('09. Cancel Distance %', 0.21, 'How much price can move to the opposite direction before orders are cancelled and replaced')
local hoursUntilRenewSlot = Input('10. Hrs until renewing slot', 3, 'Hours until a slot with a filled order is placed again because of missing volatility')
InputGroupHeader('Profit')
local takeProfit = Input('11. Take-Profit %', 0.28, 'Fixed take-profit value, based on price change')
local tpOrderType = InputOrderType('12. TP Order Type', LimitOrderType, 'The order type for take-profit')
InputGroupHeader('Other Settings')
local dbg = Input('01. Debug Mode', false, 'Turn on for logs')
sLog("Relevant change: " .. Parse(relevantChange, StringType), dbg)
-- price and data
local cp = CurrentPrice()
local c = ClosePrices()
local SRCounter = Load('SRCounter', 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 canc_l = false
sLog('Long Position ID: ' .. aep_l .. ' (' .. pamt_l .. ' @ ' .. aep_l .. ')', dbg)
local dir_s = GetPositionDirection(hedge_shortPosId)
local aep_s = GetPositionEnterPrice(hedge_shortPosId)
local pamt_s = GetPositionAmount(hedge_shortPosId)
local proi_s = GetPositionROI(hedge_shortPosId)
local canc_s = false
sLog('Short Position ID: ' .. aep_s .. ' (' .. pamt_s .. ' @ ' .. aep_s .. ')', dbg)
local lInitOrder = Load('lInitOrder', {price = 0})
local sInitOrder = Load('sInitOrder', {price = 0})
sLog('LONG position ROI: '..Round(proi_l, 4)..'%', dbg)
sLog('SHORT position ROI: '..Round(proi_s, 4)..'%', dbg)
local function sDateString(timestamp)
return CurrentYear(timestamp).."-"..
SubString(CurrentMonth(timestamp)+100,2,2).."-"..
SubString(CurrentDate(timestamp)+100,2,2).." "..
SubString(CurrentHour(timestamp)+100,2,2)..":"..
SubString(CurrentMinute(timestamp)+100,2,2)..":"..
SubString(CurrentSecond(timestamp)+100,2,2)
end
-- wallet check
local TMC = TradeMarketContainer(getMarket)
local MTA = TMC.minimumTradeAmount
local profitLabel = ProfitLabel()
if profitLabel == nil then profitLabel = QuoteCurrency() end
local availBal = testWallet and testBal or WalletAmount(getMarket, profitLabel)
local getProfitL = GetCurrentProfit(PositionLong)
local getProfitS = GetCurrentProfit(PositionShort)
local getProfit = getProfitL + getProfitS
local usedLong = UsedMargin(getMarket, aep_l, pamt_l, leverage)
local usedShort = UsedMargin(getMarket, aep_s, pamt_s, leverage)
sLog('usedLong '..usedLong..' usedShort '..usedShort, dbg)
local botProfit = GetBotProfit()
if isBybit then
wallMount = availBal - getProfit
xchange = 'BYBIT'
else
wallMount = availBal
xchange = 'BINANCE'
end
-- balance warning
local walletBal = compound and (wallMount + botProfit) or wallMount
local workBal = usedLong + usedShort - getProfit
local budgetBal = maxBudget * walletBal
local balRatio = budgetBal > 0 and workBal / budgetBal or 0
sLog('walletBal '..walletBal, dbg)
if balRatio > 0.5 and balRatio < 0.8 then LogWarning('Working balance is > 50% of Budget!!!') end
if balRatio > 0.8 then LogWarning('Working balance is > 80% of Budget!!!') end
sLog('Balance Monitor '..xchange..' -> Budget Balance: '..Round(budgetBal, 5)..' '..profitLabel..' | Working Balance: '..Round(workBal, 5)..' '..profitLabel..' | Ratio: '..Round(balRatio, 3))
local BRCounter = Load('BRCounter', 0)
if balRatio > BRCounter then Save('BRCounter', balRatio) end
-- budgeting
--max position
if profitLabel == 'USD' or profitLabel == 'USDT' or profitLabel == 'BUSD' then
maxMax = walletBal * maxBudget * maxOpen / cp.close
else
maxMax = walletBal * maxBudget * maxOpen * cp.close / contVal
end
local maxCheck = autoMax and (maxMax * leverage)
local maxValue = autoMax and (maxCheck > maxSizeD) and maxCheck or maxSizeD
local maxSize = autoMax and maxValue or maxSizeD
-- check max open
if autoMax then
if maxCheck > maxSizeD then
sLog('Dynamic Max Open: '..Round(maxCheck, 5)..' '..AmountLabel())
else
sLog('Dynamic Max Open: '..Round(maxCheck, 5)..' but using default value: '..Round(maxSizeD, 5)..' '..AmountLabel())
end
end
-- slot size
if autoSlot then
local slotCheck = maxSize / slotBudget
slotSize = slotCheck > slotSizeD and slotCheck or slotSizeD
sLog('Dynamic Slot Size: '..Round(slotSize, 5)..' '..AmountLabel())
else
slotSize = slotSizeD
sLog('Slot Size Value: '..Round(slotSize, 5))
end
relevantChange = hasChanged("slotSize", slotSize, 0.07) and true or relevantChange
-- CORE LOGIC
-- custom arrayAdd because native ArrayAdd does not cope with nested arrays
local function arrayAdd(array, element)
local len = #array
array[len + 1] = element
return array
end
-- get price base
local getPriceBase = function(isLong)
local priceBase = isLong
and cp.bid
or cp.ask
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
return priceBase
end
-- manage position ids
local lDelta = Delta(lInitOrder.price, getPriceBase(true))
sLog("lInitOrder.price: " .. lInitOrder.price .. " / lDelta: "..lDelta, dbg)
local sDelta = Delta(getPriceBase(false), sInitOrder.price)
sLog("sInitOrder.price: " .. sInitOrder.price .. " / sDelta: "..sDelta, dbg)
if pamt_l == 0 and (IsPositionClosed(hedge_longPosId) or (lInitOrder.price > 0 and lDelta >= slotCancel)) then
if IsAnyOrderOpen(hedge_longPosId) then
CancelAllOrders(hedge_longPosId)
canc_l = true
sLog('lOrders cancelled', dbg)
else
hedge_longPosId = NewGuid()
dir_l = GetPositionDirection(hedge_longPosId)
aep_l = GetPositionEnterPrice(hedge_longPosId)
pamt_l = GetPositionAmount(hedge_longPosId)
proi_l = GetPositionROI(hedge_longPosId)
end
Save('lSlots', {})
Save('lInitOrder', {price = 0})
end
if pamt_s == 0 and (IsPositionClosed(hedge_shortPosId) or (sInitOrder.price > 0 and sDelta >= slotCancel)) then
if IsAnyOrderOpen(hedge_shortPosId) then
CancelAllOrders(hedge_shortPosId)
canc_s = true
sLog('sOrders cancelled', dbg)
else
hedge_shortPosId = NewGuid()
dir_s = GetPositionDirection(hedge_shortPosId)
aep_s = GetPositionEnterPrice(hedge_shortPosId)
pamt_s = GetPositionAmount(hedge_shortPosId)
proi_s = GetPositionROI(hedge_shortPosId)
end
Save('sSlots', {})
Save('sInitOrder', {price = 0})
end
-- get pos id
local getPositionId = function(isLong)
return isLong and hedge_longPosId or hedge_shortPosId
end
-- get price
local getPrice = function(isLong, spr)
local basePrice = isLong and lInitOrder.price or sInitOrder.price
local price = 0
if basePrice > 0 then
price = isLong
and SubPerc(lInitOrder.price, spr)
or AddPerc(sInitOrder.price, spr)
else
price = isLong
and SubPerc(getPriceBase(isLong), spr)
or AddPerc(getPriceBase(isLong), spr)
end
return price
end
-- get spread
local getSpread = function(index)
index = index - 1 -- we start from 0
if index == 0 then
spr = slotSpreadMin
elseif index > 0 and index <= slotCountLow then
spr = slotSpreadMin + slotSpreadLow * index
else
spr = slotSpreadMin + (slotSpreadLow * slotCountLow) + (AddPerc(slotSpreadHigh, ((slotSpreadHighFactor * (index - slotCountLow)))) * (index - slotCountLow))
end
sLog("slotSpreadMin + (slotSpreadLow * slotCountLow) + (AddPerc(slotSpreadHigh, ((slotSpreadHighFactor * (index - slotCountLow)))) * (index - slotCountLow))", dbg)
sLog(slotSpreadMin.." + ("..slotSpreadLow.." * "..slotCountLow..") + (AddPerc("..slotSpreadHigh..", (("..slotSpreadHighFactor.." * ("..index.." - "..slotCountLow..")))) * ("..index.." - "..slotCountLow.."))", dbg)
sLog("Slot: " .. index .. " / Spread: " .. spr .. " (" .. AddPerc(10000, spr) .. " / " .. SubPerc(10000, spr) .. ")", dbg)
return spr
end
-- place order
local placeOrder = function(isLong, price, amount, name, posId)
local cmd = isLong and PlaceGoLongOrder or PlaceGoShortOrder
local oid = ''
oid = cmd(price, amount, {type = MakerOrCancelOrderType, note = name, timeout = 3600, positionId = posId})
return oid
end
-- slot management
local updateSlots = function(isLong, amount, slotCountTotal, cancelDist, canPlace)
local prefix = isLong and 'l' or 's'
local slots = Load(prefix..'Slots', {})
local posId = getPositionId(isLong)
local openOrders = 0
slots['orders'] = IsNull(slots['orders']) and {} or slots['orders']
if (Count(slots['orders']) > 0) then
for key,order in pairs(slots['orders']) do
local name = prefix .. key
if order.oid != '' then
local orderContainer = OrderContainer(order.oid)
if relevantChange and orderContainer.isOpen then
CancelOrder(order.oid)
sLog("Order "..name.." cancelled due to relevant changes", dbg, true)
end
sLog(name..'order '..name..' is open ('..Parse(orderContainer.isOpen, StringType)..') or cancelled ('..Parse(orderContainer.isCancelled, StringType)..') or -filled ('..Parse(orderContainer.isFilled, StringType)..')-', dbg)
local price = getPrice(isLong, getSpread(key))
if orderContainer.isFilled then
slots['orders'][key]['fillTS'] = (order.fillTS != '' and order.fillTS > 0) and order.fillTS or AdjustTimestamp(order.placeTS, {addSeconds = orderContainer.openTime})
sLog("("..order.fillTS.." != '' and "..order.fillTS.." > 0) and "..order.fillTS.." or AdjustTimestamp("..order.placeTS..", {addSeconds = "..orderContainer.openTime.."})", dbg)
sLog(order.name .. ">fillTS: "..sDateString(order.fillTS), dbg)
end
sLog(order.name..">isFilled: "..Parse(orderContainer.isFilled,StringType).." and "..Time()..">"..AdjustTimestamp(slots['orders'][key]['fillTS'], {addHours = hoursUntilRenewSlot}).." (AdjustTimestamp("..slots['orders'][key]['fillTS']..", {addHours = "..hoursUntilRenewSlot.."})",dbg)
if orderContainer.isOpen then
sLog("canPlace: "..Parse(canPlace,StringType).." or "..openOrders.." >= "..slotCountTotal, dbg)
if not canPlace or openOrders >= slotCountTotal then
CancelOrder(order.oid)
sLog('Not allowed right now '..name, dbg, true)
else
openOrders = openOrders + 1
sLog('Open order counted: '..name, dbg)
end
elseif (
orderContainer.isCancelled
or (
orderContainer.isFilled
and hoursUntilRenewSlot > 0
and Time() > AdjustTimestamp(slots['orders'][key]['fillTS'], {addHours = hoursUntilRenewSlot})
)
) and ((isLong and cp.ask > price) or (not isLong and cp.bid < price)) and canPlace and openOrders < slotCountTotal then
slots['orders'][key]['placeTS'] = Time()
slots['orders'][key]['fillTS'] = 0
slots['orders'][key]['name'] = (orderContainer.isFilled and 'x' or 'r') .. prefix..key..' '..sDateString(slots['orders'][key]['placeTS'])
slots['orders'][key]['oid'] = placeOrder(isLong, price, amount, slots['orders'][key]['name'], order.posId)
openOrders = openOrders + 1
sLog(name..'order '..name..' placed because it was cancelled ('..Parse(orderContainer.isCancelled, StringType)..') or -filled ('..Parse(orderContainer.isFilled, StringType)..')-', dbg)
end
end
end
end
local slotCount = Count(slots['orders'])
sLog('Open '..prefix..' orders: '..openOrders..' of '..slotCountTotal, dbg)
for i = slotCount + 1, slotCount + slotCountTotal - openOrders do
sLog('New order slot ('..i..')', dbg)
if canPlace then
if isBacktest then SetFee(Abs(MakersFee())*-1) end
local name = 'n' .. prefix .. i ..' '..sDateString(Time())
local price = getPrice(isLong, getSpread(i))
oid = placeOrder(isLong, price, amount, name, posId)
orderInfo = {oid = oid, placeTS = Time(), fillTS = 0, price = price, amount = amount, name = name, posId = posId}
slots['orders'] = arrayAdd(slots['orders'], orderInfo)
sLog(name..' order '..name..' placed because '..(i - slotCount + openOrders - 1)..' slots are less than '..slotCountTotal, dbg)
if i == 1 then
Save(prefix..'InitOrder', {oid = oid, price = price})
end
end
end
Save(prefix..'Slots', slots)
end
-- update take-profit
local updateTakeProfit = function(isLong, entryPrice, 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', 0)
local posId = getPositionId(isLong)
local tp_delta = isLong and Delta(entryPrice, cp.bid) or Delta(cp.ask, entryPrice)
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)
sLog('Delta cancelled '..name, dbg, true)
end
else
if order.isCancelled then
timer = 0
end
oid = ''
end
else
if tp_delta >= targetRoi and Time() >= timer then
if isBacktest then SetFee(tpOrderType == MarketOrderType and TakersFee() or Abs(MakersFee())*-1) end
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)
sLog('Delta cancelled '..name, dbg, true)
end
else
oid = ''
end
else
if currentSize > sizeLimit and not noReduce and Time() >= timer then
if isBacktest then SetFee(reduceOrderType == MarketOrderType and TakersFee() or Abs(MakersFee())*-1) end
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, aep_l, takeProfit, slotCancel)
updateTakeProfit(false, aep_s, takeProfit, slotCancel)
-- risk management
updatePositionManagement(true, pamt_l, maxSize, slotCancel)
updatePositionManagement(false, pamt_s, maxSize, slotCancel)
-- update slots
sLog("canc_l: "..Parse(canc_l, StringType))
if not canc_l then
sLog("Update Long Slots",dbg)
updateSlots(true, slotSize, slotCountTotal, slotCancel, okLong) -- long slot
end
sLog("canc_s: "..Parse(canc_s, StringType))
if not canc_s then
sLog("Update Short Slots",dbg)
updateSlots(false, slotSize, slotCountTotal, slotCancel, okShort) -- 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)
if not isBacktest then CC_VPM3() end -- please install ... first
-- WTF
if wtfStop then
local longNull = pamt_l == 0
local shortNull = pamt_s == 0
if longNull and shortNull then
DeactivateBot('Deactivate on No Position is active', true)
else
sLog('Will deactivate on No Position, waiting...maybe next second, maybe never :)', true, true)
end
end
if wtfBal then
if workBal > budgetBal then
DeactivateBot('Over Budget', true)
end
end
if wtfAmount then
if pamt_l > maxSize or pamt_s > maxSize then
DeactivateBot('Over Size', true)
end
end
-- PlotExposure
local lineLong = IfElse(pamt_l > 0, pamt_l, 0)
local lineShort = IfElse(pamt_s > 0, -pamt_s, 0)
Plot(1, 'Longs', lineLong, {c=Green, s=Step})
Plot(1, 'Max Longs', maxSize, {c=White, s=Step})
Plot(1, 'Shorts', lineShort, {c=Red, s=Step})
Plot(1, 'Max Shorts', -maxSize, {c=White, s=Step})
ChartSetOptions(1, 'Exposure')
Plot(2, 'Budget Balance', (walletBal * maxBudget), {c=White, s=Step})
Plot(2, 'WorkBal', workBal, {c=Red, s=Step})
ChartSetOptions(2, 'Balance Monitor')
sLog('Size Reduction: '..SRCounter..' times')
if not okLong then
sLog('Bot is not allowed to open LONG', true, true)
end
if not okShort then
sLog('Bot is not allowed to open SHORT', true, true)
end
Finalize(function()
CustomReport('Current Balance Ratio', Round(100 * balRatio, 2)..'%')
CustomReport('Highest Balance Ratio', Round(100 * BRCounter, 2)..'%')
CustomReport('Size Reduction', SRCounter..' times')
CustomReport('Final Wallet Balance', Round(walletBal, 5)..' '..profitLabel)
if testWallet then
local profitPercent = Round(PercentageChange(testBal, walletBal), 2)
CustomReport('Profit %: ', profitPercent..'%')
CustomReport('Profit to Highest Bal Ratio', Round(profitPercent / (100 * BRCounter), 2))
end
end)
sLog(' ')
8 Comments
Sign in to leave a comment.
Hi, thanks for the post,
Think that calls a custom command:
ERROR: Unknown references: CC_VPM3
Daniel.
Sorry, saw it now in the comments...
Hi,
I have a question. Could you make a function that increase the slot size? So for example first order slot is 0.15 second order slot 0.20 etc
Hi,
I started implementing it a while ago and I remember there was some hurdle (I don't know exactly what it was) why I stopped working on it. It's still in my backlog but not with high priority. I am not sure how benificiary it really is. I also remember that I did some calculations and found the dynamic spreads renewing the slots after a while more helpful. If you think it's a very helpful strategy contact me via discord and I am happy to hear your thoughts.
Hi, first of all thanks a lot for releasing your code! Secondly after quickly looking over it i was not able to explain myself what the part is for:
if isBacktest then SetFee(Abs(MakersFee())*-1) end
I mean i get that you are actually making negative fees positive and vice versa, but what for? I really would love to understand that one, maybe you could explain? 🙂
Thanks!
P.S: And this one as well 🙂
if not isBacktest then CC_VPM3() end
Dan
Hi Dan, thanks for taking a look at the details. :) It feels good seeing people digging deeper and challenging the code.
Honestly, I don't understand the first line either. This is coming from the original by Pshai. The only thing I added was checking for isBacktest because SetFee() is applied in backtests only anyways and I thought it might speed up the backtest by a nano second if I check this first. If you would like to understand why setFee() is used like this please approach Pshai, most preferrably on the Discord server in the #haasscript-bots channel. I am sure he has an answer to this...I just trusted him with this line of code but now that you're asking I am interested as well.
Regarding if not isBacktest then CC_VPM3() end I have a proper answer. :D It's loading the Custom Command for Virtual Position Management by Kobalt but this is definitely not used in backtests why I also tried to save some time in backtests not loading it at all. I did some performance tests and it doesn't save as much as I was hoping for but if you're testing a large timeframe and some variations (HaasLabs) it definitely counts.
I hope this helps.
Cheers,
Shoe
Hi Shoe,
thanks a lot for the quick reply, now i understand the approach of speeding up the backtest by disabling CC_VPM3, a smart idea! Regarding the other question i will get in contact with Pshai directly, thank you!
Best
Dan
Hey Shoe,
thank you for this awesome version of the SMM. I wrote you a DM on discord concerning a problem I have with a mod I did to your script. Kind regards Hiromatsu