稼ぐ電子工作

趣味の電子工作をするために電子工作で稼ぐブログ。

組み込みC高速化 コンパイラ設定

Cで書かれた組み込みソフトウェアの高速化をする機会があったので, その時の方針をメモしておきます。

  1. バックアップをとる
  2. 演算時間を測定する
  3. 未定義動作を修正
  4. 最適化されると困る部分を修正
  5. コンパイラの最適化オプションを設定
  6. 外部入出力が減らせるように設計修正
  7. 内部処理のアルゴリズムを修正
  8. ループ最適化
  9. メモリアクセス削減
  10. キャッシュミスを減らす
  11. CPU固有の命令を使う
  12. CPUの命令セットを眺める

バックアップをとる

高速化をしようとして動かなくなったけど, もとのコードがなくて戻せないという状態にならないようにバックアップをとっておきます。 バージョン管理している場合は当たり前のように ソフトウェアを以前の状態に戻せるかと思います。 高速化のためにコードだけでなくコンパイル時の設定を変えることもあります。 Makefile や開発環境の設定も残しておかないと, コードは戻したのに同じ結果がでない状況になります。

演算時間を測定する

高速化に労力を割く前に, そもそもどこが遅いのか確認します。 成果が求められる場合は,比較対象として残しておき,高速化の証拠にします。

演算時間を測定するために,何らかの外部出力をすると思います。 最適化がかかっているソフトウェアでは測定用の出力を追加することによって, 最適化のかかり方が変わる場合があります。 実動作と大きな差がないか確認しながら 測定用出力を少しずつ挿入します。

  1. 外部入力・出力の時間と,内部処理の時間を測定
  2. 内部の機能ごとの時間を測定
  3. 特に時間がかかっている機能を細分して測定

未定義動作を修正

内部処理の時間はコンパイラの最適化設定を見直すだけで解決することがあります。 コンパイラは賢いので, 人間が勝手な思い込みでつくったおかしい部分を吹っ飛ばす場合があります。 未定義動作で検索すると大量に出てくる例を参考にしながら, 常に意図した通りに解釈される正しい記述に改めます。

最適化されると困る部分を修正

コード上現れず,コンパイラが認識できない値変化や, 一見すると無駄な外部機器への入出力の記述などは 最適化をかけると意図した通りにならない場合があります。

最適化をかけてほしくない入出力変数などには volatile をつけておきます。

  • 外部機器の同じ設定レジスタを何度も読み書きしている部分
  • 外部機器が更新した値の読み込み
  • 値変更がなくても定期的の書き込む必要がある外部機器への出力
  • 1関数内の処理だが,実は割り込みで中断され,途中で値が変わる変数

コンパイラの最適化オプションを設定

未定義動作修正・volatile 設定を必要な部分に施したら, コンパイラの最適化オプションを変えて,最適化をかけてみます。 高速化にはほぼ労力を払うことなく高速化が成功する場合があります。

次回はソフトウェアの設計を改善して高速化します。

  • 外部入出力が減らせるように設計修正
  • 内部処理のアルゴリズムを修正

ラズパイで仮想通貨取引 LINE Notify で状態通知

前回は購入価格平均値を使って簡易に取引価格の谷と山を判別し, 売買のタイミングを決定しました。 pkeisrebpys.hatenablog.com

今回は,この取引システムを連続運用するにあたり, 定期的に状態を通知して異常が起こっていないか確認します。 通知には LINE Notify を使います。

LINE Notify でメッセージを遅れるようにする

LINE Notify でメッセージを送るためには, 以下の LINE Nofity のページに LINE のアカウントでログインして アクセストークンを発行します。

https://notify-bot.line.me/ja/

以下のように LINE Notify の API に接続すれば, メッセージが送信できます。

${token} には取得したトークン文字列, ${message} には送信したいメッセージを入力します。 うまく送信できると 200 が返ってきます。

curl -X POST -H "Authorization: Bearer ${token}" -F "message=${message}" https://notify-api.line.me/api/notify 2>/dev/null | \
  jq -r ".status"

