カスタム取引戦略の開発方法

CoinTraderでは、カスタム取引戦略機能により、ユーザーが自由にボットの取引戦略を組み立てることができます。
このドキュメントでは、カスタム取引戦略のプログラムの仕様とAPIについて説明します。

取引プログラムの概要

取引プログラムはJavaScript言語によって記述します。NodeJS 9.0 で実行可能なプログラムである必要があります。

取引プログラムは必ずtick関数をエクスポートする必要があります。
tick関数はボットの実行間隔毎に呼び出される関数です。ボットは最低1分から指定の間隔で実行することができます。

ここで簡単な取引プログラムの例を見てみましょう。

const {trade} = require('cointrader')

exports.tick = async (context) => {
  const {params} = context

  // 通貨ペア
  const pair = params.currency_pair
  // 一回あたりの購入金額(JPY)
  const amount = params.amount

  const ticker = await trade.getTicker()
  const tradingPrice = ticker.last * 1.2
  const tradingAmount = amount / ticker.last
  await trade.order({
    pair: pair,
    action: 'buy',
    price: tradingPrice,
    amount: tradingAmount,
  })
}

このプログラムは実行毎に指定の日本円の金額分だけ通貨を購入するプログラムです。ドルコスト平均法とよばれる積立投資戦略をもっとも簡単に記述した例です。
tick関数にはさまざまな情報を保持するcontextパラメータが渡されます。contextパラメータのもっとも重要なプロパティはparamsプロパティです。paramsを使ってボットに設定した変数が取得できます。
この例では通貨ペアと一回の実行あたりの購入金額をparamsから取得しています。

  const ticker = await trade.getTicker()
  const tradingPrice = ticker.last * 1.2
  const tradingAmount = amount / ticker.last

この部分では trade.getTicker() APIを使って最新の取引データを取得し、注文の金額と数量を計算しています。
ticker.last から最後の取引の価格が取得できますので、これを使って指定金額分の注文数量を計算し、trade.order()APIにより注文を行なっています。

ライフサイクルと設定

取引プログラムではtick関数のようにエクスポートできる関数がいくつかあります。

config関数

この関数は取引プログラムの動作に関わる設定を行うためにあります。現時点では永続ストレージを有効化するためにのみ利用できます。

exports.config = async () => {
  return {
    useStorage: true,
  }
}

onStart関数

onStart関数はボットが起動する時に一度だけ呼ばれます。永続ストレージの初期化などに使用することができます。

exports.onStart = async (context) => {
  context.storage.start_time = Date.now()
}

contextパラメータ

contextパラメータはonStart関数とtick関数に渡されるさまざまな情報を持ったパラメータです。このパラメータが持つプロパティを以下に示します。

context.params: ボットの設定で指定した変数値
context.storage: 永続ストレージ
context.bot.id: ボットのID
context.bot.tick_count: ボットの実行回数

パラメータの定義

context.paramsにはボットに設定したパラメータが設定されます。
ボットに設定できるパラメータはカスタム取引戦略の取引戦略編集画面で定義することができます。

なお、次のパラメータ名は予約されています。これらは、取引戦略プログラムからは自由に参照できますが、
パラメータ定義に追加してはいけません。

  • currency_pair: 通貨ペア
  • exchange: 取引所の識別コード

メモリと永続ストレージ

取引プログラムは実行される度にメモリが初期化されます。つまり、tick関数の外のスコープに宣言された変数であっても、次回の実行時には再度初期化され、情報は保持されません。

情報を保持するには永続ストレージを利用します。永続ストレージを利用するには、config関数をエクスポートし、永続ストレージの使用を宣言する必要があります。

exports.config = () => {
  return {
    useStorage: true
  }
}

永続ストレージにはcontext.storageでアクセスできます。永続ストレージでボットの実行をカウントする例を示します。

exports.tick = async (context) => {
  if (typeof context.storage.count === 'undefined') {
    context.storage.count = 1
  } else {
    context.storage.count += 1
  }
}

少々上記の例は煩雑に感じたかと思います。永続ストレージの初期化にはonStart関数を使うと便利です。onStart関数はボットの起動時に一度だけ呼ばれる関数です。onStart関数を使って先ほどのプログラムを改良した例です。

exports.onStart = async (context) => {
  context.storage.count = 0
}
exports.tick = async (context) => {
  context.storage.count += 1
}

永続ストレージに保存できるデータと有効期間

永続ストレージには基本データ型の値のみ保持できます。クラスインスタンスなどの基本データ型以外のデータを代入した場合の動作は保証されません。

データはJSON文字列に変換した時に50KBを超えてはいけません。50KBを超えるデータを更新しようとした場合はエラーとなります。

データは最大で5日間保持されます。この期間に永続ストレージの更新が発生しない場合、データは消去されます。

永続ストレージのコミットとロールバック

永続ストレージは値を代入したタイミングで保存されるわけではないことに注意してください。
永続ストレージはonStart()もしくはtick()が正常終了した場合にだけ値が確定し、次の実行で参照できるようになります。

APIについて

画面左のメニューよりAPIの詳細をご覧になれます。
require('cointrader') によってそれぞれのクラスのインスタンスを取得できますので、取引ボット内でそれぞれのAPIのクラスのコンストラクタを呼び出す必要はありません。

次のようにすべてのAPIのクラスのインスタンスをインポートできます。

const {trade, bot, log, notification, storage} = require('cointrader')

利用できるライブラリ

テクニカル分析のためにnode-talibを使用できます。

サンプルプログラム

こちらのGitHubリポジトリでサンプルプログラムがあります。
https://github.com/OnetapInc/cointrader-bot-example

制限

  • ソースコードは50KB未満である必要があります。
  • ボットの一回の実行時間は30秒以内に完了する必要があります。これを超えた場合は強制終了します。
  • 過度にCPUやメモリを消費するボットの稼働は禁止します。
  • 定められたAPI以外を使ってネットワークまたはディスクのIOが発生する処理は禁止します。