Advertisement
PineCoders

Backtesting on Non-Standard Charts: Caution! - PineCoders FA

Oct 27th, 2019
259
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.38 KB | None | 0 0
  1. //@version=4
  2. //@author=LucF, for PineCoders
  3.  
  4. // Backtesting on Non-Standard Charts: Caution! - PineCoders FAQ
  5. // v1.0, 2019.10.27 20:12 — LucF
  6.  
  7. // PineCoders, Tools and ideas for all Pine coders: pinecoders.com
  8.  
  9. // This script is a strategy intended to show the results of backtesting
  10. // on non-standard chart types using chart (normal backtesting) prices vs market prices.
  11.  
  12. // PineCoders FAQ & Code question linking here:
  13. // This indicator's TradingView page:
  14.  
  15. 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)
  16.  
  17. // ——————————————— Functions
  18. // ————— Round prices to tick size.
  19. f_roundToTick( _price) => round(_price / syminfo.mintick) * syminfo.mintick
  20. // ————— Round values to given decimal places.
  21. f_round( _val, _decimals) =>
  22. _p = pow(10, _decimals)
  23. round(abs(_val) * _p) / _p * sign(_val)
  24.  
  25. // ——————————————— Inputs
  26. ST1 = "Second reversal bar"
  27. ST2 = "MA crosses"
  28. strategy = input(ST2, "Strategy used", options = [ST1, ST2])
  29. useHA = input(false, "Use Heikin Ashi instead of chart prices to calculate strategy")
  30. noRepaint = input(true, "No repainting (delays signals one bar)")
  31. showMas = input(false, "Show MAs")
  32. showMarkers = input(false, "Show entry/exit markers")
  33. showFills = input(true, "Show market fills")
  34. showCandles = input(false, "Show market price candlesticks")
  35. showOpen = input(false, "Show market open as line")
  36. showBg = input(false, "Show background on fill delta")
  37. fastLength = input(5, "Fast MA length", minval = 2)
  38. slowLength = input(25, "Slow MA length", minval = 3)
  39. src = input(close, "Source")
  40.  
  41.  
  42. // ——————————————— Get market, HA and chart prices, all rounded to tick.
  43. // ————— Fetch underlying normal bar (market) prices.
  44. // "lookahead" must be used so that the correct price is retrieved when fetching prices from a chart
  45. // where there is no one-to-one correspondence between the time of non-standard bars and normal market underlying bars.
  46. ticker = tickerid(syminfo.prefix, syminfo.ticker)
  47. realOpen = security(ticker, timeframe.period, open, lookahead = barmerge.lookahead_on)
  48. realHigh = security(ticker, timeframe.period, high, lookahead = barmerge.lookahead_on)
  49. realLow = security(ticker, timeframe.period, low, lookahead = barmerge.lookahead_on)
  50. realClose = security(ticker, timeframe.period, close, lookahead = barmerge.lookahead_on)
  51. // ————— Calculate Heikin Ashi prices.
  52. // Full precision for HA calcs.
  53. haClose_ = 0.0
  54. haOpen_ = 0.0
  55. haClose_ := avg(realOpen, realHigh, realLow, realClose)
  56. haOpen_ := avg(nz(haOpen_[1], realOpen), nz(haClose_[1], realClose))
  57. haHigh_ = max(max(realHigh, haOpen_), haClose_)
  58. haLow_ = min(min(realLow, haOpen_), haClose_)
  59. // Display precision for rest of indicator calcs.
  60. haOpen = f_roundToTick(haOpen_)
  61. haHigh = f_roundToTick(haHigh_)
  62. haLow = f_roundToTick(haLow_)
  63. haClose = f_roundToTick(haClose_)
  64. // ————— Get current chart prices rounded to tick.
  65. chartOpen = f_roundToTick(open)
  66. chartHigh = f_roundToTick(high)
  67. chartLow = f_roundToTick(low)
  68. chartClose = f_roundToTick(close)
  69. // ————— Get prices used for calculations from HA or chart.
  70. calcOpen = useHA ? haOpen : chartOpen
  71. calcClose = useHA ? haClose : chartClose
  72.  
  73.  
  74. // ——————————————— Calculations and conditions
  75. // ————— When not repainting, we will reference bars one bar back.
  76. idx = noRepaint ? 1 : 0
  77. // ————— Long/Short conditions.
  78. upBar = calcClose > calcOpen
  79. fastMa = sma(calcClose, fastLength)
  80. slowMa = sma(calcClose, slowLength)
  81. longCondition = strategy == ST1 ? upBar[idx] and upBar[idx + 1] and not upBar[idx + 2] : crossover(fastMa, slowMa)[idx]
  82. shortCondition = strategy == ST1 ? not upBar[idx] and not upBar[idx + 1] and upBar[idx + 2] : crossunder(fastMa, slowMa)[idx]
  83.  
  84. // ——————————————— Entries & Exits
  85. // ————— 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.
  86. var inLong = false
  87. // ————— We re-initialize these vars to false every bar because we only want them true for one bar when the order must be given.
  88. goLong = false
  89. goShort = false
  90. // ————— We will only emit an order if our trigger condition occurs and:
  91. // - we are in a trade in the opposite direction,
  92. // - there is some equity left,
  93. // - we have not issued an order on the previous bar (because we need to process trade transitions),
  94. // - we have underlying real market data.
  95. if not inLong and longCondition and strategy.equity > 0 and not goShort[1] and not na(realOpen)
  96. goLong := true
  97. inLong := true
  98. if inLong and shortCondition and strategy.equity > 0 and not goLong[1] and not na(realOpen)
  99. goShort := true
  100. inLong := false
  101. // ————— Enter/Exit trades at chart price as usual, but also fetch real market prices.
  102. var float chartFill = na
  103. var float marketFill = na
  104. var float previousChartFill = na
  105. var float previousMarketFill = na
  106. var float contracts = na
  107. // An order is emitted on this bar's close.
  108. onOrderBar = goLong or goShort
  109. // An order is filled on this bar's open.
  110. onFillBar = goLong[1] or goShort[1]
  111. // ————— Save previous fills before emitting order.
  112. if onOrderBar
  113. // Save previous fills.
  114. previousChartFill := chartFill
  115. previousMarketFill := marketFill
  116. // Emit order.
  117. if goLong
  118. strategy.entry("Long", strategy.long)
  119. else
  120. if goShort
  121. strategy.entry("Short", strategy.short)
  122. // ————— An order was filled on this bar; remember chart and market fills.
  123. if onFillBar
  124. chartFill := strategy.position_avg_price
  125. marketFill := realOpen
  126. contracts := abs(strategy.position_size)
  127.  
  128.  
  129. // ——————————————— Stats
  130. var marketNetProfit = 0.0
  131. chartProfit = 0.0
  132. chartPctProfit = 0.0
  133. marketProfit = 0.0
  134. marketPctProfit = 0.0
  135. marketTradeProfit = 0.0
  136. // ————— After the first fill, whenever a new fill occurs, update stats.
  137. if onFillBar and not na(previousChartFill)
  138. chartProfit := (chartFill - previousChartFill) * (goShort[1] ? 1 : -1)
  139. chartPctProfit := chartProfit / previousChartFill
  140. marketProfit := (marketFill - previousMarketFill) * (goShort[1] ? 1 : -1)
  141. marketPctProfit := marketProfit / previousMarketFill
  142. marketTradeProfit := contracts[1] * marketProfit
  143. marketNetProfit := marketNetProfit + marketTradeProfit
  144. // ————— Global numbers.
  145. chartNetProfit = strategy.netprofit
  146. deltaImpactPct = 100 * (marketNetProfit - chartNetProfit) / abs(chartNetProfit)
  147.  
  148.  
  149. // ——————————————— Plots
  150. // ————— Function to return green/red/blue color from value.
  151. f_color(_val) => _val > 0 ? color.green : _val < 0 ? color.red : color.blue
  152. invisible = #00000000
  153. // ————— Stats.
  154. // Global.
  155. plotchar(false, "═══════════ Cumulative Results", "", location.top, invisible)
  156. plotchar(f_round(chartNetProfit, 2), "Chart Net Profit", "", location.top, f_color(chartNetProfit))
  157. plotchar(f_round(marketNetProfit, 2), "Market Net Profit", "", location.top, f_color(marketNetProfit))
  158. plotchar(f_round(deltaImpactPct, 2), "Delta Net Profit %", "", location.top, f_color(deltaImpactPct))
  159. plotchar(false, "═══════════ Trade Fills", "", location.top, invisible)
  160. plotchar(onFillBar ? previousChartFill : na, "Chart Previous Fill", "", location.top)
  161. plotchar(onFillBar ? chartFill : na, "Chart Current Fill", "", location.top)
  162. plotchar(onFillBar ? previousMarketFill : na, "Market Previous Fill", "", location.top, f_color((previousMarketFill - previousChartFill) * (goShort[1] ? -1 : 1)))
  163. plotchar(onFillBar ? marketFill : na, "Market Current Fill", "", location.top, f_color((marketFill - chartFill) * (goShort[1] ? 1 : -1)))
  164. plotchar(false, "═══════════ Trade Results", "", location.top, invisible)
  165. plotchar(onFillBar ? f_round(100 * chartPctProfit, 2) : na, "Chart Trade Profit %", "", location.top, f_color(chartPctProfit))
  166. plotchar(onFillBar ? f_round(100 * marketPctProfit, 2) : na, "Market Trade Profit %", "", location.top, f_color(marketPctProfit))
  167. plotchar(onFillBar ? f_round(100 * (marketPctProfit - chartPctProfit), 2): na, "Delta Trade Profit %", "", location.top, f_color(marketPctProfit - chartPctProfit))
  168. plotchar(false, "══════════════════════", "", location.top, invisible)
  169. // ————— Mas
  170. plot(strategy == ST2 and showMas ? fastMa : na, "Fast MA", color.fuchsia)
  171. plot(strategy == ST2 and showMas ? slowMa : na, "Slow MA")
  172. // ————— Entry/Exit markers.
  173. plotchar(showMarkers and goLong, "goLong", "▲", location.bottom, color.green, size = size.tiny)
  174. plotchar(showMarkers and goShort, "goShort", "▼", location.top, color.red, size = size.tiny)
  175. // ————— Market Fills.
  176. plot(showFills and not change(marketFill) ? marketFill : na, "Market Fills", color.gray, style = plot.style_linebr, offset = -1)
  177. // ————— Market open.
  178. plot(showOpen ? realOpen : na, "Market open", color = color.gray, linewidth = 3, trackprice = true)
  179. // ————— Market candles.
  180. invisibleColor = color.new(color.white, 100)
  181. plotcandle(showCandles ? realOpen : na, showCandles ? realHigh : na, showCandles ? realLow : na, showCandles ? realClose : na, "Candles", realClose > realOpen ? color.orange : color.fuchsia, color.orange)
  182. // ————— Label.
  183. 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)
  184. f_print(
  185. "Chart Net Profit (currency) = " + tostring(chartNetProfit, "###,###,###.00") +
  186. "\nMarket Net Profit (currency) = " + tostring(marketNetProfit, "###,###,###.00") +
  187. "\nDelta % = " + tostring(deltaImpactPct, "#.##") + " %")
  188. // Background on fill deltas.
  189. bgcolor(showBg and onFillBar and chartFill != marketFill ? color.red : na)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement