-
Notifications
You must be signed in to change notification settings - Fork 0
/
AutoTrade.py
306 lines (280 loc) · 14.2 KB
/
AutoTrade.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
import os, sys, ctypes
import win32com.client
import pandas as pd
from datetime import datetime
import requests
import time, calendar
def post_message(token, channel, text):
response = requests.post("https://slack.com/api/chat.postMessage",
headers={"Authorization": "Bearer "+token},
data={"channel": channel, "text": text}
)
myToken = ""
def dbgout(message):
"""인자로 받은 문자열을 파이썬 셸과 슬랙으로 동시에 출력한다."""
print(datetime.now().strftime('[%m/%d %H:%M:%S]'), message)
strbuf = datetime.now().strftime('[%m/%d %H:%M:%S] ') + message
post_message(myToken,"#stock", strbuf)
def printlog(message, * args):
"""인자로 받은 문자열을 파이썬 셸에 출력한다."""
print(datetime.now().strftime('[%m/%d %H:%M:%S]'), message, *args)
# 크레온 플러스 공통 OBJECT
cpCodeMgr = win32com.client.Dispatch('CpUtil.CpStockCode')
cpStatus = win32com.client.Dispatch('CpUtil.CpCybos')
cpTradeUtil = win32com.client.Dispatch('CpTrade.CpTdUtil')
cpStock = win32com.client.Dispatch('DsCbo1.StockMst')
cpOhlc = win32com.client.Dispatch('CpSysDib.StockChart')
cpBalance = win32com.client.Dispatch('CpTrade.CpTd6033')
cpCash = win32com.client.Dispatch('CpTrade.CpTdNew5331A')
cpOrder = win32com.client.Dispatch('CpTrade.CpTd0311')
def check_creon_system():
"""크레온 플러스 시스템 연결 상태를 점검한다."""
# 관리자 권한으로 프로세스 실행 여부
if not ctypes.windll.shell32.IsUserAnAdmin():
printlog('check_creon_system() : admin user -> FAILED')
return False
# 연결 여부 체크
if (cpStatus.IsConnect == 0):
printlog('check_creon_system() : connect to server -> FAILED')
return False
# 주문 관련 초기화 - 계좌 관련 코드가 있을 때만 사용
if (cpTradeUtil.TradeInit(0) != 0):
printlog('check_creon_system() : init trade -> FAILED')
return False
return True
def get_current_price(code):
"""인자로 받은 종목의 현재가, 매도호가, 매수호가를 반환한다."""
cpStock.SetInputValue(0, code) # 종목코드에 대한 가격 정보
cpStock.BlockRequest()
item = {}
item['cur_price'] = cpStock.GetHeaderValue(11) # 현재가
item['ask'] = cpStock.GetHeaderValue(16) # 매도호가
item['bid'] = cpStock.GetHeaderValue(17) # 매수호가
return item['cur_price'], item['ask'], item['bid']
def get_ohlc(code, qty):
"""인자로 받은 종목의 OHLC 가격 정보를 qty 개수만큼 반환한다."""
cpOhlc.SetInputValue(0, code) # 종목코드
cpOhlc.SetInputValue(1, ord('2')) # 1:기간, 2:개수
cpOhlc.SetInputValue(4, qty) # 요청개수
cpOhlc.SetInputValue(5, [0, 2, 3, 4, 5]) # 0:날짜, 2~5:OHLC
cpOhlc.SetInputValue(6, ord('D')) # D:일단위
cpOhlc.SetInputValue(9, ord('1')) # 0:무수정주가, 1:수정주가
cpOhlc.BlockRequest()
count = cpOhlc.GetHeaderValue(3) # 3:수신개수
columns = ['open', 'high', 'low', 'close']
index = []
rows = []
for i in range(count):
index.append(cpOhlc.GetDataValue(0, i))
rows.append([cpOhlc.GetDataValue(1, i), cpOhlc.GetDataValue(2, i),
cpOhlc.GetDataValue(3, i), cpOhlc.GetDataValue(4, i)])
df = pd.DataFrame(rows, columns=columns, index=index)
return df
def get_stock_balance(code):
"""인자로 받은 종목의 종목명과 수량을 반환한다."""
cpTradeUtil.TradeInit()
acc = cpTradeUtil.AccountNumber[0] # 계좌번호
accFlag = cpTradeUtil.GoodsList(acc, 1) # -1:전체, 1:주식, 2:선물/옵션
cpBalance.SetInputValue(0, acc) # 계좌번호
cpBalance.SetInputValue(1, accFlag[0]) # 상품구분 - 주식 상품 중 첫번째
cpBalance.SetInputValue(2, 50) # 요청 건수(최대 50)
cpBalance.BlockRequest()
if code == 'ALL':
dbgout('계좌명: ' + str(cpBalance.GetHeaderValue(0)))
dbgout('결제잔고수량 : ' + str(cpBalance.GetHeaderValue(1)))
dbgout('평가금액: ' + str(cpBalance.GetHeaderValue(3)))
dbgout('평가손익: ' + str(cpBalance.GetHeaderValue(4)))
dbgout('종목수: ' + str(cpBalance.GetHeaderValue(7)))
stocks = []
for i in range(cpBalance.GetHeaderValue(7)):
stock_code = cpBalance.GetDataValue(12, i) # 종목코드
stock_name = cpBalance.GetDataValue(0, i) # 종목명
stock_qty = cpBalance.GetDataValue(15, i) # 수량
if code == 'ALL':
dbgout(str(i+1) + ' ' + stock_code + '(' + stock_name + ')'
+ ':' + str(stock_qty))
stocks.append({'code': stock_code, 'name': stock_name,
'qty': stock_qty})
if stock_code == code:
return stock_name, stock_qty
if code == 'ALL':
return stocks
else:
stock_name = cpCodeMgr.CodeToName(code)
return stock_name, 0
def get_current_cash():
"""증거금 100% 주문 가능 금액을 반환한다."""
cpTradeUtil.TradeInit()
acc = cpTradeUtil.AccountNumber[0] # 계좌번호
accFlag = cpTradeUtil.GoodsList(acc, 1) # -1:전체, 1:주식, 2:선물/옵션
cpCash.SetInputValue(0, acc) # 계좌번호
cpCash.SetInputValue(1, accFlag[0]) # 상품구분 - 주식 상품 중 첫번째
cpCash.BlockRequest()
return cpCash.GetHeaderValue(9) # 증거금 100% 주문 가능 금액
def get_target_price(code):
"""매수 목표가를 반환한다."""
try:
time_now = datetime.now()
str_today = time_now.strftime('%Y%m%d')
ohlc = get_ohlc(code, 10)
if str_today == str(ohlc.iloc[0].name):
today_open = ohlc.iloc[0].open
lastday = ohlc.iloc[1]
else:
lastday = ohlc.iloc[0]
today_open = lastday[3]
lastday_high = lastday[1]
lastday_low = lastday[2]
target_price = today_open + (lastday_high - lastday_low) * 0.5
return target_price
except Exception as ex:
dbgout("`get_target_price() -> exception! " + str(ex) + "`")
return None
def get_movingaverage(code, window):
"""인자로 받은 종목에 대한 이동평균가격을 반환한다."""
try:
time_now = datetime.now()
str_today = time_now.strftime('%Y%m%d')
ohlc = get_ohlc(code, 20)
if str_today == str(ohlc.iloc[0].name):
lastday = ohlc.iloc[1].name
else:
lastday = ohlc.iloc[0].name
closes = ohlc['close'].sort_index()
ma = closes.rolling(window=window).mean()
return ma.loc[lastday]
except Exception as ex:
dbgout('get_movingavrg(' + str(window) + ') -> exception! ' + str(ex))
return None
def buy_etf(code):
"""인자로 받은 종목을 최유리 지정가 FOK 조건으로 매수한다."""
try:
global bought_list # 함수 내에서 값 변경을 하기 위해 global로 지정
if code in bought_list: # 매수 완료 종목이면 더 이상 안 사도록 함수 종료
#printlog('code:', code, 'in', bought_list)
return False
time_now = datetime.now()
current_price, ask_price, bid_price = get_current_price(code)
target_price = get_target_price(code) # 매수 목표가
ma5_price = get_movingaverage(code, 5) # 5일 이동평균가
ma10_price = get_movingaverage(code, 10) # 10일 이동평균가
buy_qty = 0 # 매수할 수량 초기화
if ask_price > 0: # 매도호가가 존재하면
buy_qty = buy_amount // ask_price
stock_name, stock_qty = get_stock_balance(code) # 종목명과 보유수량 조회
#printlog('bought_list:', bought_list, 'len(bought_list):',
# len(bought_list), 'target_buy_count:', target_buy_count)
if current_price > target_price and current_price > ma5_price \
and current_price > ma10_price:
printlog(stock_name + '(' + str(code) + ') ' + str(buy_qty) +
'EA : ' + str(current_price) + ' meets the buy condition!`')
cpTradeUtil.TradeInit()
acc = cpTradeUtil.AccountNumber[0] # 계좌번호
accFlag = cpTradeUtil.GoodsList(acc, 1) # -1:전체,1:주식,2:선물/옵션
# 최유리 FOK 매수 주문 설정
cpOrder.SetInputValue(0, "2") # 2: 매수
cpOrder.SetInputValue(1, acc) # 계좌번호
cpOrder.SetInputValue(2, accFlag[0]) # 상품구분 - 주식 상품 중 첫번째
cpOrder.SetInputValue(3, code) # 종목코드
cpOrder.SetInputValue(4, buy_qty) # 매수할 수량
cpOrder.SetInputValue(7, "2") # 주문조건 0:기본, 1:IOC, 2:FOK
cpOrder.SetInputValue(8, "12") # 주문호가 1:보통, 3:시장가
# 5:조건부, 12:최유리, 13:최우선
# 매수 주문 요청
ret = cpOrder.BlockRequest()
printlog('최유리 FoK 매수 ->', stock_name, code, buy_qty, '->', ret)
if ret == 4:
remain_time = cpStatus.LimitRequestRemainTime
printlog('주의: 연속 주문 제한에 걸림. 대기 시간:', remain_time/1000)
time.sleep(remain_time/1000)
return False
time.sleep(2)
printlog('현금주문 가능금액 :', buy_amount)
stock_name, bought_qty = get_stock_balance(code)
printlog('get_stock_balance :', stock_name, stock_qty)
if bought_qty > 0:
bought_list.append(code)
dbgout("`buy_etf("+ str(stock_name) + ' : ' + str(code) +
") -> " + str(bought_qty) + "EA bought!" + "`")
except Exception as ex:
dbgout("`buy_etf("+ str(code) + ") -> exception! " + str(ex) + "`")
def sell_all():
"""보유한 모든 종목을 최유리 지정가 IOC 조건으로 매도한다."""
try:
cpTradeUtil.TradeInit()
acc = cpTradeUtil.AccountNumber[0] # 계좌번호
accFlag = cpTradeUtil.GoodsList(acc, 1) # -1:전체, 1:주식, 2:선물/옵션
while True:
stocks = get_stock_balance('ALL')
total_qty = 0
for s in stocks:
total_qty += s['qty']
if total_qty == 0:
return True
for s in stocks:
if s['qty'] != 0:
cpOrder.SetInputValue(0, "1") # 1:매도, 2:매수
cpOrder.SetInputValue(1, acc) # 계좌번호
cpOrder.SetInputValue(2, accFlag[0]) # 주식상품 중 첫번째
cpOrder.SetInputValue(3, s['code']) # 종목코드
cpOrder.SetInputValue(4, s['qty']) # 매도수량
cpOrder.SetInputValue(7, "1") # 조건 0:기본, 1:IOC, 2:FOK
cpOrder.SetInputValue(8, "12") # 호가 12:최유리, 13:최우선
# 최유리 IOC 매도 주문 요청
ret = cpOrder.BlockRequest()
printlog('최유리 IOC 매도', s['code'], s['name'], s['qty'],
'-> cpOrder.BlockRequest() -> returned', ret)
if ret == 4:
remain_time = cpStatus.LimitRequestRemainTime
printlog('주의: 연속 주문 제한, 대기시간:', remain_time/1000)
time.sleep(1)
time.sleep(30)
except Exception as ex:
dbgout("sell_all() -> exception! " + str(ex))
if __name__ == '__main__':
try:
symbol_list = ['A122630', 'A252670', 'A233740', 'A250780', 'A225130',
'A280940', 'A261220', 'A217770', 'A295000', 'A176950']
bought_list = [] # 매수 완료된 종목 리스트
target_buy_count = 5 # 매수할 종목 수
buy_percent = 0.19
printlog('check_creon_system() :', check_creon_system()) # 크레온 접속 점검
stocks = get_stock_balance('ALL') # 보유한 모든 종목 조회
total_cash = int(get_current_cash()) # 100% 증거금 주문 가능 금액 조회
buy_amount = total_cash * buy_percent # 종목별 주문 금액 계산
printlog('100% 증거금 주문 가능 금액 :', total_cash)
printlog('종목별 주문 비율 :', buy_percent)
printlog('종목별 주문 금액 :', buy_amount)
printlog('시작 시간 :', datetime.now().strftime('%m/%d %H:%M:%S'))
soldout = False
while True:
t_now = datetime.now()
t_9 = t_now.replace(hour=9, minute=0, second=0, microsecond=0)
t_start = t_now.replace(hour=9, minute=5, second=0, microsecond=0)
t_sell = t_now.replace(hour=15, minute=15, second=0, microsecond=0)
t_exit = t_now.replace(hour=15, minute=20, second=0,microsecond=0)
today = datetime.today().weekday()
if today == 5 or today == 6: # 토요일이나 일요일이면 자동 종료
printlog('Today is', 'Saturday.' if today == 5 else 'Sunday.')
sys.exit(0)
if t_9 < t_now < t_start and soldout == False:
soldout = True
sell_all()
if t_start < t_now < t_sell : # AM 09:05 ~ PM 03:15 : 매수
for sym in symbol_list:
if len(bought_list) < target_buy_count:
buy_etf(sym)
time.sleep(1)
if t_now.minute == 30 and 0 <= t_now.second <= 5:
get_stock_balance('ALL')
time.sleep(5)
if t_sell < t_now < t_exit: # PM 03:15 ~ PM 03:20 : 일괄 매도
if sell_all() == True:
dbgout('`sell_all() returned True -> self-destructed!`')
sys.exit(0)
if t_exit < t_now: # PM 03:20 ~ :프로그램 종료
dbgout('`self-destructed!`')
sys.exit(0)
time.sleep(3)
except Exception as ex:
dbgout('`main -> exception! ' + str(ex) + '`')