以下のようにスクリプトファイルを作っておくと, スクリプトの引数として与えた文字列を送信できます。 YOUR_TOKEN は取得したトークンに置き換えてください。

token="YOUR_TOKEN"
message="$*"
curl -X POST -H "Authorization: Bearer ${token}" -F "message=${message}" https://notify-api.line.me/api/notify 2>/dev/null | \
  jq -r ".status"

送信する状態をまとめる

作成したスクリプトの引数に与えて送信する文字列を作ります。 とりあえず,システムの状態として取引ペアごとの平均購入価格と, 買い注文が約定している取引の数を取得します。

データベースから以下のようにして表示用の文字列を取得します。

showAverage() {
  sqlite3 -header -column ${fpath_order} "select pair,avg(buyprice) from orders where state!='buy' group by pair;select pair,count(id) from orders where state!='buy' group by pair;"
}

これで取引ペアごとに集計した平均購入価格と買い注文が約定した取引数を 表示に適した形で取得できます。

この関数の出力文字列を前節のスクリプトに与えれば通知完了です。

30 分おきなど,定期的に送るように設定しておきます。

概要 pkeisrebpys.hatenablog.com

ラズパイで仮想通貨取引 平均値を基準に取引

前回はデータベースを導入して,取引情報を保持することで, 複数の取引を平行して行い,取引機会を増やしました。 取引機会が増えることで,より多くの取引差益が期待できます。 pkeisrebpys.hatenablog.com

しかし,買い注文価格に対して一定の割合を掛けて売り注文を計算する方法だと, 1度の取引が成功した場合に得られる利益はほぼ一定になります。

理想的には安い時に買ってしばらく売らずに待っておき, 高くなってきたら売ることで,1取引で得られる利益を大きくしたいです。 取引価格チャートで言えば, 価格の谷に入り始めたら買い, 谷の高さの半分くらい1まで抜けて山に近づいてきたら売るようなことがしたいです。

このような取引に近い動きを簡易に実現するため, 購入価格の平均値を基準に売り買いのタイミングを制御します。

取引方法

簡易な方法のため取引方法は以下の通り単純です。

  1. 購入価格平均値を取得する
  2. 平均値 > 現在の取引価格 なら買い注文を出す
  3. 平均値 < 現在の取引価格 なら現在の取引価格より安い購入価格の取引を選んで売り注文を出す
  4. この処理を定期的に実行する

手順の 2. で価格の谷に落ちていく時から谷を抜けて上がる途中まで買い続けます。

手順の 3. で谷の半分くらいまで価格が回復したところで山と判定して売り始めます。

購入価格平均値の取得

前回導入したデータベースを使えば, 取引に関する統計値を簡単に取得できます。 たとえば約定済みの買い注文の価格と購入数量から 購入価格の平均値を算出できます。

今回は (取引価格と購入数量の掛け算が面倒なので) すべての取引で一定の数量を購入することにします。 この場合,購入価格の平均値は以下で取得できます。

getAvgCost() {
  local pair=$1
  sqlite3 ${fpath_order} "select avg(buyprice) from orders where pair='${pair}' and state!='buy';"
}

前回,買い注文約定までは state == 'buy' としたので, state != 'buy' を条件にすることで,売り注文を出すのを待機しているか, 売り注文を出して約定待ちかを選択できます。

平均値より安い購入価格の取引の選択

手順 3. で購入価格平均値より安い購入価格を持つ取引データを選択する必要があります。 購入価格がまんべんなくバラけている場合, 取引データの半数くらいは平均値より安くなります。 この中で最も平均値に影響を与えにくくて,かつ取引価格下落時に売りにくくなるのは, 最も購入価格が高い取引です。 このような取引の ID は以下の refprice に平均購入価格を入力することで選択できます。

getIdBuyLessThan() {
  local pair=$1
  local refprice=$2
  sqlite3 ${fpath_order} "select min(id) from orders where buyprice=(select max(buyprice) from orders where pair='${pair}' and state='wait' and buyprice < ${refprice});"
}

