# 取引戦略搭建サンプル
ご注意
- 以下の取引戦略は投資助言を構成するものではなく、学習参考用です。
# 戦略概要
ダブル移動平均線戦略を構築します:
ある銘柄の1分足ローソク足を使用し、異なる期間の2本の移動平均線MA1とMA3を算出し、MA1とMA3の相対的な大きさを追跡して売買タイミングを判断します。
MA1 >= MA3 のとき、その銘柄は強気状態にあり、市場は上昇トレンドであると判断し、新規建てを行います。
MA1 < MA3 のとき、その銘柄は弱気状態にあり、市場は下降トレンドであると判断し、決済を行います。
# フローチャート

# コードサンプル
- Example
from futu import *
############################ グローバル変数設定 ############################
FUTUOPEND_ADDRESS = '127.0.0.1' # OpenD リスニングアドレス
FUTUOPEND_PORT = 11111 # OpenD リスニングポート
TRADING_ENVIRONMENT = TrdEnv.SIMULATE # 取引環境:本番 / デモ
TRADING_MARKET = TrdMarket.HK # 取引市場権限。対応する取引市場権限のアカウントをフィルタするために使用
TRADING_PWD = '123456' # 取引パスワード。取引のロック解除に使用
TRADING_PERIOD = KLType.K_1M # シグナル ローソク足周期
TRADING_SECURITY = 'HK.00700' # 取引原資産
FAST_MOVING_AVERAGE = 1 # 短期移動平均線の期間
SLOW_MOVING_AVERAGE = 3 # 長期移動平均線の期間
quote_context = OpenQuoteContext(host=FUTUOPEND_ADDRESS, port=FUTUOPEND_PORT) # 相場オブジェクト
trade_context = OpenSecTradeContext(filter_trdmarket=TRADING_MARKET, host=FUTUOPEND_ADDRESS, port=FUTUOPEND_PORT, security_firm=SecurityFirm.FUTUSECURITIES) # 取引オブジェクト。取引商品に応じて取引オブジェクトの型を変更
# ロック解除取引
def unlock_trade():
if TRADING_ENVIRONMENT == TrdEnv.REAL:
ret, data = trade_context.unlock_trade(TRADING_PWD)
if ret != RET_OK:
print('取引ロック解除に失敗:', data)
return False
print('取引ロック解除に成功!')
return True
# 市場状態の取得
def is_normal_trading_time(code):
ret, data = quote_context.get_market_state([code])
if ret != RET_OK:
print('市場ステータスの取得に失敗:', data)
return False
market_state = data['market_state'][0]
'''
MarketState.MORNING 香港・A株 前場
MarketState.AFTERNOON 香港・A株 後場、米国株 全日
MarketState.FUTURE_DAY_OPEN 香港・SG・日本先物 日中取引開始
MarketState.FUTURE_OPEN 米国先物 取引開始
MarketState.FUTURE_BREAK_OVER 米国先物 休憩後再開
MarketState.NIGHT_OPEN 香港・SG・日本先物 夜間取引開始
'''
if market_state == MarketState.MORNING or \
market_state == MarketState.AFTERNOON or \
market_state == MarketState.FUTURE_DAY_OPEN or \
market_state == MarketState.FUTURE_OPEN or \
market_state == MarketState.FUTURE_BREAK_OVER or \
market_state == MarketState.NIGHT_OPEN:
return True
print('現在は取引時間外です。')
return False
# ポジション数量の取得
def get_holding_position(code):
holding_position = 0
ret, data = trade_context.position_list_query(code=code, trd_env=TRADING_ENVIRONMENT)
if ret != RET_OK:
print('ポジションデータの取得に失敗:', data)
return None
else:
for qty in data['qty'].values.tolist():
holding_position += qty
print('【ポジション状況】 {} のポジション数量:{}'.format(TRADING_SECURITY, holding_position))
return holding_position
# ローソク足を取得し、移動平均線を計算、強弱を判断
def calculate_bull_bear(code, fast_param, slow_param):
if fast_param <= 0 or slow_param <= 0:
return 0
if fast_param > slow_param:
return calculate_bull_bear(code, slow_param, fast_param)
ret, data = quote_context.get_cur_kline(code=code, num=slow_param + 1, ktype=TRADING_PERIOD)
if ret != RET_OK:
print('ローソク足の取得に失敗:', data)
return 0
candlestick_list = data['close'].values.tolist()[::-1]
fast_value = None
slow_value = None
if len(candlestick_list) > fast_param:
fast_value = sum(candlestick_list[1: fast_param + 1]) / fast_param
if len(candlestick_list) > slow_param:
slow_value = sum(candlestick_list[1: slow_param + 1]) / slow_param
if fast_value is None or slow_value is None:
return 0
return 1 if fast_value >= slow_value else -1
# 板情報の ask1 と bid1 を取得
def get_ask_and_bid(code):
ret, data = quote_context.get_order_book(code, num=1)
if ret != RET_OK:
print('板情報の取得に失敗:', data)
return None, None
return data['Ask'][0][0], data['Bid'][0][0]
# 新規建て関数
def open_position(code):
# 板情報データの取得
ask, bid = get_ask_and_bid(code)
# 発注数量の計算
open_quantity = calculate_quantity()
# 購買力が十分かどうかを判定
if is_valid_quantity(TRADING_SECURITY, open_quantity, ask):
# 発注
ret, data = trade_context.place_order(price=ask, qty=open_quantity, code=code, trd_side=TrdSide.BUY,
order_type=OrderType.NORMAL, trd_env=TRADING_ENVIRONMENT,
remark='moving_average_strategy')
if ret != RET_OK:
print('新規建てに失敗:', data)
else:
print('発注数量が最大購入可能数量を超えています。')
# 決済関数
def close_position(code, quantity):
# 板情報データの取得
ask, bid = get_ask_and_bid(code)
# 決済数量の確認
if quantity == 0:
print('無効な発注数量です。')
return False
# 決済
ret, data = trade_context.place_order(price=bid, qty=quantity, code=code, trd_side=TrdSide.SELL,
order_type=OrderType.NORMAL, trd_env=TRADING_ENVIRONMENT, remark='moving_average_strategy')
if ret != RET_OK:
print('決済に失敗:', data)
return False
return True
# 発注数量の計算
def calculate_quantity():
price_quantity = 0
# 最小取引数量を使用
ret, data = quote_context.get_market_snapshot([TRADING_SECURITY])
if ret != RET_OK:
print('スナップショットの取得に失敗:', data)
return price_quantity
price_quantity = data['lot_size'][0]
return price_quantity
# 購買力が十分かどうかを判定
def is_valid_quantity(code, quantity, price):
ret, data = trade_context.acctradinginfo_query(order_type=OrderType.NORMAL, code=code, price=price,
trd_env=TRADING_ENVIRONMENT)
if ret != RET_OK:
print('最大売買可能数量の取得に失敗:', data)
return False
max_can_buy = data['max_cash_buy'][0]
max_can_sell = data['max_sell_short'][0]
if quantity > 0:
return quantity < max_can_buy
elif quantity < 0:
return abs(quantity) < max_can_sell
else:
return False
# 注文コールバックの表示
def show_order_status(data):
order_status = data['order_status'][0]
order_info = dict()
order_info['銘柄コード'] = data['code'][0]
order_info['価格'] = data['price'][0]
order_info['売買方向'] = data['trd_side'][0]
order_info['数量'] = data['qty'][0]
print('【注文ステータス】', order_status, order_info)
############################ 以下の関数を実装して戦略を完成させてください ############################
# 戦略起動時に一度実行。戦略の初期化に使用
def on_init():
# ロック解除取引(デモ取引の場合はロック解除不要)
if not unlock_trade():
return False
print('************ 戦略実行開始 ***********')
return True
# ティックごとに一度実行。戦略のメインロジックをここに記述可能
def on_tick():
pass
# 新しいローソク足が生成されるたびに一度実行。戦略のメインロジックをここに記述可能
def on_bar_open():
# 区切り線の出力
print('*************************************')
# 通常取引時間帯のみ取引
if not is_normal_trading_time(TRADING_SECURITY):
return
# ローソク足を取得し、移動平均線を計算、強弱を判断
bull_or_bear = calculate_bull_bear(TRADING_SECURITY, FAST_MOVING_AVERAGE, SLOW_MOVING_AVERAGE)
# ポジション数量の取得
holding_position = get_holding_position(TRADING_SECURITY)
# 発注判断
if holding_position == 0:
if bull_or_bear == 1:
print('【シグナル】 買いシグナル、新規買い建て。')
open_position(TRADING_SECURITY)
else:
print('【シグナル】 売りシグナル、空売りは見送り。')
elif holding_position > 0:
if bull_or_bear == -1:
print('【シグナル】 売りシグナル、ポジション決済。')
close_position(TRADING_SECURITY, holding_position)
else:
print('【シグナル】 買いシグナル、追加建て不要。')
# 約定に変化があった場合に一度実行
def on_fill(data):
pass
# 注文ステータスに変化があった場合に一度実行
def on_order_status(data):
if data['code'][0] == TRADING_SECURITY:
show_order_status(data)
################################ フレームワーク実装部分(読み飛ばし可) ###############################
class OnTickClass(TickerHandlerBase):
def on_recv_rsp(self, rsp_pb):
on_tick()
class OnBarClass(CurKlineHandlerBase):
last_time = None
def on_recv_rsp(self, rsp_pb):
ret_code, data = super(OnBarClass, self).on_recv_rsp(rsp_pb)
if ret_code == RET_OK:
cur_time = data['time_key'][0]
if cur_time != self.last_time and data['k_type'][0] == TRADING_PERIOD:
if self.last_time is not None:
on_bar_open()
self.last_time = cur_time
class OnOrderClass(TradeOrderHandlerBase):
def on_recv_rsp(self, rsp_pb):
ret, data = super(OnOrderClass, self).on_recv_rsp(rsp_pb)
if ret == RET_OK:
on_order_status( data)
class OnFillClass(TradeDealHandlerBase):
def on_recv_rsp(self, rsp_pb):
ret, data = super(OnFillClass, self).on_recv_rsp(rsp_pb)
if ret == RET_OK:
on_fill(data)
# メイン関数
if __name__ == '__main__':
# 初期化戦略
if not on_init():
print('戦略の初期化に失敗、スクリプトを終了します!')
quote_context.close()
trade_context.close()
else:
# コールバックの設定
quote_context.set_handler(OnTickClass())
quote_context.set_handler(OnBarClass())
trade_context.set_handler(OnOrderClass())
trade_context.set_handler(OnFillClass())
# 銘柄のティック、ローソク足、板情報を登録してデータを取得
quote_context.subscribe(code_list=[TRADING_SECURITY], subtype_list=[SubType.TICKER, SubType.ORDER_BOOK, TRADING_PERIOD])
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
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
- Output
************ 戦略実行開始 ***********
*************************************
【ポジション状況】 HK.00700 のポジション数量:0
【シグナル】 買いシグナル、新規買い建て。
【注文ステータス】 SUBMITTING {'銘柄コード': 'HK.00700', '価格': 597.5, '売買方向': 'BUY', '数量': 100.0}
【注文ステータス】 SUBMITTED {'銘柄コード': 'HK.00700', '価格': 597.5, '売買方向': 'BUY', '数量': 100.0}
【注文ステータス】 FILLED_ALL {'銘柄コード': 'HK.00700', '価格': 597.5, '売買方向': 'BUY', '数量': 100.0}
*************************************
【ポジション状況】 HK.00700 のポジション数量:100.0
【シグナル】 売りシグナル、ポジション決済。
【注文ステータス】 SUBMITTING {'銘柄コード': 'HK.00700', '価格': 596.5, '売買方向': 'SELL', '数量': 100.0}
【注文ステータス】 SUBMITTED {'銘柄コード': 'HK.00700', '価格': 596.5, '売買方向': 'SELL', '数量': 100.0}
【注文ステータス】 FILLED_ALL {'銘柄コード': 'HK.00700', '価格': 596.5, '売買方向': 'SELL', '数量': 100.0}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13