FX BOT 4: AWS Cloud9へのBOT実装で注意すること5つ

FX BOT 4: AWS Cloud9へのBOT実装で注意すること5つ

はじめて作成したBOTを、はじめてAWS Cloud9で稼働してみて気がついたことと、その対応のまとめ。長期間に渡って安定稼働するための注意点でもあると思います。今後も気がついたことがあれば追記していく予定です。

AWS Cloud9へのBOT実装で注意すること5つ

  1. Cloud9の環境を python2.7 → python3.6
  2. ta-libのインストール
  3. Google APIへの接続
    1. トークンのリフレッシュが必要に
    2. 503エラーが発生 → APIの制限オーバー?
  4. 稼働状況の把握
    1. 稼働詳細:テキストのログ
    2. 稼働概要:Discordに通知
    3. 現在のポジションと損益:OANDA
    4. 資金の推移と売買の履歴:Googleシート
  5. システムのデーモン化
    1. 再起動でBOT独自の情報を引き継ぐように
    2. シェルスクリプトでpythonファイルを複数回動かす
    3. サービスに登録する

1. Cloud9の環境を python2.7 → python3.6

大まかな切り替えは以前の記事で完了していたが、aliasupdate-alternativesの変更も必要だった。以下の記事を参考に簡単に対応できた。

Pythonのaliasを変更

viで編集

$ vi ~/.bashrc

alias python=python27alias python=python36に変更する。

変更を反映

$ source ~/.bashrc
出典元【Python】Cloud9上でPython3系を使うとき絶対にやっておくべき環境設定【AWS】

update-alternativesの設定

そもそもupdate-alternativesが何なのか。はじめて見た単語だったので調べてみた。ここでは、「類似のプログラムの内どちらを使用するかを指定する」ということをしているよう。

で、設定方法。

pythonの設定を開く

$ sudo update-alternatives --config python

バージョン別のPythonがいくつか表示されるので、/usr/bin/python3.6の番号を入力する。

目次へ

2. ta-libのインストール

AWS cloud9 ta-libのインストールでつまづいたが、「Cloud9でTA-Libをインストールする - Qiita」で紹介されているコードですんなり解決できた。

ta-libのインストール

wget http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz
tar -zxvf ta-lib-0.4.0-src.tar.gz
cd ta-lib
./configure --prefix=/usr
make
sudo make install

sudo bash -c "echo "/usr/local/lib64" >> /etc/ld.so.conf"
sudo /sbin/ldconfig
sudo pip install ta-lib
出典元Cloud9でTA-Libをインストールする - Qiita

目次へ

3. Google APIへの接続

数日間、実際に継続運用してみると、短期間のテストでは見えなかった問題がいくつか発生した。それぞれ以下の方法で対処し、現在は問題なく稼働している。

3―1. トークンのリフレッシュが必要に

修正後のコードも記録しておく。gs_client.login()が該当のコード。

n = 0
while True :
  try : 
    gs_client = gspread.authorize( self.google_credentials )
    gs_sh = gs_client.open_by_key( self.spreadsheet_key ).worksheet( sh_name )
    if self.google_credentials.access_token_expired :
        gs_client.login() # refreshes the token
    return gs_sh.append_row( values ,'USER_ENTERED' )
  except APIError as e :
    self.__logger.error( f"SheetのAPIでエラー発生 : {e}" )
    self.__logger.error( f"{2**n}秒待機してやり直します" )
    time.sleep( 2 ** n )
    n = n + 1
  except RequestException as e :
      self.__logger.error( f"Requestでエラー発生 : {e}" )
      self.__logger.error( f"{2**n}秒待機してやり直します" )
      time.sleep( 2 ** n )
      n = n + 1

目次へ

3―2. 503エラーが発生 → APIの制限オーバー?

トークンリフレッシュが実装できて安心していたところ、今度は別のエラーが発生。以下が原因。

出典元Search returns error "503: Service Unavailable" - Google Search Appliance Help

つまり、APIの制限をオーバーしているらしい。

  • 500回/100秒 1プロジェクト(500 requests per 100 seconds per project)
  • 100回/100秒 1ユーザー(100 requests per 100 seconds per user)
  • 日ごとの制限はなし(There is no daily usage limit)
出典元Usage Limits  |  Sheets API  |  Google Developers

回数を減らして無事解決。

目次へ

4. 稼働状況の把握

当初は、Cloud9上でのprintとGoogleシートのみで稼働状況を把握していたが以下に改善を行った。

4―1. 稼働詳細:テキストのログ

  • 方法:サーバーにテキストでログを残す
  • 目的:正常に動作しているかの確認やエラー箇所の特定に使用する。大量にログを残しておく。

いくつかの記事を参考に、「Pythonのロギングを覚えた - Qiita」に掲載されているコードを組み込んだ。

loggerを初期化する関数

def setup_logger(name, logfile='LOGFILENAME.txt'):
  logger = logging.getLogger(name)
  logger.setLevel(logging.DEBUG)
  
  # create file handler which logs even DEBUG messages
  fh = logging.FileHandler(logfile)
  fh.setLevel(logging.DEBUG)
  fh_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(filename)s - %(name)s - %(funcName)s - %(message)s')
  fh.setFormatter(fh_formatter)
  
  # create console handler with a INFO log level
  ch = logging.StreamHandler()
  ch.setLevel(logging.INFO)
  ch_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s', '%Y-%m-%d %H:%M:%S')
  ch.setFormatter(ch_formatter)
  
  # add the handlers to the logger
  logger.addHandler(fh)
  logger.addHandler(ch)
  return logger

使い方

