Эта стратегия использует подход прорыва уровня, чтобы пойти длинным или коротким при определенных условиях прорыва, и имеет возможности автоматического тестирования для поиска оптимальной комбинации параметров.
Входящие параметры включают дни обратного просмотра, процент получения прибыли, процент остановки потери и параметры автоматического обратного тестирования, такие как диапазон обратного просмотра, диапазон получения прибыли / остановки потери и т. Д.
Во время обратного тестирования пересекайте различные комбинации обратного просмотра, получайте прибыль и остановите убытки и записывайте PnL для каждой комбинации.
Логика прорыва сигнала: длинный, когда близкий перерыв выше верхней полосы, а не входной стойки, короткий, когда близкий перерыв ниже нижней полосы, а не входной стойки.
Условие стоп-лосса: если не получать прибыль и стоп-лосс запускается, выйти из сделки.
Условие получения прибыли: если вы не остановились и получили прибыль, выходите из сделки.
Показать подробную таблицу результатов обратного теста, сортируемую по показателю выигрыша, чистой прибыли или количеству сделок на основе настроек пользователя.
Авто-обратное тестирование может быстро найти оптимальные наборы параметров без ручного тестирования.
Сортировать результаты обратного теста гибко по показателю выигрыша, чистой прибыли, количеству сделок и т.д. в соответствии с потребностями.
Визуализируйте PnL для каждой сделки.
Настраиваемые параметры обратного теста для тестирования более широкого пространства параметров для поиска глобального оптимального.
Простые и понятные правила торговли, которые легко понять и применить.
Краткий период обратного тестирования может привести к нестабильным результатам.
Частая торговля, склонная к сдвигам, влияющим на рентабельность.
Ответ: испытание на различных продуктах для поиска надежных наборов параметров.
Сверхоптимизированные параметры приводят к переподключению.
Игнорирование затрат на транзакции приводит к искажению результатов.
Увеличьте параметры оптимизации, например, добавить ограничения на остановку или торговые лимиты.
Оптимизируйте условия входа с помощью фильтров трендов.
Улучшить получение прибыли/остановка убытков, как динамический получение прибыли или отслеживание стоп-убытка.
Внедрить машинное обучение для оптимизации параметров.
Оптимизируйте структуру кода для более быстрого обратного тестирования.
Проверка надежности параметров на различные продукты и временные рамки.
Подумайте об интеграции возможностей автоматической торговли.
Стратегия имеет ясную и простую логику, автоматическое обратное тестирование позволяет быстро настраивать параметры, дисплей PnL облегчает дальнейшие улучшения. Риски существуют, но могут быть уменьшены с помощью многомерной оптимизации, с сильной практической ценностью.
/*backtest start: 2023-09-16 00:00:00 end: 2023-10-16 00:00:00 period: 1d basePeriod: 1h exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}] */ // © -_- //@version=5 // strategy("[-_-] LBAB", process_orders_on_close=true, overlay=true, max_labels_count=500, max_lines_count=500, max_boxes_count=500, default_qty_type=strategy.cash, default_qty_value=100, initial_capital=10000, commission_type=strategy.commission.percent, commission_value=0.075) // Inputs lookback = input.int(2, title="Lookback", minval=2, maxval=15) tp = input.float(5, title="TP (%)", minval=1, maxval=10000) sl = input.float(5, title="SL (% from Low)", minval=1, maxval=100) com = input.float(0.075, title="Commission (%)", minval=0, maxval=50) min_lookback_tr = input.float(2, title="Min Lookback", minval=1, maxval=500, inline="tr_lookback", group="Optimisation") max_lookback_tr = input.float(5, title="Max Lookback", minval=1, maxval=500, inline="tr_lookback", group="Optimisation") min_tp_tr = input.float(5, title="Min TP (%)", minval=1, maxval=10000, inline="tr_tp", group="Optimisation") max_tp_tr = input.float(10, title="Max TP (%)", minval=1, maxval=10000, inline="tr_tp", group="Optimisation") min_sl_tr = input.float(1, title="Min SL (%)", minval=1, maxval=100, inline="tr_sl", group="Optimisation") max_sl_tr = input.float(5, title="Max SL (%)", minval=1, maxval=100, inline="tr_sl", group="Optimisation") imp_perc_profit = input.bool(true, title="Percentage profitable", group="Optimisation") imp_netprofit = input.bool(false, title="Net profit", group="Optimisation") imp_numtrades = input.bool(false, title="Number of trades", group="Optimisation") table_pos = input.string("Bottom Right", title="Position", options=["Top Left", "Top Center", "Top Right", "Middle Left", "Middle Center", "Middle Right", "Bottom Left", "Bottom Center", "Bottom Right"], group="Table") table_font_size = input.string("Normal", title="Font size", options=["Auto", "Tiny", "Small", "Normal", "Large"], group="Table") // Table parameters table_pos_ = switch table_pos "Top Left" => position.top_left "Top Center" => position.top_center "Top Right" => position.top_right "Middle Left" => position.middle_left "Middle Center" => position.middle_center "Middle Right" => position.middle_right "Bottom Left" => position.bottom_left "Bottom Center" => position.bottom_center "Bottom Right" => position.bottom_right table_font_size_ = switch table_font_size "Auto" => size.auto "Tiny" => size.tiny "Small" => size.small "Normal" => size.normal "Large" => size.large // Sorting function (first element will be largest) sortArr(arr, arr_index) => n = array.size(arr) - 1 for i = 0 to n - 1 for j = 0 to n - i - 1 if array.get(arr, j) < array.get(arr, j + 1) temp = array.get(arr, j) temp_index = array.get(arr_index, j) array.set(arr, j, array.get(arr, j + 1)) array.set(arr, j + 1, temp) array.set(arr_index, j, array.get(arr_index, j + 1)) array.set(arr_index, j + 1, temp_index) // Safe checks if min_lookback_tr > max_lookback_tr runtime.error("Min Lookback must be less than Max Lookback") if min_tp_tr > max_tp_tr runtime.error("Min Take Profit must be less than Max Take Profit") if min_sl_tr > max_sl_tr runtime.error("Min Stop Loss must be less than Max Stop Loss") // tp_min_ = int(min_tp_tr / 1) tp_max_ = int(max_tp_tr / 1) sl_min_ = int(min_sl_tr / 1) sl_max_ = int(max_sl_tr / 1) // Size for arrays arr_size = int((max_lookback_tr - min_lookback_tr + 1) * (tp_max_ - tp_min_ + 1) * (sl_max_ - sl_min_ + 1)) // Arrays var arr_bi = array.new_int(arr_size, na) // bar_index of Smash Day var arr_in_pos = array.new_bool(arr_size, false) // are we in a position? var arr_params = array.new_string(arr_size, "") var arr_wonlost = array.new_string(arr_size, "") var arr_profit = array.new_float(arr_size, 0) // Testing what parameters are best index = 0 // Lookback for lookback_i = min_lookback_tr to max_lookback_tr // Take profit for tp_i = tp_min_ to tp_max_ // Stop loss for sl_i = sl_min_ to sl_max_ // Parameters of current iteration lookback_ = lookback_i tp_ = tp_i sl_ = sl_i // if array.get(arr_params, index) == "" array.set(arr_params, index, str.tostring(lookback_) + " " + str.tostring(tp_) + " " + str.tostring(sl_)) // Was there an entry? was_edone = false // If entry price reached if not array.get(arr_in_pos, index) and not na(array.get(arr_bi, index)) if high >= high[bar_index - array.get(arr_bi, index)] and bar_index != array.get(arr_bi, index) array.set(arr_in_pos, index, true) was_edone := true // If we're in a position if array.get(arr_in_pos, index) and bar_index != array.get(arr_bi, index) and not was_edone low_sl = low[bar_index - array.get(arr_bi, index)] * (1 - sl_ / 100) high_ep = high[bar_index - array.get(arr_bi, index)] high_tp = high_ep * (1 + tp_ / 100) amount = 100 // Stop loss if low <= low_sl array.set(arr_in_pos, index, false) array.set(arr_wonlost, index, array.get(arr_wonlost, index) + "0") array.set(arr_profit, index, array.get(arr_profit, index) - math.abs(amount / high_ep * low_sl - amount) - com / 100 * amount * 2) array.set(arr_bi, index, na) // Take profit if high >= high_tp array.set(arr_in_pos, index, false) array.set(arr_wonlost, index, array.get(arr_wonlost, index) + "1") array.set(arr_profit, index, array.get(arr_profit, index) + math.abs(amount / high_ep * high_tp - amount) - com / 100 * amount * 2) array.set(arr_bi, index, na) // Entry condition cond = barstate.isconfirmed and close < low[1] and high[1] < high[lookback_ + 1] //and not array.get(arr_in_pos, index) // New entry price if cond and not array.get(arr_in_pos, index) array.set(arr_bi, index, bar_index) // Update index index := index + 1 // Checking the results var table t = na var result_index = array.new_int(0, na) var result_arr_winrate = array.new_float(0, na) var result_arr_tradenum = array.new_int(0, na) var sort_array = array.new_float(0, na) if (barstate.islast or barstate.islastconfirmedhistory) and na(t) for i = 0 to array.size(arr_params) - 1 wins = 0 losses = 0 arr = array.get(arr_wonlost, i) for j = 0 to str.length(arr) - 1 str_ = str.substring(arr, j, j + 1) if str_ == "0" losses := losses + 1 if str_ == "1" wins := wins + 1 // Push percentage profitable trades perc_profit = math.round(wins / (wins + losses) * 100, 2) array.push(result_arr_winrate, perc_profit) // Push number of trades trade_num = str.length(array.get(arr_wonlost, i)) array.push(result_arr_tradenum, trade_num) // Push index array.push(result_index, i) // For combined sorting array.push(sort_array, (imp_netprofit ? array.get(arr_profit, i) : 1) * (imp_perc_profit ? perc_profit : 1) * (imp_numtrades ? trade_num : 1)) // Sort sortArr(array.copy(sort_array), result_index) t := table.new(columns=6, rows=13, bgcolor=color.white, border_color=color.new(color.blue, 0), border_width=1, frame_color=color.new(color.blue, 0), frame_width=1, position=table_pos_) table.cell(t, 0, 0, "% Profitable" + (imp_perc_profit ? " ↓" : ""), bgcolor=imp_perc_profit ? color.rgb(23, 18, 25) : color.white, text_color=imp_perc_profit ? color.white : color.black, text_size=table_font_size_) table.cell(t, 1, 0, "Net Profit" + (imp_netprofit ? " ↓" : ""), bgcolor=imp_netprofit ? color.rgb(23, 18, 25) : color.white, text_color=imp_netprofit ? color.white : color.black, text_size=table_font_size_) table.cell(t, 2, 0, "# of trades" + (imp_numtrades ? " ↓" : ""), bgcolor=imp_numtrades ? color.rgb(23, 18, 25) : color.white, text_color=imp_numtrades ? color.white : color.black, text_size=table_font_size_) table.cell(t, 3, 0, "Lookback", text_size=table_font_size_) table.cell(t, 4, 0, "Take Profit %", text_size=table_font_size_) table.cell(t, 5, 0, "Stop Loss %", text_size=table_font_size_) counter = 0 forloop_counter = math.min(array.size(result_index) - 1, 10) for i = 0 to forloop_counter i_ = array.get(result_index, i) params_ = str.split(array.get(arr_params, i_), " ") col_ = color.new(color.blue, 75) table.cell(t, 0, i + 1, str.tostring(array.get(result_arr_winrate, i_)) + "%", bgcolor=col_, text_size=table_font_size_) table.cell(t, 1, i + 1, str.tostring(math.round(array.get(arr_profit, i_), 2)) + "$", bgcolor=col_, text_size=table_font_size_) table.cell(t, 2, i + 1, str.tostring(array.get(result_arr_tradenum, i_)), bgcolor=col_, text_size=table_font_size_) table.cell(t, 3, i + 1, array.get(params_, 0), bgcolor=col_, text_size=table_font_size_) table.cell(t, 4, i + 1, array.get(params_, 1), bgcolor=col_, text_size=table_font_size_) table.cell(t, 5, i + 1, array.get(params_, 2), bgcolor=col_, text_size=table_font_size_) counter := counter + 1 // Warn if timeframe is <= 10 minutes if timeframe.in_seconds(timeframe.period) <= 600 table.cell(t, 0, forloop_counter + 2, "Timeframe might be too low", bgcolor=color.orange, text_size=table_font_size_, tooltip="Selected timeframe might be too low and cause an error") table.merge_cells(t, 0, forloop_counter + 2, 5, forloop_counter + 2) // Strategy var int bi = na var int pos_bi = na // Buy condition cond = barstate.isconfirmed and close < low[1] and high[1] < high[lookback + 1] and strategy.position_size == 0 // Stop loss, Take profit if strategy.position_size[1] == 0 and strategy.position_size > 0 and bar_index != bi strategy.exit("TP/SL", "Long", stop=low[bar_index - bi] * (1 - sl / 100), limit=high[bar_index - bi] * (1 + tp / 100)) pos_bi := bar_index // Buy if cond strategy.order("Long", strategy.long, stop=high) bi := bar_index // Box if strategy.position_size[1] != 0 and strategy.position_size == 0 tn = strategy.closedtrades - 1 penp = strategy.closedtrades.entry_price(tn) pexp = strategy.closedtrades.exit_price(tn)