152 lines
4.2 KiB
Python
152 lines
4.2 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
批量更新数据库中缺少财务指标的基本面数据
|
|
"""
|
|
import akshare as ak
|
|
import pandas as pd
|
|
import psycopg2
|
|
from datetime import date, datetime
|
|
import time
|
|
|
|
# 数据库配置
|
|
DB_CONFIG = {
|
|
'host': 'localhost',
|
|
'port': 5432,
|
|
'dbname': 'stock_app',
|
|
'user': 'postgres',
|
|
'password': 'xypg5432'
|
|
}
|
|
|
|
def get_db():
|
|
"""获取数据库连接"""
|
|
try:
|
|
conn = psycopg2.connect(**DB_CONFIG)
|
|
return conn
|
|
except Exception as e:
|
|
print(f"数据库连接失败: {e}")
|
|
return None
|
|
|
|
def get_stocks_need_update():
|
|
"""获取需要更新财务指标的股票列表"""
|
|
conn = get_db()
|
|
if not conn:
|
|
return []
|
|
|
|
try:
|
|
cur = conn.cursor()
|
|
cur.execute("""
|
|
SELECT code, name
|
|
FROM stock_fundamental
|
|
WHERE roe IS NULL OR eps IS NULL
|
|
""")
|
|
return cur.fetchall()
|
|
finally:
|
|
conn.close()
|
|
|
|
def fetch_financial_indicators(stock_code):
|
|
"""从akshare获取股票财务指标"""
|
|
try:
|
|
df = ak.stock_financial_analysis_indicator(symbol=stock_code, start_year='2024')
|
|
if df is None or df.empty:
|
|
return None
|
|
|
|
latest = df.iloc[-1]
|
|
|
|
def safe_float(val):
|
|
if pd.isna(val):
|
|
return None
|
|
try:
|
|
return float(val)
|
|
except:
|
|
return None
|
|
|
|
return {
|
|
'eps': safe_float(latest.get('摊薄每股收益(元)')),
|
|
'bps': safe_float(latest.get('每股净资产_调整后(元)')),
|
|
'roe': safe_float(latest.get('净资产收益率(%)')),
|
|
'gross_margin': safe_float(latest.get('销售毛利率(%)')),
|
|
'net_margin': safe_float(latest.get('销售净利率(%)')),
|
|
'revenue_yoy': safe_float(latest.get('主营业务收入增长率(%)')),
|
|
'profit_yoy': safe_float(latest.get('净利润增长率(%)')),
|
|
}
|
|
except Exception as e:
|
|
print(f" 获取 {stock_code} 财务指标失败: {e}")
|
|
return None
|
|
|
|
def update_stock_fundamental(stock_code, data):
|
|
"""更新股票基本面数据"""
|
|
conn = get_db()
|
|
if not conn:
|
|
return False
|
|
|
|
try:
|
|
cur = conn.cursor()
|
|
cur.execute("""
|
|
UPDATE stock_fundamental
|
|
SET roe = %s, eps = %s, bps = %s,
|
|
revenue_yoy = %s, profit_yoy = %s,
|
|
gross_margin = %s, net_margin = %s,
|
|
updated_at = NOW()
|
|
WHERE code = %s
|
|
""", (
|
|
data.get('roe'),
|
|
data.get('eps'),
|
|
data.get('bps'),
|
|
data.get('revenue_yoy'),
|
|
data.get('profit_yoy'),
|
|
data.get('gross_margin'),
|
|
data.get('net_margin'),
|
|
stock_code
|
|
))
|
|
conn.commit()
|
|
return True
|
|
except Exception as e:
|
|
conn.rollback()
|
|
print(f" 更新 {stock_code} 失败: {e}")
|
|
return False
|
|
finally:
|
|
conn.close()
|
|
|
|
def main():
|
|
print("=" * 50)
|
|
print("批量更新基本面财务指标")
|
|
print("=" * 50)
|
|
|
|
# 获取需要更新的股票
|
|
stocks = get_stocks_need_update()
|
|
print(f"\n需要更新的股票数量: {len(stocks)}")
|
|
|
|
if not stocks:
|
|
print("所有股票财务指标已完整,无需更新")
|
|
return
|
|
|
|
success_count = 0
|
|
fail_count = 0
|
|
|
|
for i, (code, name) in enumerate(stocks, 1):
|
|
print(f"\n[{i}/{len(stocks)}] 正在更新 {code} {name}...")
|
|
|
|
# 获取财务指标
|
|
data = fetch_financial_indicators(code)
|
|
|
|
if data:
|
|
# 更新数据库
|
|
if update_stock_fundamental(code, data):
|
|
print(f" ✓ 更新成功: EPS={data.get('eps')}, ROE={data.get('roe')}%")
|
|
success_count += 1
|
|
else:
|
|
fail_count += 1
|
|
else:
|
|
print(f" ✗ 无法获取财务指标")
|
|
fail_count += 1
|
|
|
|
# 避免请求过快
|
|
time.sleep(0.5)
|
|
|
|
print("\n" + "=" * 50)
|
|
print(f"更新完成! 成功: {success_count}, 失败: {fail_count}")
|
|
print("=" * 50)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|