""" 该代码用于更新日 K 数据 """ from futu import * from pymysql import Error from MySQLHelper import MySQLHelper # MySQLHelper类保存为单独文件 from datetime import datetime import logging from typing import Optional, List, Dict, Union, Tuple import time import akshare as ak def get_market_data(market: Market) -> List[str]: """ 从Futu API获取指定市场的股票代码列表 Args: market (Market): 市场枚举值,如 Market.SH, Market.SZ Returns: List[str]: 股票代码列表 """ quote_ctx = OpenQuoteContext(host='127.0.0.1', port=11111) try: ret, data = quote_ctx.get_stock_basicinfo(market, SecurityType.STOCK) if ret == RET_OK: # 提取code列并转换为列表 codes = data['code'].astype(str).tolist() logging.info(f"获取到 {market} 市场 {len(codes)} 个股票代码") return codes else: logging.error(f"获取股票代码失败: {data}") return [] except Exception as e: logging.error(f"获取股票代码时发生异常: {str(e)}") return [] finally: quote_ctx.close() def preprocess_quote_data(df: pd.DataFrame) -> List[Dict]: """ 预处理行情数据,转换为适合数据库存储的格式 Args: df (pd.DataFrame): 原始行情数据DataFrame Returns: List[Dict]: 处理后的数据列表 """ processed_data = [] for _, row in df.iterrows(): try: # 提取市场标识 # market = row['code'].split('.')[0] if '.' in row['code'] else 'UNKNOWN' # 转换时间格式 # trade_time = datetime.strptime(row['date'], '%Y-%m-%d %H:%M:%S') item = { 'trade_date': str(row['date']), 'open_price': float(row['open']), 'close_price': float(row['close']), 'high_price': float(row['high']), 'low_price': float(row['low']), 'volume': int(row['volume']) if pd.notna(row['volume']) else None, } processed_data.append(item) except Exception as e: logging.warning(f"处理行情数据时跳过异常行 {row.get('code', '未知')}: {str(e)}") continue return processed_data def save_quotes_to_db(db_config: dict, quote_data: pd.DataFrame, table_name: str = 'stock_quotes') -> bool: """ 将行情数据保存到数据库 Args: db_config (dict): 数据库配置 quote_data (pd.DataFrame): 行情数据DataFrame table_name (str): 目标表名(默认为'stock_quotes') Returns: bool: 是否成功保存 """ # 预处理数据 processed_data = preprocess_quote_data(quote_data) if not processed_data: logging.error("没有有效数据需要保存") return False # 动态生成SQL插入语句 insert_sql = f""" INSERT INTO {table_name} ( trade_date, open_price, close_price, high_price, low_price, volume ) VALUES ( %(trade_date)s, %(open_price)s, %(close_price)s, %(high_price)s, %(low_price)s, %(volume)s ) ON DUPLICATE KEY UPDATE open_price = VALUES(open_price), close_price = VALUES(close_price), high_price = VALUES(high_price), low_price = VALUES(low_price), volume = VALUES(volume) """ try: with MySQLHelper(**db_config) as db: # 检查表是否存在,不存在则创建 if not db.table_exists(table_name): create_table_sql = f""" CREATE TABLE IF NOT EXISTS {table_name} ( id INT AUTO_INCREMENT PRIMARY KEY, trade_date DATETIME NOT NULL COMMENT '交易日期时间', open_price DECIMAL(10, 3) COMMENT '开盘价', close_price DECIMAL(10, 3) COMMENT '收盘价', high_price DECIMAL(10, 3) COMMENT '最高价', low_price DECIMAL(10, 3) COMMENT '最低价', volume BIGINT COMMENT '成交量(股)', create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', UNIQUE KEY idx_trade_date (trade_date) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='股票行情数据表' """ db.execute_update(create_table_sql) logging.info(f"创建了新表: {table_name}") affected_rows = db.execute_many(insert_sql, processed_data) logging.info(f"成功插入/更新 {affected_rows} 条行情记录到表 {table_name}") return True except Exception as e: logging.error(f"保存行情数据到表 {table_name} 失败: {str(e)}") return False def read_missing_codes_basic(file_path='data\missing_tables_小航富途.txt'): """基础读取方法 - 按行读取所有内容""" try: with open(file_path, 'r', encoding='utf-8') as f: lines = f.readlines() # 去除每行末尾的换行符,并过滤空行 codes = [line.strip() for line in lines if line.strip()] return codes except FileNotFoundError: print(f"文件 {file_path} 不存在") return [] except Exception as e: print(f"读取文件失败: {str(e)}") return [] def get_stock_codes() -> List[str]: """从conditionalselection表获取所有股票代码""" try: with MySQLHelper(**db_config) as db: sql = f"SELECT DISTINCT stock_code FROM conditionalselection" 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)}") return [] if __name__ == "__main__": # 配置日志 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler('Debug.log', encoding='utf-8'), # 关键在这里 logging.StreamHandler() ] ) # 数据库配置 db_config = { 'host': 'localhost', 'user': 'root', 'password': 'bzskmysql', 'database': 'klinedata_1d_hk_akshare' } # market_data = get_market_data(Market.HK) # 获取香港市场数据,后面需要改成按照筛选来获取 # # 小航富途 # market_data_hang = read_missing_codes_basic() # # market_data = list(set(market_data_all) - set(market_data_hang)) nCount = 0 # 记录账号获取多少只股票的数据 market_data_all = get_stock_codes() for code in market_data_all: stock_hk_index_daily_sina_df = ak.stock_hk_index_daily_sina(symbol=code[3:]) custom_table_name = 'hk_' + code[3:] # 自定义表名 success = save_quotes_to_db(db_config, stock_hk_index_daily_sina_df, table_name=custom_table_name) time.sleep(5)