ダイナミック平均コスト ドル平均コスト 複合戦略

作者: リン・ハーンチャオチャン,日付: 2024-01-04 16:34:10
タグ:

img

概要

ダイナミック・平均コストDCA複合戦略は,各開設ポジションの量を動的に調整する.トレンドの開始時に,まず小さなポジションを開いてポジションを構築する.統合の深さが増加するにつれて,徐々にポジションサイズを増やす.戦略は,ストップ損失価格レベルを計算するために指数関数を使用し,起動すると新しいバッチを再開する.これは,ポジション保持コストが指数的に低下し続けることを引き起こします.深さが増加するにつれて,ポジションのコストは徐々に減少することができます.価格が逆転すると,バッチ利益を取ることでより大きな利益を得ることができます.

戦略の論理

この戦略は,RSIオーバーセールシグナルと移動平均のタイミングの単純な組み合わせを使用してエントリー機会を決定する.RSIがoversoldレベルを下回り,価格が移動平均を下回ると最初のエントリーオーダーが送信される.最初のエントリー後,指数関数は次のレベルでの価格減少パーセントを計算する.DCAオーダーをトリガーするたびに,ポジションサイズが再計算され,エントリーあたり同じ金額を維持する.ポジションサイズとコストが動的に変化すると,レバレッジ効果が生じる.

DCA数値が増加するにつれて,平均保有コストは低下し続けます.各ポジションの利益を得るために,ほんの小さなリバウンドだけで十分です.連続したエントリが提出された後,平均保有価格の上にストップ・ロスは描かれます.価格が平均価格とストップ・ロスの線を超えると,すべてのポジションが閉鎖されます.

最大の利点は,保有コストが継続的に低下しているため,統合中でも,コストは段階的に累積的に削減することができるということです.傾向が逆転すると,市場価格よりもはるかに低い保有コストのために,はるかに大きな利益が得られます.

危険 と 欠陥

ストップ・ロスのリスクは,初期に限られたポジションサイズです. 継続的な減少の間,ストップ・ロスのリスクがあります. したがって,ストップ・ロスの割合は,個人的なリスク欲求に基づいて合理的に設定する必要があります.

また,ストップロスのレベルを設定する際には,2つの極端があります.あまりにも緩やかであれば,十分なリトラセーションを捕まえることはできません.しかし,あまりにも緊密に場合は,中期調整中にストップアウトされる可能性が増加します.したがって,異なる市場状況とリスク優先順位に応じて適切なストップロスのレベルを選択することが重要です.

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




もっと