Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

275
LINES

< > BotCompany Repo | #1036603 // Mystery Strategy Mark II [Pine Script]

Document

1  
// "Mystery Strategy Mark II" / "Holy Grail for ETH"
2  
//
3  
// Preconfigured for ETH 30m on Bitget
4  
// Works on: ETH since 2022-12, AVAX since 2023-3
5  
//
6  
// (c) Stefan Reich
7  
//@version=5
8  
9  
strategy(shorttitle="Stefan Reich's Mystery Strategy Mark II v15", title="Stefan Reich's Mystery Strategy Mark II", 
10  
  overlay=true, 
11  
  commission_type=strategy.commission.percent, commission_value=0.07,
12  
  initial_capital = 100, default_qty_type=strategy.cash, default_qty_value=1000,
13  
  calc_on_every_tick = true,
14  
  max_boxes_count = 500, max_lines_count = 500, max_labels_count = 500)
15  
16  
// commission_value is an approximately correct estimate for fees+spread on ETH on typical exchanges (Bitget, Bybit)
17  
// This strategy opens few positions though, so fees+spread are not a big deal anyway.
18  
19  
startingDate = input.time(timestamp("8 Dec 2022"))
20  
useEndDate = input.bool(false, tooltip="Activate the end date below")
21  
endDate = input.time(timestamp("31 Dec 2023"), tooltip="When to end the backtest or execution (if checkbox above is set)")
22  
23  
enabledHoldSides = input.string(title="Hold side",  defval="Longs + Shorts", options=["Longs", "Shorts", "Longs + Shorts"])
24  
doLongs=str.contains(enabledHoldSides, "Longs")
25  
doShorts=str.contains(enabledHoldSides, "Shorts")
26  
27  
positionSize = input.float(1000, "Position size in $", tooltip="After leverage")
28  
intraCandleOperation = input.bool(false, tooltip="Open or close trades within a candle")
29  
30  
hotSwitchOkay = input.bool(true, tooltip="Can we kill a long by opening a short and vice versa?")
31  
macdType = input.string(title="MACD type", options=["Relative", "Absolute"], defval="Relative", tooltip="Relative=MACD normalized to asset price, Absolute=Normal MACD")
32  
useRelativeMACD = macdType == "Relative"
33  
34  
switchOnLongsAbove = input.float(5, step=1, tooltip="Allow longs only if MACD is above this level")
35  
switchOnShortsBelow = input.float(-5, step=1, tooltip="Allow shorts only if MACD is below this level")
36  
37  
enterThresholdLong = input.float(1, step=0.1, tooltip="MACD histogram value to enter a long at")
38  
exitThresholdLong = input.float(-1, step=0.1, tooltip="MACD histogram value to exit a long at")
39  
enterThresholdShort = input.float(-1, step=0.1, tooltip="MACD histogram value to enter a short at")
40  
exitThresholdShort = input.float(1, step=0.1, tooltip="MACD histogram value to exit a short at")
41  
42  
// Layout options
43  
44  
arrowOffset = input.float(10, group="Layout")
45  
initialOffset = input.float(10, group="Layout")
46  
equityOffset = input.float(10, group="Layout")
47  
topOffset = input.float(1, step=0.25, group="Layout")
48  
filesSlanted = input.bool(true, group="Layout")
49  
equityXOffset = input.int(10, tooltip="Move equity box to the right using this value", group="Layout")
50  
equityShort = input.bool(true, tooltip="Show only the new equity, no text", group="Layout")
51  
ieBoxColor = input.color(color.black, group="Layout")
52  
ieTextColor = input.color(color.blue, group="Layout")
53  
54  
// UTILITY FUNCTIONS
55  
56  
drift() =>
57  
    sumDrift = 0.0
58  
    for tradeNo = 0 to strategy.opentrades - 1
59  
        sumDrift += strategy.opentrades.size(tradeNo)
60  
    result = sumDrift
61  
62  
// macro logFloat(value, title) { plotchar(value, title, "", location.top) }
63  
    
64  
// MACD inputs
65  
fast_length = input(title="Fast Length", defval=25)
66  
slow_length = input(title="Slow Length", defval=100)
67  
macd_src = input(title="Source", defval=close)
68  
signal_length = input.int(title="Signal Smoothing",  minval = 1, defval = 9)
69  
sma_source = input.string(title="Oscillator MA Type",  defval="EMA", options=["SMA", "EMA"])
70  
sma_signal = input.string(title="Signal Line MA Type", defval="EMA", options=["SMA", "EMA"])
71  
72  
// Add more inputs down here to not mess up configurations of older versions
73  
74  
// MACD calculation
75  
76  
fast_ma = sma_source == "SMA" ? ta.sma(macd_src, fast_length) : ta.ema(macd_src, fast_length)
77  
slow_ma = sma_source == "SMA" ? ta.sma(macd_src, slow_length) : ta.ema(macd_src, slow_length)
78  
macd = fast_ma - slow_ma
79  
signal = sma_signal == "SMA" ? ta.sma(macd, signal_length) : ta.ema(macd, signal_length)
80  
hist = macd - signal
81  
82  
// Plot MACD
83  
plot(fast_ma, title="MACD Fast MA", color=color.yellow)
84  
plot(slow_ma, title="MACD Slow MA", color=color.blue)
85  
plotchar(macd, "MACD", "", location.top)
86  
87  
// Relative MACD values (in preparation for Mark II strategy)
88  
factor = 1000 / slow_ma
89  
relMACD = macd * factor
90  
relSignal = signal * factor
91  
relHist = (macd - signal) * factor
92  
93  
plotchar(relMACD, "Relative MACD", "", location.top) // logFloat
94  
plotchar(hist, "MACD Histogram", "", location.top) // logFloat
95  
plotchar(relHist, "Relative MACD Histogram", "", location.top) // logFloat
96  
97  
macdToUse = useRelativeMACD ? relMACD : macd
98  
histToUse = useRelativeMACD ? relHist : hist
99  
100  
if time >= startingDate and (not useEndDate or time <= endDate) and (intraCandleOperation or barstate.isconfirmed)
101  
    qty = positionSize/close
