%matplotlib inline
import ctypes
import numpy as np
import pandas as pd
from pandas import DataFrame
import matplotlib.pyplot as plt
hst_path = 'USDJPY1.hst'
with open(hst_path, 'rb') as f:
dtype = [('Time','u8'), ('Open','f8'), ('High','f8'), ('Low','f8'),
('Close','f8'), ('Volume','i8'), ('s','i4'), ('r','i8')]
df = pd.DataFrame(np.frombuffer(f.read()[148:], dtype=dtype).astype(dtype[:-3]))
df = df.set_index(pd.to_datetime(df['Time'], unit='s')).drop('Time', axis=1)
print(len(df))
df['Close'].resample('W').last().dropna().plot(figsize=(15,5))
これを MinGW g++ でコンパイルした(64bitのやつ)
こんな感じ
g++ -shared -o simple_backtest.dll simple_backtest.cpp -Wl,-k -static -O2
※この例だとポジションの価格に使う始値しか渡してないけど
高値安値、利食い、損切り、トレール幅、等を渡すようにすれば
それらのチェックができるように作り変えられると思います
"""
#include <stdint.h>
#define EXPORT extern "C" __declspec(dllexport)
#define NONE 0
#define BUY 1
#define SELL -1
EXPORT void backtest(uint64_t *tradetime,
double *tradeprice,
bool *openbuy,
bool *opensell,
bool *closebuy,
bool *closesell,
int *postype,
uint64_t *opentime,
double *openprice,
uint64_t *closetime,
double *closeprice,
double *profit,
long n)
{
int pos = NONE;
int posindex = 0;
for(int i=0; i<n-1; i++){
if((pos==BUY && closebuy[i]) || (pos==SELL && closesell[i])){
closetime [posindex] = tradetime[i+1];
closeprice[posindex] = tradeprice[i+1];
profit [posindex] = (closeprice[posindex]-openprice[posindex]) * (pos==BUY? 1: -1);
pos = NONE;
posindex++;
}
if(pos==NONE){
if(openbuy[i] ) pos=BUY;
if(opensell[i]) pos=SELL;
if(pos!=NONE){
postype [posindex] = pos;
opentime [posindex] = tradetime[i+1];
openprice[posindex] = tradeprice[i+1];
}
}
}
if(pos!=NONE){
closetime [posindex] = tradetime[n-1];
closeprice[posindex] = tradeprice[n-1];
profit [posindex] = (closeprice[posindex]-openprice[posindex]) * (pos==BUY? 1: -1);
}
}
""";
※このipynbファイルと同じディレクトリにindicators.pyがあるものとしてimportしています
import indicators as ind
plt.figure(figsize=(15,7))
d1close = df[['Close']].resample('D').last().dropna()
d1close.plot(c='k', ax=plt.subplot2grid((10,1), (0,0), 7, 1))
ind.iMA(d1close, 100).plot(c='g')
ind.iMA(d1close, 200).plot(c='r')
plt.grid()
ind.iRSI(d1close, 14).plot(c='dodgerblue', ax=plt.subplot2grid((10,1), (7,0), 3, 1))
plt.grid()
class CTester(object):
def __init__(self, df, dllpath):
self.df = df if df['Open'].flags.c_contiguous else df.copy()
self.Index = self.df.index.values
self.tradetime = self.Index.astype(np.uint64)
self.tradeprice = self.df['Open'].values
[setattr(self, c, self.df[c].values) for c in self.df.columns]
lib = ctypes.windll.LoadLibrary(dllpath)
type_uint64_t = np.ctypeslib.ndpointer(dtype=np.uint64)
type_double = np.ctypeslib.ndpointer(dtype=np.float64)
type_int = np.ctypeslib.ndpointer(dtype=int)
type_bool = np.ctypeslib.ndpointer(dtype=np.bool)
lib.backtest.argtypes = [type_uint64_t, # tradetime
type_double, # tradeprice
type_bool, # ob
type_bool, # os
type_bool, # cb
type_bool, # cs
type_int, # postype
type_uint64_t, # opentime
type_double, # openprice
type_uint64_t, # closetime
type_double, # closeprice
type_double, # profit
ctypes.c_long] # n
lib.backtest.restype = None
self.backtest = lib.backtest
self.lib_handle = lib._handle
def __del__(self):
ctypes.windll.kernel32.FreeLibrary(self.lib_handle)
def run(self):
n = len(self.tradetime)
postype = np.zeros(n, dtype=int)
opentime = np.zeros(n, dtype=np.uint64)
openprice = np.zeros(n, dtype=np.float64)
closetime = np.zeros(n, dtype=np.uint64)
closeprice = np.zeros(n, dtype=np.float64)
profit = np.zeros(n, dtype=np.float64)
self.backtest(self.tradetime,
self.tradeprice,
self.ob,
self.os,
self.cb,
self.cs,
postype,
opentime,
openprice,
closetime,
closeprice,
profit,
n)
mask = (postype!=0)
return DataFrame({'pos': postype[mask],
'ot' : pd.to_datetime(opentime[mask], unit='ns'),
'op' : openprice[mask],
'ct' : pd.to_datetime(closetime[mask], unit='ns'),
'cp' : closeprice[mask],
'pl' : profit[mask]})['pos ot op ct cp pl'.split()]
class MA(CTester):
def __init__(self, df, dllpath):
super(MA, self).__init__(df, dllpath)
self.set_signal()
def set_signal(self, f_period=500, s_period=1000):
maf = ind.iMA(self.df, f_period).values
mas = ind.iMA(self.df, s_period).values
self.ob = maf > mas
self.os = maf < mas
self.cb = self.os
self.cs = self.ob
dllpath = 'simple_backtest.dll'
%time bt = MA(df, dllpath)
%time res = bt.run()
print('df len:{}'.format(len(df)))
print('trades:{}'.format(len(res)))
res.tail(10)
%time bt.set_signal(1000, 2000)
%time res = bt.run()
print('df len:{}'.format(len(df)))
print('trades:{}'.format(len(res)))
res.tail(10)
print(df.Close.flags)
print(df.Close.values.strides)
import pandas_datareader.data as web
n225 = web.get_data_yahoo('^N225')
print(n225.Close.flags)
print(n225.Close.values.strides)
print(n225.copy().Close.flags)
print(n225.copy().Close.values.strides)