이하영

simulator기능 구현

1 import datetime 1 import datetime
2 from sqlalchemy import * 2 from sqlalchemy import *
3 import pymysql 3 import pymysql
4 -from pandas import DataFrame 4 +from pandas import *
5 5
6 -import config 6 +from Logger import *
7 +import cf
7 8
8 -class Simulator_Api: 9 +class Simulator_Func:
9 def __init__(self,simul_num,op,db_name): 10 def __init__(self,simul_num,op,db_name):
10 self.simul_num=int(simul_num) 11 self.simul_num=int(simul_num)
11 12
12 - if op=="real": 13 + if self.simul_num==-1:
14 + self.set_date()
15 +
16 + elif op=='reset':
17 + self.op='reset'
18 + self.simul_reset=True
19 + self.set_variable()
20 + self.rotate_date()
21 +
22 + elif op=='real':
13 self.op='real' 23 self.op='real'
14 self.simul_reset=False 24 self.simul_reset=False
15 self.db_name=db_name 25 self.db_name=db_name
16 self.set_variable() 26 self.set_variable()
17 27
28 + elif op=='continue':
29 + self.op='continue'
30 + self.simul_reset=False
31 + self.set_variable()
32 + self.rotate_date()
33 +
34 + else:
35 + logger.error("Invalid option")
36 +
37 + # 오늘 날짜를 설정하는 함수
18 def set_date(self): 38 def set_date(self):
19 self.today=datetime.datetime.today().strftime("%Y%m%d") 39 self.today=datetime.datetime.today().strftime("%Y%m%d")
40 + self.today_with_time=datetime.datetime.today().strftime("%Y%m%d%H%M")
41 + self.today_form=datetime.datetime.strptime(self.today,"%Y%m%d").date()
20 42
43 + # 사용되는 변수를 설정하는 함수
21 def set_variable(self): 44 def set_variable(self):
22 self.set_date() 45 self.set_date()
23 - self.set_database() 46 + self.simul_end_date=self.today # 시뮬레이션이 끝나는 날짜
47 + self.start_min="0900" # 장 시작 시간 : 9시
24 48
25 - # 매수/매도 알고리즘 선택 -> 차후 알고리즘 별로 구현 및 재설정 49 + self.use_min=False # 분별 시뮬레이션을 사용하는 변수
50 + self.use_nine=True # 9시에만 거래를 수행하는 변수
51 + self.buy_stop=False # 거래를 중지하는 변수
52 + self.trade_check_num=False # 실시간 조건 매수 옵션. 분별 시뮬레이팅의 경우 True, 일별 시뮬레이팅의 경우 False
53 +
54 + print("simul_num : ",self.simul_num)
55 +
56 + if self.simul_num==1:
57 + self.simul_start_date="20190101"
58 +
59 + # 매수/매도 알고리즘 설정
26 self.buy_algorithm=1 60 self.buy_algorithm=1
27 self.sell_algorithm=1 61 self.sell_algorithm=1
28 62
29 - # 투자에서 사용할 변수 설정 63 + # 시뮬레이션 변수 설정
30 - self.per_invest=1000000 # 한 항목 당 구매할 금액 64 + self.start_invest_price=1000000 # 초기 투자자금
65 + self.invest_unit=100000 # 매수 금액 단위
66 + self.limit_money=300000 # 자산 중 최소로 남겨 둘 금액
67 + self.sell_point=10 # 익절 수익률 기준
68 + self.losscut_point=-2 # 손절 수익률 기준
69 +
70 + self.invest_limit_rate=1.01 # 매수하는 순간 종목의 최신 종가보다 가격이 n%이상 상승했을 때는 매수하지 않음
71 + self.invest_min_limit_rate=0.98 # 매수하는 순간 종목의 최신 종가보다 가격이 n%이상 하락했을 경우 매수하지 않음
72 +
73 + elif self.simul_num==2:
74 + self.simul_start_date="20190101"
75 +
76 + self.buy_algorithm=2
77 + self.sell_algorithm=2
78 +
79 + self.start_invest_price=1000000
80 + self.invest_unit=100000
81 + self.limit_money=300000
82 + self.sell_point=10
83 + self.losscut_point=-2
84 +
85 + self.invest_buy_limit_rate=1.01
86 + self.invest_sell_limit_rate=0.98
31 87
32 - self.sell_point=10 # 매도 수익률 88 + elif self.simul_num==3:
33 - self.limit_money=1000000 # 최소로 남겨놓을 금액 89 + self.simul_start_date = "20190101"
90 +
91 + self.buy_algorithm = 3
92 + self.sell_algorithm = 3
93 +
94 + self.start_invest_price = 1000000
95 + self.invest_unit = 100000
96 + self.limit_money = 300000
97 + self.sell_point = 10
98 + self.losscut_point = -2
99 +
100 + self.invest_buy_limit_rate = 1.01
101 + self.invest_sell_limit_rate = 0.98
102 +
103 + else:
104 + logger.error("Invalid simul_num")
105 +
106 + self.set_db_control()
107 +
108 + if self.op!='real':
109 + self.set_table()
110 + self.get_date_for_simul()
111 +
112 + self.total_valuation_profit=0 # 매도 종목들에 대한 총 수익
113 + self.sum_valuation_profit=0 # 실제 수익 = 매도종목에 대한 수익 + 현재 보유 종목의 수익
114 + self.total_invest_price=self.start_invest_price # 총자산 : 투자금액 + 실제 수익
115 + self.total_purchase_price=0 # 투자금
116 + self.d2_deposit=self.start_invest_price # 예수금 = 초기자본 + 매도수익 - 투자금
117 +
118 + self.update_balance() # 일별 정산
119 +
120 + self.tax_rate=0.0025 # 거래 세금
121 + self.fees_rate=0.00015 # 거래 수수료
122 +
123 + self.simul_reset_lock=False # 시뮬레이터를 멈춘 지점부터 다시 돌릴 것인지를 선택하는 변수
124 +
125 + # database를 control하기 위한 eninge 및 connection을 저장하는 함수
126 + def set_db_control(self):
127 + # simul database에 접속하는 engine
128 + if self.op=='real':
129 + self.engine_simul=create_engine("mysql+pymysql://" + cf.db_id + ":" + cf.db_pw + "@" + cf.db_ip + ":" +
130 + cf.db_port + "/" + str(self.db_name), encoding='utf-8')
131 + else:
132 + self.db_name="simul"+str(self.simul_num)
133 + self.engine_simul=create_engine("mysql+pymysql://" + cf.db_id + ":" + cf.db_pw + "@" + cf.db_ip + ":" +
134 + cf.db_port + "/" + str(self.db_name), encoding='utf-8')
135 + # daily_craw database에 접속하는 engine
136 + # daily_craw : 각 종목에 대해 날짜별 데이터를 저장하는 데이터베이스
137 + self.engine_daily_craw = create_engine("mysql+pymysql://" + cf.db_id + ":" + cf.db_pw + "@" + cf.db_ip + ":" +
138 + cf.db_port + "/daily_craw" , encoding='utf-8')
139 + # min_craw database에 접속하는 engine
140 + # min_craw : 각 종목에 대해 분별 데이터를 저장하는 데디터베이스
141 + self.engine_craw = create_engine("mysql+pymysql://" + cf.db_id + ":" + cf.db_pw + "@" + cf.db_ip + ":" +
142 + cf.db_port + "/min_craw", encoding='utf-8')
143 + # daily_buy_list database에 접속하는 engine
144 + # daily_buy_list : 날짜별로 종목에 대한 데이터를 저장하는 데이터베이스
145 + self.engine_daily_buy_list = create_engine("mysql+pymysql://" + cf.db_id + ":" + cf.db_pw + "@" + cf.db_ip + ":" +
146 + cf.db_port + "/daily_buy_list", encoding='utf-8')
147 +
148 + # mysql에 접속하는 객체 생성
149 + self.db_conn = pymysql.connect(
150 + host=cf.db_ip,
151 + port=int(cf.db_port),
152 + user=cf.db_id,
153 + password=cf.db_pw,
154 + charset='utf8'
155 + )
34 156
35 - # 특정 데이터베이스 내에 특정 테이블이 존재하는지 확인하는 함수 157 + # 데이터베이스와 테이블을 설정
36 - def is_table_exist(self,db_name,table_name): 158 + def set_table(self):
37 - query="select 1 from information_schema.tables where table_schema='%s' and table_name='%s'" 159 + # simul_reset==True인 경우, 시뮬레이터를 초기화하고 다시 구축
38 - result=self.engine_simul.execute(query%(db_name,table_name)).fetchall() 160 + if self.simul_reset:
161 + self.init_database()
162 +
163 + # simul_reset==False인 경우, 시뮬레이터를 초기화하지 않고 마지막으로 끝난 시점 부터 다시 구동
164 + else:
165 + # 시뮬레이터 데이터베이스, transaction 테이블, account_balance 테이블이 모두 존재하는 경우 이어서 시뮬레이션
166 + if (self.is_simul_database_exist() and
167 + self.is_simul_table_exist(self.db_name,"transaction") and
168 + self.is_simul_table_exist(self.db_name,"account_balance")):
169 + self.init_df_transaction()
170 + self.init_df_account_balance()
171 + self.last_simul_date=self.get_last_date_account_balance()
172 + # 필요한 데이터베이스와 테이블이 모두 존재하지 않는 경우, 다시 생성
173 + else:
174 + self.init_database()
175 + self.simul_reset=True
176 +
177 + # 시뮬레이터 데이터베이스 초기화
178 + def init_database(self):
179 + self.drop_database()
180 + self.create_database()
181 + self.init_df_account_balance()
182 + self.init_df_transaction()
183 +
184 + # 시뮬레이터 데이터베이스 삭제
185 + def drop_database(self):
186 + if self.is_simul_database_exist():
187 + query=f"drop DATABASE {self.db_name}"
188 + self.db_conn.cursor().execute(query)
189 + self.db_conn.commit()
190 +
191 + # 시뮬레이터 데이터베이스 생성
192 + def create_database(self):
193 + if not self.is_simul_database_exist():
194 + query=f"create DATABASE {self.db_name}"
195 + self.db_conn.cursor().execute(query)
196 + self.db_conn.commit()
197 +
198 + # 시뮬레이터 데이터베이스가 존재하는지 확인하는 함수
199 + def is_simul_database_exist(self):
200 + query = f"SELECT 1 FROM Information_schema.SCHEMATA WHERE SCHEMA_NAME = '{self.db_name}'"
201 + result=self.engine_daily_buy_list.execute(query).fetchall()
202 + if len(result):
203 + return True
204 + else:
205 + return False
206 +
207 + # 시뮬레이터 데이터베이스 안에 특정 이름을 가진 테이블이 존재하는지 확인하는 함수
208 + def is_simul_table_exist(self,db_name,table_name):
209 + query = f"select 1 from information_schema.tables where table_schema = '{db_name}' and table_name = '{table_name}'"
210 + result=self.engine_simul.execute(query).fetchall()
39 if len(result)==1: 211 if len(result)==1:
40 return True 212 return True
41 else: 213 else:
42 return False 214 return False
43 215
44 - # 데이터베이스 연결 설정 216 + # account_balance table 생성
45 - def set_database(self): 217 + def init_df_account_balance(self):
46 - if self.op=="real": 218 + a_balance={'id':[]}
47 - self.engine_simul=create_engine("mysql+pymysql://"+config.db_id+":"+config.db_pw+"@"+config.db_ip+ 219 + self.account_balance=DataFrame(a_balance,
48 - ":"+config.db_port+"/"+str(self.db_name),encoding='utf-8') 220 + columns=['date', 'today_earning_rate', 'sum_valuation_profit', 'total_profit',
49 - else: 221 + 'today_profit','today_profitcut_count', 'today_losscut_count',
50 - self.db_name='simulator'+str(self.simul_num) 222 + 'today_profitcut','today_losscut','d2_deposit', 'total_possess_count',
51 - self.engine_simul=create_engine("mysql+pymysql://"+config.db_id+":"+config.db_pw+"@"+config.db_ip+ 223 + 'today_buy_count', 'total_invest',
52 - ":"+config.db_port+"/"+str(self.db_name),encoding='utf-8') 224 + 'sum_item_total_purchase', 'total_evaluation', 'today_rate',
53 - 225 + 'today_invest_price','today_sell_price', 'volume_limit', 'sell_point',
54 - self.engine_daily = create_engine("mysql+pymysql://" + config.db_id + ":" + config.db_pw + "@" + config.db_ip + 226 + 'invest_unit','limit_money', 'total_profitcut',
55 - ":" + config.db_port + "/daily_info" , encoding='utf-8') 227 + 'total_losscut','total_profitcut_count','total_losscut_count',
56 - self.engine_stock = create_engine("mysql+pymysql://" + config.db_id + ":" + config.db_pw + "@" + config.db_ip + 228 + 'today_buy_today_sell_count','today_buy_today_possess_count',
57 - ":" + config.db_port + "/stock_info", encoding='utf-8') 229 + 'today_buy_today_profitcut_count','today_buy_today_losscut_count',
58 - self.engine_minute = create_engine("mysql+pymysql://" + config.db_id + ":" + config.db_pw + "@" + config.db_ip + 230 + 'today_buy_today_profitcut_rate','today_buy_today_losscut_rate'],
59 - ":" + config.db_port + "/minute_info", encoding='utf-8') 231 + index=a_balance['id'])
60 - 232 +
61 - self.conn=pymysql.connect( 233 + # transaction table 생성
62 - host=config.db_ip, 234 + def init_df_transaction(self):
63 - port=int(config.db_port), 235 + trs={'id':[]}
64 - user=config.db_id, 236 + self.df_transaction=DataFrame(trs,
65 - password=config.db_pw, 237 + columns=['id', 'order_num', 'code', 'code_name', 'rate', 'purchase_rate',
66 - charset='utf8' 238 + 'purchase_price','present_price', 'valuation_price','valuation_profit',
67 - ) 239 + 'holding_amount', 'buy_date', 'item_total_purchase','chegyul_check',
240 + 'invest_unit','sell_date', 'sell_price', 'sell_rate', 'rate_std',
241 + 'rate_std_mod_val','rate_std_htr', 'rate_htr','rate_std_mod_val_htr',
242 + 'yes_close', 'close', 'd1_diff_rate', 'd1_diff',
243 + 'open', 'high','low','volume',
244 + 'clo5', 'clo10', 'clo20', 'clo60','clo120',
245 + "clo5_diff_rate", "clo10_diff_rate","clo20_diff_rate",
246 + "clo60_diff_rate", "clo120_diff_rate"])
247 +
248 + # account_balance(잔고 데이터)에 저장된 가장 최근의 날짜를 가져오는 함수
249 + def get_last_date_account_balance(self):
250 + query = "SELECT date from account_balance order by date desc limit 1"
251 + result=self.engine_simul.execute(query).fechall()
252 + return result[0][0]
253 +
254 + # 시뮬레이팅 할 날짜 리스트를 가져오는 함수
255 + # 장이 열렸던 날을 self.date_rows에 담기 위해 gs글로벌의 date값을 이용
256 + def get_date_for_simul(self):
257 + query = f"select date from `gs글로벌` " \
258 + f"where date >= '{self.simul_start_date}' and date <= '{self.simul_end_date}' group by date"
259 + self.date_rows = self.engine_daily_craw.execute(query).fetchall()
260 +
261 + # 거래한 데이터를 바탕으로 거래이력(transaction) 테이블을 정산하는 함수
262 + def update_balance(self):
263 + # transaction table이 존재하지 않을 경우 return
264 + if not self.is_simul_table_exist(self.db_name,"transaction"):
265 + return
266 +
267 + query="select sum(valuation_profit) from transaction"
268 + self.sum_valuation_profit=self.engine_simul.execute(query).fethcall()[0][0] # 총수익금 (종목별 평가 금액의 합)
269 +
270 + self.total_invest_price=self.start_invest_price+self.sum_valuation_profit # 총자산
271 +
272 + query="select sum(item_total_purchase) from transaction where sell_date='%s'"
273 + self.total_purchase_price=self.engine_simul.execute(query%(0)).fethcall()[0][0] # 총투자금액
274 + if self.total_purchase_price is None:
275 + self.total_purchase_price=0
276 +
277 + query="select sum(valuation_profit) from transaction where sell_date!='%s'"
278 + self.total_valuation_profit=self.engine_simul.execute(query%(0)).fetchall()[0][0] # 매도 종목들에 대한 수익
279 + if self.total_valuation_profit is None:
280 + self.total_valuation_profit=0
281 +
282 + self.d2_deposit=self.start_invest_price+self.total_valuation_profit-self.total_purchase_price # 예수금
283 +
284 + # 날짜별로 돌아가면서 시뮬레이션을 실행하는 함수
285 + def rotate_date(self):
286 + for i in range(self.date_rows):
287 + date_rows_today=self.date_rows[i][0] # 시뮬레이팅 할 날짜
288 + date_rows_yesterday=self.date_rows[i-1][0] # 시뮬레이팅 하루 전 날짜
289 +
290 + # simul_reset=False인 경우, 시뮬레이터를 멈춘 지점부터 다시 시작
291 + if not self.simul_reset and not self.simul_reset_lock:
292 + if int(date_rows_today)<=int(self.last_simul_date):
293 + continue
294 + else:
295 + self.simul_reset_lock=True
296 +
297 + # 분별 시뮬레이션
298 + if self.use_min:
299 + self.simul_by_min(date_rows_today,date_rows_yesterday,i)
300 + # 일별 시뮬레이션
301 + else:
302 + self.simul_by_date(date_rows_today,date_rows_yesterday,i)
303 + # 시뮬레이션을 완료한 후 account_balance 테이블 업데이트
304 + self.arrange_account_balance()
305 +
306 + # 하루에 대한 시뮬레이션이 끝난 뒤 해당 날짜의 잔고(account_balance)정보를 업데이트하는 함수
307 + def arrange_account_balance(self):
308 + if self.engine_simul.dialect.has_table(self.engine_simul,"account_balance"):
309 + len_date=self.get_len_account_balance_data()
310 + query="select date from account_balance"
311 + rows=self.engine_simul.execute(query).fetchall()
312 +
313 + logger.debug("account balance 최종 정산")
314 +
315 + for i in range(len_date):
316 + # today_buy_count : 오늘 매수한 종목의 수
317 + query="update account_balance " \
318 + "set " \
319 + "today_buy_count=(select count(*) from (select code from transaction where buy_date like '%s')) "\
320 + "where date='%s'"
321 + self.engine_simul.execute(query%("%%"+str(rows[i][0])+"%%",rows[i][0]))
322 +
323 + # today_buy_today_sell_count : 오늘 매수하고 오늘 매도한 종목의 수
324 + query="update account_balance " \
325 + "set " \
326 + "today_buy_today_sell_count=" \
327 + "(select count(*) from " \
328 + "(select code from transaction where buy_date like '%s' and sell_date!=0 group by code)) " \
329 + "where date='%s'"
330 + self.engine_simul.execute(query%("%%"+str(rows[i][0])+"%%",rows[i][0]))
68 331
69 - # 날짜별 주식 데이터를 저장해 놓은 데이터베이스에서 가장 최근 날짜를 가져오는 함수 332 + # today_buy_today_possess_count : 오늘 매수하였으나 매도하지 않은 종목의 수
70 - def get_latest_date(self): 333 + query="update account_balance " \
71 - query="select table_name from information_schema.tables where table_schema='daily_info' and " \ 334 + "set" \
72 - "table_name like '%s' order by table_name desc limit 1" 335 + "today_buy_today_possess_count=" \
73 - result=self.engine_daily.execute(query%("20%%")).fetchall() 336 + "(select count(*) from " \
74 - if len(result)==0: 337 + "(select code from transaction where buy_date like '%s' and sell_date='%s' group by code)) " \
338 + "where date='%s'"
339 + self.engine_simul.execute(query%("%%"+rows[i][0]+"%%",0,rows[i][0]))
340 +
341 + # today_buy_today_profitcut_count : 오늘 매수하고 익절한 종목의 수
342 + query="update account_balance " \
343 + "set " \
344 + "today_buy_today_profitcut_count=" \
345 + "(select count(*) from " \
346 + "(select code from transaction " \
347 + "where buy_date like '%s' and sell_date like '%s' and sell_rate>'%s' group by code)) " \
348 + "where date='%s'"
349 + self.engine_simul.execute(query%("%%" + rows[i][0] + "%%", "%%" + rows[i][0] + "%%", 0, rows[i][0]))
350 +
351 + # today_buy_today_profitcut_rate : 오늘 매수하고 익절한 종목의 수익률
352 + query = "update account_balance " \
353 + "set " \
354 + "today_buy_today_profitcut_rate=round(today_buy_today_profitcut_count /today_buy_count *100,2) "\
355 + "where date = '%s'"
356 + self.engine_simul.execute(query%(rows[i][0]))
357 +
358 + # today_buy_today_losscut_count : 오늘 매수하고 손절한 종목의 수
359 + query = "update account_balance " \
360 + "set " \
361 + "today_buy_today_losscut_count=" \
362 + "(select count(*) from " \
363 + "(select code from transaction " \
364 + "where buy_date like '%s' and sell_date like '%s' and sell_rate < '%s' group by code)) " \
365 + "WHERE date='%s'"
366 + self.engine_simul.execute(query%("%%" + rows[i][0] + "%%", "%%" + rows[i][0] + "%%", 0, rows[i][0]))
367 +
368 + # today_buy_today_losscut_rate : 오늘 매수하고 손절한 종목의 수익률
369 + query = "update account_balance " \
370 + "set " \
371 + "today_buy_today_losscut_rate=round(today_buy_today_losscut_count /today_buy_count *100,2) " \
372 + "WHERE date = '%s'"
373 + self.engine_simul.execute(query%(rows[i][0]))
374 +
375 + # total_profitcut_count : 총 익절한 종목의 수
376 + query = "update account_balance " \
377 + "set " \
378 + "total_profitcut_count=" \
379 + "(select count(*) from " \
380 + "(select code from transaction where sell_rate >= '%s' group by code)) "\
381 + "WHERE date='%s'"
382 + self.engine_simul.execute(query%(0, rows[i][0]))
383 +
384 + # total_profitcut : 총 익절한 금액 (매도가 - 매수가)
385 + query = "update account_balance " \
386 + "set " \
387 + "total_profitcut=sum" \
388 + "(select sell_price-purchase_price from transaction " \
389 + "where sell_price>=purchase_price group by code))" \
390 + "WHERE date = '%s'"
391 + self.engine_simul.execute(query%(rows[i][0]))
392 +
393 + # total_losscut_count : 총 손절한 종목의 수
394 + query = "update account_balance " \
395 + "set " \
396 + "total_losscut_count=" \
397 + "(select count(*) from " \
398 + "(select code from transaction where sell_rate < '%s' group by code )) " \
399 + "WHERE date='%s'"
400 + self.engine_simul.execute(query%(0,rows[i][0]))
401 +
402 + # total_losscut : 총 손절한 금액 (매수가 - 매도가)
403 + query = "update account_balance " \
404 + "set " \
405 + "total_losscut=sum" \
406 + "(select purchase_price-sell_price from transaction " \
407 + "where purchase_price>=sell_price)) " \
408 + "where date='%s'"
409 + self.engine_simul.execute(query%(rows[i][0]))
410 + print("account balance 정산 완료")
411 +
412 + # account_balance에 저장된 일자를 반환하는 함수
413 + def get_len_account_balance_data(self):
414 + query="select date from account_balance"
415 + rows=self.engine_simul.execute(query).fetchall()
416 + return len(rows)
417 +
418 + # 분별 시뮬레이팅
419 + def simul_by_min(self, date_rows_today, date_rows_yesterday, i):
420 + print("************************** date: " + date_rows_today)
421 + # 시뮬레이팅 전에 변수 초기화
422 + self.set_daily_variable()
423 + # daily_buy_list에 시뮬레이팅 할 날짜에 해당하는 테이블과 전 날 테이블이 존재하는지 확인
424 + if self.is_date_exist(date_rows_today) and self.is_date_exist(date_rows_yesterday):
425 + self.db_to_realtime_daily_buy_list(date_rows_today, date_rows_yesterday, i) # 매수리스트 확인
426 + self.trading_by_min(date_rows_today, date_rows_yesterday, i) # 시뮬레이팅 시작
427 + self.db_to_account_balance(date_rows_today) # 잔고 테이블 업데이트
428 +
429 + # transaction 테이블이 존재하고, 현재 보유 중인 종목이 있는 경우, 해당 종목에 대해 데이터베이스 업데이트
430 + if self.is_simul_table_exist(self.db_name, "transaction") and len(self.get_data_from_possessed_item()) != 0:
431 + self.update_transaction_by_date(date_rows_today, option='ALL')
432 +
433 + else:
434 + print("테이블이 존재하지 않습니다")
435 +
436 + # 매일 시뮬레이션이 돌기 전에 변수를 초기화하는 함수
437 + def set_daily_variable(self):
438 + self.buy_stop=False
439 + self.today_invest_price=0
440 +
441 + # daily_buy_list에 특정 날짜의 이름을 가진 테이블이 존재하는지 확인하는 함수
442 + def is_date_exist(self,date):
443 + query = f"select 1 from information_schema.tables where table_schema ='daily_buy_list' and table_name ='{date}'"
444 + result = self.engine_daily_buy_list.execute(query).fetchall()
445 + if len(result) == 1:
446 + return True
447 + else:
75 return False 448 return False
449 +
450 + # 매수 할 종목의 리스트를 선정하는 함수
451 + def db_to_realtime_daily_buy_list(self,date_rows_today,date_rows_yesterday,i):
452 + to_buy_list=None
453 +
454 + # (5,20) 골든크로스
455 + if self.buy_algorithm == 1:
456 + query=f"select * from '{date_rows_yesterday}' " \
457 + f"where yes_clo20>yes_clo5 and clo5>clo20 and close<'{self.invest_unit}' group by code"
458 + to_buy_list=self.engine_daily_buy_list.execute(query).fetchall()
459 +
460 + # (20,60) 골든크로스
461 + elif self.buy_algorithm == 2:
462 + query = f"select * from '{date_rows_yesterday}' " \
463 + f"where yes_clo40 > yes_clo5 and clo5 > clo40 and close < '{self.invest_unit}' group by code"
464 + to_buy_list = self.engine_daily_buy_list.execute(query).fetchall()
465 +
466 + else:
467 + logger.error("Invalid Algorithm Setting...")
468 +
469 + # 알고리즘에 의해 선택된 항목이 존재한다면 데이터베이스에 해당 항목 업데이트
470 + if len(to_buy_list) > 0:
471 + df_to_buy_list = DataFrame(to_buy_list,
472 + columns=['index', 'index2', 'date', 'check_item', 'code',
473 + 'code_name', 'd1_diff','d1_diff_rate',
474 + 'close', 'open', 'high','low', 'volume',
475 + 'clo5', 'clo10', 'clo20', 'clo60', 'clo120',
476 + "clo5_diff_rate", "clo10_diff_rate", "clo20_diff_rate",
477 + "clo60_diff_rate", "clo120_diff_rate",
478 + 'yes_clo5', 'yes_clo10', 'yes_clo20', 'yes_clo60','yes_clo120',
479 + 'vol5', 'vol10', 'vol20', 'vol60', 'vol120'])
480 +
481 + # to_buy_list 중 종목 code가 6자리의 정수로 된 항목만 선택
482 + to_buy_list['code'] = to_buy_list['code'].apply(lambda x: "{:0>6d}".format(int(x)))
483 +
484 + # 시뮬레이터
485 + if self.op != 'real':
486 + to_buy_list['check_item'] = int(0)
487 + to_buy_list.to_sql('realtime_daily_buy_list', self.engine_simul, if_exists='replace')
488 +
489 + # 현재 보유 중인 종목은 매수 리스트(realtime_daily_buy_list) 에서 제거
490 + if self.is_simul_table_exist(self.db_name, "transaction"):
491 + query = "delete from realtime_daily_buy_list " \
492 + "where code in " \
493 + "(select code from transaction where sell_date = '0')"
494 + self.engine_simul.execute(query)
495 +
496 + # realtime_daily_buy_list 테이블에 저장 된 종목들을 저장
497 + self.get_realtime_daily_buy_list()
498 +
499 + # 모의투자 / 실전투자
500 + else:
501 + to_buy_list['check_item'] = int(0)
502 + to_buy_list.to_sql('realtime_daily_buy_list', self.engine_simul, if_exists='replace')
503 +
504 + # 현재 보유 중인 종목들은 삭제
505 + query = "delete from realtime_daily_buy_list where code in (select code from possessed_item)"
506 + self.engine_simul.execute(sql)
507 + else:
508 + self.len_df_realtime_daily_buy_list = 0
509 +
510 + # realtime_daily_buy_list 테이블의 매수 리스트를 가져오는 함수
511 + def get_realtime_daily_buy_list(self):
512 + # check_item = 매수 했을 시 날짜. 매수 하지 않았을 때는 0
513 + query = "select * from realtime_daily_buy_list where check_item = '0' group by code"
514 + realtime_daily_buy_list=self.engine_simul.execute(query).fetchall()
515 +
516 + self.df_realtime_daily_buy_list=DataFrame(realtime_daily_buy_list,
517 + columns=['index', 'index2', 'date', 'check_item', 'code',
518 + 'code_name', 'd1_diff','d1_diff_rate',
519 + 'close', 'open', 'high','low', 'volume',
520 + 'clo5', 'clo10', 'clo20', 'clo60', 'clo120',
521 + "clo5_diff_rate", "clo10_diff_rate", "clo20_diff_rate",
522 + "clo60_diff_rate", "clo120_diff_rate",
523 + 'yes_clo5', 'yes_clo10', 'yes_clo20', 'yes_clo60','yes_clo120',
524 + 'vol5', 'vol10', 'vol20', 'vol60', 'vol120'])
525 +
526 + self.len_df_realtime_daily_buy_list = len(self.df_realtime_daily_buy_list)
527 +
528 + # 분별 시뮬레이팅 함수
529 + # 기능 : 새로운 종목 매수 / 보유종목 매도 / 보유종목 업데이트
530 + def trading_by_min(self,date_rows_today,date_rows_yesterday,i):
531 + self.show_info(date_rows_today)
532 +
533 + # 현재 보유중인 종목이 존재한다면, 보유 종목의 주가를 업데이트
534 + if self.is_simul_table_exist(self.db_name,"transaction"):
535 + self.update_transaction_by_date(date_rows_today,option='OPEN')
536 +
537 + # 분별 시간 데이터를 가져오기
538 + self.get_min_data_for_simul(date_rows_today)
539 + if len(self.min_data_rows)!=0:
540 + # 분 단위로 시뮬레이팅
541 + for t in range(len(self.min_data_rows)):
542 + min=self.min_data_rows[t][0]
543 + # 현재 보유 종목이 있는 경우
544 + if self.is_simul_table_exist(self.db_name,"account_balance"):
545 + self.show_info(min)
546 + # 종목 데이터 업데이트
547 + self.update_transaction_by_min(min)
548 + self.update_transaction_etc()
549 + # 매도
550 + self.auto_trade_sell_stock(min,i)
551 +
552 + # 매수를 진행할 금액이 남아있는 경우
553 + if not self.buy_stop and self.check_balance():
554 + # 매수할 종목리스트 저장
555 + self.get_realtime_daily_buy_list()
556 + # 매수할 종목이 존재한다면 매수
557 + if self.len_df_realtime_daily_buy_list>0:
558 + self.auto_trade_buy_stock(min,date_rows_today,date_rows_yesterday)
559 + else:
560 + print("금일 매수할 종목이 없습니다")
561 + # 보유 종목이 없는 경우
562 + else:
563 + if not self.buy_stop and self.check_balance():
564 + self.auto_trade_buy_stock(min,date_rows_today,date_rows_yesterday)
565 +
566 + if not self.buy_stop and self.use_nine:
567 + print("9시 매수를 종료합니다")
568 + self.buy_stop=True
569 +
570 + # 시뮬레이팅 정보를 출력하는 함수
571 + def show_info(self,date):
572 + print("simulator num : ",self.simul_num)
573 + if self.is_simul_table_exist(self.db_name,"transaction"):
574 + print("simulating 시간 : ",date)
575 + print("보유종목 수 : ",self.get_count_possess_item())
576 +
577 + # 현재 보유하고 있는 종목수를 반환하는 함수
578 + def get_count_possess_item(self):
579 + query="select count(*) from transaction where sell_date='0'"
580 + result=self.engine_simul.execute(query).fetchall()
581 + return result[0][0]
582 +
583 + # 보유 종목의 주가를 일별로 업데이트하는 함수
584 + def update_transaction_by_date(self,date,option='ALL'):
585 + possess_item_list=self.get_data_from_possess_item()
586 +
587 + if len(possess_item_list)==0:
588 + print("보유 종목이 없습니다")
589 +
590 + for i in range(len(possess_item_list)):
591 + code_name=possess_item_list[i][0]
592 + result=self.get_current_price_by_date(code_name,date)
593 + if result==False:
594 + continue
595 + d1_diff_rate = result[0][0]
596 + close = result[0][1]
597 + open = result[0][2]
598 + high = result[0][3]
599 + low = result[0][4]
600 + volume = result[0][5]
601 + clo5 = result[0][6]
602 + clo10 = result[0][7]
603 + clo20 = result[0][8]
604 + clo60 = result[0][9]
605 + clo120 = result[0][10]
606 +
607 + if open:
608 + self.db_to_transaction_update(code_name,d1_diff_rate,close,open,high,low,volume,
609 + clo5,clo10,clo20,clo60,clo120,option)
76 else: 610 else:
611 + continue
612 +
613 + # 보유한 종목의 이름을 반환하는 함수
614 + def get_data_from_possess_item(self):
615 + query="select code_name from transaction where sell_date='0'"
616 + result=self.engine_simul.execute(query).fetchall()
77 return result[0][0] 617 return result[0][0]
78 618
79 - # transaction_history 테이블의 Dataframe 생성
80 - def df_transaction_history(self):
81 - df_temp={'id':[]}
82 - self.df_th=DataFrame(df_temp,
83 - columns=['id','order_num','code','code_name','rate','purchase_rate',
84 - 'purchase_price','present_price','valuation_price','valuation_profit',
85 - 'holding_amount','buy_date','total_purchase_price','contract_check','per_invest',
86 - 'sell_date','sell_price','sell_rate'
87 - ])
88 -
89 - # daily_info 데이터베이스에서 특정 날짜에서 특정 코드에 해당하는 정보만 가져오는 함수수
90 - def get_daily_info_by_code(self,code,date):
91 - query="select * from {} where code='{}' group by code"
92 - daily_info=self.engine_daily.execute(query.format(date,code)).fetchall()
93 - df_daily_info=DataFrame(daily_info,
94 - columns=['index','index2','date','check_item','code',
95 - 'code_name','dff_rate',
96 - 'close','open','high','low','volume',
97 - 'avg5','avg10','avg20','avg60','avg120',
98 - 'prev_avg5','prev_avg10','prev_avg20','prev_avg60','prev_avg120',
99 - 'avg5_diff_rate','avg10_diff_rate','avg20_diff_rate','avg60_diff_rate',
100 - 'avg120_diff_rate',
101 - 'vol5','vol10','vol20','vol60','vol120'])
102 - return df_daily_info
...\ No newline at end of file ...\ No newline at end of file
619 + # daily_buy_list를 통해 특정 날짜에 해당하는 주가정보를 가져오는 함수
620 + def get_current_price_by_date(self,code_name,date):
621 + query = f"select d1_diff_rate, close, open, high, low, volume, clo5, clo10, clo20, clo60, clo120 from `{date}` " \
622 + f"where code_name = '{code_name}' group by code"
623 + result=self.engine_daily_buy_list.execute(query).fetchall()
624 +
625 + if len(result)==1:
626 + return result
627 + else:
628 + return False
629 +
630 + # 현재의 주가를 거래이력(transaction)에 있는 보유 종목에 반영
631 + def db_to_transaction_update(self,code_name,d1_diff_rate,close,open,high,low,volume,
632 + clo5,clo10,clo20,clo60,clo120,option='ALL'):
633 + # 모의투자 / 실전투자의 경우 현재가를 종가로 업데이트
634 + if self.op=='real':
635 + present_price=close
636 + # 시뮬레이터의 경우 현재가를 시가로 업데이트
637 + else:
638 + present_price=open
639 +
640 + # option==ALL이면 모든 데이터를 업데이트
641 + if option=="ALL":
642 + query = f"update transaction " \
643 + f"set " \
644 + f"d1_diff_rate = {d1_diff_rate}, " \
645 + f"close = {close}, open = {open}, high = {high}, low = {low}, volume = {volume}, " \
646 + f"present_price = {present_price}, " \
647 + f"clo5 = {clo5}, clo10 = {clo10}, clo20 = {clo20}, clo60 = {clo60}, clo120 = {clo120} " \
648 + f"where code_name = '{code_name}' and sell_date = {0}"
649 + # option==open이면 open,present_price값만 업데이트
650 + else:
651 + query = f"update transaction " \
652 + f"set " \
653 + f"open = {open}, present_price = {present_price} " \
654 + f"where code_name = '{code_name}' and sell_date = {0}"
655 +
656 + self.engine_simul.execute(query)
657 +
658 + # 분별 데이터를 가져오는 함수
659 + def get_min_data_for_simul(self,simul_start_date):
660 + simul_start_date_min=simul_start_date+self.start_min # 장 시작시간 = 9시
661 + simul_end_date_min=simul_start_date+"1530" # 장 마감시간 = 3시 30분
662 +
663 + query = f"select date from `gs글로벌` " \
664 + f"where date >= '{simul_start_date_min}' and date <='{simul_end_date_min}' and open != 0 group by date"
665 + self.min_data_rows = self.engine_craw.execute(query).fetchall()
666 +
667 + # 거래이력(transaction)의 시세(close) 데이터를 분 별로 업데이트
668 + def update_transaction_by_min(self,min):
669 + possess_item=self.get_data_from_possess_item()
670 + for i in range(len(possess_item)):
671 + code_name=possess_item[i][0]
672 + current_close_price=self.get_current_close_price_by_min(code_name,min)
673 + if current_close_price:
674 + self.db_to_transaction_update_present_price_by_min(code_name,current_close_price)
675 + else:
676 + continue
677 +
678 + # 분별 현재 종가(close)를 가져오는 함수
679 + # 분별 데이터에서 종가는 현재가를 의미하므로 1분마다 시세를 가져오는 함수
680 + def get_current_close_price_by_min(self,code_name,min):
681 + query=f"select close from '{code_name}' " \
682 + f"where date='{min}' and open!=0 and volume!=0 order by sum_volume desc limit 1"
683 + result=self.engine_craw.execute(query).fethcall()
684 + if len(result)==1:
685 + return result[0][0]
686 + else:
687 + return False
688 +
689 + # 보유한 종목에 현재가를 실시간으로 업데이트하는 함수
690 + def db_to_transaction_update_present_price_by_min(self,code_name,current_close_price):
691 + query=f"update transaction set present_price={current_close_price} " \
692 + f"where code_name={code_name} and sell_date='{0}'"
693 + self.engine_simul.execute(query)
694 +
695 + # 잔고 테이블의 주가 이외의 기타 정보를 업데이트
696 + def update_transaction_etc(self):
697 + # valuation_price : 평가금액
698 + # 평가금액 = (보유주 현재가 * 보유주 수량) - (총 구매금액 * 수수료) - (보유주 현재가 * 보유주 수량 * (수수료 + 세금))
699 + query = f"update transaction " \
700 + f"set " \
701 + f"valuation_price = " \
702 + f"round((present_price * holding_amount) - item_total_purchase * {self.fees_rate} - " \
703 + f"present_price*holding_amount*{self.fees_rate + self.tax_rate}) " \
704 + f"where sell_date = '{0}'"
705 + self.engine_simul.execute(query)
706 +
707 + # rate : 현재 실시간 수익률
708 + # valuation_profit : 수익 금액
709 + # 수익 금액 = 평가금액 - 총 구매금액
710 + # 수익률 = 수익금액 / 구매금액 * 100
711 + query = "update account_balance " \
712 + "set " \
713 + "rate= round((valuation_price - item_total_purchase)/item_total_purchase*100,2), " \
714 + "valuation_profit = valuation_price - item_total_purchase " \
715 + "where sell_date = '0';"
716 + self.engine_simul.execute(query)
717 +
718 + # 매도 함수
719 + def auto_trade_sell_stock(self,date,i):
720 + # 매도할 리스트를 저장
721 + sell_list=self.get_sell_list(i)
722 +
723 + for j in range(len(sell_list)):
724 + sell_code=sell_list[j][0]
725 + sell_rate=sell_list[j][1]
726 + present_price=sell_list[j][2]
727 + valuation_profit=sell_list[j][3]
728 +
729 + if sell_rate<0:
730 + print("손절 매도!")
731 + print("=========================================")
732 + print("종목 코드 : ",sell_code)
733 + print("수익 : ",valuation_profit)
734 + print("수익률 : ",sell_rate)
735 + print("=========================================")
736 +
737 + else:
738 + print("익절 매도!")
739 + print("=========================================")
740 + print("종목 코드 : ",sell_code)
741 + print("수익 : ",valuation_profit)
742 + print("수익률 : ",sell_rate)
743 + print("=========================================")
744 +
745 + # 매도 결과를 데이터베이스에 반영
746 + self.send_sell_order(date,present_price,sell_rate,sell_code)
747 +
748 + # 매도한 결과를 transaction에 반영하는 함수
749 + def send_sell_order(self,min,sell_price,sell_rate,code):
750 + query=f"update transaction " \
751 + f"set " \
752 + f"sell_date='{min}', sell_price='{sell_price}', sell_rate='{sell_rate}' " \
753 + f"where code='{code}' and sell_date='{0}' order by buy_date desc limit 1"
754 + self.engine_simul.execute(query)
755 + # 매도 후 정산
756 + self.update_balance()
757 +
758 + # 매도함수를 이용하여 매도할 종목을 가져오는 함수
759 + def get_sell_list(self,i):
760 + sell_list=None
761 +
762 + # (5,20) 데드크로스
763 + # 또는 손절 기준 수익률 이하로 떨어졌을 경우 매도
764 + if self.sell_algorithm==1:
765 + query=f"select code,rate, present_price,valuation_price from transaction " \
766 + f"where sell_date='0' and (clo5<clo20 or rate<='{self.losscut_point}') group by code"
767 + sell_list=self.engine_simul.execute(query).fetchall()
768 +
769 + # (20,60) 데드크로스
770 + elif self.sell_algorithm==2:
771 + query=f"select code,rate, present_price,valuation_price from transaction " \
772 + f"where sell_date='0' and (clo20<clo60 or rate<='{self.losscut_point}') group by code"
773 + sell_list=self.engine_simul.execute(query).fetchall()
774 +
775 + # RSI
776 + #elif self.sell_algorithm==3:
777 + # RMI
778 + #elif self.sell_algorithm==4:
779 +
780 + else:
781 + logger.error("Invalid sell algorithm setting...")
782 +
783 + return sell_list
784 +
785 + # 남은 금액을 확인하는 함수
786 + # 남은 금액이 최소 보유금 + 투자단위금액 이상이면 True, 아니면 False 반환
787 + def check_balance(self):
788 + if int(self.d2_deposit)>=int(self.limit_money)+int(self.invest_unit):
789 + return True
790 + else:
791 + return False
792 +
793 + # 매수 함수
794 + def auto_trade_buy_stock(self,min,date_rows_today,date_rows_yesterday):
795 + # self.df_realtime_daily_buy_list에 있는 모든 종목을 매수
796 + for i in range(self.len_df_realtime_daily_buy_list):
797 + if self.check_balance():
798 + code=self.df_realtime_daily_buy_list.loc[i,'code'].rjust(6,"0")
799 + code_name=self.df_realtime_daily_buy_list.loc[i,'code_name']
800 +
801 + # 분별 시뮬레이팅인 경우
802 + if self.use_min:
803 + # 분별 종목데이터가 존재하지 않는 항목이라면 매수하지 않음
804 + if not self.is_min_craw_table_exist(code_name):
805 + continue
806 + # 일별 시뮬레이팅인 경우
807 + else:
808 + # 일별 종목데이터가 존재하지 않는 항목이라면 매수하지 않음
809 + if not self.is_daily_craw_table_exist(code_name):
810 + continue
811 +
812 + # 일별 시뮬레이팅인 경우
813 + if not self.use_min:
814 + price=self.get_current_open_price_by_date(code,date_rows_today)
815 + else:
816 + price=self.get_current_close_price_by_min(code_name,min)
817 +
818 + # 전날 종가를 저장
819 + yes_close=self.get_yes_close_price_by_date(code,date_rows_yesterday)
820 +
821 + # 종목명 또는 시작가가 존재하지 않는다면 매수하지 않음
822 + if code_name==False or price==0 or price==False:
823 + continue
824 +
825 + # 분별 시뮬레이팅인 경우
826 + if self.use_min and not self.use_nine and self.trade_check_num:
827 + open=self.get_current_open_price_by_date(code,date_rows_today) # 시가
828 + sum_volume=self.get_current_volume_by_min(code_name,min) # 당일 누적 거래량
829 +
830 + if open and sum_volume:
831 + # 실시간 매수조건에 부합하지 않는 경우 매수하지 않음
832 + if not self.check_trade(self.df_realtime_daily_buy_list.loc[i],open,price,sum_volume):
833 + continue
834 +
835 + self.send_buy_order(min,code,code_name,price,yes_close,i)
836 + else:
837 + break
838 +
839 + # min_craw 데이터베이스에 code_name 테이블이 존재하는지 확인하는 함수
840 + def is_min_craw_table_exist(self,code_name):
841 + query = f"select 1 from information_schema.tables where table_schema = 'min_craw' and table_name = '{code_name}'"
842 + result = self.engine_craw.execute(query).fetchall()
843 + if len(result) == 1:
844 + return True
845 + else:
846 + return False
847 +
848 + # daily_craw 데이터베이스에 code_name 테이블이 존재하는지 확인하는 함수
849 + def is_daily_craw_table_exist(self,code_name):
850 + query = f"select 1 from information_schema.tables where table_schema = 'daily_craw' and table_name = '{code_name}'"
851 + result = self.engine_daily_craw.execute(query).fetchall()
852 + if len(result) == 1:
853 + return True
854 + else:
855 + return False
856 +
857 + # 특정 날짜의 테이블에서 특정 종목의 시가를 가져오는 함수
858 + def get_current_open_price_by_date(self,code,date):
859 + query=f"select open from `{date}` where code = '{code}' group by code"
860 + open=self.engine_daily_buy_list.execute(query).fetchall()
861 + if len(open)==1:
862 + return open[0][0]
863 + else:
864 + return False
865 +
866 + # 전날 종가(close)를 가져오는 함수
867 + def get_yes_close_price_by_date(self,code,date):
868 + query = f"select close from `{date}` where code = '{code}' group by code"
869 + result = self.engine_daily_buy_list.execute(query).fetchall()
870 + if len(result) == 1:
871 + return result[0][0]
872 + else:
873 + return False
874 +
875 + # 분별 현재 누적 거래량을 가져오는 함수
876 + def get_current_volume_by_min(self,code_name,min):
877 + query= f"select sum_volume from `{code_name}` " \
878 + f"where date = '{min}' and open != 0 and volume !=0 order by sum_volume desc limit 1"
879 + result = self.engine_craw.execute(query).fetchall()
880 + if len(result) == 1:
881 + return result[0][0]
882 + else:
883 + return False
884 +
885 + # 실시간 주가를 분석하여 매수 여부를 결정하는 함수
886 + # param : df_row - 매수 종목 리스트
887 + # open_price - 시가
888 + # current_price - 현재가
889 + # current_sum_volume - 현재 누적 거래량
890 + # return : True인 경우 매수, False인 경우 매수하지 않음
891 + def check_trade(self,df_row,open_price,current_price,current_sum_volume):
892 + code_name = df_row['code_name']
893 + yes_vol20 = df_row['vol20']
894 + yes_close = df_row['close']
895 + yes_high = df_row['high']
896 + yes_low = df_row['low']
897 + yes_volume = df_row['volume']
898 +
899 + # 실시간 거래 대금 체크 알고리즘
900 + # 어제 종가 보다 현재가가 증가했고, 거래 대금이 어제 거래대금에 비해서 x배 올라갔을 때 매수
901 + if self.trade_check_num == 1:
902 + yes_total_tr_price = yes_close * yes_volume # 전날 거래 대금
903 + current_total_tr_price = current_price * current_sum_volume # 현재 거래 대금
904 +
905 + if current_price > yes_close and current_total_tr_price > yes_total_tr_price * self.volume_up:
906 + return True
907 + else:
908 + return False
909 +
910 + # 현재가가 매수 가격 최저 범위와 매수 가격 최고 범위 안에 들어와 있다면 매수
911 + elif self.trade_check_num == 2:
912 + min_buy_limit = int(yes_close) * self.invest_min_limit_rate # 매수가격 최저 범위
913 + max_buy_limit = int(yes_close) * self.invest_limit_rate # 매수가격 최고 범위
914 +
915 + if min_buy_limit < current_price < max_buy_limit:
916 + return True
917 + else:
918 + return False
919 +
920 + # 래리 윌리엄스 변동성 돌파 알고리즘(매수)
921 + elif self.trade_check_num == 3:
922 + # 변동폭(range): 전일 고가(yes_high)에서 전일 저가(yes_low)를 뺀 가격
923 + # 매수시점 : 현재가 > 시작가 + (변동폭 * k) [k는 0~1 사이 수]
924 + range = yes_high - yes_low
925 + if open_price + range * self.rarry_k < current_price:
926 + return True
927 + else:
928 + return False
929 +
930 + else:
931 + logger.error("Invalid trade_check_num")
932 +
933 + # 데이터베이스에 매수 내역 업데이트
934 + def send_buy_order(self,date,code,code_name,price,yes_close,j):
935 + # 가격이 invest_unit보다 작은 경우 매수
936 + if price < self.invest_unit:
937 + # 매수하는 경우 account_balance에 해당 내역을 추가
938 + self.db_to_transaction(date,self.df_realtime_daily_buy_list,j,code,code_name,price,yes_close)
939 +
940 + # 매수를 성공적으로 했으면 realtime_daily_buy_list 테이블의 check_item 에 매수 시간을 설정
941 + self.update_realtime_daily_buy_list(code, date)
942 +
943 + # 정산
944 + self.update_balance()
945 +
946 + # transaction 테이블에 추가하는 함수
947 + def db_to_transaction(self,min,df,index,code,code_name,purchase_price,yesterday_close):
948 + self.df_transaction.loc[0,'code']=code
949 + self.df_transaction.loc[0,'code_name']=code_name
950 + self.df_transaction.loc[0,'rate']=float(-0.33)
951 +
952 + if yesterday_close:
953 + self.df_transaction.loc[0, 'purchase_rate'] = round(
954 + (float(purchase_price) - float(yesterday_close)) / float(yesterday_close) * 100, 2)
955 +
956 + self.df_transaction.loc[0, 'purchase_price'] = purchase_price
957 + self.df_transaction.loc[0, 'present_price'] = purchase_price
958 +
959 + self.df_transaction.loc[0, 'holding_amount'] = int(self.invest_unit / purchase_price)
960 + self.df_transaction.loc[0, 'buy_date'] = min
961 + self.df_transaction.loc[0, 'item_total_purchase'] = self.df_transaction.loc[0, 'purchase_price'] * \
962 + self.df_transaction.loc[0, 'holding_amount']
963 +
964 + self.today_invest_price = self.today_invest_price + self.df_transaction.loc[0, 'item_total_purchase']
965 +
966 + self.df_transaction.loc[0, 'chegyul_check'] = 0
967 + self.df_transaction.loc[0, 'id'] = 0
968 + self.df_transaction.loc[0, 'invest_unit'] = self.invest_unit
969 +
970 + self.df_transaction.loc[0, 'yes_close'] = yesterday_close
971 + self.df_transaction.loc[0, 'close'] = df.loc[index, 'close']
972 +
973 + self.df_transaction.loc[0, 'open'] = df.loc[index, 'open']
974 + self.df_transaction.loc[0, 'high'] = df.loc[index, 'high']
975 + self.df_transaction.loc[0, 'low'] = df.loc[index, 'low']
976 + self.df_transaction.loc[0, 'volume'] = df.loc[index, 'volume']
977 +
978 + self.df_transaction.loc[0, 'd1_diff_rate'] = float(df.loc[index, 'd1_diff_rate'])
979 + self.df_transaction.loc[0, 'clo5'] = df.loc[index, 'clo5']
980 + self.df_transaction.loc[0, 'clo10'] = df.loc[index, 'clo10']
981 + self.df_transaction.loc[0, 'clo20'] = df.loc[index, 'clo20']
982 + self.df_transaction.loc[0, 'clo60'] = df.loc[index, 'clo40']
983 + self.df_transaction.loc[0, 'clo120'] = df.loc[index, 'clo60']
984 +
985 + if df.loc[index, 'clo5_diff_rate'] is not None:
986 + self.df_transaction.loc[0, 'clo5_diff_rate'] = float(df.loc[index, 'clo5_diff_rate'])
987 + if df.loc[index, 'clo10_diff_rate'] is not None:
988 + self.df_transaction.loc[0, 'clo10_diff_rate'] = float(df.loc[index, 'clo10_diff_rate'])
989 + if df.loc[index, 'clo20_diff_rate'] is not None:
990 + self.df_transaction.loc[0, 'clo20_diff_rate'] = float(df.loc[index, 'clo20_diff_rate'])
991 + if df.loc[index, 'clo60_diff_rate'] is not None:
992 + self.df_transaction.loc[0, 'clo40_diff_rate'] = float(df.loc[index, 'clo40_diff_rate'])
993 + if df.loc[index, 'clo120_diff_rate'] is not None:
994 + self.df_transaction.loc[0, 'clo60_diff_rate'] = float(df.loc[index, 'clo60_diff_rate'])
995 +
996 + self.df_transaction.loc[0, 'valuation_profit'] = int(0)
997 +
998 + self.df_transaction=self.df_transaction.fillna(0)
999 +
1000 + # 거래이력 테이블이 이미 존재한다면 데이터 추가
1001 + if self.is_simul_table_exist(self.db_name, "transaction"):
1002 + self.df_transaction.to_sql('transaction', self.engine_simul, if_exists='append')
1003 + # 테이블이 존재하지 않는다면 생성
1004 + else:
1005 + self.df_transaction.to_sql('transaction', self.engine_simul, if_exists='replace')
1006 +
1007 + # 매수할 리스트(realtime_daily_buy_list)에서 특정 종목을 매수한 경우 check_item칼럼에 매수한 날짜를 저장하는 함수
1008 + def update_realtime_daily_buy_list(self,code,min):
1009 + query = f"update realtime_daily_buy_list set check_item = '{min}' where code = '{code}'"
1010 + self.engine_simul.execute(query)
1011 +
1012 + # account_balance(잔고) 테이블을 생성 및 데이터를 추가하는 함수
1013 + def db_to_account_balance(self,date_rows_today):
1014 + self.update_balance()
1015 + # 거래이력(transaction)테이블이 존재하지 않는다면 return
1016 + if not self.is_simul_table_exist(self.db_name,"transaction"):
1017 + return
1018 +
1019 + columns = ['date', 'today_earning_rate', 'sum_valuation_profit', 'total_profit',
1020 + 'today_profit', 'today_profitcut_count', 'today_losscut_count',
1021 + 'today_profitcut', 'today_losscut', 'd2_deposit', 'total_possess_count',
1022 + 'today_buy_count','total_invest',
1023 + 'sum_item_total_purchase', 'total_evaluation', 'today_rate',
1024 + 'today_invest_price', 'today_sell_price', 'volume_limit', 'sell_point',
1025 + 'invest_unit', 'limit_money', 'total_profitcut',
1026 + 'total_losscut', 'total_profitcut_count', 'total_losscut_count']
1027 +
1028 + self.account_balance.loc[0,'date']=date_rows_today # 구매일
1029 + self.account_balance.loc[0, 'sum_valuation_profit'] = self.sum_valuation_profit # 총평가금액
1030 + self.account_balance.loc[0, 'total_profit'] = self.total_valuation_profit # 총수익
1031 + self.account_balance.loc[0, 'today_profit'] = self.get_today_profit(date_rows_today) # 당일수익
1032 +
1033 + self.account_balance.loc[0, 'today_profitcut_count'] = self.get_today_profitcut_count(date_rows_today) # 당일 익절종목 수
1034 + self.account_balance.loc[0, 'today_losscut_count'] = self.get_today_losscut_count(date_rows_today) # 당일 손절종목 수
1035 + self.account_balance.loc[0, 'today_profitcut'] = self.get_sum_today_profitcut(date_rows_today) # 당일 익절금액
1036 + self.account_balance.loc[0, 'today_losscut'] = self.get_sum_today_losscut(date_rows_today) # 당일 손절금액
1037 +
1038 + self.account_balance.loc[0, 'd2_deposit'] = self.d2_deposit # 예수금
1039 + self.account_balance.loc[0, 'total_possess_count'] = self.get_total_possess_count() # 보유종목 수
1040 + self.account_balance.loc[0, 'today_buy_count'] = 0 # 오늘 구매한 종목수
1041 + self.account_balance.loc[0, 'total_invest'] = self.total_invest_price # 총 투자금액
1042 +
1043 + self.account_balance.loc[0, 'sum_item_total_purchase'] = self.get_sum_item_total_purchase() # 총매입금액
1044 + self.account_balance.loc[0, 'total_evaluation'] = self.get_sum_valuation_price() # 총평가금액
1045 + try:
1046 + self.account_balance.loc[0, 'today_rate'] = round(
1047 + (float(self.account_balance.loc[0, 'total_evaluation']) -
1048 + float(self.account_balance.loc[0, 'sum_item_total_purchase'])) /
1049 + float(self.account_balance.loc[0, 'sum_item_total_purchase']) * (100 - 0.33), 2) # 당일 기준 수익률
1050 + except ZeroDivisionError:
1051 + pass
1052 +
1053 + self.account_balance.loc[0, 'today_invest_price'] = float(self.today_invest_price) # 당일 투자금액
1054 + self.account_balance.loc[0, 'today_sell_price'] = self.get_sum_today_sell_price(date_rows_today)
1055 + self.account_balance.loc[0, 'volume_limit'] = self.volume_limit
1056 + self.account_balance.loc[0, 'sell_point'] = self.sell_point
1057 + self.account_balance.loc[0, 'invest_unit'] = self.invest_unit
1058 +
1059 + self.account_balance.loc[0, 'limit_money'] = self.limit_money
1060 + self.account_balance.loc[0, 'total_profitcut'] = self.get_sum_total_profitcut()
1061 + self.account_balance.loc[0, 'total_losscut'] = self.get_sum_total_losscut()
1062 + self.account_balance.loc[0, 'total_profitcut_count'] = self.get_sum_total_profitcut_count()
1063 + self.account_balance.loc[0, 'total_losscut_count'] = self.get_sum_total_losscut_count()
1064 +
1065 + # 데이터베이스에 테이블 추가
1066 + self.account_balance.to_sql('account_balance', self.engine_simul, if_exists='append')
1067 +
1068 + # 매도종목에 대한 당일 수익
1069 + query = f"update account_balance " \
1070 + f"set " \
1071 + f"today_earning_rate =round(today_profit / total_invest * 100, 2) WHERE date='{date_rows_today}'"
1072 + self.engine_simul.execute(query)
1073 +
1074 + # 당일 수익을 계산하는 함수
1075 + def get_today_profit(self,date):
1076 + query="SELECT sum(valuation_profit) from transaction where sell_date like '%s'"
1077 + result=self.engine_simul.execute(query%("%%"+date+"%%")).fetchall()
1078 + return result[0][0]
1079 +
1080 + # 당일 익절 종목 수를 반환하는 함수
1081 + def get_today_profitcut_count(self,date):
1082 + query = "SELECT count(code) from transaction where sell_date like '%s' and sell_rate>='%s'"
1083 + return self.engine_simul.execute(query % ("%%" + date + "%%", 0)).fetchall()[0][0]
1084 +
1085 + # 당일 손절 종목 수를 반환하는 함수
1086 + def get_today_losscut_count(self, date):
1087 + query = "SELECT count(code) from transaction where sell_date like '%s' and sell_rate<'%s'"
1088 + return self.engine_simul.execute(query % ("%%" + date + "%%", 0)).fetchall()[0][0]
1089 +
1090 + # 당일 익절 금액을 반환하는 함수
1091 + def get_sum_today_profitcut(self, date):
1092 + query = "SELECT sum(valuation_profit) from transaction where sell_date like '%s' and valuation_profit >= '%s' "
1093 + return self.engine_simul.execute(query % ("%%" + date + "%%", 0)).fetchall()[0][0]
1094 +
1095 + # 당일 손절 금액을 반환하는 함수
1096 + def get_sum_today_losscut(self, date):
1097 + query = "SELECT sum(valuation_profit) from transaction where sell_date like '%s' and valuation_profit < '%s' "
1098 + return self.engine_simul.execute(query % ("%%" + date + "%%", 0)).fetchall()[0][0]
1099 +
1100 + # 총 보유한 종목 수를 반환하는 함수
1101 + def get_total_possess_count(self):
1102 + query = "select count(code) from transaction where sell_date = '%s'"
1103 + return self.engine_simul.execute(query % (0)).fetchall()[0][0]
1104 +
1105 + # 총 매입금액을 계산하는 함수
1106 + def get_sum_item_total_purchase(self):
1107 + query = "SELECT sum(item_total_purchase) from transaction where sell_date = '%s'"
1108 + result = self.engine_simul.execute(query % (0)).fetchall()[0][0]
1109 + if result is not None:
1110 + return result
1111 + else:
1112 + return 0
1113 +
1114 + # 총평가금액을 계산하는 함수
1115 + def get_sum_valuation_price(self):
1116 + query = "SELECT sum(valuation_price) from transaction where sell_date = '%s'"
1117 + result = self.engine_simul.execute(query % (0)).fetchall()[0][0]
1118 + if result is not None:
1119 + return result
1120 + else:
1121 + return 0
1122 +
1123 + # 당일 매도금액을 계산하는 함수
1124 + def get_sum_today_sell_price(self, date):
1125 + query = "SELECT sum(valuation_price) from transaction where sell_date like '%s'"
1126 + return self.engine_simul.execute(query % ("%%" + date + "%%")).fetchall()[0][0]
1127 +
1128 + # 총 익절금액을 계산하는 함수
1129 + def get_sum_total_profitcut(self):
1130 + query = "SELECT sum(valuation_profit) from transaction where sell_date != 0 and valuation_profit >= '%s' "
1131 + return self.engine_simul.execute(query % (0)).fetchall()[0][0]
1132 +
1133 + # 총 손절금액을 계산하는 함수
1134 + def get_sum_total_losscut(self):
1135 + query = "SELECT sum(valuation_profit) from transaction where sell_date != 0 and valuation_profit < '%s' "
1136 + return self.engine_simul.execute(query % (0)).fetchall()[0][0]
1137 +
1138 + # 총 익절종목수를 반환하는 함수
1139 + def get_sum_total_profitcut_count(self):
1140 + query = "select count(code) from transaction where sell_date != 0 and valuation_profit >= '%s'"
1141 + return self.engine_simul.execute(query % (0)).fetchall()[0][0]
1142 +
1143 + # 총 손절종목수를 반환하는 함수
1144 + def get_sum_total_losscut_count(self):
1145 + query = "select count(code) from transaction where sell_date != 0 and valuation_profit < '%s' "
1146 + return self.engine_simul.execute(query % (0)).fetchall()[0][0]
1147 +
1148 + # 보유종목의 종목명을 반환하는 함수
1149 + def get_data_from_possessed_item(self):
1150 + query="SELECT code_name from transaction where sell_date = '%s'"
1151 + return self.engine_simul.execute(query % (0)).fetchall()
1152 +
1153 + # 날짜별 시뮬레이팅 함수
1154 + def simul_by_date(self,date_rows_today,date_rows_yesterday,i):
1155 + # 시뮬레이팅 전 변수 초기화
1156 + self.set_daily_variable()
1157 +
1158 + # daily_buy_list에 시뮬레이팅 할 날짜에 해당하는 테이블과 전 날 테이블이 존재하는지 확인
1159 + if self.is_date_exist(date_rows_today) and self.is_date_exist(date_rows_yesterday):
1160 + # 당일 매수 할 종목 저장
1161 + self.db_to_realtime_daily_buy_list(date_rows_today, date_rows_yesterday, i)
1162 + # 매수/매도
1163 + self.trading_by_date(date_rows_today, date_rows_yesterday, i)
1164 +
1165 + if self.is_simul_table_exist(self.db_name, "transaction") and len(self.get_data_from_possessed_item()) != 0:
1166 + # 보유 중인 종목들의 주가를 일별로 업데이트
1167 + self.update_transaction_by_date(date_rows_today,option="ALL")
1168 +
1169 + # 정산
1170 + self.db_to_account_balance(date_rows_today)
1171 +
1172 + else:
1173 + print("날짜 테이블이 존재하지 않습니다.")
1174 +
1175 + # 날짜별 거래 함수
1176 + def trading_by_date(self,date_rows_today,date_rows_yesterday,i):
1177 + self.show_info(date_rows_today)
1178 +
1179 + # 보유주 정보 업데이트
1180 + if self.is_simul_table_exist(self.db_name, "transaction") and len(self.get_data_from_possessed_item()) != 0:
1181 + self.update_transaction_by_date(date_rows_today,option="OPEN")
1182 + self.update_transaction_etc()
1183 +
1184 + # 매도
1185 + self.auto_trade_sell_stock(date_rows_today,i)
1186 +
1187 + # 매수할 잔액이 존재한다면 매수
1188 + if self.check_balance():
1189 + self.auto_trade_buy_stock(str(date_rows_today) + "0900", date_rows_today, date_rows_yesterday)
1190 +
1191 + # 매도 리스트가 존재하지 않는다면 매수만 진행
1192 + else:
1193 + if self.check_balance():
1194 + self.auto_trade_buy_stock(str(date_rows_today) + "0900", date_rows_today, date_rows_yesterday)
...\ No newline at end of file ...\ No newline at end of file
......
1 from Open_Api import * 1 from Open_Api import *
2 from collections import OrderedDict 2 from collections import OrderedDict
3 from pandas import DataFrame 3 from pandas import DataFrame
4 +import time
4 5
5 from Daily_Info import * 6 from Daily_Info import *
6 from Stock_Info import * 7 from Stock_Info import *
...@@ -23,27 +24,40 @@ class Collector_Api(): ...@@ -23,27 +24,40 @@ class Collector_Api():
23 "today_buy_list,stock_info,min_info,daily_info from setting_data" 24 "today_buy_list,stock_info,min_info,daily_info from setting_data"
24 result=self.engine_bot.execute(query).fetchall() 25 result=self.engine_bot.execute(query).fetchall()
25 26
27 + today=self.open_api.today
28 +
26 # 오늘 날짜에 종목리스트가 업데이트 되지 않았다면 업데이트를 실행 29 # 오늘 날짜에 종목리스트가 업데이트 되지 않았다면 업데이트를 실행
27 - if result[0][0]!=self.open_api.today: 30 + if result[0][0]!=today:
28 self.open_api.get_balance() 31 self.open_api.get_balance()
29 self.get_code_list() 32 self.get_code_list()
30 33
31 # 계좌정보 업데이트 34 # 계좌정보 업데이트
32 - if result[0][1]!=self.open_api.today or result[0][2]!=self.open_api.today: 35 + if result[0][1]!=today or result[0][2]!=today:
33 self.check_balance() 36 self.check_balance()
34 self.open_api.set_per_invest() 37 self.open_api.set_per_invest()
35 38
36 # 현재 보유종목 테이블 업데이트 39 # 현재 보유종목 테이블 업데이트
37 - if result[0][2]!=self.open_api.today: 40 + if result[0][2]!=today:
38 self.open_api.get_posses_item() 41 self.open_api.get_posses_item()
39 self.open_api.setting_data_posses_stock() 42 self.open_api.setting_data_posses_stock()
40 43
41 - if result[0][3]!=self.open_api.today: 44 + # 수익률 테이블 업데이트
45 + if result[0][3]!=today:
42 self.update_today_profit_list() 46 self.update_today_profit_list()
43 47
44 - if result[0][7]!=self.open_api.today: 48 + # stock_info 테이블 업데이트
49 + if result[0][7]!=today:
50 + self.check_stock_info()
51 +
52 + if result[0][9]!=today:
45 self.check_daily_info() 53 self.check_daily_info()
46 54
55 + if result[0][4]!=today:
56 + self.open_api.check_contract()
57 + self.open_api.final_check_contract()
58 +
59 + if result[0][6]!=today:
60 + self.realtime_daily_info_check()
47 61
48 62
49 # 코스피, 코스닥 리스트 확인 및 데이터베이스 업데이트 63 # 코스피, 코스닥 리스트 확인 및 데이터베이스 업데이트
...@@ -256,7 +270,7 @@ class Collector_Api(): ...@@ -256,7 +270,7 @@ class Collector_Api():
256 query = "UPDATE setting_data SET today_profit='%s' limit 1" 270 query = "UPDATE setting_data SET today_profit='%s' limit 1"
257 self.engine_bot.execute(query % (self.open_api.today)) 271 self.engine_bot.execute(query % (self.open_api.today))
258 272
259 - def check_daily_info(self): 273 + def check_stock_info(self):
260 self.update_daily_info() 274 self.update_daily_info()
261 query="update setting_data set daily_info='%s'" 275 query="update setting_data set daily_info='%s'"
262 self.engine_bot.execute(query%(self.open_api.today)) 276 self.engine_bot.execute(query%(self.open_api.today))
...@@ -273,12 +287,102 @@ class Collector_Api(): ...@@ -273,12 +287,102 @@ class Collector_Api():
273 code=code_list[i][0] 287 code=code_list[i][0]
274 code_name=code_list[i][1] 288 code_name=code_list[i][1]
275 289
276 - check_item_sort=self.set_minute_info_table(code,code_name) 290 + check_item_sort=self.set_stock_info_table(code,code_name)
277 291
278 self.open_api.engine_daily.execute(query%(check_item_sort,code)) 292 self.open_api.engine_daily.execute(query%(check_item_sort,code))
279 293
280 - def set_minute_info_table(self,code,code_name): 294 + # stock_info 데이터베이스에 테이블 생성 및 추가
281 - df=self.open_api. 295 + def set_stock_info_table(self,code,code_name):
296 + df=self.open_api.get_date_data(code,code_name,self.open_api.today)
297 +
298 + df=DataFrame(df,
299 + columns=['date', 'check_item', 'code', 'code_name', 'd1_diff_rate',
300 + 'close', 'open', 'high','low','volume',
301 + 'clo5', 'clo10', 'clo20', 'clo60','clo120',
302 + 'pre_clo5', 'pre_clo10', 'pre_clo20', 'pre_clo60','pre_clo120',
303 + 'vol5', 'vol10', 'vol20', 'vol60','vol120'])
304 +
305 + df=df.sort_values(by=['date'],ascending=True)
306 +
307 + df['code']=code
308 + df['code_name']=code_name
309 + df['d1_diff_rate']=float((df['close']-df['close'].shift(1))/df['close'].shift(1)*100)
310 +
311 + df['clo5']=df['close'].rolling(window=5).mean()
312 + df['clo10']=df['close'].rolling(window=10).mean()
313 + df['clo20']=df['close'].rolling(window=20).mean()
314 + df['clo60']=df['close'].rolling(window=60).mean()
315 + df['clo120']=df['close'].rolling(window=120).mean()
316 +
317 + df['pre_col5']=df['clo5'].shift(1)
318 + df['pre_col10']=df['clo10'].shift(1)
319 + df['pre_col20']=df['clo20'].shift(1)
320 + df['pre_col60']=df['clo60'].shift(1)
321 + df['pre_col120']=df['clo120'].shift(1)
322 +
323 + df['vlo5']=df['volume'].rolling(window=5).mean()
324 + df['vlo10']=df['volume'].rolling(window=10).mean()
325 + df['vlo20']=df['volume'].rolling(window=20).mean()
326 + df['vlo60']=df['volume'].rolling(window=60).mean()
327 + df['vlo120']=df['volume'].rolling(window=120).mean()
328 +
329 + # 이미 테이블이 존재한다면 저장된 가장 최신의 날짜 이후의 데이터만 저장
330 + if self.engine_bot.dialect.has_table(self.open_api.engine_stock,code_name):
331 + df=df[df.date>self.open_api.get_latest_date_from_stock_info(code_name)]
332 +
333 + df[['date', 'check_item', 'code', 'code_name', 'd1_diff_rate',
334 + 'close', 'open', 'high','low','volume',
335 + 'clo5', 'clo10', 'clo20', 'clo60','clo120',
336 + 'pre_clo5', 'pre_clo10', 'pre_clo20', 'pre_clo60','yes_clo120',
337 + 'vol5', 'vol10', 'vol20', 'vol60','vol120']]=\
338 + df[['date', 'check_item', 'code', 'code_name', 'd1_diff_rate',
339 + 'close', 'open', 'high','low','volume',
340 + 'clo5', 'clo10', 'clo20', 'clo60','clo120',
341 + 'pre_clo5', 'pre_clo10', 'pre_clo20', 'pre_clo60','yes_clo120',
342 + 'vol5', 'vol10', 'vol20', 'vol60','vol120']].fillna(0).astype(int)
343 +
344 + df.to_sql(name=code_name,con=self.open_api.engine_stock,if_exists='append')
345 +
346 + check_item_sort = 1
347 + return check_item_sort
348 +
349 + def check_daily_info(self):
350 + self.daily_info.create_daily_table()
351 + query="update setting_data set daily_info='%s'"
352 + self.engine_bot.execute(query%(self.open_api.today))
353 +
354 + def realtime_daily_info_check(self):
355 + if self.open_api.simul_api.is_date_exist(self.open_api.today):
356 + self.open_api.simul_api.get_date_for_simul()
357 + self.open_api.simul_api.choose_to_buy_list(self.open_api.today,self.open_api.today,len(self.open_api.simul_api.date_rows))
358 +
359 +
360 +
361 +
362 +
363 + if self.open_api.sf.is_date_exist(self.open_api.today):
364 + logger.debug("daily_buy_list DB에 {} 테이블이 있습니다. jackbot DB에 realtime_daily_buy_list 테이블을 생성합니다".format(self.open_api.today))
365 +
366 + self.open_api.sf.get_date_for_simul()
367 + # 첫 번째 파라미터는 여기서는 의미가 없다.
368 + # 두 번째 파라미터에 오늘 일자를 넣는 이유는 매수를 하는 시점인 내일 기준으로 date_rows_yesterday가 오늘 이기 때문
369 + self.open_api.sf.db_to_realtime_daily_buy_list(self.open_api.today, self.open_api.today, len(self.open_api.sf.date_rows))
370 +
371 +
372 + # all_item_db에서 open, clo5~120, volume 등을 오늘 일자 데이터로 업데이트 한다.
373 + self.open_api.sf.update_all_db_by_date(self.open_api.today)
374 + self.open_api.rate_check()
375 + # realtime_daily_buy_list(매수 리스트) 테이블 세팅을 완료 했으면 아래 쿼리를 통해 setting_data의 today_buy_list에 오늘 날짜를 찍는다.
376 + sql = "UPDATE setting_data SET today_buy_list='%s' limit 1"
377 + self.engine_JB.execute(sql % (self.open_api.today))
378 + else:
379 + logger.debug(
380 + """daily_buy_list DB에 {} 테이블이 없습니다. jackbot DB에 realtime_daily_buy_list 테이블을 생성 할 수 없습니다.
381 + realtime_daily_buy_list는 daily_buy_list DB 안에 오늘 날짜 테이블이 만들어져야 생성이 됩니다.
382 + realtime_daily_buy_list 테이블을 생성할 수 없는 이유는 아래와 같습니다.
383 + 1. 장이 열리지 않은 날 혹은 15시 30분 ~ 23시 59분 사이에 콜렉터를 돌리지 않은 경우
384 + 2. 콜렉터를 오늘 날짜 까지 돌리지 않아 daily_buy_list의 오늘 날짜 테이블이 없는 경우
385 + """.format(self.open_api.today))
282 386
283 387
284 app=QApplication(sys.argv) 388 app=QApplication(sys.argv)
......
...@@ -329,13 +329,13 @@ class Open_Api(QAxWidget): ...@@ -329,13 +329,13 @@ class Open_Api(QAxWidget):
329 def create_setting_data(self): 329 def create_setting_data(self):
330 df_data={"limit_money":[],"per_invest":[],"max_per_invest":[],"min_per_invest":[],"set_per_invest":[], 330 df_data={"limit_money":[],"per_invest":[],"max_per_invest":[],"min_per_invest":[],"set_per_invest":[],
331 "code_update":[],"today_finish":[],"balance_to_db":[],"posses_stocks":[],"today_profit":[], 331 "code_update":[],"today_finish":[],"balance_to_db":[],"posses_stocks":[],"today_profit":[],
332 - "contract_check":[],"db_to_daily_info":[],"today_buy_list":[],"stock_info":[],"min_info":[], 332 + "contract_check":[],"final_contract_check":[],"db_to_daily_info":[],"today_buy_list":[],
333 - "daily_info":[]} 333 + "stock_info":[],"min_info":[],"daily_info":[]}
334 df_setting_data=DataFrame(df_data, 334 df_setting_data=DataFrame(df_data,
335 columns=['limit_money','per_invest','max_per_invest','min_per_invest','set_per_invest', 335 columns=['limit_money','per_invest','max_per_invest','min_per_invest','set_per_invest',
336 'code_update','today_finish','balance_to_db','posses_stocks','today_profit', 336 'code_update','today_finish','balance_to_db','posses_stocks','today_profit',
337 - 'contract_check','db_to_daily_info','today_buy_list','stock_info','min_info', 337 + 'contract_check','final_contract_check','db_to_daily_info','today_buy_list',
338 - 'daily_info']) 338 + 'stock_info','min_info','daily_info'])
339 339
340 df_setting_data.loc[0,'limit_money']=int(0) 340 df_setting_data.loc[0,'limit_money']=int(0)
341 df_setting_data.loc[0,'per_invest']=int(0) 341 df_setting_data.loc[0,'per_invest']=int(0)
...@@ -350,11 +350,12 @@ class Open_Api(QAxWidget): ...@@ -350,11 +350,12 @@ class Open_Api(QAxWidget):
350 df_setting_data.loc[0,'today_profit']=float(0) 350 df_setting_data.loc[0,'today_profit']=float(0)
351 351
352 df_setting_data.loc[0,'contract_check']=str(0) 352 df_setting_data.loc[0,'contract_check']=str(0)
353 + df_setting_data.loc[0,'final_contract_check']=str(0)
353 df_setting_data.loc[0,'db_to_daily_info']=str(0) 354 df_setting_data.loc[0,'db_to_daily_info']=str(0)
354 df_setting_data.loc[0,'today_buy_list']=str(0) 355 df_setting_data.loc[0,'today_buy_list']=str(0)
355 df_setting_data.loc[0,'stock_info']=str(0) 356 df_setting_data.loc[0,'stock_info']=str(0)
356 - df_setting_data.loc[0,'min_info']=str(0)
357 357
358 + df_setting_data.loc[0,'min_info']=str(0)
358 df_setting_data.loc[0,'daily_info']=str(0) 359 df_setting_data.loc[0,'daily_info']=str(0)
359 360
360 df_setting_data.to_sql("setting_data",self.engine_bot,if_exists="replace") 361 df_setting_data.to_sql("setting_data",self.engine_bot,if_exists="replace")
...@@ -588,9 +589,113 @@ class Open_Api(QAxWidget): ...@@ -588,9 +589,113 @@ class Open_Api(QAxWidget):
588 589
589 self.opt10073_output['multi'].append([date,code,code_name,amount,today_profit,earning_rate]) 590 self.opt10073_output['multi'].append([date,code,code_name,amount,today_profit,earning_rate])
590 591
592 + # 특정 종목의 일자별 거래 데이터 조회 함수
593 + def get_date_data(self,code,code_name,date):
594 + self.ohlcv=defaultdict(list)
595 + self.set_input_value("종목코드",code)
596 + self.set_input_value("기준일자",date)
597 + self.set_input_value("수정주가구분",1)
598 +
599 + # 한번에 600일치의 데이터를 가져온다
600 + self.comm_rq_data("opt10081_req","opt10081",0,"0101")
601 +
602 + # stock_info 테이블이 존재하지 않는다면 600일치 이상의 전체 데이터를 가져와야 하므로 다음 페이지가 존재하지 않을 때까지 조회
603 + if not self.is_stock_table_exist(code_name):
604 + while self.remained_data==True:
605 + self.set_input_value("종목코드",code)
606 + self.set_input_value("기준일자",date)
607 + self.set_input_value("수정주가구분",1)
608 + self.comm_rq_data("opt10081_req","opt10081",2,"0101")
609 +
610 + if len(self.ohlcv)==0:
611 + return []
612 + if self.ohlcv['date']=='':
613 + return []
614 +
615 + df=DataFrame(self.ohlcv,columns=['date','open','high','low','close','volume'])
616 +
617 + return df
618 +
619 + # stock_info 데이터베이스가 존재하는지 확인하는 함수
620 + def is_stock_table_exist(self,code_name):
621 + query = "select 1 from information_schema.tables where table_schema ='stock_info' and table_name = '{}'"
622 + result=self.engine_stock.execute(query.format(code_name)).fetchall()
623 + if result:
624 + return True
625 + else:
626 + return False
627 +
628 + # 주식 일봉차트 조회 요청
629 + # 주식의 날짜별 데이터를 저장하는 함수
630 + def collector_opt10081(self,sRQName,sTrCode):
631 + ohlcv_cnt=self._get_repeat_cnt(sTrCode,sRQName)
591 632
633 + for i in range(ohlcv_cnt):
634 + date=self._get_comm_data(sTrCode,sRQName,i,"일자")
635 + open=self._get_comm_data(sTrCode,sRQName,i,"시가")
636 + high=self._get_comm_data(sTrCode,sRQName,i,"고가")
637 + low=self._get_comm_data(sTrCode,sRQName,i,"저가")
638 + close=self._get_comm_data(sTrCode,sRQName,i,"현재가")
639 + volume=self._get_comm_data(sTrCode,sRQName,i,"거래량")
640 +
641 + self.ohlcv['date'].append(date)
642 + self.ohlcv['open'].append(int(open))
643 + self.ohlcv['high'].append(int(high))
644 + self.ohlcv['low'].append(int(low))
645 + self.ohlcv['close'].append(int(close))
646 + self.ohlcv['volume'].append(int(volume))
647 +
648 + # stock_info의 특정 종목 테이블에서 마지막으로 저장된 날짜를 가져오는 함수
649 + # 저장된 데이터가 없다면 '0'문자를 반환
650 + def get_latest_date_from_stock_info(self,code_name):
651 + query="select date from {} order by date desc limit 1"
652 + result=self.engine_stock.execute(query.format(code_name)).fetchall()
653 + if len(result):
654 + return result[0][0]
655 + else:
656 + return str(0)
657 +
658 + def check_contract(self):
659 + query="select code from transaction_history where contract_check='1' and (sell date='0' or sell_date='')"
660 + rows=self.engine_bot.execute(query).fetchall()
592 661
662 + for r in rows:
663 + self.set_input_value("종목코드",r.code)
664 + self.set_input_value("조회구분",1)
665 + self.set_input_value("계좌번호",self.account_no)
666 + self.comm_rq_data("opt10076_req","opt10076",0,"0350")
593 667
668 + query="update transaction_history set contract_check='0' where code='{}' and sell_date='0'"
669 + # 거래가 완료된 항목은 주문번호가 존재하지 않음
670 + # 거래가 완료된 항목에 대해서 contract_check항목을 '0'으로 업데이트
671 + if not self.not_contract['주문번호']:
672 + self.engine_bot.execute(query.format(r.code))
673 + # 미체결 수량이 없을 경우 contract_check항목을 '0'으로 업데이트
674 + elif self.not_contract['미체결수량']==0:
675 + logger.debug("미체결 항목이 없습니다")
676 + self.engine_bot.execute(query.format(r.code))
677 + else:
678 + logger.debug("미체결 종목이 존재합니다")
679 +
680 + # posses_item테이블에는 존재하지만 transaction_history테이블에 존재하지 않는 항목에 대해 업데이트
681 + def final_check_contract(self):
682 + query="select code from transaction_history" \
683 + "where" \
684 + "sell_date='%s' or sell_date='%s' and code not in (select code from posses_item) and contract_check!='%s'"
685 + result=self.engine_bot.execute(query%(0,"",1)).fetchall()
686 + num=len(result)
687 + for i in range(num):
688 + self.sell_check(result[i][0])
689 + query="update setting_data set final_contract_check='%s'"
690 + self.engine_bot.execute(query%(self.today))
691 +
692 + # 가지고 있던 종목을 판매하여 posses_item테이블에 내역이 존재하지 않지만 transaction_history에 매도처리가 되지 않은 항목 업데이트
693 + def sell_check(self,code):
694 + query="update transaction_history" \
695 + "set" \
696 + "contract_check='%s', sell_date='%s'" \
697 + "where code='%s' and sell_date='%s'"
698 + self.engine_bot.execute(query%(0,self.today_time,code,0))
594 699
595 700
596 if __name__=="__main__": 701 if __name__=="__main__":
......