Py学习  »  Python

年化454%,回撤6.9%,附backtrader引擎和策略python源代码

七年实现财富自由 • 2 月前 • 131 次点击  
原创内容第1003篇,专注AGI+,AI量化投资、个人成长与财富自由。
今天发布backtrader引擎的代码,带三个策略:
任务配置方式不变:
新的backtrader引擎,支持按第二天开盘价交易,支持止盈止损,支持多策略组合,支持多参数优化。
代码包下载地址:AI量化实验室——量化投资的星辰大海
import pandas as pd
class SelectWhere:    def __init__(self, signal):        self.signal = signal
    def __call__(self, target):        signal = self.signal        current_date = target.datetime.date(0)  # 获取当前日期
        current_date_pd = pd.Timestamp(current_date)  # 转换为Pandas时间戳
        # 检查当前日期是否在信号索引中        if current_date_pd in signal.index:            # 获取当前日期的信号行,并筛选值为True的列            selected_columns = signal.loc[current_date_pd][signal.loc[current_date_pd]==1].index.to_list()            target.temp["selected"] = selected_columns
            return True        else:            target.temp["selected"] = []            return True  # 若日期不存在返回空列表
class SelectAll:    def __call__(self, target):
        symbols = []        for data in target.datas:            if len(data) > 0:                symbols.append(data._name)        target.temp["selected"] = symbols        return True
class SelectTopK:    def __init__(self, signal, K=1, drop_top_n=0, b_ascending=False):        self.K = K        self.drop_top_n = drop_top_n  # 这算是一个魔改,就是把最强的N个弃掉,尤其动量指标,过尤不及。        self.b_ascending = b_ascending        self.signal = signal
    def __call__(self, target):        """            根据当天信号值排序,丢弃N个信号后选取前K个信号
            参数:            signal: pd.DataFrame - 信号数据框,索引为日期            N: int - 要丢弃的信号数量            K: int - 要选取的信号数量        """        current_date = target.datetime.date()  # 获取当前日期        current_date_pd = pd.Timestamp(current_date)  # 转换为Pandas时间戳        #print(current_date_pd)        signal =  self.signal
        # 检查当前日期是否在信号索引中        if current_date_pd not in signal.index:            target.temp["selected"] = []            return True  # 若日期不存在返回空列表
        # 获取当前日期的信号行        daily_signals = signal.loc[current_date_pd]
        # 移除NaN值        valid_signals = daily_signals.dropna()        if 'selected' in target.temp.keys():
            selected = target.temp['selected']            if selected:                valid_signals = valid_signals[selected]            else:                return True        else:            return False

        # 按信号值降序排序        sorted_signals = valid_signals.sort_values(ascending=self.b_ascending)
        # 丢弃前N个信号(如果需要)        N = self.drop_top_n        if N > 0:            # 确保N不超过有效信号数量            N = min(N, len(sorted_signals))            sorted_signals = sorted_signals.iloc[N:]
        # 取前K个信号        K = self.K        if len(sorted_signals) > 0:            # 确保K不超过剩余信号数量            K = min(K, len(sorted_signals))            topK_signals = sorted_signals.head(K)            target.temp["selected"] = topK_signals.index.tolist()
            return True        else:            target.temp["selected"] = []            return True  # 如果没有剩余信号,返回空列表
class WeightEqually:    def __init__(self):        pass
    def __call__(self, target):        selected = target.temp["selected"]        n = len(selected)
        if n == 0:            target.temp["weights"] = {}        else:            w = 1.0 / n            target.temp["weights"] = {x: w for x in selected}        return True
class ReBalance:    def __init__(self):        self.pre_symbols = None
    def __call__(self, target):
        if "weights" not in target.temp:            return True
        # 这就是目标调仓表,如果没有在调仓表里的,就需要先平仓,把资金腾出来        target_weights = target.temp["weights"]        if type(target_weights) is pd.Series:            target_weights = target_weights.to_dict()
        if self.pre_symbols == set(target_weights.keys()):            #print('持仓一样,不调仓')            return
        #print('当前调仓表', target_weights.keys())
        # 不存在即平仓        datas = target.get_current_holding_datas()        for data in datas:            if data._name not in target_weights.keys():                target.close(data)
        # 存在的按要求调仓        #logger.debug(target_weights)        total_value = target.broker.getvalue()
        to_buy = []        for symbol, w in target_weights.items():            # 卖出的先执行            pos = target.getposition(target.getdatabyname(symbol))            close = target.getdatabyname(symbol).close[0]            data_value = pos.size * close            percent = data_value / total_value            #logger.debug('总市值,{},{},{}-size:{},{}'.format(symbol, total_value, data_value, pos.size,close))            if percent > w:                 #logger.debug('卖出:{}'.format(percent-w))                target.order_target_percent(symbol, w)            else:                #logger.debug('买入:{}'.format(w - percent))                to_buy.append(symbol)
        for s in to_buy:            target.order_target_percent(s, w * 0.99)        self.pre_symbols = set(target_weights.keys())        target.temp = {}        return True
吾日三省吾身
对于做量化的思路,随着自己的成长,也在一步步清晰。
之前花过不少时间写回测引擎,对于回测引擎的工作原理有了比较深入的了解。不过,一个成熟尤其是实盘导向的框架,还是有很多细节要考虑。
在熟悉一个引擎之后,使用成熟的框架是有好处的,比如backtrader。
后来花了不少时间搞低代码和gui,在进展,有沉淀。
但更重要的事情是策略,是因子。
以及如何快速地挖掘因子和开发策略。

做事情,更少但更好。
把重要的事情,做精,做扎实,自然往外延伸,而不是求多,求快。
写到3000篇看看什么效果?还有2000篇,6年多的样子,就算七年吧。
新的七年计划:从2025-2032年。
想想,按以前是知天命的年岁。

如今,随着健康水平和生活质量的提高,50岁早已不再是“老人”的代名词,而被赋予了新的意义:

  • 人生的新起点很多人在这个年龄,孩子已经长大成人(生娃晚的话,可能孩子还在长大中),事业趋于稳定,反而有了更多的时间和精力去追求自己年轻时未完成的梦想,发展新的爱好,开启人生的“第二春”

  • 智慧与成熟的巅峰:积累了丰富的人生阅历、工作经验和处世智慧,看问题更加通透,决策更加沉稳。

  • 承上启下的阶段:通常是家庭中的支柱,上有年迈的父母需要赡养,下有可能刚刚步入社会的子女需要指导,是家庭的核心力量。

每天“不管”一点点,每天就变强一天天。
代码和数据下载:AI量化实验室——2025量化投资的星辰大海

AI量化实验室 星球,已经运行三年多,1800+会员。

aitrader代码,因子表达式引擎、遗传算法(Deap)因子挖掘引等,支持vnpy,qlib,backtrader和bt引擎,内置多个年化30%+的策略,每周五迭代一次,代码和数据在星球全部开源。


点击 “查看原文”,直接访问策略集合

扩展  •  历史文章   


年化390%,回撤7%,夏普6.32 | A股量化策略配置

年化30.24%,最大回撤19%,综合动量多因子评分策略再升级(python代码+数据)

年化429%,夏普5.51 | 全A股市场回测引擎构建

年化443%,回撤才7%,夏普5.53,3积分可查看策略参数

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/186936