Smokybot MK4
stableDescription
Hi guys...thank you for everyone who use my previous bots and everyone who inspired me with their snippets and sharing their knowledge in DIscord.
So..version 4 now.
The basic trading risk management is the same as MK3. Still using balance risk to determine the order size with some additions/improvements.
Now let's jump to NEW features:
1. Easy replaceable indicators.
You can easily use any indicators as many as you like! By default it has 3 entries but you can add as many as you like. All you need to do is go to line 170 and do a little modification there (somehow i can't upload screenshots).
2. Auto adjustment for exchange limits.
To avoid order rejected because over size or above exchange's trading rule MK4 has entry where you have to (if you want to) enter the exchange limits. More about it in the Discord post.
If you enter the limits parameter (position limit and order limit) then bot will always check the risk based calculated size against it and make adjustments to make sure the size sent to exchange is within limit.
You will be notified in the log if this happen and then you can decide to lower the leverage to gain more limits or just let it run which makes it like ProfitHodl feature.
Remember, more risk more profit, less risk less profit.
3. Multiple TP Logic
Like MK3 you can use RR for TP. Risking $5 for potential getting $10 at TP means 2RR.
You can trail you profit but unlike MK3 the trailing now based on RR or TDST (from TD Sequential).
Another option is using price change % if you just want to scalp or have fixed % TP level.
That is the main thing in MK4. There are some small changes/updates/improvements which going to be too long to explain here (assuming i remember it).
Have fun with it and may the profits be with you!
More questions ? Find me in Haasonline Discord.
If you would like to buy me a cup of coffee or diamond:
ENS: smokyho.eth
HaasScript
--[[
MK4.2.2
If you would like to buy me a cup of coffee or diamond
ENS: smokyho.eth
--]]
EnableHighSpeedUpdates(true)
HideOrderSettings()
HideTradeAmountSettings()
--INPUTS
InputGroupHeader('Trade Settings')
local mainInterval = CurrentInterval()
local okLong = Input('Long Entry', false, 'Allow bot to open Long')
local okProfitL = Input('Long Exit', false, 'Allow bot to exit Long')
local okShort = Input('Short Entry', false, 'Allow bot to open Short')
local okProfitS = Input('Short Exit', false, 'Allow bot to exit Short')
local okReduce = Input('BalRatio Reduction', false, 'Allow Bal Ratio based size reduction')
local okDerisk = Input('Risk Reduction', false, 'Allow Signal based size reduction')
local oneWay = Input("Not Hedge", false, "Trading one way even if your position mode in exchange is Hedge Mode. If your position mode in exchange is One-Way the bot will automatically trading One-Way.")
local okCycle = Input('Position Cycle', false, 'If activated = 1 position per Indicator(easy1) cycle')
InputGroupHeader('Bot Settings')
local showDetails = Input("Show Details", false, "Show trading details in Log.")
local wtfStop = Input('Stop at no position', false, 'Deactivate bot when there is no open position.')
local FOL = Input('Force Open Long', false, 'Force open LONG position. Turn off position cycle when activated and set the settings back after position opened.')
local FEL = Input('Force Exit Long', false, 'Force exit LONG position.')
local FOS = Input('Force Open Short', false, 'Force open SHORT position. Turn off position cycle when activated and set the settings back after position opened.')
local FES = Input('Force Exit Short', false, 'Force exit SHORT position.')
InputGroupHeader('BACKTEST SETTINGS')
local wtfBal = Input('Deactivate on Over Budget', false, 'Disable bot when Balance Ratio hit trigger or Trading Balance is 5% from Starting Balance.')
local wtfSize = Input('Deactivate on Over Size', false, 'Disable bot when order size need adjustment.')
InputGroupHeader('Budget')
local startingBalance = Input('Starting Balance', 0, 'Starting trading balance which also maximum COST to limit working balance (margin cost + unrealised loss)')
local isolated = GetMarginMode() == IsolatedMarginMode
local leverage = GetLeverage()
local contVal = ContractValue()
local strictBudget = Input('Strict Budget', false, 'When applied bot will close all position and stop if highest bot balance - current bot balance > starting balance.')
InputGroupHeader("Exchange Order & Position Limits")
local positionLimitTypes = {base = AmountLabel(), quote = ProfitLabel()}
local positionLimitType = InputOptions("POSITION Limit Type", positionLimitTypes.quote, positionLimitTypes)
local positionLimitAmount = Input("POSITION Limit Amount", 10000)
local orderLimitTypes = {base = AmountLabel(), quote = ProfitLabel()}
local orderLimitType = InputOptions("ORDER Limit Type", orderLimitTypes.base, orderLimitTypes)
local orderLimitAmount = Input("ORDER Limit Amount", 10000000)
InputGroupHeader('Safety')
local slTrigger = Input('Fix SL %', 0, '0 is disable, else DISREGARDING RISK REDUCTION option and signal bot will derisking when % price change againts average entry.')
local reduceTrigger = Input('Bal Ratio Trigger', 0.8, 'Ratio between working balance and trading balance to trigger size reduction')
local profitHodl = Input('Profit Hodl %', 100, '% of the bot profit taken out or not compounded into trading balance calculation')
local dynamicHodl = Input('Dynamic Hodl', false, 'After in profit will only take out profit if current trading balance < 80% of highest trading balance.')
local delay = Input('SL Delay (Min)', 0, 'Stop the bot from opening new long/short order for X minutes after close at loss.')
InputGroupHeader('Order')
local posRisk = Input('Risk % per POSITION', 1, '10 = risk losing ~10% of trading balance PER POSITION if SL hit from current order price.')
local slotSizeD = Input('Min. Order Size ', 0, 'Minimum order size if Dynamic Slot Size activated. Static amount if Dynamic Slot Size disabled.')
local autoSlot = Input('Dynamic Order Size', true, 'Dynamically change order size. Calculated based on distance to SL price.')
InputGroupHeader('Profit')
local RR = Input('Target Reward to Risk', 1, '2 = reward 2x risk. At this level bot will close position.')
local trailingTDST = Input('Trailing TDST', false, 'Moving SL following TDST.')
local RRtrailing = Input('RR Trailing', 0, 'RR to start trailing SL. 0 is disable. Only working if Trailing TDST is off. EXAMPLE: If 2 = at 2RR SL move to Entry, at 4RR SL move to 2RR from entry and so on.')
local sizeHalving = Input('Size Halving', false, 'Halving the position and its remaining size at trailing event. With Trailing TDST only when TDST crossing entry. With RR Trailing at every trailing point.')
local okScalping = Input('Scalping', false, 'Take profit minimum at % below as an alternative to RR method.')
local minScalping = Input('Min. scalping profit %', 0.1, 'TP when profit (price change) above this value.')
if startingBalance == 0 then
DeactivateBot('Please enter Starting Balance', true)
end
if GetPositionMode() == OneWayPositionMode then
oneWay = true
end
---------------------------------------------------------------------------------
-- LABS STUFF
local in_labs = Input('In Labs?', false, '', 'BACKTEST SETTINGS')
local market = PriceMarket()
local markets = CC_All_BF_Markets()
local selected = InputOptions('Market', markets[1], markets, '', 'BACKTEST SETTINGS')
if in_labs then
market = CreateMarket({baseCurrency = selected})
priceSource = market
labPlot = 99
PlotPrice(labPlot, market)
ChartSetOptions(0, '', 0.002)
ChartSetOptions(labPlot, selected, 0.4)
if not init_lev then
local max_lev = GetMaxLeverage(market)
SetLeverage(max_lev, market)
init_lev = true
LogWarning('--- MAX LEVERAGE SET: '..max_lev..' ---')
end
else
labPlot = 0
--ChartSetOptions(0, 'Market')
priceSource = InputPriceSourceMarket("Indicator Price Source", market, {group = 'Trade Settings'})
end
---------------------
--DATA
local getAccount = AccountGuid()
local report = GetTradingReport()
local highestP = report.highestPointInProfit
local lowestP = report.lowestPointInProfit
local highestBal = startingBalance + highestP
local lowestBal = startingBalance + lowestP
---------------------
-- PERSISTENT STORAGE
if not inited then
bot = Load('bot', {})
if Count(bot) == 0 then
-- positions
bot.longPosId = NewGuid()
bot.shortPosId = NewGuid()
bot.timerL = Time()
bot.timerS = Time()
-- prices
bot.targetPriceL = 0
bot.targetPriceS = 0
bot.orderDistanceL = 0
bot.orderDistanceS = 0
bot.longCycle = 1
bot.shortCycle = 1
bot.longHalving = 0
bot.shortHalving = 0
-- TD
bot.bullCount = 0
bot.bearCount = 0
bot.longTDST = 0
bot.longSL = l
bot.shortTDST = 0
bot.shortSL = h
bot.bullish = false
bot.bearish = false
-- stat
bot.SRCounter = 0
bot.BRCounter = 0
bot.longFilled = 0
bot.shortFilled = 0
bot.longTrail = 0
bot.shortTrail = 0
bot.TP_rr = 0
bot.TP_trailing = 0
bot.TP_scalping = 0
bot.orderCountL = 1
bot.orderCountS = 1
end
inited = true
end
---------------------
-- PRICES & INDICATORS
local cp = CurrentPrice(priceSource)
local cpDefault = CurrentPrice(market)
-- Function for adjusting position limits in hedge mode
local function adjustPositionLimits()
if GetPositionMode() == HedgePositionMode then
positionLimitAmount = positionLimitAmount / 2
end
end
-- Function for calculating dynamic percentage
local function calculateDynamicPercentage(h, l, c)
local tr = TRANGE(h, l, c)
return Max(tr, DEMA(tr, 7), SMA(tr, 7), TRIMA(tr, 7)) / c * 100
end
-- Function for calculating skew
local function calculateSkew(c)
local rsi = RSI(c, 14)
return Max(Abs(50 - rsi) / 50, DEMA(Abs(50 - rsi) / 50, 20))
end
-- Combined function for calculating Indicators
InputGroupHeader('Indicators')
local atrTDM = Input("SL ATR Multiplier", 1.5)
local value1 = Input("Custom Value 1", 0, "Custom value for indicator's parameter.")
local value2 = Input("Custom Value 2", 0, "Custom value for indicator's parameter.")
local value3 = Input("Custom Value 3", 0, "Custom value for indicator's parameter.")
-- Add mode if needed
local consensus = Input("Consensus Signal Decision", false, "Activate to make majority decides else the decicion has to be unanimous.")
local function calculateIndicators(h, l, c)
----------------------------------------------------------------------
--You can change this to any EASY Indicators or Custom Indicators as long as the Return is SIGNAL
--https://help.haasonline.com/api/haasscript/commands/easy-indicators
--If you are using Custom Indicator then you can use the Custome Value for parameters
local easy1 = CC_EasySuperTrend(0,'', value1)
local easy2 = EasyMA(0) --example usage of custom value
local easy3 = easy1
if consensus then
easy = GetConsensusSignal(easy1, easy2, easy3)
else
easy = GetUnanimousSignal(easy1, easy2, easy3)
end
--Log(easy)
----------------------------------------------------------------------
local atr = ATR(h, l, c, 9)
return easy1, easy, atr
end
-- Function for updating TD Sequential Count
local function updateTDSequential(c, h, l, bot, offset)
if offset == nil then
offset = 1
end
bullCount = c[offset] > c[offset + 4]
bearCount = c[offset] < c[offset + 4]
if bot.bullCount == 0 and bot.bearCount == 0 then
if bullCount and c[offset + 1] < c[offset + 5] then
bot.bullCount = 1
elseif bearCount and c[offset + 1] > c[offset + 5] then
bot.bearCount = 1
end
end
if bot.bullCount >= 1 then
if bullCount then
bot.bullCount = bot.bullCount + 1
elseif bearCount then
bot.bullCount = 0
bot.bearCount = 1
end
elseif bot.bearCount >= 1 then
if bearCount then
bot.bearCount = bot.bearCount + 1
elseif bullCount then
bot.bullCount = 1
bot.bearCount = 0
end
end
return bot
end
-- Function for plotting shapes based on TD Sequential
local function plotTDSequentialShapes(c, h, l, bot, offset)
if offset == nil then
offset = 1
end
if bot.bullCount == 1 then
PlotShape(0, ShapeText, Green(50), 1, true, bot.bullCount, Green(50))
elseif bot.bearCount == 1 or bot.bearCount == 9 then
PlotShape(0, ShapeText, Red(50), 1, false, bot.bearCount, Red(50))
end
if bot.bullCount == 9 then
bot.longTDST = GetLow(l, 9, offset)
if h[offset] > h[offset + 2] and h[offset] > h[offset + 3] then
PlotShape(0, ShapeTriangleDown, Green(50), 3, true, bot.bullCount, Green(50))
else
PlotShape(0, ShapeText, Green(50), 1, true, bot.bullCount, Green(50))
end
elseif bot.bearCount == 9 then
bot.shortTDST = GetHigh(h, 9, offset)
if l[offset] < l[offset + 2] and l[offset] < l[offset + 3] then
PlotShape(0, ShapeTriangleUp, Red(50), 3, false, bot.bearCount, Red(50))
else
PlotShape(0, ShapeText, Red(50), 1, false, bot.bearCount, Red(50))
end
end
end
-- Function for calculating and checking signals
local function checkSignals(bot)
local above = l > bot.longTDST and easy == SignalLong
local below = h < bot.shortTDST and easy == SignalShort
if (above and bot.bearCount == 0 and bot.bullCount < 9) or FOL then
bot.bullish = true
else
bot.bullish = false
end
if (below and bot.bullCount == 0 and bot.bearCount < 9) or FOS then
bot.bearish = true
else
bot.bearish = false
end
return bot
end
-- Function for plotting indicators
local function plotIndicators(bot)
if bot.longTDST > 0 then
Plot(labPlot, 'Long TDST', bot.longTDST, {c = Green, d = Dotted})
end
if bot.shortTDST > 0 then
Plot(labPlot, 'Short TDST', bot.shortTDST, {c = Red, d = Dotted})
end
end
-- Main execution function
OptimizedForInterval(0, function()
h = HighPrices(mainInterval, true, priceSource)
l = LowPrices(mainInterval, true, priceSource)
c = ClosePrices(mainInterval, true, priceSource)
adjustPositionLimits()
diff5 = calculateDynamicPercentage(h, l, c)
skew = calculateSkew(c)
easy1, easy, atr = calculateIndicators(h, l, c, value1, value3, value3)
-- Warmup
if Load('warmup') == nil then
LogWarning('Warming up TD Sequential...')
local wm_len = ArrayGet(Min(500, #c, #atr), 1)
for i=wm_len, 1, -1 do
bot = updateTDSequential(c, h, l, bot, i)
plotTDSequentialShapes(c, h, l, bot, i)
end
LogWarning('Warmup completed.')
Save('warmup', false)
else
-- only update current step
bot = updateTDSequential(c, h, l, bot)
plotTDSequentialShapes(c, h, l, bot)
end
-----
bot = checkSignals(bot)
plotIndicators(bot)
end)
---------------------
-- POSITIONS
local dir_l = GetPositionDirection(bot.longPosId)
local aep_l = GetPositionEnterPrice(bot.longPosId)
local pamt_l = GetPositionAmount(bot.longPosId)
local delta_l = pamt_l > 0 and (cpDefault.close - aep_l) / aep_l * 100 or 0
local dir_s = GetPositionDirection(bot.shortPosId)
local aep_s = GetPositionEnterPrice(bot.shortPosId)
local pamt_s = GetPositionAmount(bot.shortPosId)
local delta_s = pamt_s > 0 and (aep_s - cpDefault.close) / aep_s * 100 or 0
-- manage position ids
if pamt_l == 0 and IsPositionClosed(bot.longPosId) then
if IsAnyOrderOpen(bot.longPosId) then
CancelAllOrders(bot.longPosId)
else
if okCycle then
bot.longCycle = 1
end
if LastLongProfit(bot.longPosId) > 0 then
bot.TP_trailing = bot.TP_trailing + 1
end
bot.longPosId = NewGuid()
dir_l = GetPositionDirection(bot.longPosId)
aep_l = GetPositionEnterPrice(bot.longPosId)
pamt_l = GetPositionAmount(bot.longPosId)
bot.longTrail = 0
bot.longFilled = 0
bot.longHalving = 0
bot.targetPriceL = 0
bot.orderCountL = 1
end
end
if pamt_s == 0 and IsPositionClosed(bot.shortPosId) then
if IsAnyOrderOpen(bot.shortPosId) then
CancelAllOrders(bot.shortPosId)
else
if okCycle then
bot.shortCycle = 1
end
if LastShortProfit(bot.shortPosId) > 0 then
bot.TP_trailing = bot.TP_trailing + 1
end
bot.shortPosId = NewGuid()
dir_s = GetPositionDirection(bot.shortPosId)
aep_s = GetPositionEnterPrice(bot.shortPosId)
pamt_s = GetPositionAmount(bot.shortPosId)
bot.shortTrail = 0
bot.shortFilled = 0
bot.shortHalving = 0
bot.targetPriceS = 0
bot.orderCountS = 1
end
end
-- get pos id
local getPositionId = function(isLong)
return isLong and bot.longPosId or bot.shortPosId
end
---------------------
-- WALLET CHECK
local profitLabel = ProfitLabel(market)
if profitLabel == nil then profitLabel = QuoteCurrency(market) end
-- inverse or not
if profitLabel == 'USD' or profitLabel == 'USDT' or profitLabel == 'BUSD' or profitLabel == 'USDC' or profitLabel == 'TUSD' then
isInverse = false
else
isInverse = true
end
-- check balance usage
local usedLong = UsedMargin(market, aep_l, pamt_l, leverage)
local getProfitL = GetCurrentProfit(PositionLong, market)
local usedShort = UsedMargin(market, aep_s, pamt_s, leverage)
local getProfitS = GetCurrentProfit(PositionShort, market)
local getProfit = getProfitL + getProfitS
local botProfit = GetBotProfit(market, false)
local netbotProfit = GetBotProfit(market, true)
local walletBal = WalletAmount(getAccount, profitLabel, market)
local workBal = usedLong + usedShort - getProfit
local botBalance = startingBalance + botProfit
local netBalance = startingBalance + netbotProfit - usedLong - usedShort
if dynamicHodl and botBalance >= highestBal then
tradeBal = AddPerc(startingBalance, SubPerc(botProfit, profitHodl) / startingBalance * 100)
--PlotSignalBar(-99, White)
else
tradeBal = IfElse(profitHodl > 0 and botProfit > 0, startingBalance + SubPerc(botProfit, profitHodl),
botBalance)
end
ChartSetOptions(5, 'Balance Monitor')
Plot(5, 'Net Balance', netBalance, {c=Orange, s=Step})
Plot(5, 'Trading Balance', tradeBal, {c=White, s=Step})
Plot(5, 'WorkBal', workBal, {c=Red, s=Step})
Plot(5, 'Bot Profit', botProfit, {c=DarkGreen, s=Step})
Plot(5, 'Bot Balance', botBalance, {c=Yellow, s=Step})
Plot(5, 'Highest Balance', highestBal, {c=Green, s=Step})
Plot(5, 'Lowest Balance', lowestBal, {c=Fuchsia, s=Step})--]]
---------------------
-- BALANCE WARNING
local balRatio = workBal / tradeBal
local okBalance = walletBal + workBal >= botBalance
if not okBalance then
DeactivateBot('TRADING BALANCE < EXCHANGE BALANCE', true)
end
if tradeBal <= 0 then
DeactivateBot('OUT of TRADING BALANCE', true)
end
if balRatio > 0.8 then
LogWarning('Working balance is > 80% of Budget!!!')
end
if balRatio > bot.BRCounter then
bot.BRCounter = balRatio
end
if strictBudget and highestBal - botBalance >= startingBalance then
okLong = false
FEL = true
okShort = false
FES = true
if pamt_l == 0 and pamt_s == 0 then
DeactivateBot('LOSS < STARTING BALANCE', true)
end
end
---------------------
-- WTF
if wtfBal then
if balRatio > reduceTrigger or botBalance <= startingBalance * 0.05 then
DeactivateBot('Deactivated because over budget or bot balance is 5% left.', true)
Log('STOP at over budget safety activated. Is this a backtest ?', Yellow)
end
end
if wtfStop then
if pamt_l == 0 and pamt_s == 0 then
DeactivateBot('Deactivated by No Position', true)
end
Log('Deactivate on No Position is active.', Yellow)
end
---------------------
-- DYNAMICS
-- ENTRY
local longPrice = cpDefault.bid
local shortPrice = cpDefault.ask
if okCycle then
if pamt_l == 0 and bot.longCycle == 1 and easy1 == SignalShort then
bot.longCycle = 0
end
if pamt_s == 0 and bot.shortCycle == 1 and easy1 == SignalLong then
bot.shortCycle = 0
end
end
-- SL
-- Calculate SL
OptimizedForInterval(0, function()
if pamt_l == 0 then
if slTrigger == 0 then
if longPrice < bot.shortTDST then
longRisk = bot.longTDST - (atr * atrTDM)
else
longRisk = GetLow(l, 9) - (atr * atrTDM)
end
else
longRisk = SubPerc(longPrice, slTrigger)
end
longSLP = longRisk
longRisk_range = pamt_l == 0 and longPrice - longRisk or aep_l - longRisk
longSL_range = longPrice - longRisk
longTrailPrice = 0
end
if pamt_s == 0 then
if slTrigger == 0 then
if shortPrice > bot.longTDST then
shortRisk = bot.shortTDST + (atr * atrTDM)
else
shortRisk = GetHigh(h, 9) + (atr * atrTDM)
end
else
shortRisk = AddPerc(shortPrice, slTrigger)
end
shortSLP = shortRisk
shortRisk_range = pamt_s == 0 and shortRisk - shortPrice or shortRisk - aep_s
shortSL_range = shortRisk - shortPrice
shortTrailPrice = 0
end
end)
-- Check SL
-- Function to adjust long position SL
local function adjustLongSL(bot, cp, pamt_l, aep_l, longRisk_range, atr, atrTDM, trailingTDST, RRtrailing)
if pamt_l > 0 then
longSLP = longRisk
longTrailPrice = bot.longTrail >= 1 and aep_l + (longRisk_range * bot.longTrail) or aep_l
local LT = Max(bot.longTrail, bot.longHalving)
if trailingTDST and bot.longTDST > aep_l then
if sizeHalving and bot.longHalving == 0 then
PlaceExitLongOrder(cpDefault.ask, pamt_l / 2, {note = 'TDST Halving'})
bot.longHalving = 1
end
longSLP = bot.longTDST - (atr * atrTDM)
elseif RRtrailing > 0 and LT >= RRtrailing then
longSLP = longTrailPrice - (RRtrailing * longRisk_range)
end
end
end
-- Function to adjust short position SL
local function adjustShortSL(bot, cp, pamt_s, aep_s, shortRisk_range, atr, atrTDM, trailingTDST, RRtrailing)
if pamt_s > 0 then
shortSLP = shortRisk
shortTrailPrice = bot.shortTrail >= 1 and aep_s - (shortRisk_range * bot.shortTrail) or aep_s
local ST = Max(bot.shortTrail, bot.shortHalving)
if trailingTDST and bot.shortTDST < aep_s then
if sizeHalving and bot.shortHalving == 0 then
PlaceExitShortOrder(cpDefault.bid, pamt_s / 2, {note = 'TDST Halving'})
bot.shortHalving = 1
end
shortSLP = bot.shortTDST + (atr * atrTDM)
elseif RRtrailing > 0 and ST >= RRtrailing then
shortSLP = shortTrailPrice + (RRtrailing * shortRisk_range)
end
end
end
adjustLongSL(bot, cp, pamt_l, aep_l, longRisk_range, atr, atrTDM, trailingTDST, RRtrailing)
adjustShortSL(bot, cp, pamt_s, aep_s, shortRisk_range, atr, atrTDM, trailingTDST, RRtrailing)
-- TP RR
-- LONG
if pamt_l != 0 and bot.longTrail == 0 and bot.targetPriceL == 0 then
bot.targetPriceL = aep_l + (longRisk_range * RR)
end
-- SHORT
if pamt_s != 0 and bot.shortTrail == 0 and bot.targetPriceS == 0 then
bot.targetPriceS = aep_s - (shortRisk_range * RR)
end--]]
-- the force
if FEL then
okLong = false
okProfitL = false
exitPriceL = cpDefault.ask
LET = 'Forced'
Log('FORCE EXIT LONG POSITION ACTIVATED', Red)
end
if FES then
okShort = false
okProfitS = false
exitPriceS = cpDefault.bid
SET = 'Forced'
Log('FORCE EXIT SHORT POSITION ACTIVATED', Red)
end
--TRAILING
--LONG
local longRRT = pamt_l > 0 and cp.close > aep_l + (longRisk_range * (bot.longTrail + 1))
if longRRT then
bot.longTrail = bot.longTrail + 1
end
--SHORT
local shortRRT = pamt_s > 0 and cp.close < aep_s - (shortRisk_range * (bot.shortTrail + 1))
if shortRRT then
bot.shortTrail = bot.shortTrail + 1
end
---------------------
-- SLOT SIZE
if isolated and not oneWay then
tradeBal = tradeBal / 2
end
local risk = posRisk / 100 * tradeBal
local slotSizeL = slotSizeD
local slotSizeS = slotSizeD
if autoSlot then
if bot.bullish then
local sizeL = isInverse and risk * longRisk / longSL_range * longPrice / contVal
or risk / longSL_range
slotSizeL = sizeL > slotSizeD and sizeL or slotSizeD
end
if bot.bearish then
local sizeS = isInverse and risk * shortRisk / shortSL_range * shortPrice / contVal
or risk / shortSL_range
slotSizeS = sizeS > slotSizeD and sizeS or slotSizeD
end
else
slotSizeL = slotSizeD
slotSizeS = slotSizeD
end
--Log("slotSizeL "..slotSizeL)
--Log("slotSizeS "..slotSizeS)
-- LIMIT CALCULATION
adjustedPositionL = false
adjustedPositionS = false
if positionLimitAmount > 0 and orderLimitAmount > 0 then
-- Function to set POSITION LIMITS
if positionLimitType == positionLimitTypes.quote then
positionLimitAmount = isInverse and positionLimitAmount * cp.close / contVal or positionLimitAmount / cp.close
end
-- Function to set ORDER LIMITS
local function setOrderLimits(orderLimitAmount, orderLimitType, longPrice, shortPrice, contVal, isInverse)
local order_limitAmountL, order_limitAmountS
if orderLimitType == orderLimitTypes.quote then
order_limitAmountL = isInverse and orderLimitAmount * longPrice / contVal or orderLimitAmount / longPrice
order_limitAmountS = isInverse and orderLimitAmount * shortPrice / contVal or orderLimitAmount / shortPrice
else
order_limitAmountL = orderLimitAmount
order_limitAmountS = orderLimitAmount
end
return order_limitAmountL, order_limitAmountS
end
-- Function to adjust long position
local function adjustLongPosition(bot, slotSizeL, positionLimitAmountL, order_limitAmountL)
local slotSizeLADJ, orderCountL, adjustedPositionL
if bot.bullish and pamt_l == 0 then
if slotSizeL > positionLimitAmountL then
totalSizeL = positionLimitAmountL * 0.95
adjustedPositionL = true
else
totalSizeL = slotSizeL
end
if totalSizeL > order_limitAmountL then
bot.orderCountL = Round(totalSizeL / order_limitAmountL, 0) + 1
splitSizeL = totalSizeL / bot.orderCountL
adjustedPositionL = true
else
splitSizeL = totalSizeL
end
return splitSizeL, bot.orderCountL, adjustedPositionL
elseif bot.orderCountL > 1 then
return splitSizeL, bot.orderCountL, true
end
end
-- Function to adjust short position
local function adjustShortPosition(bot, slotSizeS, positionLimitAmountS, order_limitAmountS)
local slotSizeSADJ, orderCountS, adjustedPositionS
if bot.bearish and pamt_s == 0 then
if slotSizeS > positionLimitAmountS then
totalSizeS = positionLimitAmountS * 0.95
adjustedPositionS = true
else
totalSizeS = slotSizeS
end
if totalSizeS > order_limitAmountS then
bot.orderCountS = Round(totalSizeS / order_limitAmountS, 0) + 1
splitSizeS = totalSizeS / bot.orderCountS
adjustedPositionS = true
else
splitSizeS = totalSizeS
end
return splitSizeS, bot.orderCountS, adjustedPositionS
elseif bot.orderCountS > 1 then
return splitSizeS, bot.orderCountS, true
end
end
-- Main Execution
local order_limitAmountL, order_limitAmountS = setOrderLimits(orderLimitAmount, orderLimitType, longPrice, shortPrice, contVal, isInverse)
slotSizeLADJ, orderCountL, adjustedPositionL = adjustLongPosition(bot, slotSizeL, positionLimitAmount, order_limitAmountL)
slotSizeSADJ, orderCountS, adjustedPositionS = adjustShortPosition(bot, slotSizeS, positionLimitAmount, order_limitAmountS)
slotSizeL = slotSizeLADJ != nil and slotSizeLADJ or slotSizeL
slotSizeS = slotSizeSADJ != nil and slotSizeSADJ or slotSizeS
end
-- Cost Check
local potentialCostL = isInverse and slotSizeL * contVal / longPrice / leverage or slotSizeL * longPrice / leverage
local maxUsedL = bot.bullish and longRisk_range > 0 and potentialCostL + risk or 0
local potentialCostS = isInverse and slotSizeS * contVal / shortPrice / leverage or slotSizeS * shortPrice / leverage
local maxUsedS = bot.bearish and shortRisk_range > 0 and potentialCostS + risk or 0
--Log('maxUsedL '..maxUsedL)
--Log('maxUsedS '..maxUsedS)
local okCostL = maxUsedL > 0 and maxUsedL < tradeBal * 0.8 or false
local okCostS = maxUsedS > 0 and maxUsedS < tradeBal * 0.8 or false
---------------------
-- EXECUTION
--LONG
--OPEN
local longOpen1 = pamt_l == 0
and bot.bullish
and l > longRisk
and okBalance
and okCostL
local longDCA = pamt_l != 0
and bot.orderCountL > 1
if longOpen1 then
LT = FOL and 'ForceLong' or '1stOrder'
elseif longDCA then
LT = 'Splits'
end
local longOpen = okLong
and bot.longFilled < bot.orderCountL
and longSLP != 0
and IfElse(oneWay, pamt_s == 0, true)
and IfElse(okCycle, bot.longCycle == 0, true)
and (longOpen1 or longDCA)
if pamt_l > 0 then
--EXIT
local longExit1 = cp.close > bot.targetPriceL
local longExit2 = okScalping
and delta_l > Max(minScalping, diff5) + skew
if longExit1 then
LET = 'RR'
elseif longExit2 then
LET = 'Scalping'
end
longExit = okProfitL
and pamt_l > 0
and not FEL
and delta_l > 0.1
and (longExit1 or longExit2)
--DERISK
local okLongDerisk1 = IfElse(delta_l < 0, cp.close <= Average(aep_l, longSLP),
cp.close <= Average(longTrailPrice, longSLP))
local okLongDerisk2 = RRtrailing > 0
and sizeHalving
and delta_l > 0
and cp.close < bot.targetPriceL
and not okLongDerisk1
if okLongDerisk1 then
LDP = longSLP
LDT = 'Stop'
elseif okLongDerisk2 then
LDPc = aep_l + (RRtrailing * longRisk_range * (bot.longHalving + 1))
LDP = IfElse(cp.close < LDPc, LDPc, cpDefault.ask)
LDT = 'Halving'
else
LDP = longSLP
end
okLongDerisk = okDerisk
and pamt_l > 0
and not longExit
and (okLongDerisk1 or okLongDerisk2)
else
longExit = false
okLongDerisk = false
end
--SHORT
--OPEN
local shortOpen1 = pamt_s == 0
and bot.bearish
and h < shortRisk
and okBalance
and okCostS
local shortDCA = pamt_s != 0
and bot.orderCountS > 1
if shortOpen1 then
ST = FOS and 'ForceShort' or '1stOrder'
elseif shortDCA then
ST = 'Splits'
end
local shortOpen = okShort
and bot.shortFilled < bot.orderCountS
and shortSLP != 0
and IfElse(oneWay, pamt_l == 0, true)
and IfElse(okCycle, bot.shortCycle == 0, true)
and (shortOpen1 or shortDCA)
if pamt_s > 0 then
--EXIT
local shortExit1 = cp.close < bot.targetPriceS
local shortExit2 = okScalping
and delta_s > Max(minScalping, diff5) + skew
if shortExit1 then
SET = 'RR'
elseif shortExit2 then
SET = 'Scalping'
end
shortExit = okProfitS
and pamt_s > 0
and not FES
and delta_s > 0.1
and (shortExit1 or shortExit2)
--DERISK
local okShortDerisk1 = IfElse(delta_s < 0, cp.close >= Average(aep_s, shortSLP),
cp.close >= Average(shortTrailPrice, shortSLP))
local okShortDerisk2 = RRtrailing > 0
and sizeHalving
and delta_s > 0
and cp.close > bot.targetPriceS
and not okShortDerisk1
if okShortDerisk1 then
SDP = shortSLP
SDT = 'Stop'
elseif okShortDerisk2 then
SDPc = aep_s - (RRtrailing * shortRisk_range * (bot.shortHalving + 1))
SDP = IfElse(cp.close > SDPc, SDPc, cpDefault.bid)
SDT = 'Halving'
else
SDP = shortSLP
end
okShortDerisk = okDerisk
and pamt_s > 0
and not shortExit
and (okShortDerisk1 or okShortDerisk2)
else
shortExit = false
okShortDerisk = false
end
---------------------
-- FUNCTIONS
-- ENTRY
function slot (isLong, price, amount, timer, trigger, canPlace)
local name = isLong and 'L' or 'S'
local cmd = isLong and PlaceGoLongOrder or PlaceGoShortOrder
local posId = getPositionId(isLong)
local filled = isLong and bot.longFilled + 1 or bot.shortFilled + 1
local oid = Load(name..'oid', '') -- order id
if oid != '' then
local order = OrderContainer(oid)
if order.isOpen then
if not canPlace then
CancelOrder(oid)
LogWarning('Not allowed right now '..name)
end
if MinutesTillCandleClose(mainInterval) == 0 then
CancelAllOrders()
Log("Canceled by new candle", Yellow)
end
elseif order.isFilled then
CancelAllOrders()
if isLong then
bot.longFilled = bot.longFilled + 1
Log('Target TP: '..bot.targetPriceL)
Log("SL: "..longSLP)
else
bot.shortFilled = bot.shortFilled + 1
Log('Target TP '..bot.targetPriceS)
Log("SL: "..shortSLP)
end
oid = ''
else
oid = ''
end
else
if canPlace and TradeOncePerBar(1, posId) and Time() >= timer then
oid = cmd(price, amount, {market = market, timeout = mainInterval * 60, type = LimitOrderType, note = name..filled..'-'..trigger, positionId = posId})
if isLong then
if pamt_l == 0 then
Save('longRisk', longRisk)
end
if adjustedPositionL then
Log("Long order size adjusted to exchange limits.", Gold)
ChartSetOptions(-2, "Order Size Adjustment")
PlotSignalBar(-2, Gold)
if wtfSize and adjustedPositionL then
DeactivateBot("Over Size", true)
end
end
else
if pamt_s == 0 then
Save('shortRisk', shortRisk)
end
if adjustedPositionS then
Log("Short order size adjusted to exchange limits.", Gold)
ChartSetOptions(-2, "Order Size Adjustment")
PlotSignalBar(-2, Gold)
if wtfSize and adjustedPositionS then
DeactivateBot("Over Size", true)
end
end
end
end
end
Save(name..'oid', oid)
end
-- EXIT
function updateTakeProfit (isLong, ET, canExit)
local prefix = isLong and 'L' or 'S'
local name = prefix .. ' TP'
local oid = Load(prefix .. 'tp_oid', '')
local posId = getPositionId(isLong)
local amount = isLong and pamt_l or pamt_s
local price = isLong and cp.ask or cp.bid
local cmd = isLong and PlaceExitLongOrder or PlaceExitShortOrder
if orderLimitAmount > 0 and amount > orderLimitAmount then
amount = orderLimitAmount
end
if oid != '' then
local order = OrderContainer(oid)
if order.isOpen then
if not canExit or MinutesTillCandleClose(mainInterval) == 0 then
CancelOrder(oid)
LogWarning('Exit cancelled '..name)
end
elseif order.isFilled then
if ET == 'RR' then
bot.TP_rr = bot.TP_rr + 1
elseif ET == 'Scalping' then
bot.TP_scalping = bot.TP_scalping + 1
end
oid = ''
else
oid = ''
end
else
if canExit then
local pDelta = isLong and Round(delta_l, 2) or Round(delta_s, 2)
oid = cmd(price, amount, {market = market, timeout = mainInterval * 60, type = LimitOrderType, note = name..'-'..ET..' '..pDelta..'%', positionId = posId})
end
end
Save(prefix .. 'tp_oid', oid)
end
-- Balance Ratio REDUCTION
function updatePositionManagement (isLong, amount, canReduce)
local price = isLong and cpDefault.bid - PriceStep() or cpDefault.ask + PriceStep()
local prefix = isLong and 'Long' or 'Short'
local name = prefix .. ' Size Reduction'
local oid = Load(prefix .. 'pos_oid', '')
local posId = getPositionId(isLong)
local cmd = isLong and PlaceExitLongOrder or PlaceExitShortOrder
local timer = Load(prefix .. 'pos_timer', Time())
if orderLimitAmount > 0 and amount > orderLimitAmount then
amount = orderLimitAmount
end
if oid != '' then
local order = OrderContainer(oid)
if order.isOpen then
if not canReduce or MinutesTillCandleClose(mainInterval) == 0 then
CancelOrder(oid)
LogWarning('Reduction cancelled '..name)
end
else
bot.SRCounter = bot.SRCounter + 1
oid = ''
end
else
if canReduce and Time() >= timer then
CancelAllOrders()
local pDelta = isLong and Round(delta_l, 2) or Round(delta_s, 2)
oid = cmd(price, amount, {market = market, type = MarketOrderType, note = name..' '..pDelta..'%', timeout = mainInterval * 60, positionId = posId})
end
end
Save(prefix .. 'pos_oid', oid)
Save(prefix .. 'pos_timer', timer)
end
-- Derisking REDUCTION
function derisking(isLong, size, price, DT, canDerisk)
local openDT = Load('openDT', DT)
local prefix = isLong and 'Long ' or 'Short '
local halvingCount = isLong and bot.longHalving + 1 or bot.shortHalving + 1
local name = prefix..' Derisking'
local oid = Load(prefix .. 'derisk_oid', '')
local posId = getPositionId(isLong)
local cmd = isLong
and PlaceExitLongOrder
or PlaceExitShortOrder
local trigger = isLong and longSLP or shortSLP
-- order check
if DT == 'Halving' then
deriskOrderType = LimitOrderType
name = prefix..' '..DT..'-'..halvingCount
amount = size / 2
if amount < MinimumTradeAmount(market, price) then
amount = size
end
else
if isLong then
deriskOrderType = IfElse(cp.close > longSLP, StopMarketOrderType, MarketOrderType)
else
deriskOrderType = IfElse(cp.close < shortSLP, StopMarketOrderType, MarketOrderType)
end
amount = size
end
if orderLimitAmount > 0 and amount > orderLimitAmount then
amount = orderLimitAmount
end
-- placing order
if oid != '' then
local order = OrderContainer(oid)
if order.isOpen then
if not canDerisk or MinutesTillCandleClose(mainInterval) == 0 then
CancelOrder(oid)
LogWarning('Reduction cancelled '..name)
end
if openDT != DT then
CancelOrder(oid)
end
elseif order.isFilled then
if isLong then
if LDT == 'Halving' and sizeHalving then
bot.longHalving = bot.longHalving + 1
end
bot.timerL = Time() + (delay * 60)
else
if SDT == 'Halving' and sizeHalving then
bot.shortHalving = bot.shortHalving + 1
end
bot.timerS = Time() + (delay * 60)
end
oid = ''
else
oid = ''
end
else
if canDerisk then
CancelAllOrders()
local pDelta = isLong and Round(delta_l, 2) or Round(delta_s, 2)
oid = cmd(price, amount, {market = market, triggerPrice = trigger, type = deriskOrderType, note = name..'-'..DT..' '..pDelta..'%', timeout = -1, positionId = posId})
Save('openDT', DT)
end
end
Save(prefix .. 'derisk_oid', oid)
end
--CORE LOGIC HEDGE MODE
slot(true, longPrice, slotSizeL, bot.timerL, LT, longOpen or FOL) -- long slot
slot(false, shortPrice, slotSizeS, bot.timerS, ST, shortOpen or FOS) -- short slot
if pamt_l > 0 then
updateTakeProfit(true, LET, longExit or FEL)
updatePositionManagement(true, pamt_l, okReduce and pamt_l > 0 and balRatio > reduceTrigger and delta_l < delta_s)
derisking(true, pamt_l, LDP, LDT, okLongDerisk)
end
if pamt_s > 0 then
updateTakeProfit(false, SET, shortExit or FES)
updatePositionManagement(false, pamt_s, okReduce and pamt_s > 0 and balRatio > reduceTrigger and delta_s < delta_l)
derisking(false, pamt_s, SDP, SDT, okShortDerisk)
end
---------------------
--PLOT
--AEP Plot
if aep_l > 0 then
local posId = getPositionId(true)
Plot(labPlot, 'AvgEP Long', aep_l, {c=Green, id=posId, w=2})
Plot(labPlot, 'SL Long', longSLP, {c=Green, id=posId, w=1})
if bot.longTrail >= 1 then
Plot(labPlot, bot.longTrail.." RR Long", longTrailPrice, {c=Cyan, id=posId, w=1})
end
end
if aep_s > 0 then
local posId = getPositionId(false)
Plot(labPlot, 'AvgEP Short', aep_s, {c=Red, id=posId, w=2})
Plot(labPlot, 'SL Short', shortSLP, {c=Red, id=posId, w=1})
if bot.shortTrail >= 1 then
Plot(labPlot, bot.shortTrail.." RR Short", shortTrailPrice, {c=Yellow, id=posId, w=1})
end
end
---------------------
-- FINAL REPORT
Finalize(function()
CustomReport('BalRatio Reduction', bot.SRCounter..' times')
CustomReport('TP by RR', bot.TP_rr..' times')
CustomReport('TP by Trailing', bot.TP_trailing..' times')
CustomReport('TP by Scalping', bot.TP_scalping..' times')
CustomReport('Final Bot Net Balance', Round(netBalance, 5)..' '..profitLabel)
CustomReport('Final Bot Balance', Round(botBalance, 5)..' '..profitLabel)
CustomReport('Highest Balance', Round(highestBal, 5)..' '..profitLabel)
CustomReport('Lowest Balance', Round(lowestBal, 5)..' '..profitLabel)
CustomReport('Current Balance Ratio', Round(balRatio, 2))
CustomReport('Highest Balance Ratio', Round(bot.BRCounter, 2))
local realprofitPercent = Round(botProfit / startingBalance * 100, 2)
local runprofitPercent = Round(netbotProfit / startingBalance * 100, 2)
CustomReport('Running Profit %: ', runprofitPercent..'%')
CustomReport('Realized Profit %: ', realprofitPercent..'%')
CustomReport('Profit to Highest Bal Ratio', Round(realprofitPercent / (100 * bot.BRCounter), 2))
local checkBal = lowestBal / startingBalance / 0.2
local checkRatio = bot.BRCounter > 0 and 0.8 / bot.BRCounter or 0
local suggest = posRisk * Min(checkBal, checkRatio)
local suggestion = IfElse(suggest > 50, 50, suggest)
local potential = Min(checkBal, checkRatio) * realprofitPercent * suggestion / suggest
if potential > realprofitPercent then
CustomReport("SMOKGESTION", "Potential profit at "..potential.."%".." by changing STARTING RISK to "..suggestion)
elseif runprofitPercent > realprofitPercent and runprofitPercent > 0 then
CustomReport("SMOKGESTION", "The settings has good potential because Running Profit > Realized Profit")
end
end)
---------------------
-- INFO
if showDetails then
-- positions data
if pamt_l > 0 then
if RRtrailing > 0 then
Log('Next LONG halving is at '..LDP)
end
Log('LONG Delta: '..Round(delta_l, 2)..'% | Amount: '..getProfitL)
end
if pamt_s > 0 then
if RRtrailing > 0 then
Log('Next SHORT halving is at '..SDP)
end
Log('SHORT Delta: '..Round(delta_s, 2)..'% | Amount: '..getProfitS)
end
-- balance data
if (CurrentMinute() == 0 and CurrentSecond() == 0) or pamt_l > 0 or pamt_s > 0 then
Log('Balance Monitor -> Wallet: '..Round(walletBal, 5)..' '..profitLabel..
' | Bot: '..Round(botBalance, 5)..' '..profitLabel..
' | Trading: '..Round(tradeBal, 5)..' '..profitLabel..
' | Net: '..Round(netBalance, 5)..' '..profitLabel..
' | Working: '..Round(workBal, 5)..' '..profitLabel..
' | Ratio: '..Round(balRatio, 3))
end
end
---------------------
Save('bot', bot)
4 Comments
Sign in to leave a comment.
Same all of the time Greatness !
Thank you for sharing. Ive returned after years and seen its all on Cloud now which is pretty cool.
Will report back once I figure it out
Hi. I cant find the ref to CC_All_BF_Markets Script anywhere =/
have you tried the discord ?