見出し画像

pythonで仮想通貨② 暗号通貨自動売買MLbotのエッジを探す。【イーサリアム(ETH-USDT)  ロジスティック回帰】

暗号通貨の自動売買botにおいて、エッジは無限にあります。
しかし、収益化出来るエッジは中々ネットには落ちていません。

今回はロジスティック回帰という【※枯れた技術】でありながら、イーサリアムの自動売買において、収益化できそうなエッジである『p値のズレ具合』を見つけたっぽい気がするので、ご紹介したいと思います。
※枯れた技術とは、価格が安くなった古い技術の新しい使い道を考えることで、新しい商品を生み出すという手法のこと。任天堂の立役者:横井軍平の理論

まず初めに損益グラフを見て頂きたいと思います。
これは、2022年5月13日~2022年10月30日(本日)まで稼働させたBOTの収益グラフです。👇
・左側の縦軸がデイリーの損益(緑色のグラフ)
・右側の縦軸が期間累計の損益(赤色のグラフ)

とりあえず、1,090ドル(≒161,208円 レート:1ドル=147.9円)のプラスで終えています。

5ヵ月で約16万円の利益なので、月額32,000円の収益という計算になります。

まぁ、悪くない金額だと思料します。



1.MLbotのロジック公開

ロジックは単純です。
売買単位は1回のトレードで1ETH(イーサリアム)です。

ロジスティック回帰を使います。
説明変数:昨日の【始値、終値、出来高、曜日】を設定。
目的変数:当日の【上昇、下落】確率です。
確率が60%を超えるときだけ売買します。


・上昇確率が60%を超える:当日の始値でイーサリアム現物購入。終値で売却。
・下落確率が60%を超える:当日の始値でイーサリアムショート。終値で決済。

これだけです。

ロジックは単純なのですが、二点ポイントがあります。
それは、
1.『訓練データとテストデータの各予測値をt検定し、p値が概ね50%以上になるように学習させる。※このとき時系列に学習させない!学習期間はバラバラにする』
2.学習期間は長すぎない方がよい(大体40日~60日程度の学習が良さそう)。
ということです。

これが冒頭で述べたエッジである『p値のズレ具合』です。

仮説検定
H0:訓練データとテストデータの予測値に何ら関係は無い。
H1:訓練データとテストデータの予測値に何らかの有意性がある。

というt検定に置いて、学習の結果算出された予測値が有意ではない(つまりH0が採択される)、場合にワークすることが多いです。

これは、例え訓練データの正解率が高く、テストデータの正解率が低い場合でも、予測値に差がないことを意味します。つまり、訓練データの正解率が高ければ、テストデータの予測値も低いとは言えない、という状況を確認するために行います。

また、学習期間が長すぎると、確率が50%付近に収束してきて、60%以上の確率が算出されなくなります。


2.ロジック考察

データは下記サイトより取得させて頂きます。
http://nipper.work/btc/index.php?market=BitMEX&coin=btcjpy-quarterly-futures&periods=86400&after=1420070400


ライブラリインポート

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

曜日と翌日のイーサリアムが上昇したか、下落したかをyに正解として格納。

df = pd.read_csv("ETH.csv")
df 
# 上記サイトのBittrex時間は24時間ずれるため、shiftで補正。
df["Date"] = df["Date"].shift(1)
df
import datetime as dt
import locale
df["week"] = pd.to_datetime(df["Date"], format="%Y-%m-%d")
df["week_num"] = df["week"].dt.dayofweek
df["week"] = df["week"].dt.day_name()
df
# 前日との差分をDiffへ格納
df["Diff"] = df["Close"].diff()
df["y"] = df["Diff"].apply(lambda x: 1 if x > 0 else 0).shift(-1)
df = df.dropna()
# CSV保存
#df.to_csv('history.csv')
df

ここまでの結果👇

値上がり日数と値下がり日数を見てみます。

