Chiến lược chi phí trung bình năng động Chi phí trung bình đô la

Tác giả:ChaoZhang, Ngày: 2024-01-04 16:34:10
Tags:

img

Tổng quan

Chiến lược tổng hợp chi phí trung bình động DCA điều chỉnh động số lượng của mỗi vị trí mở. Vào đầu xu hướng, nó đầu tiên mở các vị trí nhỏ để xây dựng một vị trí. Khi độ sâu của việc hợp nhất tăng lên, nó dần dần tăng kích thước vị trí. Chiến lược sử dụng các hàm số nhân để tính mức giá dừng lỗ, và mở lại các lô mới khi được kích hoạt, có thể khiến chi phí giữ vị trí tiếp tục giảm theo cấp số nhân. Khi độ sâu tăng lên, chi phí của các vị trí có thể được giảm dần. Khi giá đảo ngược, việc lấy lợi nhuận lô cho phép lợi nhuận lớn hơn.

Chiến lược logic

Chiến lược này sử dụng một sự kết hợp đơn giản của tín hiệu bán quá mức RSI và thời gian trung bình động để xác định cơ hội nhập cảnh. Một lệnh nhập cảnh đầu tiên được gửi khi RSI giảm xuống dưới mức bán quá mức và giá đóng dưới mức trung bình động. Sau khi nhập cảnh đầu tiên, hàm số nhân tính toán tỷ lệ giảm giá cho các mức tiếp theo. Mỗi lần kích hoạt lệnh DCA, kích thước vị trí được tính lại để giữ số tiền bằng nhau cho mỗi mục nhập. Khi kích thước vị trí và chi phí thay đổi năng động, nó tạo ra hiệu ứng đòn bẩy.

Khi số lượng DCA tăng lên, chi phí nắm giữ trung bình tiếp tục giảm. Chỉ cần một sự phục hồi nhỏ là đủ để kiếm lợi nhuận từ mỗi vị trí. Sau khi các mục liên tục được gửi, một đường dừng lỗ được vẽ trên giá nắm giữ trung bình. Một khi giá vượt qua mức giá trung bình và đường dừng lỗ, tất cả các vị trí được đóng.

Ưu điểm lớn nhất là khi chi phí nắm giữ tiếp tục giảm, ngay cả trong quá trình hợp nhất, chi phí vẫn có thể được giảm tích lũy từng bước. Khi xu hướng đảo ngược, do chi phí nắm giữ thấp hơn nhiều so với giá thị trường, lợi nhuận lớn hơn nhiều có thể được thực hiện.

Rủi ro và khuyết điểm

Rủi ro lớn nhất là kích thước vị trí giới hạn ban đầu. Trong quá trình giảm liên tục, có thể có rủi ro dừng lỗ. Vì vậy, tỷ lệ giảm lỗ dừng cần phải được đặt hợp lý dựa trên sự thèm rủi ro cá nhân.

Ngoài ra, việc thiết lập mức dừng lỗ có hai cực đoan. Nếu quá lỏng lẻo, không có đủ retrace có thể được nắm bắt. Nhưng nếu quá chặt chẽ, xác suất bị dừng trong các điều chỉnh trung hạn tăng lên. Vì vậy, việc chọn mức dừng lỗ phù hợp theo các điều kiện thị trường khác nhau và sở thích rủi ro là rất quan trọng.

Nếu có quá nhiều mức DCA, khi giá tăng đáng kể, chi phí nắm giữ cực kỳ cao có thể ngăn chặn lỗ dừng hiệu quả. Vì vậy, các lớp tối đa của DCA cần phải được thiết lập hợp lý dựa trên tổng phân bổ vốn và chi phí cao nhất có thể chịu được.

Các đề xuất tối ưu hóa

  1. Tối ưu hóa các tín hiệu thời gian vào, bằng cách thử nghiệm các tham số và các kết hợp các chỉ số khác cho các tín hiệu tỷ lệ thắng cao hơn.

  2. Tối ưu hóa các cơ chế dừng lỗ, bằng cách thử nghiệm Λ trailing stop loss hoặc curve fitted trailing stop loss để có được kết quả tốt hơn.

  3. Tối ưu hóa các cách kiếm lợi nhuận. Các loại lợi nhuận kéo dài khác nhau có thể được kiểm tra để có cơ hội thoát tốt hơn và tổng lợi nhuận cao hơn.

  4. Thêm cơ chế chống chích. Đôi khi tín hiệu DCA có thể được kích hoạt lại ngay sau khi dừng mất mát. Một phạm vi chích có thể được thêm để tránh tái nhập tích cực ngay sau khi dừng lại.

Kết luận

