[DMcL] Divergence YOLO
betaDescription
Basic idea:
- only 2 entries
- finding divergences on the RSI, combined with isAbnormal() to filter out many.
- exit when the CC Stochastic Momentum Index or RSI peaks
- will hold onto a trade if the market is trending in the right direction (3 x EMAs)
- will ignore opposing trades in a trending market (3 x EMAs).
- trend will also act as stoploss if in an opposite postition.
*Update: added option to turn off the holding onto trades in a trend & the trend acting as a stoploss
HaasScript
EnableHighSpeedUpdates(true)
HideOrderSettings()
HideTradeAmountSettings()
SetFee(-0.02) -- for backtesting
-- 5 min interval
function wholeLogicInaFunction()
-- inputs
local orderTypes = {
limit = 'LIMIT',
postLimit = 'LIMIT (Post-Only)',
market = 'MARKET'
}
local TOTAL_margin = Input('Order Size Margin', 100, 'margin used of quote currency (e.g. USDT) per order. 2 orders are placed total, so total margin used is double this', 'TRADE SETTINGS')
local entry_order = InputOptions('Entry Order Type', orderTypes.market, orderTypes, 'Entry order type.', 'TRADE SETTINGS')
local tp_scaler = Input('take profit scaler', 0.2, 'the take profit threshold is calculated from the atr(percentage) multiplied by this number. Only over this threshold will the bot look for exit conditions (a peaking SMI or RSI). In more volitile markets this threshold will automatically be higher. If you want to see this value on the chart, uncomment the atr plot at the end of the script', 'TRADE SETTINGS')
local sl = Input('stop loss', 3, 'The percentage after the second (and final) entry the stop loss is placed', 'TRADE SETTINGS')
local slmult = Input('stop loss mult (first entry)', 3, 'This times the regular stoploss value is where a stoploss will be placed from the first entry. E.g. 3 x 3 = 9', 'TRADE SETTINGS')
local ent_mult = Input('Entry Ab Mult', 1, 'How sensitive entries are triggered. 1 is most sensitive. 1-3.', 'TRADE SETTINGS')
local exit_mult = Input('Exit Ab Mult', 1, 'How sensitive exits are triggered (only when the medium ema is moving in unprofitable direction). 1 is most sensitive. 1-3', 'TRADE SETTINGS')
local right = Input("Pivot Lookback Right", 3, 'How many bars to the right needed to find a local high/low. Will also be the delay for entries', 'PIVOT POINTS')
local left = Input("Pivot Lookback Left", 5, 'How many bars to the left needed to find a local high/low.', 'PIVOT POINTS')
local rangeUpper = Input("Max of Lookback Range", 60, 'After this many bars, old pivot points will be cleared', 'PIVOT POINTS')
local lengthK = Input('Length K', 10, '', 'SMI SETTINGS')
local lengthD = Input('Length D', 3, '', 'SMI SETTINGS')
local lengthEMA = Input('Length EMA', 4, '', 'SMI SETTINGS')
local smi_mult = Input('SMI Abnormal Multiplier', 1.4, '1-3. This is used for take profits. Higher settings will result in letting winners ride but potentially more losing trades', 'SMI SETTINGS')
local rsi_period = Input('RSI period', 14, '', 'RSI SETTINGS')
local ema_int = InputInterval('EMA interval', 45, 'Keep', 'EMA SETTING')
local ema_period = Input('EMA short Period', 20, '', 'EMA SETTING')
local ema_med_period = Input('EMA medium Period', 50, '', 'EMA SETTING')
local ema_long_period = Input('EMA long Period', 100, '', 'EMA SETTING')
local order_offset = Input('entry price offset', 0.01, 'Only used for LIMIT orders')
local order_timeout_input = Input('order timeout (min)', 60, 'Only used for LIMIT orders')
local short = Input('allow short', true)
local long = Input('allow long', true)
local ma_yolo = Input('MA Yolo', true, 'Will hold onto trades when in a trending market')
local ma_stoploss = Input('MA stoploss', false, 'A change into an opposing trend will stoploss')
-- rescale function
local function rescale(_src, oldMin, oldMax, newMin, newMax)
return newMin + (newMax - newMin) * (_src - oldMin) / (oldMax - oldMin)
end
-- data
local ob = GetOrderbook()
local cp = CurrentPrice()
local h = HighPrices()
local l = LowPrices()
local c = ClosePrices()
local o = OpenPrices()
local c_ema = ClosePrices(ema_int)
local ema = EMA(c_ema, ema_period)
local ema_med = EMA(c_ema, ema_med_period)
local ema_long = EMA(c_ema, ema_long_period)
local downtrend = (ema < ema_med) and (ema_med < ema_long)
local uptrend = (ema > ema_med) and (ema_med > ema_long)
-- values
local profit = GetBotProfit()
local order_timeout = order_timeout_input * 60
local timeout = Load('timeout', 0)
local SMI = CC_Stochastic_Momentum_Index(lengthK, lengthD, lengthEMA)
local rsi = RSI(c, rsi_period)
local rsi_long = RSI(c_ema, rsi_period)
local lookback_rsi = ArrayGet(rsi, right+1)
local lookback_close = ArrayGet(c, right+1)
local lookback_low = ArrayGet(l, right+1)
local lookback_high = ArrayGet(h, right+1)
local SMI_Ab = IsAbnormal(SMI[1], smi_mult)
local RSI_Ab_long = (rsi < 50) and not (IsAbnormal(rsi[right+1], ent_mult))
local RSI_Ab_low = (rsi < 50) and not (IsAbnormal(rsi, exit_mult))
local RSI_Ab_short = (rsi > 50) and not (IsAbnormal(rsi[right+1], ent_mult))
local RSI_Ab_high = (rsi > 50) and not (IsAbnormal(rsi, exit_mult))
local rsi_lowLeft = GetLow(Grab(rsi, right + 1, left), left)
local rsi_lowRight = GetLow(rsi, right)
local rsi_highLeft = GetHigh(Grab(rsi, right + 1, left), left)
local rsi_highRight = GetHigh(rsi, right)
local cv = ContractValue()
local total_amount = TOTAL_margin/cv * Leverage()
local smi = SMI[1]
local smiEma = SMI[2]
local atr = (((ATR(h, l, c, 14))/cp)*1000)*tp_scaler
-- positions
local pezId = Load('pezId', NewGuid())
local position = PositionContainer(pezId)
-- memory
local index = Load('index', 0)
local leid = Load('leid', '')
local seid = Load('seid', '')
local xeid = Load('xeid', '')
local long_exit = Load('long_exit', false)
local short_exit = Load('short_exit', false)
local curPivotLow = Load('curPivotLow', 0)
local prePivotLow = Load('prePivotLow', 0)
local curPriceLow = Load('curPriceLow', 0)
local prePriceLow = Load('prePriceLow', 0)
local curPivotHigh = Load('curPivotHigh', 0)
local prePivotHigh = Load('prePivotHigh', 0)
local curPriceHigh = Load('curPriceHigh', 0)
local prePriceHigh = Load('prePriceHigh', 0)
local lookback_window = Load('lookback_window', 0)
-- reset function
local function reset()
curPivotLow = 0
prePivotLow = 0
curPriceLow = 0
prePriceLow = 0
curPivotHigh = 0
prePivotHigh = 0
curPriceHigh = 0
prePriceHigh = 0
end
-- price adjustment functions
function adjustBuyPrice(price)
if ob.bidPrices[1] <= price then
price = SubPerc(ob.bidPrices[1], order_offset)
end
return price
end
function adjustBuyPriceTrigger(price)
price = ob.askPrices[1] + PriceStep()
return price
end
function adjustSellPrice(price)
if ob.askPrices[1] >= price then
price = AddPerc(ob.askPrices[1], order_offset)
end
return price
end
function adjustSellPriceTrigger(price)
price = ob.bidPrices[1] - PriceStep()
return price
end
-- entry order type
function getOrderType(strType)
if strType == orderTypes.limit then
return LimitOrderType
elseif strType == orderTypes.postLimit then
return MakerOrCancelOrderType
elseif strType == orderTypes.market then
return MarketOrderType
end
end
-- new position function
function newPosition()
index = 0
xeid = ''
leid = ''
seid = ''
curPivotLow = 0
prePivotLow = 0
curPriceLow = 0
prePriceLow = 0
short_exit = false
long_exit = false
pezId = NewGuid()
end
-- dump function
function dump()
if position.amount > 0 then
PlaceExitPositionOrder(pezId, {type = MarketOrderType})
newPosition()
end
end
-- pivot points
-- low
if lookback_rsi < rsi_lowLeft and lookback_rsi < rsi_lowRight and RSI_Ab_long and long then
PlotShape(2, ShapeTriangleUp, Gold, 3, false, {offset = -right})
PlotShape(0, ShapeTriangleUp, Gold, 3, false, {offset = -right})
prePivotLow = curPivotLow
curPivotLow = lookback_rsi
prePriceLow = curPriceLow
curPriceLow = lookback_close
lookback_window = Time() + (CurrentInterval() * rangeUpper * 60)
end
local low_not_null = (prePivotLow > 0) and
(curPivotLow > 0) and
(prePriceLow > 0) and
(curPriceLow > 0)
-- high
if lookback_rsi > rsi_highLeft and lookback_rsi > rsi_highRight and RSI_Ab_short and short then
PlotShape(2, ShapeTriangleDown, Fuchsia(50), 3, true, {offset = -right})
PlotShape(0, ShapeTriangleDown, Fuchsia(50), 3, true, {offset = -right})
prePivotHigh = curPivotHigh
curPivotHigh = lookback_rsi
prePriceHigh = curPriceHigh
curPriceHigh = lookback_close
lookback_window = Time() + (CurrentInterval() * rangeUpper * 60)
end
local high_not_null = (prePivotHigh > 0) and
(curPivotHigh > 0) and
(prePriceHigh > 0) and
(curPriceHigh > 0)
-- entry logic
local long_price = adjustBuyPrice(cp.bid)
local long_price_trigger = adjustBuyPriceTrigger(cp.bid)
local short_price = adjustSellPrice(cp.ask)
local short_price_trigger = adjustSellPriceTrigger(cp.ask)
if timeout < Time() then
-- short entry logic
local short_ok = prePivotHigh > curPivotHigh and prePriceHigh < curPriceHigh and high_not_null
if short and short_ok and (index < 2) and TradeOncePerBar() and not uptrend then
if (GetPositionDirection(pezId) == NoPosition) then
if leid == '' and seid == '' then
seid = PlaceGoShortOrder(short_price, total_amount, {type=getOrderType(entry_order), note='Short Entry 1', timeout=order_timeout, positionId=pezId})
index = index + 1
reset()
end
elseif position.isShort and (index == 1) and StopLoss(1.3*atr) then
seid = PlaceGoShortOrder(short_price, total_amount, {type=getOrderType(entry_order), note='Short Entry 2', timeout=order_timeout, positionId=pezId})
index = index + 1
end
end
-- long entry logic
local long_ok = prePivotLow < curPivotLow and prePriceLow > curPriceLow and low_not_null
if long and long_ok and (index < 2) and TradeOncePerBar() and not downtrend then
if (GetPositionDirection(pezId) == NoPosition) then
if leid == '' and seid == '' then
leid = PlaceGoLongOrder(long_price, total_amount, {type=getOrderType(entry_order), note='Long Entry 1', timeout=order_timeout, positionId=pezId})
index = index + 1
reset()
end
elseif position.isLong and (index == 1) and StopLoss(1.3*atr) then
leid = PlaceGoLongOrder(long_price, total_amount, {type=getOrderType(entry_order), note='Long Entry 2', timeout=order_timeout, positionId=pezId})
index = index + 1
end
end
end
-- exit logic (tp)
local posRoi = GetPositionROI(pezId)
if position.isLong and TradeOncePerBar() then
if downtrend then
if TakeProfit(0.1) then
PlaceExitPositionOrder(pezId, cp, MarketOrderType, "Long Take Profit (down)")
newPosition()
end
elseif ma_yolo then
if RSI_Ab_high and TakeProfit(atr) and not IsRising(ema_med, 1) then
PlaceExitPositionOrder(pezId, cp, MarketOrderType, "Long Take Profit rsi")
newPosition()
elseif TakeProfit(atr) and ((smi > 0) and not SMI_Ab) and not IsRising(ema_med, 1) then
long_exit = true
end
if long_exit and IsFalling(c, 1) then
PlaceExitPositionOrder(pezId, cp, MarketOrderType, "Long Take Profit")
newPosition()
end
else
if RSI_Ab_high and TakeProfit(atr) then
PlaceExitPositionOrder(pezId, cp, MarketOrderType, "Long Take Profit rsi")
newPosition()
elseif TakeProfit(atr) and ((smi > 0) and not SMI_Ab) then
long_exit = true
end
if long_exit and IsFalling(c, 1) then
PlaceExitPositionOrder(pezId, cp, MarketOrderType, "Long Take Profit")
newPosition()
end
end
end
if position.isShort and TradeOncePerBar() then
if uptrend then
if TakeProfit(0.1) then
PlaceExitPositionOrder(pezId, cp, MarketOrderType, "Short Take Profit (up)")
newPosition()
end
elseif ma_yolo then
if RSI_Ab_low and TakeProfit(atr) and not IsFalling(ema_med, 1) then
PlaceExitPositionOrder(pezId, cp, MarketOrderType, "Short Take Profit rsi")
newPosition()
elseif TakeProfit(atr) and ((smi < 0) and not SMI_Ab) and not IsFalling(ema_med, 1) then
short_exit = true
end
if short_exit and IsRising(c, 1) then
PlaceExitPositionOrder(pezId, cp, MarketOrderType, "Short Take Profit")
newPosition()
end
else
if RSI_Ab_low and TakeProfit(atr) then
PlaceExitPositionOrder(pezId, cp, MarketOrderType, "Short Take Profit rsi")
newPosition()
elseif TakeProfit(atr) and ((smi < 0) and not SMI_Ab) then
short_exit = true
end
if short_exit and IsRising(c, 1) then
PlaceExitPositionOrder(pezId, cp, MarketOrderType, "Short Take Profit")
newPosition()
end
end
end
-- exit logic (sl)
if ma_stoploss then
if uptrend then
if position.isShort then
PlaceExitPositionOrder(pezId, cp, MarketOrderType, "Short StopLoss (crossover)")
newPosition()
timeout = Time() + order_timeout
end
end
if downtrend then
if position.isLong then
PlaceExitPositionOrder(pezId, cp, MarketOrderType, "Long StopLoss (crossover)")
newPosition()
timeout = Time() + order_timeout
end
end
end
-- sl
if (GetPositionDirection(pezId) != NoPosition) and (index == 1) and not IsAnyOrderOpen(pezId) then
if StopLoss(sl*slmult) then
PlaceExitPositionOrder(pezId, cp, MarketOrderType, 'STOPLOSS')
newPosition()
timeout = Time() + order_timeout
end
end
if (GetPositionDirection(pezId) != NoPosition) and (index == 2) and not IsAnyOrderOpen(pezId) then
if StopLoss(sl) then
PlaceExitPositionOrder(pezId, cp, MarketOrderType, 'STOPLOSS')
newPosition()
timeout = Time() + order_timeout
end
end
-- clear timed-out orders
if (GetPositionDirection(pezId) == NoPosition) then
if seid != '' and not IsOrderOpen(seid) then
newPosition()
end
if leid != '' and not IsOrderOpen(leid) then
newPosition()
end
end
-- plot
if not SMI_Ab then
PlotShape(1, ShapeCross, Yellow(90), 2)
end
if position.isShort and RSI_Ab_low and TakeProfit(atr) and not IsFalling(ema_med, 1) then
PlotShape(2, ShapeCross, Green, 3)
end
if position.isLong and RSI_Ab_high and TakeProfit(atr) and not IsRising(ema_med, 1) then
PlotShape(2, ShapeCross, Red, 3)
end
-- dump
function stopExit()
if IsAnyOrderOpen() then
CancelAllOrders()
end
dump()
Log('Stop & Dump!', Purple)
end
InputButton('Stop And Exit', stopExit, 'Optimize for Interval must be unchecked', 'EXIT SETTINGS')
-- lookback window
if lookback_window < Time() then
reset()
end
-- plot technical indicators
ChartSetOptions(0, '', 0.75)
ChartSetOptions(1, 'smi', 0.25)
ChartSetOptions(2, 'rsi', 0.25)
-- ChartSetOptions(3, 'atr', 0.25)
ChartSetOptions(8, 'PnL', 0.25)
local prof_plot = Plot(8, "PnL", profit, Green(40))
Plot(0, 'ema', ema)
Plot(0, 'ema_med', ema_med, Yellow)
Plot(0, 'ema_long', ema_long, Orange)
Plot(2, "rsi", rsi)
-- Plot(3, "atr", atr)
PlotDoubleColor(prof_plot, 0, Red)
Save('timeout', timeout)
Save('index', index)
Save('pezId', pezId)
Save('leid', leid)
Save('seid', seid)
Save('xeid', xeid)
Save('long_exit', long_exit)
Save('short_exit', short_exit)
Save('curPivotLow', curPivotLow)
Save('prePivotLow', prePivotLow)
Save('curPriceLow', curPriceLow)
Save('prePriceLow', prePriceLow)
Save('curPivotHigh', curPivotHigh)
Save('prePivotHigh', prePivotHigh)
Save('curPriceHigh', curPriceHigh)
Save('prePriceHigh', prePriceHigh)
Save('lookback_window', lookback_window)
end
local optimize = Input('Backtest optimization', true, '', 'OPTIMIZE')
local opt_interval = Input('Backtest optimization interval', 0, '', 'OPTIMIZE')
if optimize then
OptimizedForInterval(opt_interval, wholeLogicInaFunction)
else
wholeLogicInaFunction()
end
local report = GetTradingReport()
local los_pos = (report.losingPositions)
local posPnL = GetPositionProfit(pezId)
local maxPnLDD = Load('maxPnLDD', 0)
if posPnL < maxPnLDD then
maxPnLDD = posPnL
end
Save('maxPnLDD', maxPnLDD)
Finalize(function()
CustomReport('Max Drawdown', Round(maxPnLDD, 2))
CustomReport('Losing Positions', los_pos)
end)
0 Comments
Sign in to leave a comment.
No comments yet. Be the first!