まず,購入価格が平均購入価格より安い取引のうちで最も購入価格が高い (平均購入価格に一番近い) 取引の購入価格を出します。 次に,その購入価格を持つ取引 ID を取り出します。 購入価格が同額の取引が複数存在する可能性があるため, 取引 ID が最も小さいものを選びます。

これで簡易な方法ではあるものの, 価格の谷,山を区別して取引することができます。

この取引システムを連続動作させる場合, システムが異常をおこしていないか定期的にチェックしたくなります。

次回は定期的にシステムの状態を通知してみます。 pkeisrebpys.hatenablog.com

概要 pkeisrebpys.hatenablog.com


  1. 実際には将来の価格推移がわからないので谷の高さの半分くらいかどうか取引時点ではわかりませんが。

ラズパイで仮想通貨取引 データベースで取得価格の記録

前回は同時に1取引のみを繰り返しました。 pkeisrebpys.hatenablog.com

今回は時間的に分散させながら,複数の取引を平行して実行してみます。 複数の取引を平行させる場合,それぞれの注文 ID を覚えておく必要があります。 平行する取引数が少ない場合は適当に変数に格納してもよいのですが, 将来のことを考えて,データベースで取引情報を管理することにします。

データベースの導入

ちょっとしたデータを管理するために, データベースサーバを立ち上げるのは面倒なので, ファイル単位でデータベースをつくれる sqlite3 を使うことにしました。

最初にデータベースファイルをつくり,注文情報を格納するテーブルを作成しました。 以下のコマンドで ${fpath_order}sqlite3 のデータベースファイルを作ります。 テーブルの項目は id, pair, order_id だけ保存して, API でその他の情報を取得してもよいのですが, なるべく通信回数を抑えるため, 取引に必要な情報はデータベース上に残すことにしました。

fpath_order="/dev/shm/order.sqlite3"
sqlite3 ${fpath_order} "create table orders (id UNIQUE,pair,state,amount,buyprice,sellprice,order_id);"
  • id は発注順に振っていきます。取引に使う order_id とは異なります。
  • pair は取引ペア文字列です。
  • state は取引状態を示します。
  • amount は取引数量です。
  • buypricesellprice はそれぞれ買い注文,売り注文時の価格です。
  • order_id は現在発注している注文 ID です。

一つの id に前回の買い注文・約定・売り注文・約定の一連の取引を紐付けます。 このため,一つの id に対して買い注文価格・売り注文価格が紐づきます。 order_id は注文するたびに変更します。

取引状態を示す state は以下のようにしました。 単純な取引では買い注文が約定した後, すぐに売り注文に移りますが, 今後,より良い条件が整うまで売り注文を待つ可能性を考えて, 状態 wait を入れています。

  • 買い注文を出してから約定するまでを buy
  • 回注文が約定してから,売り注文を出すまでをwait
  • 売り注文を出してから約定までを sell

売り注文が約定したら,紐付けていた id を含むデータを削除します。

買い注文の記録

買い注文を出すときは,データベースに新たなデータを追加します。 cmd_id はデータベース側の id です。 連番なんかで重複しないようにします。

addNewBuy() {
  local cmd_id=$1
  local pair=$2
  local price=$3
  local amount=$4
  if [ "${amount}" = "" ]; then
    return 1
  fi
  sqlite3 ${fpath_order} "insert into orders (id,pair,state,amount,buyprice) values (${cmd_id},'${pair}','buy',${amount},${price});"
  return $?
}

注文 ID の記録

実際に買い注文を出して,注文 ID が取得できたら ID を記録します。 cmd_id はデータベース側の ID , order_id は取引に使う注文 ID です。

setOrderID() {
  local cmd_id=$1
  local order_id=$2
  sqlite3 ${fpath_order} "update orders set order_id=${order_id} where id=${cmd_id};"
  return $?
}

注文 ID の取得

注文状態を確認するために特定の取引の注文 ID を取得します。 データベース側 ID を引数に注文 ID を取得します。

