Bot Engine - Strooth Version [FEBE Mod]
stableDescription
A modified version of Pshai's FEBE
1. Install all of these (they have their own requirements as well )
Required - https://www.haasscripts.com/t/cmd-weighted-slot-spread-price-and-amount/
Required - https://www.haasscripts.com/t/bot-engine-settings-strooth-version-febe-mod/
Required - https://www.haasscripts.com/t/cmd-totext/
Required - https://www.haasscripts.com/t/cmd-backtestsettings-strooth-mod
Required - https://www.haasscripts.com/t/cmdsetperc/
Required - https://www.haasscripts.com/t/cmd-vpm-haasscript-version/
2. Install this bot engine itself
3. load the up ve template and play around with some ideas or
VE Template - https://www.haasscripts.com/t/bot-engine-template-ve-strooth-version-febe-mod/
4. Create a new script in se and as in the example below
An Example of how to use the bot engine in Haas Script without the VE Template provided is in the first 4 lines of the bot engine but here it is anyway
local defaults = CC_BotEngine_Settings({}, true, 'S', 10, MinimumTradeAmount(), 0.1, 0.1, 0.1, false, false, LimitOrderType, 600, false, false, 1440, 14, 1, true, LimitOrderType, 600, false, false, 1440, 14, 1, true, 1, LimitOrderType, true, 1, LimitOrderType)
local default = CC_BotEngine_Settings(defaults, true, 'L', 10, MinimumTradeAmount(), 0.1, 0.1, 0.1, false, true, LimitOrderType, 600, false, false, 1440, 14, 1, false, LimitOrderType, 600, false, false, 1440, 14, 1, true, 1, LimitOrderType, true, 1, LimitOrderType)
CC_BotEngine(default , true, false)
HaasScript
DefineCommand('BotEngine', 'Execute Bot Engine logic with parameters')
EnableHighSpeedUpdates(true)
local defaults = CC_BotEngine_Settings({}, true, 'S', 10, MinimumTradeAmount(), 0.1, 0.1, 0.1, false, false, LimitOrderType, 600, false, false, 1440, 14, 1, true, LimitOrderType, 600, false, false, 1440, 14, 1, true, 1, LimitOrderType, true, 1, LimitOrderType)
local default = CC_BotEngine_Settings(defaults, true, 'L', 10, MinimumTradeAmount(), 0.1, 0.1, 0.1, false, true, LimitOrderType, 600, false, false, 1440, 14, 1, false, LimitOrderType, 600, false, false, 1440, 14, 1, true, 1, LimitOrderType, true, 1, LimitOrderType)
local ps = DefineParameter(ListDynamicType, 'params', 'Parameters for the core logic', false, default)
local backtest = DefineParameter(BooleanType, 'backtest', 'Enable backtest settings', false, true)
--=================================================================================================================================================
--- Logger Functions
--=================================================================================================================================================
local LoggerLevels = {
ErrorsOnly = 'Errors Only',
ErrorsAndWarnings = 'Erorrs & Warnings',
All = 'All'
}
local Logger = {
_level = InputOptions('DEBUG Level',
LoggerLevels.All,
LoggerLevels,
{group = ' DEBUG'}),
_in = 'Main',
_prevIn = {},
_log = Log,
_warn = LogWarning,
_error = LogError
}
local verbose = DefineParameter(BooleanType, 'log', 'show logs', false, Input('DEBUG Enable', false, {group = ' DEBUG'}))
function Logger:level()
if Logger._level == LoggerLevels.ErrorsOnly then
return 0
elseif Logger._level == LoggerLevels.ErrorsAndWarnings then
return 1
elseif Logger._level == LoggerLevels.All then
return 2
end
LogError('Logger level undefined: "' .. Logger._level .. '"')
end
function Logger:where()
local ret = ''
local prevs = self._prevIn
if #prevs > 0 then
for i = 1, #prevs do
if prevs[i] != '' and #prevs[i] > 0 then
ret = ret .. prevs[i] .. '::'
end
end
end
return ret .. self._in
end
function Logger:enter(to, enable)
if enable then
self._prevIn = ArrayAdd(self._prevIn, self._in)
self._in = to
end
end
function Logger:exit(enable)
if enable then
self._in = ArrayLast(self._prevIn)
self._prevIn = ArrayPop(self._prevIn)
end
end
function Logger:log(msg, enable)
if enable then
if self:level() >= 2 then
local _in = Logger:where()
self._log('['.._in..'] '..msg)
end
end
end
function Logger:warn(msg, enable)
if enable then
if self:level() >= 1 then
local _in = Logger:where()
self._warn('['.._in..'] '..msg)
end
end
end
function Logger:error(msg, enable)
if enable then
local _in = Logger:where()
self._error('['.._in..'] '..msg)
end
end
---=============================================================================================================================
Logger:log('start bot engine <3', verbose)
---=============================================================================================================================
Logger:log('define function to parse order type', verbose)
local ParseOrderType = function(type)
local ret
local arr = {
LimitOrderType,
MarketOrderType,
MakerOrCancelOrderType,
NoTimeOutOrderType,
StopLimitOrderType,
StopMarketOrderType,
TakeProfitLimitOrderType,
TakeProfitMarketOrderType
}
for i=1, #arr do
if type == i then
ret = arr[i]
break
elseif type == arr[i] then
ret = i
break
end
end
return ret
end
Logger:log('start positionid loop', verbose)
for i=1, Count(ps) do
Logger:enter('positionid loop = '..i, verbose)
local pid,p = ps[i].pid, ps[i].params
pid = IfNull(pid, i)
Logger:log('Id = '..pid, verbose)
Logger:log('position information', verbose)
local pos = {guid = Load(pid..'guid', NewGuid())}
pos.filled_l = Load(pos.guid..'filled_l', 1)
pos.filled_s = Load(pos.guid..'filled_s', 1)
pos.dir = PositionToBool(GetPositionDirection(pos.guid))
pos.aep = GetPositionEnterPrice(pos.guid, false)
pos.amt = GetPositionAmount(pos.guid)
if pos.amt == 0 and IsPositionClosed(pos.guid) then
if IsAnyOrderOpen(pos.guid) then
CancelAllOrders(pos.guid)
end
if IsAnyOrderFinished(pos.guid) and IsPositionClosed(pos.guid) and not IsAnyOrderOpen(pos.guid) then
if Count(GetAllOpenPositions()) >= Count(ps) then break
else
pos.guid = NewGuid()
pos.filled_l = 1
pos.filled_s = 1
pos.dir = PositionToBool(GetPositionDirection(pos.guid))
pos.aep = GetPositionEnterPrice(pos.guid, false)
pos.amt = GetPositionAmount(pos.guid)
end
end
end
Logger:log(' setup trading commands', verbose)
local cmd_exitPosition = PlaceExitPositionOrder
Logger:log(' price information', verbose)
local cp = CurrentPrice()
Logger:log(' supported modules array', verbose)
local supported_modules = {
's_orders',
's_tp',
's_sl',
's_buy',
's_sell',
}
local active_modules = {}
for i = 1, #supported_modules do
active_modules[ supported_modules[i] ] = false
end
local updateOrderBundle = function(isLong, id, settings)
Logger:enter(' update order bundle (buy/sell entries)', verbose)
local weights, pricelist, start
local active = settings.active
Logger:log(' get price and amount spread', verbose)
if settings.order_refill == true then start = 1 else start = isLong and pos.filled_l or pos.filled_s end
if active then
if settings.order_bestprice then
pricelist = isLong and Min(ArrayFilter({GetPositionEnterPrice(pos.guid, false), cp.low, cp.bid, LastLongPrice()}, 0, ArrayFilterGreaterThanType)) or Max(ArrayFilter({GetPositionEnterPrice(pos.guid, false), cp.high, cp.ask, LastShortPrice()}, 0, ArrayFilterGreaterThanType))
else pricelist = isLong and cp.bid or cp.ask end
weights = CC_WeightedSpread(isLong, settings.order_size, settings.order_count, pricelist, settings.order_distribution, settings.order_spread, settings.order_atr, settings.order_atrint, settings.order_atrper)
end
for i = Parse(start, NumberType), settings.order_count do
local name = id .. i
local oid = Load(pos.guid .. name .. 'oid', '')
local place_price = Load(pos.guid .. name .. 'pp', 0)
local filled = Load(pos.guid .. name .. 'f', false)
if settings.order_refill == false and filled == true then return end
if oid != '' then
local order = OrderContainer(oid)
local delta = -1
local max_dist = active and settings.order_cancel_dist or 0
Logger:log(' only calculate delta if cancel distance is used', verbose)
if max_dist > 0 then
delta = isLong
and Delta(cp.bid, place_price)
or Delta(place_price, cp.ask)
end
if order.isOpen then
if not active then
CancelOrder(oid)
Logger:log('Deactivated '..name, verbose)
elseif delta >= settings.order_cancel_dist then
CancelOrder(oid)
Logger:log('Delta cancelled '..name..' (delta: '..Round(delta, 4)..' %)', verbose)
end
if IsOrderOpen(oid) then
Plot(0, name, order.price, {c=Yellow(70), id=oid, w=1.5})
end
elseif order.isCancelled and active and not( delta >= settings.order_cancel_dist) then
oid = PlaceCancelledOrder(oid, order.price)
elseif order.isFilled then
if isLong then
pos.filled_l = pos.filled_l+1
else
pos.filled_s = pos.filled_s+1
end
oid = ''
filled = true
else
oid = ''
end
elseif active then
if filled == false then
local delta = -1
local max_dist = settings.order_cancel_dist or 0
Logger:log(' order price with spread', verbose)
local price = ParseTradePrice(PriceMarket(), weights.price[i])
if max_dist > 0 then
delta = isLong
and Delta(cp.ask, price)
or Delta(price, cp.bid)
end
if delta >= (settings.order_cancel_dist*(i+1)) then
return
else
Logger:log(' select trading command', verbose)
local cmd = isLong and PlaceGoLongOrder or PlaceGoShortOrder
Logger:log(' order settings', verbose)
local order_settings = {
type = ParseOrderType(settings.order_type),
note = pid..' '..name,
timeout = settings.order_timeout,
positionId = pos.guid
}
Logger:log(' order size', verbose)
local orderSize = ParseTradeAmount(PriceMarket(), price, Max(weights.amount[i], MinimumTradeAmount()))
Logger:log(' place order', verbose)
oid = cmd(price, orderSize, order_settings)
Logger:log(' save price for cancel threshold calcs', verbose)
place_price = cp.close
filled = false
end
end
end
if filled and Or(pos.dir.isNone, pos.amt == 0, pos.aep == 0) then
Logger:log(' reset', verbose)
filled = false
place_price = 0
end
Save(pos.guid .. name .. 'oid', oid)
Save(pos.guid .. name .. 'pp', place_price)
Save(pos.guid .. name .. 'f', filled)
end
Logger:exit(verbose)
end
local updateExitOrder = function(id, note, price, type, active, tprice)
Logger:log(' update exit order', verbose)
local oid = Load(pos.guid .. id .. 'oid', '')
if oid == '' then
if active then
local settings = {
type = ParseOrderType(type),
note = pid..' '..note,
timeout = 9999999,
positionId = pos.guid,
triggerPrice = tprice
}
local isLong = pos.dir.isLong
local cmd = isLong and PlaceExitLongOrder or PlaceExitShortOrder
oid = cmd(price, pos.amt, settings)
end
else
local order = OrderContainer(oid)
if order.isOpen then
if not active then
CancelOrder(oid)
Logger:log('Deactivated '..guid ..' '.. note, verbose)
elseif order.executedAmount != pos.amt then
CancelOrder(oid)
Logger:log('Refreshing order size - '..note, verbose)
elseif order.price != price then
CancelOrder(oid)
Logger:log('Refreshing order price - '..note, verbose)
end
elseif order.isFilled then
if pos.dir.isLong then
pos.filled_l = 1
else
pos.filled_s = 1
end
oid = ''
end
end
Save(pos.guid .. id .. 'oid', oid)
Logger:exit(verbose)
end
Logger:log(' pre-process parameters', verbose)
local params = {}
local plen
if IsNotNull(p) then
if #p > 0 then plen = #p
else return end
else return end
local key = ''
local value
for i = 1, plen do
key = p[i][1]
value = p[i][2]
params[ key ] = {}
Logger:log(' build section parameters', verbose)
if value != nil then
local vlen = #value
for j = 1, vlen do
params[ key ][ value[j][1] ] = value[j][2]
end
end
active_modules[ key ] = true
Logger:log( params[key] , verbose)
end
---------------------------- MODULES ------------------------------
-- Module functionality should be placed below these lines! --
---------------------------------------------------------------------
Logger:log(' pre-define required variables', verbose)
local orders = {}
local module = {}
module.name = supported_modules[1]
module.max_open = 'max_open'
module.size = 'size'
module.spread = 'spread'
module.cancel_dist = 'cancel_dist'
module.trigger = 'trigger'
module.bestprice = 'bestprice'
if active_modules[ module.name ] == true then
Logger:enter('Found order settings ', verbose)
local module_params = params[ module.name ]
Logger:log(' grab order settings', verbose)
orders.max_open = module_params[ module.max_open ]
orders.size = module_params[ module.size ]
orders.spread = module_params[ module.spread ]
orders.cancel_dist = module_params[ module.cancel_dist ]
orders.trigger = module_params[ module.trigger ]
orders.bestprice = module_params[ module.bestprice ]
Logger:exit(verbose)
else
Logger:enter(' setup 1 order and use main TradeAmount()', verbose)
orders.max_open = 1
orders.size = MinimumTradeAmount()
orders.spread = 1
orders.cancel_dist = 1
orders.trigger = 1
orders.bestprice = true
Logger:exit(verbose)
end
---=============================================================================================================================
Logger:log(' setup take-profit', verbose)
module = {}
module.name = supported_modules[2]
module.percent = 'percent'
module.order_type = 'order_type'
if active_modules[ module.name ] == true then
Logger:enter('Found take-profit settings ', verbose)
local module_params = params[ module.name ]
Logger:log(' grab settings', verbose)
local tp = {}
tp.percent = module_params[ module.percent ]
tp.tpercent = orders.trigger
tp.order_type = module_params[ module.order_type ]
tp.active = Load('tpactivated', false)
tp.price = 0
tp.trigger = 0
tp.safety = IfElseIf(pos.dir.isLong, pos.dir.isShort, Delta(pos.aep, cp), Delta(cp, pos.aep), 0)
if Load(pos.guid..'tpsafety', false) == false then
if tp.safety != 0 and IsSmallerThan(tp.safety, 0) then
if tp.safety < Mul(tp.percent, -1) then
Save(pos.guid..'tpsafety', true)
end
end
end
Logger:log(' if using market order, wait for trigger', verbose)
if tp.order_type == 2 then
if IfElseIf(pos.dir.isLong, pos.dir.isShort, Delta(pos.aep, cp.bid) > tp.percent, Delta(cp.ask, pos.aep) > tp.percent, false) then
tp.active = true
tp.price = cp.close Logger:log(' price isnt important for market orders', verbose)
end
Logger:log(' if using other than stop orders, we can place it immediately', verbose)
elseif tp.order_type <= 4 or tp.order_type >= 7 then
Logger:log(' place TP beforehand', verbose)
tp.active = true
Logger:log(' calculate trigger price', verbose)
if pos.dir.isLong then
tp.price = AddPerc( pos.aep, tp.percent )
if tp.tpercent != 0 then
tp.trigger = AddPerc( tp.price, tp.tpercent )
end
elseif pos.dir.isShort then
tp.price = SubPerc( pos.aep, tp.percent )
if tp.tpercent != 0 then
tp.trigger = SubPerc( tp.price, tp.tpercent )
end
end
else
Logger:error('Native Stop order types not supported for Take-Profit ')
tp.active = false
end
tp.active = tp.active and pos.dir.isNone == false
if Load(pos.guid..'tpsafety', false) then
if GetPositionProfit(pos.guid) > 1 then
Save(pos.guid..'tpsafetytrail', true)
end
end
if Load(pos.guid..'tpsafetytrail', false) then
if CC_ProfitTrailer(tp.percent, tp.percent/3, 'shrink', 0, pos.guid) then
tp.active = false
tp.price = 0
tp.trigger = 0
if pos.dir.isLong then
pos.filled_l = 1
elseif pos.dir.isShort then
pos.filled_s = 1
end
if IsAnyOrderOpen(pos.guid) then
CancelAllOrders(pos.guid)
end
if IsAnyOrderFinished(pos.guid) or not IsAnyOrderOpen(pos.guid) then
PlaceExitPositionOrder(pos.guid, cp, MarketOrderType, 'Bot Safety Exit')
Save(pos.guid..'tpsafety', false)
Save(pos.guid..'tpsafetytrail', false)
end
end
end
if tp.active then
Save('tpactivated', true)
if pos.aep > 0 then
Plot(0, 'TP-Trailing Activated', AddPerc(pos.aep, tp.percent), {c=Green(50), w=2, id=pos.guid})
end
if CC_ProfitTrailer(tp.percent, tp.percent/2, 'default', 2, pos.guid) then
Save('tptrigger', true)
end
end
if Load('tptrigger', false) then
Logger:log(' if we got a price, parse it properly', verbose)
if tp.price > 0 then
tp.price = ParseTradePrice(PriceMarket(), tp.price)
end
if tp.trigger > 0 then
tp.trigger = ParseTradePrice(PriceMarket(), tp.trigger)
Logger:log(' run', verbose)
updateExitOrder('tp', 'Take-Profit', tp.price, tp.order_type, tp.active, tp.trigger)
Save('tpactivated', false)
Save('tptrigger', false)
else
Logger:log(' run', verbose)
updateExitOrder('tp', 'Take-Profit', tp.price, tp.order_type, tp.active)
Save('tpactivated', false)
Save('tptrigger', false)
end
end
Logger:exit(verbose)
end
---=============================================================================================================================
Logger:log(' setup stop-loss', verbose)
module.name = supported_modules[3]
if active_modules[ module.name ] == true then
Logger:enter('Found stop-loss settings ', verbose)
local module_params = params[ module.name ]
Logger:log(' grab settings', verbose)
local sl = {}
sl.percent = module_params[ module.percent ]
sl.tpercent = orders.trigger
sl.order_type = module_params[ module.order_type ]
sl.active = false
sl.price = 0
sl.trigger = 0
Logger:log(' regular order types', verbose)
if sl.order_type <= 4 then
Logger:log(' wait until percent price change and place order', verbose)
--if StopLoss(sl.percent, pos.guid, GetPositionDirection(pos.guid)) then
if IfElseIf(pos.dir.isLong, pos.dir.isShort, Delta(cp.bid, pos.aep) > sl.percent, Delta(pos.aep, cp.ask) > sl.percent, false) then
sl.active = true
if pos.dir.isLong then
sl.price = cp.ask
elseif pos.dir.isShort then
sl.price = cp.bid
end
end
Logger:log(' native stop types', verbose)
elseif sl.order_type > 4 and sl.order_type < 7 then
Logger:log(' place SL beforehand when using native stops', verbose)
sl.active = true
Logger:log(' calculate trigger price', verbose)
if pos.dir.isLong then
sl.price = SubPerc( pos.aep, sl.percent )
if sl.tpercent != 0 then
sl.trigger = SubPerc( sl.price, sl.tpercent )
end
elseif pos.dir.isShort then
sl.price = AddPerc( pos.aep, sl.percent )
if sl.tpercent != 0 then
sl.trigger = AddPerc( sl.price, sl.tpercent )
end
end
else
Logger:error('Native Take-Profit order types not supported for Stop-Loss ')
end
sl.active = sl.active and pos.dir.isNone == false
Logger:log(' if we got a price, parse it properly', verbose)
if sl.price > 0 then
sl.price = ParseTradePrice(PriceMarket(), sl.price)
end
if sl.trigger > 0 then
sl.trigger = ParseTradePrice(PriceMarket(), sl.trigger)
Logger:log(' run', verbose)
updateExitOrder('sl', 'Stop-Loss', sl.price, sl.order_type, sl.active, sl.trigger)
else
Logger:log(' run', verbose)
updateExitOrder('sl', 'Stop-Loss', sl.price, sl.order_type, sl.active)
end
Logger:exit(verbose)
end
---=============================================================================================================================
Logger:log(' setup and run buying', verbose)
module = {}
module.name = supported_modules[4]
module.order_timeout = 'order_timeout'
module.order_refill = 'order_refill'
module.order_atr = 'order_atr'
module.order_atrint = 'order_atrint'
module.order_atrper = 'order_atrper'
module.order_distribution = 'order_distribution'
module.order_type = 'order_type'
if active_modules[ module.name ] == true then
Logger:enter('Found buy settings ', verbose)
local module_params = params[ module.name ]
Logger:log(' grab settings', verbose)
local buy = {}
buy.active = true
buy.order_timeout = module_params[ module.order_timeout ]
buy.order_refill = module_params[ module.order_refill ]
buy.order_type = module_params[ module.order_type ]
buy.order_distribution = module_params[ module.order_distribution ]
buy.order_atr = module_params[ module.order_atr ]
buy.order_atrint = module_params[ module.order_atrint ]
buy.order_atrper = module_params[ module.order_atrper ]
buy.order_count = orders.max_open
buy.order_size = orders.size
buy.order_spread = orders.spread
buy.order_cancel_dist = orders.cancel_dist
buy.order_bestprice = orders.bestprice
Logger:log(' update orders with buy settings', verbose)
updateOrderBundle( true, 'buy', buy )
Logger:exit(verbose)
else
Logger:enter(' grab settings', verbose)
local buy = {}
buy.active = false
buy.order_count = orders.max_open
Logger:log(' cancel all open orders and reset', verbose)
updateOrderBundle( true, 'buy', buy )
Logger:exit(verbose)
end
---=============================================================================================================================
Logger:log(' setup and run selling', verbose)
module.name = supported_modules[5]
if active_modules[ module.name ] == true then
Logger:enter('Found sell settings ', verbose)
local module_params = params[ module.name ]
Logger:log(' grab settings', verbose)
local sell = {}
sell.active = true
sell.order_timeout = module_params[ module.order_timeout ]
sell.order_refill = module_params[ module.order_refill ]
sell.order_type = module_params[ module.order_type ]
sell.order_distribution = module_params[ module.order_distribution ]
sell.order_atr = module_params[ module.order_atr ]
sell.order_atrint = module_params[ module.order_atrint ]
sell.order_atrper = module_params[ module.order_atrper ]
sell.order_count = orders.max_open
sell.order_size = orders.size
sell.order_spread = orders.spread
sell.order_cancel_dist = orders.cancel_dist
sell.order_bestprice = orders.bestprice
Logger:log(' update orders with sell settings', verbose)
updateOrderBundle( false, 'sell', sell )
Logger:exit(verbose)
else
Logger:enter(' grab settings', verbose)
local sell = {}
sell.active = false
sell.order_count = orders.max_open
Logger:log(' cancel all open orders and reset', verbose)
updateOrderBundle( false, 'sell', sell )
Logger:exit(verbose)
end
if pos.amt == 0 or pos.aep == 0 then Save(pos.guid..'guid', NewGuid()) end
---=============================================================================================================================
if pos.dir.isNone == false then
Logger:enter(' plot position information on the chart', verbose)
local pc = PositionContainer(pos.guid, false)
local isLong = pos.dir.isLong
Logger:log(' position average entry price', verbose)
if pc.amount ~= 0 or pc.enterPrice ~= 0 then
Plot(0, pid..' EnterPrice', pc.enterPrice, LineOptions({color = isLong and Teal or Fuchsia, w=2, id = pos.guid}))
end
Logger:exit(verbose)
end
Save(pid..'guid', pos.guid)
Save(pos.guid..'filled_l', pos.filled_l)
Save(pos.guid..'filled_s', pos.filled_s)
Logger:log('End of for loop of pids', verbose)
Logger:exit(verbose)
end
if backtest then
Logger:enter('Load Backtest Settings', verbose)
CC_BacktestSettings()
Logger:log('Finished Backtest Settings', verbose)
Logger:exit(verbose)
end
---=============================================================================================================================
DefineOutput(VoidType)
4 Comments
Sign in to leave a comment.
7. 20 Jun 2021 14:57:13 ERROR: DefineOutput() is not allowed in bot scripts.
6. 20 Jun 2021 14:57:13 ERROR: DefineParameter() is not allowed in bot scripts.
5. 20 Jun 2021 14:57:13 ERROR: DefineParameter() is not allowed in bot scripts.
4. 20 Jun 2021 14:57:13 ERROR: DefineParameter() is not allowed in bot scripts.
3. 20 Jun 2021 14:57:13 ERROR: DefineCommand() is not allowed in bot scripts
What shall i do?
Save it as the correct type might help.
Its a command not a bot script. Select command in the dropdown when you open a new script.
6. 20 Jun 2021 21:19:45 ERROR: Command script is invalid and can not be used
5. 20 Jun 2021 21:19:45 ERROR: DefineParameter(): Attempting to call with 'null' value for defaultValue.
4. 20 Jun 2021 21:19:45 ERROR: CC_BotEngine_Settings(): Invalid input count of 31 (only takes 30).
3. 20 Jun 2021 21:19:45 ERROR: CC_BotEngine_Settings(): Invalid input count of 31 (only takes 30).
2. 20 Jun 2021 21:19:45 High speed updates enabled.
1. 20 Jun 2021 21:19:45 Doing compile test..
Updated the instructions and example