Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //@version=4
- //@author=LucF, for PineCoders
- // Backtesting on Non-Standard Charts: Caution! - PineCoders FAQ
- // v1.0, 2019.10.27 20:12 — LucF
- // PineCoders, Tools and ideas for all Pine coders: pinecoders.com
- // This script is a strategy intended to show the results of backtesting
- // on non-standard chart types using chart (normal backtesting) prices vs market prices.
- // PineCoders FAQ & Code question linking here:
- // This indicator's TradingView page:
- strategy("Backtesting on Non-Standard Charts: Caution! [PineCoders]", "", true, precision = 8, initial_capital = 100000, default_qty_type = strategy.percent_of_equity, default_qty_value = 100)
- // ——————————————— Functions
- // ————— Round prices to tick size.
- f_roundToTick( _price) => round(_price / syminfo.mintick) * syminfo.mintick
- // ————— Round values to given decimal places.
- f_round( _val, _decimals) =>
- _p = pow(10, _decimals)
- round(abs(_val) * _p) / _p * sign(_val)
- // ——————————————— Inputs
- ST1 = "Second reversal bar"
- ST2 = "MA crosses"
- strategy = input(ST2, "Strategy used", options = [ST1, ST2])
- useHA = input(false, "Use Heikin Ashi instead of chart prices to calculate strategy")
- noRepaint = input(true, "No repainting (delays signals one bar)")
- showMas = input(false, "Show MAs")
- showMarkers = input(false, "Show entry/exit markers")
- showFills = input(true, "Show market fills")
- showCandles = input(false, "Show market price candlesticks")
- showOpen = input(false, "Show market open as line")
- showBg = input(false, "Show background on fill delta")
- fastLength = input(5, "Fast MA length", minval = 2)
- slowLength = input(25, "Slow MA length", minval = 3)
- src = input(close, "Source")
- // ——————————————— Get market, HA and chart prices, all rounded to tick.
- // ————— Fetch underlying normal bar (market) prices.
- // "lookahead" must be used so that the correct price is retrieved when fetching prices from a chart
- // where there is no one-to-one correspondence between the time of non-standard bars and normal market underlying bars.
- ticker = tickerid(syminfo.prefix, syminfo.ticker)
- realOpen = security(ticker, timeframe.period, open, lookahead = barmerge.lookahead_on)
- realHigh = security(ticker, timeframe.period, high, lookahead = barmerge.lookahead_on)
- realLow = security(ticker, timeframe.period, low, lookahead = barmerge.lookahead_on)
- realClose = security(ticker, timeframe.period, close, lookahead = barmerge.lookahead_on)
- // ————— Calculate Heikin Ashi prices.
- // Full precision for HA calcs.
- haClose_ = 0.0
- haOpen_ = 0.0
- haClose_ := avg(realOpen, realHigh, realLow, realClose)
- haOpen_ := avg(nz(haOpen_[1], realOpen), nz(haClose_[1], realClose))
- haHigh_ = max(max(realHigh, haOpen_), haClose_)
- haLow_ = min(min(realLow, haOpen_), haClose_)
- // Display precision for rest of indicator calcs.
- haOpen = f_roundToTick(haOpen_)
- haHigh = f_roundToTick(haHigh_)
- haLow = f_roundToTick(haLow_)
- haClose = f_roundToTick(haClose_)
- // ————— Get current chart prices rounded to tick.
- chartOpen = f_roundToTick(open)
- chartHigh = f_roundToTick(high)
- chartLow = f_roundToTick(low)
- chartClose = f_roundToTick(close)
- // ————— Get prices used for calculations from HA or chart.
- calcOpen = useHA ? haOpen : chartOpen
- calcClose = useHA ? haClose : chartClose
- // ——————————————— Calculations and conditions
- // ————— When not repainting, we will reference bars one bar back.
- idx = noRepaint ? 1 : 0
- // ————— Long/Short conditions.
- upBar = calcClose > calcOpen
- fastMa = sma(calcClose, fastLength)
- slowMa = sma(calcClose, slowLength)
- longCondition = strategy == ST1 ? upBar[idx] and upBar[idx + 1] and not upBar[idx + 2] : crossover(fastMa, slowMa)[idx]
- shortCondition = strategy == ST1 ? not upBar[idx] and not upBar[idx + 1] and upBar[idx + 2] : crossunder(fastMa, slowMa)[idx]
- // ——————————————— Entries & Exits
- // ————— We initialize this variable only once (with "var" keyword) because we want its value to persist across bars until it is changed when an order is issued.
- var inLong = false
- // ————— We re-initialize these vars to false every bar because we only want them true for one bar when the order must be given.
- goLong = false
- goShort = false
- // ————— We will only emit an order if our trigger condition occurs and:
- // - we are in a trade in the opposite direction,
- // - there is some equity left,
- // - we have not issued an order on the previous bar (because we need to process trade transitions),
- // - we have underlying real market data.
- if not inLong and longCondition and strategy.equity > 0 and not goShort[1] and not na(realOpen)
- goLong := true
- inLong := true
- if inLong and shortCondition and strategy.equity > 0 and not goLong[1] and not na(realOpen)
- goShort := true
- inLong := false
- // ————— Enter/Exit trades at chart price as usual, but also fetch real market prices.
- var float chartFill = na
- var float marketFill = na
- var float previousChartFill = na
- var float previousMarketFill = na
- var float contracts = na
- // An order is emitted on this bar's close.
- onOrderBar = goLong or goShort
- // An order is filled on this bar's open.
- onFillBar = goLong[1] or goShort[1]
- // ————— Save previous fills before emitting order.
- if onOrderBar
- // Save previous fills.
- previousChartFill := chartFill
- previousMarketFill := marketFill
- // Emit order.
- if goLong
- strategy.entry("Long", strategy.long)
- else
- if goShort
- strategy.entry("Short", strategy.short)
- // ————— An order was filled on this bar; remember chart and market fills.
- if onFillBar
- chartFill := strategy.position_avg_price
- marketFill := realOpen
- contracts := abs(strategy.position_size)
- // ——————————————— Stats
- var marketNetProfit = 0.0
- chartProfit = 0.0
- chartPctProfit = 0.0
- marketProfit = 0.0
- marketPctProfit = 0.0
- marketTradeProfit = 0.0
- // ————— After the first fill, whenever a new fill occurs, update stats.
- if onFillBar and not na(previousChartFill)
- chartProfit := (chartFill - previousChartFill) * (goShort[1] ? 1 : -1)
- chartPctProfit := chartProfit / previousChartFill
- marketProfit := (marketFill - previousMarketFill) * (goShort[1] ? 1 : -1)
- marketPctProfit := marketProfit / previousMarketFill
- marketTradeProfit := contracts[1] * marketProfit
- marketNetProfit := marketNetProfit + marketTradeProfit
- // ————— Global numbers.
- chartNetProfit = strategy.netprofit
- deltaImpactPct = 100 * (marketNetProfit - chartNetProfit) / abs(chartNetProfit)
- // ——————————————— Plots
- // ————— Function to return green/red/blue color from value.
- f_color(_val) => _val > 0 ? color.green : _val < 0 ? color.red : color.blue
- invisible = #00000000
- // ————— Stats.
- // Global.
- plotchar(false, "═══════════ Cumulative Results", "", location.top, invisible)
- plotchar(f_round(chartNetProfit, 2), "Chart Net Profit", "", location.top, f_color(chartNetProfit))
- plotchar(f_round(marketNetProfit, 2), "Market Net Profit", "", location.top, f_color(marketNetProfit))
- plotchar(f_round(deltaImpactPct, 2), "Delta Net Profit %", "", location.top, f_color(deltaImpactPct))
- plotchar(false, "═══════════ Trade Fills", "", location.top, invisible)
- plotchar(onFillBar ? previousChartFill : na, "Chart Previous Fill", "", location.top)
- plotchar(onFillBar ? chartFill : na, "Chart Current Fill", "", location.top)
- plotchar(onFillBar ? previousMarketFill : na, "Market Previous Fill", "", location.top, f_color((previousMarketFill - previousChartFill) * (goShort[1] ? -1 : 1)))
- plotchar(onFillBar ? marketFill : na, "Market Current Fill", "", location.top, f_color((marketFill - chartFill) * (goShort[1] ? 1 : -1)))
- plotchar(false, "═══════════ Trade Results", "", location.top, invisible)
- plotchar(onFillBar ? f_round(100 * chartPctProfit, 2) : na, "Chart Trade Profit %", "", location.top, f_color(chartPctProfit))
- plotchar(onFillBar ? f_round(100 * marketPctProfit, 2) : na, "Market Trade Profit %", "", location.top, f_color(marketPctProfit))
- plotchar(onFillBar ? f_round(100 * (marketPctProfit - chartPctProfit), 2): na, "Delta Trade Profit %", "", location.top, f_color(marketPctProfit - chartPctProfit))
- plotchar(false, "══════════════════════", "", location.top, invisible)
- // ————— Mas
- plot(strategy == ST2 and showMas ? fastMa : na, "Fast MA", color.fuchsia)
- plot(strategy == ST2 and showMas ? slowMa : na, "Slow MA")
- // ————— Entry/Exit markers.
- plotchar(showMarkers and goLong, "goLong", "▲", location.bottom, color.green, size = size.tiny)
- plotchar(showMarkers and goShort, "goShort", "▼", location.top, color.red, size = size.tiny)
- // ————— Market Fills.
- plot(showFills and not change(marketFill) ? marketFill : na, "Market Fills", color.gray, style = plot.style_linebr, offset = -1)
- // ————— Market open.
- plot(showOpen ? realOpen : na, "Market open", color = color.gray, linewidth = 3, trackprice = true)
- // ————— Market candles.
- invisibleColor = color.new(color.white, 100)
- plotcandle(showCandles ? realOpen : na, showCandles ? realHigh : na, showCandles ? realLow : na, showCandles ? realClose : na, "Candles", realClose > realOpen ? color.orange : color.fuchsia, color.orange)
- // ————— Label.
- f_print(_txt) => var _lbl = label(na), label.delete(_lbl), _lbl := label.new(time + (time-time[1])*0, highest(20), _txt, xloc.bar_time, yloc.price, f_color(deltaImpactPct), size = size.normal)
- f_print(
- "Chart Net Profit (currency) = " + tostring(chartNetProfit, "###,###,###.00") +
- "\nMarket Net Profit (currency) = " + tostring(marketNetProfit, "###,###,###.00") +
- "\nDelta % = " + tostring(deltaImpactPct, "#.##") + " %")
- // Background on fill deltas.
- bgcolor(showBg and onFillBar and chartFill != marketFill ? color.red : na)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement