Backtest Custom HiLo Strategy
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © luyikaibr

//@version=4
strategy("Backtest Custom HiLo Strategy", "BTCHiLoS", overlay=true, precision=2,
         process_orders_on_close=true,
         pyramiding=1,
         close_entries_rule="FIFO",
         initial_capital=100000, 
         default_qty_type=strategy.percent_of_equity,
         default_qty_value=100.0)


// Configure backtest start date with inputs
startDay = input(title="Start Day", type=input.integer,
     defval=2, minval=1, maxval=31)
startMonth = input(title="Start Month", type=input.integer,
     defval=1, minval=1, maxval=12)
startYear = input(title="Start Year", type=input.integer,
     defval=2020, minval=1800, maxval=2100)

// Set backtest end date with inputs
endDay = input(title="End Day", type=input.integer,
     defval=31, minval=1, maxval=31)
endMonth = input(title="End Month", type=input.integer,
     defval=10, minval=1, maxval=12)
endYear = input(title="End Year", type=input.integer,
     defval=2020, minval=1800, maxval=2100)
     
endHour = input(title="End Hour", type=input.integer,
     defval=17, minval=0, maxval=23)
endMin = input(title="End Minute", type=input.integer,
     defval=0, minval=0, maxval=59)

startAtOrigin = input(false, title="Start at Origin")
endNow = input(false, title="End Now")

len = input(34, title="Length", minval=0)
offset = input(0, title="Offset", minval=0)

hiloType = input(title="Type", defval="HiLo", options=["HiLo", "HiLo Activator"])
simpleHiLo = (hiloType == "HiLo")

maType = input(title="MA Type", defval="SMA", options=["SMA", "EMA"])
useEMA = (maType == "EMA")

opType = input(title="Operation Type", defval="Long", options=["Long", "Short", "Both"])
opShort = (opType != "Long")
opLong  = (opType != "Short")
opBoth  = (opType == "Both")


hima = (useEMA ? ema(high, len) : sma(high, len))
loma = (useEMA ? ema(low, len) : sma(low, len))

hihi = (simpleHiLo ? na: highest(high, len))
lolo = (simpleHiLo ? na: lowest(low, len))

hilo = close
hilo := if simpleHiLo
    (close < loma[offset] ? hima : (close > hima[offset] ? loma : hilo[1]))
else
    (close < loma[offset] ? hihi : (close > hima[offset] ? lolo : hilo[1]))

hlColor = color.red
hlColor := (close < loma[offset] ? color.red : (close > hima[offset] ? color.green : hlColor[1]))

buyArith  = sign(close - hima[offset])
sellArith = sign(close - loma[offset])

buyCond = (close > hima[offset])
sellCond = (close < loma[offset])

buy = crossover(buyArith, 0.5)
sell = crossunder(sellArith, -0.5)

buyState = false
buyState :=  buy ? true : (sell ? false : buyState[1])

sellState = false
sellState :=  buy ? false : (sell ? true : sellState[1])

enterBuyState  = buyState[0]  and not buyState[1]
enterSellState = sellState[0] and not sellState[1]
 
plot(hilo, color=hlColor, linewidth=2, style=plot.style_cross, transp=0, title="HiLo")

plotshape(enterBuyState,  title = "Buy", text = 'Buy', style = shape.labelup, location = location.belowbar, color= color.green,textcolor = color.white, transp = 0, size = size.tiny)
plotshape(enterSellState, title = "Sell", text = 'Sell', style = shape.labeldown, location = location.abovebar, color= color.maroon,textcolor = color.white, transp = 0, size = size.tiny)


// See if this bar's time happened on/after start date
startDate = timestamp(syminfo.timezone, startYear, startMonth, startDay, 0, 0)
afterStartDate = if startAtOrigin
    true
else
    (time >= startDate)

atStartDate = if startAtOrigin
    false
else
    (time >= startDate and time < (startDate + time_close[1]-time_close[2]))
     
// See if bar's close time is before end date
endTS = if endNow
    timenow - 2*(time_close[1]-time_close[2]) // 86400
else
    timestamp(syminfo.timezone, endYear, endMonth, endDay, endHour, endMin, 0)
beforeEndDate = (time < endTS)

// Only submit enter long orders
strategy.risk.allow_entry_in(opBoth ? strategy.direction.all : (opLong ? strategy.direction.long : strategy.direction.short))

// Submit entry orders, but only on/after start date
if (atStartDate and beforeEndDate and buyCond)
    strategy.entry(id="EL", long=true)

if (atStartDate and beforeEndDate and sellCond)
    strategy.entry(id="ES", long=false)

if (afterStartDate and beforeEndDate and enterBuyState)
    strategy.entry(id="EL", long=true)

if (afterStartDate and beforeEndDate and enterSellState)
    strategy.entry(id="ES", long=false)
              
// if (barstate.islast or not beforeEndDate)
if (not beforeEndDate and strategy.position_size != 0)
    strategy.close_all(comment="End")
    
