最近,在IT领域最火的必然是deepseek了,不仅在应用领域火的一塌糊涂,在国家政客也频频提起,并受到相关领导人的多次接见。
今天我们不聊ds ,而是基于ds背后老板,我们聊聊量化投资。量化听起来很高大山,本质是通过技术选择合适的指标(策略)对市场上的股票进行买入和卖出。在最近一段时间里面,我基于个人在投资时用到的几个指标,用python code 复现出来,用于对市场上5600家股票进行选股策略,以下是代码:
import backtrader as bt
import akshare as ak
import pandas as pd
from datetime import datetime, timedelta
from concurrent.futures import ThreadPoolExecutor
pd.set_option("display.max_columns", None) # 显示所有列
pd.set_option("display.expand_frame_repr", False) # 禁止自动换行
pd.set_option("display.max_colwidth", None) # 显示完整列内容
"""
python --versionv :Python 3.11.2
akshare : pip install akshare
backtrader : pip install backtrader
"""
# 策略类
class BollEMA_strategy(bt.Strategy):
params = (
('period_boll', 20),
('period_ema12', 12),
('period_ema50', 50),
('pe_dict', dict), # 接收预获取的市盈率数据
)
def __init__(self):
self.boll = bt.indicators.BollingerBands(self.data.close, period=self.p.period_boll)
self.ema12 = bt.indicators.EMA(self.data.close, period=self.p.period_ema12) #ExponentialMovingAverage
self.ema50 = bt.indicators.EMA(self.data.close, period=self.p.period_ema50)
self.crossover = bt.ind.CrossOver(self.ema12, self.ema50)
self.current_pe = self.p.pe_dict.get(self.data._name) # 从字典获取市盈率
def next(self):
if len(self) < self.p.period_ema50:
return
# 计算买卖条件
# buy_condition1 = self.data.close[0] > self.boll.mid[0] > self.data.close[-1]
buy_condition2 = self.crossover[0] == 1 and self.crossover[-1] != 1
sell_condition1 = self.data.close[0] < self.boll.mid[0] and self.data.close[-1] >= self.boll.mid[-1]
sell_condition2 = self.crossover[0] == -1
buy_signal = buy_condition2 and (0 < self.current_pe < 60) # pe 是硬性条件
sell_signal = sell_condition1 or sell_condition2
# 执行交易
#if buy_signal:
# self.buy()
#elif sell_signal:
# self.sell()
# 输出结果
current_date = self.data.datetime.date(0)
# 以特定格式输出日期
# 获取今天的日期
today = datetime.now().date()
formatted_date = today.strftime('%Y-%m-%d')
target_date = datetime.strptime(formatted_date, "%Y-%m-%d").date()
# 减去一天
days_to_subtract = timedelta(days=1)
previous_date = target_date - days_to_subtract
if current_date == previous_date: #target_date
result = {
'日期': current_date,
'代码': self.data._name,
'收盘价': self.data.close[0],
'BOLL中轨': self.boll.mid[0],
'BOLL下轨': self.boll.bot[0],
'EMA金叉': buy_condition2,
'EMA1': self.ema12[0],
'EMA2': self.ema50[0],
'EMA死叉': sell_condition2,
'市盈率': self.current_pe,
'信号': '买入' if buy_signal else '卖出' if sell_signal else '持有'
}
df = pd.DataFrame([result])
if (df['信号'].values == '买入'):
print(df)
def get_stock_data(code, start_date, end_date):
try:
df = ak.stock_zh_a_hist(symbol=code, period="daily", start_date=start_date, end_date=end_date, adjust="")
if df.empty:
return None
df['日期'] = pd.to_datetime(df['日期'])
df.set_index('日期', inplace=True)
df.columns = ['code', 'open', 'close', 'high', 'low', 'volume', 'amount', 'amplitude', 'rise', 'rise_amt',
'turnover']
return df[['open', 'high', 'low', 'close', 'volume']]
except Exception as e:
print(f"获取{code}数据失败: {str(e)}")
return None
def batch_fetch_data(stock_list, start_date, end_date, max_workers=90):
"""多线程批量获取股票数据"""
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = {executor.submit(get_stock_data, code, start_date, end_date): (code, name) for code, name in
stock_list}
results = {}
for future in futures:
code, name = futures[future]
try:
data = future.result()
if data is not None and len(data) >= 60:
# 计算最后一天的涨幅
if len(data) >= 2:
last_close = data.close.iloc[-1]
pre_close = data.close.iloc[-2]
pct_change = (last_close - pre_close) / pre_close * 100
#print(code, last_close, pre_close, pct_change)
if pct_change < 8 and pct_change > 0 : # 涨幅低于8%
results[code] = data
except Exception as e:
print(f"Error processing {code}: {str(e)}")
return results
def get_all_a_shares():
df = ak.stock_zh_a_spot_em()
return df[['代码', '名称']].values.tolist()
def main():
# 获取基础信息
end_date = datetime.now().strftime("%Y%m%d")
start_date = (datetime.now() - timedelta(days=300)).strftime("%Y%m%d")
# 预获取市盈率数据
spot_df = ak.stock_zh_a_spot_em()
pe_dict = spot_df.set_index('代码')['市盈率-动态'].to_dict()
# 获取股票列表
all_stocks = get_all_a_shares() # 测试时限制数量
# 批量获取数据
data_dict = batch_fetch_data(all_stocks, start_date, end_date)
# 添加数据到引擎
i = 1
for code, df in data_dict.items():
# 初始化回测引擎
cerebro = bt.Cerebro()
#print('current sequence : ', i, ' of ', len(all_stocks))
i = i + 1
data = bt.feeds.PandasData(dataname=df, name=code)
# 添加策略(传递市盈率字典)
cerebro.addstrategy(BollEMA_strategy, pe_dict=pe_dict)
cerebro.adddata(data)
# 批量运行策略
try:
cerebro.run()
#print("end back strategy")
except Exception as e:
print(f"Cerebro run:{e}")
continue
if __name__ == "__main__":
main()
今天运行了下周五的数据,满足指标买入要求的大概是 40只左右,大家可以加入自选股中,做一个观察仓,如果发现有突破或是起量的趋势,可以逐步买入,建仓。
以下是运行结果截图: