python:backtrader 如何使用 tushare 股票数据?

我来详细介绍在Backtrader中使用Tushare数据的几种方法,从简单到复杂,包括示例代码。

方法1:使用PandasData(推荐,最灵活)

这是最常用的方法,将Tushare数据转换为Pandas DataFrame,然后用Backtrader的PandasData适配器加载。

基本示例

import backtrader as bt
import tushare as ts
import pandas as pd
from datetime import datetime

# 1. 从Tushare获取数据
def get_tushare_data(ts_code='000001.SZ', start_date='20200101', end_date='20231231'):
    """
    获取Tushare股票数据并转换为Backtrader需要的格式
    """
    # 初始化tushare(需要token)
    ts.set_token('你的tushare_token')  # 替换为你的token
    pro = ts.pro_api()
    
    # 获取日线数据
    df = pro.daily(ts_code=ts_code, 
                   start_date=start_date, 
                   end_date=end_date)
    
    # 数据清洗和格式化
    df = df.sort_values('trade_date')  # 按日期排序
    df['trade_date'] = pd.to_datetime(df['trade_date'])  # 转换为datetime
    df.set_index('trade_date', inplace=True)  # 设置日期为索引
    
    # 重命名列以匹配Backtrader的PandasData
    df.rename(columns={
        'open': 'Open',
        'high': 'High', 
        'low': 'Low',
        'close': 'Close',
        'vol': 'Volume'
    }, inplace=True)
    
    # 只保留需要的列
    df = df[['Open', 'High', 'Low', 'Close', 'Volume']]
    
    return df

# 2. 创建自定义的PandasData类(可选,处理Tushare特有字段)
class TusharePandasData(bt.feeds.PandasData):
    """
    自定义PandasData以处理Tushare数据
    可以添加更多字段如换手率、涨跌幅等
    """
    params = (
        ('datetime', None),  # 使用索引作为datetime
        ('open', 'Open'),
        ('high', 'High'),
        ('low', 'Low'),
        ('close', 'Close'),
        ('volume', 'Volume'),
        ('openinterest', -1),  # 无持仓量字段
        # 可以添加额外字段
        # ('pct_chg', 'pct_chg'),  # 如果需要涨跌幅
    )

# 3. 完整的回测示例
class SmaCrossStrategy(bt.Strategy):
    params = (
        ('fast', 10),
        ('slow', 30),
    )
    
    def __init__(self):
        self.fast_sma = bt.indicators.SMA(self.data.close, period=self.params.fast)
        self.slow_sma = bt.indicators.SMA(self.data.close, period=self.params.slow)
        self.crossover = bt.indicators.CrossOver(self.fast_sma, self.slow_sma)
        
    def next(self):
        if not self.position:
            if self.crossover > 0:  # 金叉
                self.buy()
        elif self.crossover < 0:  # 死叉
            self.close()

def run_backtest():
    # 创建Cerebro引擎
    cerebro = bt.Cerebro()
    
    # 获取数据
    print("正在获取数据...")
    df = get_tushare_data('000001.SZ', '20200101', '20231231')
    
    if df.empty:
        print("数据获取失败,请检查网络或token")
        return
    
    print(f"数据量: {len(df)} 条")
    print(f"日期范围: {df.index[0]}{df.index[-1]}")
    
    # 创建数据源
    data = TusharePandasData(dataname=df)
    
    # 添加到引擎
    cerebro.adddata(data)
    
    # 添加策略
    cerebro.addstrategy(SmaCrossStrategy)
    
    # 设置初始资金
    cerebro.broker.setcash(100000.0)
    
    # 设置佣金
    cerebro.broker.setcommission(commission=0.001)  # 0.1%佣金
    
    # 添加分析器
    cerebro.addanalyzer(bt.analyzers.Returns, _name='returns')
    cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe')
    cerebro.addanalyzer(bt.analyzers.DrawDown, _name='drawdown')
    cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='trades')
    
    # 运行回测
    print('初始资金: %.2f' % cerebro.broker.getvalue())
    results = cerebro.run()
    print('最终资金: %.2f' % cerebro.broker.getvalue())
    
    # 打印分析结果
    strat = results[0]
    
    print("\n=== 策略绩效 ===")
    print(f"总收益率: {strat.analyzers.returns.get_analysis()['rtot']:.2%}")
    
    sharpe_ratio = strat.analyzers.sharpe.get_analysis()
    print(f"夏普比率: {sharpe_ratio['sharperatio']:.3f}")
    
    drawdown = strat.analyzers.drawdown.get_analysis()
    print(f"最大回撤: {drawdown['max']['drawdown']:.2%}")
    print(f"最长回撤周期: {drawdown['max']['len']} 天")
    
    trades = strat.analyzers.trades.get_analysis()
    if 'total' in trades:
        print(f"总交易次数: {trades['total']['total']}")
        print(f"盈利交易: {trades['won']['total']}")
        print(f"亏损交易: {trades['lost']['total']}")
        if trades['won']['total'] > 0:
            print(f"胜率: {trades['won']['total']/trades['total']['total']:.2%}")
    
    # 绘制图表
    cerebro.plot(style='candlestick', volume=True)