getOrderID() {
  local cmd_id=$1
  sqlite3 ${fpath_order} "select order_id from orders where id=${cmd_id};"
}

待機状態の記録

注文が約定していたら,待機状態に設定します。

setOrderWait() {
  local cmd_id=$1
  sqlite3 ${fpath_order} "update orders set state='wait', order_id=NULL, sellprice=NULL where id=${cmd_id};"
  return $?
}

売り注文の記録

待機状態の取引を売りに出す前にデータベースに売り注文の情報を記録します。 数量は買い注文時と同数にするので, 売り価格のみ記録します。

setSellPrice() {
  local cmd_id=$1
  local price=$2
  if [ "${price}" = "" ]; then
    return 1
  fi
  sqlite3 ${fpath_order} "update orders set sellprice=${price}, state='sell' where id=${cmd_id} and state='wait';"
  return $?
}

実際に発注して注文 ID が取得できたら,買い注文時と同様に記録します。

売り注文約定時の記録

売り注文が約定したら取引終了なのでデータを削除します。

deleteOrder() {
  local cmd_id=$1
  sqlite3 ${fpath_order} "delete from orders where id=${cmd_id};"
  return $?
}

実際の取引の流れ

これでデータベースに取引の情報を記録できるようになりました。 実際に取引を行うときは,以下のようになります。

  1. 買い注文を定期的に出す。
  2. state == 'buy' の取引状態を確認して約定していたら state == 'wait' にする
  3. データベースで state == 'wait' となっている取引を探す
  4. state == 'wait' が見つかったら買い注文価格より高い価格で売り注文を出す
  5. state == 'sell' の取引状態を確認して約定していたらデータを削除する

次回は取引価格の変動を簡易に判定して売買のタイミングを調整します。 pkeisrebpys.hatenablog.com

概要 pkeisrebpys.hatenablog.com

ラズパイで仮想通貨取引 簡単なロジックでの取引

前回までで,取引価格の確認・発注・注文の確認・キャンセルができるようになりました。 pkeisrebpys.hatenablog.com

今回は単純な手順で売買を自動でくりかえしてみます。

取引の流れ

最初の自動取引なので,稼げるかどうかはおいておきます。 とりあえず,自動で繰り返し売買ができるかを確認します。

取引の流れは非常に単純です。

  1. 現在の取引価格,最安の売り注文価格を確認し最安値を基準価格とする
  2. 基準価格より安い価格で買い注文を出す
  3. 出した注文が約定したか定期的に確認する
  4. 約定したら現在の取引価格,最高の買い注文価格,約定価格を確認し最高値を基準価格とする
  5. 基準価格より高い価格で売り注文を出す
  6. 出した注文が約定したか定期的に確認する
  7. 約定したら 1. に戻る

更に単純にするために約定価格は注文価格と同一だと仮定します。

この流れで調整できるパラメータは2つです。

  • 買い注文を出す時に基準価格より何 % 安くするか
  • 売り注文を出す時に基準価格より何 % 高くするか

売り注文時のパラメータは大きくするほど1回の取引の流れでの利益を大きくできます。 ただし大きな値を設定すると取引価格から乖離するため,約定しにくくなります。

買い注文時の値も大きくするとなかなか約定しなくなります。

逆に両方のパラメータともあまり小さくすると, 手数料が不利な条件での取引になる可能性が高まります。 今回は指値注文に post_only 条件をつけているため, 注文が自動でキャンセルされることが多くなります。

取引の値動きを見て,ほどよい値に調整する必要があります。

取引スクリプト

基本はいままでに作った関数やスクリプト片をつないてつくります。 取引価格の大小比較や,注文価格の計算は bc コマンドを使うと簡単です。

この方法の欠点

取引ができるかお試しでやった方法なので, この方法では1回の取引に大金を入れるような無謀なことをしない限り, 利益も損失も僅かです。 大金を1度に入れると運がよければ利益がでますが,発注開始のタイミングが悪くて, 高値の時だと大損します。

次回は1度の取引は少額で,時間的に分けて複数の取引を行うことで, もう少しましな取引をやってみます。 pkeisrebpys.hatenablog.com

