昨日のダウは一時500ドルを超える下落幅でしたが引けにかけて下げ幅を縮小し、125ドル安となりました。
10月10日の約1000ドルの下落以降、サウジアラビアやトルコの政情不安も加わって相場全体が方向感が定まらない展開となっており、長期投資家の皆様におかれましては追加投資のタイミングを悩んでいる方も多いのかもしれません。
今回の相場がリーマンショックの前夜に酷似しているという向きも少なくありませんが、アメリカはリーマンショックの後、1年半という短期間で驚異的な復活を遂げました。
今すべきことは時間が癒してくれることを期待して待ち続けるというのが最も共通する正しい選択肢なのかもしれません。
それでも多くの人は今買った銘柄がすぐに上がるかもしれないと期待し、短期売買をしてしまいます。
さてタイトルの通り、長期投資家かどうかにかかわらず、一般的に短期売買をすべきでないという主張にはどのような背景や根拠があるのでしょうか。
一般的によく言われているのは、市場や経済が中長期的には右肩上がりであり、市場が合理的なファンダメンタルを前提として売買されていれば、短期で売買するよりも長期で売買した方が得であるというものです。
これらは先人の経験則です。
経験則は便利です。自分たちで試行錯誤をしなくても先人の知恵を拝借してショートカットできるのですから。
学術的な市場の合理性には複数のレベルがありますが、個人的には市場が合理的であるという前提は感覚的に理解はしているものの、実際に肌で感じる機会は少ないです。
市場が合理的であれば、裁定機会は存在しないということになりますが、実際には存在するように思うからです。
また長期投資家にとって短期売買は市場の適正価格を歪め、短期売買によってもたらされる価格変動はノイズでしかないと言われています。
短期売買の究極形である高頻度で場に出ては消える注文は流動性にすらなり得ないと考えられており、実際に高頻度取引(HFT)はフラッシュクラッシュのように市場に良い影響を与えないからです。
長期投資家は銘柄分散かつ長期保有が最も確実でリスクが低いのは分かっていますが、長期であろうが短期であろうが、注文がザラ場に出て約定される以上、株式市場のマーケットマイクロストラクチャーを解析し、理解する必要があると考えます。
例えばtickデータなどは個別の値動きについて振り返ることはできますが、その時の相場の強さや投資家の売買意欲やトレンドなどはリアルタイムで板の厚みと時間変化を見てみないと分かりません。
木を見て森を見ないのも良くないですが、森だけを見て木を見ないというのも真に理解していることにはなりません。
そこで今回は市場にある裁定機会について調べ、実際に短期投資をすべきでないのかについて検証してみたいと思います。
Contents
素人でも簡単に手を出せる検証環境を探す
管理人には、市場には短期的には利益になりうる裁定機会があるのに、難しかったり制約が多いという理由で選択肢から外しても良いのかという根本的な疑問を持っています。
いわゆる食わず嫌いではいけませんよね、何事もやってみないとという感じ。
検証手段として、長期投資とは真逆のアルゴリズムトレードによる短期売買で裁定機会が得られるのか、サヤが抜けるのかを検証してみたいと思います。
この検証を株式市場で試すのは難しいので、プログラムの実行環境を提供している仮想通貨市場で検証してみたいと思います。
東証のコロケーションサービスなど、光速のレベルで物理的な距離を縮めなければならないほど、HFTの技術が進んでいる株式市場よりも、取引所各社が提供しているプログラミング用のインターフェース(API)で売買できる環境の方が同じスタートラインで勝負できると考えたからです。
株式に比べて、実体としての機能性がある仮想通貨の方が本質的価値というものは計りやすそうな気もするのですが、送金機能としてのファンダメンタルに対するコンセンサスが形成されづらいという特徴があります。
また金融庁などの監督官庁が不在で何よりも投機性が高く、その投機性が実勢価格に反映されてしまっている点では株式市場に対する特異性は大きく、大口の投資家の売買によって値動きも大きくなってしまうというデメリットもあります。
逆にいうとこうした事実はアルゴリズムトレードによるアービトラージが可能かどうかを検証するにはぴったりです。
以下では主に仮想通貨に対するアービトラージについて見てみたいと思います。
様々な裁定機会
株式に限らずアービトラージにはいくつかの手段があります。
適正価格アービトラージ
マーケットは短期的には小さな波を形成しながら、中期的には適正価格に収斂するという特性を持っています。
そのため、波の谷で買って、山で売るということを繰り返すことによってサヤが抜けます。
これが最も簡単ですが、簡単がゆえに多くの参加者が狙っているのでその分、利幅も小さいです。
マーケット間アービトラージ
マーケットによっては流動性にばらつきがあるので、市場間で価格の乖離が発生します。
A市場では100円で買えるBTCがB市場では200円で売られているなら、Aで買ってBで売れば100円の儲けになります。
大きな事件やイベントが発生した際に、その影響がマーケットによって織り込まれる時間が異なるため、有効な戦略と言えます。
ただし、平時ではマーケット間の資金移動に時間がかかる上、場合によっては手数料が取られる市場もあるので容易ではありません。
資産間アービトラージ
同じマーケットで複数資産が売買されている時に、変換レートによってサヤが抜けることがあります。
例えば簡単な例だと、1BTC=50万JPY、1XRP=50JPYの時に、1BTC=2万XRPであれば、
手元にある50万円を使って1BTCを購入し、それを売却して2万XRPにした上で更にXRPを日本円に変換すれば100万円となります。
どのように裁定取引をするのか
どんな仮想通貨マーケットでも良かったのですが、今回はcoincheckで裁定機会を調べてみようかなと思います。
https://github.com/kmn/coincheck
またpythonという言語を使っていますが、初心者でも簡単にコピペで実装できるという観点以外、特にこだわりはありません。
基本的には重要なのは板情報を読み取るコマンド、買いオーダー、売りオーダーを出すコマンドくらいです。
板情報の読み取り
orderbooks()
買いオーダー
buy_btc_jp
売りオーダー
sell_btc_jp
やっていることも極めて簡単で、板情報を見る→場に出ている買い注文の中で安い金額より少し低い金額で買い注文を入れる→約定すれば利益が出る値段で売り注文を入れる→最初に戻る、の繰り返しです。
まず最初はパラメータや使用しているライブラリです。
import pandas as pd from datetime import datetime import time from time import sleep import requests import json import pybitflyer from coincheck import market, order, account #パラメータを保存しているファイルの場所 IMPORT_FILE_PATH = "/**********/order_parameter.csv" monitoring_mode = False buy_wait_mode = False#買い注文が出来るまで待機するモード sell_wait_mode = False#売り注文が出来るまで待機するモード emergency_mode = False cc_ask_bid_threashold = 100 vol = 0.05#global cc_order = None m1 = None a1 = None total_profit = 0 loss_rate = 0.01 cnt_loss_cut = 0
次に実際に売買する部分です。
def short_profit_fix(): global m1; global IMPORT_FILE_PATH; cc_init();#global parameterの設定 print(m1.orderbooks()); for i in range(50): parameter_file = pd.read_csv(IMPORT_FILE_PATH, index_col="parameter_id") isGoingToArb = True if(int(parameter_file.loc[['check_souba']]['parameter'])==1): isGoingToArb=judge_invest(); if (isGoingToArb): order_vol = float(parameter_file.loc[['order_vol']]['parameter']) orders=m1.orderbooks(); bid = float(orders['bids'][0][0]) order_buy_price = bid * (1-float(parameter_file.loc[['order_buy_discount']]['parameter'])) profit_value = float(parameter_file.loc[['profit_value']]['parameter']) order_sell_price = order_buy_price + profit_value limit_price = int(parameter_file.loc[['limit_price']]['parameter'])#parameter_file.iloc[0,0] buy_loop(order_buy_price, order_vol, limit_price) long_position = float(a1.get_balance()['btc']) if(long_position>0.005): all_cancel(); long_position = float(a1.get_balance()['btc']) compArb = sell_loop(order_sell_price, long_position) if(compArb>0): print("裁定完了→" + str((order_sell_price - order_buy_price)*long_position) + "円の利確") elif(compArb<0): print("裁定失敗") else:#売り注文失敗 print("売り注文が出せません") return 1#利確済み else: print("注文成立せず") else: print("直近高値のため実施せず") return 0;
上記の売買するプログラムから呼び出される判断ロジックなどです。
def judge_invest(): periods = ["300"] query = {"periods":','.join(periods)} res = json.loads(requests.get("https://api.cryptowat.ch/markets/bitflyer/btcjpy/ohlc",params=query).text)["result"] row = res[periods[0]] low = row[-1][3]#-1は最後の一行 high = row[-1][2] close = row[-1][4] short_judge = close < low + (high - low) * .7 print("現値=" + str(close) + ", 短期高値=" + str(high) + ", 短期安値=" + str(low) + " => " + str(short_judge)) if(short_judge):return short_judge length = len(row) # 直近30分間のhigh_low)*.7+low>closeの時にTrue return for column in row[:length-6:-1]: if(column[3] < low):low = column[3] for column in row[:length-6:-1]: if(column[2] > high):high = column[2] long_judge = close < low + (high - low) * .7 if(not(long_judge)): time.sleep(5) return long_judge
次はライブラリの初期化などです。
def cc_init(): global cc_order; global m1; global a1; parameter_file = pd.read_csv(IMPORT_FILE_PATH, index_col="parameter_id", dtype={'parameter':'object'}) your_secret_key = str(parameter_file.loc["secret_key", "parameter"])#'IjSPB4KiqEealVlweDF-MW_svX653aSB' your_access_key = str(parameter_file.loc["access_key", "parameter"])#'kHZdB4wjbZKIzduT' cc_order = order.Order(secret_key=your_secret_key, access_key=your_access_key) a1 = account.Account(secret_key=your_secret_key, access_key=your_access_key) m1 = market.Market() #loss_cutしたらTrue, してなければFalse def loss_cut(loss_cut_level): global cc_order; global m1; global a1; global cnt_loss_cut; # 現値を取得 orders = m1.orderbooks(); now_ask = float(orders['asks'][0][0]) # 現値がloss_cut_levelを下回っていたら売り成り if(now_ask < loss_cut_level): all_cancel(); nari_sell = cc_order.sell_btc_jpy(rate=now_ask-100, amount=float(a1.get_balance()['btc']))#安いところで全売却 print("ロスカットしました") cnt_loss_cut = cnt_loss_cut+1 return True; return False; def get_mean_trades(): global m1; trades = m1.trades();#直近50件の取引 # trades[0]['created_at'] # trades[len(trades)-1]['created_at'] sum = 0; for trade in trades: sum = sum + trade['rate'] return sum / len(trades)
実際にマーケットに注文を出すコードをまとめたものです。
def sell_loop(order_sell_price, order_vol): global cc_order; global m1; global monitoring_mode; global buy_wait_mode; global sell_wait_mode; global total_profit; order_sell = cc_order.sell_btc_jpy(rate=order_sell_price, amount=min(order_vol,float(a1.get_balance()['btc']))) buy_wait_mode = False sell_wait_mode = True # 売り注文が出来なくなったらちょっと一大事(あとで考える→最悪ロスカット) if(not(bool(order_sell['success']))): emergency_mode=True; sell_wait_mode=False;#売りを出したいけど、売れないので本当はTrueの方がいい? print("emergency mode発動→売り注文が場に出ない") print("cause : " + order_sell['error']) return 0; else: print("売り注文が場に出ました") while(sell_wait_mode): # loss_cut基準を読み込む parameter_file = pd.read_csv(IMPORT_FILE_PATH, index_col="parameter_id") loss_rate = float(parameter_file.loc[['loss_cut_ratio']]['parameter']) if(loss_rate<=0 or loss_rate > 0.05): loss_rate = 0.02 isLossCut = loss_cut(order_sell_price*(1-0.02));#loss_cut水準に達しているか判定して該当したら売却 if(isLossCut): return -1; sell_order_list = cc_order.list() if(len(sell_order_list['orders'])>0):#売り注文の出来待ち orders = m1.orderbooks(); ask = float(orders['asks'][0][0])#最安売り注文 print("売り注文出来待ち..." + str(order_sell_price) + ', ask = ' + str(ask))#現値を取得する time.sleep(1) else: if(float(a1.get_balance()['btc']) >= 0.0001): continue; sell_wait_mode=False; print("全部売りました→残ったポジション" + str(a1.get_balance()['btc'])) return 1; def buy_loop(order_buy_price, order_vol, limit_price): global cc_order; global m1; global monitoring_mode; global buy_wait_mode; global sell_wait_mode; global total_profit; if(order_buy_price > limit_price): print("買値:" + str(order_buy_price) + ">閾値:" + str(limit_price) + "なので売買しません") return; order_buy = cc_order.buy_btc_jpy(rate=order_buy_price, amount=order_vol) # 注文が場に出たかどうか if(bool(order_buy['success'])): # 注文IDの取得 ordered_id = order_buy["id"] # 買い注文が約定した場合、order待ち行列のbuy_order_listのordersには何もないので # この注文が消えるまで判定ループを繰り返す for buy_wait in range(50): #注文状況の取得 buy_order_list = cc_order.list() # 買い注文ができるまでx秒待機 if(len(buy_order_list['orders'])>0): print('買い注文:残り'+str(buy_order_list['orders'][0]['pending_amount'])+"btc @"+\ str(buy_order_list['orders'][0]['rate'])+"出来待ち...") else: #買いができた場合(一部出来でもbuy_order_listには注文がなくなってしまう可能性濃厚) if(float(a1.get_balance()['btc']) >= 0.005): return#売り開始 # 売買可能なボリュームまで約定出来たら完了(売却に進む) else: buy_wait=1;#一部出来の場合は買い待ちループを最初からやり直す(ある程度出来たら売り注文を出す) continue; all_cancel(); else: print("失敗原因 => " + str(order_buy))
これは最初に実行されるプログラムです。
if __name__ == "__main__": rikaku_kaisuu = 0 cnt_loss_cut = 0 for i in range(500): rikaku_kaisuu=rikaku_kaisuu+short_profit_fix() #ループ終了条件を設定する parameter_file = pd.read_csv(IMPORT_FILE_PATH, index_col="parameter_id") if(int(parameter_file.loc[['loop_duration']]['parameter'])==0): continue time.sleep(0.5);#買い注文待ち
実際に書いてみると長い(自分のプログラムが冗長なだけ)ですが、実際やっていることはそんなに難しいことはやっておらず、売買ロジック以外は初期化と変数の割り当てくらいです。
このコードによって得られた利益の時間推移です。
3分(180秒)回して40円くらいです。
もちろん、損切りが続けばマイナスになることもありますし、逆に幸運が重なってプラスに大きく振れることもあるかもしれません。
以上の通り、素人のプログラムと取引だけでも利益が得られました。
ロジックとしては最良買い気配よりも少し良い値段で買い注文を入れて約定したらすぐに利益が出るように売り注文を出すだけです。
こんな簡単なロジックでも3分で40円稼げたので、仮に1秒1円稼ぐことができれば、自分に時給3600円のサラリーマンがもう一人働いてくれていることになりますね。
実際に動かすとなるとアルゴリズムをもっと改良して収益力を高める必要はありそうですが、最初の疑問であった短期売買によって裁定機会が得られるのかについては一定の答えを得ることができました。
しかし、それでも私は短期売買ではなく、株式については長期で取引をしていきます。
なぜなら、短期も長期もどちらも利益が得られる科学的根拠はなく、どちらも損をする可能性があるにも関わらず、多くの人が短期売買で損をし、反対に長期投資で財を成している人がいるからです。
おそらく短い機会では利益になったとしても長い時間かけて短期売買で利益を上げ続けるのは難しいのだと思います。
なによりも長期投資家には大きな利益を得ているウォーレン・バフェットというお手本がいるのに対して少なくとも短期投資で売買して利益を得続けている人を管理人自身が知りません。
↓ポチっと押して頂けるととても喜びます。
この記事を読んだ人は以下も読んでいます...

パウエル五郎

最新記事 by パウエル五郎 (全て見る)
- 【ポストコロナの世界】様々な民主化の流れが加速する - 2020年4月25日
- コロナを受け保有銘柄の入れ替えを行う - 2020年4月23日
- コロナを受けてポートフォリオ戦略を変更する! - 2020年4月22日