
この記事の目的は、戦略開発におけるいくつかの経験とヒントを共有し、読者が取引戦略開発の経験をすぐに把握できるようにすることです。 戦略設計において同様の詳細な問題に遭遇した場合、合理的な解決策をすぐに考え出すことができます。 Inventor Quantitative Trading Platform は、説明、テスト、実践のためのプラットフォームとして使用されます。 ポリシープログラミング言語: JavaScript 取引市場:ブロックチェーン資産市場(BTC、ETHなど)
通常、戦略ロジックに応じて、市場データを取得するために次の異なるインターフェースを使用することが可能です。これは、戦略の取引ロジックが通常市場データによって駆動されるためです(もちろん、市場を参照しない戦略もあります)。 (固定投資戦略など)。
GetTicker: リアルタイムのティック情報を取得します。 一般的には、現在の最新価格、買値、売値を素早く取得するために使用されます。
GetDepth: 注文書の深さの見積もりを取得します。 通常、各レベルの価格と注文のサイズを取得するために使用されます。 ヘッジ戦略、マーケットメイク戦略などに使用されます。
GetTrade: 市場の最新の取引記録を取得します。 一般的には、短期間での市場行動を分析し、市場のミクロな変化を分析するために使用されます。 通常、高頻度戦略およびアルゴリズム戦略で使用されます。
GetRecords: 市場の K ライン データを取得します。 トレンドフォロー戦略でよく使用されます。 指標を計算するために使用されます。
戦略を設計する際、初心者は通常、さまざまなエラー状況を無視し、戦略内の各リンクの結果が事前に決定されていると直感的に信じます。しかし、実際にはそうではありません。戦略プログラムの実行中に市場データを要求すると、さまざまな予期しない状況が発生します。 たとえば、一部のマーケット インターフェースは異常なデータを返します。
var depth = exchange.GetDepth()
// depth.Asks[0].Price < depth.Bids[0].Price 卖一价格低于了买一价格,这种情况不可能存在于盘面上,
// 因为卖出的价格低于买入的价格,必定已经成交了。
// depth.Bids[n].Amount = 0 订单薄买入列表第n档,订单量为0
// depth.Asks[m].Price = 0 订单薄卖出列表第m档,订单价格为0
または、exchange.GetDepth() は直接 null 値を返します。
そういった奇妙な状況はたくさんあります。 したがって、これらの予測可能な問題に対しては対応する処理を行う必要があり、このタイプの処理ソリューションはフォールト トレラント処理と呼ばれます。
通常のフォールト トレランス アプローチは、データを破棄して再度取得することです。
例えば:
function main () {
while (true) {
onTick()
Sleep(500)
}
}
function GetTicker () {
while (true) {
var ticker = exchange.GetTicker()
if (ticker.Sell > ticker.Buy) { // 以 检测卖一价格是不是小于买一价这个错误的容错处理为例,
// 排除这个错误,当前函数返回 ticker 。
return ticker
}
Sleep(500)
}
}
function onTick () {
var ticker = GetTicker() // 确保获取到的 ticker 不会存在 卖一价格小于买一价格这种数据错误的情况。
// ... 具体的策略逻辑
}
他の予測可能なフォールト トレラント プロセスも同様の方法で処理できます。 設計原則は、戦略ロジックを実行するために誤ったデータを使用してはならないということです。
Kラインデータ取得、呼び出し:
var r = exchange.GetRecords()
取得された K ライン データは次のような配列になります。
[
{"Time":1562068800000,"Open":10000.7,"High":10208.9,"Low":9942.4,"Close":10058.8,"Volume":6281.887000000001},
{"Time":1562072400000,"Open":10058.6,"High":10154.4,"Low":9914.5,"Close":9990.7,"Volume":4322.099},
...
{"Time":1562079600000,"Open":10535.1,"High":10654.6,"Low":10383.6,"Close":10630.7,"Volume":5163.484000000004}
]
それぞれの中括弧が{}時間、始値(open)、最高値(high)、最低値(low)、終値(close)、取引量(volume)が含まれます。
これはローソク足です。一般的に、K ライン データは、MA 移動平均、MACD などの指標を計算するために使用されます。
K ライン データをパラメーター (原材料データ) として入力し、インジケーター パラメーターを設定してインジケーター データの関数を計算します。これをインジケーター関数と呼びます。
Inventor Quantitative Trading Platform には多くのインジケーター機能があります。
たとえば、移動平均インジケーターを計算する場合、渡した K ライン データのさまざまな期間に基づいて、対応する期間の移動平均を計算します。 たとえば、日次Kラインデータが渡された場合(1本のKラインバーは1日を表します)、計算される指標は日次移動平均です。同様に、移動平均指標関数に渡されたKラインデータが1 時間の期間の場合、計算される指標は 1 時間の移動平均です。
通常、指標を計算するときに、問題を見落としがちです。5 日間の移動平均指標を計算する場合は、まず日次 K ライン データを準備します。
var r = exchange.GetRecords(PERIOD_D1) // 给GetRecords 函数传入参数 PERIOD_D1就是指定获取日K线,
// 具体函数使用可以参看:https://www.fmz.com/api#GetRecords
日次 K ライン データを使用して、移動平均インジケーターを計算できます。5 日間の移動平均を計算する場合は、インジケーター関数のインジケーター パラメーターを 5 に設定する必要があります。
var ma = TA.MA(r, 5) // TA.MA() 就是指标函数,用来计算均线指标,第一个参数设置刚才获取的日K线数据r,
// 第二个参数设置5,计算出来的就是5日均线,其它指标函数同理。
潜在的な問題を見落としていました。r 日の K ライン データの K ライン バーの数が 5 未満の場合、どうなるでしょうか。有効な 5 日間の移動平均インジケーターを計算できますか。 答えは間違いなくノーです。 移動平均インジケーターは、一定数の K ライン バーの終値の平均を求めるものであるためです。

