동적 평균 비용 달러 비용 평균 합성 전략

저자:차오장, 날짜: 2024-01-04 16:34:10
태그:

img

전반적인 설명

동적 평균 비용 DCA 복합 전략은 각 개설 포지션의 양을 동적으로 조정합니다. 트렌드의 시작에서 먼저 작은 포지션을 개설하여 포지션을 구축합니다. 통합의 깊이가 증가함에 따라 점차 포지션 크기를 증가시킵니다. 전략은 스톱 로스 가격 수준을 계산하기 위해 기하수수 함수를 사용하고, 활성화되면 새로운 팩을 다시 열고, 이는 포지션을 보유하는 비용이 기하수수적으로 계속 감소하도록 만들 수 있습니다. 깊이가 증가함에 따라 포지션의 비용이 점차 감소 할 수 있습니다. 가격이 역전되면 팩 수익이 더 큰 수익을 얻을 수 있습니다.

전략 논리

이 전략은 엔트리 기회를 결정하기 위해 RSI 과잉 판매 신호와 이동 평균 타이밍의 간단한 조합을 사용합니다. RSI가 과잉 판매 수준 이하로 떨어지고 가격이 이동 평균 이하로 닫을 때 첫 번째 엔트리 오더가 제출됩니다. 첫 번째 엔트리 후, 기하급수 함수는 다음 레벨의 가격 하락 비율을 계산합니다. DCA 오더를 트리거 할 때마다 입상당 동일한 금액을 유지하도록 포지션 사이징이 재 계산됩니다. 포지션 크기와 비용이 동적으로 변화함에 따라 레버리지 효과를 만듭니다.

DCA 수치가 증가함에 따라 평균 보유 비용이 계속 감소합니다. 각 포지션의 수익을 취하기 위해 작은 리바운드만 충분합니다. 연속적인 엔트리가 제출 된 후 평균 보유 가격 위에 스톱 로스 라인이 그려집니다. 가격이 평균 가격과 스톱 로스 라인을 넘어서면 모든 포지션이 종료됩니다.

가장 큰 장점은 보유 비용이 계속 감소함에 따라 통합 중에도 비용이 단계적으로 축적적으로 감소 할 수 있다는 것입니다. 추세가 역전되면 시장 가격보다 훨씬 낮은 보유 비용으로 인해 훨씬 더 큰 이익을 얻을 수 있습니다.

위험 과 결함

가장 큰 위험은 초기에는 제한된 포지션 크기입니다. 지속적인 하락 기간 동안 스톱 로스 위험이 발생할 수 있습니다. 따라서 스톱 로스 비율은 개인적인 위험 욕구에 따라 합리적으로 설정해야합니다.

또한, 스톱 로스 레벨을 설정하는 것은 두 가지 극단적 측면이 있다. 너무 느슨하다면, 충분한 리트레이싱이 포착될 수 없다. 그러나 너무 긴다면, 중장기 수정 중에 스톱 로스를 받을 확률이 증가한다. 따라서 다른 시장 조건과 위험 선호도에 따라 적절한 스톱 로스 레벨을 선택하는 것은 매우 중요하다.

너무 많은 DCA 수준이 있으면 가격이 크게 상승하면 매우 높은 보유 비용이 효과적인 스톱 손실을 막을 수 있습니다. 따라서 최대 DCA 층은 총 자본 할당과 가장 높은 비용을 기준으로 합리적으로 설정해야합니다.

최적화 제안

  1. 입시 시기의 신호를 최적화하여 매개 변수와 다른 지표 조합을 테스트하여 높은 승률 신호를 얻습니다.

  2. 더 나은 결과를 얻기 위해 L 후속 스톱 손실 또는 곡선 부착 후속 스톱 손실을 테스트하여 스톱 손실 메커니즘을 최적화하십시오. 또한 레벨은 위치 할당 비율에 따라 동적으로 조정 할 수 있습니다.

  3. 수익을 취하는 방법을 최적화하십시오. 더 나은 출구 기회와 더 높은 총 수익을 위해 다른 유형의 후속 수익을 조사 할 수 있습니다.

  4. 반 윙사 메커니즘을 추가한다. 때로는 DCA 신호는 중지 손실 직후 다시 활성화 될 수 있다. 윙사 범위는 중지 직후 공격적인 재입구를 피하기 위해 추가될 수 있다.

결론

이 전략은 엔트리를 결정하기 위해 RSI를 활용하고, 엑스포넌셜 다이내믹 스톱 로스 (DCA) 메커니즘을 사용하여 포지션 사이즈와 평균 비용을 동적으로 조정하여 통합 과정에서 가격 우위를 확보합니다. 주요 최적화 영역은 엔트리 / 엑스포넌스 신호, 스톱 로스 및 영업에 초점을 맞추고 있습니다. 지수적 DCA의 핵심 개념은 보유 비용을 지속적으로 낮추기 위해 구현되며, 이로 인해 통합 중에 더 많은 공간을 제공하고, 트렌드 발생 시 배수 수익을 달성합니다. 그러나 매개 변수는 여전히 전반적인 포지션 위험을 제어하기 위해 자본 할당 계획에 따라 신중하게 설정해야합니다.


/*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




더 많은