Pythonで日経225先物システムトレードバックテスト

#日経225先物とは
日経平均株価を元にした、指数先物取引のことで主に大阪証券取引所に上場されていて、取引時間としては、日本時間の 08:45 - 15:15、16:30 - 翌日05:30に取引される。
扱う銘柄は、日経225先物ラージなので、呼値の刻みが10円、指数の1000倍となる。

下記リンクから今回使用する過去の日経225のデータを取得することが可能
[225Labo](https://225labo.com/modules/downloads_data/index.php?cid=2)

#元ネタのテクニカル
カギ足
書籍名：定本 酒田罫線法 
著者：林　輝太郎
出版社: 同友館 (1991/12/1)

#実装したアルゴリズム

##テクニカル指標
カギ足

##売買ルール
###カギ足プロットルール
1. 現在のレートをプロットする
2. 8:50~15:00の時間内で10分区切りで終値を確認する
3. 2.で現在のプロットから30円以上離れていたらその終値をプロットする
※カギ足の作成方法はここでは省きますので検索してください

###買い
1. カギ足で山を10円上回ったら買い注文
2. 谷を10円下回ったら決済して、同時に売り注文

###売り
1. カギ足で谷を10円下回ったら売り注文
2. 山を10円上回ったら決済して、同時に買い注文

#実装したコード

##10分足のシートの取得

```system.py
def get10minSheet():
	##プログラムが実行されたカレントディレクトリのpathを取得
	print(os.getcwd()) 

	##エクセルのファイル名を入力させる
	print("please input the excel data name ->")
	input_data = input() 

	##225データがあるパスを表示
	print(os.getcwd()+ "\\" + input_data)

	##225データのワークブックを取得
	wb = px.load_workbook(os.getcwd()+ "\\" + input_data)

	#wb = px.load_workbook("N225minif_2017.xlsx")
	
	##10分足シートの取得
	return wb["10min"]
```

##8:50~15:00でルールを満たすものを抽出する関数

```system.py
def get225Data():

	##10分足のシートが返される
	sheet = get10minSheet()

	##カギ足グラフを画像として保存するプログラムをここに書く
	#print("please input the picture name which you want to save(~.xlsx)->")
	#input_save_data = input()

	list1 = []
	list2 = []
	list3 = []
	list4 = []
	i = 2
	count = -1
	yori_flag = False
	prev = 0
	prev1 = 0
	prev2 = 0
	prev3 = 0

	yori_time = datetime.time(8,50,0)  ##datetimeクラスのtimeオブジェクトで保存
	hike_time = datetime.time(15,0,0)
	change_sq = datetime.date(2017,1,1)
	change_3sq = datetime.date(2017,3,1) 
	change_6sq = datetime.date(2017,6,1)
	change_9sq = datetime.date(2017,9,1)
	change_12sq = datetime.date(2017,12,1)

	while(True):
		##not ~ で、~が空白文字列だったらTrueが返ってくる
		if not sheet.cell(row=i,column=1).value:
			break

		if 1 <= sheet.cell(row=i,column=1).value.month and sheet.cell(row=i,column=1).value.month <= 3:
			#8:50が始まったらフラグを立てる
			if yori_time == sheet.cell(row=i,column=2).value:
				yori_flag = True

			if yori_flag == True and sheet.cell(row=i,column=2).value <= hike_time:
				if yori_time == sheet.cell(row=i,column=2).value and abs(sheet.cell(row=i,column=3).value - prev) >= 50:
					current = sheet.cell(row=i,column=3).value
					n225data = Structure()
					n225data.date = sheet.cell(row=i,column=1).value.date()
					n225data.time = sheet.cell(row=i,column=2).value
					n225data.rate = current
					prev = current
					current = sheet.cell(row=i,column=6).value
					list1.append(n225data)
				else:
					current = sheet.cell(row=i,column=6).value

				if abs(current - prev) >= 50:
					n225data = Structure()
					n225data.date = sheet.cell(row=i,column=1).value.date()
					n225data.time = sheet.cell(row=i,column=2).value
					n225data.rate = current
					prev = current
					list1.append(n225data)
			else:
				yori_flag = False

		elif 3 < sheet.cell(row=i,column=1).value.month and sheet.cell(row=i,column=1).value.month <= 6:
			#8:50が始まったらフラグを立てる
			if yori_time == sheet.cell(row=i,column=2).value:
				yori_flag = True

			if yori_flag == True and sheet.cell(row=i,column=2).value <= hike_time:
				if yori_time == sheet.cell(row=i,column=2).value and abs(sheet.cell(row=i,column=3).value - prev1) >= 50:
					current1 = sheet.cell(row=i,column=3).value
					n225data = Structure()
					n225data.date = sheet.cell(row=i,column=1).value.date()
					n225data.time = sheet.cell(row=i,column=2).value
					n225data.rate = current1
					prev1 = current1
					current1 = sheet.cell(row=i,column=6).value
					list2.append(n225data)
				else:
					current1 = sheet.cell(row=i,column=6).value

				if abs(current1 - prev1) >= 50:
					n225data = Structure()
					n225data.date = sheet.cell(row=i,column=1).value.date()
					n225data.time = sheet.cell(row=i,column=2).value
					n225data.rate = current1
					prev1 = current1
					list2.append(n225data)
			else:
				yori_flag = False

		elif 6 < sheet.cell(row=i,column=1).value.month and sheet.cell(row=i,column=1).value.month <= 9:
			#8:50が始まったらフラグを立てる
			if yori_time == sheet.cell(row=i,column=2).value:
				yori_flag = True

			if yori_flag == True and sheet.cell(row=i,column=2).value <= hike_time:
				if yori_time == sheet.cell(row=i,column=2).value and abs(sheet.cell(row=i,column=3).value - prev2) >= 50:
					current2 = sheet.cell(row=i,column=3).value
					n225data = Structure()
					n225data.date = sheet.cell(row=i,column=1).value.date()
					n225data.time = sheet.cell(row=i,column=2).value
					n225data.rate = current2
					prev2 = current2
					current2 = sheet.cell(row=i,column=6).value
					list3.append(n225data)
				else:
					current2 = sheet.cell(row=i,column=6).value

				if abs(current2 - prev2) >= 50:
					n225data = Structure()
					n225data.date = sheet.cell(row=i,column=1).value.date()
					n225data.time = sheet.cell(row=i,column=2).value
					n225data.rate = current2
					prev2 = current2
					list3.append(n225data)
			else:
				yori_flag = False

		elif 9 < sheet.cell(row=i,column=1).value.month and sheet.cell(row=i,column=1).value.month <= 12:
			#8:50が始まったらフラグを立てる
			if yori_time == sheet.cell(row=i,column=2).value:
				yori_flag = True

			if yori_flag == True and sheet.cell(row=i,column=2).value <= hike_time:
				if yori_time == sheet.cell(row=i,column=2).value and abs(sheet.cell(row=i,column=3).value - prev3) >= 50:
					current3 = sheet.cell(row=i,column=3).value
					n225data = Structure()
					n225data.date = sheet.cell(row=i,column=1).value.date()
					n225data.time = sheet.cell(row=i,column=2).value
					n225data.rate = current3
					prev3 = current3
					current3 = sheet.cell(row=i,column=6).value
					list4.append(n225data)
				else:
					current3 = sheet.cell(row=i,column=6).value

				if abs(current3 - prev3) >= 50:
					n225data = Structure()
					n225data.date = sheet.cell(row=i,column=1).value.date()
					n225data.time = sheet.cell(row=i,column=2).value
					n225data.rate = current3
					prev3 = current3
					list4.append(n225data)
			else:
				yori_flag = False

		i += 1


	##カギ足のシグナルと損益計算
	print("please input the sonnekihyou name->")
	input_data_kagi = input()
	entry_point1_kagi = []
	entry_point2_kagi = []
	entry_point3_kagi = []
	entry_point4_kagi = []
	kagiSignal(list1,entry_point1_kagi,input_data_kagi+"1_3"+".xlsx")
	kagiSignal(list2,entry_point2_kagi,input_data_kagi+"4_6"+".xlsx")
	kagiSignal(list3,entry_point3_kagi,input_data_kagi+"7_9"+".xlsx")
	kagiSignal(list4,entry_point4_kagi,input_data_kagi+"10_12"+".xlsx")
```

##売買ルール計算

```system.py
def kagiSignal(list1,entry_point,save_name):
	list_count = 0
	retu_count = 1 #x座標のカウント
	prev_list1 = Structure() #現在の1つ前の点を表す
	yama_tani_queue = deque([]) #終点(山とか谷)を表すキューを作成
	nuki_point = []  #山谷を抜いた瞬間のデータを列挙するリスト

	yama_nuki = False #山を抜いたときにTrueになる
	tani_nuki = False #谷を抜いたときにTrueになる
	
	##このfor文は、山谷を抜いた点を抽出する部分
	for w in list1:
		#初期動作
		if list_count == 0 and list1[0].rate > list1[1].rate:
			prev_list1.rate = list1[1].rate
			prev_list1.vector = -1
		elif list_count == 0 and list1[0].rate < list1[1].rate:
			prev_list1.rate = list1[1].rate
			prev_list1.vector = 1

		#初期動作を終えて3点目からスタート
		if list_count >= 2:
			#前の点のベクトルが1なら上昇中だから、現在値が上ならそのまま上に引くだけ(下降中でも同じ)
			if prev_list1.vector == 1 and prev_list1.rate < w.rate:
				prev_list1.rate = w.rate
				prev_list1.vector = 1
			elif prev_list1.vector == -1 and prev_list1.rate > w.rate:
				prev_list1.rate = w.rate
				prev_list1.vector = -1
			#前の点のベクトルと現在値の方向が逆の時の処理は、列変更で山谷ができることから保存する
			elif prev_list1.vector == 1 and prev_list1.rate > w.rate:
				#山を格納。キューが3個になってしまうようなら、先頭を削除する
				if len(yama_tani_queue) == 2:
					yama_tani_queue.popleft()
					yama_tani_queue.append(prev_list1)
				else:
					yama_tani_queue.append(prev_list1)

				#現在値をprevに設定。Structureで初期化しないとqueueの中身のprevまで変わってしまう。pythonは、初期化された瞬間に新しいアドレスとなるから。
				prev_list1 = Structure()
				prev_list1.rate = w.rate
				prev_list1.vector = -1

				retu_count += 1

			elif prev_list1.vector == -1 and prev_list1.rate < w.rate:
				#谷を格納。キューが3個になってしまうようなら、先頭を削除する
				if len(yama_tani_queue) == 2:
					yama_tani_queue.popleft()
					yama_tani_queue.append(prev_list1)
				else:
					yama_tani_queue.append(prev_list1)

				prev_list1 = Structure()
				prev_list1.rate = w.rate
				prev_list1.vector = 1

				retu_count += 1

			#山谷キューが2個あるときにシグナル判定をする。
			if len(yama_tani_queue) == 2:
				temp = yama_tani_queue.popleft()
				print(w.rate,temp.rate,temp.vector,w.rate-temp.rate,list_count) 
				
				#山を抜いたときに山を抜いたフラグをTrueにする。谷はその逆。それ以外の時は、まだシグナルが発生していないのでdequeueしたデータを戻す
				if temp.vector == 1 and w.rate - temp.rate > 0:
					temp3 = Structure()
					yama_nuki = True
					temp3.x = retu_count
					temp3.rate = w.rate
					temp3.date = w.date
					temp3.signal = "L"
					nuki_point.append(temp3)
				elif temp.vector == -1 and temp.rate - w.rate > 0:
					temp3 = Structure()
					tani_nuki = True
					temp3.x = retu_count
					temp3.rate = w.rate
					temp3.date = w.date
					temp3.signal = "S"
					nuki_point.append(temp3)
				else:
					yama_tani_queue.appendleft(temp)
		list_count += 1

	#損益表作成
	make_performance(entry_point,save_name)
```


##損益データ作成

```system.py
def make_performance(entry_point,save_name):
	#エクセルに書き込む用に準備する
	wb2 = px.Workbook()
	sheet2 = wb2.active
	sheet2.title = "kagiashi"
	sheet2["A1"] = "エントリー日付"
	sheet2["B1"] = "エントリー"
	sheet2["C1"] = "決済日付"
	sheet2["D1"] = "決済"
	sheet2["E1"] = "シグナル"
	sheet2["F1"] = "損益"

	#損益計算
	column_count = 2
	for i in range(1,len(entry_point)):
		sonneki = 0
		#if i <= len(entry_point)-1 and i+1 <= len(entry_point)-1:
		sonneki = profit_loss(entry_point[i-1].rate,entry_point[i].rate,entry_point[i-1].signal)
		sheet2.cell(row=column_count, column=1, value=entry_point[i-1].date)
		sheet2.cell(row=column_count, column=2, value=entry_point[i-1].rate)
		sheet2.cell(row=column_count, column=3, value=entry_point[i].date)
		sheet2.cell(row=column_count, column=4, value=entry_point[i].rate)
		sheet2.cell(row=column_count, column=5, value=entry_point[i-1].signal)
		sheet2.cell(row=column_count, column=6, value=sonneki)
		column_count += 1	
	wb2.save(save_name)
```

#損益結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434980/781a9951-a074-2482-8b77-693c232501c1.png)

損益結果は、縦軸が「何円幅」を表すので、miniを取引していた場合100倍の値段の収益となる。

#総評
良いものを作れたと思うが、思い付きの突貫工事で作成したため、かなり汚いコードとなってしまっている、、、
収益結果としては良いものを作れたと考えている。