// "Mystery Strategy Mark II" / "Holy Grail for ETH" // // Preconfigured for ETH 30m on Bitget // Works on: ETH since 2022-12, AVAX since 2023-3 // // (c) Stefan Reich //@version=5 strategy(shorttitle="Stefan Reich's Mystery Strategy Mark II v15", title="Stefan Reich's Mystery Strategy Mark II", overlay=true, commission_type=strategy.commission.percent, commission_value=0.07, initial_capital = 100, default_qty_type=strategy.cash, default_qty_value=1000, calc_on_every_tick = true, max_boxes_count = 500, max_lines_count = 500, max_labels_count = 500) // commission_value is an approximately correct estimate for fees+spread on ETH on typical exchanges (Bitget, Bybit) // This strategy opens few positions though, so fees+spread are not a big deal anyway. startingDate = input.time(timestamp("8 Dec 2022")) useEndDate = input.bool(false, tooltip="Activate the end date below") endDate = input.time(timestamp("31 Dec 2023"), tooltip="When to end the backtest or execution (if checkbox above is set)") enabledHoldSides = input.string(title="Hold side", defval="Longs + Shorts", options=["Longs", "Shorts", "Longs + Shorts"]) doLongs=str.contains(enabledHoldSides, "Longs") doShorts=str.contains(enabledHoldSides, "Shorts") positionSize = input.float(1000, "Position size in $", tooltip="After leverage") intraCandleOperation = input.bool(false, tooltip="Open or close trades within a candle") hotSwitchOkay = input.bool(true, tooltip="Can we kill a long by opening a short and vice versa?") macdType = input.string(title="MACD type", options=["Relative", "Absolute"], defval="Relative", tooltip="Relative=MACD normalized to asset price, Absolute=Normal MACD") useRelativeMACD = macdType == "Relative" switchOnLongsAbove = input.float(5, step=1, tooltip="Allow longs only if MACD is above this level") switchOnShortsBelow = input.float(-5, step=1, tooltip="Allow shorts only if MACD is below this level") enterThresholdLong = input.float(1, step=0.1, tooltip="MACD histogram value to enter a long at") exitThresholdLong = input.float(-1, step=0.1, tooltip="MACD histogram value to exit a long at") enterThresholdShort = input.float(-1, step=0.1, tooltip="MACD histogram value to enter a short at") exitThresholdShort = input.float(1, step=0.1, tooltip="MACD histogram value to exit a short at") // Layout options arrowOffset = input.float(10, group="Layout") initialOffset = input.float(10, group="Layout") equityOffset = input.float(10, group="Layout") topOffset = input.float(1, step=0.25, group="Layout") filesSlanted = input.bool(true, group="Layout") equityXOffset = input.int(10, tooltip="Move equity box to the right using this value", group="Layout") equityShort = input.bool(true, tooltip="Show only the new equity, no text", group="Layout") ieBoxColor = input.color(color.black, group="Layout") ieTextColor = input.color(color.blue, group="Layout") // UTILITY FUNCTIONS drift() => sumDrift = 0.0 for tradeNo = 0 to strategy.opentrades - 1 sumDrift += strategy.opentrades.size(tradeNo) result = sumDrift // macro logFloat(value, title) { plotchar(value, title, "", location.top) } // MACD inputs fast_length = input(title="Fast Length", defval=25) slow_length = input(title="Slow Length", defval=100) macd_src = input(title="Source", defval=close) signal_length = input.int(title="Signal Smoothing", minval = 1, defval = 9) sma_source = input.string(title="Oscillator MA Type", defval="EMA", options=["SMA", "EMA"]) sma_signal = input.string(title="Signal Line MA Type", defval="EMA", options=["SMA", "EMA"]) // Add more inputs down here to not mess up configurations of older versions // MACD calculation fast_ma = sma_source == "SMA" ? ta.sma(macd_src, fast_length) : ta.ema(macd_src, fast_length) slow_ma = sma_source == "SMA" ? ta.sma(macd_src, slow_length) : ta.ema(macd_src, slow_length) macd = fast_ma - slow_ma signal = sma_signal == "SMA" ? ta.sma(macd, signal_length) : ta.ema(macd, signal_length) hist = macd - signal // Plot MACD plot(fast_ma, title="MACD Fast MA", color=color.yellow) plot(slow_ma, title="MACD Slow MA", color=color.blue) plotchar(macd, "MACD", "", location.top) // Relative MACD values (in preparation for Mark II strategy) factor = 1000 / slow_ma relMACD = macd * factor relSignal = signal * factor relHist = (macd - signal) * factor plotchar(relMACD, "Relative MACD", "", location.top) // logFloat plotchar(hist, "MACD Histogram", "", location.top) // logFloat plotchar(relHist, "Relative MACD Histogram", "", location.top) // logFloat macdToUse = useRelativeMACD ? relMACD : macd histToUse = useRelativeMACD ? relHist : hist if time >= startingDate and (not useEndDate or time <= endDate) and (intraCandleOperation or barstate.isconfirmed) qty = positionSize/close if drift() < 0.0 and histToUse >= exitThresholdShort strategy.close_all("MACD Green") else if drift() > 0.0 and histToUse <= exitThresholdLong strategy.close_all("MACD Red") if hotSwitchOkay or drift() == 0.0 if doLongs and histToUse >= enterThresholdLong and macdToUse >= switchOnLongsAbove strategy.entry("Long", strategy.long, qty=qty) else if doShorts and histToUse <= enterThresholdShort and macdToUse <= switchOnShortsBelow strategy.entry("Short", strategy.short, qty=qty) // PAINT TRADES & INITIAL EQUITY plusMinusFix(s) => currency = " $" str.startswith(s, "-") ? "-" + currency + str.substring(s, 1) : "+" + currency + s formatProfit(profit) => plusMinusFix(str.tostring(profit, "#,##0.00")) var int tradesPainted = 0 var label initialEquityLabel = na if na(initialEquityLabel) and time >= startingDate ieText = "⬆\nWe invest\n$" + str.tostring(strategy.initial_capital, "#,##0") + "\nand start\nthe strategy!" ieTime = startingDate-2*24*60*60*1000 // 2 days back //ieY = close*(1-arrowOffset/100) // for style_label_center //style=label.style_label_center ieY = close*(1-initialOffset/100) // for style_label_up //boxColor = color.black //textColor = color.white style = label.style_label_up //style = label.style_triangleup initialEquityLabel := label.new(ieTime, ieY, ieText, xloc=xloc.bar_time, color=ieBoxColor, textcolor=ieTextColor, tooltip="Initial equity", size=size.huge, style=style) type PaintedTrade array<line> lines array<label> labels drawTrade(bar1, bar2, price1, price2, profit, open, long) => t = PaintedTrade.new() t.lines := array.new<line>() t.labels := array.new<label>() totalProfit = strategy.equity // -strategy.initial_capital //line.new(bar1, price1, bar2, price2, xloc=xloc.bar_index, width = 10, color=color.black, style=line.style_arrow_right) float y1 = price1*(1-arrowOffset/100) float y2 = price2*(1-arrowOffset/100) float top1 = price1*(1-topOffset/100) float top2 = (open ? math.min(price2, low) : price2)*(1-topOffset/100) color_solid = profit > 0 ? color.lime : color.red color = color.new(color_solid, 50) if not filesSlanted y1 := math.min(y1, y2) y2 := y1 array.push(t.lines, line.new(bar1, top1, bar1, y1, color=color, width=3)) array.push(t.lines, line.new(bar2, top2, bar2, y2, color=color, width=3)) array.push(t.lines, line.new(bar1, y1, bar2, y2, xloc=xloc.bar_index, width = 10, color=color)) //, style=line.style_arrow_right) ypos = 0.6 string profitText = formatProfit(profit) yPrice = (price1+price2)/2 yBottom = (y1+y2)/2 yText = yPrice*(1-ypos)+yBottom*ypos priceTooltip = "This " + (long ? "long" : "short") + " trade " + (str.startswith(profitText, "+") ? "made" : "lost") + " " + profitText if open profitText := "OPEN TRADE. " + str.replace(profitText, " $", "$") array.push(t.labels, label.new((bar1+bar2)/2, yText, profitText, textcolor=color.new(color_solid, 25), color=color.black, style=label.style_text_outline, size=size.huge, tooltip=priceTooltip)) // TODO: Show current equity even if there is no open trade if not na(totalProfit) //totalProfitText = "Total: " + formatProfit(totalProfit) //totalProfitText = plusMinusFix(str.tostring(math.round(totalProfit))) amount = "$" + str.tostring(totalProfit, "#,##0") //totalProfitText = "⇒ " + amount string format = na if open format := "Current\nequity:\n\n*" else if equityShort format := "*" else if profit <= 0 format := "*\nleft" else if tradesPainted > 1 format := "Now we\nhave\n*" else format := "Now we\nhave\n*." totalProfitText = str.replace(format, "*", amount) totalColor = totalProfit > 0 ? color.lime : color.orange //yTotal = yBottom+(yBottom-yText)*4 float yTotal = y2 //xTotal = (bar1+bar2)/2 int xTotal = bar2+(open ? equityXOffset : 4) days = math.round((time-startingDate)/1000.0/60/60/24+1) tooltip = "New total equity (started with $" + str.tostring(strategy.initial_capital, "#,##0") + " - " + str.tostring(days) + " " + (days == 1 ? "day" : "days") + " ago)" string style = label.style_label_left string size = size.large if open style := label.style_label_center xTotal := xTotal+15 yTotal := price2*(1-equityOffset/100) size := size.huge array.push(t.labels, label.new(xTotal, yTotal, totalProfitText, textcolor=color.new(totalColor, 25), style=style, color=color.black, size=size, tooltip=tooltip)) t method delete(PaintedTrade this) => for line in this.lines line.delete(line) for lbl in this.labels label.delete(lbl) while tradesPainted < strategy.closedtrades bar1 = strategy.closedtrades.entry_bar_index(tradesPainted) bar2 = strategy.closedtrades.exit_bar_index(tradesPainted) price1 = strategy.closedtrades.entry_price(tradesPainted) price2 = strategy.closedtrades.exit_price(tradesPainted) //direction = strategy.closedtrades.size(tradesPainted) profit = strategy.closedtrades.profit(tradesPainted) long = strategy.closedtrades.size(tradesPainted) > 0 drawTrade(bar1, bar2, price1, price2, profit, false, long) tradesPainted := tradesPainted+1 var PaintedTrade openTrade = na var label macdLabel = na if not barstate.ishistory if not na(openTrade) openTrade.delete() if strategy.opentrades != 0 bar1 = strategy.opentrades.entry_bar_index(0) bar2 = bar_index price1 = strategy.opentrades.entry_price(0) price2 = close profit = strategy.opentrades.profit(0) long = strategy.opentrades.size(0) > 0 openTrade := drawTrade(bar1, bar2, price1, price2, profit, true, long) // Show/update floating MACD value if not na(macdLabel) label.delete(macdLabel) macdValue = str.tostring(histToUse, "0.0#") lowerBound = enterThresholdShort upperBound = enterThresholdLong macdPercent = (histToUse-lowerBound)/(upperBound-lowerBound)*100 percentText = str.tostring(macdPercent, "0") + "%" tooltip = "MACD value: " + macdValue + " (" + percentText + " between close and open)" macdColor = macdPercent >= 0 and macdPercent <= 100 ? color.blue : color.yellow macdLabel := label.new(bar_index, close, percentText, tooltip=tooltip, yloc=yloc.abovebar, textcolor=macdColor, color=color.black, style=label.style_text_outline, size=size.large) // Show unrealized P&L in data window unrealizedPnL = strategy.opentrades == 0 ? na : strategy.opentrades.profit(0) plotchar(unrealizedPnL, "Unrealized P&L", "") // Record max drawdown of winning trades var float drawdownInTrade = 0 var int tradesClosed = 0 var float maxDrawdown = 0 if not na(unrealizedPnL) drawdownInTrade := math.max(0, -unrealizedPnL, drawdownInTrade) if tradesClosed < strategy.closedtrades profit = strategy.closedtrades.profit(tradesClosed) if profit > 0 maxDrawdown := math.max(maxDrawdown, drawdownInTrade) drawdownInTrade := 0 tradesClosed := strategy.closedtrades plotchar(maxDrawdown, "Max drawdown in a winning trade", "")
Began life as a copy of #1036597
Travelled to 1 computer(s): mqqgnosmbjvj
No comments. add comment
Snippet ID: | #1036603 |
Snippet name: | Mystery Strategy Mark II [Pine Script] |
Eternal ID of this version: | #1036603/1 |
Text MD5: | c933355dec24a3dc9d7085991d22f6b0 |
Author: | stefan |
Category: | pine script |
Type: | Document |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2023-04-24 19:35:04 |
Source code size: | 12451 bytes / 275 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 183 / 45 |
Referenced in: | -