if __name__ == '__main__':
    run_backtest()

方法2:使用通用CSVData(保存为CSV再读取)

如果不想每次都从Tushare API获取,可以先保存到本地CSV。

import backtrader as bt
import tushare as ts
import pandas as pd
import os

class TushareCSVData(bt.feeds.GenericCSVData):
    """
    自定义CSV数据加载器
    """
    params = (
        ('datetime', 0),  # 日期列索引
        ('open', 1),      # 开盘价列索引
        ('high', 2),      # 最高价列索引
        ('low', 3),       # 最低价列索引
        ('close', 4),     # 收盘价列索引
        ('volume', 5),    # 成交量列索引
        ('openinterest', -1),  # 无持仓量
        ('dtformat', '%Y-%m-%d'),  # 日期格式
        ('nullvalue', 0.0),
    )

def download_and_save_tushare_data(ts_code='000001.SZ', 
                                   start_date='20200101', 
                                   end_date='20231231',
                                   save_path='data.csv'):
    """下载Tushare数据并保存为CSV"""
    ts.set_token('你的tushare_token')
    pro = ts.pro_api()
    
    df = pro.daily(ts_code=ts_code, start_date=start_date, end_date=end_date)
    
    # 格式化
    df = df.sort_values('trade_date')
    df['trade_date'] = pd.to_datetime(df['trade_date'])
    
    # 保存为CSV
    df.to_csv(save_path, index=False)
    print(f"数据已保存到: {save_path}")
    return save_path

# 使用示例
csv_file = download_and_save_tushare_data('000001.SZ')
data = TushareCSVData(dataname=csv_file)

方法3:使用自定义Data Feed(最灵活,但复杂)

对于需要特殊处理的场景,可以创建完全自定义的Data Feed。

class TushareDirectData(bt.feeds.DataBase):
    """
    直接连接Tushare的Data Feed(示例)
    注意:这需要处理实时数据连接,实际使用较少
    """
    params = (
        ('ts_code', '000001.SZ'),
        ('start_date', '20200101'),
        ('end_date', '20231231'),
    )
    
    def __init__(self):
        super().__init__()
        # 初始化tushare连接
        ts.set_token('你的tushare_token')
        self.pro = ts.pro_api()
        self.data_df = None
        self.idx = 0
        
    def start(self):
        """开始加载数据"""
        self.data_df = self.pro.daily(
            ts_code=self.p.ts_code,
            start_date=self.p.start_date,
            end_date=self.p.end_date
        )
        self.data_df = self.data_df.sort_values('trade_date')
        self.idx = 0
        
    def _load(self):
        """加载下一行数据"""
        if self.idx >= len(self.data_df):
            return False
            
        row = self.data_df.iloc[self.idx]
        
        # 设置当前bar的数据
        self.lines.datetime[0] = bt.date2num(pd.to_datetime(row['trade_date']))
        self.lines.open[0] = row['open']
        self.lines.high[0] = row['high']
        self.lines.low[0] = row['low']
        self.lines.close[0] = row['close']
        self.lines.volume[0] = row['vol']
        self.lines.openinterest[0] = 0
        
        self.idx += 1
        return True

