Showing
2 changed files
with
543 additions
and
126 deletions
... | @@ -22,3 +22,5 @@ real_num=1 | ... | @@ -22,3 +22,5 @@ real_num=1 |
22 | real_bot_name="AutoBot"+str(real_num) | 22 | real_bot_name="AutoBot"+str(real_num) |
23 | real_stockInfo_name="stock_info" | 23 | real_stockInfo_name="stock_info" |
24 | real_dailyInfo_name="daily_info" | 24 | real_dailyInfo_name="daily_info" |
25 | + | ||
26 | +max_api_call=98 | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
1 | from PyQt5.QtCore import * | 1 | from PyQt5.QtCore import * |
2 | from PyQt5.QAxContainer import * | 2 | from PyQt5.QAxContainer import * |
3 | from PyQt5.QtWidgets import * | 3 | from PyQt5.QtWidgets import * |
4 | -import pymysql | ||
5 | import datetime | 4 | import datetime |
6 | -from sqlalchemy import * | ||
7 | from collections import defaultdict | 5 | from collections import defaultdict |
8 | from pandas import DataFrame | 6 | from pandas import DataFrame |
9 | import re | 7 | import re |
8 | +import time | ||
9 | + | ||
10 | +import warnings | ||
11 | +warnings.simplefilter(action='ignore',category=UserWarning) | ||
12 | + | ||
13 | +from sqlalchemy import create_engine,event,Text,Float | ||
14 | +from sqlalchemy.pool import Pool | ||
15 | +import pymysql | ||
16 | +pymysql.install_as_MySQLdb() | ||
10 | 17 | ||
11 | from Logger import * | 18 | from Logger import * |
12 | import cf | 19 | import cf |
13 | from Simulator_Api import * | 20 | from Simulator_Api import * |
14 | 21 | ||
15 | -pymysql.install_as_MySQLdb() | 22 | +# open_api에 tr요청을 보낼 때, 연속해서 보내면 오류가 발생하기 때문에 중간에 interval을 줘야 한다. |
23 | +TR_REQ_TIME_INTERVAL=0.5 # interval값을 저장하는 변수 | ||
24 | +code_pattern=re.compile(r'\d{6}') # 코드 패턴을 저장. 코드는 연속된 6자리의 숫자 | ||
25 | + | ||
26 | +# query문의 %를 %%로 변환하는 함수 | ||
27 | +def escape_percentage(conn,clauseelement,multiparmas,parmas): | ||
28 | + if isinstance(clauseelement,str) and '%' in clauseelement and multiparmas is not None: | ||
29 | + while True: | ||
30 | + replaced=re.sub(r'([^%])%([^%])', r'\1%%\2', clauseelement) | ||
31 | + if replaced==clauseelement: | ||
32 | + break | ||
33 | + clauseelement=replaced | ||
34 | + return clauseelement,multiparmas,parmas | ||
35 | + | ||
36 | +# MySQL의 default sql_mode 설정 | ||
37 | +def setup_sql_mod(dbapi_connection): | ||
38 | + cursor=dbapi_connection.cursor() | ||
39 | + cursor.execute("SET sql_mode=''") | ||
40 | + | ||
41 | +event.listen(Pool,'connect',setup_sql_mod) | ||
42 | +event.listen(Pool,'first_connect',setup_sql_mod) | ||
16 | 43 | ||
17 | class Open_Api(QAxWidget): | 44 | class Open_Api(QAxWidget): |
18 | def __init__(self): | 45 | def __init__(self): |
... | @@ -191,13 +218,10 @@ class Open_Api(QAxWidget): | ... | @@ -191,13 +218,10 @@ class Open_Api(QAxWidget): |
191 | def _receive_chejan_data(self,sGubun,nItemCnt,sFIdList): | 218 | def _receive_chejan_data(self,sGubun,nItemCnt,sFIdList): |
192 | # 체결구분. 접수와 체결 | 219 | # 체결구분. 접수와 체결 |
193 | if sGubun=="0": | 220 | if sGubun=="0": |
194 | - code=self.get_chejan_data(9001) # 현재 체결 진행 중인 코드 | 221 | + code = code_pattern.search(self.get_chejan_data(9001)).group(0) # 주식 코드가 숫자만오지 않아서 정규식으로 필터링 |
195 | - code=re.compile(r'\d{6}') # 종목코드는 연속된 숫자 6자리 | 222 | + order_num = self.get_chejan_data(9203) # 주문번호 |
196 | - order_num=self.get_chejan_data(9203) # 주문번호 | ||
197 | - | ||
198 | - # 주문번호가 존재하지 않는다면 주문실패 | ||
199 | if not order_num: | 223 | if not order_num: |
200 | - logger.debug(code,"주문 실패") | 224 | + logger.debug(f'{code} 주문 실패') |
201 | return | 225 | return |
202 | 226 | ||
203 | chegyul_fail_amount=self.get_chejan_data(902) # 미체결 수량 | 227 | chegyul_fail_amount=self.get_chejan_data(902) # 미체결 수량 |
... | @@ -208,17 +232,17 @@ class Open_Api(QAxWidget): | ... | @@ -208,17 +232,17 @@ class Open_Api(QAxWidget): |
208 | if code: | 232 | if code: |
209 | if chegyul_fail_amount!="": | 233 | if chegyul_fail_amount!="": |
210 | # 해당 종목을 보유하고 있지 않은 경우 | 234 | # 해당 종목을 보유하고 있지 않은 경우 |
211 | - if not self.is_exist_possess_item_in_transaction(code): | 235 | + if not self.is_all_stock_check(code): |
212 | # 해당 종목의 체결 실패 내역이 없다면 | 236 | # 해당 종목의 체결 실패 내역이 없다면 |
213 | # transaction 테이블에 업데이트. 정상 체결 시 chegyul_check=0 | 237 | # transaction 테이블에 업데이트. 정상 체결 시 chegyul_check=0 |
214 | if chegyul_fail_amount=="0": | 238 | if chegyul_fail_amount=="0": |
215 | logger.debug(code, "체결 완료") | 239 | logger.debug(code, "체결 완료") |
216 | - self.db_to_transaction(order_num,code,0,purchase_price,0) | 240 | + self.db_to_all_stocks(order_num,code,0,purchase_price,0) |
217 | # 체결 실패 내역이 존재한다면 | 241 | # 체결 실패 내역이 존재한다면 |
218 | # transaction 테이블에 업데이트. 미체결 시 chegyul_check=1 | 242 | # transaction 테이블에 업데이트. 미체결 시 chegyul_check=1 |
219 | else: | 243 | else: |
220 | logger.debug(code,"미체결") | 244 | logger.debug(code,"미체결") |
221 | - self.db_to_transaction(order_num,code,1,purchase_price,0) | 245 | + self.db_to_all_stocks(order_num,code,1,purchase_price,0) |
222 | 246 | ||
223 | # 매수하는 경우 | 247 | # 매수하는 경우 |
224 | elif order_gubun=="+매수": | 248 | elif order_gubun=="+매수": |
... | @@ -227,7 +251,7 @@ class Open_Api(QAxWidget): | ... | @@ -227,7 +251,7 @@ class Open_Api(QAxWidget): |
227 | pass | 251 | pass |
228 | elif chegyul_fail_amount=="0" and self.check_stock_chegyul(code): | 252 | elif chegyul_fail_amount=="0" and self.check_stock_chegyul(code): |
229 | logger.debug("매수 완료") | 253 | logger.debug("매수 완료") |
230 | - self.check_end_invest(code) | 254 | + self.end_invest_count_check(code) |
231 | else: | 255 | else: |
232 | pass | 256 | pass |
233 | 257 | ||
... | @@ -235,7 +259,7 @@ class Open_Api(QAxWidget): | ... | @@ -235,7 +259,7 @@ class Open_Api(QAxWidget): |
235 | elif order_gubun=="-매도": | 259 | elif order_gubun=="-매도": |
236 | if chegyul_fail_amount=="0": | 260 | if chegyul_fail_amount=="0": |
237 | logger.debug("전량 매도") | 261 | logger.debug("전량 매도") |
238 | - self.check_end_sell(code) | 262 | + self.sell_final_check(code) |
239 | else: | 263 | else: |
240 | logger.debug("부분 매도") | 264 | logger.debug("부분 매도") |
241 | self.check_sell_chegyul_fail(code) | 265 | self.check_sell_chegyul_fail(code) |
... | @@ -255,33 +279,16 @@ class Open_Api(QAxWidget): | ... | @@ -255,33 +279,16 @@ class Open_Api(QAxWidget): |
255 | else: | 279 | else: |
256 | logger.debug("Invlid _receive_chejan_data") | 280 | logger.debug("Invlid _receive_chejan_data") |
257 | 281 | ||
258 | - # 특정 종목을 보유하고 있는지 확인하는 함수 | ||
259 | - def is_exist_possess_item_in_transaction(self,code): | ||
260 | - query = "select code from transaction " \ | ||
261 | - "where code='%s' and (sell_date ='%s' or sell_date='%s') ORDER BY buy_date desc LIMIT 1" | ||
262 | - result = self.engine_bot.execute(query % (code, 0, "")).fetchall() | ||
263 | - if len(result) != 0: | ||
264 | - return True | ||
265 | - else: | ||
266 | - return False | ||
267 | - | ||
268 | # 해당 종목이 체결되었는지 확인하는 함수 | 282 | # 해당 종목이 체결되었는지 확인하는 함수 |
269 | def check_stock_chegyul(self,code): | 283 | def check_stock_chegyul(self,code): |
270 | - query = "SELECT chegyul_check FROM transaction " \ | 284 | + query = f"SELECT chegyul_check FROM all_stocks " \ |
271 | - "where code='%s' and sell_date = '%s' ORDER BY buy_date desc LIMIT 1" | 285 | + f"where code='{code}' and sell_date = '0' ORDER BY buy_date desc LIMIT 1" |
272 | - result = self.engine_bot.execute(query % (code, 0)).fetchall() | 286 | + result = self.engine_bot.execute(query).fetchall() |
273 | if result[0][0] == 1: | 287 | if result[0][0] == 1: |
274 | return True | 288 | return True |
275 | else: | 289 | else: |
276 | return False | 290 | return False |
277 | 291 | ||
278 | - # 체결 완료 시 transaction 테이블의 chegyul_check항목을 업데이트 | ||
279 | - def check_end_invest(self,code): | ||
280 | - query = "UPDATE transaction " \ | ||
281 | - "SET " \ | ||
282 | - "chegyul_check='%s' WHERE code='%s' and sell_date = '%s' ORDER BY buy_date desc LIMIT 1" | ||
283 | - self.engine_bot.execute(query % (0, code, 0)) | ||
284 | - | ||
285 | # 매도 완료 후 DB 업데이트트 | 292 | # 매도 완료 후 DB 업데이트트 |
286 | def check_sell_end(self,code): | 293 | def check_sell_end(self,code): |
287 | query="select valuation_profit,rate,item_total_urchase,present_price" \ | 294 | query="select valuation_profit,rate,item_total_urchase,present_price" \ |
... | @@ -307,9 +314,9 @@ class Open_Api(QAxWidget): | ... | @@ -307,9 +314,9 @@ class Open_Api(QAxWidget): |
307 | 314 | ||
308 | # 매도 체결 실패시 DB 업데이트 | 315 | # 매도 체결 실패시 DB 업데이트 |
309 | def check_sell_chegyul_fail(self,code): | 316 | def check_sell_chegyul_fail(self,code): |
310 | - query = "UPDATE transaction SET chegyul_check='%s' " \ | 317 | + query = f"UPDATE all_stocks SET chegyul_check='1' WHERE code='{code}' and sell_date = '0' " \ |
311 | - "WHERE code='%s' and sell_date = '%s' ORDER BY buy_date desc LIMIT 1" | 318 | + f"ORDER BY buy_date desc LIMIT 1" |
312 | - self.engine_bot.execute(query % (1, code, 0)) | 319 | + self.engine_JB.execute(query) |
313 | 320 | ||
314 | # OnReceiveChejan()이벤트가 호출될때 체결정보나 잔고정보를 얻어오는 함수 | 321 | # OnReceiveChejan()이벤트가 호출될때 체결정보나 잔고정보를 얻어오는 함수 |
315 | # param : nFid - 실시간 타입에 포함된 FID | 322 | # param : nFid - 실시간 타입에 포함된 FID |
... | @@ -340,8 +347,7 @@ class Open_Api(QAxWidget): | ... | @@ -340,8 +347,7 @@ class Open_Api(QAxWidget): |
340 | try: | 347 | try: |
341 | self.dynamicCall("SetInputValue(QString, QString)", sId, sValue) | 348 | self.dynamicCall("SetInputValue(QString, QString)", sId, sValue) |
342 | except Exception as e: | 349 | except Exception as e: |
343 | - print(e) | 350 | + logger.critical(e) |
344 | - sys.exit() | ||
345 | 351 | ||
346 | # 조회요청함수 | 352 | # 조회요청함수 |
347 | # param : sRQName - 사용자 구분명 | 353 | # param : sRQName - 사용자 구분명 |
... | @@ -349,7 +355,16 @@ class Open_Api(QAxWidget): | ... | @@ -349,7 +355,16 @@ class Open_Api(QAxWidget): |
349 | # nPrevNext - 연속조회여부 | 355 | # nPrevNext - 연속조회여부 |
350 | # sScreenNo - 화면번호 | 356 | # sScreenNo - 화면번호 |
351 | def comm_rq_data(self,sRQName,sTrData,nPrevNext,sScrNo): | 357 | def comm_rq_data(self,sRQName,sTrData,nPrevNext,sScrNo): |
352 | - self.dynamicCall("CommRqData(QString, QString, int, QString", sRQName, sTrData, nPrevNext, sScrNo) | 358 | + self.exit_check() |
359 | + ret=self.dynamicCall("CommRqData(QString, QString, int, QString", sRQName, sTrData, nPrevNext, sScrNo) | ||
360 | + | ||
361 | + if ret==-200: | ||
362 | + logger.critical("요청 제한 횟수 초과") | ||
363 | + | ||
364 | + self.call_time=datetime.datetime.now() | ||
365 | + | ||
366 | + if ret==0: | ||
367 | + self.tr_loop_count+=1 | ||
353 | self.tr_event_loop.exec_() | 368 | self.tr_event_loop.exec_() |
354 | 369 | ||
355 | # OnReceiveTRData()이벤트가 호출될때 조회데이터를 얻어오는 함수 | 370 | # OnReceiveTRData()이벤트가 호출될때 조회데이터를 얻어오는 함수 |
... | @@ -369,8 +384,7 @@ class Open_Api(QAxWidget): | ... | @@ -369,8 +384,7 @@ class Open_Api(QAxWidget): |
369 | ret=self.dynamicCall("GetRepeatCnt(QString, QString)",sTrCode,sRecordName) | 384 | ret=self.dynamicCall("GetRepeatCnt(QString, QString)",sTrCode,sRecordName) |
370 | return ret | 385 | return ret |
371 | except Exception as e: | 386 | except Exception as e: |
372 | - print(e) | 387 | + logger.critical(e) |
373 | - sys.exit() | ||
374 | 388 | ||
375 | # 변수 설정 | 389 | # 변수 설정 |
376 | # 실전투자인지 모의투자인지 여부를 확인하고 그에 해당하는 데이터베이스를 생성하는 함수 | 390 | # 실전투자인지 모의투자인지 여부를 확인하고 그에 해당하는 데이터베이스를 생성하는 함수 |
... | @@ -396,7 +410,7 @@ class Open_Api(QAxWidget): | ... | @@ -396,7 +410,7 @@ class Open_Api(QAxWidget): |
396 | logger.debug("Invalid Account Number. Check the Config.py") | 410 | logger.debug("Invalid Account Number. Check the Config.py") |
397 | exit(1) | 411 | exit(1) |
398 | 412 | ||
399 | - self.is_balance_null=True | 413 | + self.jango_is_null=True |
400 | self.py_gubun=False | 414 | self.py_gubun=False |
401 | 415 | ||
402 | # 데이터베이스 생성 및 엔진 설정 | 416 | # 데이터베이스 생성 및 엔진 설정 |
... | @@ -411,16 +425,14 @@ class Open_Api(QAxWidget): | ... | @@ -411,16 +425,14 @@ class Open_Api(QAxWidget): |
411 | charset='utf8mb4', | 425 | charset='utf8mb4', |
412 | cursorclass=pymysql.cursors.DictCursor | 426 | cursorclass=pymysql.cursors.DictCursor |
413 | ) | 427 | ) |
414 | - | 428 | + with conn.cursor() as cursor: |
415 | - cursor=conn.cursor() | ||
416 | if not self.is_database_exist(cursor): | 429 | if not self.is_database_exist(cursor): |
417 | self.create_database(cursor) | 430 | self.create_database(cursor) |
418 | - self.engine_bot=create_engine("mysql+pymysql://"+self.cf.db_id+":"+self.cf.db_pw+"@"+ | 431 | + self.engine_bot = create_engine("mysql+pymysql://" + self.cf.db_id + ":" + self.cf.db_pw + "@" + |
419 | - self.cf.db_ip + ":" + self.cf.db_port+"/"+db_name,encoding='utf-8') | 432 | + self.cf.db_ip + ":" + self.cf.db_port + "/" + db_name, encoding='utf-8') |
420 | self.create_basic_database(cursor) | 433 | self.create_basic_database(cursor) |
421 | 434 | ||
422 | conn.commit() | 435 | conn.commit() |
423 | - cursor.close() | ||
424 | conn.close() | 436 | conn.close() |
425 | 437 | ||
426 | self.engine_craw = create_engine("mysql+pymysql://" + cf.db_id + ":" + cf.db_pw + "@" + cf.db_ip + ":" + | 438 | self.engine_craw = create_engine("mysql+pymysql://" + cf.db_id + ":" + cf.db_pw + "@" + cf.db_ip + ":" + |
... | @@ -430,6 +442,10 @@ class Open_Api(QAxWidget): | ... | @@ -430,6 +442,10 @@ class Open_Api(QAxWidget): |
430 | self.engine_daily_buy_list = create_engine("mysql+pymysql://" + cf.db_id + ":" + cf.db_pw + "@" + cf.db_ip + ":" | 442 | self.engine_daily_buy_list = create_engine("mysql+pymysql://" + cf.db_id + ":" + cf.db_pw + "@" + cf.db_ip + ":" |
431 | + cf.db_port + "/daily_buy_list",encoding='utf-8') | 443 | + cf.db_port + "/daily_buy_list",encoding='utf-8') |
432 | 444 | ||
445 | + event.listen(self.engine_craw,'before_execute',escape_percentage,retval=True) | ||
446 | + event.listen(self.engine_daily_craw,'before_execute',escape_percentage,retval=True) | ||
447 | + event.listen(self.engine_daily_buy_list,'before_execute',escape_percentage,retval=True) | ||
448 | + | ||
433 | # bot database가 존재하는지 확인하는 함수 | 449 | # bot database가 존재하는지 확인하는 함수 |
434 | def is_database_exist(self,cursor): | 450 | def is_database_exist(self,cursor): |
435 | query=f"select 1 from information_schema.schemata where schema_name='{self.db_name}'" | 451 | query=f"select 1 from information_schema.schemata where schema_name='{self.db_name}'" |
... | @@ -505,23 +521,23 @@ class Open_Api(QAxWidget): | ... | @@ -505,23 +521,23 @@ class Open_Api(QAxWidget): |
505 | 521 | ||
506 | # simulator_fun 에서 설정한 변수를 가져오는 함수 | 522 | # simulator_fun 에서 설정한 변수를 가져오는 함수 |
507 | def set_simul_variable(self): | 523 | def set_simul_variable(self): |
508 | - logger.debug("-* set simul variable 함수 *-") | 524 | + logger.debug("-* set simul variable function *-") |
509 | # daily_buy_list에 저장된 가장 최신 날짜 | 525 | # daily_buy_list에 저장된 가장 최신 날짜 |
510 | self.date_rows_yesterday=self.sf.get_recent_daily_buy_list_date() | 526 | self.date_rows_yesterday=self.sf.get_recent_daily_buy_list_date() |
511 | 527 | ||
512 | # AutoBot 데이터베이스에 all_stock 테이블이 존재하지 않을 경우 테이블 생성 및 초기화 | 528 | # AutoBot 데이터베이스에 all_stock 테이블이 존재하지 않을 경우 테이블 생성 및 초기화 |
513 | - # all_stock : 모든 주식 거래 내역을 저장하는 테이블 | 529 | + # all_stocks : 모든 주식 거래 내역을 저장하는 테이블 |
514 | if not self.sf.is_simul_table_exist(self.db_name,"all_stocks"): | 530 | if not self.sf.is_simul_table_exist(self.db_name,"all_stocks"): |
515 | logger.debug("all_stocks 테이블을 생성합니다") | 531 | logger.debug("all_stocks 테이블을 생성합니다") |
516 | # 테이블 생성 후 초기화 | 532 | # 테이블 생성 후 초기화 |
517 | self.invest_unit=0 | 533 | self.invest_unit=0 |
518 | self.db_to_transaction(0,0,0,0,0) | 534 | self.db_to_transaction(0,0,0,0,0) |
519 | - self.delete_transaction_item("0") | 535 | + self.delete_all_stock("0") |
520 | 536 | ||
521 | # setting_data에 invest_unit값이 없다면 설정 | 537 | # setting_data에 invest_unit값이 없다면 설정 |
522 | if not self.check_set_invest_unit(): | 538 | if not self.check_set_invest_unit(): |
523 | self.set_invest_unit() | 539 | self.set_invest_unit() |
524 | - # 존재한다면 해당 값을 simulator_func에 저장 | 540 | + # 존재한다면 해당 값을 simulator_api에 저장 |
525 | else: | 541 | else: |
526 | self.invest_unit=self.get_invest_unit() | 542 | self.invest_unit=self.get_invest_unit() |
527 | self.sf.invest_unit=self.invest_unit | 543 | self.sf.invest_unit=self.invest_unit |
... | @@ -529,6 +545,7 @@ class Open_Api(QAxWidget): | ... | @@ -529,6 +545,7 @@ class Open_Api(QAxWidget): |
529 | # all_stock(거래 내역) 테이블 생성 | 545 | # all_stock(거래 내역) 테이블 생성 |
530 | def db_to_all_stocks(self,order_num,code,chegyul_check,purchase_price,rate): | 546 | def db_to_all_stocks(self,order_num,code,chegyul_check,purchase_price,rate): |
531 | logger.debug("-* db_to_all_stocks function *-") | 547 | logger.debug("-* db_to_all_stocks function *-") |
548 | + | ||
532 | self.date_setting() | 549 | self.date_setting() |
533 | self.sf.init_df_all_stocks() # all_stocks 테이블 데이터프레임 생성 | 550 | self.sf.init_df_all_stocks() # all_stocks 테이블 데이터프레임 생성 |
534 | 551 | ||
... | @@ -544,6 +561,7 @@ class Open_Api(QAxWidget): | ... | @@ -544,6 +561,7 @@ class Open_Api(QAxWidget): |
544 | 561 | ||
545 | if order_num != 0: | 562 | if order_num != 0: |
546 | recent_daily_buy_list_date=self.sf.get_recent_daily_buy_list_date() | 563 | recent_daily_buy_list_date=self.sf.get_recent_daily_buy_list_date() |
564 | + # 구매 내역이 존재하는 경우 해당 데이터를 추가 | ||
547 | if recent_daily_buy_list_date: | 565 | if recent_daily_buy_list_date: |
548 | # 특정 날짜, 특정 종목의 주가 데이터 | 566 | # 특정 날짜, 특정 종목의 주가 데이터 |
549 | df=self.sf.get_daily_buy_list_by_code(code,recent_daily_buy_list_date) | 567 | df=self.sf.get_daily_buy_list_by_code(code,recent_daily_buy_list_date) |
... | @@ -593,14 +611,14 @@ class Open_Api(QAxWidget): | ... | @@ -593,14 +611,14 @@ class Open_Api(QAxWidget): |
593 | 'clo120_diff_rate': Float, | 611 | 'clo120_diff_rate': Float, |
594 | }) | 612 | }) |
595 | 613 | ||
596 | - # 거래내역(transaction) 테이블에서 특정 종목을 삭제하는 함수 | 614 | + # all_stocks(거래내역) 테이블에서 특정 종목을 삭제하는 함수 |
597 | def delete_transaction_item(self,code): | 615 | def delete_transaction_item(self,code): |
598 | - query=f"delete from transaction where code={code}" | 616 | + query=f"delete from all_stocks where code={code}" |
599 | self.engine_bot.execute(query) | 617 | self.engine_bot.execute(query) |
600 | 618 | ||
601 | # setting_data 테이블에 invest_unit이 오늘 업데이트 되었는지 확인 | 619 | # setting_data 테이블에 invest_unit이 오늘 업데이트 되었는지 확인 |
602 | def check_set_invest_unit(self): | 620 | def check_set_invest_unit(self): |
603 | - query="select invest_unit, set_invest_unit from setting_data" | 621 | + query="select invest_unit, set_invest_unit from setting_data limit 1" |
604 | result=self.engine_bot.execute(query).fetchall() | 622 | result=self.engine_bot.execute(query).fetchall() |
605 | if result[0][1]==self.today: | 623 | if result[0][1]==self.today: |
606 | self.invest_unit=result[0][0] | 624 | self.invest_unit=result[0][0] |
... | @@ -610,6 +628,7 @@ class Open_Api(QAxWidget): | ... | @@ -610,6 +628,7 @@ class Open_Api(QAxWidget): |
610 | 628 | ||
611 | # 데이터베이스에서 invest_unit값을 가져오는 함수 | 629 | # 데이터베이스에서 invest_unit값을 가져오는 함수 |
612 | def get_invest_unit(self): | 630 | def get_invest_unit(self): |
631 | + logger.debug("-* get_invest_unit function *-") | ||
613 | query="select invest_unit from setting_data" | 632 | query="select invest_unit from setting_data" |
614 | result=self.engine_bot.execute(query).fetchall() | 633 | result=self.engine_bot.execute(query).fetchall() |
615 | return result[0][0] | 634 | return result[0][0] |
... | @@ -619,7 +638,7 @@ class Open_Api(QAxWidget): | ... | @@ -619,7 +638,7 @@ class Open_Api(QAxWidget): |
619 | def set_invest_unit(self): | 638 | def set_invest_unit(self): |
620 | self.get_deposit() | 639 | self.get_deposit() |
621 | self.get_balance() | 640 | self.get_balance() |
622 | - self.total_invest=self.deposit+self.total_purchase_price | 641 | + self.total_invest=self.change_format(str(int(self.deposit)+int(self.total_purchase_price))) |
623 | 642 | ||
624 | self.invest_unit=self.sf.invest_unit | 643 | self.invest_unit=self.sf.invest_unit |
625 | query=f"update setting_data set invest_unit='{self.invest_unit}', set_invest_unit='{self.today}'" | 644 | query=f"update setting_data set invest_unit='{self.invest_unit}', set_invest_unit='{self.today}'" |
... | @@ -643,36 +662,38 @@ class Open_Api(QAxWidget): | ... | @@ -643,36 +662,38 @@ class Open_Api(QAxWidget): |
643 | self.comm_rq_data("opw00018_req","opw00018",2,"2000") | 662 | self.comm_rq_data("opw00018_req","opw00018",2,"2000") |
644 | 663 | ||
645 | # open_api를 통해 보유한 종목을 가져오는 함수 | 664 | # open_api를 통해 보유한 종목을 가져오는 함수 |
646 | - # 가져온 정보를 possesd_item이라는 테이블에 저장 | 665 | + # 가져온 정보를 possessed_item이라는 테이블에 저장 |
647 | - def db_to_possessd_item(self): | 666 | + def db_to_possessed_item(self): |
648 | item_count=len(self.opw00018_output['multi']) | 667 | item_count=len(self.opw00018_output['multi']) |
649 | - possesd_item_data={'date':[],'code':[],'code_name':[],'holding_amount':[],'purchase_price':[], | 668 | + possessed_item_data={'date':[],'code':[],'code_name':[],'holding_amount':[],'purchase_price':[], |
650 | 'present_price':[],'valuation_profit':[],'rate':[],'item_total_purchase':[]} | 669 | 'present_price':[],'valuation_profit':[],'rate':[],'item_total_purchase':[]} |
651 | 670 | ||
652 | - possesd_item=DataFrame(possesd_item_data, | 671 | + possessed_item=DataFrame(possessed_item_data, |
653 | columns=['date','code','code_name','holding_amount','purchase_price', | 672 | columns=['date','code','code_name','holding_amount','purchase_price', |
654 | 'present_price','valuation_profit','rate','item_total_purchase']) | 673 | 'present_price','valuation_profit','rate','item_total_purchase']) |
655 | 674 | ||
656 | for i in range(item_count): | 675 | for i in range(item_count): |
657 | item=self.opw00018_output['multi'][i] | 676 | item=self.opw00018_output['multi'][i] |
658 | 677 | ||
659 | - possesd_item.loc[i,'date']=self.today | 678 | + possessed_item.loc[i,'date']=self.today |
660 | - possesd_item.loc[i,'code']=item[7] | 679 | + possessed_item.loc[i,'code']=item[7] |
661 | - possesd_item.loc[i,'code_name']=item[0] | 680 | + possessed_item.loc[i,'code_name']=item[0] |
662 | - possesd_item.loc[i,'holding_amount']=int(item[1]) | 681 | + possessed_item.loc[i,'holding_amount']=int(item[1]) # 보유량 |
663 | - possesd_item.loc[i,'purchase_price']=int(item[2]) | 682 | + possessed_item.loc[i,'purchase_price']=int(item[2]) # 매수가 |
664 | - possesd_item.loc[i,'present_price']=int(item[3]) | 683 | + possessed_item.loc[i,'present_price']=int(item[3]) # 현재가 |
665 | - possesd_item.loc[i,'valuation_profit']=int(item[4]) | 684 | + possessed_item.loc[i,'valuation_profit']=int(item[4]) # 평가수익률 |
666 | - possesd_item.loc[i,'rate']=float(item[5]) | 685 | + possessed_item.loc[i,'rate']=float(item[5]) # 수익률 |
667 | - possesd_item.loc[i,'item_total_purchase']=int(item[6]) | 686 | + possessed_item.loc[i,'item_total_purchase']=int(item[6]) # 총매수금액 |
668 | 687 | ||
669 | - possesd_item.to_sql("possesd_item",self.engine_bot,if_exists='replace') | 688 | + possessed_item.to_sql("possessed_item",self.engine_bot,if_exists='replace') |
670 | - self.contract_sync() | 689 | + self.chegyul_sync() |
671 | 690 | ||
672 | - # 현재 소유하고 있는 종목에 대해 transaction 테이블을 업데이트 | 691 | + # 매수 완료한 항목을 all_stocks에 기록하는 함수 |
673 | def chegyul_sync(self): | 692 | def chegyul_sync(self): |
674 | - query="select code,code_name,rate from possessed_item " \ | 693 | + # 신규 매수한 항목을 가져온다 |
675 | - "where code not in (select code from transaction where sell_date='0' group by code) group by code" | 694 | + query = "select code, code_name, rate from possessed_item p " \ |
695 | + "where p.code not in (select a.code from all_stocks a where a.sell_date = '0' group by a.code) " \ | ||
696 | + "group by p.code" | ||
676 | result=self.engine_bot.execute(query).fetchall() | 697 | result=self.engine_bot.execute(query).fetchall() |
677 | 698 | ||
678 | for item in result: | 699 | for item in result: |
... | @@ -692,7 +713,7 @@ class Open_Api(QAxWidget): | ... | @@ -692,7 +713,7 @@ class Open_Api(QAxWidget): |
692 | else: | 713 | else: |
693 | continue | 714 | continue |
694 | 715 | ||
695 | - self.db_to_transaction(self.not_contract['주문번호'],item.code,contract_check,self.not_contract['체결가'],item.rate) | 716 | + self.db_to_all_stocks(self.not_contract['주문번호'],item.code,chegyul_check,self.not_contract['체결가'],item.rate) |
696 | 717 | ||
697 | # posses_item 테이블을 업데이트 했을 경우 setting data 테이블에 업데이트 한 날짜를 표시 | 718 | # posses_item 테이블을 업데이트 했을 경우 setting data 테이블에 업데이트 한 날짜를 표시 |
698 | def setting_data_posses_stock(self): | 719 | def setting_data_posses_stock(self): |
... | @@ -730,7 +751,7 @@ class Open_Api(QAxWidget): | ... | @@ -730,7 +751,7 @@ class Open_Api(QAxWidget): |
730 | def is_stock_table_exist(self,code_name): | 751 | def is_stock_table_exist(self,code_name): |
731 | query = "select 1 from information_schema.tables where table_schema ='stock_info' and table_name = '{}'" | 752 | query = "select 1 from information_schema.tables where table_schema ='stock_info' and table_name = '{}'" |
732 | result=self.engine_stock.execute(query.format(code_name)).fetchall() | 753 | result=self.engine_stock.execute(query.format(code_name)).fetchall() |
733 | - if result: | 754 | + if result:ㄴ |
734 | return True | 755 | return True |
735 | else: | 756 | else: |
736 | return False | 757 | return False |
... | @@ -745,20 +766,20 @@ class Open_Api(QAxWidget): | ... | @@ -745,20 +766,20 @@ class Open_Api(QAxWidget): |
745 | else: | 766 | else: |
746 | return str(0) | 767 | return str(0) |
747 | 768 | ||
748 | - # 체결이 완료되었는지 확인하고 transaction(거래내역) 테이블을 업데이트하는 함수 | 769 | + # 체결이 완료되었는지 확인하고 all_stocks(거래내역)테이블을 업데이트 |
749 | def check_chegyul(self): | 770 | def check_chegyul(self): |
750 | - query="select code from transaction where chegyul_check='1'" | 771 | + query="select code from all_stocks where chegyul_check='1' and (sell_date='0' or sell_date=''" |
751 | - rows=self.engine_bot.execute(query).fetchall() | 772 | + result=self.engine_bot.execute(query).fetchall() |
752 | 773 | ||
753 | - for r in rows: | 774 | + for r in result: |
754 | self.set_input_value("종목코드",r.code) | 775 | self.set_input_value("종목코드",r.code) |
755 | self.set_input_value("조회구분",1) | 776 | self.set_input_value("조회구분",1) |
756 | self.set_input_value("계좌번호",self.account_no) | 777 | self.set_input_value("계좌번호",self.account_no) |
757 | self.comm_rq_data("opt10076_req","opt10076",0,"0350") | 778 | self.comm_rq_data("opt10076_req","opt10076",0,"0350") |
758 | 779 | ||
759 | - query=f"update transaction set chegyul_check='0' where code='{r.code}' and sell_data='0' " \ | 780 | + query=f"update all_stocks set chegyul_check='0' where code='{r.code}' and sell_data='0' " \ |
760 | f"order by buy_date desc limit 1" | 781 | f"order by buy_date desc limit 1" |
761 | - # 거래가 완료된 항목은 주문번호가 존재하지 않음 | 782 | + # 거래가 완료된 항목은 주문번호 등 데이터가 존재하지 않음 |
762 | # 거래가 완료된 항목에 대해서 contract_check항목을 '0'으로 업데이트 | 783 | # 거래가 완료된 항목에 대해서 contract_check항목을 '0'으로 업데이트 |
763 | if not self.not_contract['주문번호']: | 784 | if not self.not_contract['주문번호']: |
764 | self.engine_bot.execute(query) | 785 | self.engine_bot.execute(query) |
... | @@ -769,28 +790,45 @@ class Open_Api(QAxWidget): | ... | @@ -769,28 +790,45 @@ class Open_Api(QAxWidget): |
769 | else: | 790 | else: |
770 | logger.debug("미체결 종목이 존재합니다") | 791 | logger.debug("미체결 종목이 존재합니다") |
771 | 792 | ||
793 | + | ||
772 | # 매도했을 경우 possessed_item(보유종목) 테이블에서 항목을 삭제 | 794 | # 매도했을 경우 possessed_item(보유종목) 테이블에서 항목을 삭제 |
773 | def delete_possessed_item(self,code): | 795 | def delete_possessed_item(self,code): |
774 | query=f"delete from possessed_item where code={code}" | 796 | query=f"delete from possessed_item where code={code}" |
775 | self.engine_bot.execute(query) | 797 | self.engine_bot.execute(query) |
776 | 798 | ||
777 | - # 매도한 후 transaction 테이블 업데이트 | 799 | + # 매도한 후 all_stocks 테이블 업데이트 |
778 | - def check_sell_final(self,code): | 800 | + def sell_final_check(self,code): |
779 | - query=f"update transaction chegyul_check='0', sell_date='{self.today_time}' " \ | 801 | + query=f"SELECT valuation_profit, rate, item_total_purchase, present_price FROM possessed_item " \ |
780 | - f"where code='{code}' and sell_date='0' order by buy_date desc" | 802 | + f"WHERE code='{code}' LIMIT 1" |
803 | + result=self.engine_bot.execute(query).fetchall() | ||
804 | + | ||
805 | + if result: | ||
806 | + item = result[0] | ||
807 | + query=f"update all_stocks set " \ | ||
808 | + f"item_total_purchase={item.item_total_purchase}, chegyul_check=0, " \ | ||
809 | + f"sell_date={self.today_time}, valuation_profit={item.valuation_profit}," \ | ||
810 | + f"sell_rate={item.rate}, sell_price={item.present_price} " \ | ||
811 | + f"where code='{code}' and sell_date='0' order by buy_date desc limit 1" | ||
781 | self.engine_bot.execute(query) | 812 | self.engine_bot.execute(query) |
782 | 813 | ||
783 | - # posses_item테이블에는 존재하지만 transaction_history테이블에 존재하지 않는 항목에 대해 업데이트 | 814 | + # 매도한 항목을 possessed_item(보유종목) 테이블에서 삭제 |
784 | - def final_check_contract(self): | 815 | + self.engine_bot.execute(f"DELETE FROM possessed_item WHERE code = '{code}'") |
785 | - query="select code from transaction_history" \ | 816 | + |
786 | - "where" \ | 817 | + # 매도했을 때, possessed_item에서 삭제되었지만 all_stocks에 sell_date 칼럼이 업데이트 되지 않은 항목들을 처리하는 함수 |
787 | - "sell_date='%s' or sell_date='%s' and code not in (select code from posses_item) and contract_check!='%s'" | 818 | + def final_chegyul_check(self): |
788 | - result=self.engine_bot.execute(query%(0,"",1)).fetchall() | 819 | + query = "select code from all_stocks a " \ |
789 | - num=len(result) | 820 | + "where (a.sell_date = '0' or a.sell_date ='') and a.code not in ( select code from possessed_item) "\ |
790 | - for i in range(num): | 821 | + "and a.chegyul_check != '1'" |
791 | - self.sell_check(result[i][0]) | 822 | + |
792 | - query="update setting_data set final_contract_check='%s'" | 823 | + result = self.engine_bot.execute(query).fetchall() |
793 | - self.engine_bot.execute(query%(self.today)) | 824 | + num = len(result) |
825 | + | ||
826 | + for t in range(num): | ||
827 | + self.sell_final_check2(result[t][0]) | ||
828 | + | ||
829 | + # 모든 항목을 처리했으면 setting_data테이블의 final_chegyul_check 항목에 오늘 날짜를 저장 | ||
830 | + query = f"UPDATE setting_data SET final_chegyul_check='{self.today}' limit 1" | ||
831 | + self.engine_bot.execute(query) | ||
794 | 832 | ||
795 | # 가지고 있던 종목을 판매하여 posses_item테이블에 내역이 존재하지 않지만 transaction_history에 매도처리가 되지 않은 항목 업데이트 | 833 | # 가지고 있던 종목을 판매하여 posses_item테이블에 내역이 존재하지 않지만 transaction_history에 매도처리가 되지 않은 항목 업데이트 |
796 | def sell_check(self,code): | 834 | def sell_check(self,code): |
... | @@ -808,9 +846,10 @@ class Open_Api(QAxWidget): | ... | @@ -808,9 +846,10 @@ class Open_Api(QAxWidget): |
808 | 846 | ||
809 | # setting_data에 possessed_item 항목을 업데이트 | 847 | # setting_data에 possessed_item 항목을 업데이트 |
810 | def set_setting_data_possessed_item(self): | 848 | def set_setting_data_possessed_item(self): |
811 | - query=f"update setting_data set possessed_item={self.today}" | 849 | + query=f"update setting_data set possessed_item={self.today} limit 1" |
812 | self.engine_bot.execute(query) | 850 | self.engine_bot.execute(query) |
813 | 851 | ||
852 | + # 특정 종목의 틱(1분별) 데이터 조회 함수 | ||
814 | def get_total_data_min(self,code,code_name,start): | 853 | def get_total_data_min(self,code,code_name,start): |
815 | self.ohlcv=defaultdict(list) | 854 | self.ohlcv=defaultdict(list) |
816 | 855 | ||
... | @@ -819,10 +858,10 @@ class Open_Api(QAxWidget): | ... | @@ -819,10 +858,10 @@ class Open_Api(QAxWidget): |
819 | self.set_input_value("수정주가구분",1) | 858 | self.set_input_value("수정주가구분",1) |
820 | self.comm_rq_data("opt10080_req","opt10080",0,"1999") | 859 | self.comm_rq_data("opt10080_req","opt10080",0,"1999") |
821 | 860 | ||
822 | - self.is_craw_table_exist=False | 861 | + self.craw_table_exist=False |
823 | 862 | ||
824 | if self.is_min_craw_table_exist(code_name): | 863 | if self.is_min_craw_table_exist(code_name): |
825 | - self.is_craw_table_exist=True | 864 | + self.craw_table_exist=True |
826 | self.craw_db_last_min=self.get_craw_db_last_min(code_name) | 865 | self.craw_db_last_min=self.get_craw_db_last_min(code_name) |
827 | self.craw_db_last_min_sum_volume=self.get_craw_db_last_min_sum_volume(code_name) | 866 | self.craw_db_last_min_sum_volume=self.get_craw_db_last_min_sum_volume(code_name) |
828 | 867 | ||
... | @@ -840,11 +879,10 @@ class Open_Api(QAxWidget): | ... | @@ -840,11 +879,10 @@ class Open_Api(QAxWidget): |
840 | if self.ohlcv['date'][-1]<self.craw_db_last_min: | 879 | if self.ohlcv['date'][-1]<self.craw_db_last_min: |
841 | break | 880 | break |
842 | 881 | ||
843 | - time.sleep(TR_REQ_TIME_INTERBVAL) | 882 | + time.sleep(TR_REQ_TIME_INTERVAL) |
844 | 883 | ||
845 | if len(self.ohlcv['date']==0 or self.ohlcv['date'][0]==''): | 884 | if len(self.ohlcv['date']==0 or self.ohlcv['date'][0]==''): |
846 | return [] | 885 | return [] |
847 | - | ||
848 | if self.ohlcv['date']=='': | 886 | if self.ohlcv['date']=='': |
849 | return [] | 887 | return [] |
850 | 888 | ||
... | @@ -852,6 +890,305 @@ class Open_Api(QAxWidget): | ... | @@ -852,6 +890,305 @@ class Open_Api(QAxWidget): |
852 | 890 | ||
853 | return df | 891 | return df |
854 | 892 | ||
893 | + # 특정 종목의 일자별 거래 데이터 조회 함수 | ||
894 | + def get_total_data(self,code,code_name,date): | ||
895 | + logger.debug("-* get_total_data function *-") | ||
896 | + | ||
897 | + self.ohlcv=defaultdict(list) | ||
898 | + self.set_input_value("종목코드",code) | ||
899 | + self.set_input_value("기준일자",date) | ||
900 | + self.set_input_value("수정주가구분",1) | ||
901 | + | ||
902 | + self.comm_rq_data("opt10081_req","opt10081",0,"0101") | ||
903 | + | ||
904 | + if not self.is_craw_table_exist(code_name): | ||
905 | + while self.remained_data: | ||
906 | + self.set_input_value("종목코드", code) | ||
907 | + self.set_input_value("기준일자", date) | ||
908 | + self.set_input_value("수정주가구분", 1) | ||
909 | + self.comm_rq_data("opt10081_req", "opt10081", 2, "0101") | ||
910 | + | ||
911 | + if len(self.ohlcv)==0: | ||
912 | + return [] | ||
913 | + if self.ohlcv['date']=='': | ||
914 | + return [] | ||
915 | + | ||
916 | + df=DataFrame(self.ohlcv,columns=['date','open','high','low','close','volume']) | ||
917 | + return df | ||
918 | + | ||
919 | + # daily_craw 데이터베이스에 {code_name} 테이블이 존재하는지 확인하는 함수 | ||
920 | + def is_craw_table_exist(self,code_name): | ||
921 | + query="select 1 from information_schema.tables where table_schema='daily_craw' and table_name='{}'" | ||
922 | + result=self.engine_daily_craw.execute(query.format(code_name)) | ||
923 | + if result: | ||
924 | + return True | ||
925 | + else: | ||
926 | + return False | ||
927 | + | ||
928 | + # min_craw 데이터베이스에 {code_name} 테이블이 존재하는지 확인하는 함수 | ||
929 | + def is_min_craw_table_exist(self,code_name): | ||
930 | + query = "select 1 from information_schema.tables where table_schema ='min_craw' and table_name = '{}'" | ||
931 | + result = self.engine_craw.execute(query.format(code_name)).fetchall() | ||
932 | + if result: | ||
933 | + return True | ||
934 | + else: | ||
935 | + return False | ||
936 | + | ||
937 | + # min_craw 테이블에서 마지막에 저장한 행의 sum_volume값을 가져오는 함수 | ||
938 | + def get_craw_db_last_min_sum_volume(self,code_name): | ||
939 | + query = f"SELECT sum_volume from `{code_name}` order by date desc limit 1" | ||
940 | + result = self.engine_craw.execute(query).fetchall() | ||
941 | + if len(result): | ||
942 | + return result[0][0] | ||
943 | + else: | ||
944 | + return str(0) | ||
945 | + | ||
946 | + # min_craw 테이블에서 마지막에 저장한 행의 시간(분) 정보를 가져오는 함수 | ||
947 | + def get_craw_db_last_min(self,code_name): | ||
948 | + query = f"SELECT date from `{code_name}` order by date desc limit 1" | ||
949 | + result = self.engine_craw.execute(query).fetchall() | ||
950 | + if len(result): | ||
951 | + return result[0][0] | ||
952 | + # 신생 | ||
953 | + else: | ||
954 | + return str(0) | ||
955 | + | ||
956 | + # min_craw 테이블에서 마지막에 저장한 행의 날짜 정보를 가져오는 함수 | ||
957 | + def get_craw_db_last_date(self,code_name): | ||
958 | + query = f"SELECT date from `{code_name}` order by date desc limit 1" | ||
959 | + result = self.engine_craw.execute(query).fetchall() | ||
960 | + if len(result): | ||
961 | + return result[0][0] | ||
962 | + else: | ||
963 | + return str(0) | ||
964 | + | ||
965 | + # 특정 종목의 특정 날짜의 특정 데이터(시가/종가/고가/저가/거래량)를 반환하는 함수 | ||
966 | + def get_one_day_option_data(self,code,start,option): | ||
967 | + self.ohlcv=defaultdict(list) | ||
968 | + | ||
969 | + self.set_input_value("종목코드",code) | ||
970 | + self.set_input_value("기준일자",start) | ||
971 | + self.set_input_value("수정주가구분",1) | ||
972 | + self.comm_rq_data("opt10081_req","opt10081",0,"0101") | ||
973 | + | ||
974 | + if self.ohlcv['date']=='': | ||
975 | + return False | ||
976 | + | ||
977 | + df=DataFrame(self.ohlcv,columns=['open','high','low','close','volume'],index=self.ohlcv['date']) | ||
978 | + | ||
979 | + if df.empty: | ||
980 | + return False | ||
981 | + | ||
982 | + if option=="open": | ||
983 | + return df.iloc[0,0] | ||
984 | + elif option=='high': | ||
985 | + return df.iloc[0,1] | ||
986 | + elif option=='low': | ||
987 | + return df.iloc[0,2] | ||
988 | + elif option=='close': | ||
989 | + return df.iloc[0,3] | ||
990 | + elif option=='volume': | ||
991 | + return df.iloc[0,4] | ||
992 | + else: | ||
993 | + return False | ||
994 | + | ||
995 | + # open_api에 주문요청을 보내는 함수. 성공할 경우 return 0 | ||
996 | + # param - sRQName : 사용자 구분명 | ||
997 | + # sScrNo : 화면번호 | ||
998 | + # sAccNo : 계좌번호 | ||
999 | + # nOrderType : 주문유형 (1.신규매수/2.신규매도/3.매수취소/4.매도취소/5.매수정정/6.매도정정 | ||
1000 | + # code : 종목코드 | ||
1001 | + # nQty : 주문수량 | ||
1002 | + # nPrice : 주문가격 | ||
1003 | + # sHogaGb : 거래구분 (00.지정가/03.시장가/05.조건부지정가/06.최유리지정가/07.최우선지정가/10.지정가IOC/ | ||
1004 | + # 13.시장가IOC/16.최유리IOC/20.지정가FOK/23.시장가FOK/26.최유리FOK/61.장전시간외종가 | ||
1005 | + # 62.시간외단일가매매/81.장후시간외종가) | ||
1006 | + # sOrgOrderNo : 원주문번호. 신규주문은 공백, 정정(취소) 주문할 원주문번호 입력 | ||
1007 | + # return - 0 : 성공 | ||
1008 | + # - -308 : 주문가능횟수 초과. 1초에 5회만 주문 가능 | ||
1009 | + # - 이외 : 주문실패 | ||
1010 | + def send_order(self,sRQName,sScrNo,sAccNo,nOrderType,code,nQty,nPrice,sHogaGb,sOrgOrderNo): | ||
1011 | + logger.debug("send order") | ||
1012 | + try: | ||
1013 | + self.exit_check() | ||
1014 | + self.dynamicCall("SendOrder(QString, QString, QString, int, QString, int, int, QString, QString)", | ||
1015 | + [sRQName,sScrNo,sAccNo,nOrderType,code,nQty,nPrice,sHogaGb,sOrgOrderNo]) | ||
1016 | + except Exception as e: | ||
1017 | + logger.critical(e) | ||
1018 | + | ||
1019 | + # 코드명에 해당하는 종목코드를 반환하는 함수 | ||
1020 | + def codename_to_code(self,codename): | ||
1021 | + # 데이터베이스에 종목명과 같은 값을 가지는 행의 code를 반환 | ||
1022 | + query = f"select code from stock_item_all where code_name={codename}" | ||
1023 | + result = self.engine_daily_buy_list.execute(query).fetchall() | ||
1024 | + if len(result)!=0: | ||
1025 | + return result[0][0] | ||
1026 | + | ||
1027 | + # 종목명의 길이가 길 경우 | ||
1028 | + query = f"select code from stock_item_all where code_name like '{codename}%'" | ||
1029 | + result = self.engine_daily_buy_list.execute(query).fetchall() | ||
1030 | + if len(result)!=0: | ||
1031 | + return result[0][0] | ||
1032 | + | ||
1033 | + # 종목명에 해당하는 값이 없을 경우 return False | ||
1034 | + return False | ||
1035 | + | ||
1036 | + # 매수 완료 후 데이터베이스를 업데이트 하는 함수 | ||
1037 | + def end_invest_count_check(self,code): | ||
1038 | + query = f"update all_stocks set chegyul_check='0' WHERE code='{code}' and sell_date = '0' " \ | ||
1039 | + f"ORDER BY buy_date desc LIMIT 1" | ||
1040 | + self.engine_bot.execute(query) | ||
1041 | + | ||
1042 | + # possessed_item 테이블에 중복으로 데이터가 반영되는 것을 막기 위해 possessed_item에서 해당 종목 삭제 | ||
1043 | + query = f"delete from possessed_item where code ={code}" | ||
1044 | + self.engine_bot.execute(query) | ||
1045 | + | ||
1046 | + # 잔액이 생겨서 다시 매수할 수 있는 상황이 되었을 경우, setting_data의 today_buy_stop 옵션을 0으로 변경경 | ||
1047 | + def reset_buy_check(self): | ||
1048 | + query = "UPDATE setting_data SET today_buy_stop='0' WHERE id='1'" | ||
1049 | + self.engine_bot.execute(query) | ||
1050 | + | ||
1051 | + # 투자 가능한 잔액이 부족하거나, 매수할 종목이 더 이상 없는 경우 setting_data의 today_buy_stop옵션을 0으로 변경 | ||
1052 | + # 더이상 매수하지 않음 | ||
1053 | + def buy_check_stop(self): | ||
1054 | + query = f"UPDATE setting_data SET today_buy_stop='{self.today}' limit 1" | ||
1055 | + self.engine_bot.execute(query) | ||
1056 | + | ||
1057 | + # 잔액을 확인하는 함수 | ||
1058 | + # 잔액이 부족하여 더이상 투자를 진행할 수 없는 경우, janfo_is_null을 True로 설정한 후 False 반환 | ||
1059 | + def check_jango(self): | ||
1060 | + self.get_deposit() | ||
1061 | + try: | ||
1062 | + if int(self.d2_deposit_before_format) > (int(self.sf.limit_money)): | ||
1063 | + self.jango_is_null = False # trade 루프를 돌다가 잔액이 부족해졌을 때 루프를 빠져나오기 위한 변수 | ||
1064 | + return True | ||
1065 | + else: | ||
1066 | + self.jango_is_null = True | ||
1067 | + return False | ||
1068 | + except Exception as e: | ||
1069 | + logger.critical(e) | ||
1070 | + | ||
1071 | + # today_buy_stop 칼럼을 확인하는 함수 | ||
1072 | + # setting_data테이블의 today_buy_stop 칼럼에 오늘 날짜가 적혀있는 경우 매수 중지 | ||
1073 | + def buy_check(self): | ||
1074 | + query = "select today_buy_stop from setting_data limit 1" | ||
1075 | + result = self.engine_JB.execute(sql).fetchall()[0][0] | ||
1076 | + | ||
1077 | + if result != self.today: | ||
1078 | + return True | ||
1079 | + else: | ||
1080 | + return False | ||
1081 | + | ||
1082 | + # 구매할 수량을 계산하는 함수 | ||
1083 | + def buy_num_count(self,invest_unit,present_price): | ||
1084 | + return int(invest_unit/present_price) | ||
1085 | + | ||
1086 | + # 거래 함수 | ||
1087 | + def trade(self): | ||
1088 | + # 실시간 현재가(close)를 저장 | ||
1089 | + # 현재시점의 종가(close)는 현재가와 같다 | ||
1090 | + current_price = self.get_one_day_option_data(self.get_today_buy_list_code, self.today, 'close') | ||
1091 | + | ||
1092 | + if current_price == False: | ||
1093 | + return False | ||
1094 | + | ||
1095 | + min_buy_limit = int(self.get_today_buy_list_close) * self.sf.invest_min_limit_rate # 매수가격 최저 범위 | ||
1096 | + max_buy_limit = int(self.get_today_buy_list_close) * self.sf.invest_limit_rate # 매수가격 최고 범위 | ||
1097 | + | ||
1098 | + # 현재가가 매수 가격 최저 범위와 매수 가격 최고 범위 안에 들어와 있다면 매수 | ||
1099 | + if min_buy_limit < current_price < max_buy_limit: | ||
1100 | + buy_num = self.buy_num_count(self.invest_unit, int(current_price)) | ||
1101 | + logger.debug( | ||
1102 | + "매수+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- code :%s, 목표가: %s, 현재가: %s, 매수량: %s, min_buy_limit: %s, max_buy_limit: %s , invest_limit_rate: %s,예수금: %s , today : %s, today_min : %s, date_rows_yesterday : %s, invest_unit : %s, real_invest_unit : %s +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-", | ||
1103 | + self.get_today_buy_list_code, self.get_today_buy_list_close, current_price, buy_num, min_buy_limit, | ||
1104 | + max_buy_limit, self.sf.invest_limit_rate, self.d2_deposit_before_format, self.today, self.today_detail, | ||
1105 | + self.date_rows_yesterday, self.invest_unit, int(current_price) * int(buy_num)) | ||
1106 | + | ||
1107 | + # 03 시장가 매수 | ||
1108 | + # 4번째 parameter: 1: 신규매수 / 2: 신규매도 / 3:매수취소 / 4:매도취소 / 5: 매수정정 / 6:매도정정 | ||
1109 | + self.send_order("send_order_req", "0101", self.account_number, 1, self.get_today_buy_list_code, buy_num, 0, | ||
1110 | + "03", "") | ||
1111 | + | ||
1112 | + # 만약 sf.only_nine_buy가 False 이면, 매도 후에 잔액이 생기면 다시 매수를 시작 | ||
1113 | + # sf.only_nine_buy가 True이면 1회만 매수, 1회 매수 시 잔액이 부족해지면 바로 매수 중단 | ||
1114 | + if not self.jango_check() and self.sf.use_nine: | ||
1115 | + # setting_data에 today_buy_stop을 1 로 설정 | ||
1116 | + self.buy_check_stop() | ||
1117 | + | ||
1118 | + def get_today_buy_list(self): | ||
1119 | + # 구매할 목록을 저장하는 테이블(realtime_daily_buy_list)이 존재하지 않거나, 테이블 내에 항목이 존재하지 않는다면 return | ||
1120 | + if self.sf.is_simul_table_exist(self.db_name, "realtime_daily_buy_list"): | ||
1121 | + self.sf.get_realtime_daily_buy_list() | ||
1122 | + if self.sf.len_df_realtime_daily_buy_list == 0: | ||
1123 | + return | ||
1124 | + else: | ||
1125 | + return | ||
1126 | + | ||
1127 | + # 만약에 realtime_daily_buy_list 의 종목 수가 1개 이상이면 | ||
1128 | + for i in range(self.sf.len_df_realtime_daily_buy_list): | ||
1129 | + code = self.sf.df_realtime_daily_buy_list.loc[i, 'code'] | ||
1130 | + close = self.sf.df_realtime_daily_buy_list.loc[i, 'close'] | ||
1131 | + check_item = self.sf.df_realtime_daily_buy_list.loc[i, 'check_item'] # 매수확인. 오늘 매수한 종목이면 1, 아니면 0 | ||
1132 | + | ||
1133 | + # 잔액이 부족한 경우 break | ||
1134 | + if self.jango_is_null: | ||
1135 | + break | ||
1136 | + # 이미 매수한 종목은 건너뛰고 다음종목 거래래 | ||
1137 | + if check_item == True: | ||
1138 | + continue | ||
1139 | + else: | ||
1140 | + self.get_today_buy_list_code = code | ||
1141 | + self.get_today_buy_list_close = close | ||
1142 | + # 매수 하기 전에 해당 종목의 check_item을 1로 변경 | ||
1143 | + query = f"UPDATE realtime_daily_buy_list SET check_item='1' WHERE code='{self.get_today_buy_list_code}'" | ||
1144 | + self.engine_bot.execute(query) | ||
1145 | + self.trade() | ||
1146 | + | ||
1147 | + # 모든 매수를 마쳤으면 더이상 매수 하지 않도록 설정 | ||
1148 | + if self.sf.use_nine: | ||
1149 | + self.buy_check_stop() | ||
1150 | + | ||
1151 | + # 보유하고 있는 종목에 대해 all_stocks(거래내역) 테이블을 업데이트 | ||
1152 | + def rate_check(self): | ||
1153 | + query = "select code,holding_amount,purchase_price,present_price,valuation_profit,rate,item_total_purchase "\ | ||
1154 | + "from possessed_item group by code" | ||
1155 | + result=self.engine_bot.execute(query).fetchall() | ||
1156 | + num = len(result) | ||
1157 | + | ||
1158 | + for k in range(num): | ||
1159 | + code = result[k][0] | ||
1160 | + holding_amount = result[k][1] | ||
1161 | + purchase_price = result[k][2] | ||
1162 | + present_price =result[k][3] | ||
1163 | + valuation_profit=result[k][4] | ||
1164 | + rate = result[k][5] | ||
1165 | + item_total_purchase = result[k][6] | ||
1166 | + | ||
1167 | + query = f"update all_stocks set " \ | ||
1168 | + f"holding_amount ='{holding_amount}', purchase_price ='{purchase_price}', " \ | ||
1169 | + f"present_price='{present_price}',valuation_profit='{valuation_profit}',rate='{rate}'," \ | ||
1170 | + f"item_total_purchase='{item_total_purchase}' " \ | ||
1171 | + f"where code='{code}' and sell_date = '0'" | ||
1172 | + self.engine_bot.execute(query) | ||
1173 | + | ||
1174 | + # 매도 후 possessed_item(보유종목) 테이블에는 없지만, | ||
1175 | + # all_stocks(거래내역) 테이블에 sell_date가 업데이트되지 않은 항목을 처리하는 함수 | ||
1176 | + def sell_final_check2(self,code): | ||
1177 | + query = f"UPDATE all_stocks SET chegyul_check='0', sell_date ='{self.today_time}' " \ | ||
1178 | + f"WHERE code='{code}' and sell_date ='0' ORDER BY buy_date desc LIMIT 1" | ||
1179 | + self.engine_bot.execute(query) | ||
1180 | + | ||
1181 | + # all_stocks(거래내역) 테이블에서 현재 보유중인 종목이 존재하는지 확인하는 함수 | ||
1182 | + def is_all_stock_check(self,code): | ||
1183 | + query = f"select code from all_stocks where code='{code}' and (sell_date ='0' or sell_date='') " \ | ||
1184 | + f"ORDER BY buy_date desc LIMIT 1" | ||
1185 | + result=self.engine_bot.execute(query).fetchall() | ||
1186 | + if len(result) != 0: | ||
1187 | + return True | ||
1188 | + else: | ||
1189 | + return False | ||
1190 | + | ||
1191 | + | ||
855 | # 주식일봉차트조회 요청 함수 - 하나의 데이터만 저장 | 1192 | # 주식일봉차트조회 요청 함수 - 하나의 데이터만 저장 |
856 | def _opt10081(self, sRQName, sTrCode): | 1193 | def _opt10081(self, sRQName, sTrCode): |
857 | code = self._get_comm_data(sTrCode, sRQName, 0, "종목코드") | 1194 | code = self._get_comm_data(sTrCode, sRQName, 0, "종목코드") |
... | @@ -895,8 +1232,11 @@ class Open_Api(QAxWidget): | ... | @@ -895,8 +1232,11 @@ class Open_Api(QAxWidget): |
895 | 1232 | ||
896 | # 예수금 상세현황요청 함수 | 1233 | # 예수금 상세현황요청 함수 |
897 | def _opw00001(self,sRQName,sTrCode): | 1234 | def _opw00001(self,sRQName,sTrCode): |
898 | - self.deposit=self._get_comm_data(sTrCode,sRQName,0,"d+2출금가능금액") | 1235 | + try: |
899 | - self.deposit=int(self.deposit) | 1236 | + self.d2_deposit_before_format = self._get_comm_data(sTrCode, sRQName, 0, "d+2출금가능금액") |
1237 | + self.d2_deposit = self.change_format(self.d2_deposit_before_format) | ||
1238 | + except Exception as e: | ||
1239 | + logger.critical(e) | ||
900 | 1240 | ||
901 | # 계좌평가 잔고내역을 저장하는 변수 초기화 | 1241 | # 계좌평가 잔고내역을 저장하는 변수 초기화 |
902 | def reset_opw00018_output(self): | 1242 | def reset_opw00018_output(self): |
... | @@ -913,23 +1253,23 @@ class Open_Api(QAxWidget): | ... | @@ -913,23 +1253,23 @@ class Open_Api(QAxWidget): |
913 | self.total_earning_rate=self._get_comm_data(sTrCode,sRQName,0,"총수익률(%)") | 1253 | self.total_earning_rate=self._get_comm_data(sTrCode,sRQName,0,"총수익률(%)") |
914 | self.estimated_deposit=self._get_comm_data(sTrCode,sRQName,0,"추정예탁자산") | 1254 | self.estimated_deposit=self._get_comm_data(sTrCode,sRQName,0,"추정예탁자산") |
915 | 1255 | ||
916 | - self.total_purchase_price=int(self.total_purchase_price) | 1256 | + self.change_total_purchase_price = self.change_format(self.total_purchase_price) |
917 | - self.total_eval_price=int(self.total_eval_price) | 1257 | + self.change_total_eval_price = self.change_format(self.total_eval_price) |
918 | - self.total_eval_profit_loss_price=int(self.total_eval_profit_loss_price) | 1258 | + self.change_total_eval_profit_loss_price = self.change_format(self.total_eval_profit_loss_price) |
919 | - self.total_earning_rate=float(self.total_earning_rate) | 1259 | + self.change_total_earning_rate = self.change_format2(self.total_earning_rate) |
920 | - self.estimated_deposit=int(self.estimated_deposit) | 1260 | + self.change_estimated_deposit = self.change_format(self.estimated_deposit) |
921 | 1261 | ||
922 | - self.opw00018_output['single'].append(self.total_purchase_price) | 1262 | + self.opw00018_output['single'].append(self.change_total_purchase_price) |
923 | - self.opw00018_output['single'].append(self.total_eval_price) | 1263 | + self.opw00018_output['single'].append(self.change_total_eval_price) |
924 | - self.opw00018_output['single'].append(self.total_eval_profit_loss_price) | 1264 | + self.opw00018_output['single'].append(self.change_total_eval_profit_loss_price) |
925 | - self.opw00018_output['single'].append(self.total_earning_rate) | 1265 | + self.opw00018_output['single'].append(self.change_total_earning_rate) |
926 | - self.opw00018_output['single'].append(self.estimated_deposit) | 1266 | + self.opw00018_output['single'].append(self.change_estimated_deposit) |
927 | 1267 | ||
928 | # 종목별 평가 잔고 데이터 - 멀티데이터 저장 | 1268 | # 종목별 평가 잔고 데이터 - 멀티데이터 저장 |
929 | rows=self._get_repeat_cnt(sTrCode,sRQName) | 1269 | rows=self._get_repeat_cnt(sTrCode,sRQName) |
930 | 1270 | ||
931 | for i in range(rows): | 1271 | for i in range(rows): |
932 | - code=self._get_comm_data(sTrCode,sRQName,i,"종목번호") | 1272 | + code=code_pattern.search(self._get_comm_data(sTrCode,sRQName,i,"종목번호")).group(0) |
933 | name=self._get_comm_data(sTrCode,sRQName,i,"종목명") | 1273 | name=self._get_comm_data(sTrCode,sRQName,i,"종목명") |
934 | quantity=self._get_comm_data(sTrCode,sRQName,i,"보유수량") | 1274 | quantity=self._get_comm_data(sTrCode,sRQName,i,"보유수량") |
935 | purchase_price=self._get_comm_data(sTrCode,sRQName,i,"매입가") | 1275 | purchase_price=self._get_comm_data(sTrCode,sRQName,i,"매입가") |
... | @@ -938,13 +1278,12 @@ class Open_Api(QAxWidget): | ... | @@ -938,13 +1278,12 @@ class Open_Api(QAxWidget): |
938 | earning_rate=self._get_comm_data(sTrCode,sRQName,i,"수익률(%)") | 1278 | earning_rate=self._get_comm_data(sTrCode,sRQName,i,"수익률(%)") |
939 | item_total_purchase=self._get_comm_data(sTrCode,sRQName,i,"매입금액") | 1279 | item_total_purchase=self._get_comm_data(sTrCode,sRQName,i,"매입금액") |
940 | 1280 | ||
941 | - code=code[1:] | 1281 | + quantity = self.change_format(quantity) |
942 | - quantity=int(quantity) | 1282 | + purchase_price = self.change_format(purchase_price) |
943 | - purchase_price=int(purchase_price) | 1283 | + current_price = self.change_format(current_price) |
944 | - current_price=int(current_price) | 1284 | + eval_profit_loss_price = self.change_format(eval_profit_loss_price) |
945 | - eval_profit_loss_price=int(eval_profit_loss_price) | 1285 | + earning_rate = self.change_format2(earning_rate) |
946 | - earning_rate=float(earning_rate) | 1286 | + item_total_purchase = self.change_format(item_total_purchase) |
947 | - item_total_purchase=int(item_total_purchase) | ||
948 | 1287 | ||
949 | self.opw00018_output['multi'].append( | 1288 | self.opw00018_output['multi'].append( |
950 | [name,quantity,purchase_price,current_price,eval_profit_loss_price,earning_rate,item_total_purchase,code] | 1289 | [name,quantity,purchase_price,current_price,eval_profit_loss_price,earning_rate,item_total_purchase,code] |
... | @@ -952,8 +1291,12 @@ class Open_Api(QAxWidget): | ... | @@ -952,8 +1291,12 @@ class Open_Api(QAxWidget): |
952 | 1291 | ||
953 | # 일자별 실현 손익 요청 | 1292 | # 일자별 실현 손익 요청 |
954 | def _opt10074(self,sRQName,sTrCode): | 1293 | def _opt10074(self,sRQName,sTrCode): |
955 | - self.total_profit=self._get_comm_data(sTrCode,sRQName,0,"실현손익") | 1294 | + try: |
956 | - self.today_profit=self._get_comm_data(sTrCode,sRQName,0,"당일매도손익") | 1295 | + rows = self._get_repeat_cnt(sTrCode, sRQName) |
1296 | + self.total_profit = self._get_comm_data(sTrCode,sRQName, 0, "실현손익") | ||
1297 | + self.today_profit = self._get_comm_data(sTrCode,sRQName, 0, "당일매도손익") | ||
1298 | + except Exception as e: | ||
1299 | + logger.critical(e) | ||
957 | 1300 | ||
958 | # 위탁종합거래내역 요청 함수 | 1301 | # 위탁종합거래내역 요청 함수 |
959 | def _opw00015(self,sRQName,sTrCode): | 1302 | def _opw00015(self,sRQName,sTrCode): |
... | @@ -966,7 +1309,7 @@ class Open_Api(QAxWidget): | ... | @@ -966,7 +1309,7 @@ class Open_Api(QAxWidget): |
966 | def _opt10076(self,sRQName,sTrCode): | 1309 | def _opt10076(self,sRQName,sTrCode): |
967 | outputs=['주문번호','종목명','주문구분','주문가격','주문수량','체결가','체결량','미체결수량', | 1310 | outputs=['주문번호','종목명','주문구분','주문가격','주문수량','체결가','체결량','미체결수량', |
968 | '당일매매수수료','당일매매세금','주문상태','매매구분','원주문번호','주문시간','종목코드'] | 1311 | '당일매매수수료','당일매매세금','주문상태','매매구분','원주문번호','주문시간','종목코드'] |
969 | - self._data={} | 1312 | + self._data={} # 미체결 항목을 저장하는 변수 |
970 | 1313 | ||
971 | for key in outputs: | 1314 | for key in outputs: |
972 | if key not in ['주문번호','원주문번호','주문시간','종목코드']: | 1315 | if key not in ['주문번호','원주문번호','주문시간','종목코드']: |
... | @@ -990,7 +1333,7 @@ class Open_Api(QAxWidget): | ... | @@ -990,7 +1333,7 @@ class Open_Api(QAxWidget): |
990 | today_profit=self._get_comm_data(sTrCode,sRQName,i,"당일매도손익") | 1333 | today_profit=self._get_comm_data(sTrCode,sRQName,i,"당일매도손익") |
991 | earning_rate=self._get_comm_data(sTrCode,sRQName,i,"손익율") | 1334 | earning_rate=self._get_comm_data(sTrCode,sRQName,i,"손익율") |
992 | 1335 | ||
993 | - code=code.lstrip('A') | 1336 | + code=self.change_format4(code) |
994 | 1337 | ||
995 | self.opt10073_output['multi'].append([date,code,code_name,amount,today_profit,earning_rate]) | 1338 | self.opt10073_output['multi'].append([date,code,code_name,amount,today_profit,earning_rate]) |
996 | 1339 | ||
... | @@ -1013,6 +1356,78 @@ class Open_Api(QAxWidget): | ... | @@ -1013,6 +1356,78 @@ class Open_Api(QAxWidget): |
1013 | self.ohlcv['volume'].append(int(volume)) | 1356 | self.ohlcv['volume'].append(int(volume)) |
1014 | self.ohlcv['sum_volume'].append(int(0)) | 1357 | self.ohlcv['sum_volume'].append(int(0)) |
1015 | 1358 | ||
1359 | + # 데이터베이스로부터 보유하고 있는 종목의 보유량(holding amount)을 가져오는 함수 | ||
1360 | + def get_holding_amount(self,code): | ||
1361 | + logger.debug("-* get_holding_amount function *-") | ||
1362 | + query=f"select holding_amount from possessed_item where code={code}" | ||
1363 | + result=self.engine_bot.execute(query).fetchall() | ||
1364 | + if len(result): | ||
1365 | + return result[0][0] | ||
1366 | + else: | ||
1367 | + logger.debug("Holding amount is empty.") | ||
1368 | + return False | ||
1369 | + | ||
1370 | + # open_api로부터 받아온 데이터의 형식을 변형하는 함수 | ||
1371 | + # 데이터 값 앞의 의미없는 0을 제거 | ||
1372 | + def change_format(self,data): | ||
1373 | + try: | ||
1374 | + strip_data=data.lstrip('0') | ||
1375 | + # 유효한 값이 존재하지 않을 경우 0으로 대체 | ||
1376 | + if strip_data=="": | ||
1377 | + strip_data='0' | ||
1378 | + return int(strip_data) | ||
1379 | + except Exception as e: | ||
1380 | + logger.critical(e) | ||
1381 | + | ||
1382 | + # 수익률을 소수 형태로 변환하는 함수 | ||
1383 | + def change_format2(self,data): | ||
1384 | + try: | ||
1385 | + strip_data=data.lstrip('-0') | ||
1386 | + # 유효한 값이 없을 경우 0으로 대체 | ||
1387 | + if strip_data=="": | ||
1388 | + strip_data='0' | ||
1389 | + else: | ||
1390 | + strip_data=str(float(strip_data)/self.mod_gubun) | ||
1391 | + # 수익률< 1 일 경우 | ||
1392 | + if strip_data.startswith('.'): | ||
1393 | + strip_data='0'+strip_data | ||
1394 | + # 수익률 < 0 일 경우 | ||
1395 | + if data.startswith('-'): | ||
1396 | + strip_data='-'+strip_data | ||
1397 | + return strip_data | ||
1398 | + except Exception as e: | ||
1399 | + logger.critical(e) | ||
1400 | + | ||
1401 | + # 특수문자(%), 앞뒤 공백 제거 함수 | ||
1402 | + def change_format3(self,data): | ||
1403 | + try: | ||
1404 | + strip_data=data.stript('%') | ||
1405 | + strip_data=strip_data.strip() | ||
1406 | + return strip_data | ||
1407 | + except Exception as e: | ||
1408 | + logger.critical(e) | ||
1409 | + | ||
1410 | + # 코드 앞의 문자를 제거하는 함수수 | ||
1411 | + def change_format4(sel,data): | ||
1412 | + try: | ||
1413 | + strip_data=data.lstrip('A') | ||
1414 | + return strip_data | ||
1415 | + except Exception as e: | ||
1416 | + logger.critical(e) | ||
1417 | + | ||
1418 | + # open api 조회 요청 수를 체크하는 함수 | ||
1419 | + # 최대 조회 요청 가능 횟수를 넘어서면, 프로그램을 종료 | ||
1420 | + def exit_check(self): | ||
1421 | + rq_delay=datetime.timedelta(seconds=0.6) | ||
1422 | + time_diff=datetime.datetime.now()-self.call_time | ||
1423 | + if rq_delay>time_diff: | ||
1424 | + time.sleep((rq_delay-time_diff).total_seconds()) | ||
1425 | + | ||
1426 | + self.rq_count+=1 | ||
1427 | + | ||
1428 | + logger.debug(self.rq_count) | ||
1429 | + if self.rq_count==cf.max_api_call: | ||
1430 | + sys.exit(1) | ||
1016 | 1431 | ||
1017 | if __name__=="__main__": | 1432 | if __name__=="__main__": |
1018 | app=QApplication(sys.argv) | 1433 | app=QApplication(sys.argv) | ... | ... |
-
Please register or login to post a comment