# 各モジュールの最初のほうで、グローバルに宣言しておく
logger = setup_logger(__name__)
出典元Pythonのロギングを覚えた - Qiita

以下は参考にした記事。

保留中の課題

大きな問題ではないが、以下が解決できていない。

目次へ

4―2. 稼働概要:Discordに通知

  • 方法:注文と売買をdiscordに通知
  • 目的:サクッと動作を確認する。スマホにPush通知がくるので便利。

LINE通知よりも便利なDiscord通知を使ってみよう!|asim|note」で簡単に実装できた。Discordは他にも使えそう。便利。

discord

# coding: utf-8
import requests

def discord(message):
  # Discordで発行したWebhookのURLを入れる
  discord_webhook_url = 'あなたのWebhookURL'
  data = {"content": " " + message + " "}
  requests.post(discord_webhook_url, data=data)

discord('discord通知テスト')
出典元LINE通知よりも便利なDiscord通知を使ってみよう!|asim|note

目次へ

4―3. 現在のポジションと損益:OANDA

  • 方法:OANDA
  • 目的:サクッと損益やチャート、トレード状況を確認する。

これは特に作業をしたわけではない。スマホにアプリを入れるだけ。この他にも、TradingViewのストラテジーでシグナルを確認できるようにもしている。

目次へ

4―4. 資金の推移と売買の履歴:Googleシート

  • 方法:Googleシート
  • 目的:統計を自動算出。損益の推移やトレード履歴を確認する。

元から実装していたもの。分析用。OANDAだけでは欲しい情報が記録されないので、OANDA+BOTの情報をすべて記録しておく。

目次へ

5. システムのデーモン化

システムがエラーを吐かずに停止することが何回かあった。以下の仮説を立て、調査と対応を行った。

  • 「Run」で起動していることが原因?(起動時間の制限みたいなものがあり、勝手に停止してしまう?)
  • エラーを吐いていないだけで、エラーは起きている?
Cloud9 の Run で起動していることが原因?

実際の原因は特定できなかったが、起動方法を変更とデーモン化で同様の問題は起こらなくなった。デーモン化とは、特定のプログラムを永続化すること。

目次へ

5―1. 再起動でBOT独自の情報を引き継ぐように

今回の対応を行うにあたって、OANDAに保存されないBOT独自の情報を引き継ぐロジックを作成した。と言っても複雑なことをしたわけではなく、情報を都度サーバーに保存し、再起動時に読み込む処理を作成した。

バックアップする関数

def __backup( self ,data ) :
  data = str( data ).replace( 'nan' ,'np.nan' )
  self.__ss_backup( str( { 'datetime': self.__get_now() ,'FLAGS': data } ) )
  self.__txt_write( self.backup_file ,data )

バックアップを読み込む関数

def __load_backup( self ) :
  s = self.__txt_read( self.backup_file )
  if not s==False :
    # 文字列をコードとして実行する
    exec( f'self.FLAGS = {s}' )
    self.__count_positions()
    self.__is_init_on_tick = False
    self.__logger.info('ポジションとオーダーを読み込みました')
  else :
    # 初回起動としてOANDA側の初期化
    self.exit_all()
    self.cancel_all()
    self.__logger.info('情報がないため初回の起動として処理します')

初回のon_tickで読みに行く

def __on_tick_start( self ) :
  if self.__is_init_on_tick : self.__load_backup()

目次へ

5―2. シェルスクリプトでpythonファイルを複数回動かす

Linux系はまったく詳しくなくて、調べてはじめて知ったことが多い。シェルスクリプトとはターミナルで実行できるスクリプトのことで、組み方でPythonファイルの無限ループを設定することもできる。

具体的には、以下のコードをシェルスクリプトファイル(「YOUR_BOT.sh」など)として保存し、

再起動するシェルスクリプト

while true; do
  python ./YOUR_BOT_FILE.py; sleep 5;
  echo "YOUR_BOT_FILE.py を再起動します"
done

以下のコードをターミナルで実行する。

シェルスクリプトを実行

$ ./YOUR_BOT.sh

すると、設定したPythonファイルが手動で停止するまで実行され続ける。

上記のサンプルは、直下に保存したファイルのみを対象とする。他の階層にあるファイルは別途パスを指定する必要がある。また、ターミナル上でCtrl+cを2回押すことで停止することができる。

以下は参考にした記事。

目次へ

5―3. サービスに登録する

前述のシェルスクリプトを用いた方法では、(おそらく)単一のプログラムにしか対応できない。「サービスへの登録」を行うことで複数のプログラムをデーモン化することができると理解している。(間違っているかもしれない)

今のところ単独のロジックしか回していないので対応は保留しているが、今後のために参考になりそうなリンクを残しておく。また、対応が完了したところで、この記事も更新したいと思う。

目次へ

その他の対応方法

今回は実装しなかったが、以下のような方法もあるよう。

Back to Top

abbamboo

タカハシ / 7年目の兼業トレーダー

このブログの目的は、「学習の備忘録」と「アウトプットして理解を深めること」。「トレードで稼ぐために学んだこと」を徹底的に公開していきます。

元・日本料理の板前、現・金融畑のウェブ屋さん
保有資格:証券外務員1種、認定テクニカルアナリスト

更新のお知らせは、各SNSやLINEで。LINEだと1対1でお話することもできます!

>> このブログと著者についての詳細
>> 使っているツールの紹介

Investment Tech Hack

Sorry... doesn't support your browser

To get the best possible experience using our site we recommend that you upgrade to a modern web browser. Investment Tech Hackではご利用中のブラウザサポートはしていません。
Internet Explorerのアップグレード行う、もしくはその他のブラウザを使用しての閲覧をお願いします。