Files
stock/stockapp/get_sp500_his_kline.py
T
2024-10-09 11:46:30 +08:00

136 lines
4.9 KiB
Python

"""
Script Name:
Description: 从yahoo获取美股股票的历史K线, 通过 auto_adjust 参数来控制是否获取前复权数据。默认为true, 如果设置为false, 那么结果中会自动带 adj Close.
参考地址: https://github.com/ranaroussi/yfinance
https://aroussi.com/post/python-yahoo-finance
Author: [Your Name]
Created Date: YYYY-MM-DD
Last Modified: YYYY-MM-DD
Version: 1.0
Modification History:
- YYYY-MM-DD [Your Name]:
- YYYY-MM-DD [Your Name]:
- YYYY-MM-DD [Your Name]:
"""
import yfinance as yf
import pymysql
import logging
import time
import sys
import os
from datetime import datetime
import config # 引入 config.py 中的配置
# 股票代码集合,如果属于这些股票,则使用 "max" 时间段
special_stock_codes = ('ABNB', 'CARR', 'CEG', 'GEHC', 'GEV', 'HUBB', 'KVUE', 'OTIS', 'PLTR', 'SOLV', 'VLTO')
# K线调整选项,决定是否使用前复权价格
kline_adjust = True
# 根据 kline_adjust 决定使用的表名
table_name = 'sp500_qfq_his_202410' if kline_adjust else 'sp500_his_kline_none'
# 使用 config.py 中的日志配置
filename = os.path.splitext(os.path.basename(__file__))[0]
config.setup_logging(f"./log/{filename}.log")
logger = logging.getLogger()
# MySQL数据库连接
def connect_to_db():
try:
#return pymysql.connect(**config.db_config)
return pymysql.connect(
**config.db_config,
cursorclass=pymysql.cursors.DictCursor # 确保使用字典形式的游标
)
except pymysql.MySQLError as e:
logger.error(f"Error connecting to the database: {e}", exc_info=True)
return None
# 从MySQL读取sp500表中的股票代码和名称
def fetch_sp500_codes():
db = connect_to_db()
if db is None:
logger.error("Failed to connect to database.")
return []
try:
with db.cursor() as cursor:
cursor.execute("SELECT code_inner, code_name FROM sp500")
codes = cursor.fetchall()
return codes
finally:
db.close()
# 插入数据到指定表名
def insert_stock_data_to_db(data, code, name):
try:
db = connect_to_db()
if db is None:
return
with db.cursor() as cursor:
insert_query = f"""
INSERT INTO {table_name} (time_key, open, high, low, close, adj_close, volume, dividends, stock_splits, code, name)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
ON DUPLICATE KEY UPDATE
open = VALUES(open),
high = VALUES(high),
low = VALUES(low),
close = VALUES(close),
adj_close = VALUES(adj_close),
volume = VALUES(volume),
dividends = VALUES(dividends),
stock_splits = VALUES(stock_splits)
"""
# auto_adjust=True: Date,Open,High,Low,Close,Volume,Dividends,Stock Splits
# auto_adjust=False: Date,Open,High,Low,Close,Adj Close,Volume,Dividends,Stock Splits
for index, row in data.iterrows():
time_key = index.strftime('%Y-%m-%d %H:%M:%S')
# 判断 row['Adj Close'] 是否存在,若不存在则使用 0
adj_close = row['Adj Close'] if 'Adj Close' in row else 0
values = (time_key, row['Open'], row['High'], row['Low'], row['Close'], adj_close, row['Volume'], row['Dividends'], row['Stock Splits'], code, name)
cursor.execute(insert_query, values)
db.commit()
except pymysql.MySQLError as e:
logger.error(f"Error occurred while inserting data: {e}", exc_info=True)
finally:
if db:
db.close()
# 拉取股票的历史数据
def fetch_and_store_stock_data():
codes = fetch_sp500_codes()
for row in codes:
code_inner = row['code_inner']
code_name = row['code_name']
logger.info(f"Fetching data for {code_name} ({code_inner})...")
# 判断使用的时间段,特殊股票使用 max,其他使用 10y
period = "max" if code_inner in special_stock_codes else "10y"
try:
stock = yf.Ticker(code_inner)
# 拉取股票历史数据,使用 kline_adjust 决定 auto_adjust 是否为 True
hist_data = stock.history(period=period, auto_adjust=kline_adjust)
if not hist_data.empty:
logger.info(f"Inserting data for {code_name} ({code_inner}) into {table_name}...")
insert_stock_data_to_db(hist_data, code_inner, code_name)
else:
logger.warning(f"No data found for {code_name} ({code_inner})")
# 每次请求完后休眠3秒
time.sleep(3)
except Exception as e:
logger.error(f"Error fetching data for {code_name} ({code_inner}): {e}", exc_info=True)
if __name__ == "__main__":
fetch_and_store_stock_data()