したがって、Kラインデータと指標関数を使用して指標データを計算する前に、Kラインデータ内のKライン列の数が指標計算の条件(指標パラメータ)を満たしているかどうかを判断する必要があります。
したがって、5日移動平均を計算する前に、判断を下す必要があります。完全なコードは次のとおりです。
function CalcMA () {
var r = _C(exchange.GetRecords, PERIOD_D1) // _C() 是容错函数,目的就是避免 r 为 null , 具体可以查询文档:https://www.fmz.com/api#_C
if (r.length > 5) {
return TA.MA(r, 5) // 用均线指标函数 TA.MA 计算出均线数据,做为函数返回值,返回。
}
return false
}
function main () {
var ma = CalcMA()
Log(ma)
}

バックテストの結果: [null,null,null,null,4228.7,4402.9400000000005, … ]
計算された 5 日間の移動平均指標の最初の 4 つは、K ラインの列数が 5 未満で平均を計算できないため、null であることがわかります。 5本目のローソク足で計算できます。
いくつかの戦略を記述する場合、各 K ライン サイクルが完了したときに、いくつかの操作を処理したり、いくつかのログを印刷したりする必要があるシナリオがよくあります。 この機能はどのように実現するのでしょうか?プログラミング経験のない初心者にとっては、どのような仕組みで対処すればよいのか思いつかないかもしれません。ここでは、いくつかのヒントを直接お伝えします。
Kライン列サイクルが完了したかどうかは、Kラインデータの時間属性から判断できます。Kラインデータを取得するたびに、最後のKライン列のデータの時間属性を判断します。このKラインデータの属性値が変更されたかどうか。変更された場合は、新しいKライン列が生成されたことを意味します(新しく生成されたKライン列の前のKライン列サイクルが変更されていない場合は、新しいローソク足が生成されていないことを意味します (現在の最後のローソク足サイクルはまだ完了していません)。
したがって、ローソク足データの最後のローソク足列の時間を記録する変数が必要です。
var r = exchange.GetRecords()
var lastTime = r[r.length - 1].Time // lastTime 用来记录最后一根K线柱的时间。
実際のアプリケーションでは、構造は通常次のようになります。
function main () {
var lastTime = 0
while (true) {
var r = _C(exchange.GetRecords)
if (r[r.length - 1].Time != lastTime) {
Log("新K线柱产生")
lastTime = r[r.length - 1].Time // 一定要更新 lastTime ,这个至关重要。
// ... 其它处理逻辑
// ...
}
Sleep(500)
}
}

バックテストでは、Kラインの期間が日に設定されていることがわかります(exchange.GetRecords関数はパラメータを指定せずに呼び出され、バックテストに従って設定されたKラインの期間はデフォルトのパラメータです)。新しいK行の列が表示され、Aログが印刷されます。
戦略が取引所のインターフェースにアクセスするのにかかる時間を表示または制御する場合は、次のコードを使用できます。
function main () {
while (true) {
var beginTime = new Date().getTime()
var ticker = exchange.GetTicker()
var endTime = new Date().getTime()
LogStatus(_D(), "GetTicker() 函数耗时:", endTime - beginTime, "毫秒")
Sleep(1000)
}
}
簡単に言うと、GetTicker 関数を呼び出した後に記録されたタイムスタンプを、呼び出し前のタイムスタンプから減算して、経過したミリ秒数、つまり GetTicker 関数が実行されて結果が返されるまでにかかる時間を計算します。
数値の上限が必要な場合は、通常Math.minを使用して制限します。
たとえば、売り注文を出す場合、注文金額はアカウント内のコインの数を超えてはなりません。 アカウント内の利用可能なコインの数より多い場合、注文時にエラーが報告されるためです。
通常は次のように制御されます。 たとえば、0.2 コインの売り注文を出す予定です。
var planAmount = 0.2
var account = _C(exchange.GetAccount)
var amount = Math.min(account.Stocks, planAmount)
これにより、発注される注文の金額がアカウントで利用可能なコインの数を超えないことが保証されます。
同様に、Math.max は値の下限値を確保するために使用されます。 これは通常どのようなシナリオに適用されますか? 通常、取引所では特定の取引ペアに対して最低注文数量制限を設けています。注文数量がこの最低注文数量を下回る場合、注文は拒否されます。この注文は失敗します。 BTC の最小注文数量は通常 0.01 であると仮定します。 取引戦略によっては、注文数量が 0.01 未満であると計算される場合があるため、Math.max を使用して最小注文数量を確保できます。
使用可能_精度を制御するには、N() 関数または SetPrecision 関数を使用します。
SetPrecision() 関数は一度設定するだけで、注文数量と価格の値の小数点以下の余分な桁がシステムによって自動的に切り捨てられます。
_N() 関数は、値を特定の小数点以下の桁数に切り捨てるために使用されます (精度制御)
例えば:
var pi = _N(3.141592653, 2)
Log(pi)
円周率の値は小数点以下2桁に切り捨てられ、3.14となる。
詳細については、API ドキュメントを参照してください。
このメカニズムを使用すると、タイムスタンプ検出方法を使用して、現在のタイムスタンプからスケジュールされたタスクが最後に完了したタイムスタンプを差し引いて、経過時間をリアルタイムで計算できます。経過時間が一定の設定時間を超えると、 、新しい操作が実行されます。
たとえば、固定投資戦略で使用されます。
var lastActTime = 0
var waitTime = 1000 * 60 * 60 * 12 // 一天的毫秒数
function main () {
while (true) {
var nowTime = new Date().getTime()
if (nowTime - lastActTime > waitTime) {
Log("执行定投")
// ... 具体的定投操作,买入操作。
lastActTime = nowTime
}
Sleep(500)
}
}
これは簡単な例です。
発明者の量子化された _G() 関数と終了保存関数を使用すると、終了して進行状況を保存し、再起動して自動的に状態を復元する戦略を設計するのが非常に便利です。
var hold = {
price : 0,
amount : 0,
}
function main () {
if (_G("hold")) {
var ret = _G("hold")
hold.price = ret.price
hold.amount = ret.amount
Log("恢复 hold:", hold)
}
var count = 1
while (true) {
// ... 策略逻辑
// ... 策略运行中,可能开仓,交易,把开仓的持仓价格赋值给 hold.price ,开仓的数量赋值给 hold.amount,用以记录持仓信息。
hold.price = count++ // 模拟一些数值
hold.amount = count/10 // 模拟一些数值
Sleep(500)
}
}
function onexit () { // 点击机器人上的停止按钮,会触发执行这个函数,执行完毕机器人停止。
_G("hold", hold)
Log("保存 hold:", JSON.stringify(hold))
}

ロボットが停止するたびにホールドオブジェクトのデータが保存され、再起動するたびにデータが読み込まれ、ホールド値が前回停止時の状態に復元されていることがわかります。 もちろん、上記は簡単な例です。実際の戦略で使用する場合は、戦略で復元する必要がある主要なデータ(通常はアカウント情報、ポジション、利益値、取引方向など)に応じて設計する必要があります。 。 もちろん、復元するかどうかを決定するための条件をいくつか設定することもできます。
上記は戦略を立てる際のヒントです。初心者や戦略開発者の皆さんのお役に立てれば幸いです。 上達する一番早い方法は練習することです!皆様の益々のご発展をお祈り申し上げます。