Pythonでニュース速報と為替レートの関係を調べてみる
FXのシステムトレードについて、私はほぼすべてを信用していない。おそらく巨大なファンドがお金をかけて開発したシステムやごくごく一部の個人トレーダーがひたすら時間をかけて作ったシステムはもしかしたら稼げるのかもわからないが、その他のネット上に転がっている「FX自動売買システム」は誰でも簡単に儲けられ …
前回の記事でFX(為替)のレートと速報ニュースの関係についてデータを調査してみたが、今回は速報ニュースを受けたらすぐに機械学習(AI)のNLPモデルで分類して、ポジションの命令を出せるようなシステムを考えてみる。基本の考え方は前回の記事に詳細を載せている。
今回は機械学習の実装や詳細は書かないことにする。かなり良い成果が出ているのだが、トレードの責任は持てないのでモデルの配布もしない。まぁ気楽に読んでいって~な。
ここ数年、NLPの分野でBERTとその派生形が天下をとっているように見える。なのでその中でも学習メソッドが効率的なElectraを使用してみる。また、こういうのは英語を対象とする方が簡単なんだが、後述するが、私の場合日本取引時間に出る日本語ニュースがメインになると思われるため、言語対象は日本語とする。
Electra Baseモデルを使用。事前学習は以下の学習を行ったモデルを独自に用意した。
16GBメモリGPUを8枚使用して25日程度で完成した。ちなみにLargeモデルは45日程度で完成。(ただし今回はなぜかBaseモデルの方が高い精度をだすのでLargeは無視)
ポジションをL、Sするべきと判断されるデータは、ドル円、ユーロ円だとかなり少なくなってしまう。そしてある程度、L、S、何もしないのデータが均等に存在しないと機械学習の精度が高まらないことが分かった。そのため通貨ペアはボラティリティが高いポンド円を採用。
また、学習データは前回の記事で書いたロ○ターニュースの2016年~2021年のもので以下の条件でフィルタリングした。
そしてラベリングは以下のもので行った。
そしてラベル1とラベル2の学習データをすべて1回複製して(×2して)データをかさましした。
# 学習データ数
{'0': 3625, '1': 2898, '2': 3050, '3': 126}
# テストデータ数
{'0': 579, '1': 132, '2': 167, '3': 2}
やはり学習データが少ない。あと10倍くらいは欲しいところだが、今回は用意できないのでこれで。
普通のGPU(Tesla T4)で2epoch学習。約30分くらいで完了する。またpytorchのフレームワークで学習を行った。正直丸1日いろいろチューニングを試したけどさほど結果は変わらず。
# 正しく分類できた2021年のニュース
749/880(0.8511)
まずまずな結果だ。つまり、2016~2020年のニュースデータで学習したAIで2021年のニュースを解析した結果約85%の確率で正しいポジションを当てられたってこと。
一応シミュレーションしてみよう。2021/1/1~2021/11/30の実際の相場で今回作成したモデル(AI)を利用してやってみる。すでに現時点から過去の期間なので、AIによるクラスタリングはすべて行って以下のようなCSV形式で出力している。
label, date
0,20210104010100
2,20210104062600
2,20210104063600
2,20210104100700
0,20210105002200
:
このデータは私が作った機械学習モデルによって出力された売買の命令みたいなものを表す。例えば、2021/01/04/06:26(標準時)に出たニュースで"label=2"、つまりポンド円のSポジションをもちなさいってこと。そしてトレードのルールは以下のように規定する。多分この以下の7は、実際のトレードではかなり難しいのではないかな。
これらの条件を反映したシミュレーションは以下のコードで用意した。めちゃくちゃ汚いコードだが、今回はどうでもいい!ゼニ儲けの方が重要やろ!
import math
import csv
def get_csv_data():
with open(f'GBPJPY.txt') as f:
reader = csv.reader(f)
ret = []
for row in reader: ret.append(row)
return ret
def get_result():
with open(f'NLPの結果CSV') as f:
reader = csv.reader(f)
dic = {}
for row in reader:
if row[0] == 0 or row[0] == 3: continue
dic[row[2]] = row[0]
return dic
def get_profit(csv, i, type_num):
l_or_s = 1
if type_num == 2: l_or_s = -1
if i >= len(csv): return 0
spread = 0
start = float(csv[i][3]) # 1分後のレート
fix_profit_per = 1 + (0.0035 * l_or_s) # 利確逆指値を入れるポイント
fix_profit_border_per = 0.0015 * l_or_s # fix_profit_perの0.15%againstで逆指値
fix_profit_border = 0
is_set_profit = False
loss_cut_per = 1 - (0.0025 * l_or_s) # 損切り逆指値
end = i
for plus in range(99999):
end += plus
rate = float(csv[end][3])
if end >= (len(csv) - 1): # 最新のレートの場合
spread = (rate - start) * l_or_s
break
if rate < start * loss_cut_per and l_or_s == 1: # 損切り
spread = (rate - start) * l_or_s
break
elif rate > start * loss_cut_per and l_or_s == -1: # 損切り
spread = (rate - start) * l_or_s
break
if is_set_profit is False: # 利確ルールの設定
if rate > start * fix_profit_per and l_or_s == 1:
fix_profit_border = start * (fix_profit_per - fix_profit_border_per)
is_set_profit = True
elif rate < start * fix_profit_per and l_or_s == -1:
fix_profit_border = start * (fix_profit_per - fix_profit_border_per)
is_set_profit = True
else:
if rate < fix_profit_border and l_or_s == 1: #利確処理
spread = (rate - start) * l_or_s
break
elif rate > fix_profit_border and l_or_s == -1:
spread = (rate - start) * l_or_s
break
if rate > (fix_profit_border + start * fix_profit_border_per) and l_or_s == 1: #利確ポイントを1分ごとに再設定
fix_profit_border = rate - start * fix_profit_border_per
elif rate < (fix_profit_border + start * fix_profit_border_per) and l_or_s == -1:
fix_profit_border = rate - start * fix_profit_border_per
return spread, end
if __name__ == '__main__':
csv_data = get_csv_data()
result_dic = get_result()
asset_sum = 1000000 # 初期投資100万円
leverage = 10 # レバは常に10倍
unit = 10000 # 10,000通貨で1枚とする
i = -1
end = 0 # ポジションが被らないように終了時点のindexをもつ
for c in csv_data:
i += 1
if i < end: continue
result_data = result_dic.get(c[1] + c[2])
if result_data is None: continue
rate = float(csv_data[i + 1][3]) # 1分後のレート
position_unit = math.floor((asset_sum * leverage) / (unit * rate)) # n枚のポジションを持つ
spread, end = get_prof(csv_data, i + 1, result_data)
asset_sum += position_unit * unit * spread
print(math.floor(asset_sum))
全部で113回のトレードを行い、 100万円がおよそ250万円に増えています! うそやろ?何かすごくねこれ。。
正直試行回数が少なすぎるのと、ハイレバレッジでたまたまうまくいっているだけかもしれない。しかし11か月にわたって113回トレードしてうまいこと増えているのは驚きだ。前述の通り、利確の逆指値をうまいことスライドさせるのは多分難しいと思うので実際にはこんなパフォーマンスはでないのかも。また、システムトレードのシミュレーションで上手くいって、実際のトレードでボロボロってあるあるだから信用ならないけどな。
う~ん。こんなにうまくいくとは全く思っていなかったんですが。。実際のトレードでは以下のような感じで行う。日本のFXの会社でAPIで取引できるところがなさそうなので、取引自体は人間が手で行う必要がある。残念。
最新のニュースを常に取得して、その都度機械学習のモデルに通して判定する。ポジションをとるべきと判断した場合LINEで私に通知する。その後すぐにFXのアプリを開いてLかSのポジションを持つ。あとは上記のルールで決済する。ちなみに今回作成したAIモデルの実行時間は1秒程度なので、私がLINEに気付くことができれば1分以内にポジることは全然可能だ。LINEはDeveloper登録したら以下のコードで簡単にAPI送信できる。
import requests
def send_line(text):
response = requests.post(
'https://api.line.me/v2/bot/message/push',
json={
"to": "toid",
"messages":[
{
"type":"text",
"text":text
}
]
},
headers={
"Authorization": "Bearer TOKEN"
}
)
また1か月に一回、機械学習モデルの追加学習を行う。一か月で為替ニュースがどんどん増えていくので、AIをアップデートしていくことで最新の情報を判断できるように強化していくわけだ。
ただ、ここまで書いておいて思うんだけど、 世の中そんな甘くないやろ。明らかに上手くいきすぎていないか? てかなんか上記の線グラフとか詐欺サイトで出てきそうなやつやん(笑)。
私が懸念していたのは、経済ニュースって連続性があると思うが、今回のモデルでは1つのニュースのみで判断している。なので例えば「FRBが利上げを示唆」からの「FRB金利据え置き」ってニュースがあった場合、2つ目のニュースが強すぎてポジションを再考する必要があると思うけど、今回のモデルでは最初のニュースを優先するようなルールにしている。また、2つのニュースから”大きなギャップ”が合った時に大きくレートが変動するものなので、ニュースを1つずつ判断するなんて明らかにおかしいと思うんだよね。
しかし今回のAIモデルは上手く行っちゃっているので試さざるを得ない。まぁこのサイトで今後のパフォーマンスは書かないと思うけど。(なんか技術ブログではなくなっている気がする。。)
FXのシステムトレードについて、私はほぼすべてを信用していない。おそらく巨大なファンドがお金をかけて開発したシステムやごくごく一部の個人トレーダーがひたすら時間をかけて作ったシステムはもしかしたら稼げるのかもわからないが、その他のネット上に転がっている「FX自動売買システム」は誰でも簡単に儲けられ …
なんとなく悪いことってのは、魅力的だったりする。 おそらく1970年代~1990年前半に生まれた人で、学生時代にPCを触っていた人ってアングラな世界に1度や2度触れたことがあるのではないでしょうか?特にエロ系ね。私も若かりし頃は、なんとかしてずりネタを確保しようと、意味も分かってなかった通信プロト …