%matplotlib inline
import pandas as pd
from pandas import DataFrame, Series
import matplotlib.pyplot as plt
http://www.histdata.com/download-free-forex-historical-data/?/ascii/1-minute-bar-quotes/usdjpy/2016
簡単にするためにOpen,Closeだけ使ってやってみます
csvpath = 'R:/WORK/DAT_ASCII_USDJPY_M1_2016.csv'
df = pd.read_csv(csvpath, sep=';', names=('Time','Open','High','Low','Close', ''),
index_col='Time', parse_dates=True)[['Open', 'Close']]
df.index += pd.offsets.Hour(7)
df.head()
https://github.com/toyolab/MT5IndicatorsPy/blob/master/indicators.py
import indicators as indi
CloseがOpenより高ければ 1 低ければ -1 同じ、またはドローの範囲であれば 0
になるような列を追加します
def calc_answer(df, draw_range=0.0):
df['ans'] = 0
df['ans'] = df['ans'].mask(df['Close']>df['Open']+draw_range, 1)
df['ans'] = df['ans'].mask(df['Close']<df['Open']-draw_range, -1)
return df
calc_answer(df).head()
次はエントリシグナル配列を作ります
HsigがTrueならHighエントリ、LsigがTrueならLoエントリという感じの配列です
終値で算出してるので、配列を1つshiftします
足が切り替わった瞬間に出たサインに従ってエントリするイメージです
def calc_rsi_signal(df, period=14, leveldiff=20):
rsi = indi.iRSI(df, period)
df['Hsig'] = (rsi<50-leveldiff).shift()
df['Lsig'] = (rsi>50+leveldiff).shift()
return df
calc_rsi_signal(df).head()
シグナルでエントリした足の'ans'の列の値をみて勝ち負けの集計をします
resultの列は 勝ち、負け、引き分けをそれぞれ 1, -1, 0 で表します
なにも無いところには None にしておきます
HsigがTrueかつ、ansが 1 なら勝ちなのでresultの列に 1 を入れます
ansが 0 のときはドローなので resultには 0
HsigがTrueでansが -1 のときは負けなので -1 を入れます
Lsigも同様です
def calc_result(df):
df['result'] = None
df['result'] = df['result'].mask((df['Hsig']&(df['ans']==1)) | (df['Lsig']&(df['ans']==-1)), 1)
df['result'] = df['result'].mask((df['Hsig']&(df['ans']!=1)) | (df['Lsig']&(df['ans']!=-1)), -1)
df['result'] = df['result'].mask((df['Hsig']|df['Lsig']) & (df['ans']==0), 0)
return df
calc_result(df).head()
集計結果を見てみます
勝率はドローを含んだものと、ドローを除いたものを表示してます
totalが勝ち越し数です
def summary(df):
bars = len(df)
n = len(df['result'].dropna())
win = (df['result']==1).sum()
loss = (df['result']==-1).sum()
draw = (df['result']==0).sum()
print('bars : {}'.format(bars))
print('N : {}'.format(n))
print('win : {}({:.2f}%)({:.2f}%)'.format(win, win/n*100, win/(win+loss)*100))
print('loss : {}({:.2f}%)({:.2f}%)'.format(loss, loss/n*100, loss/(win+loss)*100))
print('draw : {}({:.2f}%)'.format(draw, draw/n*100))
print('total: {}'.format(win-loss))
summary(df)
損益をグラフにしてみます
ans列を計算するときスプレッド?を0のまま求めたので
たしかスプがないやつは払い戻しが掛け金の8割とかだった気がするので
勝ちの 1 を 0.8 に置き換えて集計してプロットします
def plot(df, payout=1):
df['result'].mask(df['result']==1, payout).dropna().cumsum().plot(figsize=(15,5))
plot(df, 0.8)
rsiのパラメータを変えてテストしてみます
rsi_params = range(10, 31, 5)
for rsi_period in rsi_params:
calc_rsi_signal(df, rsi_period)
calc_result(df)
plot(df, payout=0.8)
plt.legend(rsi_params, loc='best')
複雑なやり方はまた別でしょうけど単純なものだと
全体を見ても ↓ これだけなので結構簡単ですね for文使わずにできましたね(´・ω・`)
def calc_answer(df, draw_range=0.0):
df['ans'] = 0
df['ans'] = df['ans'].mask(df['Close']>df['Open']+draw_range, 1)
df['ans'] = df['ans'].mask(df['Close']<df['Open']-draw_range, -1)
return df
def calc_rsi_signal(df, period=14, leveldiff=20):
rsi = indi.iRSI(df, period)
df['Hsig'] = (rsi<50-leveldiff).shift()
df['Lsig'] = (rsi>50+leveldiff).shift()
return df
def calc_result(df):
df['result'] = None
df['result'] = df['result'].mask((df['Hsig']&(df['ans']==1)) | (df['Lsig']&(df['ans']==-1)), 1)
df['result'] = df['result'].mask((df['Hsig']&(df['ans']!=1)) | (df['Lsig']&(df['ans']!=-1)), -1)
df['result'] = df['result'].mask((df['Hsig']|df['Lsig']) & (df['ans']==0), 0)
return df
def summary(df):
bars = len(df)
n = len(df['result'].dropna())
win = (df['result']==1).sum()
loss = (df['result']==-1).sum()
draw = (df['result']==0).sum()
print('bars : {}'.format(bars))
print('N : {}'.format(n))
print('win : {}({:.2f}%)({:.2f}%)'.format(win, win/n*100, win/(win+loss)*100))
print('loss : {}({:.2f}%)({:.2f}%)'.format(loss, loss/n*100, loss/(win+loss)*100))
print('draw : {}({:.2f}%)'.format(draw, draw/n*100))
print('total: {}'.format(win-loss))
def plot(df, payout=1):
df['result'].mask(df['result']==1, payout).dropna().cumsum().plot(figsize=(15,5))
calc_answer(df)
calc_rsi_signal(df)
calc_result(df)
summary(df)
plot(df, 0.8)
resultに結果が入っていて、変則的な賭け方をする場合は
この配列に掛け金をかけてやれば収益を算出できますね
df['result'].dropna().head()
それと、BOの人はマーチンゲールで張る人が結構いるらしいので(よく知らない)、
連敗数もかなり気になるところらしいので調べてみます
まずは、勝ち、または、負けの結果だけを抽出します
(resultからNoneと0を除外した配列を作ります)
result = df['result'][(df['result']==1)|(df['result']==-1)].astype(float)
print(' ↑ これで1または-1だけになっているはずです ->', result.unique())
l = []
# ここにきてfor文登場!!!(´・ω・`)
for res in result:
if not l:
l = [res]
else:
if res==prev:
l[-1] += res
else:
l.append(res)
prev = res
consecutive = Series(l)
th = 1
consecutive_win = consecutive[consecutive>th]
consecutive_loss = abs(consecutive[consecutive<-th])
print('meanは2以上の連続した結果の平均です')
print('mean: w:{:.2f} l:{:.2f}'.format(consecutive_win.mean(), consecutive_loss.mean()))
print('max : w:{} l:{}'.format(consecutive_win.max(), consecutive_loss.max()))
consecutive.plot(figsize=(15,3)); plt.show()
consecutive.value_counts().sort_index().plot.bar(figsize=(15,3), title='consecutive')
plt.figure(figsize=(15,3))
consecutive_win.value_counts().plot.bar(ax=plt.subplot(121), title='consecutive_win')
consecutive_loss.value_counts().plot.bar(ax=plt.subplot(122), title='consecutive_loss');
最大連敗は13回のようですね
これはマーチンゲールしてたらしんでしまいます(^q^)
仮に払い戻しが1.0だったとしても
sumloss = 0
for i in range(1, 14):
sumloss += 2**(i-1)
bet = 2**(i)
print('{:3d}回負けた 現時点の損失{:6d} でも次は{:6d}賭けて一気に取り返してプラスにするぞ!'.format(
i, sumloss, bet))
これはきつい(´・ω・`)
この方のブログがわかりやすい
http://algorithmtrade.blog110.fc2.com/blog-entry-8.html
(難しい記事も多いですが勉強になるブログです(^ω^))
Zスコア>0なら、負の従属性(勝ちの後は負け易い、負けの後は勝ち易い)
Zスコア<0なら、正の従属性(勝ちの後は勝ち易い、負けの後は負け易い)
と書いてありますな
import scipy.stats as stats
w = float(sum(result==1))
l = float(sum(result==-1))
n = w + l
x = 2 * w * l
r = len(consecutive)
z = (n*(r-0.5)-x) / (x*(x-n)/(n-1))**0.5
print('z score', z)
print('probability', (stats.norm.cdf(abs(z))-0.5) * 2.0)
# これでいいんかな(´・ω・`)?
print('corr', result.corr(result.shift()))