
A estratégia de repetição de lucro de custo médio dinâmico ajusta dinamicamente a quantidade de cada posição aberta, abrindo uma pequena quantidade de posições no início da fase de tendência, aumentando gradualmente a posição à medida que a profundidade de liquidação aumenta. A estratégia usa uma função de índice para calcular o preço de parada de cada camada e desencadear a reabertura de novas posições em lotes, permitindo que a linha de custo de manutenção da posição seja mantida em declínio exponencial.
A estratégia consiste em escolher a hora de abertura da posição com um simples RSI que combina o sinal de ponto de venda do RSI com a escolha da linha de equilíbrio. O primeiro sinal de abertura de posição é gerado quando o RSI está abaixo da linha de venda e o preço de fechamento é menor que a média. Após a abertura da primeira posição, o DCA é gerado com base no limite inferior da amplitude da queda do preço calculada pela função do índice. O custo de posse diminui com o aumento do número de DCA, e uma pequena reversão de cada parada é suficiente para obter lucro. Depois de várias saídas consecutivas, uma linha de stop-loss é traçada acima da média. Uma vez que o preço retorna para cima, acima da média de manutenção e da linha de stop-loss, a saída é parada.
A maior vantagem da estratégia é que, com o custo de manutenção de posição em declínio, mesmo com a liquidação do mercado, os custos podem ser reduzidos gradualmente. Quando a tendência se inverte, os custos de manutenção já estão muito abaixo do preço de mercado, o que permite obter lucros mais substanciais.
O maior risco desta estratégia é que a posição inicial é limitada. Se houver uma tendência de queda contínua, haverá um risco de parada. Portanto, é necessário definir o tamanho de parada que você pode suportar.
Além disso, a configuração do tamanho de parada também existe em dois extremos. A configuração de um tamanho de parada muito grande não tem uma rebote profunda o suficiente. A configuração de um tamanho de parada muito pequeno tem uma maior probabilidade de sofrer uma reversão de paralisação de preços no ajuste de médio prazo.
Depois de um ciclo de DCA mais longo e com mais níveis, se o preço aumentar drasticamente, o risco de custos de posição é muito alto e não pode ser interrompido. Isso também requer um nível de DCA razoavelmente definido de acordo com o volume total de suas posições e o custo de posição mais acessível.
Otimizar a escolha de sinais. Você pode testar diferentes parâmetros e diferentes combinações de indicadores para selecionar um sinal com maior taxa de sucesso.
Mecanismos de parada de otimização. Pode ser testado com o tipo Λ de parada ou de parada de arco em vez de simples parada móvel, que pode obter um melhor efeito de parada. Também pode ser adicionado a estratégia de tempo de posição para ajustar a amplitude de parada.
Optimizar o modo de suspensão. Você pode testar diferentes tipos de suspensão móvel para encontrar melhores oportunidades de suspensão e, assim, aumentar a taxa de retorno geral.
Adicionar o mecanismo de defesa contra rebatimento. Após a parada, pode ocorrer a reabertura de posições com a ativação do sinal DCA novamente. Neste caso, pode-se considerar adicionar um certo alcance de defesa contra rebatimento, evitando a reativação imediata da posição após a parada.
Esta estratégia utiliza o indicador RSI para determinar o momento de compra, bem como a estratégia de DCA de parada dinâmica calculada com base na função do índice, para ajustar dinamicamente o número de posições e o custo de posição, obtendo assim vantagem de preço no mercado de bandas. O programa de otimização concentra-se principalmente em sinais de entrada e saída, paradas e paradas.
/*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