概要 pkeisrebpys.hatenablog.com

ラズパイで仮想通貨取引 APIの使用 取引編 注文状態確認・キャンセル

前回は rest-api を用いて注文を発注し,その成否を確認しました。 pkeisrebpys.hatenablog.com

今回は,発注した注文が成立したか API で確認します。 また,発注を取り消したい場合に備えてキャンセルをしてみます。

注文状態の確認,キャンセルともに基本的な流れは発注と同様で, 返ってくる値も注文の状態なので成否判定も発注と同じ処理が使えます。 ただし,前回行った発注や注文のキャンセルは POST でリクエストするのに対し, 注文の確認は GET でリクエストする必要があります。

以下に GET リクエストの場合の処理を関数にまとめました。

api_get_req() {
  local path="$1"
  [ "${path}" != "" ] || return 1
  local access_nonce="$(date +%s%3N)"
  local signature=$(echo -n "${access_nonce}${path}" | openssl dgst -sha256 -hmac "${API_SECRET}" | sed -e "s/(stdin)= //")
  curl -m 10 -s -H 'ACCESS-KEY:'"$API_KEY"'' -H 'ACCESS-NONCE:'"$access_nonce"'' \
    -H 'ACCESS-SIGNATURE:'"${signature}"'' \
    ${api_api_url}${path}
}

注文状態の確認

注文状態を確認するために必要な情報は2つです。

  • ペア pair
  • 注文 ID order_id

ペアは発注時同様です。 円でビットコインを売り買いする場合は btc_jpy となります。

注文 ID は前回最後の発注時の成否判定で得られる ID を入力します。

処理を関数にまとめると以下となります。 GET リクエストのため,パラメータは URL に含めます。

api_getOrder() {
  local pair="$1"
  local order_id="$2"
  exist_pair ${pair} || return 1
  local path="/v1/user/spot/order?pair=${pair}&order_id=${order_id}"
  api_get_req ${path}
}

前回同様,注文確認が成功したか判定し,失敗したら 0 ERROR を, 成功したら確認した注文の ID と状態を返すようにします。

ret=$(api_getOrder ${pair} ${order_id} | parse_order)
if [ "${ret}" = "" ]; then
  ret="0 ERROR"
fi

これで,発注した注文が約定したか判定できるようになります。

注文のキャンセル

あまりに現在の取引価格から外れた価格で発注すると, いつまでたっても約定せず,注文が残り続けます。 当分約定しない注文であっても, 注文に出した分の資産は新しい注文には使えません。 二重に発注するなんていう都合の良いことでできないのは, 当たり前といえば当たり前です。

取引の差額で稼ぐことを考えると, なるべく取引を繰り返して利益を増やしたいところです。 いつまでも約定せず待っているだけというのは非常にもったいないです。

そこで,当分約定しないなと思った注文をキャンセルできるようにします。

以下がキャンセル処理をまとめた関数です。 pairorder_id は注文の確認と同様です。

api_cancelOrder() {
  local pair="$1"
  local order_id="$2"
  exist_pair ${pair} || return 1
  local path="/v1/user/spot/cancel_order"
  local body="{\"pair\": \"${pair}\", \"order_id\": \"${order_id}\"}"
  api_post_req "${path}" "${body}"
}

成否判定を含めた流れは発注・注文確認と同様です。

ret=$(api_cancelOrder ${pair} ${order_id} | parse_result)
if [ "${ret}" = "" ]; then
  ret="0 ERROR"
fi

次回は簡単な取引を繰り返してみます。 pkeisrebpys.hatenablog.com

概要 pkeisrebpys.hatenablog.com

ラズパイで仮想通貨取引 APIの使用 取引編 発注

前回は public-api を用いてティッカー情報を取得しました。 pkeisrebpys.hatenablog.com

今回は rest-api を用い,事前に取得したトークンで認証・発注を試します。

rest-api 使用の流れ