完整的多股票回测示例

def multi_stock_backtest():
    """多股票回测示例"""
    cerebro = bt.Cerebro()
    
    # 股票列表
    stocks = ['000001.SZ', '000002.SZ', '000858.SZ']
    
    for stock_code in stocks:
        try:
            # 获取数据
            df = get_tushare_data(stock_code, '20200101', '20231231')
            
            if not df.empty:
                # 创建数据源
                data = TusharePandasData(dataname=df, name=stock_code)
                cerebro.adddata(data)
                print(f"已添加: {stock_code}")
            else:
                print(f"跳过 {stock_code},无数据")
        except Exception as e:
            print(f"获取 {stock_code} 数据失败: {e}")
    
    if len(cerebro.datas) == 0:
        print("没有可用的数据")
        return
    
    # 添加策略
    cerebro.addstrategy(SmaCrossStrategy)
    
    # 设置初始资金和佣金
    cerebro.broker.setcash(100000.0)
    cerebro.broker.setcommission(commission=0.001)
    
    # 运行回测
    print(f"初始资金: {cerebro.broker.getvalue():.2f}")
    cerebro.run()
    print(f"最终资金: {cerebro.broker.getvalue():.2f}")
    
    # 绘图
    cerebro.plot()

Tushare数据字段映射表

Tushare字段Backtrader字段说明
trade_datedatetime交易日期
openopen开盘价
highhigh最高价
lowlow最低价
closeclose收盘价
volvolume成交量
amount-成交额(需特殊处理)
pct_chg-涨跌幅(可添加为额外字段)

实用技巧和注意事项

1. 数据预处理

def preprocess_tushare_data(df):
    """数据预处理"""
    # 1. 处理缺失值
    df = df.fillna(method='ffill')
    
    # 2. 处理异常值
    df = df[(df['High'] >= df['Low']) & (df['Volume'] > 0)]
    
    # 3. 添加额外指标
    df['Returns'] = df['Close'].pct_change()
    
    # 4. 确保索引是datetime
    df.index = pd.to_datetime(df.index)
    
    return df

2. 使用缓存提高效率

import pickle
import hashlib
from functools import lru_cache

@lru_cache(maxsize=10)
def get_cached_tushare_data(ts_code, start_date, end_date):
    """带缓存的数据获取"""
    cache_key = f"{ts_code}_{start_date}_{end_date}"
    cache_file = f"cache/{hashlib.md5(cache_key.encode()).hexdigest()}.pkl"
    
    if os.path.exists(cache_file):
        with open(cache_file, 'rb') as f:
            return pickle.load(f)
    else:
        df = get_tushare_data(ts_code, start_date, end_date)
        os.makedirs('cache', exist_ok=True)
        with open(cache_file, 'wb') as f:
            pickle.dump(df, f)
        return df

3. 处理复权数据

def get_adj_data(ts_code, start_date, end_date):
    """获取复权数据"""
    ts.set_token('你的token')
    pro = ts.pro_api()
    
    # 获取复权因子
    df_adj = pro.adj_factor(ts_code=ts_code, 
                           trade_date='')
    
    # 获取前复权数据
    df = pro.pro_bar(ts_code=ts_code, 
                    adj='qfq',  # qfq:前复权, hfq:后复权
                    start_date=start_date,
                    end_date=end_date)
    
    # ... 后续处理
    return df

4. 常见问题解决

问题1:数据时间范围不正确

# 确保数据包含回测所需的时间范围
start_date = '20190101'  # 比回测开始时间早,用于计算指标
end_date = '20231231'

问题2:成交量单位不一致

# Tushare的成交量单位是"手",需要转换为"股"
df['Volume'] = df['Volume'] * 100  # 1手 = 100股

问题3:非交易日数据缺失

# 填充非交易日
df = df.asfreq('D')  # 设置为每日频率
df = df.fillna(method='ffill')  # 向前填充

