updata updatekline.py
This commit is contained in:
@@ -10,18 +10,20 @@
|
||||
—— 根据股票代码,获取股票历史数据
|
||||
—— 根据股票代码,获取流通股数量
|
||||
—— 根据股票代码,新建/更新数据表
|
||||
—— 更新完成需检查 ,数据是否完整 -> checktable.py 操作界面做好了之后,再融合到整体代码中
|
||||
"""
|
||||
from futu import *
|
||||
from pymysql import Error
|
||||
from MySQLHelper import MySQLHelper # MySQLHelper类保存为单独文件
|
||||
from LogHelper import LogHelper
|
||||
from datetime import datetime
|
||||
import logging
|
||||
from typing import Optional, List, Dict, Union, Tuple
|
||||
import time
|
||||
from typing import Optional, List, Dict
|
||||
from ConditionalSelection import FutuStockFilter
|
||||
from tqdm import tqdm
|
||||
import pandas as pd
|
||||
import time
|
||||
|
||||
# 基本用法(自动创建日期日志+控制台输出)
|
||||
logger = LogHelper(logger_name = 'KLine').setup()
|
||||
|
||||
def get_market_data(market: Market) -> List[str]:
|
||||
"""
|
||||
@@ -39,13 +41,13 @@ def get_market_data(market: Market) -> List[str]:
|
||||
if ret == RET_OK:
|
||||
# 提取code列并转换为列表
|
||||
codes = data['code'].astype(str).tolist()
|
||||
logging.info(f"获取到 {market} 市场 {len(codes)} 个股票代码")
|
||||
logger.info(f"获取到 {market} 市场 {len(codes)} 个股票代码")
|
||||
return codes
|
||||
else:
|
||||
logging.error(f"获取股票代码失败: {data}")
|
||||
logger.error(f"获取股票代码失败: {data}")
|
||||
return []
|
||||
except Exception as e:
|
||||
logging.error(f"获取股票代码时发生异常: {str(e)}")
|
||||
logger.error(f"获取股票代码时发生异常: {str(e)}")
|
||||
return []
|
||||
finally:
|
||||
quote_ctx.close()
|
||||
@@ -89,7 +91,7 @@ def preprocess_quote_data(df: pd.DataFrame, float_share: Optional[int] = None) -
|
||||
}
|
||||
processed_data.append(item)
|
||||
except Exception as e:
|
||||
logging.warning(f"处理行情数据时跳过异常行 {row.get('code', '未知')}: {str(e)}")
|
||||
logger.warning(f"处理行情数据时跳过异常行 {row.get('code', '未知')}: {str(e)}")
|
||||
continue
|
||||
|
||||
return processed_data
|
||||
@@ -115,7 +117,7 @@ def get_float_share(quote_ctx: OpenQuoteContext, code: str) -> Optional[int]:
|
||||
return int(float_share)
|
||||
return None
|
||||
except Exception as e:
|
||||
logging.error(f"获取股票 {code} 的流通股数量失败: {str(e)}")
|
||||
logger.error(f"获取股票 {code} 的流通股数量失败: {str(e)}")
|
||||
return None
|
||||
|
||||
def get_float_share_data(db_config: dict, table_name: str) -> Optional[Dict[str, int]]:
|
||||
@@ -139,7 +141,7 @@ def get_float_share_data(db_config: dict, table_name: str) -> Optional[Dict[str,
|
||||
""")
|
||||
|
||||
if not data:
|
||||
logging.error(f"表 {table_name} 中没有流通股本数据")
|
||||
logger.error(f"表 {table_name} 中没有流通股本数据")
|
||||
return None
|
||||
|
||||
# 构建股票代码到流通股数量的映射字典
|
||||
@@ -150,11 +152,11 @@ def get_float_share_data(db_config: dict, table_name: str) -> Optional[Dict[str,
|
||||
if stock_code and float_share is not None:
|
||||
float_share_dict[stock_code] = int(float_share)
|
||||
|
||||
logging.info(f"成功从 {table_name} 表加载 {len(float_share_dict)} 条流通股数据")
|
||||
logger.info(f"成功从 {table_name} 表加载 {len(float_share_dict)} 条流通股数据")
|
||||
return float_share_dict
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"从数据库读取流通股本数据失败: {str(e)}")
|
||||
logger.error(f"从数据库读取流通股本数据失败: {str(e)}")
|
||||
return None
|
||||
|
||||
def save_quotes_to_db(db_config: dict, quote_data: pd.DataFrame, table_name: str = 'stock_quotes', float_share: Optional[int] = None) -> bool:
|
||||
@@ -173,7 +175,7 @@ def save_quotes_to_db(db_config: dict, quote_data: pd.DataFrame, table_name: str
|
||||
# 预处理数据
|
||||
processed_data = preprocess_quote_data(quote_data, float_share)
|
||||
if not processed_data:
|
||||
logging.error("没有有效数据需要保存")
|
||||
logger.error("没有有效数据需要保存")
|
||||
return False
|
||||
|
||||
# 动态生成SQL插入语句
|
||||
@@ -230,13 +232,13 @@ def save_quotes_to_db(db_config: dict, quote_data: pd.DataFrame, table_name: str
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='股票行情数据表'
|
||||
"""
|
||||
db.execute_update(create_table_sql)
|
||||
logging.info(f"创建了新表: {table_name}")
|
||||
logger.info(f"创建了新表: {table_name}")
|
||||
|
||||
affected_rows = db.execute_many(insert_sql, processed_data)
|
||||
logging.info(f"成功插入/更新 {affected_rows} 条行情记录到表 {table_name}")
|
||||
logger.info(f"成功插入/更新 {affected_rows} 条行情记录到表 {table_name}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logging.error(f"保存行情数据到表 {table_name} 失败: {str(e)}")
|
||||
logger.error(f"保存行情数据到表 {table_name} 失败: {str(e)}")
|
||||
return False
|
||||
|
||||
def read_single_account_stock_codes(file_path='data\missing_tables_小航富途.txt'):
|
||||
@@ -262,22 +264,28 @@ def get_stock_codes() -> List[str]:
|
||||
results = db.execute_query(sql)
|
||||
return [row['stock_code'] for row in results if row['stock_code']]
|
||||
except Exception as e:
|
||||
logging.error(f"获取股票代码失败: {str(e)}")
|
||||
logger.error(f"获取股票代码失败: {str(e)}")
|
||||
return []
|
||||
|
||||
def write_missing_codes_to_txt(missing_codes: list, filename: str = "config\HK_futu.txt"):
|
||||
"""将缺失的股票代码追加到TXT文件,如果文件不存在则创建"""
|
||||
try:
|
||||
# 确保目录存在
|
||||
directory = os.path.dirname(filename)
|
||||
if directory and not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
logger.info(f"创建目录: {directory}")
|
||||
|
||||
# # 检查文件是否存在
|
||||
# file_exists = os.path.exists(filename)
|
||||
with open(filename, 'a', encoding='utf-8') as f:
|
||||
for code in missing_codes:
|
||||
f.write(f"\n{code}")
|
||||
logger.info(f"已将 {len(missing_codes)} 个缺失表对应的股票代码写入 {filename}")
|
||||
except Exception as e:
|
||||
logger.error(f"写入TXT文件失败: {str(e)}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
# 基本用法(自动创建日期日志+控制台输出)
|
||||
logger = LogHelper().setup()
|
||||
|
||||
# # 高级用法(自定义配置)
|
||||
# logger = LogHelper(
|
||||
# level=logging.DEBUG,
|
||||
# log_dir="my_logs",
|
||||
# format='%(levelname)s - %(message)s'
|
||||
# ).setup()
|
||||
|
||||
# 数据库配置
|
||||
db_config = {
|
||||
'host': 'localhost',
|
||||
@@ -286,19 +294,25 @@ if __name__ == "__main__":
|
||||
'database': 'hk_kline_1d'
|
||||
}
|
||||
|
||||
# 创建导入器并运行, 用于每日更新流通股数量
|
||||
# 创建导入器并运行, 用于每日更新流通股数量 -> 操作界面中增加一个开关,每天只需要更新一次
|
||||
futuStockFilter = FutuStockFilter(db_config)
|
||||
futuStockFilter.run_direct_import()
|
||||
|
||||
# 每个账号获取的数据独立开来
|
||||
# 每个账号获取的数据独立开来 -> 操作见面可以选择
|
||||
market_data_all = get_stock_codes()
|
||||
market_data_hang = read_single_account_stock_codes('config\hang_futu.txt')
|
||||
market_data_kevin= read_single_account_stock_codes('config\kevin_futu.txt')
|
||||
|
||||
market_data = list(set(market_data_all) - set(market_data_hang) - set(market_data_kevin))
|
||||
market_data_HK= read_single_account_stock_codes('config\HK_futu.txt')
|
||||
market_data_new = list(set(market_data_all) - set(market_data_hang) - set(market_data_kevin) - set(market_data_HK))
|
||||
|
||||
# 每天收盘后更新数据
|
||||
start_date = (datetime.now() - timedelta(days = 3 * 365)).strftime("%Y-%m-%d")
|
||||
# write_missing_codes_to_txt(market_data_new) # 新股票添加到文件中 -> 暂时手动设置
|
||||
|
||||
# 动态调整
|
||||
"""*********************************************"""
|
||||
market_data = market_data_new
|
||||
|
||||
# 每天收盘后更新数据 -> 操作界面中,这个参数需要放出来
|
||||
start_date = (datetime.now() - timedelta(days = 5)).strftime("%Y-%m-%d")
|
||||
end_date = (datetime.now() + timedelta(days = 1)).strftime("%Y-%m-%d")
|
||||
|
||||
# 获取流通股数据字典
|
||||
@@ -319,7 +333,7 @@ if __name__ == "__main__":
|
||||
if float_share is not None:
|
||||
logger.info(f"从API获取股票 {code} 的流通股数量: {float_share}")
|
||||
else:
|
||||
logging.warning(f"无法获取股票 {code} 的流通股数量")
|
||||
logger.warning(f"无法获取股票 {code} 的流通股数量")
|
||||
|
||||
# 获取历史K线数据
|
||||
ret, data, page_req_key = quote_ctx.request_history_kline(code, start=start_date, end=end_date, max_count=100)
|
||||
@@ -341,9 +355,9 @@ if __name__ == "__main__":
|
||||
else:
|
||||
logger.error(f'error:{ data}')
|
||||
|
||||
time.sleep(1)
|
||||
time.sleep(0.7)
|
||||
|
||||
if isWhile == False:
|
||||
time.sleep(1)
|
||||
time.sleep(0.7)
|
||||
|
||||
quote_ctx.close() # 结束后记得关闭当条连接,防止连接条数用尽
|
||||
|
||||
Reference in New Issue
Block a user