前回の記事でMyLanguageの戦略を移植するシンプルな MyLanguage 戦略が移植に試されています. より複雑な MyLanguage 言語であれば,どのように 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... *)
MyLanguage 戦略の初めには,バックテスト設定のための設定コードがあります.比較を容易にするため,統一されたバックテスト設定が設定されています.この戦略もランダムで,複雑ではありません (前回の記事よりも複雑です).これは代表的な戦略です. MyLanguage 戦略を移植するには,まず戦略全体を眺めなければなりません. 戦略のコードは簡潔で,全体的な戦略を理解することができます. この戦略のために,いくつかの指標機能が見られました.EMA
, SMA
使用されたもの:
エイマ
インディケーター機能は,JavaScript言語で戦略を書くときにFMZプラットフォームで直接利用可能な準備済みのインディケーターライブラリ機能があります.TA.MA
.
SMA
必要なのはSMA
SMA インディケーター機能には対応せず,タリブのライブラリとMyLanguageの SMA インディケーターには違いがあります.
パラメータのパラメータは, 周期パラメータに加えて重量パラメータがあります.
FMZ API ドキュメンテーションの talib ライブラリ内の SMA インジケーター関数は次のように記述されています.
明らかにtalib.SMA
簡単な移動平均指標です
この方法では, SMA を自分で実装するしかありません. JavsScript 言語を使って戦略を書く開発者として,これは必要なスキルの一つです. 結局のところ,準備済みのホイールがない場合,プログラムはまだ実行する必要があります.
実際には,指標などに関する研究があまり行われていません. 一般的には,理解できない情報を検索します. 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
}
戦略の枠組みは,記事と同じ枠組みを使用します.MyLanguageの戦略を移植する主に2つの部分で満たされています.
まず ティッカーのデータ処理と インデックス計算をします
この部分を MyLanguage の文ごとに 機能ごとに取りましょう:
AP:=(HIGH+LOW+CLOSE)/3;
K行データ内の各 BAR の最高値,最低値,閉値を足して 3 で割って平均値を計算し,各 BAR に一致する配列として保存すると理解できます. 次の方法で処理できます:
function CalcAP (r) { // AP:=(HIGH+LOW+CLOSE)/3;
var arrAP = [] // Declare an empty array
for (var i = 0; i < r.length; i++) { // r is the incoming K-line data, which is an array, use for to traverse this array.
v = (r[i].High + r[i].Low + r[i].Close) / 3 // Calculate the average value.
arrAP.push(v) // Add to the end of the arrAP array, the end is the first when arrAP is empty.
}
return arrAP // Returns this average array, i.e., the AP calculated in the MyLanguage
}
この関数は,メインループのOnTick関数で呼び出すことができます.例えば:
// Calculation of indicators
// AP
var ap = CalcAP(records)
ESA:=EMA(AP,N1);
:このESAは,EMA指標の"指数関数移動平均"です.EMA指標を計算するには,EMA指標のデータとN1をパラメータとして使用します.
function CalcESA (ap, n1) { // ESA:=EMA(AP,N1);
if (ap.length <= n1) { // If the AP length is less than the indicator parameter, valid data cannot be calculated. At this time, let the function return 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++) {
// When calculating the value of the indicator, it is necessary to determine the validity of the data, because the first few EMA calculations may be the beginning of the array of data is NaN, or null.
// So it must be judged that the data involved in the calculation are all valid values to proceed, and if there are any invalid values, they are filled with NaN to arrABS_APminusESA.
// The data thus calculated, each position corresponds to the previous data one by one, without misalignment.
if (ap[i] && esa[i] && !isNaN(ap[i]) && !isNaN(esa[i])) {
v = Math.abs(ap[i] - esa[i]) // According to ABS(AP-ESA), the specific value is calculated and put into the arrABS_APminusESA array.
arrABS_APminusESA.push(v)
} else {
arrABS_APminusESA.push(NaN)
}
}
if (arrABS_APminusESA.length <= n1) {
return false
}
return TA.EMA(arrABS_APminusESA, n1) // Calculate the EMA indicator of the array arrABS_APminusESA and get the data D (array structure).
}
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) // The SMA indicator for wt1 is calculated by using our own implementation of the SMA function.
}
トレーディング・シグナルをトランスプリートするのはとても簡単です.
AA:=CROSS(WT1,WT2);
BB:=CROSSDOWN(WT1,WT2);
REF(AA,1),BPK;
REF(BB,1),SPK;
MyLanguage のこれらのコードを読み終えたら,WT1 と WT2 のゴールデンクロスとベアッシュクロスオーバーが開き条件として使用されていることがわかります.前回のクロスシグナルが使用されていることに注意してください. MyLanguage 戦略のバックテストを直接使用すると,以下のように見ることができます.
MyLanguage戦略の実際の動作を観察すると,開口位置で信号が検出されたとき,実際には2BARを前にカウントする開口点にある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") // test
}
if (_State == SHORT) {
_State = COVERSHORT
Log("COVERSHORT") // test
}
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") // test
}
if (_State == LONG) {
_State = COVERLONG
Log("COVERLONG") // test
}
isOK = false
}
なぜ上記のコードで MyLanguage の SPK と BPK 命令を実装できるのか考えてみてください.
バックテスト設定:
MyLanguage のバックテスト:
JavaScript バックテスト:
OnTick関数の初めのコードは,バックテストを速くするために使用されます. Barモデルに基づいて戦略を実行するために使用されます.興味がある場合は,詳細に分析することができます.
function OnTick(){
// The ticker processing part of the driving strategy.
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