Showing
4 changed files
with
1383 additions
and
82 deletions
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 | ... | ... |
File moved
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__": | ... | ... |
-
Please register or login to post a comment