Chiến lược giao dịch trung bình di chuyển kép tạo ra tín hiệu giao dịch bằng cách tính toán các đường trung bình di chuyển theo cấp số nhân (EMA) của các khung thời gian khác nhau để tạo ra EMA nhanh và EMA chậm, và quan sát các đường chéo vàng và đường chéo chết của chúng. Nó dài khi EMA nhanh vượt qua EMA chậm, và ngắn khi EMA nhanh vượt qua dưới EMA chậm. Chiến lược này nắm bắt các điểm đảo ngược xu hướng của các đường trung bình di chuyển và là một chiến lược theo xu hướng thường được sử dụng.
Các chỉ số cốt lõi của chiến lược trung bình di chuyển kép là EMA nhanh và EMA chậm. EMA nhanh có tham số mặc định là 12 ngày, trong khi EMA chậm có tham số mặc định là 26 ngày. Công thức cho trung bình di chuyển theo cấp số nhân là:
EMA (t) = (C (t) - EMA (t-1)) x SF + EMA (t-1)
Trong đó C (t) là giá đóng cửa ngày hôm nay, và SF là yếu tố làm mịn.
Các quy tắc giao dịch là:
Nhập các vị trí dài trên đường chéo vàng của đường EMA nhanh vượt qua đường EMA chậm từ dưới.
Nhập các vị trí ngắn trên đường chéo chết của EMA nhanh vượt qua dưới EMA chậm từ trên.
Các vị trí ra khỏi EMA.
Bằng cách nắm bắt các mô hình chéo của EMA, nó có thể phản ánh xu hướng thị trường và tăng lợi nhuận.
Là một chiến lược chỉ số kỹ thuật trưởng thành, chiến lược trung bình động kép có những điểm mạnh sau:
Lý luận của nó rất rõ ràng và dễ hiểu và thực hiện.
Nó cung cấp đánh giá rất chính xác về nguồn cung và nhu cầu thị trường và do đó có tỷ lệ thắng tương đối cao.
Nó lọc hiệu quả tiếng ồn thị trường và nắm bắt các xu hướng chính.
Nó có thể được áp dụng trên các công cụ và khung thời gian khác nhau.
Nó có thể được kết hợp với các chỉ số khác để làm giàu chiến lược.
Nó có hiệu quả sử dụng vốn cao cho giao dịch vốn lớn.
Ngoài ra còn có một số hạn chế của chiến lược:
Nó không phản ứng với các chuyển động thị trường mạnh mẽ như bán tháo thị trường gấu.
Nó có xu hướng tạo ra các tín hiệu sai thường xuyên và whipsaws trong các thị trường phạm vi bên.
Các thông số của nó cần tối ưu hóa trên các thị trường và khung thời gian khác nhau.
Nó không thể xác định mức độ đảo ngược thích hợp của xu hướng.
Các rủi ro có thể được giảm thiểu bằng cách điều chỉnh thời gian EMA, thêm các bộ lọc bổ sung vv để làm cho chiến lược mạnh mẽ hơn.
Chiến lược trung bình động kép có thể được cải thiện từ các khía cạnh sau:
Kết hợp chỉ số MACD để đánh giá sức mạnh xu hướng và tránh giao dịch sai.
Thêm khối lượng giao dịch để xác nhận tín hiệu đột phá thực sự.
Kết hợp với Bollinger Bands, mô hình nến cho các quy tắc nhập và xuất chính xác hơn.
Sử dụng các phương pháp học máy như LSTM để tự động tối ưu hóa các thông số để thích nghi tốt hơn.
Chiến lược giao dịch trung bình động kép nắm bắt các cơ hội giao dịch từ các đường chéo vàng EMA và đường chéo chết để xác định các điểm đảo ngược xu hướng cho lợi nhuận ổn định. Với những lợi thế đơn giản, hiệu quả vốn và dễ thực hiện, nó là một lựa chọn ưa thích cho người mới bắt đầu giao dịch thuật toán. Nhưng nó cũng có một số khuyết điểm như tạo ra tín hiệu sai. Cần giới thiệu nhiều chỉ số hơn để tối ưu hóa nó cho các thị trường và môi trường cụ thể. Nói chung, đây là một chiến lược chỉ số kỹ thuật rất thực tế và hữu ích.
/*backtest start: 2022-11-24 00:00:00 end: 2023-11-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/ // © antondmt //@version=5 strategy("Returns & Drawdowns Table", "R & DD", true, calc_on_every_tick = false, default_qty_type = strategy.percent_of_equity, default_qty_value = 100, process_orders_on_close = true) i_eq_to_dd = input.string("Compound Equity", "Mode", ["Simple Equity", "Compound Equity", "Drawdown"], group = "R & DD Table") i_precision = input.int(2, "Return Precision", group = "R & DD Table") i_headers_col = input.color(#D4D4D4, "Headers Color", group = "R & DD Table") i_headers_text_col = input.color(color.black, "Headers Text Color", group = "R & DD Table") i_pos_col = input.color(color.green, "Positive Color", group = "R & DD Table") i_neg_col = input.color(color.red, "Negative Color", group = "R & DD Table") i_zero_col = input.color(#DDDDDD, "Zero Color", group = "R & DD Table") i_cell_text_col = input.color(color.white, "Cell Text Color", group = "R & DD Table") // TIME { var month_times = array.new_int(0) // Array of all month times new_month = month(time) != month(time[1]) if(new_month or barstate.isfirst) array.push(month_times, time) var year_times = array.new_int(0) new_year = year(time) != year(time[1]) if (new_year or barstate.isfirst) array.push(year_times, time) //} // SIMPLE EQUITY CALCULATIONS { // Simple equity is strictly calculated from start to end of each month/year equity. There is no compound var monthly_simp_pnls = array.new_float(0) // Array of all monthly profits and losses var yearly_simp_pnls = array.new_float(0) if(i_eq_to_dd == "Simple Equity") var initial_monthly_equity = strategy.equity // Starting equity for each month cur_month_pnl = nz((strategy.equity - initial_monthly_equity) / initial_monthly_equity) // Current month's equity change if(new_month or barstate.isfirst) initial_monthly_equity := strategy.equity array.push(monthly_simp_pnls, cur_month_pnl) else array.set(monthly_simp_pnls, array.size(monthly_simp_pnls) - 1, cur_month_pnl) var initial_yearly_equity = strategy.equity cur_year_pnl = nz((strategy.equity - initial_yearly_equity) / initial_yearly_equity) if (new_year or barstate.isfirst) initial_yearly_equity := strategy.equity array.push(yearly_simp_pnls, cur_year_pnl) else array.set(yearly_simp_pnls, array.size(yearly_simp_pnls) - 1, cur_year_pnl) // } // COMPOUND EQUITY CALCULATIONS { // Compound equity is strictly calculated based on equity state from the beginning of time until the end of each month/year equity. It shows the exact equity movement through time var monthly_comp_pnls = array.new_float(0) // Array of all monthly profits and losses var yearly_comp_pnls = array.new_float(0) if(i_eq_to_dd == "Compound Equity") var initial_equity = strategy.equity cur_month_pnl = nz((strategy.equity - initial_equity) / initial_equity) // Current month's equity change if(new_month or barstate.isfirst) array.push(monthly_comp_pnls, cur_month_pnl) else array.set(monthly_comp_pnls, array.size(monthly_comp_pnls) - 1, cur_month_pnl) cur_year_pnl = nz((strategy.equity - initial_equity) / initial_equity) if (new_year or barstate.isfirst) array.push(yearly_comp_pnls, cur_year_pnl) else array.set(yearly_comp_pnls, array.size(yearly_comp_pnls) - 1, cur_year_pnl) // } // DRAWDOWN CALCULATIONS { // Drawdowns are calculated from highest equity to lowest trough for the month/year var monthly_dds = array.new_float(0) // Array of all monthly drawdowns var yearly_dds = array.new_float(0) if (i_eq_to_dd == "Drawdown") total_equity = strategy.equity - strategy.openprofit var cur_month_dd = 0.0 var m_ATH = total_equity // Monthly All-Time-High (ATH). It is reset each month m_ATH := math.max(total_equity, nz(m_ATH[1])) m_drawdown = -math.abs(total_equity / m_ATH * 100 - 100) / 100 // Drawdown at current bar if(m_drawdown < cur_month_dd) cur_month_dd := m_drawdown if(new_month or barstate.isfirst) cur_month_dd := 0.0 m_ATH := strategy.equity - strategy.openprofit array.push(monthly_dds, 0) else array.set(monthly_dds, array.size(monthly_dds) - 1, cur_month_dd) var cur_year_dd = 0.0 var y_ATH = total_equity y_ATH := math.max(total_equity, nz(y_ATH[1])) y_drawdown = -math.abs(total_equity / y_ATH * 100 - 100) / 100 if(y_drawdown < cur_year_dd) cur_year_dd := y_drawdown if (new_year or barstate.isfirst) cur_year_dd := 0.0 y_ATH := strategy.equity - strategy.openprofit array.push(yearly_dds, 0) else array.set(yearly_dds, array.size(yearly_dds) - 1, cur_year_dd) // } // TABLE LOGIC { var main_table = table(na) table.clear(main_table, 0, 0, 13, new_year ? array.size(year_times) - 1 : array.size(year_times)) main_table := table.new(position.bottom_right, columns = 14, rows = array.size(year_times) + 1, border_width = 1) t_set_headers() => // Sets time headers of the table // Set month headers table.cell(main_table, 0, 0, "", text_color = i_headers_text_col, bgcolor = i_headers_col) table.cell(main_table, 1, 0, "Jan", text_color = i_headers_text_col, bgcolor = i_headers_col) table.cell(main_table, 2, 0, "Feb", text_color = i_headers_text_col, bgcolor = i_headers_col) table.cell(main_table, 3, 0, "Mar", text_color = i_headers_text_col, bgcolor = i_headers_col) table.cell(main_table, 4, 0, "Apr", text_color = i_headers_text_col, bgcolor = i_headers_col) table.cell(main_table, 5, 0, "May", text_color = i_headers_text_col, bgcolor = i_headers_col) table.cell(main_table, 6, 0, "Jun", text_color = i_headers_text_col, bgcolor = i_headers_col) table.cell(main_table, 7, 0, "Jul", text_color = i_headers_text_col, bgcolor = i_headers_col) table.cell(main_table, 8, 0, "Aug", text_color = i_headers_text_col, bgcolor = i_headers_col) table.cell(main_table, 9, 0, "Sep", text_color = i_headers_text_col, bgcolor = i_headers_col) table.cell(main_table, 10, 0, "Oct", text_color = i_headers_text_col, bgcolor = i_headers_col) table.cell(main_table, 11, 0, "Nov", text_color = i_headers_text_col, bgcolor = i_headers_col) table.cell(main_table, 12, 0, "Dec", text_color = i_headers_text_col, bgcolor = i_headers_col) table.cell(main_table, 13, 0, str.tostring(i_eq_to_dd), text_color = i_headers_text_col, bgcolor = i_headers_col) // Set year headers for i = 0 to array.size(year_times) - 1 table.cell(main_table, 0, i + 1, str.tostring(year(array.get(year_times, i))), text_color = i_headers_text_col, bgcolor = i_headers_col) t_set_months() => // Sets inner monthly data of the table display_array = switch i_eq_to_dd "Simple Equity" => monthly_simp_pnls "Compound Equity" => monthly_comp_pnls => monthly_dds for i = 0 to array.size(month_times) - 1 m_row = year(array.get(month_times, i)) - year(array.get(year_times, 0)) + 1 m_col = month(array.get(month_times, i)) m_color = array.get(display_array, i) == 0 ? color.new(i_zero_col, transp = 30) : array.get(display_array, i) > 0 ? color.new(i_pos_col, transp = 30) : color.new(i_neg_col, transp = 30) table.cell(main_table, m_col, m_row, str.tostring(math.round(array.get(display_array, i) * 100, i_precision)), bgcolor = m_color, text_color = i_cell_text_col) t_set_years() => // Sets inner yearly data of the table display_array = switch i_eq_to_dd "Simple Equity" => yearly_simp_pnls "Compound Equity" => yearly_comp_pnls => yearly_dds for i = 0 to array.size(year_times) - 1 y_color = array.get(display_array, i) == 0 ? color.new(i_zero_col, transp = 30) : array.get(display_array, i) > 0 ? color.new(i_pos_col, transp = 20) : color.new(i_neg_col, transp = 20) table.cell(main_table, 13, i + 1, str.tostring(math.round(array.get(display_array, i) * 100, i_precision)), bgcolor = y_color, text_color = i_cell_text_col) t_set_headers() t_set_months() t_set_years() // } // PLACE YOUR STRATEGY CODE HERE { // This is a sample code of a working strategy to show the table in action fastLength = input(12) slowlength = input(26) MACDLength = input(9) MACD = ta.ema(close, fastLength) - ta.ema(close, slowlength) aMACD = ta.ema(MACD, MACDLength) delta = MACD - aMACD if (ta.crossover(delta, 0)) strategy.entry("MacdLE", strategy.long, comment = "MacdLE") if (ta.crossunder(delta, 0)) strategy.entry("MacdSE", strategy.short, comment = "MacdSE") // }