Showing
13 changed files
with
181 additions
and
9 deletions
... | @@ -15,6 +15,7 @@ from library.open_api import setup_sql_mod | ... | @@ -15,6 +15,7 @@ from library.open_api import setup_sql_mod |
15 | listen(Pool,'connect',setup_sql_mod) | 15 | listen(Pool,'connect',setup_sql_mod) |
16 | listen(Pool,'first_connect',setup_sql_mod) | 16 | listen(Pool,'first_connect',setup_sql_mod) |
17 | 17 | ||
18 | + | ||
18 | def filter_by_ai(db_name,simul_num): | 19 | def filter_by_ai(db_name,simul_num): |
19 | from library.simulator_api import simulator_api | 20 | from library.simulator_api import simulator_api |
20 | sf=simulator_api(simul_num,'real',db_name) | 21 | sf=simulator_api(simul_num,'real',db_name) |
... | @@ -22,3 +23,119 @@ def filter_by_ai(db_name,simul_num): | ... | @@ -22,3 +23,119 @@ def filter_by_ai(db_name,simul_num): |
22 | ai_filter(sf.ai_num,sf.engine_simul) | 23 | ai_filter(sf.ai_num,sf.engine_simul) |
23 | except AttributeError: | 24 | except AttributeError: |
24 | sys.exit(1) | 25 | sys.exit(1) |
26 | + | ||
27 | + | ||
28 | +def lstm_algorithm(dataset,ai_setting): | ||
29 | + shuffled_data=load_data(df=dataset.copy(),n_steps=ai_setting['n_steps'],test_size=ai_setting['test_size']) | ||
30 | + model=create_model(n_steps=ai_setting['n_steps'],loss=ai_setting['loss'],units=ai_setting['units'], | ||
31 | + n_layers=ai_setting['n_layers'],dropout=ai_setting['dropout']) | ||
32 | + early_stopping=EarlyStopping(monitor='val_loss',patience=50) | ||
33 | + model.fit(shuffled_data["X_train"],shuffled_data['y_train'], | ||
34 | + batch_size=ai_setting['batch_size'], | ||
35 | + epochs=ai_setting['epochs'], | ||
36 | + validation_data=(shuffled_data['X_test'],shuffled_data['y_test']), | ||
37 | + callbacks=[early_stopping], | ||
38 | + verbose=1) | ||
39 | + scaled_data=load_data(df=dataset.copy(),n_steps=ai_setting['n_steps'],test_size=ai_setting['test_size'], | ||
40 | + shuffle=False) | ||
41 | + mse=evaluate(scaled_data,model) | ||
42 | + | ||
43 | + future_price=predict(scaled_data,model,n_steps=ai_setting['n_steps']) | ||
44 | + predicted_y=model.predict(scaled_data['X_test']) | ||
45 | + real_y=np.squeeze(scaled_data['column_scaler']['close'].inverse_transform(predicted_y)) | ||
46 | + | ||
47 | + if ai_setting['is_used_predicted_close']: | ||
48 | + close=real_y[-1] | ||
49 | + else: | ||
50 | + close=dataset.iloc[-1]['close'] | ||
51 | + | ||
52 | + ratio=(future_price-close)/close*100 | ||
53 | + | ||
54 | + msg = f"After {ai_setting['lookup_step']}: {int(close)} -> {int(future_price)}" | ||
55 | + | ||
56 | + if ratio > 0: # lookup_step(분, 일) 후 상승 예상일 경우 출력 메시지 | ||
57 | + msg += f' {ratio:.2f}% ⯅ ' | ||
58 | + elif ratio < 0: # lookup_step(분, 일) 후 하락 예상일 경우 출력 메시지 | ||
59 | + msg += f' {ratio:.2f}% ⯆ ' | ||
60 | + print(msg, end=' ') | ||
61 | + | ||
62 | + return ai_setting['ratio_cut'] | ||
63 | + | ||
64 | + | ||
65 | +def create_training_engine(db_name): | ||
66 | + return pymysql.connect( | ||
67 | + host=cf.db_ip, | ||
68 | + port=int(cf.db_port), | ||
69 | + user=cf.db_id, | ||
70 | + password=cf.db_pw, | ||
71 | + db=db_name, | ||
72 | + charset='utf8mb4', | ||
73 | + cursorclass=pymysql.cursors.DictCursor | ||
74 | + ) | ||
75 | + | ||
76 | + | ||
77 | +def ai_filter(ai_filter_num,engine,until=datetime.datetime.today()): | ||
78 | + if ai_filter_num == 1: | ||
79 | + ai_setting = { | ||
80 | + "n_steps": 100, # 시퀀스 데이터를 몇개씩 담을지 설정 | ||
81 | + "lookup_step": 30, # 단위 : 몇 일(분) 뒤의 종가를 예측 할 것 인지 | ||
82 | + "test_size": 0.2, # train 범위 | ||
83 | + "n_layers": 4, # LSTM layer 개수 | ||
84 | + "units": 50, # LSTM neurons 개수 | ||
85 | + "dropout": 0.2, # overfitting 방지를 위한 dropout | ||
86 | + "loss": "mse", # loss : 최적화 과정에서 최소화될 손실 함수 | ||
87 | + "optimizer": "adam", # optimizer : 최적화 알고리즘 선택 | ||
88 | + "batch_size": 64, # 각 학습 반복에 사용할 데이터 샘플 수 | ||
89 | + "epochs": 400, # 몇 번 테스트 할지 | ||
90 | + "ratio_cut": 3, # 단위:(%) lookup_step 기간 뒤 ratio_cut(%) 만큼 증가 할 것이 예측 된다면 매수 | ||
91 | + "table": "daily_craw", # 분석 시 daily_craw(일별데이터)를 이용 할지 min_craw(분별데이터)를 이용 할지 | ||
92 | + "is_used_predicted_close" : True # ratio(예상 상승률) 계산 시 예측 그래프의 close 값을 이용 할 경우 True, | ||
93 | + # 실제 close 값을 이용할 시 False | ||
94 | + } | ||
95 | + tr_engine = create_training_engine(ai_setting['table']) | ||
96 | + | ||
97 | + buy_list=None | ||
98 | + | ||
99 | + try: | ||
100 | + query="SELECT DISTINCT code_name FROM realtime_daily_buy_list" | ||
101 | + buy_list=engine.execute(query).fetchall() | ||
102 | + except (InternalError,ProgrammingError) as err: | ||
103 | + if 'Table' in str(err): | ||
104 | + print("realtime_daily_buy_list 테이블이 존재하지 않습니다.") | ||
105 | + else: | ||
106 | + print("autobot 데이터베이스가 존재하지 않습니다.") | ||
107 | + exit(1) | ||
108 | + | ||
109 | + feature_columns = ["close", "volume", "open", "high", "low"] | ||
110 | + filtered_list = [] | ||
111 | + for code_name in buy_list: | ||
112 | + sql = """ | ||
113 | + SELECT {} FROM `{}` | ||
114 | + WHERE STR_TO_DATE(date, '%Y%m%d%H%i') <= '{}' | ||
115 | + """.format(','.join(feature_columns), code_name, until) | ||
116 | + df = pd.read_sql(sql, tr_engine) | ||
117 | + | ||
118 | + # 데이터가 1000개(1000일 or 1000분)가 넘지 않으면 예측도가 떨어지기 때문에 필터링 | ||
119 | + if len(df) < 1000: | ||
120 | + filtered_list.append(code_name) | ||
121 | + print(f"테스트 데이터가 적어서 realtime_daily_buy_list 에서 제외") | ||
122 | + continue | ||
123 | + try: | ||
124 | + filtered = lstm_algorithm(df, ai_setting) | ||
125 | + except ValueError: | ||
126 | + print(f"테스트 데이터가 적어서 realtime_daily_buy_list 에서 제외") | ||
127 | + filtered_list.append(code_name) | ||
128 | + continue | ||
129 | + | ||
130 | + print(code_name) | ||
131 | + | ||
132 | + # filtered가 True 이면 filtered_list(필터링 종목)에 해당 종목을 append | ||
133 | + if filtered: | ||
134 | + print(f"기준에 부합하지 않으므로 realtime_daily_buy_list 에서 제외") | ||
135 | + filtered_list.append(code_name) | ||
136 | + | ||
137 | + # filtered_list에 있는 종목들을 realtime_daily_buy_list(매수리스트)에서 제거 | ||
138 | + if len(filtered_list) > 0: | ||
139 | + engine.execute(f""" | ||
140 | + DELETE FROM realtime_daily_buy_list WHERE code_name in ({','.join(map('"{}"'.format, filtered_list))}) | ||
141 | + """) | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
This diff is collapsed. Click to expand it.
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
... | @@ -326,7 +326,8 @@ class collector_api(): | ... | @@ -326,7 +326,8 @@ class collector_api(): |
326 | "clo5_diff_rate", "clo10_diff_rate","clo20_diff_rate", "clo60_diff_rate", | 326 | "clo5_diff_rate", "clo10_diff_rate","clo20_diff_rate", "clo60_diff_rate", |
327 | "clo120_diff_rate", | 327 | "clo120_diff_rate", |
328 | 'yes_clo5', 'yes_clo10', 'yes_clo20', 'yes_clo60', 'yes_clo120', | 328 | 'yes_clo5', 'yes_clo10', 'yes_clo20', 'yes_clo60', 'yes_clo120', |
329 | - 'vol5', 'vol10', 'vol20', 'vol60', 'vol120', 'vol80' | 329 | + 'vol5', 'vol10', 'vol20', 'vol60', 'vol120', |
330 | + 'd1_diff','dn_diff' | ||
330 | ]) | 331 | ]) |
331 | 332 | ||
332 | df_temp = df_temp.sort_values(by=['date'], ascending=True) | 333 | df_temp = df_temp.sort_values(by=['date'], ascending=True) |
... | @@ -365,6 +366,9 @@ class collector_api(): | ... | @@ -365,6 +366,9 @@ class collector_api(): |
365 | df_temp['vol60'] = df_temp['volume'].rolling(window=60).mean() | 366 | df_temp['vol60'] = df_temp['volume'].rolling(window=60).mean() |
366 | df_temp['vol120'] = df_temp['volume'].rolling(window=120).mean() | 367 | df_temp['vol120'] = df_temp['volume'].rolling(window=120).mean() |
367 | 368 | ||
369 | + df_temp['d1_diff']=df_temp['close']-df_temp['close'].shift(1) | ||
370 | + df_temp['dn_diff']=df_temp['close']-df_temp['close'].shift(14) | ||
371 | + | ||
368 | # daily_craw테이블이 존재할 경우, 저장되어있는 날짜 이후의 값을 저장 | 372 | # daily_craw테이블이 존재할 경우, 저장되어있는 날짜 이후의 값을 저장 |
369 | if self.engine_bot.dialect.has_table(self.open_api.engine_daily_craw, code_name): | 373 | if self.engine_bot.dialect.has_table(self.open_api.engine_daily_craw, code_name): |
370 | df_temp = df_temp[df_temp.date > self.open_api.get_daily_craw_db_last_date(code_name)] | 374 | df_temp = df_temp[df_temp.date > self.open_api.get_daily_craw_db_last_date(code_name)] |
... | @@ -587,3 +591,39 @@ class collector_api(): | ... | @@ -587,3 +591,39 @@ class collector_api(): |
587 | self.open_api.comm_rq_data("opt10074_req", "opt10074", 2, "0329") | 591 | self.open_api.comm_rq_data("opt10074_req", "opt10074", 2, "0329") |
588 | 592 | ||
589 | self.db_to_jango() | 593 | self.db_to_jango() |
594 | + | ||
595 | + def get_code_list(self): | ||
596 | + self.dc.get_item_kospi() | ||
597 | + self.stock_to_db(self.dc.code_df_kospi,"item_all") | ||
598 | + | ||
599 | + def stock_to_db(self,origin_df,type): | ||
600 | + checking_stocks=['kospi'] | ||
601 | + stock_df=DataFrame() | ||
602 | + stock_df['code']=origin_df['code'] | ||
603 | + name_list=[] | ||
604 | + for info in origin_df.itertuples(): | ||
605 | + kiwoom_name = self.open_api.dynamicCall("GetMasterCodeName(QString)", info.code).strip() | ||
606 | + name_list.append(kiwoom_name) | ||
607 | + if not kiwoom_name: | ||
608 | + if type in checking_stocks: | ||
609 | + logger.error( | ||
610 | + f"종목명이 비어있습니다. - " | ||
611 | + f"종목: {info.code_name}, " | ||
612 | + f"코드: {info.code}" | ||
613 | + ) | ||
614 | + | ||
615 | + stock_df['code_name']=name_list | ||
616 | + stock_df['check_item']=0 | ||
617 | + | ||
618 | + if type in checking_stocks: | ||
619 | + stock_df=stock_df[stock_df['code_name'].map(len)>0] | ||
620 | + | ||
621 | + if type=="item_all": | ||
622 | + stock_df['check_daily_crawler']='0' | ||
623 | + stock_df['check_min_crawler']='0' | ||
624 | + | ||
625 | + dtypes=dict(zip(list(stock_df.columns),[Text]*len(stock_df.columns))) | ||
626 | + dtypes['check_item']=Integer | ||
627 | + | ||
628 | + stock_df.to_sql(f'stock_{type}',self.open_api.engine_daily_buy_list,if_exists='replace',dtype=dtypes) | ||
629 | + return stock_df | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -64,7 +64,7 @@ class daily_buy_list(): | ... | @@ -64,7 +64,7 @@ class daily_buy_list(): |
64 | if not self.is_table_exist_daily_craw(code, code_name): | 64 | if not self.is_table_exist_daily_craw(code, code_name): |
65 | continue | 65 | continue |
66 | 66 | ||
67 | - query = f"select * from {self.stock_item_all[i][0]} where date = '{self.date_rows[k][0]}' " \ | 67 | + query = f"select * from `{self.stock_item_all[i][0]}` where date = '{self.date_rows[k][0]}' " \ |
68 | f"group by date" | 68 | f"group by date" |
69 | rows = self.engine_daily_craw.execute(query).fetchall() | 69 | rows = self.engine_daily_craw.execute(query).fetchall() |
70 | multi_list += rows | 70 | multi_list += rows |
... | @@ -72,13 +72,14 @@ class daily_buy_list(): | ... | @@ -72,13 +72,14 @@ class daily_buy_list(): |
72 | if len(multi_list) != 0: | 72 | if len(multi_list) != 0: |
73 | df_temp = DataFrame(multi_list, | 73 | df_temp = DataFrame(multi_list, |
74 | columns=['index', 'date', 'check_item', 'code', 'code_name', | 74 | columns=['index', 'date', 'check_item', 'code', 'code_name', |
75 | - 'd1_diff','d1_diff_rate', | 75 | + 'd1_diff_rate', |
76 | 'close', 'open', 'high', 'low','volume', | 76 | 'close', 'open', 'high', 'low','volume', |
77 | 'clo5', 'clo10', 'clo20', 'clo60', 'clo120', | 77 | 'clo5', 'clo10', 'clo20', 'clo60', 'clo120', |
78 | "clo5_diff_rate", "clo10_diff_rate","clo20_diff_rate", | 78 | "clo5_diff_rate", "clo10_diff_rate","clo20_diff_rate", |
79 | "clo60_diff_rate", "clo120_diff_rate", | 79 | "clo60_diff_rate", "clo120_diff_rate", |
80 | 'yes_clo5', 'yes_clo10', 'yes_clo20', 'yes_clo60', 'yes_clo120', | 80 | 'yes_clo5', 'yes_clo10', 'yes_clo20', 'yes_clo60', 'yes_clo120', |
81 | - 'vol5', 'vol10', 'vol20', 'vol60', 'vol120' | 81 | + 'vol5', 'vol10', 'vol20', 'vol60', 'vol120', |
82 | + 'd1_diff','dn_diff' | ||
82 | ]) | 83 | ]) |
83 | 84 | ||
84 | df_temp.to_sql(name=self.date_rows[k][0], con=self.engine_daily_buy_list, if_exists='replace') | 85 | df_temp.to_sql(name=self.date_rows[k][0], con=self.engine_daily_buy_list, if_exists='replace') | ... | ... |
... | @@ -47,3 +47,15 @@ class daily_crawler(): | ... | @@ -47,3 +47,15 @@ class daily_crawler(): |
47 | return True | 47 | return True |
48 | else: | 48 | else: |
49 | return False | 49 | return False |
50 | + | ||
51 | + def get_item_kospi(self): | ||
52 | + self.code_df_kospi = pd.read_html('http://kind.krx.co.kr/corpgeneral/corpList.do?method=download&searchType=13&marketType=stockMkt',header=0)[0] # 종목코드가 6자리이기 때문에 6자리를 맞춰주기 위해 설정해줌 | ||
53 | + | ||
54 | + # 6자리 만들고 앞에 0을 붙인다. | ||
55 | + self.code_df_kospi.종목코드 = self.code_df_kospi.종목코드.map('{:06d}'.format) | ||
56 | + | ||
57 | + # 회사명과 종목코드를 제외한 칼럼은 삭제 | ||
58 | + self.code_df_kospi = self.code_df_kospi[['회사명', '종목코드']] | ||
59 | + | ||
60 | + # 한글로된 컬럼명을 영어로 변경 | ||
61 | + self.code_df_kospi = self.code_df_kospi.rename(columns={'회사명': 'code_name', '종목코드': 'code'}) | ... | ... |
... | @@ -126,6 +126,7 @@ class open_api(QAxWidget): | ... | @@ -126,6 +126,7 @@ class open_api(QAxWidget): |
126 | self.login_event_loop.exit() | 126 | self.login_event_loop.exit() |
127 | except Exception as e: | 127 | except Exception as e: |
128 | logger.critical(e) | 128 | logger.critical(e) |
129 | + sys.exit() | ||
129 | 130 | ||
130 | # 사용자의 계좌정보 저장 및 출력 | 131 | # 사용자의 계좌정보 저장 및 출력 |
131 | def account_info(self): | 132 | def account_info(self): |
... | @@ -243,12 +244,12 @@ class open_api(QAxWidget): | ... | @@ -243,12 +244,12 @@ class open_api(QAxWidget): |
243 | # 해당 종목의 체결 실패 내역이 없다면 | 244 | # 해당 종목의 체결 실패 내역이 없다면 |
244 | # all_stocks 테이블에 업데이트. 정상 체결 시 chegyul_check=0 | 245 | # all_stocks 테이블에 업데이트. 정상 체결 시 chegyul_check=0 |
245 | if chegyul_fail_amount=="0": | 246 | if chegyul_fail_amount=="0": |
246 | - logger.debug(code, "체결 완료") | 247 | + logger.debug(code+ "체결 완료") |
247 | self.db_to_all_stocks(order_num,code,0,purchase_price,0) | 248 | self.db_to_all_stocks(order_num,code,0,purchase_price,0) |
248 | # 체결 실패 내역이 존재한다면 | 249 | # 체결 실패 내역이 존재한다면 |
249 | # all_stocks 테이블에 업데이트. 미체결 시 chegyul_check=1 | 250 | # all_stocks 테이블에 업데이트. 미체결 시 chegyul_check=1 |
250 | else: | 251 | else: |
251 | - logger.debug(code,"미체결") | 252 | + logger.debug(code+"미체결") |
252 | self.db_to_all_stocks(order_num,code,1,purchase_price,0) | 253 | self.db_to_all_stocks(order_num,code,1,purchase_price,0) |
253 | 254 | ||
254 | # 매수하는 경우 | 255 | # 매수하는 경우 |
... | @@ -281,7 +282,7 @@ class open_api(QAxWidget): | ... | @@ -281,7 +282,7 @@ class open_api(QAxWidget): |
281 | # 국내주식 잔고전달 | 282 | # 국내주식 잔고전달 |
282 | elif sGubun=="1": | 283 | elif sGubun=="1": |
283 | chegyul_fail_amount=self.get_chejan_data(902) | 284 | chegyul_fail_amount=self.get_chejan_data(902) |
284 | - logger.debug("미체결 수량",chegyul_fail_amount) | 285 | + logger.debug("미체결 수량 : "+chegyul_fail_amount) |
285 | 286 | ||
286 | else: | 287 | else: |
287 | logger.debug("Invlid _receive_chejan_data") | 288 | logger.debug("Invlid _receive_chejan_data") |
... | @@ -784,7 +785,7 @@ class open_api(QAxWidget): | ... | @@ -784,7 +785,7 @@ class open_api(QAxWidget): |
784 | self.set_input_value("계좌번호",self.account_no) | 785 | self.set_input_value("계좌번호",self.account_no) |
785 | self.comm_rq_data("opt10076_req","opt10076",0,"0350") | 786 | self.comm_rq_data("opt10076_req","opt10076",0,"0350") |
786 | 787 | ||
787 | - query=f"update all_stocks set chegyul_check='0' where code='{r.code}' and sell_data='0' " \ | 788 | + query=f"update all_stocks set chegyul_check='0' where code='{r.code}' and sell_date='0' " \ |
788 | f"order by buy_date desc limit 1" | 789 | f"order by buy_date desc limit 1" |
789 | # 과거에 거래한 내역이 존재하는 경우 opt10076 조회 시 주문번호 등의 데이터가 존재하지 않음 | 790 | # 과거에 거래한 내역이 존재하는 경우 opt10076 조회 시 주문번호 등의 데이터가 존재하지 않음 |
790 | # 거래가 완료된 항목에 대해서 contract_check항목을 '0'으로 업데이트 | 791 | # 거래가 완료된 항목에 대해서 contract_check항목을 '0'으로 업데이트 | ... | ... |
... | @@ -14,7 +14,7 @@ class Trader(QMainWindow): | ... | @@ -14,7 +14,7 @@ class Trader(QMainWindow): |
14 | 14 | ||
15 | self.market_start_time=QTime(9,0,0) # 장시작 시간 | 15 | self.market_start_time=QTime(9,0,0) # 장시작 시간 |
16 | self.market_end_time=QTime(15,30,0) # 장마감 시간 | 16 | self.market_end_time=QTime(15,30,0) # 장마감 시간 |
17 | - self.buy_end_time=QTime(9,6,0) # 매수를 몇 시까지 할지 | 17 | + self.buy_end_time=QTime(9,30,0) # 매수를 몇 시까지 할지 |
18 | 18 | ||
19 | # 매수 함수 | 19 | # 매수 함수 |
20 | def auto_trade_stock(self): | 20 | def auto_trade_stock(self): |
... | @@ -42,6 +42,7 @@ class Trader(QMainWindow): | ... | @@ -42,6 +42,7 @@ class Trader(QMainWindow): |
42 | self.open_api.final_chegyul_check() | 42 | self.open_api.final_chegyul_check() |
43 | # 매도리스트 저장 | 43 | # 매도리스트 저장 |
44 | self.get_sell_list_trade() | 44 | self.get_sell_list_trade() |
45 | + print(self.sell_list) | ||
45 | 46 | ||
46 | for i in range(len(self.sell_list)): | 47 | for i in range(len(self.sell_list)): |
47 | get_sell_code=self.sell_list[i][0] # 종목코드 | 48 | get_sell_code=self.sell_list[i][0] # 종목코드 | ... | ... |
-
Please register or login to post a comment