
Ý tưởng chính của chiến lược này là xác định hướng xu hướng trên các khung thời gian lớn hơn và tìm điểm đột phá vào các khung thời gian nhỏ hơn, trong khi điểm dừng lại là theo dõi đường trung bình di chuyển trên các khung thời gian lớn hơn.
Chiến lược này được đánh giá dựa trên ba chỉ số chính:
Thứ nhất, tính toán trung bình di chuyển đơn giản X ngày của một chu kỳ dài (như đường hằng ngày) và chỉ cho phép mua khi trung bình di chuyển đó ở vị trí giá. Đ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 biến động giao dịch.
Thứ hai, tính toán giá Swing High cao nhất trong một chu kỳ ngắn hơn (ví dụ như 5 ngày), kích hoạt tín hiệu mua khi giá vượt qua mức cao nhất đó. Ở đây kết hợp với tham số chu kỳ lb để tìm điểm phá vỡ phù hợp.
Thứ ba, thiết lập một đường dừng lỗ. Sau khi vào vị trí, đường dừng lỗ sẽ bị khóa ở mức giá thấp nhất của lbStop trong một khoảng thời gian nhất định từ mức thấp gần nhất. Đồng thời thiết lập một đường trung bình di chuyển (như đường giao dịch 10 ngày EMA) làm cơ chế thoát ra khỏi vị trí khi giá thấp hơn đường trung bình di chuyển đó.
Chiến lược này cũng đặt giá trị ATR để tránh mua các điểm mở rộng quá mức. Ngoài ra còn có các điều kiện phụ trợ khác như phạm vi thời gian đo lường lại.
Sự tương tác của ba chỉ số trên tạo nên logic cốt lõi của chiến lược này.
Đây là một chiến lược theo dõi đột phá với một số ưu điểm:
Sử dụng hai khung thời gian để tránh bị mắc kẹt trong các đột phá giả của thị trường lắc. Khung thời gian dài để đánh giá xu hướng tổng thể, khung thời gian ngắn để tìm điểm vào cụ thể.
Sử dụng điểm đột phá hình thành từ swing high, loại đột phá này có một số tính quán tính và dễ bị theo dõi. Đồng thời xem lại tham số lb có thể được điều chỉnh để tìm kiếm đột phá thực sự hiệu quả.
Hạn chế thiệt hại nghiêm ngặt hơn, theo dõi mức thấp gần đây và để lại một khoảng cách đệm nhất định để tránh bị đóng cửa.
Sử dụng trung bình di chuyển như một cơ chế thoát, có thể dừng lại một cách linh hoạt tùy theo tình hình.
Chỉ số ATR tránh nguy cơ phát hành quá mức.
Có thể thiết lập các kết hợp tham số khác nhau để kiểm tra hiệu quả, có nhiều không gian tối ưu hóa.
Chiến lược này cũng có một số rủi ro:
Khi giá dao động lên và xuống gần đường trung bình di chuyển, dễ bị chuyển đổi vào và ra vị trí nhiều lần. Khi đó, bạn có nguy cơ phải trả phí cao hơn.
Khi phá vỡ điểm mua gần với đường trung bình di chuyển, sẽ có rủi ro rút lui lớn hơn. Đây là đặc điểm của chính chiến lược.
Khi thị trường không có xu hướng rõ ràng, thời gian giữ vị thế có thể quá dài, đối mặt với rủi ro thời gian.
Cần thiết lập các tham số ATR hợp lý. ATR quá nhỏ sẽ làm giảm hiệu quả lọc, quá lớn sẽ làm giảm cơ hội nhập.
Cần kiểm tra ảnh hưởng của các tham số lb khác nhau đối với kết quả. Các tham số quá lớn có thể bỏ lỡ một số cơ hội, và các tham số quá nhỏ có thể nhận diện các đột phá giả.
Phương pháp giải quyết rủi ro:
Chiến lược này cũng có thể được tối ưu hóa từ các khía cạnh sau:
Kiểm tra các kết hợp tham số trung bình di chuyển khác nhau để tìm tham số tối ưu.
Thử các thiết lập tham số ATR khác nhau để cân bằng cơ hội nhập và kiểm soát rủi ro.
Tối ưu hóa các tham số lb của chu kỳ xem lại để xác định đột phá hiệu quả hơn.
Cố gắng thiết lập dừng động, kiểm soát rủi ro theo tỷ lệ biến động và rút tiền.
Kết hợp các yếu tố khác như chỉ số khối lượng giao dịch để đánh giá hiệu quả của đột phá.
Phát triển các phương pháp để tìm các điểm cực đoan như <, <, > để tham khảo.
Cố gắng Machine Learning để đào tạo các tham số để có được tham số tối ưu
Chiến lược tổng thể là một chiến lược theo dõi đột phá điển hình. Quyết định khung thời gian kép, Swing High xác định cơ chế thoát ra hai bảo hiểm nhập cảnh, dừng lỗ và trung bình di chuyển, tạo thành một hệ thống logic hoàn chỉnh. Các đặc điểm rủi ro và lợi nhuận của chiến lược được xác định rõ ràng, phù hợp với các nhà đầu tư theo dõi loại đường dài trung bình. Mặc dù có một số rủi ro, nhưng có thể giảm mức độ rủi ro bằng cách tối ưu hóa tham số và tối ưu hóa quy tắc.
/*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.