前回のティッカー情報取得は誰がやっても問題がなく, 同じ結果が返ってくるものでした。 一方で発注や資産状況の確認は, それを行おうとしている人に応じて取引所の処理が異なります。 誰が出した注文なのか,そしてその注文をしたのが確かに本人 (の意思に基づいて動いているシステム) からのものなのか, 確認する必要があります。

rest-api ではパラメータに注文情報の他, ユーザ識別のための API_KEY を送信して, 誰の注文であるのか識別します。 そしてそれが本当に本人の注文であることを証明するため, 注文情報と適当な数値のハッシュを本人しか持たない鍵 API_SECRET を使って求めます。

このため public-api よりも送信するパラメータが増え, ハッシュを求める手順が増え,複雑になりますが, 一度関数化してしまえばスクリプト上での取り扱いは public-api と同様になります。

発注の下準備

下準備として,シェルスクリプトの関数として API の仕様手順をまとめておきます。

エンドポイント URL の定義

エンドポイント URL を変数に格納しておきます。 また,事前に取得した API キー,トークンを API_KEY API_SECRET に格納します。

api_api_url="https://api.bitbank.cc"
API_KEY=""
API_SECRET=""

rest-api アクセス関数

public-api では path のみを引数に与えました。 rest-api では肝心の取引情報のほか, 認証のために追加のパラメータが必要です。

取引情報は ${body}JSON 形式で与えます。 JSON 文字列をどう作るかはひとまずおいて, 先に通信部分をつくります。

認証のため,取引情報と適当な数値 access_nonceAPI_SECRET でハッシュにします。 これが本人が出した取引であることを示す署名 signature になります。

acccess_nonce は通信の度に増加する数値であればよく, 通常は UNIX タイムとするとあります。 UNIX タイムといえば 10 桁の秒単位の数値だと思って, 最初は秒単位で入力していました。 これだと1秒以内に複数回の取引を出すと失敗します。 acccess_nonce としてミリ秒単位の数値を与えることで, 短時間に複数の取引を出してもエラーにならないようにしました。

api_post_req() {
  local path="$1"
  [ "${path}" != "" ] || return 1
  local body="$2"
  [ "${body}" != "" ] || return 1

  local access_nonce="$(date +%s%3N)"
  local signature=$(echo -n "${access_nonce}${body}" | openssl dgst -sha256 -hmac "${API_SECRET}" | sed -e "s/(stdin)= //")
  curl -s -H 'ACCESS-KEY:'"$API_KEY"'' -H 'ACCESS-NONCE:'"$access_nonce"'' \
    -H 'ACCESS-SIGNATURE:'"${signature}"'' \
    -H "Content-Type: application/json" \
    -d ''"${body}"'' \
    ${api_api_url}${path}
}

注文に必要な情報

注文に必要な情報は,以下の項目です。

  • どの通貨ペアを取引するか pair
  • 価格はいくらか price
  • 数量はいくつか amount
  • 取引方向は買いか売りか side
  • 注文方法は指値か成行か thetype

通貨ペアはティッカー情報取得の際と同様の項目です。

価格

価格はペアの後ろの通貨で示すようです。 btc_jpy なら jpy なので日本円で 1 ビットコインがいくらかを指定します。

数量

数量はペアの前の通貨の量を指定します。 btc_jpy なら btc なので何ビットコイン取引するかを指定します。

取引方向

ペアの後ろの通貨を出して前の通貨を受け取る場合,買い buy, ペアの前の通貨を出して後ろの通貨を受け取る場合,売り sell です。 btc_jpy で円を払ってビットコインを受け取るのは buyビットコインを払って円を受け取るのは sell になります。

以下の関数でおかしな引数を弾きます。

exist_side() {
  local side="$1"
  case $side in
    buy)
      return 0;;
    sell)
      return 0;;
    *)
      log "Invalid side."
      return 1;;
  esac
}

注文方法

注文方法には指値と成行があります。 それぞれ,取引成立までの速さ,価格が指定できるか,手数料の高低で得失があります。

