Smokybot RS MK1 (Remote Signal)
stableDescription
First of all, make sure you know how to create signal from other website. For example this is a tutorial made by the excellent Haasonline Team on how to create signal from Trading View https://youtu.be/SMqRw89OnT4
BOT FEATURES
- Bot has 3 signal entries. The 1st one is the primary and mandatory entry. It "read" Entry, Exit, and Custom Signal (SignalReservedA). You can use all 3 or just one.
- The decision for entry is unanimous. Supertrend can be added for additional signal (personally i use this).
- For Exit Position bot has 3 options. Risk Reward system (on by default), SignalExit from Signal 1 (if you set the alert for it in TradingView) or % scalping (if you set it up in the bot).
- Do you love Heikin Ashi candle? . There is an option to use is as base of the chart. Just make sure that the indicators also calculated based on Heikin Ashi.
BUT I USE FREE PLAN IN TRADING VIEW. I CAN ONLY HAVE 1 ALERT!
Worry my young padawan do not. Advantage of this bot you still can take. Yrsssss.
As described earlier the Signal 1 accept Custom Signal (SignalReservedA). Bot will read this as "open position". Long or short? This is where supertrend come in. if it happens when it's uptrend, then it's long.
So if you are sending Custom Signal make sure to enable Supertrend filter or else bot will open both position
when it comes. Unless if that is what you want.
Fortunately there is an indicator other than alerting to open position it is also a Machine Learning classification algorithm capable of categorizing historical data from a multi-dimensional feature space (according to the desription).
You can find it here
How to do it:
1. Add that indicator into your TradingView chart.
2. Add new alerts in TradingView and pick "Lorentzian..." in condition.
3. Choose " Open Position" on the drawdown below it.
4. For trigger i usually use Once Per Bar Close for more confirmation. Read more the difference in their docs.
5. Set expired to maximum.
6. Click on Notifications tab
7. In the Webhook URL paste the link from Haasonline Signal Details email under Custom signal 1.
8. Create the alarm.
CAN I BACKTEST
Nope. You can't backtest Remote Signal BUT Haasonline log signal received from TradingView. So you can backtest it later in the future after Haasonline server collected some of the signals.
Assuming you understand PineScript then you can mod the script you like to become trading strategy.
Fortunately, again, someone already have done Lorentzian script trading strategy. You can find it here.
So play with the setting on that strategy script, find something you like then apply it on the original script that sending alert and hope the best.
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
-- How to create signal from TradingView https://youtu.be/SMqRw89OnT4
EnableHighSpeedUpdates(true)
HideOrderSettings()
HideTradeAmountSettings()
-- inputs
InputGroupHeader('Trade Settings')
local useHA = Input('Use Heikin Ashi', false, 'Heikin Ashi as price source. Match this with webhook source (i.e. TradingView).')
local signalName1 = Input('Signal 1 Name', '')
local signalPK1 = Input('Signal Identifier 1', '', 'Primary signal. Recognize SignalLong/Short and Custom signal 1 for entry, SignalExitLong/Short for alternative to RR.')
local signalName2 = Input('Signal 2 Name', '')
local signalPK2 = Input('Signal Identifier 2', '', 'Empty means it will be ignored.')
local signalName3 = Input('Signal 3 Name', '')
local signalPK3 = Input('Signal Identifier 3', '', 'Empty means it will be ignored.')
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 Hedging', false, 'Only have 1 type of position per trade. Long will not open if Short is open and vice versa.')
local okCycle = Input('Position Cycle', false, 'If true then bot only open one position per supertrend cycle.')
InputGroupHeader('Bot Settings')
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')
local FEL = Input('Force Exit Long', false, 'Force exit LONG position')
local FOS = Input('Force Open Short', false, 'Force open SHORT position')
local FES = Input('Force Exit Short', false, 'Force exit SHORT position')
local showData = Input('Show Balance & Position Data', false, 'Show balance and position data in the logs.')
InputGroupHeader('Custom Wallet')
local wtfBal = Input('Deactivate on Over Budget', false, 'ONLY FOR BACKTEST. Disable bot when Bal. Ratio hit trigger')
local testWallet = Input('Activate Custom Wallet', false, 'Use custom wallet amount. A must in Haaslab')
local testBal = Input('Custom Wallet Balance', 0, 'Amount of custom wallet')
InputGroupHeader('Budget')
local startBal = Input('Starting Balance', 0, 'Starting trading balance which also maximum COST to limit working balance (margin cost + unrealised loss)')
local leverage = Leverage()
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('Safety')
local slTrigger = Input('Fix SL %', 0, '0 is using Supertrend value. > 0 is fixed % and ignoring Supertrend ')
local reduceTrigger = Input('Bal Ratio Trigger', 0.8, 'Ratio between working balance and trading balance to trigger size reduction')
local profitOut = 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 profit < 80% of highest profit recorded.')
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 orderCount = Input('Orders Count', 1, 'How many total filled orders are allowed for DCA.')
local slotBudget = Input('Risk % per order', 1, '1 = risk losing ~1% of trading balance PER ORDER if SL hit from current order price. Total risk PER POSITION = order count * risk.')
local orderDistance = Input('Fix Orders Distance %', 0, 'Minimum % price distance from filled order to the next.')
local dynamicDist = Input('Dynamic Order Distance %', true, 'Auto calculate order distance. If ticked will ignore Order Distance value')
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 start looking to close position.')
local RRtrailing = Input('SL RR Trailing', 0, 'RR to start trailing SL. 0 is disable. 1st step is moving SL to entry.')
local RRhalving = Input('RR Halving', false, 'IF RR Trailing enabled, halving the size at trailing point.')
local okScalping = Input('Scalping', false, 'Take profit at min % below when price below target RR.')
local minScalping = Input('Min. scalping profit %', 0.1, 'TP when profit (price change) above this value and ADX SMA < threshold.')
if startBal == 0 then
DeactivateBot('Please enter trading balance', true)
end
---------------------
-- DATA & PRICES
local getMarket = PriceMarket()
local getAccount = AccountGuid()
local cp = CurrentPrice(getMarket)
local mainInterval = CurrentInterval()
local h, l, c = OptimizedForInterval(mainInterval, function()
local o = useHA and HeikinOpenPrices(mainInterval) or OpenPrices(mainInterval)
local c = useHA and HeikenClosePrices(mainInterval) or ClosePrices(mainInterval)
local h = useHA and Max(HighPrices(mainInterval), o, c) or HighPrices(mainInterval)
local l = useHA and Min(LowPrices(mainInterval), o, c) or LowPrices(mainInterval)
return h, l, c
end)
---------------------
-- Indicators
InputGroupHeader('Supertrend')
local stFilter = Input('Supertrend Filter', false, 'Filter order based on Supertrend. Example long signals will only executed if Supertrend is uptrend.')
local stPeriod = Input('Period', 13)
local stMulti = Input('ATR Multiplier', 5)
local supertrend = CC_SuperTrendExt(h, l, c, Average(h, l), stPeriod, stMulti, true)
Plot(0, 'Supertrend', supertrend)
---------------------
-- 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()
bot.longFilled = 0
bot.shortFilled = 0
bot.longTrail = 0
bot.shortTrail = 0
bot.longHalving = 0
bot.shortHalving = 0
bot.targetPriceL = cp.ask
bot.targetPriceS = cp.bid
bot.orderDistanceL = 0
bot.orderDistanceS = 0
bot.highest_delta_l = 0
bot.highest_delta_s = 0
bot.prevLongSLP = 0
bot.prevShortSLP = 0
bot.longCycle = 0
bot.shortCycle = 0
-- stat
bot.highestP = 0
bot.lowestP = 0
bot.SRCounter = 0
bot.BRCounter = 0
-- signal
bot.signal1 = SignalNone
bot.signal2 = SignalNone
bot.signal3 = SignalNone
end
inited = true
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 (cp.close - aep_l) / aep_l * 100 or 0
if delta_l > bot.highest_delta_l then
bot.highest_delta_l = delta_l
end
--[[Plot(9, 'delta_l', delta_l)
Plot(9, 'bot.highest_delta_l', bot.highest_delta_l, Red)--]]
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 - cp.close) / aep_s * 100 or 0
if delta_s > bot.highest_delta_s then
bot.highest_delta_s = delta_s
end
--[[Plot(8, 'delta_s', delta_s)
Plot(8, 'bot.highest_delta_s', bot.highest_delta_s, Red)--]]
-- manage position ids
if pamt_l == 0 and IsPositionClosed(bot.longPosId) then
if IsAnyOrderOpen(bot.longPosId) then
CancelAllOrders(bot.longPosId)
else
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.highest_delta_l = 0
if okCycle then
bot.longCycle = 1
end
end
end
if pamt_s == 0 and IsPositionClosed(bot.shortPosId) then
if IsAnyOrderOpen(bot.shortPosId) then
CancelAllOrders(bot.shortPosId)
else
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.highest_delta_s = 0
if okCycle then
bot.shortCycle = 1
end
end
end
-- get pos id
local getPositionId = function(isLong)
return isLong and bot.longPosId or bot.shortPosId
end
---------------------
-- WALLET CHECK
local profitLabel = ProfitLabel(getMarket)
if profitLabel == nil then profitLabel = QuoteCurrency(getMarket) end
-- inverse or not
if profitLabel == 'USD' or profitLabel == 'USDT' or profitLabel == 'BUSD' or profitLabel == 'USDC' then
isInverse = false
else
isInverse = true
end
-- check balance usage
if pamt_l > 0 then
usedLong = isInverse and ((pamt_l * contVal) / leverage) / aep_l or (pamt_l * aep_l) / leverage
getProfitL = isInverse and (cp.close - aep_l) * (leverage * usedLong) / cp.close or (cp.close - aep_l) * pamt_l
else
getProfitL = 0
usedLong = 0
end
if pamt_s > 0 then
usedShort = isInverse and ((pamt_s * contVal) / leverage) / aep_s or (pamt_s * aep_s) / leverage
getProfitS = isInverse and (aep_s - cp.close) * (leverage * usedShort) / cp.close or (aep_s - cp.close) * pamt_s
else
getProfitS = 0
usedShort = 0
end
local getProfit = getProfitL + getProfitS
local botProfit = GetBotProfit()
local netbotProfit = botProfit + getProfit
if botProfit > bot.highestP then
bot.highestP = botProfit
elseif botProfit < bot.lowestP then
bot.lowestP = botProfit
end
local balance = Balance()
local xchangeBal = balance.total
local walletBal = testWallet and testBal + botProfit
or xchangeBal
local workBal = usedLong + usedShort - getProfit
if dynamicHodl and botProfit >= bot.highestP * 0.8 then
profitOut = 0
end
local botBalance = startBal + botProfit
local netBalance = startBal + netbotProfit - usedLong - usedShort
local maxCost = profitOut > 0 and botProfit > 0
and botBalance - (botProfit * profitOut / 100)
or botBalance
-- BALANCE WARNING
local balRatio = workBal / maxCost
local okBalance = walletBal >= botBalance
local maxBalance = startBal + bot.highestP
if not okBalance then
LogWarning('TRADING BALANCE: '..botBalance..' < EXCHANGE BALANCE: '..walletBal)
end
if maxCost <= 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 maxBalance - botBalance >= startBal and strictBudget 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 then
DeactivateBot('Deactivated because over budget', true)
end
--Log('STOP at over budget safety activated. Is this a backtest ?', Yellow)
end
if wtfStop then
if pamt_l == 0 and pamt_s == 0 then
DeactivateBot('Deactivated by No Position', true)
end
if CurrentMinute() == 00 then
Log('Deactivate on No Position is active.', Yellow)
end
end
---------------------
-- DYNAMICS
-- ENTRY
local longPrice = cp.bid
local LLP = LastLongPrice()
local shortPrice = cp.ask
local LSP = LastShortPrice()
-- SL
-- LONG
if slTrigger == 0 then
longRisk = stFilter and supertrend or GetLow(l, 13)
else
longRisk = SubPerc(longPrice, slTrigger)
end
local longSLP = longRisk
if pamt_l != 0 then
longRisk = Load('longRisk')
longSLP = longRisk
end
if RRtrailing > 0 and bot.longTrail >= 1 then
longSLP = aep_l + ((aep_l - longRisk) * RRtrailing * (bot.longTrail - 1))
end
local longRisk_range = pamt_l == 0 and longPrice - longRisk or aep_l - longRisk
local longRisk_perc = longRisk_range / aep_l * 100
local longSL_range = longPrice - longRisk
--Log('longRisk '..longRisk..' longSLP '..longSLP..' longRisk_range '..longRisk_range)
-- SHORT
if slTrigger == 0 then
shortRisk = stFilter and supertrend or GetHigh(h, 13)
else
shortRisk = AddPerc(shortPrice, slTrigger)
end
local shortSLP = shortRisk
if pamt_s != 0 then
shortRisk = Load('shortRisk')
shortSLP = shortRisk
end
if RRtrailing > 0 and bot.shortTrail >= 1 then
shortSLP = aep_s - ((shortRisk - aep_s) * RRtrailing * (bot.shortTrail - 1))
end
local shortRisk_range = pamt_s == 0 and shortRisk - shortPrice or shortRisk - aep_s
local shortRisk_perc = shortRisk_range / aep_s * 100
local shortSL_range = shortRisk - shortPrice
--Log('shortRisk '..shortRisk..' shortSLP '..shortSLP..' shortRisk_range '..shortRisk_range)
--Plot(0, 'longRisk', longRisk)
--Plot(0, 'shortRisk', shortRisk)
-- TP RR
-- LONG
if pamt_l != 0 and bot.longTrail == 0 then
bot.targetPriceL = aep_l + (longRisk_range * RR)
end
-- SHORT
if pamt_s != 0 and bot.shortTrail == 0 then
bot.targetPriceS = aep_s - (shortRisk_range * RR)
end
-- the force
if FEL then
okLong = false
okProfitL = false
exitPriceL = cp.ask
LET = 'Forced'
Log('FORCE EXIT LONG POSITION ACTIVATED', Red)
end
if FES then
okShort = false
okProfitS = false
exitPriceS = cp.bid
SET = 'Forced'
Log('FORCE EXIT SHORT POSITION ACTIVATED', Red)
end
---------------------
-- SLOT SIZE
if autoSlot then
local risk = slotBudget / 100 * maxCost
--Log('risk '..risk..' longRisk_range '..longRisk_range..' shortRisk_range '..shortRisk_range)
local sizeL = isInverse and ((risk * longRisk) / longSL_range) * longPrice / contVal
or risk / longSL_range
slotSizeL = sizeL > slotSizeD and sizeL or slotSizeD
local sizeS = isInverse and ((risk * shortRisk) / shortSL_range) * shortPrice / contVal
or risk / shortSL_range
slotSizeS = sizeS > slotSizeD and sizeS or slotSizeD
--Log('slotSizeL '..slotSizeL..' slotSizeS '..slotSizeS)
else
slotSizeL = slotSizeD
slotSizeS = slotSizeD
end
local potentialCostL = isInverse and slotSizeL * contVal / longPrice / leverage or slotSizeL * longPrice / leverage
local potentialLostL = isInverse and slotSizeL * contVal / longSLP * longRisk_range / longSLP or slotSizeL * longRisk_range
local maxUsedL = longRisk_range > 0 and potentialCostL + potentialLostL or 0
local potentialCostS = isInverse and slotSizeS * contVal / shortPrice / leverage or slotSizeS * shortPrice / leverage
local potentialLostS = isInverse and slotSizeS * contVal / shortSLP * shortRisk_range / shortSLP or slotSizeS * shortRisk_range
local maxUsedS = shortRisk_range > 0 and potentialCostS + potentialLostS or 0
--Log('maxUsedL '..maxUsedL)
--Log('maxUsedS '..maxUsedS)
local okCostL = maxUsedL > 0 and maxUsedL < (maxCost * reduceTrigger * 0.8) - workBal
local okCostS = maxUsedS > 0 and maxUsedS < (maxCost * reduceTrigger * 0.8) - workBal
--[[if okCostL then
PlotSignalBar(5, Green)
end
if okCostS then
PlotSignalBar(6, Red)
end--]]
---------------------
-- EXECUTION
-- Order Count
if orderCount > 1 and bot.longFilled == 1 and bot.longTrail == 0 then
bot.orderDistanceL = dynamicDist and PercentageChange(longRisk, aep_l) / orderCount or orderDistance
end
if orderCount > 1 and bot.shortFilled == 1 and bot.shortTrail == 0 then
bot.orderDistanceS = dynamicDist and PercentageChange(aep_s, shortRisk) / orderCount or orderDistance
end
-- Signals
if signalPK1 == '' then DeactivateBot('Bot need at least one Signal Identifier', true) end
local getSignal1 = GetRemoteSignal(signalPK1)
if bot.signal1 != getSignal1 and (getSignal1 == SignalLong or getSignal1 == SignalShort) then
bot.signal1 = getSignal1
end
local getSignal2 = GetRemoteSignal(signalPK2)
if bot.signal2 != getSignal2 and (getSignal2 == SignalLong or getSignal2 == SignalShort) then
bot.signal2 = getSignal2
end
local getSignal3 = GetRemoteSignal(signalPK3)
if bot.signal3 != getSignal3 and (getSignal3 == SignalLong or getSignal3 == SignalShort) then
bot.signal3 = getSignal3
end
--Cycle
if okCycle then
ChartSetOptions(-2, 'Position Cycle Detector')
if bot.longCycle == 1 and pamt_l == 0 and c < supertrend then
bot.longCycle = 0
PlotSignalBar(-2, White)
elseif bot.shortCycle == 1 and pamt_s == 0 and c > supertrend then
bot.shortCycle = 0
PlotSignalBar(-2, White)
end
end
--LONG
local longcancelTPSL = false
--OPEN
local longOpen1 = pamt_l == 0
and IfElse(signalPK1 != '', bot.signal1 == SignalLong or getSignal1 == SignalReservedA, false)
and IfElse(signalPK2 != '', bot.signal2 == SignalLong, true)
and IfElse(signalPK3 != '', bot.signal3 == SignalLong, true)
and IfElse(stFilter, l > supertrend, true)
local longOpen2 = orderCount > 1
and pamt_l != 0
and longPrice < SubPerc(LLP, bot.orderDistanceL)
and longPrice > longSLP
if longOpen1 then
LT = '1stOrder'
elseif longOpen2 then
LT = 'DCA'
elseif FOL then
LT = 'ForceLong'
end
local longOpen = okLong
and okBalance
and okCostL
and bot.longFilled < orderCount
and IfElse(okCycle, bot.longCycle == 0, true)
and IfElse(oneWay, pamt_s == 0, true)
and (longOpen1 or longOpen2)
--EXIT
local longExit1 = pamt_l > 0
and cp.close > bot.targetPriceL
local longExit2 = pamt_l > 0
and getSignal1 == SignalExitLong
local longExit3 = pamt_l > 0
and okScalping
and delta_l > minScalping
if longExit1 then
LET = 'RR'
elseif longExit2 then
LET = 'Signal 1'
elseif longExit3 then
LET = 'Scalp'
end
local longExit = okProfitL
and not FEL
and delta_l > 0
and (longExit1 or longExit2 or longExit3)
--DERISK
local okLongDerisk1 = pamt_l > 0
and IfElse(RRhalving, cp.close < longSLP, true)
local okLongDerisk2 = pamt_l > 0
and RRtrailing > 0
and RRhalving
and bot.longHalving < RR
and cp.close < bot.targetPriceL
if okLongDerisk1 then
LDP = IfElse(longSLP <= aep_l, longSLP, cp.ask)
LDT = 'SL'
elseif okLongDerisk2 then
LDP = aep_l + (RRtrailing * longRisk_range * (bot.longHalving + 1))
LDT = 'Halving'
PlotSignalBar(3, Green)
else
LDP = longSLP
LDT = ''
end
local okLongDerisk = okDerisk
and pamt_l > 0
and not longExit
and not FEL
and (okLongDerisk1 or okLongDerisk2)
--RR TRAILING
local longRRT = pamt_l > 0
and RRtrailing > 0
and cp.close > aep_l + (RRtrailing * longRisk_range * (bot.longTrail + 1))
if longRRT and bot.longTrail < (RR / RRtrailing) then
bot.longTrail = bot.longTrail + 1
longcancelTPSL = true
end
--SHORT
local shortcancelTPSL = false
--OPEN
local shortOpen1 = pamt_s == 0
and IfElse(signalPK1 != '', bot.signal1 == SignalShort or getSignal1 == SignalReservedA, false)
and IfElse(signalPK2 != '', bot.signal2 == SignalShort, true)
and IfElse(signalPK3 != '', bot.signal3 == SignalShort, true)
and IfElse(stFilter, h < supertrend, true)
local shortOpen2 = orderCount > 1
and pamt_s != 0
and shortPrice > AddPerc(LSP, bot.orderDistanceS)
and shortPrice < shortSLP
if shortOpen1 then
ST = '1stOrder'
elseif shortOpen2 then
ST = 'DCA'
elseif FOS then
LT = 'ForceShort'
end
local shortOpen = okShort
and okBalance
and okCostS
and bot.shortFilled < orderCount
and IfElse(okCycle, bot.shortCycle == 0, true)
and IfElse(oneWay, pamt_l == 0, true)
and (shortOpen1 or shortOpen2)
-- EXIT
local shortExit1 = pamt_s > 0
and cp.close < bot.targetPriceS
local shortExit2 = pamt_s > 0
and getSignal1 == SignalExitShort
local shortExit3 = pamt_s > 0
and okScalping
and delta_s > minScalping
if shortExit1 then
SET = 'RR'
elseif shortExit2 then
SET = 'Signal 1'
elseif shortExit3 then
SET = 'Scalp'
end
local shortExit = okProfitS
and not FES
and delta_s > 0
and (shortExit1 or shortExit2 or shortExit3)
-- DERISK
local okShortDerisk1 = pamt_s > 0
and IfElse(RRhalving, cp.close > shortSLP, true)
local okShortDerisk2 = RRtrailing > 0
and RRhalving
and bot.shortHalving < RR
and cp.close > bot.targetPriceS
if okShortDerisk1 then
SDP = IfElse(shortSLP >= aep_s, shortSLP, cp.bid)
SDT = 'Derisking'
elseif okShortDerisk2 then
SDP = aep_s - (RRtrailing * shortRisk_range * (bot.shortHalving + 1))
SDT = 'Halving'
PlotSignalBar(3, Red)
else
SDP = shortSLP
SDT = ''
end
local okShortDerisk = okDerisk
and pamt_s > 0
and not shortExit
and not FES
and (okShortDerisk1 or okShortDerisk2)
-- RR Trailing
local shortRRT = pamt_s > 0
and RRtrailing > 0
and cp.close < aep_s - (RRtrailing * shortRisk_range * (bot.shortTrail + 1))
if shortRRT and bot.shortTrail < (RR / RRtrailing) then
bot.shortTrail = bot.shortTrail + 1
shortcancelTPSL = true
end
-- SLP
if pamt_l > 0 and bot.prevLongSLP != longSLP then
bot.prevLongSLP = longSLP
longcancelTPSL = true
Log('longSLP changed')
elseif pamt_s > 0 and bot.prevShortSLP != shortSLP then
bot.prevShortSLP = shortSLP
shortcancelTPSL = true
Log('shortSLP changed')
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
elseif order.isFilled then
if isLong then
bot.longFilled = bot.longFilled + 1
longcancelTPSL = true
else
bot.shortFilled = bot.shortFilled + 1
shortcancelTPSL = true
end
oid = ''
else
oid = ''
end
else
if canPlace and TradeOncePerBar(mainInterval, posId) and Time() >= timer then
oid = cmd(price, amount, {timeout = mainInterval * 60, type = LimitOrderType, note = name..filled..'-'..trigger, positionId = posId})
if isLong then
if pamt_l == 0 then
Save('longRisk', longRisk)
end
else
if pamt_s == 0 then
Save('shortRisk', shortRisk)
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 cancelTPSL = isLong and longcancelTPSL or shortcancelTPSL
if oid != '' then
local order = OrderContainer(oid)
if order.isOpen then
if not canExit then
CancelOrder(oid)
LogWarning('Exit cancelled '..name)
end
else
oid = ''
end
else
if canExit then
local pDelta = isLong and Round(delta_l, 2) or Round(delta_s, 2)
oid = PlaceExitPositionOrder({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 cp.bid - PriceStep() or cp.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 oid != '' then
local order = OrderContainer(oid)
if order.isOpen then
if not canReduce 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 = getMarket, type = MarketOrderType, note = name..' '..pDelta..'%', timeout = mainInterval * 60, positionId = posId})
timer = Time() + 60 -- 1min
end
end
Save(prefix .. 'pos_oid', oid)
Save(prefix .. 'pos_timer', timer)
end
-- Derisking REDUCTION
function derisking (isLong, amount, price, trigger, 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..' '..DT
local oid = Load(prefix .. 'derisk_oid', '')
local posId = getPositionId(isLong)
local cmd = isLong and PlaceExitLongOrder or PlaceExitShortOrder
local cancelTPSL = isLong and longcancelTPSL or shortcancelTPSL
-- amount check
if DT == 'Halving' then
reduceOrderType = LimitOrderType
name = prefix..' '..DT..'-'..halvingCount
if isLong then
amount = amount * 0.5
else
amount = amount * 0.5
end
else
if isLong then
reduceOrderType = IfElse(RRhalving, MarketOrderType, StopMarketOrderType)
else
reduceOrderType = IfElse(RRhalving, MarketOrderType, StopMarketOrderType)
end
end
if amount < MinimumTradeAmount(getMarket, price) then
amount = amount
end
if oid != '' then
local order = OrderContainer(oid)
if order.isOpen then
if not canDerisk or cancelTPSL then
CancelOrder(oid)
Log(name..' cancelled', Yellow)
end
if openDT != DT then
CancelOrder(oid)
Log(name..' updating', Yellow)
end
elseif order.isFilled then
if isLong then
if DT == 'Halving' then
bot.longHalving = bot.longHalving + 1
else
bot.longCycle = 1
end
bot.timerL = Time() + (delay * 60)
else
if DT == 'Halving' then
bot.shortHalving = bot.shortHalving + 1
end
bot.timerS = Time() + (delay * 60)
end
oid = ''
else
oid = ''
end
else
if canDerisk then
local pDelta = isLong and Round((price - aep_l) / price * 100, 2) or Round((aep_s - price) / price * 100, 2)
oid = cmd(price, amount, {market = getMarket, triggerPrice = trigger, type = reduceOrderType, note = name..' '..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
updateTakeProfit(true, LET, longExit or FEL)
updateTakeProfit(false, SET, shortExit or FES)
updatePositionManagement(true, pamt_l, okReduce and pamt_l > 0 and balRatio > reduceTrigger and delta_l < delta_s)
updatePositionManagement(false, pamt_s, okReduce and pamt_s > 0 and balRatio > reduceTrigger and delta_s < delta_l)
derisking(true, pamt_l, LDP, longSLP, LDT, okLongDerisk)
derisking(false, pamt_s, SDP, shortSLP, SDT, okShortDerisk)
---------------------
-- PLOT
-- AEP Plot
if aep_l > 0 then
local posId = getPositionId(true)
Plot(0, 'AvgEP Long', aep_l, {c=Green, id=posId, w=2})
Plot(0, 'SL Long', longSLP, {c=Green, id=posId, w=1})
--Plot(0, 'TP Long', exitPriceL, {c=Teal, id=posId, w=1})
end
if aep_s > 0 then
local posId = getPositionId(false)
Plot(0, 'AvgEP Short', aep_s, {c=Red, id=posId, w=2})
Plot(0, 'SL Short', shortSLP, {c=Red, id=posId, w=1})
--Plot(0, 'TP Short', exitPriceS, {c=Purple, id=posId, w=1})
end
--Signal Detector
ChartSetOptions(2, 'Signals Detector. 1 = Long. -1 = Short')
if signalPK1 != '' then
Plot(2, signalName1, IfElseIf(bot.signal1 == SignalLong, bot.signal1 == SignalShort, 1, -1, 0), Red)
end
if signalPK2 != '' then
Plot(2, signalName2, IfElseIf(bot.signal2 == SignalLong, bot.signal2 == SignalShort, 1, -1, 0), Green)
end
if signalPK3 != '' then
Plot(2, signalName3, IfElseIf(bot.signal3 == SignalLong, bot.signal3 == SignalShort, 1, -1, 0), SkyBlue)
end
--[[
ChartSetOptions(1, 'Exposure')
local lineLong = IfElse(pamt_l > 0, pamt_l, 0)
local lineShort = IfElse(pamt_s > 0, -pamt_s, 0)
Plot(10, 'Longs', lineLong, {c=Green, s=Step})
Plot(10, 'Shorts', lineShort, {c=Red, s=Step})
--]]
--[[ChartSetOptions(1, 'Balance Monitor')
Plot(11, 'Net Balance', netBalance, {c=Orange, s=Step})
Plot(11, 'Trading Balance', maxCost, {c=White, s=Step})
Plot(11, 'WorkBal', workBal, {c=Red, s=Step})
Plot(11, 'Bot Profit', botProfit, {c=DarkGreen, s=Step})
Plot(11, 'Bot Balance', botBalance, {c=Yellow, s=Step})
Plot(11, 'Max Balance', maxBalance, {c=Green, s=Step})--]]
---------------------
-- FINAL REPORT
Finalize(function()
CustomReport('Current Balance Ratio', Round(100 * balRatio, 2)..'%')
CustomReport('Highest Balance Ratio', Round(100 * bot.BRCounter, 2)..'%')
CustomReport('Size Reduction', bot.SRCounter..' times')
CustomReport('Final Wallet Balance', Round(walletBal, 5)..' '..profitLabel)
CustomReport('Final Net Balance', Round(netBalance, 5)..' '..profitLabel)
CustomReport('Highest Profit', Round(bot.highestP, 5)..' '..profitLabel)
CustomReport('Lowest Profit', Round(bot.lowestP, 5)..' '..profitLabel)
if testWallet then
local realprofitPercent = Round(botProfit / startBal * 100, 2)
local runprofitPercent = Round(netbotProfit / startBal * 100, 2)
CustomReport('Running Profit %: ', runprofitPercent..'%')
CustomReport('Realized Profit %: ', realprofitPercent..'%')
CustomReport('Profit to Highest Bal Ratio', Round(realprofitPercent / (100 * bot.BRCounter), 2))
end
end)
---------------------
-- INFO
--positions data
if showData then
if signalPK3 != '' then Log(signalName3..': '..getSignal3) end
if signalPK2 != '' then Log(signalName2..': '..getSignal2) end
if signalPK1 != '' then Log(signalName1..': '..getSignal1) end
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
Log('Balance Monitor -> Wallet: '..Round(walletBal, 5)..' '..profitLabel..
' | Highet: '..Round(maxBalance, 5)..' '..profitLabel..
' | Bot: '..Round(botBalance, 5)..' '..profitLabel..
' | Trading: '..Round(maxCost, 5)..' '..profitLabel..
' | Net: '..Round(netBalance, 5)..' '..profitLabel..
' | Working: '..Round(workBal, 5)..' '..profitLabel..
' | Ratio: '..balRatio
)
Log(' ')
end--]]
---------------------
Save('bot', bot)
2 Comments
Sign in to leave a comment.
Hey ! Thank you so much for these customization diamonds!
I am a young padawan who recently boarded the boat and I am truly honored to use your tool
This may give me the independent power to learn Python and Lua code and perhaps customize the MK3 with my own full indicator, but for now I'm really happy to be able to export any signal from the bot and test the good things I have I would like to use for the rest of the journey ! glad to meet you
Thank you very much for your kind words and good luck!