
前の記事戦略を段階的に書く方法を教えます - 私の言語戦略を移植するこの記事では、シンプルな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
私たちがすべきことはSMASMA インジケーター関数は FMZ の TA ライブラリではサポートされていないことがわかりました。また、talib ライブラリの SMA インジケーターと Mai 言語の SMA インジケーターには違いがあります。
ご覧のとおり、パラメータ部分には期間パラメータに加えて重みパラメータもあります。
FMZ API ドキュメントの talib ライブラリの SMA インジケーター関数の説明は次のとおりです。

見えるtalib.SMA単純な移動平均指標です。
このように、自分で実装できるのは1つだけですSMAJavascript 言語を使用して戦略を記述する開発者としては、これも必要なスキルの 1 つです。結局のところ、既製の車輪がない場合は、車を運転する必要があるため、車輪を作成するだけです。
正直、指標などについてはあまり研究していません。わからないことがあれば検索して調べることが多いです。 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
}
ポリシーフレームワークの使用戦略を段階的に書く方法を教えます - 私の言語戦略を移植する記事内の同じフレームワークは、主に次の 2 つの部分で構成されます。

まず、市場データの処理と指標の計算を行います。

Mai 言語の機能を一つずつ見ていきましょう。
AP:=(HIGH+LOW+CLOSE)/3;この文は、Kラインデータの各BARの最高値、最低値、終値を合計し、それを3で割って平均を計算し、それを各BARに1つずつ対応する配列として保存するものとして理解できます。 1つ。 これは次のように処理できます。
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言語コードの文章を読むと、2つのインジケーターライン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
}
ここで、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