Previous articleThe hand teaches you how to write a strategy -- to translate a strategy into my language.I've tried to translate a simple Mayan language strategy into JavaScript, but what if it's a bit more complicated?
Let's first look at this transplant strategy:
(*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;
This is the first part of the Ma language strategy.(*backtest...*)
This strategy is also a random search strategy, and is not too complicated (relatively more complex than in the previous article), is a more representative strategy. Porting a Mac language strategy, first of all, to look at the content of the policy, Mac language policy code is more concise, basically looking down to have a certain understanding of the strategy overall, this strategy we see using several indicator functions.EMA
,SMA
:
The EMA
This indicator function is a ready-made indicator library function that is directly available to the FMZ platform when writing the policy in JavaScript language.TA.MA
SMA
What we need to do is:SMA
This indicator, which we found in FMZ's TA library doesn't support the SMA indicator function, also has a difference between the SMA indicator in the talib library and the Malay language:As you can see, the parameter part has a weight parameter in addition to the period parameter.
The description of the SMA indicator function in the talib library in the FMZ API documentation is:
I see.talib.SMA
This is a simple moving average.
The only way to do that is to do it yourself.SMA
As a developer who writes strategies using JavaScript, this is also one of the essential skills, after all, if you don't have a ready-made wheel, the car will still start, making one is that.
To be honest, there is not much research on indicators like this, and people generally don't know much about it.
I feel that this algorithm process is very reliable, so let's implement it:
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
}
Use of strategic frameworksThe hand teaches you how to write a strategy -- to translate a strategy into my language.The same frame in the article, which is mainly filled in two parts:
The first is the processing of market data and the calculation of indicators.
We'll take a look at the functions of this part of Ma, one sentence at a time:
1、AP:=(HIGH+LOW+CLOSE)/3;
This can be understood as adding the highest, lowest, and closing prices of each BAR in the K-line data, then dividing by 3, calculating the average, and storing it as an array, with each BAR corresponding to one. This can be done as follows:
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
}
This function can be called in the OnTick function in the main loop, for example:
// 计算指标
// AP
var ap = CalcAP(records)
2 ⁄ After the AP calculation is completed, the calculation continues.ESA:=EMA(AP,N1);
:
The ESA is the "index moving average" of the AP, the EMA, so we can calculate the EMA using the AP as the data and the N1 as the parameter of the EMA.
function CalcESA (ap, n1) { // ESA:=EMA(AP,N1);
if (ap.length <= n1) { // 如果AP的长度小于指标参数,是无法计算出有效数据的,这个时候让函数返回false。
return false
}
return TA.EMA(ap, n1)
}
3、D:=EMA(ABS(AP-ESA),N1);
Calculated usingAP
、ESA
Calculated dataD
I'm not sure.
Here you can see the code notes, some tips for calculating indicators.
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(数组结构)
}
4、CI:=(AP-ESA)/(0.015*D);
This calculation is similar to step 1, which is to put the code directly out.
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
}
TCI: = EMA ((CI, N2)); The EMA of the CI array is calculated.
function CalcTCI (ci, n2) { // TCI:=EMA(CI,N2);
if (ci.length <= n2) {
return false
}
return TA.EMA(ci, n2)
}
WT2:SMA(WT1,4,1);
This last step, using the wheels we made before, is the first step.SMA
The function ≠ ∞
function CalcWT2 (wt1) { // WT2:SMA(WT1,4,1);
if (wt1.length <= 4) {
return false
}
return SMA(wt1, 4, 1) // 使用我们自己实现的SMA函数计算出wt1的SMA指标。
}
The transfer of trading signals is very simple.
AA:=CROSS(WT1,WT2);
BB:=CROSSDOWN(WT1,WT2);
REF(AA,1),BPK;
REF(BB,1),SPK;
Reading these sentences in the Macanese code, you can see that the Golden Fork and Dead Fork of the two indicator lines WT1 and WT2 are judged as opening conditions. It should be noted that the first cross signal is used. In the case of the Malayalam language, we can see that:
It can be observed that when the signal is detected at the opening position, it is actually detecting whether the position of the opening point BAR before the previous 2 BARs is a gold fork.
The fill code for the signal detection section can be written as:
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
}
Here you can think about why the SPK, BPK instructions in Ma can be implemented with the above code.
Re-test the configuration:
This is the first time that I've seen this video.
JavaScript version is backtested:
The code at the beginning of the OnTick function, which is used to speed up retrieval, is to allow the strategy to run in a closing price model, which is interesting to analyze in detail.
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
}
...
..
.
The full teaching strategy code:https://www.fmz.com/strategy/174457