Chiến lược này sử dụng RSI để xác định các mục nhập, cơ chế DCA dừng lỗ năng động theo cấp số nhân để điều chỉnh kích thước vị trí và chi phí trung bình theo cấp số nhân, để có được lợi thế giá trong quá trình hợp nhất. Các lĩnh vực tối ưu hóa chính tập trung vào tín hiệu nhập / ra, dừng lỗ và lấy lợi nhuận. Khái niệm cốt lõi của DCA theo cấp số nhân được thực hiện để chuyển chi phí nắm giữ xuống thấp hơn liên tục, do đó cung cấp nhiều không gian hơn trong quá trình hợp nhất, và đạt được lợi nhuận nhân khi xu hướng xuất hiện. Nhưng các tham số vẫn cần phải được thiết lập cẩn thận dựa trên kế hoạch phân bổ vốn để kiểm soát rủi ro vị trí tổng thể.


/*backtest
start: 2023-12-04 00:00:00
end: 2024-01-03 00:00:00
period: 1h
basePeriod: 15m
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///
// © A3Sh
//@version=5

// Study of a Simple RSI based, PA (priceaveraging) and DCA strategy that opens a new position everytime it hits a specified price level below the first entry.
// The first entry is opened when the specified rsi and moving average conditions are met. 
// The following DCA levels are calculated exponentially and set, starting with a specified % of price drop. 
// The disctance between the dca levels can be changed with the exponential scale.
// Each position closes individually when it reaches a specified take profit.
// The position can re-open again when it hits the price level again.
// Each time a position is closed and reopened, the average price drops a little.
// The position stays open until the first entry closes or when the price reaches the Stop level.
// When the price reaches the Stop level, all positions will close at once.

// The RSI and MA code for opening the entry is adapted from the Optimized RSI Buy the Dips strategy, by Coinrule.
// This code is used for study purposes, but any other low/ dip finding indicator can be used.
// https://www.tradingview.com/script/Pm1WAtyI-Optimized-RSI-Strategy-Buy-The-Dips-by-Coinrule/

// Dynamic DCA layers are inspired by the Backtesting 3commas DCA Bot v2, by rouxam
// This logic gives more flexibility because you can dyanically change the amount of dca entries.
// https://www.tradingview.com/script/8d6Auyst-Backtesting-3commas-DCA-Bot-v2/

// The use of for loops to (re)open and close different entries separately is based on the Simple_Pyramiding strategy.
// https://www.tradingview.com/script/t6cNLqDN-Simple-Pyramiding/


strategy('Simple_RSI+PA+DCA', overlay=true, pyramiding=20, initial_capital=500, calc_on_order_fills=true, default_qty_type=strategy.percent_of_equity, commission_type=strategy.commission.percent, commission_value=0.075, close_entries_rule='FIFO')

// Backtest Window //
start_time   = input(defval=timestamp("01 April 2021 20:00"), group = "Backtest Window", title="Start Time")
end_time     = input(defval=timestamp("01 Aug 2030 20:00"),   group = "Backtest Window", title="End Time")
window() => true

// Inputs //
takeProfit      = input.float  (3,           group = 'Risk',           title = 'Take Profit %', step=0.1)
takeProfitAll   = input.float  (6,           group = "Risk",           title = 'Close All %',   step=0.1)
posCount        = input.int    (8,           group = 'DCA Settings',   title = 'Max Amount of Entries')
increment       = input.float  (2,           group = 'DCA Settings',   title = 'Price Drop % to open First DCA Order', step=0.5)/100 
exponent_scale  = input.float  (1.4,         group = 'DCA Settings',   title = 'Exponential Scale DCA levels', step=0.1, minval=1.1) 
bar_lookback    = input.int    (4999,        group = 'DCA Settings',   title = 'Lines Bar Lookback', maxval = 4999)
plotMA          = input.bool   (false,       group = 'Moving Average', title = 'Plot Moving Average')
moving_average  = input.int    (100,         group = 'Moving Average', title = 'MA Length' )
rsiLengthInput  = input.int    (14,          group = 'RSI Settings',   title = "RSI Length", minval=1)
rsiSourceInput  = input.source (close,       group = 'RSI Settings',   title = 'Source')
overSold        = input.int    (29,          group = 'RSI Settings',   title = 'Oversold, Trigger to Enter First Position')

// variables //
var open_position    = true   // true when there are open positions
var entry_price      = 0.0    // the entry price of the first entry
var dca_price        = 0.0    // the price of the different dca layers
var int count        = 0      // bar counter since first open position
var int max_bar      = 0      // max bar buffer variable for DCA lines, stop lines, average price
var line dca_line    = na     // lines variable for creating dca lines

// arrays //
linesArray = array.new_float(posCount,na)  // array to store different dca price levels for creating the dca lines

// Create max bar buffer for DCA lines, Stop and average price lines //
max_bar := count >= bar_lookback ? bar_lookback : count

// Order size based on first entry and amount of DCA layers
q = (strategy.equity  / posCount + 1) / open


// Calculate Moving Averages
movingaverage_signal = ta.sma(close ,moving_average)
plot (plotMA ? movingaverage_signal : na, color = color.new(#f5ff35, 0))


// RSI calculations //
up   = ta.rma(math.max(ta.change(rsiSourceInput), 0), rsiLengthInput)
down = ta.rma(-math.min(ta.change(rsiSourceInput), 0), rsiLengthInput)
rsi  = down == 0 ? 100 : up == 0 ? 0 : 100 - (100 / (1 + up / down))


// Buy Signal (co)
co = ta.crossover(rsi, overSold) and close < movingaverage_signal


// Create a white line for average price, since the last opened position //
// average_price = line.new(x1 = bar_index - max_bar, y1 = strategy.position_avg_price, x2 = bar_index, y2 = strategy.position_avg_price, color = color.white)
    

// Stop //
// Create a red Stop level line based on a specified % above the average price //
stop_level = strategy.position_avg_price + (strategy.position_avg_price / 100 * takeProfitAll)
// stop_line  = line.new(x1 = bar_index - max_bar, y1 = stop_level, x2 = bar_index, y2 = stop_level, color = color.red)
    

// Take profit definition per open position //
take_profit_price = close * takeProfit / 100 / syminfo.mintick


// Make sure the Stop level and average price level don't excied the bar buffer to avoid errors //
// if count <= bar_lookback
//     line.set_x1(stop_line,     strategy.opentrades.entry_bar_index(strategy.opentrades - 1))
//     line.set_x1(average_price, strategy.opentrades.entry_bar_index(strategy.opentrades - 1))


// Exponential DCA Layer Calculation fucntion --> First try, needs more experimentation //
dca_price_level(index,entry_price) =>   
    entry_price * (1 - (increment * math.pow(exponent_scale, index)))


// Set  Entries //
// Open the first entry and set the entry price //
if co and strategy.position_size == 0 and window() 
    open_position := true
    entry_price   := close
    strategy.entry(id = 'FE1', direction = strategy.long, qty = q)  
    
// first_entry_line = line.new(x1 = bar_index - max_bar, y1 = entry_price, x2 = bar_index, y2 = entry_price, color = color.blue)


// Start bar counting since the position is open //
if open_position == true
    count := count + 1


// Set the DCA entries //
// Prices below 1 are not set to avoid negative prices //
if strategy.position_size > 0 and window()
    for i = 0 to strategy.opentrades
        if strategy.opentrades == i and i < posCount
            dca_price := dca_price_level(i,entry_price) > 1 ? dca_price_level(i,entry_price) : na
            entry_id = 'DCA' + str.tostring(i + 1) 
            strategy.entry(id = entry_id, direction = strategy.long, limit = dca_price, qty = q)  


// Store the values of the different dca price levels in an array and create the dca lines // 
// Prices below 1 are not stored//
if open_position==true and window() 
    for i = 1 to posCount -1
        array.push(linesArray, dca_price_level(i,entry_price) > 1 ? dca_price_level(i,entry_price) : na) 
    
    // for i = 1 to array.size(linesArray) - 1
    //     dca_line := line.new(x1 = bar_index - max_bar, y1 = array.get(linesArray, i), x2 = bar_index, y2 = array.get(linesArray, i),color = color.blue)


// Create thick line to show the last Entry price //
// last_entry_price = line.new(x1 = bar_index[5], y1 = strategy.opentrades.entry_price(strategy.opentrades - 1), x2 = bar_index, y2 = strategy.opentrades.entry_price(strategy.opentrades - 1),color = color.rgb(255, 0, 204), width = 5)


// Exit the first entry when the take profit triggered //   
if strategy.opentrades > 0 and window() 
    strategy.exit(id = 'Exit FE', from_entry = 'FE1', profit = take_profit_price)


// Exit DCA entries when take profit is triggered //
if strategy.opentrades > 0 and window() 
    for i = 0 to strategy.opentrades 
        exit_from = 'DCA' + str.tostring(i + 1)
        exit_id = 'Exit_' + str.tostring(i + 1)
        strategy.exit(id = exit_id, from_entry = exit_from, profit = take_profit_price)


// Close all positions at once when Stop is crossed //
if strategy.opentrades > 0 and ta.crossover(close,stop_level) and window() 
    strategy.close_all()


// Make sure nothing is open after alle positions are closed and set the condiftion back to be open for new entries //
if strategy.position_size[1] > 0 and strategy.position_size == 0
    strategy.cancel_all()
    strategy.close_all()
    // line.delete(average_price)
    // line.delete(stop_line)
    // line.delete(dca_line)
    open_position := false   // All position are closed, so back to false
    count := 0               // Reset bar counter




Thêm nữa