In [1]:
%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だけ使ってやってみます

In [2]:
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()
Out[2]:
Open Close
Time
2016-01-04 00:00:00 120.172 120.205
2016-01-04 00:02:00 120.202 120.210
2016-01-04 00:04:00 120.212 120.211
2016-01-04 00:05:00 120.212 120.209
2016-01-04 00:06:00 120.206 120.218

インジの算出には豊嶋先生のindicators.pyを使います

https://github.com/toyolab/MT5IndicatorsPy/blob/master/indicators.py

In [3]:
import indicators as indi

CloseがOpenより高ければ 1 低ければ -1 同じ、またはドローの範囲であれば 0
になるような列を追加します

In [4]:
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()
Out[4]:
Open Close ans
Time
2016-01-04 00:00:00 120.172 120.205 1
2016-01-04 00:02:00 120.202 120.210 1
2016-01-04 00:04:00 120.212 120.211 -1
2016-01-04 00:05:00 120.212 120.209 -1
2016-01-04 00:06:00 120.206 120.218 1

次はエントリシグナル配列を作ります
HsigがTrueならHighエントリ、LsigがTrueならLoエントリという感じの配列です
終値で算出してるので、配列を1つshiftします
足が切り替わった瞬間に出たサインに従ってエントリするイメージです

In [5]:
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()
Out[5]:
Open Close ans Hsig Lsig
Time
2016-01-04 00:00:00 120.172 120.205 1 NaN NaN
2016-01-04 00:02:00 120.202 120.210 1 False False
2016-01-04 00:04:00 120.212 120.211 -1 False True
2016-01-04 00:05:00 120.212 120.209 -1 False True
2016-01-04 00:06:00 120.206 120.218 1 False True

シグナルでエントリした足の'ans'の列の値をみて勝ち負けの集計をします
resultの列は 勝ち、負け、引き分けをそれぞれ 1, -1, 0 で表します
なにも無いところには None にしておきます
HsigがTrueかつ、ansが 1 なら勝ちなのでresultの列に 1 を入れます
ansが 0 のときはドローなので resultには 0
HsigがTrueでansが -1 のときは負けなので -1 を入れます
Lsigも同様です

In [6]:
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()
Out[6]:
Open Close ans Hsig Lsig result
Time
2016-01-04 00:00:00 120.172 120.205 1 NaN NaN None
2016-01-04 00:02:00 120.202 120.210 1 False False None
2016-01-04 00:04:00 120.212 120.211 -1 False True 1
2016-01-04 00:05:00 120.212 120.209 -1 False True 1
2016-01-04 00:06:00 120.206 120.218 1 False True -1

集計結果を見てみます
勝率はドローを含んだものと、ドローを除いたものを表示してます
totalが勝ち越し数です

In [7]:
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)
bars : 372417
N    : 31419
win  : 16960(53.98%)(55.90%)
loss : 13382(42.59%)(44.10%)
draw : 1077(3.43%)
total: 3578

損益をグラフにしてみます
ans列を計算するときスプレッド?を0のまま求めたので
たしかスプがないやつは払い戻しが掛け金の8割とかだった気がするので
勝ちの 1 を 0.8 に置き換えて集計してプロットします

In [8]:
def plot(df, payout=1):
    df['result'].mask(df['result']==1, payout).dropna().cumsum().plot(figsize=(15,5))

plot(df, 0.8)

rsiのパラメータを変えてテストしてみます

In [9]:
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')
Out[9]:
<matplotlib.legend.Legend at 0xde54668>