102  
103  
    if drift() < 0.0 and histToUse >= exitThresholdShort
104  
        strategy.close_all("MACD Green")
105  
    else if drift() > 0.0 and histToUse <= exitThresholdLong
106  
        strategy.close_all("MACD Red")
107  
108  
    if hotSwitchOkay or drift() == 0.0
109  
        if doLongs and histToUse >= enterThresholdLong and macdToUse >= switchOnLongsAbove
110  
            strategy.entry("Long", strategy.long, qty=qty)
111  
        else if doShorts and histToUse <= enterThresholdShort and macdToUse <= switchOnShortsBelow
112  
            strategy.entry("Short", strategy.short, qty=qty)
113  
114  
// PAINT TRADES & INITIAL EQUITY
115  
116  
plusMinusFix(s) =>
117  
    currency = " $"
118  
    str.startswith(s, "-") ? "-" + currency + str.substring(s, 1) : "+" + currency + s
119  
120  
formatProfit(profit) =>
121  
    plusMinusFix(str.tostring(profit, "#,##0.00"))
122  
123  
var int tradesPainted = 0
124  
var label initialEquityLabel = na
125  
126  
if na(initialEquityLabel) and time >= startingDate
127  
    ieText = "⬆\nWe invest\n$" + str.tostring(strategy.initial_capital, "#,##0") + "\nand start\nthe strategy!"
128  
    ieTime = startingDate-2*24*60*60*1000 // 2 days back
129  
    //ieY = close*(1-arrowOffset/100) // for style_label_center
130  
    //style=label.style_label_center
131  
    ieY = close*(1-initialOffset/100) // for style_label_up
132  
    //boxColor = color.black
133  
    //textColor = color.white
134  
    style = label.style_label_up
135  
    //style = label.style_triangleup
136  
    initialEquityLabel := label.new(ieTime, ieY, ieText, xloc=xloc.bar_time, color=ieBoxColor, textcolor=ieTextColor, tooltip="Initial equity", size=size.huge, style=style)
137  
138  
type PaintedTrade
139  
    array<line> lines
140  
    array<label> labels
141  
142  
drawTrade(bar1, bar2, price1, price2, profit, open, long) =>
143  
    t = PaintedTrade.new()
144  
    t.lines := array.new<line>()
145  
    t.labels := array.new<label>()
146  
    totalProfit = strategy.equity // -strategy.initial_capital
147  
    //line.new(bar1, price1, bar2, price2, xloc=xloc.bar_index, width = 10, color=color.black, style=line.style_arrow_right)
148  
    float y1 = price1*(1-arrowOffset/100)
149  
    float y2 = price2*(1-arrowOffset/100)
150  
    float top1 = price1*(1-topOffset/100)
151  
    float top2 = (open ? math.min(price2, low) : price2)*(1-topOffset/100)
152  
    color_solid = profit > 0 ? color.lime : color.red
153  
    color = color.new(color_solid, 50)
154  
155  
    if not filesSlanted
156  
        y1 := math.min(y1, y2)
157  
        y2 := y1
158  
159  
    array.push(t.lines, line.new(bar1, top1, bar1, y1, color=color, width=3))
160  
    array.push(t.lines, line.new(bar2, top2, bar2, y2, color=color, width=3))
161  
    array.push(t.lines, line.new(bar1, y1, bar2, y2, xloc=xloc.bar_index, width = 10, color=color)) //, style=line.style_arrow_right)
162  
163  
    ypos = 0.6
164  
    string profitText = formatProfit(profit)
165  
    yPrice = (price1+price2)/2
166  
    yBottom = (y1+y2)/2
167  
    yText = yPrice*(1-ypos)+yBottom*ypos
168  
    priceTooltip = "This " + (long ? "long" : "short") + " trade " + (str.startswith(profitText, "+") ? "made" : "lost") + " " + profitText
169  
    if open
170  
        profitText := "OPEN TRADE. " + str.replace(profitText, " $", "$")
171  
    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))
172  
173  
    // TODO: Show current equity even if there is no open trade
174  
    if not na(totalProfit)
