とりあえず、アルゴリズムは完成したので、いよいよMT4からのMLシステムを構築することとした。
MT4
Metatrader4からは、API経由でデータの受け渡しをすることにした。
メタトレード側のサーバーでやることは、3つである
- Pandasデータフレームから、tailのtimeindex情報を取得する
- Pandasデータフレームに対して、各足のチャート情報を送付する
- 売買シグナルを取得する
まずは、Oninit関数に、1,2を仕込む
#property copyright "Copyright 2016, gogojungle"
#property version "1.00"
#property strict
string URL = "http://127.0.0.1/";
int OnInit(){
string res,str,filename,sep_str[];
datetime m1,m5,m15,current;
int pos;
res = GET(URL + "getlasttime/", "");
pos =StringSplit(res,'\"',sep_str);
if(pos!=0)
{
m1 = StringToTime(sep_str[3]);
m5 = StringToTime(sep_str[7]);
m15 = StringToTime(sep_str[10]);
}
SendValue(PERIOD_M1,m1);
SendValue(PERIOD_M5,m5);
SendValue(PERIOD_M15,m15);
return(INIT_SUCCEEDED);
}
void SendValue(string peristr,datetime end){
for(int i=0; i<100000; i++){
if (end > iTime(NULL,peristr,i))
break;
string data = TimeToString(iTime(NULL,peristr,i))
+"_"+peristr + "_" + DoubleToString(iClose(NULL,peristr,i));
POST(URL + "gettick/", data);
}
}
Webserver
GET,POSTは以下の通り
今回のPOSTは、Fastapiの練習を兼ねて、JSON-BODY渡しを書いた。(別にGETでもいいのであるが・・・)
もちろん、カリパクである。
bool POST(string url, string text){
string headers;
string data;
char post[],result[];
headers = "Content-Type: application/json\r\n";
StringReplace(text, "\n", "\\n");
data = "{\"content\":\""+text+"\"}";
ArrayResize(post,StringToCharArray(data,post,0,WHOLE_ARRAY,CP_UTF8)-1);
int res=WebRequest("POST",url,headers,5000,post,result,headers);
if(res == -1){
Print(__FUNCTION__ + " Error code =",GetLastError(),data);
return(false);
}
Print("POST success! ", CharArrayToString(result, 0, -1));
return(true);
}
リクエストをGETに変えただけ・・・
string GET(string url, string text){
string headers;
string data,str;
char post[],result[];
headers = "Content-Type: application/json\r\n";
StringReplace(text, "\n", "\\n");
data = "{\"content\":\""+text+"\"}";
ArrayResize(post,StringToCharArray(data,post,0,WHOLE_ARRAY,CP_UTF8)-1);
int res=WebRequest("GET",url,headers,5000,post,result,headers);
if(res == -1){
Print(__FUNCTION__ + " Error code =",GetLastError(),data);
return(false);
}
//--- Receive a link to the image uploaded to the server
str=CharArrayToString(result);
return(str);
}
Fast-API
丸一日かかって、とりあえずAPI連携させるところまで完了した。
苦労したところは、POST側のバリデーションの部分。
何回もエラーになったが、結局、以下のコードを参考にした。
async def root(body=Body(...)):
global df1,df2,df3
time,peristr,value = body["content"].split("_")
import pandas as pd
import datetime as dt
from pandas_datareader import data
import mplfinance as mpf
import torch
from torchvision.datasets import ImageFolder
from torchvision import models, transforms
import torch.nn as nn
import numpy as np
import os
os.environ['KMP_DUPLICATE_LIB_OK']='True'
df1 = pd.read_csv(r'C://Users//User//Desktop//EURUSD.oj5k1.csv', sep=",",names=('date', 'time', 'open', 'high', 'low', 'close', 'volume'))
df1.index = pd.to_datetime(df1['date']+" "+df1['time'])
df2 = pd.read_csv(r'C://Users//User//Desktop//EURUSD.oj5k5.csv', sep=",",names=('date', 'time', 'open', 'high', 'low', 'close', 'volume'))
df2.index = pd.to_datetime(df2['date']+" "+df2['time'])
df3 = pd.read_csv(r'C://Users//User//Desktop//EURUSD.oj5k15.csv', sep=",",names=('date', 'time', 'open', 'high', 'low', 'close', 'volume'))
df3.index = pd.to_datetime(df3['date']+" "+df3['time'])
from PIL import Image
import matplotlib.pyplot as plt
from tqdm import tqdm
import pdb
def min_max(x, axis=None):
min = x.min(axis=axis, keepdims=True)
max = x.max(axis=axis, keepdims=True)
result = (x-min)/(max-min)
return result
def imagemake(dfspan1,dfspan2,dfspan3):
a = min_max(np.array(dfspan1))
d = min_max(np.array(dfspan2))
g = min_max(np.array(dfspan3))
m = np.outer(a,a).astype(np.float32)
n = np.outer(d,d).astype(np.float32)
o = np.outer(g,g).astype(np.float32)
m1 = min_max(m[0:64,0:64])
m2 = min_max(m[16:80,16:80])
n1 = min_max(n[0:64,0:64])
n2 = min_max(n[16:80,16:80])
o1 = min_max(o[0:64,0:64])
o2 = min_max(o[16:80,16:80])
te1 = np.stack([m1,n1,o1])
te2 = np.stack([m2,n2,o2])
te3=np.concatenate([te2,te2], 2)#real = fake = te2で与える
te4=np.kron(te3, np.ones((4,4)))
tmp = torch.from_numpy(te4).clone()
return transforms.ToPILImage(mode='RGB')(tmp)
当然ながら、送る方も適当なので、受ける方も適当だ。
Jsonリクエストを、splitして適当にパラメータを抜いている。
PandasのTailに、更新データを突っ込む処理がだるい・・・
→ Pandasがますます嫌になった。
調べ物をしていて、チートシートを発見した。→ 外人さんマメやなあ。。。
chrome-extension://dnkjinhmoohpidjdgehjbglmgbngnknl/pdf.js/web/viewer.html?file=https%3A%2F%2Fwww.webpages.uidaho.edu%2F~stevel%2Fcheatsheets%2FPandas%2520DataFrame%2520Notes_12pages.pdf
from fastapi import Body
from fastapi import FastAPI
app = FastAPI()
@app.post("/gettick/")
async def root(body=Body(...)):
global df1,df2,df3
time,peristr,value = body["content"].split("_")
s=pd.DataFrame({"close": [value]}, index=[time])
s.index = pd.to_datetime(s.index)
if int(peristr)==1:
df1 = df1.append(s)
grouped = df1.groupby(level=0)
df1 = grouped.last()
df1=df1.sort_index(ascending=True)
elif int(peristr)==2:
df2 = df2.append(s)
grouped = df2.groupby(level=0)
df2 = grouped.last()
df2=df2.sort_index(ascending=True)
elif int(peristr)==3:
df3 = df3.append(s)
grouped = df3.groupby(level=0)
df3 = grouped.last()
df3=df3.sort_index(ascending=True)
return peristr+"ok"
@app.get("/getlasttime/")
async def gettime():
global df1,df2,df3
return {"m1":df1.tail(1).index.astype(str)[0].replace("-","."),"m5":df2.tail(1).index.astype(str)[0].replace("-","."),"m15":df3.tail(1).index.astype(str)[0].replace("-",".")}
@app.get("/predict/")
async def predict():
global df1,df2,df3
df11 = [x for x in df1[-96:]['close']]
df22 = [x for x in df2[-96:]['close']]
df33 = [x for x in df3[-96:]['close']]
img = imagemake( df11, df22, df33)
fn = df1.tail(1).index.astype(str).values.tolist()
fname = "datasets/facades2/test/" + fn[0].replace(":","_") + "sk.png"
if len(fn[0])<16:
fname = fname.replace("sk.png"," 00_00_00sk.png")
img.save(fname)
return {"image":fname}
どうやら、メタトレードはPORT80しか使えないらしい。
import nest_asyncio
import uvicorn
if __name__ == "__main__":
nest_asyncio.apply()
uvicorn.run(app, port=80)
まとめ
機械学習アプリの学習を兼ねて、MT4とFastAPIの連携を書いてみた。
シグナルを取り出すところは、コマンドラインからPIX2PIXを呼び出す必要があり、出来ていない。
Pandasはtimeindexがメンドクサイ → MT4とPANDASで、TIMEDATAのフォーマットが違う
Pandasがめんどくさいので、データベースモデルに突っ込もうかなあ・・・ その方がCRUDの練習もできるし・・・