5. 优化建议

  1. 使用Tushare Pro:免费版有频率限制,Pro版更稳定
  2. 批量下载数据:避免在循环中频繁调用API
  3. 本地缓存:减少API调用次数
  4. 错误处理:添加重试机制
  5. 数据验证:检查数据完整性和准确性

总结

使用Tushare数据配合Backtrader的最佳实践

  1. 使用PandasData方法(方法1),灵活且高效
  2. 做好数据预处理缓存
  3. 注意数据字段映射单位转换
  4. 添加适当的错误处理日志记录

这样就能充分利用 Tushare 丰富的数据和 Backtrader 强大的回测功能了。

内容概要:文章以“智能网页数据标注工具”为例,深入探讨了谷歌浏览器扩展在毕业设计中的实战应用。通过开发具备实体识别、情感分类等功能的浏览器扩展,学生能够融合前端开发、自然语言处理(NLP)、本地存储与模型推理等技术,实现高效的网页数据标注系统。文中详细解析了扩展的技术架构,涵盖Manifest V3配置、内容脚本与Service Worker协作、TensorFlow.js模型在浏览器端的轻量化部署与推理流程,并提供了核心代码实现,包括文本选择、标注工具栏动态生成、高亮显示及模型预功能。同时展望了多模态标注、主动学习与边缘计算协同等未来发展方向。; 适合人群:具备前端开发基础、熟悉JavaScript和浏览器机制,有一定AI模型应用经验的计算机相关专业本科生或研究生,尤其适合将浏览器扩展与人工智能结合进行毕业设计的学生。; 使用场景及目标:①掌握浏览器扩展开发全流程,理解内容脚本、Service Worker与弹出页的通信机制;②实现在浏览器端运行轻量级AI模型(如NER、情感分析)的技术方案;③构建可用于真实场景的数据标注工具,提升标注效率并探索主动学习、协同标注等智能化功能。; 阅读建议:建议结合代码实例搭建开发环境,逐步实现标注功能并集成本地模型推理。重点关注模型轻量化、内存管理与DOM操作的稳定性,在实践中理解浏览器扩展的安全机制与性能优化策略。
基于Gin+GORM+Casbin+Vue.js的权限管理系统是一个采用前后端分离架构的企业级权限管理解决方案,专为软件工程和计算机科学专业的毕业设计项目开发。该系统基于Go语言构建后端服务,结合Vue.js前端框架,实现了完整的权限控制和管理功能,适用于各类需要精细化权限管理的应用场景。 系统后端采用Gin作为Web框架,提供高性能的HTTP服务;使用GORM作为ORM框架,简化数据库操作;集成Casbin实现灵活的权限控制模型。前端基于vue-element-admin模板开发,提供现代化的用户界面和交互体验。系统采用分层架构和模块化设计,确保代码的可维护性和可扩展性。 主要功能包括用户管理、角色管理、权限管理、菜单管理、操作日志等核心模块。用户管理模块支持用户信息的增删改查和状态管理;角色管理模块允许定义不同角色并分配相应权限;权限管理模块基于Casbin实现细粒度的访问控制;菜单管理模块动态生成前端导航菜单;操作日志模块记录系统关键操作,便于审计和追踪。 技术栈方面,后端使用Go语言开发,结合Gin、GORM、Casbin等成熟框架;前端使用Vue.js、Element UI等现代前端技术;数据库支持MySQL、PostgreSQL等主流关系型数据库;采用RESTful API设计规范,确保前后端通信的标准化。系统还应用了单例模式、工厂模式、依赖注入等设计模式,提升代码质量和可试性。 该权限管理系统适用于企业管理系统、内部办公平台、多租户SaaS应用等需要复杂权限控制的场景。作为毕业设计项目,它提供了完整的源码和论文文档,帮助学生深入理解前后端分离架构、权限控制原理、现代Web开发技术等关键知识点。系统设计规范,代码结构清晰,注释完整,非常适合作为计算机相关专业的毕业设计参考或实际项目开发的基础框架。 资源包含完整的系统源码、数据库设计文档、部署说明和毕
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值