175  
        //totalProfitText = "Total: " + formatProfit(totalProfit)
176  
        //totalProfitText = plusMinusFix(str.tostring(math.round(totalProfit)))
177  
        amount = "$" + str.tostring(totalProfit, "#,##0")
178  
        //totalProfitText = "⇒ " + amount
179  
        string format = na
180  
        if open
181  
            format := "Current\nequity:\n\n*"
182  
        else if equityShort
183  
            format := "*"
184  
        else if profit <= 0
185  
            format := "*\nleft"
186  
        else if tradesPainted > 1
187  
            format := "Now we\nhave\n*"
188  
        else
189  
            format := "Now we\nhave\n*."
190  
        totalProfitText = str.replace(format, "*", amount)
191  
        totalColor = totalProfit > 0 ? color.lime : color.orange
192  
        //yTotal = yBottom+(yBottom-yText)*4
193  
        float yTotal = y2
194  
        //xTotal = (bar1+bar2)/2
195  
        int xTotal = bar2+(open ? equityXOffset : 4)
196  
        days = math.round((time-startingDate)/1000.0/60/60/24+1)
197  
        tooltip = "New total equity (started with $" + str.tostring(strategy.initial_capital, "#,##0") + " - " + str.tostring(days) + " " + (days == 1 ? "day" : "days") + " ago)"
198  
        string style = label.style_label_left
199  
        string size = size.large
200  
        if open
201  
            style := label.style_label_center
202  
            xTotal := xTotal+15
203  
            yTotal := price2*(1-equityOffset/100)
204  
            size := size.huge
205  
        array.push(t.labels, label.new(xTotal, yTotal, totalProfitText, textcolor=color.new(totalColor, 25), style=style, color=color.black, size=size, tooltip=tooltip))
206  
207  
    t
208  
209  
method delete(PaintedTrade this) =>
210  
    for line in this.lines
211  
        line.delete(line)
212  
    for lbl in this.labels
213  
        label.delete(lbl)
214  
215  
while tradesPainted < strategy.closedtrades
216  
    bar1 = strategy.closedtrades.entry_bar_index(tradesPainted)
217  
    bar2 = strategy.closedtrades.exit_bar_index(tradesPainted)
218  
    price1 = strategy.closedtrades.entry_price(tradesPainted)
219  
    price2 = strategy.closedtrades.exit_price(tradesPainted)
220  
    //direction = strategy.closedtrades.size(tradesPainted)
221  
    profit = strategy.closedtrades.profit(tradesPainted)
222  
    long = strategy.closedtrades.size(tradesPainted) > 0
223  
    drawTrade(bar1, bar2, price1, price2, profit, false, long)
224  
    tradesPainted := tradesPainted+1
225  
226  
var PaintedTrade openTrade = na
227  
var label macdLabel = na
228  
229  
if not barstate.ishistory
230  
    if not na(openTrade)
231  
        openTrade.delete()
232  
233  
    if strategy.opentrades != 0
234  
        bar1 = strategy.opentrades.entry_bar_index(0)
235  
        bar2 = bar_index
236  
        price1 = strategy.opentrades.entry_price(0)
237  
        price2 = close
238  
        profit = strategy.opentrades.profit(0)
239  
        long = strategy.opentrades.size(0) > 0
240  
        openTrade := drawTrade(bar1, bar2, price1, price2, profit, true, long)
241  
242  
    // Show/update floating MACD value
243  
244  
    if not na(macdLabel)
245  
        label.delete(macdLabel)
246  
    macdValue = str.tostring(histToUse, "0.0#")
247  
    lowerBound = enterThresholdShort
248  
    upperBound = enterThresholdLong
249  
    macdPercent = (histToUse-lowerBound)/(upperBound-lowerBound)*100
250  
    percentText = str.tostring(macdPercent, "0") + "%"
251  
    tooltip = "MACD value: " + macdValue + " (" + percentText + " between close and open)"
252  
    macdColor = macdPercent >= 0 and macdPercent <= 100 ? color.blue : color.yellow
253  
    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)
254  
255  
// Show unrealized P&L in data window
256  
257  
unrealizedPnL = strategy.opentrades == 0 ? na : strategy.opentrades.profit(0)
258  
plotchar(unrealizedPnL, "Unrealized P&L", "")
259  
260  
// Record max drawdown of winning trades
261  
262  
var float drawdownInTrade = 0
263  
var int tradesClosed = 0
264  
var float maxDrawdown = 0
265  
if not na(unrealizedPnL)
266  
    drawdownInTrade := math.max(0, -unrealizedPnL, drawdownInTrade)
267  
268  
if tradesClosed < strategy.closedtrades
269  
    profit = strategy.closedtrades.profit(tradesClosed)
270  
    if profit > 0
271  
        maxDrawdown := math.max(maxDrawdown, drawdownInTrade)
272  
    drawdownInTrade := 0
273  
    tradesClosed := strategy.closedtrades
274  
275  
plotchar(maxDrawdown, "Max drawdown in a winning trade", "")

Author comment

Began life as a copy of #1036597

download  show line numbers   

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: 184 / 46
Referenced in: [show references]