
이전 기사단계별로 전략을 작성하는 방법을 알려드립니다. - 내 언어 전략을 이식하세요이 글에서 우리는 간단한 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
이 지표 함수는 FMZ 플랫폼에서 JavaScript 언어로 전략을 작성할 때 사용할 수 있는 지표 라이브러리 함수입니다. 지금 바로:TA.MA
SMA
우리가 해야 할 일은SMAFMZ의 TA 라이브러리에서는 SMA 지표 기능이 지원되지 않는다는 것을 발견했습니다. talib 라이브러리의 SMA 지표와 Mai 언어의 지표 사이에도 차이점이 있습니다.
보시다시피, 주기 매개변수 외에도 매개변수 부분에 가중치 매개변수가 있습니다.
FMZ API 문서의 talib 라이브러리에 있는 SMA 표시기 함수에 대한 설명은 다음과 같습니다.

보이는talib.SMA단순 이동평균선 지표입니다.
이런 방식으로는 여러분은 스스로 하나만 구현할 수 있습니다.SMAJavascript 언어를 사용하여 전략을 작성하는 개발자로서, 이는 또한 필요한 기술 중 하나입니다. 결국, 기성품 바퀴가 없다면 자동차는 여전히 운전되어야 하므로, 그냥 하나 만들면 됩니다.
솔직히 말해서 지표 등에 대한 연구는 별로 없습니다. 일반적으로 이해가 안 되는 것이 있으면 검색해서 정보를 찾습니다. SMA의 경우 다음 사항을 발견했습니다.

저는 여기에 설명된 알고리즘 프로세스가 매우 신뢰할 만하다고 생각합니다. 구현해 보겠습니다.
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
}
정책 프레임워크 사용단계별로 전략을 작성하는 방법을 알려드립니다. - 내 언어 전략을 이식하세요이 기사의 동일한 프레임워크는 주로 두 부분을 채웁니다.

먼저, 시장 데이터 처리와 지표 계산을 수행합니다.

Mai 언어의 기능을 하나씩 살펴보겠습니다.
AP:=(HIGH+LOW+CLOSE)/3;이 문장은 K-line 데이터의 각 BAR의 최고가, 최저가, 종가를 더한 후 3으로 나누어 평균을 구한 후 각 BAR에 대응되는 배열로 저장한다는 의미로 이해될 수 있다. 하나. 이 문제는 다음과 같이 처리할 수 있습니다.
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;
Mai 언어 코드의 이 문장을 읽으면 두 지표선 WT1과 WT2의 골든크로스와 데드크로스 판단이 포지션 오픈 조건으로 사용된다는 것을 알 수 있습니다. 이전 크로스 신호가 사용된다는 점에 유의해야 합니다.
마이크 언어로 전략을 직접 백테스트하면 다음과 같은 결과가 나타납니다.

Mai Language Strategy의 실제 작동을 통해, 오프닝 포인트에서 신호가 감지될 때 실제로는 오프닝 포인트 앞의 2개 BAR 위치가 골든 크로스인지 여부를 감지한다는 것을 알 수 있습니다. 위 그림에서 명확히 볼 수 있습니다.

신호 감지 부분의 채우기 코드는 다음과 같이 작성할 수 있습니다.
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
}
여기서 우리는 Mai 언어의 SPK와 BPK 명령어가 위의 코드로 구현될 수 있는 이유에 대해 생각해 볼 수 있습니다.
백테스트 구성:

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