print((df["y"] == 1).sum()) #値上がり日数
print((df["y"] == 0).sum()) #値下がり日数

値下がり日数が若干多いです。

次に、説明変数を作るために、新しいデータフレームdfsに前日のデータを格納します。

dfs = df[["Date", "Open", "Close","Volume", "week_num", "y"]]
#dfs = dfs.set_index('Date')
#dfs = dfs.reset_index()
dfs["yes_Open"] = dfs["Open"].shift(1)
dfs["yes_Close"] = dfs["Close"].shift(1)
dfs["yes_Volume"] = dfs["Volume"].shift(1)
dfs = dfs.dropna()
dfs

ここまでの結果👇

ついでに期間中のイーサリアムの値動きが下記です👇
5月頭から急落しています。

ここからロジスティック回帰の準備です。ライブラリをインポート。

from sklearn.preprocessing import StandardScaler
from sklearn.metrics import roc_curve, auc

from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression

from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score


X = dfs[["yes_Open","yes_Close", "yes_Volume", "week_num"]]
y = dfs.loc[:,'y']

X_train, X_test, y_train, y_test = train_test_split(X, y, train_size = 0.3, random_state = 42) 

#説明変数は標準化しておく(あとで回帰係数を比較するため)
scaler_X = StandardScaler()
X_train = scaler_X.fit_transform(X_train)
X_test = scaler_X.transform(X_test)

#モデルの構築と学習
lr = LogisticRegression() 
model = lr.fit(X_train, y_train)

#訓練データ,テストデータに対する予測
y_pred_train = lr.predict(X_train)
y_pred_test = lr.predict(X_test)
#最初の10サンプルだけ表示してみる
print(y_pred_train[:30])
print(y_pred_train.size)
print(y_pred_test.size)

ここまでの結果👇

モデル評価の為可視化👇

#訓練データ
print('accuracy:', accuracy_score(y_true=y_train, y_pred=y_pred_train))
print('precision:', precision_score(y_true=y_train, y_pred=y_pred_train))
print('recall:', recall_score(y_true=y_train, y_pred=y_pred_train))
print('f1 score:', f1_score(y_true=y_train, y_pred=y_pred_train))
print('confusion matrix = \n', confusion_matrix(y_true=y_train, y_pred=y_pred_train))
print("--------------------------------------------")

#テストデータ
print('accuracy:', accuracy_score(y_true=y_test, y_pred=y_pred_test))
print('precision:', precision_score(y_true=y_test, y_pred=y_pred_test))
print('recall:', recall_score(y_true=y_test, y_pred=y_pred_test))
print('f1 score:', f1_score(y_true=y_test, y_pred=y_pred_test))
print('confusion matrix = \n', confusion_matrix(y_true=y_test, y_pred=y_pred_test))


#ROC曲線の描画、AUCの計算(ROC曲線の下側の面積)の計算
y_score = lr.predict_proba(X_test)[:, 1] # 検証データがクラス1に属する確率
fpr, tpr, thresholds = roc_curve(y_true=y_test, y_score=y_score)

plt.plot(fpr, tpr, label='roc curve (area = %0.3f)' % auc(fpr, tpr))
plt.plot([0, 1], [0, 1], linestyle='--', label='random')
plt.plot([0, 0, 1], [0, 1, 1], linestyle='--', label='ideal')
plt.legend()
plt.title('ROC curve of test sample',fontsize=15)
plt.xlabel('false positive rate',fontsize=15)
plt.ylabel('true positive rate',fontsize=15)
plt.show()

ROC曲線は0.5以下。ダメダメです👇
しかし、訓練データの正解率は65%とまずまずです。
予測値同士のt検定に置いて、テストデータの予測値がこれと差が無ければ、テストデータの正解率が低くても、このエッジを採用するというロジックです。

ここから先は

3,956字 / 12画像

¥ 10,000

e^iπ + 1 = 0