Ý tưởng chính của chiến lược này là xác định hướng xu hướng trên một khung thời gian lớn hơn và tìm các điểm đột phá để nhập vào một khung thời gian nhỏ hơn.
Chiến lược này chủ yếu dựa trên ba chỉ số để đánh giá.
Đầu tiên, tính toán một chu kỳ dài hơn (chẳng hạn như hàng ngày) trung bình di chuyển đơn giản X ngày. Chỉ cho phép mua khi giá trên mức trung bình di chuyển này. Điều này có thể được sử dụng để xác định hướng xu hướng tổng thể và tránh các giai đoạn dao động giao dịch.
Thứ hai, tính toán giá cao nhất Swing High trong một chu kỳ ngắn hơn (chẳng hạn như 5 ngày). Khi giá vượt qua mức giá cao nhất này, một tín hiệu mua được kích hoạt.
Thứ ba, thiết lập một đường dừng lỗ. Sau khi nhập vào vị trí, đường dừng lỗ được khóa ở mức giá thấp nhất trong một khoảng thời gian nhất định lbStop xa điểm thấp nhất gần đây. Đồng thời, thiết lập một đường trung bình động (như EMA 10 ngày trên hàng ngày) như một cơ chế thoát. Ra khỏi vị trí khi giá thấp hơn đường trung bình động này.
Chiến lược cũng thiết lập giá trị ATR để tránh mua điểm quá dài.
Việc đánh giá sự tương tác của ba chỉ số trên tạo thành logic cốt lõi của chiến lược này.
Là một chiến lược theo dõi đột nhập, nó có những lợi thế sau:
Sử dụng hai khung thời gian để tránh bị mắc kẹt trong các vụ phá vỡ giả trong thị trường dao động. khung thời gian dài hơn xác định xu hướng tổng thể, và khung thời gian ngắn hơn tìm ra các điểm nhập cụ thể.
Sử dụng các điểm đột phá được hình thành bằng cách lắc cao. Loại đột phá này có quán tính nhất định và dễ dàng hình thành theo dõi.
Phương pháp dừng lỗ tương đối nghiêm ngặt, theo dõi điểm thấp gần đây nhất với một khoảng cách đệm nhất định để tránh bị trầy xước.
Sử dụng đường trung bình động như một cơ chế thoát để linh hoạt lấy lợi nhuận theo điều kiện thị trường.
Chỉ số ATR tránh rủi ro đòn bẩy quá mức.
Các kết hợp tham số khác nhau có thể được thiết lập để thử nghiệm, với không gian tối ưu hóa lớn.
Chiến lược này cũng có một số rủi ro:
Khi giá dao động lên và xuống xung quanh đường trung bình động, dễ dàng chuyển qua lại giữa các vị trí nhập và ra.
Khi điểm phá vỡ gần đường trung bình động, có một rủi ro rút lui tương đối lớn.
Khi không có xu hướng rõ ràng trên thị trường, thời gian giữ có thể quá dài, đối mặt với rủi ro thời gian.
Các thông số ATR cần phải được thiết lập hợp lý. Nếu ATR quá nhỏ, hiệu ứng lọc sẽ yếu. Nếu quá lớn, cơ hội xâm nhập sẽ giảm.
Cần phải kiểm tra tác động của các thông số lb khác nhau đối với kết quả. Các thông số quá lớn có thể bỏ lỡ một số cơ hội, trong khi các thông số quá nhỏ có thể xác định các đột phá sai.
Giảm rủi ro:
Chiến lược cũng có thể được tối ưu hóa trong các khía cạnh sau:
Kiểm tra các kết hợp khác nhau của các thông số trung bình động để tìm các thông số tối ưu.
Hãy thử các thiết lập tham số ATR khác nhau để cân bằng cơ hội nhập cảnh và kiểm soát rủi ro.
Tối ưu hóa tham số thời gian xem lại lb để xác định các sự đột phá hiệu quả hơn.
Cố gắng xây dựng một lệnh dừng lỗ năng động dựa trên biến động và giảm để kiểm soát rủi ro.
Bao gồm các yếu tố khác như khối lượng giao dịch để xác định hiệu quả của việc phá vỡ.
Phát triển/
Cố gắng Machine Learning để đào tạo các thông số cho các thông số tối ưu
Nhìn chung, đây là một chiến lược theo dõi đột phá điển hình. Nếu xét theo khung thời gian kép, sử dụng Swing High để xác định thời gian vào, và sử dụng đường dừng lỗ và cơ chế thoát bảo hiểm hai mức trung bình động tạo thành một hệ thống hợp lý hoàn chỉnh. Các đặc điểm rủi ro và lợi nhuận của chiến lược này là rõ ràng, phù hợp với các nhà đầu tư theo dõi trung và dài hạn. Mặc dù có một số rủi ro nhất định, chúng có thể được giảm bằng cách tối ưu hóa các tham số và quy tắc. Chiến lược có nhiều chỗ để cải thiện.
/*backtest start: 2023-01-24 00:00:00 end: 2024-01-30 00:00:00 period: 1d basePeriod: 1h exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}] */ // This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/ // © millerrh // The intent of this strategy is to buy breakouts with a tight stop on smaller timeframes in the direction of the longer term trend. // Then use a trailing stop of a close below either the 10 MA or 20 MA (user choice) on that larger timeframe as the position // moves in your favor (i.e. whenever position price rises above the MA). // Option of using daily ATR as a measure of finding contracting ranges and ensuring a decent risk/reward. // (If the difference between the breakout point and your stop level is below a certain % of ATR, it could possibly find those consolidating periods.) //@version=4 strategy("Qullamaggie Breakout", overlay=true, initial_capital=10000, currency='USD', default_qty_type=strategy.percent_of_equity, default_qty_value=100, commission_type=strategy.commission.percent, commission_value=0.1) // === BACKTEST RANGE === Start = input(defval = timestamp("01 Jan 2019 06:00 +0000"), title = "Backtest Start Date", type = input.time) Finish = input(defval = timestamp("01 Jan 2100 00:00 +0000"), title = "Backtest End Date", type = input.time) // Inputs lb = input(defval = 3, title = "Lookback Period for Swing High", minval = 1, tooltip = "Lookback period for defining the breakout level.") lbStop = input(defval = 3, title = "Lookback Bars for Stop Level", minval = 1, tooltip = "Initial stop placement is the lowest low this many bars back. Allows for tighter stop placement than referencing swing lows.") htf = input(defval="D", title="Timeframe of Moving Averages", type=input.resolution, tooltip = "Allows you to set a different time frame for the moving averages. The default behavior is to identify good tightening setups on a larger timeframe (like daily) and enter the trade on a breakout occuring on a smaller timeframe, using the moving averages of the larger timeframe to trail your stop.") maType = input(defval="SMA", options=["EMA", "SMA"], title = "Moving Average Type") ma1Length = input(defval = 10, title = "1st Moving Average Length", minval = 1) ma2Length = input(defval = 20, title = "2nd Moving Average Length", minval = 1) ma3Length = input(defval = 50, title = "3rd Moving Average Length", minval = 1) useMaFilter = input(title = "Use 3rd Moving Average for Filtering?", type = input.bool, defval = true, tooltip = "Signals will be ignored when price is under this slowest moving average. The intent is to keep you out of bear periods and only buying when price is showing strength or trading with the longer term trend.") trailMaInput = input(defval="2nd Moving Average", options=["1st Moving Average", "2nd Moving Average"], title = "Trailing Stop") // MA Calculations ma(maType, src, length) => maType == "EMA" ? ema(src, length) : sma(src, length) //Ternary Operator (if maType equals EMA, then do ema calc, else do sma calc) ma1 = security(syminfo.tickerid, htf, ma(maType, close, ma1Length)) ma2 = security(syminfo.tickerid, htf, ma(maType, close, ma2Length)) ma3 = security(syminfo.tickerid, htf, ma(maType, close, ma3Length)) plot(ma1, color=color.purple, style=plot.style_line, title="MA1", linewidth=2, transp = 60) plot(ma2, color=color.yellow, style=plot.style_line, title="MA2", linewidth=2, transp = 60) plot(ma3, color=color.white, style=plot.style_line, title="MA3", linewidth=2, transp = 60) // === USE ATR FOR FILTERING === // The idea here is that you want to buy in a consolodating range for best risk/reward. So here you can compare the current distance between // support/resistance vs.the ATR and make sure you aren't buying at a point that is too extended from normal. useAtrFilter = input(title = "Use ATR for Filtering?", type = input.bool, defval = false, tooltip = "Signals will be ignored if the distance between support and resistance is larger than a user-defined percentage of Daily ATR. This allows the user to ensure they are not buying something that is too extended and instead focus on names that are consolidating more.") atrPerc = input(defval = 100, title = "% of Daily ATR Value", minval = 1) atrValue = security(syminfo.tickerid, "D", atr(14))*atrPerc*.01 // === PLOT SWING HIGH/LOW AND MOST RECENT LOW TO USE AS STOP LOSS EXIT POINT === // Change these values to adjust the look back and look forward periods for your swing high/low calculations pvtLenL = lb pvtLenR = lb // Get High and Low Pivot Points pvthi_ = pivothigh(high, pvtLenL, pvtLenR) pvtlo_ = pivotlow(low, pvtLenL, pvtLenR) // Force Pivot completion before plotting. Shunt = 1 //Wait for close before printing pivot? 1 for true 0 for flase maxLvlLen = 0 //Maximum Extension Length pvthi = pvthi_[Shunt] pvtlo = pvtlo_[Shunt] // Count How many candles for current Pivot Level, If new reset. counthi = barssince(not na(pvthi)) countlo = barssince(not na(pvtlo)) pvthis = fixnan(pvthi) pvtlos = fixnan(pvtlo) hipc = change(pvthis) != 0 ? na : color.maroon lopc = change(pvtlos) != 0 ? na : color.green // Display Pivot lines plot((maxLvlLen == 0 or counthi < maxLvlLen) ? pvthis : na, color=hipc, transp=0, linewidth=1, offset=-pvtLenR-Shunt, title="Top Levels") // plot((maxLvlLen == 0 or countlo < maxLvlLen) ? pvtlos : na, color=lopc, transp=0, linewidth=1, offset=-pvtLenR-Shunt, title="Bottom Levels") plot((maxLvlLen == 0 or counthi < maxLvlLen) ? pvthis : na, color=hipc, transp=0, linewidth=1, offset=0, title="Top Levels 2") // plot((maxLvlLen == 0 or countlo < maxLvlLen) ? pvtlos : na, color=lopc, transp=0, linewidth=1, offset=0, title="Bottom Levels 2") // BUY CONDITIONS stopLevelCalc = valuewhen(pvtlo_, low[pvtLenR], 0) //Stop Level at Swing Low buyLevel = valuewhen(pvthi_, high[pvtLenR], 0) //Buy level at Swing High plot(buyLevel, style=plot.style_line, color=color.blue, title = "Current Breakout Level", show_last=1, linewidth=1, transp=50, trackprice=true) // Conditions for entry and exit stopLevel = float(na) // Define stop level here as "na" so that I can reference it in the inPosition // variable and the ATR calculation before the stopLevel is actually defined. buyConditions = (useMaFilter ? buyLevel > ma3 : true) and (useAtrFilter ? (buyLevel - stopLevel[1]) < atrValue : true) // buySignal = high > buyLevel and buyConditions buySignal = crossover(high, buyLevel) and buyConditions trailMa = trailMaInput == "1st Moving Average" ? ma1 : ma2 sellSignal = crossunder(close, trailMa) // sellSignal = security(syminfo.tickerid, htf, close < trailMa) and security(syminfo.tickerid, htf, close[1] < trailMa) // STOP AND PRICE LEVELS inPosition = bool(na) inPosition := buySignal[1] ? true : sellSignal[1] ? false : low <= stopLevel[1] ? false : inPosition[1] lowDefine = lowest(low, lbStop) stopLevel := inPosition ? stopLevel[1] : lowDefine // plot(stopLevel) buyPrice = buyLevel buyPrice := inPosition ? buyPrice[1] : buyLevel plot(stopLevel, style=plot.style_line, color=color.orange, title = "Current Stop Level", show_last=1, linewidth=1, transp=50, trackprice=true) plot(inPosition ? stopLevel : na, style=plot.style_circles, color=color.orange, title = "Historical Stop Levels", transp=50, trackprice=false) // plot(buyPrice, style=plot.style_line, color=color.blue, linewidth=1, transp=50, trackprice=true) // (STRATEGY ONLY) Comment out for Study strategy.entry("Long", strategy.long, stop = buyLevel, when = buyConditions) strategy.exit("Exit Long", from_entry = "Long", stop=stopLevel[1]) if (low[1] > trailMa) strategy.close("Long", when = sellSignal) // if (low[1] > trailMa) // strategy.exit("Exit Long", from_entry = "Long", stop=trailMa) //to get this to work right, I need to reference highest highs instead of swing highs //because it can have me buy right back in after selling if the stop level is above the last registered swing high point.