
В этом выпуске мы обсудим «магическую стратегию двойной скользящей средней EMA» с YouTube, которую называют «убийцей рынка акций и криптовалют». Посмотрев видео, я узнал, что эта стратегия представляет собой стратегию на языке Trading View Pine, которая использует 2 индикатора Trading View. Увидев, что результаты бэктестинга в видео очень хороши, а FMZ также поддерживает язык Pine от Trading View, я не мог не захотеть провести бэктестинг и проверить анализ самостоятельно. Тогда начни всю жизнь! Давайте воспроизведем стратегию из видео.
Для простоты конструкции мы не будем использовать экспоненциальную скользящую среднюю, указанную в видео. Вместо этого мы используем встроенный ta.ema в торговом представлении (на самом деле это одно и то же).
Это индикатор на Trading View. Нам нужно зайти на Trading View и скачать исходный код.

Бесплатный код VuManChu Swing:
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// Credits to the original Script - Range Filter DonovanWall https://www.tradingview.com/script/lut7sBgG-Range-Filter-DW/
// This version is the old version of the Range Filter with less settings to tinker with
//@version=4
study(title="Range Filter - B&S Signals", shorttitle="RF - B&S Signals", overlay=true)
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
//Functions
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
//Range Size Function
rng_size(x, qty, n)=>
// AC = Cond_EMA(abs(x - x[1]), 1, n)
wper = (n*2) - 1
avrng = ema(abs(x - x[1]), n)
AC = ema(avrng, wper)*qty
rng_size = AC
//Range Filter Function
rng_filt(x, rng_, n)=>
r = rng_
var rfilt = array.new_float(2, x)
array.set(rfilt, 1, array.get(rfilt, 0))
if x - r > array.get(rfilt, 1)
array.set(rfilt, 0, x - r)
if x + r < array.get(rfilt, 1)
array.set(rfilt, 0, x + r)
rng_filt1 = array.get(rfilt, 0)
hi_band = rng_filt1 + r
lo_band = rng_filt1 - r
rng_filt = rng_filt1
[hi_band, lo_band, rng_filt]
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
//Inputs
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
//Range Source
rng_src = input(defval=close, type=input.source, title="Swing Source")
//Range Period
rng_per = input(defval=20, minval=1, title="Swing Period")
//Range Size Inputs
rng_qty = input(defval=3.5, minval=0.0000001, title="Swing Multiplier")
//Bar Colors
use_barcolor = input(defval=false, type=input.bool, title="Bar Colors On/Off")
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
//Definitions
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
//Range Filter Values
[h_band, l_band, filt] = rng_filt(rng_src, rng_size(rng_src, rng_qty, rng_per), rng_per)
//Direction Conditions
var fdir = 0.0
fdir := filt > filt[1] ? 1 : filt < filt[1] ? -1 : fdir
upward = fdir==1 ? 1 : 0
downward = fdir==-1 ? 1 : 0
//Trading Condition
longCond = rng_src > filt and rng_src > rng_src[1] and upward > 0 or rng_src > filt and rng_src < rng_src[1] and upward > 0
shortCond = rng_src < filt and rng_src < rng_src[1] and downward > 0 or rng_src < filt and rng_src > rng_src[1] and downward > 0
CondIni = 0
CondIni := longCond ? 1 : shortCond ? -1 : CondIni[1]
longCondition = longCond and CondIni[1] == -1
shortCondition = shortCond and CondIni[1] == 1
//Colors
filt_color = upward ? #05ff9b : downward ? #ff0583 : #cccccc
bar_color = upward and (rng_src > filt) ? (rng_src > rng_src[1] ? #05ff9b : #00b36b) :
downward and (rng_src < filt) ? (rng_src < rng_src[1] ? #ff0583 : #b8005d) : #cccccc
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
//Outputs
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
//Filter Plot
filt_plot = plot(filt, color=filt_color, transp=67, linewidth=3, title="Filter")
//Band Plots
h_band_plot = plot(h_band, color=color.new(#05ff9b, 100), title="High Band")
l_band_plot = plot(l_band, color=color.new(#ff0583, 100), title="Low Band")
//Band Fills
fill(h_band_plot, filt_plot, color=color.new(#00b36b, 92), title="High Band Fill")
fill(l_band_plot, filt_plot, color=color.new(#b8005d, 92), title="Low Band Fill")
//Bar Color
barcolor(use_barcolor ? bar_color : na)
//Plot Buy and Sell Labels
plotshape(longCondition, title = "Buy Signal", text ="BUY", textcolor = color.white, style=shape.labelup, size = size.normal, location=location.belowbar, color = color.new(color.green, 0))
plotshape(shortCondition, title = "Sell Signal", text ="SELL", textcolor = color.white, style=shape.labeldown, size = size.normal, location=location.abovebar, color = color.new(color.red, 0))
//Alerts
alertcondition(longCondition, title="Buy Alert", message = "BUY")
alertcondition(shortCondition, title="Sell Alert", message = "SELL")
Индикатор EMA: стратегия использует две скользящие средние EMA, одну быструю линию (параметр малого цикла) и одну медленную линию (параметр большого цикла). Основная функция двойной скользящей средней EMA — помочь нам определить направление рыночных тенденций.
Длинная композиция Быстрая линия находится выше медленной линии.
Короткая аранжировка Быстрая линия находится ниже медленной линии.
Индикатор VuManChu Swing Free: Индикатор VuManChu Swing Free используется для отправки сигналов, а затем в сочетании с другими условиями определяет, следует ли размещать ордер на торговлю. Из исходного кода индикатора VuManChu Swing Free мы видим, что переменная longCondition представляет сигнал на покупку, а переменная shortCondition представляет сигнал на продажу. Эти две переменные будут использоваться позже при написании условий заказа.
Теперь поговорим о конкретных условиях срабатывания торгового сигнала стратегии:
Правила входа в длинные позиции: Цена закрытия положительной K-линии должна быть выше быстрой линии EMA, две скользящие средние EMA должны быть в бычьей компоновке (быстрая линия выше медленной линии), а индикатор VuManChu Swing Free должен показывать сигнал на покупку. (longCondition истинно). Если выполнены все три условия, то эта K-линия является ключевой K-линией для входа в длинную позицию, а цена закрытия этой K-линии является позицией входа.
Правила входа в короткую позицию (в отличие от длинной позиции): Цена закрытия отрицательной свечи должна быть ниже быстрой линии EMA, две скользящие средние EMA должны быть в короткой позиции (быстрая линия ниже медленной), а индикатор VuManChu Swing Free должен показывать сигнал на продажу (shortCondition правда). Если все три условия выполнены, то цена закрытия этой К-линии является точкой входа для короткой продажи.
Разве торговая логика не очень проста? Поскольку в исходном видео не указаны тейк-профит и стоп-лосс, редактор будет использовать более умеренный метод тейк-профита и стоп-лосса, используя фиксированный стоп-лосс и отслеживание фиксировать прибыль.
Мы вставили код индикатора VuManChu Swing Free непосредственно в код нашей стратегии без каких-либо изменений.

Затем мы пишем фрагмент кода на языке Pine для реализации функции транзакции:
// extend
fastEmaPeriod = input(50, "fastEmaPeriod") // 快线周期
slowEmaPeriod = input(200, "slowEmaPeriod") // 慢线周期
loss = input(30, "loss") // 止损点数
trailPoints = input(30, "trailPoints") // 移动止盈触发点数
trailOffset = input(30, "trailOffset") // 移动止盈偏移量(点数)
amount = input(1, "amount") // 下单量
emaFast = ta.ema(close, fastEmaPeriod) // 计算快线EMA
emaSlow = ta.ema(close, slowEmaPeriod) // 计算慢线EMA
buyCondition = longCondition and emaFast > emaSlow and close > open and close > emaFast // 做多入场条件
sellCondition = shortCondition and emaFast < emaSlow and close < open and close < emaFast // 做空入场条件
if buyCondition and strategy.position_size == 0
strategy.entry("long", strategy.long, amount)
strategy.exit("exit_long", "long", amount, loss=loss, trail_points=trailPoints, trail_offset=trailOffset)
if sellCondition and strategy.position_size == 0
strategy.entry("short", strategy.short, amount)
strategy.exit("exit_short", "short", amount, loss=loss, trail_points=trailPoints, trail_offset=trailOffset)
A. Как вы можете видеть, когда buyCondition имеет значение true:
- Переменная longCondition имеет значение true (индикатор VuManChu Swing Free подает сигнал на открытие длинной позиции).
- emaFast > emaSlow (бычья конфигурация EMA).
- закрытие > открытие (указывает, что текущий BAR является положительной линией), закрытие > emaFast (указывает, что цена закрытия находится выше быстрой линии EMA).
Соблюдены три условия для открытия длинной позиции.
B. Когда sellCondition имеет значение true, выполняются три условия для короткой продажи (здесь не объясняются).
Затем, когда условие if определяет, что сигнал сработал, используйте функцию strategy.entry для входа на рынок и открытия позиции, а функцию strategy.exit установите для стоп-лосса и трейлинг-профита.
/*backtest
start: 2022-01-01 00:00:00
end: 2022-10-08 00:00:00
period: 15m
basePeriod: 5m
exchanges: [{"eid":"Futures_Binance","currency":"ETH_USDT"}]
args: [["ZPrecision",0,358374]]
*/
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// Credits to the original Script - Range Filter DonovanWall https://www.tradingview.com/script/lut7sBgG-Range-Filter-DW/
// This version is the old version of the Range Filter with less settings to tinker with
//@version=4
study(title="Range Filter - B&S Signals", shorttitle="RF - B&S Signals", overlay=true)
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
//Functions
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
//Range Size Function
rng_size(x, qty, n)=>
// AC = Cond_EMA(abs(x - x[1]), 1, n)
wper = (n*2) - 1
avrng = ema(abs(x - x[1]), n)
AC = ema(avrng, wper)*qty
rng_size = AC
//Range Filter Function
rng_filt(x, rng_, n)=>
r = rng_
var rfilt = array.new_float(2, x)
array.set(rfilt, 1, array.get(rfilt, 0))
if x - r > array.get(rfilt, 1)
array.set(rfilt, 0, x - r)
if x + r < array.get(rfilt, 1)
array.set(rfilt, 0, x + r)
rng_filt1 = array.get(rfilt, 0)
hi_band = rng_filt1 + r
lo_band = rng_filt1 - r
rng_filt = rng_filt1
[hi_band, lo_band, rng_filt]
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
//Inputs
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
//Range Source
rng_src = input(defval=close, type=input.source, title="Swing Source")
//Range Period
rng_per = input(defval=20, minval=1, title="Swing Period")
//Range Size Inputs
rng_qty = input(defval=3.5, minval=0.0000001, title="Swing Multiplier")
//Bar Colors
use_barcolor = input(defval=false, type=input.bool, title="Bar Colors On/Off")
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
//Definitions
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
//Range Filter Values
[h_band, l_band, filt] = rng_filt(rng_src, rng_size(rng_src, rng_qty, rng_per), rng_per)
//Direction Conditions
var fdir = 0.0
fdir := filt > filt[1] ? 1 : filt < filt[1] ? -1 : fdir
upward = fdir==1 ? 1 : 0
downward = fdir==-1 ? 1 : 0
//Trading Condition
longCond = rng_src > filt and rng_src > rng_src[1] and upward > 0 or rng_src > filt and rng_src < rng_src[1] and upward > 0
shortCond = rng_src < filt and rng_src < rng_src[1] and downward > 0 or rng_src < filt and rng_src > rng_src[1] and downward > 0
CondIni = 0
CondIni := longCond ? 1 : shortCond ? -1 : CondIni[1]
longCondition = longCond and CondIni[1] == -1
shortCondition = shortCond and CondIni[1] == 1
//Colors
filt_color = upward ? #05ff9b : downward ? #ff0583 : #cccccc
bar_color = upward and (rng_src > filt) ? (rng_src > rng_src[1] ? #05ff9b : #00b36b) :
downward and (rng_src < filt) ? (rng_src < rng_src[1] ? #ff0583 : #b8005d) : #cccccc
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
//Outputs
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
//Filter Plot
filt_plot = plot(filt, color=filt_color, transp=67, linewidth=3, title="Filter")
//Band Plots
h_band_plot = plot(h_band, color=color.new(#05ff9b, 100), title="High Band")
l_band_plot = plot(l_band, color=color.new(#ff0583, 100), title="Low Band")
//Band Fills
fill(h_band_plot, filt_plot, color=color.new(#00b36b, 92), title="High Band Fill")
fill(l_band_plot, filt_plot, color=color.new(#b8005d, 92), title="Low Band Fill")
//Bar Color
barcolor(use_barcolor ? bar_color : na)
//Plot Buy and Sell Labels
plotshape(longCondition, title = "Buy Signal", text ="BUY", textcolor = color.white, style=shape.labelup, size = size.normal, location=location.belowbar, color = color.new(color.green, 0))
plotshape(shortCondition, title = "Sell Signal", text ="SELL", textcolor = color.white, style=shape.labeldown, size = size.normal, location=location.abovebar, color = color.new(color.red, 0))
//Alerts
alertcondition(longCondition, title="Buy Alert", message = "BUY")
alertcondition(shortCondition, title="Sell Alert", message = "SELL")
// extend
fastEmaPeriod = input(50, "fastEmaPeriod")
slowEmaPeriod = input(200, "slowEmaPeriod")
loss = input(30, "loss")
trailPoints = input(30, "trailPoints")
trailOffset = input(30, "trailOffset")
amount = input(1, "amount")
emaFast = ta.ema(close, fastEmaPeriod)
emaSlow = ta.ema(close, slowEmaPeriod)
buyCondition = longCondition and emaFast > emaSlow and close > open and close > emaFast
sellCondition = shortCondition and emaFast < emaSlow and close < open and close < emaFast
if buyCondition and strategy.position_size == 0
strategy.entry("long", strategy.long, amount)
strategy.exit("exit_long", "long", amount, loss=loss, trail_points=trailPoints, trail_offset=trailOffset)
if sellCondition and strategy.position_size == 0
strategy.entry("short", strategy.short, amount)
strategy.exit("exit_short", "short", amount, loss=loss, trail_points=trailPoints, trail_offset=trailOffset)
Временной диапазон бэктестинга выбран с января 2022 года по октябрь 2022 года, период K-линии составляет 15 минут, а для бэктестинга используется модель цены закрытия. Рынок выбирает бессрочный контракт Binance ETH_USDT. Настройки параметров такие же, как и в исходном видео: 50 периодов для быстрой линии и 200 периодов для медленной линии, остальные параметры по умолчанию остаются неизменными. Я немного субъективен в установке стоп-лосса и точек трейлинг-профита и просто устанавливаю их на уровне 30 пунктов.


Результаты бэктеста так себе. После нескольких бэктестов, похоже, что такие параметры, как тейк-профит и стоп-лосс, оказывают некоторое влияние на результаты бэктеста. Я считаю, что этот аспект нуждается в дальнейшей оптимизации. Однако процент выигрышей все еще высок после того, как стратегический сигнал инициирует транзакцию.
Давайте попробуем бессрочный контракт BTC_USDT:

Результаты бэктеста BTC также оказались взрывоопасными:


Адрес стратегии: https://www.fmz.com/strategy/385745
Похоже, что этот метод торговли достаточно надежен для определения тренда, и на основе этой идеи можно дополнительно оптимизировать конструкцию. В этой статье мы не только узнали идею стратегии двойной скользящей средней, но и научились обрабатывать и изучать стратегии мастеров на YouTube. Хорошо, приведенные выше коды стратегий — это всего лишь мои предложения. Результаты бэктеста не отражают конкретных фактических результатов. Коды стратегий и проекты приведены только для справки. Спасибо за вашу поддержку, увидимся в следующий раз!