
Предыдущая статьяНаучите вас, как писать стратегии шаг за шагом - пересадите мою языковую стратегиюВ статье мы провели тест трансплантации на простой стратегии языка Mai. Если это немного более сложная стратегия языка Mai, как трансплантировать ее на язык JavaScript? Какие методы для этого существуют?
Давайте сначала рассмотрим стратегию, которую следует применить на этот раз:
(*backtest
start: 2019-05-01 00:00:00
end: 2019-11-12 00:00:00
period: 1d
exchanges: [{"eid":"Futures_OKCoin","currency":"BTC_USD"}]
args: [["SlideTick",10,126961],["ContractType","quarter",126961]]
*)
N1:=10;
N2:=21;
AP:=(HIGH+LOW+CLOSE)/3;
ESA:=EMA(AP,N1);
D:=EMA(ABS(AP-ESA),N1);
CI:=(AP-ESA)/(0.015*D);
TCI:=EMA(CI,N2);
WT1:TCI;
WT2:SMA(WT1,4,1);
AA:=CROSS(WT1,WT2);
BB:=CROSSDOWN(WT1,WT2);
REF(AA,1),BPK;
REF(BB,1),SPK;
Начало этой языковой стратегии(*backtest...*)Это код конфигурации настройки бэктеста. Для облегчения сравнения устанавливается унифицированная конфигурация бэктеста. Эта стратегия также является случайно найденной стратегией, она не слишком сложна (по сравнению с стратегией в предыдущей статье) и является более репрезентативной. Чтобы пересадить стратегию языка Mai, вы должны сначала прочитать содержание стратегии. Код стратегии языка Mai относительно лаконичен, и в основном вы можете иметь определенное понимание общей стратегии. Эта стратегия использует несколько функций индикатора.EMA,SMA:
EMA
Данная функция индикатора имеет готовую функцию библиотеки индикаторов при написании стратегий на языке JavaScript на платформе FMZ. Прямо сейчас:TA.MA
SMA
Что нам нужно сделать, так этоSMAМы обнаружили, что функция индикатора SMA не поддерживается в библиотеке TA FMZ. Также есть различия между индикатором SMA в библиотеке talib и в языке Mai:
Как видите, в дополнение к параметру периода в части параметров есть также параметр веса.
Описание функции индикатора SMA в библиотеке talib в документации FMZ API следующее:

видимыйtalib.SMAПростой индикатор скользящей средней.
Таким образом, вы можете реализовать только один вариант самостоятельно.SMAКак разработчик, пишущий стратегии на языке Javascript, это также один из необходимых навыков. В конце концов, если нет готовых колес, машину все равно нужно водить, так что просто постройте одно.
Честно говоря, не так много исследований по индикаторам и т. п. Обычно, если я чего-то не понимаю, я просто ищу и выискиваю информацию. Для СМА мы обнаружили следующее:

Я считаю, что описанный здесь алгоритмический процесс вполне надежен. Давайте реализуем его:
function SMA (arr, n, m) {
var sma = []
var currSMA = null
for (var i = 0; i < arr.length; i++) {
if (arr[i] && !isNaN(arr[i])) {
if (!currSMA) {
currSMA = arr[i]
sma.push(currSMA)
continue
}
// [M*C2+(N-M)*S1]/N
currSMA = (m * arr[i] + (n - m) * currSMA) / n
sma.push(currSMA)
} else {
sma.push(NaN)
}
}
return sma
}
Использование рамок политикиНаучите вас, как писать стратегии шаг за шагом - пересадите мою языковую стратегиюВ статье одна и та же структура в основном заполняет две части:

Сначала выполните обработку рыночных данных и расчет индикаторов.

