GAS Backtest
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/

//@version=5
strategy("GAS Backtest",overlay=true, initial_capital=10000, process_orders_on_close=false)

startDate   = input.time(title="Start Date", defval=timestamp("5 Mar 2022 00:00 +0800"))
endDate     = input.time(title="Start Date", defval=timestamp("5 Mar 2022 00:00 +0800"))
gridPct     = input.float(title="Grid PCT", defval=1, minval = 0.1, step = 0.25, maxval = 100)
maxOrder    = input.int(title="Max Order", defval=2, minval=1)
initBuy     = input.bool(title="Grid Reward Only",defval=true)
userVol     = input.float(title="Grid Vol (optional)",defval=0)
refGrid     = input.float(title="Ref. Grid (optional)",defval=0, minval=0)
userSetting = input.bool(title="Apply User setting",defval=false)
today       = input.bool(title="Today Only",defval=true)


if today
    startDate := timenow - ( timenow + 28800000 ) % 86400000 - 1800000
    endDate   := timenow

if userSetting 
    switch syminfo.ticker
        "SOLUSD" =>
            gridPct  := 1
            maxOrder := 5
            userVol  := 1
            refGrid  := 45  
        => startDate := time + 120000

    
if timeframe.period != "1"
    startDate := time + 120000

pct       = 1 + gridPct / 100.0
gridLine  = open
vol       = 0.0
ratio     = 2
sellCount = 0
buyCount  = 0
gain      = 0.0
var label labelName = na


if labelName[1] != na
    label.delete(labelName[1])
         
    
if time > startDate and time <= endDate
    vol       := vol[1]
    gridLine  := gridLine[1]
    pct       := pct[1]
    
    if hour != 16 or minute != 0
        buyCount  := buyCount[1]
        sellCount := sellCount[1]
        gain      := gain[1]
        

    nt  = math.max( math.floor( math.log( high / gridLine ) / math.log( pct ) ), 0)
    nt := math.min( nt , maxOrder)
    //highLine   = gridLine * math.pow(pct,nt)
    highLine = gridLine
    if nt != 0 
        prePrice = highLine
        for i = 1 to nt
            highLine := math.ceil( highLine * pct / syminfo.mintick ) * syminfo.mintick
            gain += ( highLine - prePrice ) * vol
            prePrice := highLine
        gain := math.round( gain * 100 ) * 0.01
    
    
    sellCount += nt
    
    nb  = math.min( math.ceil( math.log( low / gridLine ) / math.log( pct ) ), 0)
    nb := math.max( nb , -1 * maxOrder)
    //lowLine = gridLine * math.pow(pct,nb)
    lowLine = gridLine
    if nb != 0
        for i = 1 to -1*nb
            lowLine := math.floor( lowLine / pct / syminfo.mintick ) * syminfo.mintick
      
    
    buyCount -= nb
    
    if ( highLine != gridLine ) and ( lowLine != gridLine )  
        gridLine := ( highLine - gridLine ) > ( gridLine - lowLine ) ? lowLine : highLine

    else if  highLine != gridLine 
        gridLine := highLine
    else if  lowLine != gridLine 
        gridLine := lowLine
        
        
    if ( gridLine != gridLine[1] ) 
        buyPrice  = gridLine
        sellPrice = gridLine 
        strategy.cancel_all()
        
        for i = 1 to maxOrder 
            //sellPrice := sellPrice * pct
            sellPrice := math.ceil( sellPrice * pct / syminfo.mintick ) * syminfo.mintick
            strategy.order( str.format("Sell{0}",i), strategy.short, vol, limit=sellPrice)
            
            //buyPrice  := buyPrice  / pct
            buyPrice  := math.floor( buyPrice / pct / syminfo.mintick ) * syminfo.mintick
            strategy.order( str.format("Buy{0}",i), strategy.long, vol, limit=buyPrice)  
            
else if time == startDate 
    //label.new(bar_index, high, syminfo.ticker )

    if refGrid != 0 
        n = math.floor( math.log( gridLine / refGrid ) / math.log( pct ) )
        
        line1 = refGrid
        line2 = line1
        
        for i = 1 to ( n >= 0 ? n+1 : -1*n )
            line2 := line1 
            line1 := if n >= 0 
                math.ceil( line1 * pct / syminfo.mintick ) * syminfo.mintick
            else 
                math.floor( line1 / pct / syminfo.mintick ) * syminfo.mintick
            
        
//        line1 = refGrid * math.pow( pct, n )
//        line2 = line1 * pct
        h = high
        l = low
        i = 1
        while true 
            if ( line1 <= h ) and ( line1 >= l )
                gridLine := line1 
                break 
            if ( line2 <= h ) and ( line2 >= l )
                gridLine := line2 
                break
            h := high[i] > h ? high[i] : h
            l := low[i]  < l ? low[i]  : l
            i += 1

     
    botBound = gridLine / 1.1
    topBound = gridLine * 1.1


    nb       = math.floor( math.log( gridLine / botBound ) / math.log( pct ) )
    vol     := gridLine / pct * ( 1 - math.pow( 1/pct, math.max(nb,0) ) ) / ( 1 - 1/pct)

    nt       = math.floor( math.log( topBound / gridLine ) / math.log( pct ) )
    
    if nt < 0
        vol -= gridLine / pct * ( 1 - math.pow( 1/pct, -1 - nt) ) / ( 1 - 1/pct )
        nt  := 0
    
    if initBuy == false
        if nb < 0
            nt  -= -1 - nb
        vol += nt * close
        
    else 
        vol += gridLine * ( 1 - math.pow( pct, nt ) ) / ( 1 - pct)
        if nb < 0
            vol -= gridLine * ( 1 - math.pow( pct, -1*nb ) ) / ( 1 - pct )
    
    if userVol == 0
        vol := strategy.equity / vol
    else if userVol > 0
        vol := userVol
    else 
        vol := -1 * userVol * gridPct 


    if initBuy == false
        strategy.order("init", strategy.long, nt*vol)
    
    buyPrice  = gridLine
    sellPrice = gridLine 
    
    for i = 1 to maxOrder 
        sellPrice := sellPrice * pct
        strategy.order( str.format("Sell{0}",i), strategy.short, vol, limit=sellPrice)
        
        buyPrice  := buyPrice  / pct 
        strategy.order( str.format("Buy{0}",i), strategy.long, vol, limit=buyPrice)


if time == endDate 
    strategy.cancel_all()


//plot(line1, color= color.red)
//plot(line2)
//plot(pct * 100 -10)
//plot(syminfo.mintick)

if today and ( time >= startDate ) and bar_index >= last_bar_index-1  
    labelName := label.new(x=bar_index+80, y=high*1.002, text = str.format("Buy : {0}\nSell : {1}\nGain : {2}",buyCount,sellCount,gain), style=label.style_label_center, textcolor = color.white)

plot( time >= startDate and time <= endDate? gridLine : na , color = color.yellow, style = plot.style_stepline )
//plot(close)

