127 lines
4.4 KiB
Python
127 lines
4.4 KiB
Python
import akshare as ak
|
|
import pandas as pd
|
|
import datetime
|
|
import time
|
|
import logging
|
|
|
|
# 配置日志
|
|
logging.basicConfig(
|
|
filename='stock_signals.log',
|
|
level=logging.INFO,
|
|
format='%(asctime)s [%(levelname)s] %(message)s',
|
|
)
|
|
|
|
# 板块关键词
|
|
TARGET_SECTORS = ['互联网服务', '芯片', '消费', '房地产']
|
|
|
|
def fetch_sector_stocks(sector_name):
|
|
"""
|
|
获取某个行业板块的股票列表
|
|
"""
|
|
try:
|
|
df_plates = ak.stock_board_industry_name_em()
|
|
if not df_plates['板块名称'].isin([sector_name]).any():
|
|
logging.warning(f"{sector_name} not exists!")
|
|
return pd.DataFrame()
|
|
|
|
df = ak.stock_board_industry_cons_em(symbol=sector_name)
|
|
return df[['代码', '名称']]
|
|
except Exception as e:
|
|
logging.error(f"获取板块 {sector_name} 股票失败: {e}")
|
|
return pd.DataFrame()
|
|
|
|
def compute_signals(df):
|
|
"""
|
|
根据行情数据计算交易信号
|
|
"""
|
|
signals = []
|
|
for _, row in df.iterrows():
|
|
try:
|
|
code = row['代码']
|
|
name = row['名称']
|
|
pct_today = row['当日涨跌幅']
|
|
pct_year = row['年内涨跌幅']
|
|
pe = row['市盈率']
|
|
pb = row['市净率']
|
|
|
|
signal = ''
|
|
if pct_today < -5:
|
|
signal += '今日大跌; '
|
|
if pct_year < -20:
|
|
signal += '年内大跌; '
|
|
if pe < 50:
|
|
signal += '低市盈率; '
|
|
|
|
if signal:
|
|
signals.append({
|
|
'代码': code,
|
|
'名称': name,
|
|
'当日涨跌幅': pct_today,
|
|
'年内涨跌幅': pct_year,
|
|
'市盈率': pe,
|
|
'市净率': pb,
|
|
'信号': signal.strip()
|
|
})
|
|
except Exception as e:
|
|
logging.warning(f"处理股票 {row} 时出错: {e}")
|
|
return pd.DataFrame(signals)
|
|
|
|
def fetch_and_analyze():
|
|
"""
|
|
获取行情并计算信号
|
|
"""
|
|
logging.info("开始获取并分析行情数据")
|
|
all_stocks = pd.DataFrame()
|
|
|
|
for sector in TARGET_SECTORS:
|
|
df = fetch_sector_stocks(sector)
|
|
if df.empty:
|
|
continue
|
|
|
|
logging.info(f"获取到板块 [{sector}] {len(df)} 只股票")
|
|
for code in df['代码']:
|
|
try:
|
|
# 获取日K线
|
|
kline = ak.stock_zh_a_hist(symbol=code, period='daily', adjust='qfq')
|
|
kline['日期'] = pd.to_datetime(kline['日期'])
|
|
kline.set_index('日期', inplace=True)
|
|
|
|
today = kline.iloc[-1]
|
|
close_today = today['收盘']
|
|
close_5d = kline.iloc[-5]['收盘'] if len(kline) >= 5 else today['收盘']
|
|
close_month = kline.iloc[-21]['收盘'] if len(kline) >= 21 else today['收盘']
|
|
close_year = kline.iloc[0]['收盘']
|
|
|
|
pct_today = (close_today / today['开盘'] - 1) * 100
|
|
pct_5d = (close_today / close_5d -1) * 100
|
|
pct_month = (close_today / close_month -1) * 100
|
|
pct_year = (close_today / close_year -1) *100
|
|
|
|
# 获取市盈率、市净率
|
|
fundamentals = ak.stock_a_lg_indicator(symbol=code)
|
|
pe = fundamentals.iloc[-1]['市盈率(TTM)']
|
|
pb = fundamentals.iloc[-1]['市净率']
|
|
|
|
all_stocks = pd.concat([all_stocks, pd.DataFrame([{
|
|
'代码': code,
|
|
'名称': df.loc[df['代码']==code, '名称'].values[0],
|
|
'当日涨跌幅': pct_today,
|
|
'5日涨跌幅': pct_5d,
|
|
'本月涨跌幅': pct_month,
|
|
'年内涨跌幅': pct_year,
|
|
'市盈率': pe,
|
|
'市净率': pb
|
|
}])])
|
|
except Exception as e:
|
|
logging.error(f"获取股票 {code} 数据失败: {e}")
|
|
continue
|
|
|
|
signals = compute_signals(all_stocks)
|
|
if not signals.empty:
|
|
signals.to_csv(f'stock_signals_{datetime.date.today()}.csv', index=False, encoding='utf-8-sig')
|
|
logging.info(f"生成信号 {len(signals)} 条,已写入 CSV。")
|
|
else:
|
|
logging.info("未发现符合条件的交易信号")
|
|
|
|
if __name__ == "__main__":
|
|
fetch_and_analyze() |