複雑なやり方はまた別でしょうけど単純なものだと
全体を見ても ↓ これだけなので結構簡単ですね for文使わずにできましたね(´・ω・`)

In [10]:
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)
bars : 372417
N    : 31419
win  : 16960(53.98%)(55.90%)
loss : 13382(42.59%)(44.10%)
draw : 1077(3.43%)
total: 3578

resultに結果が入っていて、変則的な賭け方をする場合は
この配列に掛け金をかけてやれば収益を算出できますね

In [11]:
df['result'].dropna().head()
Out[11]:
Time
2016-01-04 00:04:00     1
2016-01-04 00:05:00     1
2016-01-04 00:06:00    -1
2016-01-04 00:07:00    -1
2016-01-04 00:08:00     1
Name: result, dtype: object

それと、BOの人はマーチンゲールで張る人が結構いるらしいので(よく知らない)、
連敗数もかなり気になるところらしいので調べてみます

まずは、勝ち、または、負けの結果だけを抽出します
(resultからNoneと0を除外した配列を作ります)

In [12]:
result = df['result'][(df['result']==1)|(df['result']==-1)].astype(float)
print(' ↑ これで1または-1だけになっているはずです ->', result.unique())
 ↑ これで1または-1だけになっているはずです -> [ 1. -1.]

pandasでかっこよく連勝と連敗をカウントする方法を思いつけなかったので

普通にforでカウントします(´・ω・`)

In [13]:
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');
meanは2以上の連続した結果の平均です
mean: w:3.28  l:2.78
max : w:16.0  l:13.0

最大連敗は13回のようですね
これはマーチンゲールしてたらしんでしまいます(^q^)
仮に払い戻しが1.0だったとしても

In [14]:
sumloss = 0
for i in range(1, 14):
    sumloss += 2**(i-1)
    bet = 2**(i)
    print('{:3d}回負けた  現時点の損失{:6d}  でも次は{:6d}賭けて一気に取り返してプラスにするぞ!'.format(
            i, sumloss, bet))
  1回負けた  現時点の損失     1  でも次は     2賭けて一気に取り返してプラスにするぞ!
  2回負けた  現時点の損失     3  でも次は     4賭けて一気に取り返してプラスにするぞ!
  3回負けた  現時点の損失     7  でも次は     8賭けて一気に取り返してプラスにするぞ!
  4回負けた  現時点の損失    15  でも次は    16賭けて一気に取り返してプラスにするぞ!
  5回負けた  現時点の損失    31  でも次は    32賭けて一気に取り返してプラスにするぞ!
  6回負けた  現時点の損失    63  でも次は    64賭けて一気に取り返してプラスにするぞ!
  7回負けた  現時点の損失   127  でも次は   128賭けて一気に取り返してプラスにするぞ!
  8回負けた  現時点の損失   255  でも次は   256賭けて一気に取り返してプラスにするぞ!
  9回負けた  現時点の損失   511  でも次は   512賭けて一気に取り返してプラスにするぞ!
 10回負けた  現時点の損失  1023  でも次は  1024賭けて一気に取り返してプラスにするぞ!
 11回負けた  現時点の損失  2047  でも次は  2048賭けて一気に取り返してプラスにするぞ!
 12回負けた  現時点の損失  4095  でも次は  4096賭けて一気に取り返してプラスにするぞ!
 13回負けた  現時点の損失  8191  でも次は  8192賭けて一気に取り返してプラスにするぞ!

これはきつい(´・ω・`)

ついでになんとなくですがこれも計算してみましょう
https://books.google.co.jp/books?id=iUSzmtLJ3AgC&pg=PA6&lpg=PA6&dq=Ralph+Vince+zscore&source=bl&ots=Zuor2w3s8B&sig=oMtpHApCZex1RSe5WrJN-BRVwfE&hl=ja&sa=X&ved=0ahUKEwiayOu_g-XRAhUExbwKHXMCBgAQ6AEIGjAA#v=onepage&q=Ralph%20Vince%20zscore&f=false

この方のブログがわかりやすい
http://algorithmtrade.blog110.fc2.com/blog-entry-8.html
(難しい記事も多いですが勉強になるブログです(^ω^))
Zスコア>0なら、負の従属性(勝ちの後は負け易い、負けの後は勝ち易い)
Zスコア<0なら、正の従属性(勝ちの後は勝ち易い、負けの後は負け易い)
と書いてありますな

In [15]:
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()))
z score 0.44785862076828875
probability 0.345744766527
corr -0.00256370545052