注文方法の指値は指定した価格で取引できるまで待ちます。 例えば現在の取引価格が 100 円とし, 99 円で買い注文を出した場合, 取引価格が 99 円になるまで待たされます。 取引価格が 99 円になってからも自分より先に発注していた人の注文が満たされるまで,自分の番は回ってきません。

成行は価格はいくらでもよいので,現在の取引価格で取引します。 例えば現在の取引価格が 100 円で,買い注文を出すと, 100 円で取引が行われます。 ただし,成行注文も先に発注していた人の注文が満たされるまで,取引順は回ってきません。 100 円で買えると思っていたら, 100 円の売り注文がすべて他の人に回収され, 自分の番では 100 円より高い取引価格になっている場合があります。

指値注文は取引成立まで遅れがある代わりに価格が指定でき, 成行注文は速やかに取引成立する代わりに価格が変化します。

仮想通貨取引所では指値注文をして一度取引相手が出るのを待った場合と, 成行注文や指値注文をしたが既に取引相手がいる価格だったため待たなかった場合で, 手数料が異なります。 一度待った場合手数料は安く,待たなかった場合手数料が高くなります。

なるべく手数料を抑えたいと考えると成行ではなく,指値で注文したくなります。 しかし,指値で注文しても,タイミングよく (悪く) 同じ価格で既に逆方向の注文をした人がいると,即座に取引成立して手数料は高くなります。 このような事態になる場合,注文をキャンセルする機能があります。

以下の関数で取引方法の引数が正しいか確かめます。

exist_type() {
  local thetype="$1"
  case $thetype in
    limit)
      return 0;;
    market)
      return 0;;
    *)
      log "Invalid type."
      return 1;;
  esac
}

発注関数

以下がすべてをまとめた発注関数です。 取引条件を引数に与えると,取引条件を JSON 文字列にし, 発注します。

指値注文の場合は,手数料が有利な場合のみ注文するように, post_only のオプションを有効にしています。

api_newOrder() {
  local pair="$1"
  local price="$2"
  local amount="$3"
  local side="$4"
  local thetype="$5"
  local is_post_only="false"
  exist_pair ${pair} || return 1
  exist_side ${side} || return 1
  exist_type ${thetype} || return 1
  if [ "${thetype}" = "limit" ]; then
    is_post_only="true"
  fi
  local path="/v1/user/spot/order"
  local body="{\"pair\": \"${pair}\", \"price\": \"${price}\", \"amount\": \"${amount}\",\"side\": \"${side}\", \"type\": \"${thetype}\", \"post_only\": ${is_post_only}}"
  api_post_req "${path}" "${body}"
}

発注成否確認

発注関数を実行し,成功すると注文情報が JSON 形式で返ってきます。 場合によっては, そもそも通信がうまくできなかったり, 入力データや認証情報が正しくなかったりして発注が失敗する場合があります。 発注が失敗した場合に対処できるように, 発注の成否を判定できるようにします。

api_newOrder 関数の出力を以下の parse_result に入力します。 通信が成功して APIJSON を返して来た場合で, 発注が成功した場合にのみ,注文 ID と注文状態を取得します。 それ以外の場合は何も返さないようにします。 以下の処理では発注が成功した場合の JSON データのみを読み込むため, select(.success == 1) でデータを絞り込んでいます。

parse_result() {
  jq -r "select(.success == 1) | [.data.order_id, .data.status] | @sh" |\
    sed "s/'//g"
}

parse_result の出力が空文字列かどうか判定することで, 発注の成否を判定します。 発注を含めた全体の流れは以下となります。

ret=$(api_newOrder ${pair} ${price} ${amount} ${side} ${thetype} | parse_result)
if [ "${ret}" = "" ]; then
  ret="0 ERROR"
fi
echo ${ret}

成功した場合は,注文 ID と注文状態が出力され, 失敗した場合は,注文 ID は 0,注文状態は ERROR となります。 なお,注文 ID と注文状態は公式ドキュメントに情報があります。

次回は注文状態の確認・キャンセルを試します。 pkeisrebpys.hatenablog.com

概要 pkeisrebpys.hatenablog.com