-
Notifications
You must be signed in to change notification settings - Fork 1
/
Manager.py
361 lines (282 loc) · 9.15 KB
/
Manager.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
import numpy as np
import pandas as pd
import requests
import time
#User defined classes
from OandaEndpoints import Pricing
from OandaCandleStick import CandleStick
from Trader import Trader
from Evaluator import Evaluator
from PlotHelper import PlotHelper
from Notify import notify_from_line
from OandaEndpoints import from_byte_to_dict, from_response_to_dict
from Analysis import is_rise_with_trendline
class Manager:
"""
売買のタイミングを管理するクラス
"""
def __init__(self, param: list, instrument: str, environment: str='demo', mode: str='test'):
"""
トレードの対象となる通貨ペアと時間足(種類と本数)を定義
Parameter
---------
param : list
取り扱う時間足のリスト
instrument : str
取り扱う通貨ペア
environment : str
トレード環境(本番or仮想)を指定
mode : str
デバッグ用の出力を行うかどうかを指定
Self
----
param : list
取り扱う時間足のリスト
instrument : str
取り扱う通貨ペア
environment : str
トレード環境(本番or仮想)を指定
trader : Trader (User defined)
evaluator : Evaluator (User defined)
checking_freq : int (const)
画面出力の頻度を決定するための定数
count : int
画面出力のための内部カウンタ
"""
self.instrument = instrument
self.environment = environment
self.param = param
self.trader = Trader(instrument, environment, mode)
#self.predictors = {k: Predictor(k) for k in param.timelist}
#self.fetters = {k: Fetter(k) for k in param.timelist}
self.evaluator = Evaluator(self.param.timelist, instrument, environment)
self.checking_freq = 10
self.count = 0
def __del__(self):
self.trader.clean()
def has_price(self, msg):
if msg:
msg = from_byte_to_dict(msg)
if msg["type"] == "PRICE":
return True
else:
return False
else:
return False
def has_heartbeat(self, msg):
if msg:
msg = from_byte_to_dict(msg)
if msg["type"] == "HEARTBEAT":
return True
else:
return False
else:
return False
def driver(self, candlesticks):
"""
ローソク足データの収集、解析、取引を取り扱う
Parameters
----------
candlesticks: dict
ローソク足データを任意の時間足分格納したdict
environment: str
取引を行う環境。バーチャル口座(demo)orリアル口座(live)
instrument: str
取引を行う通貨
"""
#現在保有しているポジションをすべて決済
self.trader.clean()
print('<Manager> Close all positions')
while True:
#指定した通貨のtickをストリーミングで取得する
pricing_handler = Pricing(self.environment)
resp = pricing_handler.connect_to_stream(self.instrument)
try:
resp.raise_for_status()
except ConnectionError as e:
print(f'connect_to_stream : Catch {e}')
print(f'connect_to_stream : Retry to Connect')
time.sleep(1)
continue
try:
self.run(resp, candlesticks)
except requests.exceptions.ChunkedEncodingError as e:
print(f'run : Catch {e}')
print(f'run : Retry to Connect')
time.sleep(1)
continue
def run(self, resp, candlesticks):
"""
戦略を元に売買を実行する
Parameter
---------
resp : requests.response
ストリーミングでの接続を行うためのレスポンスデータ
candlesticks : dict
取り扱うローソク足の種類と本数を格納した辞書型
Exception
---------
ValueError
urllib3.exceptions.ProtocolError
requests.exceptions.Chunked EncodingError
"""
for line in resp.iter_lines(1):
if self.has_price(line):
recv = from_byte_to_dict(line)
self.execute_strategy(recv, candlesticks)
#一定時間ごとに注文or決済が反映されたかを確認する
if self.checking_freq == self.count:
print(f'heart beat(span): {self.count}')
#注文が反映されたか
if self.trader.test_is_reflected_order() is True:
#WAIT_ORDER -> POSITION
self.trader.switch_state()
#決済が反映されたか
if self.trader.test_is_reflected_position() is True:
#WAIT_POSITION -> ORDER
self.trader.switch_state()
self.count = 0 if self.checking_freq == self.count else (self.count + 1)
def execute_strategy(self, recv: dict, candlesticks: dict):
"""
売買の戦略を決定し、売買タイミングを決定する
Parameter
---------
recv : dict
為替のtickデータが格納された辞書型のデータ
candlesticks : dict
1つ、またはそれ以上のローソク足の組
"""
for k, v in candlesticks.items():
if v.can_update(recv) is True:
v.update_ohlc_()
print(f'{k} is updated -> total length : {len(v.ohlc)}')
if k == self.param.entry_freq:
try:
#エントリー
(is_order_created, kind) = self.entry(candlesticks)
if is_order_created is True:
self.evaluator.set_order(kind, True)
except (RuntimeError, ValueError) as e:
print(f'{e}')
#決済(クローズ)
self.settle(candlesticks)
#時間足が更新されたときにも注文が反映されたかを確認する
#注文が反映されたか
if self.trader.test_is_reflected_order() is True:
#WAIT_ORDER -> POSITION
self.trader.switch_state()
#決済が反映されたか
if self.trader.test_is_reflected_position() is True:
#WAIT_POSITION -> ORDER
self.trader.switch_state()
v.append_tickdata(recv)
def entry(self, candlesticks: dict) -> 'bool, str':
"""
エントリー状態に遷移させる
is_rise_with_xxx(任意のアルゴリズム)によって戦略を決定
現状、「買い」(今後上昇するかのみ)のための設計
Parameter
---------
candlesticks : dict
1つ、またはそれ以上のローソク足の組
Exception
---------
各is_rise_with_xxxが投げる例外を参照、基本的にValueErrorを推奨
Return
------
is_order_created : bool
注文が生成されたかどうか
kind : str
注文内容'BUY' or 'SELL'
"""
# ORDER状態じゃない場合の例外 here
if self.trader.state != 'ORDER':
raise ValueError('#entry -> Exception : trader.state is not "ORDER"')
# インターフェイスもORDER or not -> order is created & kindへ変更
#if self.trader.state != 'ORDER':
# return False
is_rises = []
error_flag = False
#error_count = 0
"""
Template(now)
-------------
try:
is_rises.append(is_rise_with_xxx(arg1, arg2, ...))
except ValueError as e:
print(f'{e}')
error_count += 1
try:
is_rises.append(is_rise_with_yyy(arg1, arg2, ...))
except ValueError as e:
print(f'{e}')
error_count += 1
if error_count > 0:
print(f'Error count : {error_count}')
raise RuntimeError('entry : error count is not 0')
|
v
Template(in future)
-------------------
try:
is_rises.append(is_rise_with_xxx(arg1, arg2, ...))
is_rises.append(is_rise_with_yyy(arg1, arg2, ...))
except ValueError as e:
print(f'{e}')
error_flag = True
if error_flag is True:
raise RuntimeError('entry : message')
"""
try:
is_rises.append(is_rise_with_xxx(self.param.target, candlesticks))
is_rises.append(is_rise_with_yyy(self.param.target, candlesticks))
except ValueError as e:
print(f'{e}')
error_flag = True
if error_flag is True:
raise RuntimeError('entry : One or more entry algorythm(is_rise_with_xxx) throw exception')
# if error_count > 0:
# print(f'Error count : {error_count}')
# raise RuntimeError('entry : error count is not 0')
if (all(is_rises) is True or any(is_rises) is False) is False:
raise ValueError('entry : is_rises is not [All True] or [All False]')
is_rise = all(is_rises)
#売買両方取り扱う場合(現時点ではオフ)
#kind = 'BUY' if True is is_rise else 'SELL'
#買いの場合のみ取り扱う
kind = 'BUY' if True is is_rise else None
is_order_created = self.trader.test_create_order(is_rise)
if True is is_order_created:
#self.evaluator.set_order(kind, True)
#ORDER状態からORDERWAITINGに状態遷移
self.trader.switch_state()
return is_order_created, kind
def settle(self, candlesticks):
threshold = 8
if self.trader.state == 'POSITION':
#ポジションを決済可能か
if self.trader.can_close_position(threshold) is True:
#決済の注文を発行する
is_position_closed = self.trader.test_close_position()
x = {}
correct = {}
for k, v in candlesticks.items():
x[k] = v.normalize_by('close').values
#or x[k] = v.normalize_by('close' raw=True)
correct[k] = np.mean(x[k][-threshold:])
self.evaluator.set_close(True)
#LINE notification
#timeframes = list(candlesticks.keys())
plothelper = PlotHelper()
plothelper.begin_plotter()
plothelper.add_candlestick(candlesticks)
plothelper.end_plotter('close.png', True)
self.evaluator.log_score()
#決済の注文が発行されたか
if True is is_position_closed:
#ORDER -> WAIT_ORDER
self.trader.switch_state()
else:
print('Position can not be closed in this update')
#決済するかを判断するアルゴリズムを更新
self.trader.update_whether_closing_position(threshold)