Мы рассмотрим функции языка май по порядку:
AP:=(HIGH+LOW+CLOSE)/3;Это предложение можно понимать как сложение самой высокой цены, самой низкой цены и цены закрытия каждого БАРА в данных K-линии и деление этого на 3, вычисление среднего значения и последующее сохранение его в виде массива, соответствующего каждому БАРУ по одному один. Это можно сделать следующим образом:
function CalcAP (r) { // AP:=(HIGH+LOW+CLOSE)/3;
var arrAP = [] // 声明一个空数组
for (var i = 0; i < r.length; i++) { // r为传入的K线数据,是一个数组,用for遍历这个数组
v = (r[i].High + r[i].Low + r[i].Close) / 3 // 计算 平均值
arrAP.push(v) // 添加在 arrAP数组的尾部,arrAP是空的时候尾部就是第一个。
}
return arrAP // 返回 这个平均值数组,即麦语言中计算的 AP
}
Просто вызовите эту функцию в основном цикле функции OnTick, например:
// 计算指标
// AP
var ap = CalcAP(records)
ESA:=EMA(AP,N1);:Здесь нам нужно использовать данные AP, рассчитанные на предыдущем шаге, чтобы вычислить ESA. Фактически, этот ESA является “экспоненциальным скользящим средним” AP, то есть индикатором EMA, поэтому мы используем AP в качестве данных и N1 в качестве параметр индикатора EMA для расчета индикатора EMA.
function CalcESA (ap, n1) { // ESA:=EMA(AP,N1);
if (ap.length <= n1) { // 如果AP的长度小于指标参数,是无法计算出有效数据的,这个时候让函数返回false。
return false
}
return TA.EMA(ap, n1)
}
D:=EMA(ABS(AP-ESA),N1);Используйте рассчитанноеAP、ESAРассчитать данныеD。
Вы можете ознакомиться с комментариями к коду здесь, чтобы получить некоторые советы по расчету индикаторов.
function CalcD (ap, esa, n1) { // D:=EMA(ABS(AP-ESA),N1);
var arrABS_APminusESA = []
if (ap.length != esa.length) {
throw "ap.length != esa.length"
}
for (var i = 0; i < ap.length; i++) {
// 计算指标数值时,必须判断一下数据的有效性,因为前几次EMA计算可能数组中的开始部分的数据是NaN,或者null
// 所以必须判断,参与计算的数据都是有效数值才能进行,如果有任何无效数值,就用NaN向arrABS_APminusESA填充
// 这样计算得到的数据,每个位置和之前的数据都是一一对应的,不会错位。
if (ap[i] && esa[i] && !isNaN(ap[i]) && !isNaN(esa[i])) {
v = Math.abs(ap[i] - esa[i]) // 根据ABS(AP-ESA) , 具体计算数值,然后放入arrABS_APminusESA数组
arrABS_APminusESA.push(v)
} else {
arrABS_APminusESA.push(NaN)
}
}
if (arrABS_APminusESA.length <= n1) {
return false
}
return TA.EMA(arrABS_APminusESA, n1) // 计算数组arrABS_APminusESA的EMA指标,得到数据D(数组结构)
}
CI:=(AP-ESA)/(0.015*D);
Этот метод расчета аналогичен шагу 1, и код выпускается напрямую. function CalcCI (ap, esa, d) { // CI:=(AP-ESA)/(0.015*D);
var arrCI = []
if (ap.length != esa.length || ap.length != d.length) {
throw "ap.length != esa.length || ap.length != d.length"
}
for (var i = 0; i < ap.length; i++) {
if (ap[i] && esa[i] && d[i] && !isNaN(ap[i]) && !isNaN(esa[i]) && !isNaN(d[i])) {
v = (ap[i] - esa[i]) / (0.015 * d[i])
arrCI.push(v)
} else {
arrCI.push(NaN)
}
}
if (arrCI.length == 0) {
return false
}
return arrCI
}
function CalcTCI (ci, n2) { // TCI:=EMA(CI,N2);
if (ci.length <= n2) {
return false
}
return TA.EMA(ci, n2)
}
На этом последнем этапе мы используем колесо, которое построили ранее.SMAфункция.
function CalcWT2 (wt1) { // WT2:SMA(WT1,4,1);
if (wt1.length <= 4) {
return false
}
return SMA(wt1, 4, 1) // 使用我们自己实现的SMA函数计算出wt1的SMA指标。
}
Трансплантация торговых сигналов очень проста.
AA:=CROSS(WT1,WT2);
BB:=CROSSDOWN(WT1,WT2);
REF(AA,1),BPK;
REF(BB,1),SPK;
Читая эти предложения кода языка май, мы можем узнать, что золотой крест и мертвый крест двух индикаторных линий WT1 и WT2 используются в качестве условий для открытия позиции. Следует отметить, что используется предыдущий сигнал пересечения.
Проведите прямое бэктестирование стратегии с помощью языка микрофона, и мы увидим:

Благодаря фактическому функционированию стратегии языка май можно увидеть, что при обнаружении сигнала в точке открытия фактически определяется, является ли позиция за 2 бара до точки открытия золотым крестом. Это хорошо видно из рисунка выше:

Код заполнения части обнаружения сигнала можно записать так:
if ((_State == IDLE || _State == SHORT) && wt1[wt1.length - 4] < wt2[wt2.length - 4] && wt1[wt1.length - 3] > wt2[wt2.length - 3]) {
if (_State == IDLE) {
_State = OPENLONG
Log("OPENLONG") // 测试
}
if (_State == SHORT) {
_State = COVERSHORT
Log("COVERSHORT") // 测试
}
isOK = false
}
if ((_State == IDLE || _State == LONG) && wt1[wt1.length - 4] > wt2[wt2.length - 4] && wt1[wt1.length - 3] < wt2[wt2.length - 3]) {
if (_State == IDLE) {
_State = OPENSHORT
Log("OPENSHORT") // 测试
}
if (_State == LONG) {
_State = COVERLONG
Log("COVERLONG") // 测试
}
isOK = false
}
Здесь мы можем подумать о том, почему инструкции SPK и BPK языка Mai можно реализовать с помощью приведенного выше кода.
Конфигурация бэктеста:

Тестирование версии на языке Mai:

Тестирование версии JavaScript:

Код в начале функции OnTick используется для ускорения процесса бэктестинга и запуска стратегии на основе модели цены закрытия. Если вам интересно, вы можете проанализировать его подробно.
function OnTick(){
// 驱动策略的行情处理部分
var records = _C(exchange.GetRecords)
if (records[records.length - 1].Time == preTime) {
if (isOK) {
Sleep(500)
return
}
} else {
preTime = records[records.length - 1].Time
}
...
..
.
Полный код стратегии обучения: https://www.fmz.com/strategy/174457