Showing
13 changed files
with
524 additions
and
239 deletions
proj/library/Logger.py
0 → 100644
1 | +import logging.handlers | ||
2 | +import sys | ||
3 | + | ||
4 | +# formmater 생성 | ||
5 | +formatter = logging.Formatter('[%(levelname)s|%(filename)s:%(lineno)s] %(asctime)s > %(message)s') | ||
6 | + | ||
7 | +# logger 인스턴스를 생성 및 로그 레벨 설정 | ||
8 | +logger = logging.getLogger("crumbs") | ||
9 | +logger.setLevel(logging.DEBUG) | ||
10 | +streamHandler = logging.StreamHandler() | ||
11 | +streamHandler.setFormatter(formatter) | ||
12 | +logger.addHandler(streamHandler) | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
proj/library/Simulator_Api.py
0 → 100644
1 | +import datetime | ||
2 | +from sqlalchemy import * | ||
3 | +import pymysql | ||
4 | +from pandas import DataFrame | ||
5 | + | ||
6 | +import config | ||
7 | + | ||
8 | +class Simulator_Api: | ||
9 | + def __init__(self,simul_num,op,db_name): | ||
10 | + self.simul_num=int(simul_num) | ||
11 | + | ||
12 | + if op=="real": | ||
13 | + self.op='real' | ||
14 | + self.simul_reset=False | ||
15 | + self.db_name=db_name | ||
16 | + self.set_variable() | ||
17 | + | ||
18 | + def set_date(self): | ||
19 | + self.today=datetime.datetime.today().strftime("%Y%m%d") | ||
20 | + | ||
21 | + def set_variable(self): | ||
22 | + self.set_date() | ||
23 | + self.set_database() | ||
24 | + | ||
25 | + # 매수/매도 알고리즘 선택 -> 차후 알고리즘 별로 구현 및 재설정 | ||
26 | + self.buy_algorithm=1 | ||
27 | + self.sell_algorithm=1 | ||
28 | + | ||
29 | + # 특정 데이터베이스 내에 특정 테이블이 존재하는지 확인하는 함수 | ||
30 | + def is_table_exist(self,db_name,table_name): | ||
31 | + query="select 1 from information_schema.tables where table_schema='%s' and table_name='%s'" | ||
32 | + result=self.engine_simul.execute(query%(db_name,table_name)).fetchall() | ||
33 | + if len(result)==1: | ||
34 | + return True | ||
35 | + else: | ||
36 | + return False | ||
37 | + | ||
38 | + # 데이터베이스 연결 설정 | ||
39 | + def set_database(self): | ||
40 | + if self.op=="real": | ||
41 | + self.engine_simul=create_engine("mysql+pymysql://"+config.db_id+":"+config.db_pw+"@"+config.db_ip+ | ||
42 | + ":"+config.db_port+"/"+str(self.db_name),encoding='utf-8') | ||
43 | + else: | ||
44 | + self.db_name='simulator'+str(self.simul_num) | ||
45 | + self.engine_simul=create_engine("mysql+pymysql://"+config.db_id+":"+config.db_pw+"@"+config.db_ip+ | ||
46 | + ":"+config.db_port+"/"+str(self.db_name),encoding='utf-8') | ||
47 | + | ||
48 | + self.engine_daily = create_engine("mysql+pymysql://" + config.db_id + ":" + config.db_pw + "@" + config.db_ip + | ||
49 | + ":" + config.db_port + "/daily_info" , encoding='utf-8') | ||
50 | + self.engine_stock = create_engine("mysql+pymysql://" + config.db_id + ":" + config.db_pw + "@" + config.db_ip + | ||
51 | + ":" + config.db_port + "/stock_info", encoding='utf-8') | ||
52 | + self.engine_minute = create_engine("mysql+pymysql://" + config.db_id + ":" + config.db_pw + "@" + config.db_ip + | ||
53 | + ":" + config.db_port + "/minute_info", encoding='utf-8') | ||
54 | + | ||
55 | + self.conn=pymysql.connect( | ||
56 | + host=config.db_ip, | ||
57 | + port=int(config.db_port), | ||
58 | + user=config.db_id, | ||
59 | + password=config.db_pw, | ||
60 | + charset='utf8' | ||
61 | + ) | ||
62 | + | ||
63 | + # 날짜별 주식 데이터를 저장해 놓은 데이터베이스에서 가장 최근 날짜를 가져오는 함수 | ||
64 | + def get_latest_date(self): | ||
65 | + query="select table_name from information_schema.tables where table_schema='daily_info' and " \ | ||
66 | + "table_name like '%s' order by table_name desc limit 1" | ||
67 | + result=self.engine_daily.execute(query%("20%%")).fetchall() | ||
68 | + if len(result)==0: | ||
69 | + return False | ||
70 | + else: | ||
71 | + return result[0][0] | ||
72 | + | ||
73 | + # transaction_history 테이블의 Dataframe 생성 | ||
74 | + def df_transaction_history(self): | ||
75 | + df_temp={'id':[]} | ||
76 | + self.df_th=DataFrame(df_temp, | ||
77 | + columns=['id','order_num','code','code_name','rate','purchase_rate', | ||
78 | + 'purchase_price','present_price','valuation_price','valuation_profit', | ||
79 | + 'holding_amount','buy_date','total_purchase_price','contract_check','per_invest', | ||
80 | + 'sell_date','sell_price','sell_rate' | ||
81 | + ]) | ||
82 | + | ||
83 | + # daily_info 데이터베이스에서 특정 날짜에서 특정 코드에 해당하는 정보만 가져오는 함수수 | ||
84 | + def get_daily_info_by_code(self,code,date): | ||
85 | + query="select * from {} where code='{}' group by code" | ||
86 | + daily_info=self.engine_daily.execute(query.format(date,code)).fetchall() | ||
87 | + df_daily_info=DataFrame(daily_info, | ||
88 | + columns=['index','index2','date','check_item','code', | ||
89 | + 'code_name','dff_rate', | ||
90 | + 'close','open','high','low','volume', | ||
91 | + 'avg5','avg10','avg20','avg60','avg120', | ||
92 | + 'prev_avg5','prev_avg10','prev_avg20','prev_avg60','prev_avg120', | ||
93 | + 'avg5_diff_rate','avg10_diff_rate','avg20_diff_rate','avg60_diff_rate', | ||
94 | + 'avg120_diff_rate', | ||
95 | + 'vol5','vol10','vol20','vol60','vol120']) | ||
96 | + return df_daily_info | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
1 | +from sqlalchemy import * | ||
2 | + | ||
1 | from open_api import * | 3 | from open_api import * |
4 | +from daily_info import * | ||
5 | +from stock_info import * | ||
6 | +import config | ||
2 | 7 | ||
3 | class CollectorApi(): | 8 | class CollectorApi(): |
4 | def __init__(self): | 9 | def __init__(self): |
5 | self.open_api=OpenApi() | 10 | self.open_api=OpenApi() |
6 | self.engine_bot=self.open_api.engine_bot | 11 | self.engine_bot=self.open_api.engine_bot |
7 | 12 | ||
13 | + def set_variable(self): | ||
14 | + self.open_api.sort="collector" | ||
15 | + self.stock_info=StockInfo(config.real_bot,config.real_stockInfo,config.real_dailyInofo) | ||
16 | + self.daily_info=DailyInfo() | ||
17 | + | ||
18 | + def update_code(self): | ||
19 | + print("update code") | ||
20 | + query = "select code_update,jango_data_db_check, possessed_item, today_profit, final_chegyul_check, " \ | ||
21 | + "db_to_buy_list,today_buy_list, daily_crawler , min_crawler, daily_buy_list " \ | ||
22 | + "from setting_data limit 1" | ||
23 | + result=self.engine_bot.execute(query).fetchall() | ||
24 | + | ||
25 | + print(result) | ||
26 | + | ||
27 | + if result[0][0]!=self.open_api.today(): | ||
28 | + self.open_api.check_balance() | ||
29 | + self.get_code_list() | ||
30 | + | ||
31 | + | ||
32 | + def set_db_minute_info(self): | ||
33 | + print("Make Minute Info Database") | ||
34 | + query="select code,code_name from stock_all" | ||
35 | + target=self.open_api.engine_dInfo.execute(query).fetchall() | ||
36 | + print(target) | ||
37 | + | ||
38 | +app = QApplication(sys.argv) | ||
39 | +c=CollectorApi() | ||
40 | +c.update_code() | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -9,5 +9,16 @@ test_account_no="8147766711" | ... | @@ -9,5 +9,16 @@ test_account_no="8147766711" |
9 | 9 | ||
10 | # 시뮬레이션을 돌리기 시작할 날짜 | 10 | # 시뮬레이션을 돌리기 시작할 날짜 |
11 | start_buying='20190101' | 11 | start_buying='20190101' |
12 | -simul_num1=1 | 12 | + |
13 | -simul_name1="AutoBot"+str(simul_num1)+"_Test" | 13 | +test_num=1 |
14 | +test_bot_name="AutoBot"+str(test_num)+"_Test" | ||
15 | + | ||
16 | +# 실전투자 정보 | ||
17 | +real_account_no="" | ||
18 | + | ||
19 | +real_num=1 | ||
20 | + | ||
21 | +# 데이터베이스 이름 | ||
22 | +real_bot_name="AutoBot"+str(real_num) | ||
23 | +real_stockInfo_name="stock_info" | ||
24 | +real_dailyInfo_name="daily_info" | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
1 | -from PyQt5.QtWidgets import * | ||
2 | -from PyQt5.QAxContainer import * | ||
3 | from PyQt5.QtCore import * | 1 | from PyQt5.QtCore import * |
4 | -from PyQt5.QtTest import * | 2 | +from PyQt5.QAxContainer import * |
5 | -from pandas import DataFrame | 3 | +from PyQt5.QtWidgets import * |
6 | -import sys | ||
7 | import pymysql | 4 | import pymysql |
5 | +import datetime | ||
8 | from sqlalchemy import * | 6 | from sqlalchemy import * |
7 | +from collections import defaultdict | ||
8 | +from pandas import DataFrame | ||
9 | 9 | ||
10 | +from Logger import * | ||
10 | import config | 11 | import config |
11 | -import error_code | 12 | +from Simulator_Api import * |
12 | 13 | ||
13 | -TR_REQ_TIME_INTERVAL = 3600 | 14 | +pymysql.install_as_MySQLdb() |
14 | 15 | ||
15 | -class OpenApi(QAxWidget): | 16 | +class Open_Api(QAxWidget): |
16 | def __init__(self): | 17 | def __init__(self): |
17 | super().__init__() | 18 | super().__init__() |
18 | 19 | ||
19 | # event_loop list | 20 | # event_loop list |
20 | - self.tr_event_loop=QEventLoop() | ||
21 | self.login_event_loop=QEventLoop() | 21 | self.login_event_loop=QEventLoop() |
22 | + self.tr_event_loop=QEventLoop() | ||
22 | 23 | ||
23 | - # variable list | 24 | + # open_api 호출 횟수를 저장f |
24 | - self.account_no=None # 계좌번호 | 25 | + self.rq_count=0 |
25 | - self.ohlcv = None # 거래정보(시가,종가,고가,저가,거래량) | 26 | + self.set_date() |
26 | - self.remained_data=None # 다음페이지 존재여부 | 27 | + self.tr_loop_count=0 |
27 | - self.simul_num=None # 모의투자 번호 | 28 | + self.call_time=datetime.datetime.now() |
28 | - self.db_name=None # 거래데이터를 담고 있는 AutoBot 데이터베이스 | ||
29 | - | ||
30 | - # engine list | ||
31 | - self.engine_bot=None # AutoBot데이터베이스에 접속하는 engine | ||
32 | - | ||
33 | - # screen number list | ||
34 | - self.screen_data_req="0101" # 거래 데이터 | ||
35 | - | ||
36 | - self.create_instance() | ||
37 | - self._signal_slots() | ||
38 | - self.comm_connect() # 로그인 | ||
39 | - self.get_account_info() # 계좌정보 가져오기 | ||
40 | - self.variable_setting() # 데이터베이스 설정 | ||
41 | - | ||
42 | - # 데이터베이스 설정 | ||
43 | - def variable_setting(self): | ||
44 | - print("============variable setting function===============") | ||
45 | - # 모의투자인 경우 | ||
46 | - if self.account_no == config.test_account_no: | ||
47 | - self.simul_num = config.simul_num1 | ||
48 | - self.db_name_setting(config.simul_name1) | ||
49 | - | ||
50 | - # AutoBot Database 생성 | ||
51 | - def create_database(self,cursor): | ||
52 | - print("create database") | ||
53 | - query="create database {}" | ||
54 | - cursor.execute(query.format(self.db_name)) | ||
55 | 29 | ||
56 | - # AutoBot Database가 존재하는지 여부를 확인 | 30 | + # open_api 연동 |
57 | - def is_BotTable_exist(self,cursor): | 31 | + self._create_instance() |
58 | - query="select 1 from information_schema.schemata where schema_name='{}'" | 32 | + self._set_signal_slots() |
59 | - print(query.format(self.db_name)) | 33 | + self.comm_connect() |
60 | - if (cursor.execute(query.format(self.db_name))): | 34 | + |
61 | - print("%s 데이터베이스가 존재합니다.",self.db_name) | 35 | + # 계좌정보 출력 |
62 | - return True | 36 | + self.get_account_info() |
37 | + | ||
38 | + # 변수 설정 | ||
39 | + self.set_variable() | ||
40 | + | ||
41 | + self.simul_api=Simulator_Api(self.simul_num,"real",self.db_name) | ||
42 | + logger.debug("알고리즘 번호 : %s",self.simul_api.simul_num) | ||
43 | + logger.debug("매수 알고리즘 번호 : %s",self.simul_api.buy_algorithm) | ||
44 | + logger.debug("매도 알고리즘 번호 : %s",self.simul_api.sell_algorithm) | ||
45 | + | ||
46 | + if not self.simul_api.is_table_exist(self.db_name,"setting_data"): | ||
47 | + self.create_setting_data() | ||
48 | + | ||
49 | + self.set_simul_variable() | ||
50 | + self.ohlcv=defaultdict(list) | ||
51 | + | ||
52 | + # 날짜 설정 | ||
53 | + def set_date(self): | ||
54 | + self.today=datetime.datetime.today().strftime("%Y%m%d") | ||
55 | + self.today_time=datetime.datetime.today().strftime("%Y%m%d%H%M") | ||
56 | + | ||
57 | + # 키움 open_api 를 사용하기 위한 ocx controller 생성 | ||
58 | + def _create_instance(self): | ||
59 | + try: | ||
60 | + self.setControl("KHOPENAPI.KHOpenAPICtrl.1") | ||
61 | + except Exception as e: | ||
62 | + logger.critical(e) | ||
63 | + | ||
64 | + # 이벤트 처리를 위한 slots | ||
65 | + def _set_signal_slots(self): | ||
66 | + try: | ||
67 | + # 로그인 처리 이벤트 | ||
68 | + self.OnEventConnect.connect(self._login_slot) | ||
69 | + # 조회요청 처리 이벤트 | ||
70 | + self.OnReceiveTrData.connect(self._receive_tr_data) | ||
71 | + # 서버 통신 후 수신메세지 처리 이벤트 | ||
72 | + self.OnReceiveMsg.connect(self._receive_msg) | ||
73 | + # 주문요청 처리 이벤트 | ||
74 | + self.OnReceiveChejanData.connect(self._receive_chejan_data) | ||
75 | + except Exception as e: | ||
76 | + logger.critical(e) | ||
77 | + | ||
78 | + # 로그인 이벤트 처리 slot | ||
79 | + # param : code - 로그인 성공 시 0 | ||
80 | + # 실패 시 에러코드 출력 | ||
81 | + def _login_slot(self,code): | ||
82 | + try: | ||
83 | + if code==0: | ||
84 | + logger.debug("connected") | ||
63 | else: | 85 | else: |
64 | - return False | 86 | + logger.debug("connection failed...") |
87 | + self.login_event_loop.exit() | ||
88 | + except Exception as e: | ||
89 | + logger.critical(e) | ||
65 | 90 | ||
66 | - # 데이터베이스 확인 및 생성 | 91 | + # 수동 로그인설정인 경우 로그인창을 출력해서 로그인을 시도 |
67 | - def db_name_setting(self,db_name): | 92 | + # 자동로그인 설정인 경우 로그인창 출력없이 로그인을 시도 |
68 | - self.db_name=db_name | 93 | + def comm_connect(self): |
69 | - print("db_name : %s"%self.db_name) | 94 | + try: |
70 | - conn = pymysql.connect( | 95 | + self.dynamicCall("CommConnect()") |
71 | - host=config.db_ip, | 96 | + self.login_event_loop.exec_() |
72 | - port=int(config.db_port), | 97 | + except Exception as e: |
73 | - user=config.db_id, | 98 | + logger.critical(e) |
74 | - password=config.db_pw, | ||
75 | - charset='utf8' | ||
76 | - ) | ||
77 | - cursor=conn.cursor() | ||
78 | - if not self.is_BotTable_exist(cursor): | ||
79 | - self.create_database(cursor) | ||
80 | - self.engine_bot=create_engine( | ||
81 | - "mysql+pymysql://" + config.db_id + ":" + config.db_pw + "@" + config.db_ip + ":" + config.db_port + "/" + | ||
82 | - db_name, encoding='utf-8') | ||
83 | - self.db_setting(cursor) | ||
84 | - | ||
85 | - # AutoBot 이외에 필요한 데이터베이스를 생성 | ||
86 | - def db_setting(self,cursor): | ||
87 | - # 생성해야 할 데이터베이스 리스트 | ||
88 | - to_create=['stock_info','daily_info','minute_info'] | ||
89 | - query="select schema_name from information_schema.schemata" | ||
90 | - cursor.execute(query) | ||
91 | - result=cursor.fetchall() | ||
92 | - create_db="CREATE DATABASE {}" | ||
93 | - # 생성되어 있는 데이터베이스 리스트 | ||
94 | - created=list() | ||
95 | - for item in result: | ||
96 | - created.append(item[0]) | ||
97 | - # 생성하야 할 데이터베이스가 존재하지 않는 경우 새로 생성 | ||
98 | - for db in to_create: | ||
99 | - if db not in created: | ||
100 | - print(db,"데이터베이스가 존재하지 않습니다. 데이터베이스를 새로 생성합니다.") | ||
101 | - create_query=create_db.format(db) | ||
102 | - cursor.execute(create_query) | ||
103 | - print(db,"데이터베이스 생성 완료!") | ||
104 | - | ||
105 | - # 주식일봉차트조회요청 | ||
106 | - def _opt10081(self,sRQName,sTrCode): | ||
107 | - QTest.qWait(TR_REQ_TIME_INTERVAL) | ||
108 | - | ||
109 | - cnt=self._get_repeat_cnt(sTrCode,sRQName) | ||
110 | - | ||
111 | - for i in range(cnt): | ||
112 | - date=self._get_comm_data(sTrCode,sRQName,i,"일자") | ||
113 | - open=self._get_comm_data(sTrCode,sRQName,i,"시가") | ||
114 | - high=self._get_comm_data(sTrCode,sRQName,i,"고가") | ||
115 | - low=self._get_comm_data(sTrCode,sRQName,i,"저가") | ||
116 | - close=self._get_comm_data(sTrCode,sRQName,i,"현재가") | ||
117 | - volume=self._get_comm_data(sTrCode,sRQName,i,"거래량") | ||
118 | - | ||
119 | - self.ohlcv['date'].append(date) | ||
120 | - self.ohlcv['open'].append(int(open)) | ||
121 | - self.ohlcv['high'].append(int(high)) | ||
122 | - self.ohlcv['low'].append(int(low)) | ||
123 | - self.ohlcv['close'].append(int(close)) | ||
124 | - self.ohlcv['volumn'].append(int(volume)) | ||
125 | 99 | ||
126 | # TR 요청을 처리하는 slot | 100 | # TR 요청을 처리하는 slot |
127 | - # param sScrNo: 스크린번호 | 101 | + # param sScrNo : 스크린번호 |
128 | - # sRQName: 요청했을 때 지은 이름 | 102 | + # sRQName : 요청했을 때 지은 이름 |
129 | - # sTrCode: 요청 id, tr코드 | 103 | + # sTrCode : 요청 id, tr코드 |
130 | - # sRecordName: 레코드 이름 | 104 | + # sRecordName : 레코드 이름 |
131 | - # sPrevNext: 다음 페이지가 있는지 여부. "2" : 다음페이지 존재, "0" or "" : 다음페이지 없음 | 105 | + # sPrevNext : 다음 페이지가 있는지 여부. "2" : 다음페이지 존재, "0" or "" : 다음페이지 없음 |
132 | def _receive_tr_data(self,sScrNo,sRQName,sTrCode,sRecordName,sPrevNext): | 106 | def _receive_tr_data(self,sScrNo,sRQName,sTrCode,sRecordName,sPrevNext): |
133 | if sPrevNext=='2': | 107 | if sPrevNext=='2': |
134 | self.remained_data=True | 108 | self.remained_data=True |
135 | else: | 109 | else: |
136 | self.remained_data=False | 110 | self.remained_data=False |
137 | 111 | ||
138 | - if sRQName=="opt10081": | 112 | + # Request 요청에 따른 함수 처리 |
139 | - print("==============주식일봉차트조회요청================") | 113 | + if sRQName == "opt10081_req" and self.purpose == "trader": |
114 | + logger.debug("주식일봉차트조회요청") | ||
140 | self._opt10081(sRQName,sTrCode) | 115 | self._opt10081(sRQName,sTrCode) |
141 | - elif sRQName=="opw0001_req": | 116 | + elif sRQName == "opt10081_req" and self.purpose == "collector": |
142 | - return | 117 | + logger.debug("주식일봉차트조회요청") |
143 | - elif sRQName=="opw00018_req": | 118 | + self.collector_opt10081(sRQName,sTrCode) |
144 | - return | 119 | + elif sRQName == "opw00001_req": |
145 | - elif sRQName=="opt10074_req": | 120 | + logger.debug("예수금상세현황요청") |
146 | - return | 121 | + self._opw00001(sRQName,sTrCode) |
147 | - elif sRQName=="opw00015_req": | 122 | + elif sRQName == "opw00018_req": |
148 | - return | 123 | + logger.debug("계좌평가잔고내역요청") |
149 | - elif sRQName=="opt10076_req": | 124 | + self._opw00018(sRQName,sTrCode) |
150 | - return | 125 | + elif sRQName == "opt10074_req": |
151 | - elif sRQName=="opt10073_req": | 126 | + logger.debug("일자별실현손익요청") |
152 | - return | 127 | + self._opt10084(sRQName,sTrCode) |
128 | + elif sRQName == "opw00015_req": | ||
129 | + logger.debug("위탁종합거래내역요청") | ||
130 | + self._opw00015(sRQName,sTrCode) | ||
131 | + elif sRQName == "opt10076_req": | ||
132 | + logger.debug("실시간체결요청") | ||
133 | + self._opt10076(sRQName,sTrCode) | ||
134 | + elif sRQName == "opt10073_req": | ||
135 | + logger.debug("일자별종목별실현손익요청") | ||
136 | + self._opt10073(sRQName,sTrCode) | ||
137 | + elif sRQName == "opt10080_req": | ||
138 | + logger.debug("주식분봉차트조회요청") | ||
139 | + self._opt10080(sRQName,sTrCode) | ||
140 | + elif sRQName == "send_order_req": | ||
141 | + pass | ||
142 | + else: | ||
143 | + logger.debug("Invalid Request Code...") | ||
153 | 144 | ||
145 | + # 다음 페이지가 존재할 경우 처리 | ||
146 | + # 다음 페이지가 존재하지 않을 경우 event loop를 종료 | ||
147 | + if sRQName!="send_order_req": | ||
148 | + self.tr_loop_count-=1 | ||
154 | try: | 149 | try: |
150 | + if self.tr_loop_count<=0: | ||
155 | self.tr_event_loop.exit() | 151 | self.tr_event_loop.exit() |
152 | + self.tr_loop_count=0 | ||
156 | except AttributeError: | 153 | except AttributeError: |
157 | pass | 154 | pass |
158 | 155 | ||
159 | - # 특정 종목의 일자별 거래 데이터 조회 함수 | 156 | + # 서버통신 후 수신한 메시지를 알려주는 slot |
160 | - # param : code - 종목코드 | 157 | + def _receive_msg(self,sScrNo,sRQName,sTrCode,sMsg): |
161 | - # start - 기준일자 | 158 | + logger.debug(sMsg) |
162 | - # return : df - 특정종목의 일자별 거래 데이터 목록 | 159 | + |
163 | - def get_total_data(self,code,start): | 160 | + # 주문요청후 주문접수, 체결통보, 잔고통보를 수신할 때 마다 호출 |
164 | - self.ohlcv = {'date': [], 'open': [], 'high': [], 'low': [], 'close': [], 'volume': []} | 161 | + # GetChejanData()함수를 이용해서 상세한 정보를 얻을 수 있다 |
165 | - | 162 | + # param : sGubun - 체결구분. 접수와 체결시 '0'값, 국내주식 잔고전달은 '1'값, 파생잔고 전달은 '4' |
166 | - self._set_input_value("종목코드",code) | 163 | + def _receive_chejan_data(self,sGubun,nItemCnt,sFIdList): |
167 | - self._set_input_value("기준일자",start) | 164 | + # 현재 체결 진행 중인 코드 |
168 | - self._set_input_value("수정주가구분",1) | 165 | + print("주분번호 : ",self.get_chejan_data(9023)) |
169 | - self._comm_rq_data("opt10081_req","opt10081",0,self.screen_data_req) | 166 | + print("종목명 : ",self.get_chejan_data(302)) |
170 | - | 167 | + print("주문수량 : ",self.get_chejan_data(900)) |
171 | - while self.remained_data: | 168 | + print("주문가격 : ",self.get_chejan_data(901)) |
172 | - self._set_input_value("종목코드",code) | 169 | + |
173 | - self._set_input_value("기준일자",start) | 170 | + # OnReceiveChejan()이벤트가 호출될때 체결정보나 잔고정보를 얻어오는 함수 |
174 | - self._set_input_value("수정주가구분",1) | 171 | + # param : nFid - 실시간 타입에 포함된 FID |
175 | - self._comm_rq_data("opt10081_req","opt10081",2,self.screen_data_req) | 172 | + def get_chejan_data(self,nFid): |
176 | - | 173 | + try: |
177 | - # 데이터가 없거나, date=''일 경우 empty list를 반환 | 174 | + ret=self.dynamicCall("GetChejanData(int)",nFid) |
178 | - if len(self.ohlcv)==0: | 175 | + return ret |
179 | - return [] | 176 | + except Exception as e: |
180 | - if self.ohlcv['date']=='': | 177 | + logger.critical(e) |
181 | - return [] | ||
182 | - | ||
183 | - # 데이터를 DataFrame형태로 저장 및 반환 | ||
184 | - df=DataFrame(self.ohlcv,columns=['open','high','low','close','volume'],index=self.ohlcv['date']) | ||
185 | - | ||
186 | - return df | ||
187 | - | ||
188 | - # 특정 종목의 특정 날짜의 시가,종가,고가,저가,거래량 중 특정한 데이터를 반환하는 함수 | ||
189 | - # param : code - 종목코드 | ||
190 | - # : date - 조회날짜 | ||
191 | - # : option - open(시가)/close(종가)/high(고가)/low(저가)/volume(거래량) | ||
192 | - # return : 조회한 데이터에 해당하는 값 | ||
193 | - # 값이 없거나, option이 오류일 경우 return False | ||
194 | - def get_oneday_option_data(self,code,date,option): | ||
195 | - self.ohlcv = {'date': [], 'open': [], 'high': [], 'low': [], 'close': [], 'volume': []} | ||
196 | - | ||
197 | - self._set_input_value("종목코드",code) | ||
198 | - self._set_input_value("기준일자",date) | ||
199 | - self._set_input_value("수정주가구분",1) | ||
200 | - self._comm_rq_data("opt10081_req","opt10081",0,self.screen_data_req) | ||
201 | - | ||
202 | - if self.ohlcv['date']=="": | ||
203 | - return False | ||
204 | - | ||
205 | - df = DataFrame(self.ohlcv, columns=['open', 'high', 'low', 'close', 'volume'], index=self.ohlcv['date']) | ||
206 | - if option == 'open': | ||
207 | - return df.iloc[0, 0] | ||
208 | - elif option == 'high': | ||
209 | - return df.iloc[0, 1] | ||
210 | - elif option == 'low': | ||
211 | - return df.iloc[0, 2] | ||
212 | - elif option == 'close': | ||
213 | - return df.iloc[0, 3] | ||
214 | - elif option == 'volume': | ||
215 | - return df.iloc[0, 4] | ||
216 | - else: | ||
217 | - return False | ||
218 | 178 | ||
219 | # 사용자의 계좌정보 저장 및 출력 | 179 | # 사용자의 계좌정보 저장 및 출력 |
220 | def get_account_info(self): | 180 | def get_account_info(self): |
221 | - account=self.get_login_info("ACCNO") | 181 | + account_no=self.get_login_info("ACCNO") |
222 | - self.account_no=account.split(";")[0] | 182 | + self.account_no=account_no.split(";")[0] |
223 | - print("======== 계좌번호 : ",self.account_no,"========") | 183 | + logger.debug(self.account_no) |
224 | 184 | ||
225 | # 원하는 사용자 정보 반환 | 185 | # 원하는 사용자 정보 반환 |
226 | # param : tag - ACCNO - 보유계좌리스트 | 186 | # param : tag - ACCNO - 보유계좌리스트 |
... | @@ -236,50 +196,7 @@ class OpenApi(QAxWidget): | ... | @@ -236,50 +196,7 @@ class OpenApi(QAxWidget): |
236 | ret=self.dynamicCall("GetLoginInfo(QString)",tag) | 196 | ret=self.dynamicCall("GetLoginInfo(QString)",tag) |
237 | return ret | 197 | return ret |
238 | except Exception as e: | 198 | except Exception as e: |
239 | - print(e) | 199 | + logger.critical(e) |
240 | - sys.exit() | ||
241 | - | ||
242 | - # 키움 api를 사용하기 위한 ocx controller 저장 | ||
243 | - def create_instance(self): | ||
244 | - try: | ||
245 | - self.setControl("KHOPENAPI.KHOpenAPICtrl.1") | ||
246 | - except Exception as e: | ||
247 | - print(e) | ||
248 | - sys.exit() | ||
249 | - | ||
250 | - # event처리를 위한 slot | ||
251 | - def _signal_slots(self): | ||
252 | - try: | ||
253 | - self.OnEventConnect.connect(self._login_slot) | ||
254 | - self.OnReceiveTrData.connect(self._receive_tr_data) | ||
255 | - except Exception as e: | ||
256 | - print(e) | ||
257 | - sys.exit() | ||
258 | - | ||
259 | - # 수동 로그인설정인 경우 로그인창을 출력해서 로그인을 시도 | ||
260 | - # 자동로그인 설정인 경우 로그인창 출력없이 로그인을 시도 | ||
261 | - def comm_connect(self): | ||
262 | - try: | ||
263 | - self.dynamicCall("CommConnect()") | ||
264 | - self.login_event_loop.exec_() | ||
265 | - except Exception as e: | ||
266 | - print(e) | ||
267 | - sys.exit() | ||
268 | - | ||
269 | - # 로그인 이벤트 처리 slot | ||
270 | - # param : code - 로그인 성공 시 0 | ||
271 | - # 실패 시 에러코드 출력 | ||
272 | - def _login_slot(self,code): | ||
273 | - try: | ||
274 | - result=error_code.errors(code) | ||
275 | - if code==0: | ||
276 | - print("Connected",result[1]) | ||
277 | - else: | ||
278 | - print("Failed to connect",result[1]) | ||
279 | - self.login_event_loop.exit() | ||
280 | - except Exception as e: | ||
281 | - print(e) | ||
282 | - sys.exit() | ||
283 | 200 | ||
284 | # 조회요청시 TR의 Input값을 지정하는 함수 | 201 | # 조회요청시 TR의 Input값을 지정하는 함수 |
285 | # param : sId - TR에 명시된 Input이름 | 202 | # param : sId - TR에 명시된 Input이름 |
... | @@ -320,6 +237,190 @@ class OpenApi(QAxWidget): | ... | @@ -320,6 +237,190 @@ class OpenApi(QAxWidget): |
320 | print(e) | 237 | print(e) |
321 | sys.exit() | 238 | sys.exit() |
322 | 239 | ||
323 | -if __name__ == "__main__": | ||
324 | - app = QApplication(sys.argv) | ||
325 | - api=OpenApi() | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
240 | + # 변수 설정 | ||
241 | + # 실전투자인지 모의투자인지 여부를 확인하고 그에 해당하는 데이터베이스를 생성하는 함수 | ||
242 | + def set_variable(self): | ||
243 | + self.config=config | ||
244 | + if self.account_no==self.config.real_account_no: | ||
245 | + logger.debug("실전투자") | ||
246 | + self.simul_num=self.config.real_num | ||
247 | + self.set_database(self.config.real_bot_name) | ||
248 | + elif self.account_no==self.config.test_account_no: | ||
249 | + logger.debug("모의투자") | ||
250 | + self.simul_num=self.config.test_num | ||
251 | + self.set_database(self.config.test_bot_name) | ||
252 | + else: | ||
253 | + logger.debug("Invalid Account Number. Check the Config.py") | ||
254 | + exit(1) | ||
255 | + | ||
256 | + # 데이터베이스 생성 및 엔진 설정 | ||
257 | + def set_database(self,db_name): | ||
258 | + self.db_name=db_name | ||
259 | + conn=pymysql.connect( | ||
260 | + host=config.db_ip, | ||
261 | + port=int(config.db_port), | ||
262 | + user=config.db_id, | ||
263 | + password=config.db_pw, | ||
264 | + charset='utf8' | ||
265 | + ) | ||
266 | + cursor=conn.cursor() | ||
267 | + if not self.is_database_exist(cursor): | ||
268 | + self.create_database(cursor) | ||
269 | + self.engine_bot=create_engine("mysql+pymysql://"+self.config.db_id+":"+self.config.db_pw+"@"+ | ||
270 | + self.config.db_ip + ":" + self.config.db_port+"/"+db_name,encoding='utf-8') | ||
271 | + self.create_info_database(cursor) | ||
272 | + | ||
273 | + conn.commit() | ||
274 | + cursor.close() | ||
275 | + conn.close() | ||
276 | + | ||
277 | + self.engine_daily=create_engine("mysql+pymysql://"+self.config.db_id+":"+self.config.db_pw+"@"+ | ||
278 | + self.config.db_ip + ":" + self.config.db_port+"/daily_info",encoding='utf-8') | ||
279 | + self.engine_stock=create_engine("mysql+pymysql://"+self.config.db_id+":"+self.config.db_pw+"@"+ | ||
280 | + self.config.db_ip + ":" + self.config.db_port+"/stock_info",encoding='utf-8') | ||
281 | + self.engine_minute=create_engine("mysql+pymysql://"+self.config.db_id+":"+self.config.db_pw+"@"+ | ||
282 | + self.config.db_ip + ":" + self.config.db_port+"/minute_info",encoding='utf-8') | ||
283 | + | ||
284 | + # Bot Database가 존재하는지 확인하는 함수 | ||
285 | + def is_database_exist(self,cursor): | ||
286 | + query="select 1 from information_schema.schemata where schema_name='{}'" | ||
287 | + result=cursor.execute(query.format(self.db_name)) | ||
288 | + if result: | ||
289 | + return True | ||
290 | + else: | ||
291 | + return False | ||
292 | + | ||
293 | + # Bot Database를 생성하는 함수 | ||
294 | + def create_database(self,cursor): | ||
295 | + query="create database {}" | ||
296 | + cursor.execute(query.format(self.db_name)) | ||
297 | + | ||
298 | + # information 데이터베이스가 존재하는지 확인 | ||
299 | + # 존재하지 않는다면 새로 생성 | ||
300 | + def create_info_database(self,cursor): | ||
301 | + info_list=['daily_info','stock_info','minute_info'] | ||
302 | + query="select schema_name from information_schema.schemata" | ||
303 | + cursor.execute(query) | ||
304 | + result=cursor.fetchall() | ||
305 | + print(result) | ||
306 | + exist_list=[item[0].lower() for item in result] | ||
307 | + create_query="create database {}" | ||
308 | + has_created=False | ||
309 | + for db in info_list: | ||
310 | + if db not in exist_list: | ||
311 | + has_created=True | ||
312 | + logger.debug(db,"Database not exist. Create the Database.") | ||
313 | + cursor.execute(create_query.format(db)) | ||
314 | + logger.debug(db,"Creation Completed!") | ||
315 | + | ||
316 | + if has_created and self.engine_bot.has_table('setting_data'): | ||
317 | + print("exist") | ||
318 | + self.engine_bot.execute("update setting_data set code_update='0") | ||
319 | + | ||
320 | + def create_setting_data(self): | ||
321 | + df_data={"limit_money":[],"per_invest":[],"max_per_invest":[],"min_per_invest":[],"set_per_invest":[], | ||
322 | + "code_update":[],"today_finish":[],"balance_to_db":[],"posses_stocks":[],"today_profit":[], | ||
323 | + "contract_check":[],"db_to_daily_info":[],"today_buy_list":[],"stock_info":[],"daily_info":[]} | ||
324 | + df_setting_data=DataFrame(df_data, | ||
325 | + columns=['limit_money','per_invest','max_per_invest','min_per_invest','set_per_invest', | ||
326 | + 'code_update','today_finish','balance_to_db','posses_stocks','today_profit', | ||
327 | + 'contract_check','db_to_daily_info','today_buy_list','stock_info','daily_info']) | ||
328 | + | ||
329 | + df_setting_data.loc[0,'limit_money']=int(0) | ||
330 | + df_setting_data.loc[0,'per_invest']=int(0) | ||
331 | + df_setting_data.loc[0,'max_per_invest']=int(0) | ||
332 | + df_setting_data.loc[0,'min_per_invest']=int(0) | ||
333 | + df_setting_data.loc[0,'set_per_invest']=str(0) | ||
334 | + | ||
335 | + df_setting_data.loc[0,'code_update']=str(0) | ||
336 | + df_setting_data.loc[0,'today_finish']=str(0) | ||
337 | + df_setting_data.loc[0,'balance_to_db']=str(0) | ||
338 | + df_setting_data.loc[0,'posses_stocks']=str(0) | ||
339 | + df_setting_data.loc[0,'today_profit']=float(0) | ||
340 | + | ||
341 | + df_setting_data.loc[0,'contract_check']=str(0) | ||
342 | + df_setting_data.loc[0,'db_to_daily_info']=str(0) | ||
343 | + df_setting_data.loc[0,'today_buy_list']=str(0) | ||
344 | + df_setting_data.loc[0,'stock_info']=str(0) | ||
345 | + df_setting_data.loc[0,'daily_info']=str(0) | ||
346 | + | ||
347 | + df_setting_data.to_sql("setting_data",self.engine_bot,if_exists="replace") | ||
348 | + | ||
349 | + def set_simul_variable(self): | ||
350 | + self.latest_date=self.simul_api.get_latest_date() | ||
351 | + if not self.simul_api.is_table_exist(self.db_name,"transaction_history"): | ||
352 | + logger.debug("create transaction_history table") | ||
353 | + self.per_invest=0 | ||
354 | + self.create_transaction_history(0,0,0,0,0) | ||
355 | + self.delete_item_by_code(0) | ||
356 | + | ||
357 | + # transaction_history 테이블 생성 및 column 설정 | ||
358 | + def create_transaction_history(self,order_num,code,contract_check,purchase_price,rate): | ||
359 | + logger.debug("creating transaction_history table") | ||
360 | + self.set_date() | ||
361 | + self.simul_api.df_transaction_history() | ||
362 | + self.simul_api.df_th.loc[0,'order_num']=order_num | ||
363 | + self.simul_api.df_th.loc[0,'code']=str(code) | ||
364 | + self.simul_api.df_th.loc[0,'reate']=float(rate) | ||
365 | + self.simul_api.df_th.loc[0,'buy_date']=self.today_time | ||
366 | + self.simul_api.df_th.loc[0,'contract_check']=contract_check | ||
367 | + self.simul_api.df_th.loc[0,'per_invest']=self.per_invest | ||
368 | + self.simul_api.df_th.loc[0,'purchase_price']=purchase_price | ||
369 | + | ||
370 | + self.simul_api.df_th=self.simul_api.df_th.fillna(0) | ||
371 | + self.simul_api.df_th.to_sql('transaction_history',self.engine_bot,if_exists='append') | ||
372 | + | ||
373 | + def delete_item_by_code(self,code): | ||
374 | + query="delete from transaction_history where code='%s'" | ||
375 | + self.engine_bot.execute(query%code) | ||
376 | + | ||
377 | + # setting_data에 per_invest항목이 설정되어 있는지 확인하는 함수 | ||
378 | + def check_per_invest(self): | ||
379 | + query="select per_invest, set_per_invest from setting_data" | ||
380 | + result=self.engine_bot.execute(query).fetchall() | ||
381 | + if result[0][1]==self.today: | ||
382 | + self.per_invest=result[0][0] | ||
383 | + return True | ||
384 | + else: | ||
385 | + return False | ||
386 | + | ||
387 | + def set_per_invest(self): | ||
388 | + self.get_deposit() | ||
389 | + | ||
390 | + # 예수금 조회 및 저장 | ||
391 | + def get_deposit(self): | ||
392 | + self._set_input_value("계좌번호",self.account_no) | ||
393 | + self._set_input_value("비밀번호입력매체구분",00) | ||
394 | + self._set_input_value("조회구분",1) | ||
395 | + self._comm_rq_data("opw00001_req","opw00001",0,"2000") | ||
396 | + | ||
397 | + # 예수금 상세현황요청 함수 | ||
398 | + def _opw00001(self,sRQName,sTrCode): | ||
399 | + self.deposit=self._get_comm_data(sTrCode,sRQName,0,"d+2출금가능금액") | ||
400 | + self.deposit=int(self.deposit) | ||
401 | + | ||
402 | + def get_balance(self): | ||
403 | + self._set_input_value("계좌번호",self.account_no) | ||
404 | + self._comm_rq_data("opw00018_req","opw00018",0,"2000") | ||
405 | + # 다음페이지가 존재할 경우 계속해서 조회 | ||
406 | + while self.remained_data: | ||
407 | + self._set_input_value("계좌번호",self.account_no) | ||
408 | + self._comm_rq_data("opw00018_req","opw00018",2,"2000") | ||
409 | + | ||
410 | + def _opw00018(self,sRQName,sTrCode): | ||
411 | + self.total_purchase=self._get_comm_data(sTrCode,sRQName,0,"총매입금액") | ||
412 | + self.total_evaluated_price=self._get_comm_data(sTrCode,sRQName,0,"총평가금액") | ||
413 | + self.total_valuation=self._get_comm_data(sTrCode,sRQName,0,"총평가손익금액") | ||
414 | + self.earning_rate=self._get_comm_data(sTrCode,sRQName,0,"총수익률(%)") | ||
415 | + self.estimated_deposit=self._get_comm_data(sTrCode,sRQName,0,"추정예탁자산") | ||
416 | + | ||
417 | + self.total_purchase=int(self.total_purchase) | ||
418 | + self.total_evaluated_price=int(self.total_evaluated_price) | ||
419 | + self.total_valuation=int(self.total_valuation) | ||
420 | + self.earning_rate=float(self.earning_rate) | ||
421 | + self.estimated_deposit=int(self.estimated_deposit) | ||
422 | + | ||
423 | +if __name__=="__main__": | ||
424 | + app=QApplication(sys.argv) | ||
425 | + a=Open_Api() | ||
426 | + a.get_balance() | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
1 | +from sqlalchemy import * | ||
2 | +from PyQt5.QtCore import * | ||
3 | +import datetime | ||
4 | + | ||
5 | +import config | ||
6 | + | ||
1 | class StockInfo(): | 7 | class StockInfo(): |
2 | - def __init__(self): | 8 | + def __init__(self,db_name,stock_info_name,daily_info_name): |
9 | + if db_name!=0: | ||
10 | + self.db_name=db_name | ||
11 | + self.stockInfo_name=stock_info_name | ||
12 | + self.dailyInfo_name=daily_info_name | ||
13 | + | ||
14 | + self.engine=create_engine( | ||
15 | + "mysql+pymysql://" + config.db_id + ":" + config.db_pw + "@" + config.db_ip + ":" + config.db_port + | ||
16 | + "/stock_info", encoding='utf-8') | ||
17 | + self.conn=self.engine.connect() | ||
18 | + | ||
19 | + self.set_variable() | ||
20 | + | ||
21 | + # 변수 설정 | ||
22 | + def set_variable(self): | ||
23 | + self.mkt_start_time=QTime(9,0,0) | ||
24 | + self.mkt_end_time=QTime(15,31,0) | ||
25 | + | ||
26 | + self.today=datetime.datetime.today().strftime("%Y%m%d") | ||
27 | + | ||
28 | + # 현재 시간이 주식장이 열린 시간인지 확인하는 함수 | ||
29 | + def time_check(self): | ||
30 | + self.current_time=QTime.currentTime() | ||
31 | + if (self.current_time>self.mkt_start_time and self.current_time<self.mkt_end_time): | ||
32 | + return True | ||
33 | + else: | ||
34 | + return False | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
-
Please register or login to post a comment