VeighNa量化社区
你的开源社区量化交易平台
Member
avatar
加入于:
帖子: 9
声望: 15

闲来无事,在家把之前的编写的策略如何一步一步实现的记录下来了!

在B站发了些自己做的关于量化的视频,只是简单的记录一下自己是

如何一步一步走进这个量化坑的,希望可以帮助新手少走一些坑。

这个策略,实盘肯定不行的,只是记录下编写策略的一个流程。

我在B站发的视频,欢迎大家点赞关注 /😊/😊/😊/😊/😊/😊/😊

https://www.bilibili.com/video/BV1pV411r7Kt?p=10

视频只是简单的记录自己之前做的一些思路,高手别喷(●ˇ∀ˇ●)

质量肯定没法和官方的比 最后还是希望大家多多支持官方

from vnpy.app.cta_strategy import (
    CtaTemplate,
    StopOrder,
    TickData,
    BarData,
    TradeData,
    OrderData,
    BarGenerator,
    ArrayManager,
)
from vnpy.trader.constant import Interval


class Demo01(CtaTemplate):
    """"""

    # 参数
    fast_window = 30  # 快速均线
    slow_window = 60  # 慢速均线
    x_min = 30  # 交易周期  默认是15分钟
    lots = 1  # 开仓手数
    save = 20  # 止损参数
    startstop = 100  # 开始止盈
    stoploss = 40  # 回撤点位
    daily_window = 10  # 日线的参数
    max_lots = 3
    flag=0

    # 变量
    fast_ma = 0
    fast_ma_pre = 0
    slow_ma = 0
    slow_ma_pre = 0
    price = 0  # tick的实时价格
    bartime = ""  # 时间的显示
    avg_buy_price = 0
    avg_sell_price = 0
    highest = 0
    lowest = 0
    daily_ma = 0

    liqDays = 60
    liqpoint = 0
    holding_days = 0

    run_buy=False
    run_sell=False



    parameters = ["fast_window", "slow_window", "x_min", "lots", 'stoploss', 'startstop', 'save']

    variables = ["bartime", "price", "fast_ma", "slow_ma", 'highest', 'lowest']

    def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
        """"""
        super().__init__(cta_engine, strategy_name, vt_symbol, setting)
        self.bg_x = BarGenerator(self.on_bar, self.x_min, self.on_x_bar, Interval.MINUTE)
        self.am_x = ArrayManager()

        self.bg_daily = BarGenerator(self.on_bar, 1, self.on_daily_bar, Interval.DAILY)
        self.am_daliy = ArrayManager()

    def on_init(self):
        """
        Callback when strategy is inited.
        """
        self.write_log("策略初始化")
        self.load_bar(30)

    def on_start(self):
        """
        Callback when strategy is started.
        """
        self.write_log("策略启动")

    def on_stop(self):
        """
        Callback when strategy is stopped.
        """
        self.write_log("策略停止")

    def on_tick(self, tick: TickData):
        """
        Callback of new tick data update.
        """
        self.bg_x.update_tick(tick)
        self.price = tick.last_price


        if self.pos > 0:
            if tick.last_price < self.avg_buy_price - self.save*2:
                self.sell(tick.last_price*0.9, abs(self.pos))
                print(tick.datetime, tick.last_price, "保命出场")
                self.flag+=1

        elif self.pos < 0:
            if tick.last_price > self.avg_sell_price + self.save*2:
                self.cover(tick.last_price*1.2, abs(self.pos))
                print(tick.datetime, tick.last_price, "保命出场")
                self.flag+=1

        self.put_event()

    def on_bar(self, bar: BarData):

        self.bg_x.update_bar(bar)
        self.bg_daily.update_bar(bar)

    def on_x_bar(self, bar: BarData):
        """
        Callback of new bar data update.
        """
        self.cancel_all()

        am = self.am_x

        am.update_bar(bar)

        if not am.inited:
            return
        # ma30的计算
        self.fast_ma = am.close_array[-self.fast_window:-1].mean()
        # 之前的ma30的计算
        self.fast_ma_pre = am.close_array[-self.fast_window - 1:-2].mean()
        # ma60的计算
        self.slow_ma = am.close_array[-self.slow_window:-1].mean()
        # 之前的ma60的计算
        self.slow_ma_pre = am.close_array[-self.slow_window - 1:-2].mean()

        self.run_buy=bar.close_price<=am.close_array[-2]*1.05

        self.run_sell=bar.close_price>=am.close_array[-2]*0.95





        if self.pos == 0:
            # 金叉开多
            if self.fast_ma_pre < self.slow_ma_pre and self.fast_ma > self.slow_ma:
                if bar.close_price > self.daily_ma and self.run_buy:
                    self.buy(bar.close_price,min(self.lots,self.max_lots))
                    self.highest = bar.close_price
                    self.avg_buy_price = bar.close_price
                    self.holding_days = 0
                    print(bar.datetime, bar.close_price, "开多单","开仓手数:",min(self.lots,self.max_lots))

            elif self.fast_ma_pre > self.slow_ma_pre and self.fast_ma < self.slow_ma:
                # 死叉开空
                if bar.close_price < self.daily_ma and self.run_sell:
                    self.short(bar.close_price,min(self.lots,self.max_lots))
                    self.lowest = bar.close_price
                    self.avg_sell_price = bar.close_price
                    self.holding_days = 0
                    print(bar.datetime, bar.close_price, "开空单","开仓手数:",min(self.lots,self.max_lots))

        elif self.pos > 0:
            # 持有多单的最高价记录
            self.highest = max(bar.high_price, self.highest)
            # 多单止损的逻辑
            if bar.close_price < self.avg_buy_price - self.save:
                self.sell(bar.close_price, abs(self.pos))
                print(bar.datetime, bar.close_price, "多单止损","止损手数:",abs(self.pos))
                self.flag+=1

            # 多单止盈
            elif self.highest > self.avg_buy_price + self.startstop:
                if bar.close_price < self.highest - self.stoploss:
                    self.sell(bar.close_price, abs(self.pos))
                    print(bar.datetime, bar.close_price, "多单止盈","止盈手数:",abs(self.pos))
                    self.flag=0

            elif self.holding_days > 20 and bar.close_price < self.liqpoint:
                self.sell(bar.close_price, abs(self.pos))
                print(bar.datetime, bar.close_price, "多单自适应均线出场","平仓手数:",abs(self.pos))
                # if self.avg_buy_price<bar.close_price:
                #     self.flag=0
                # else:
                #     self.flag+=1


        elif self.pos < 0:
            # 持有空单的最低价记录
            self.lowest = min(bar.low_price, self.lowest)
            # 空单止损的逻辑
            if bar.close_price > self.avg_sell_price + self.save:
                self.cover(bar.close_price, abs(self.pos))
                print(bar.datetime, bar.close_price, "空单止损","止损手数:",abs(self.pos))
                self.flag+=1

            # 空单止盈
            elif self.lowest < self.avg_sell_price - self.startstop:
                if bar.close_price > self.lowest + self.stoploss:
                    self.cover(bar.close_price, abs(self.pos))
                    print(bar.datetime, bar.close_price, "空单止盈", "止盈手数:", abs(self.pos))
                    self.flag=0


            elif self.holding_days > 20 and bar.close_price > self.liqpoint:
                self.cover(bar.close_price, abs(self.pos))
                print(bar.datetime, bar.close_price, "空单自适应均线出场","平仓手数:",abs(self.pos))
                # if self.avg_sell_price > bar.close_price:
                #     self.flag =0
                # else:
                #     self.flag += 1

        if self.pos != 0:
            self.holding_days += 1
        else:
            self.liqDays = self.slow_window

        if self.pos != 0 and self.holding_days >= 20:
            self.liqDays -= 1
            self.liqDays = max(self.liqDays, 50)

        self.liqpoint = am.close_array[-self.liqDays:].mean()

        self.lots=2 if self.flag>=2 else 1

        self.put_event()

    def on_daily_bar(self, bar: BarData):

        self.cancel_all()

        am = self.am_daliy

        am.update_bar(bar)

        if not am.inited:
            return

        self.daily_ma = am.close_array[-self.daily_window:-1].mean()

    def on_order(self, order: OrderData):
        """
        Callback of new order data update.
        """
        pass

    def on_trade(self, trade: TradeData):
        """
        Callback of new trade data update.
        """
        self.put_event()

    def on_stop_order(self, stop_order: StopOrder):
        """
        Callback of stop order update.
        """
        pass
Member
avatar
加入于:
帖子: 8
声望: 0

牛B,求指点
方便加个QQ或微信么64655161

Administrator
avatar
加入于:
帖子: 4500
声望: 320

相当不错啊,给你加个精华

Member
加入于:
帖子: 50
声望: 2

一键三连

Member
加入于:
帖子: 50
声望: 2

回测没有问题,但是
用vnpy 策略 加载后,无法 日线的数据无法初始化。
description
。小周期的数据可以初始化,但是日线的数据就不行。 并且也load(300)天的数据。
description
按说应该够了,搞不懂。
请大大们解惑。

Member
avatar
加入于:
帖子: 4618
声望: 284

是这样的,vnpy从2.1.3开始环球时区支持,所以现在datetime对象全部附加了其所在时区的信息。之前楼主改的BarGenerator里有一行:
 

            if self.last_bar and str(bar.datetime)[-8:] =='14:59:00':

挪一下区间,改成这样就好了:

            if self.last_bar and str(bar.datetime)[-14:-6] =='14:59:00':

 

Member
加入于:
帖子: 50
声望: 2

xiaohe wrote:

是这样的,vnpy从2.1.3开始环球时区支持,所以现在datetime对象全部附加了其所在时区的信息。之前楼主改的BarGenerator里有一行:
 

            if self.last_bar and str(bar.datetime)[-8:] =='14:59:00':

挪一下区间,改成这样就好了:

            if self.last_bar and str(bar.datetime)[-14:-6] =='14:59:00':

 
额 谢谢 可是我用的是 vnpy 2.11 的版本,没有升级到2.13.因为只做国内期货

Member
avatar
加入于:
帖子: 1
声望: 0

感谢

Member
avatar
加入于:
帖子: 25
声望: 0

谢谢分享!正在B站学习。期待后续课程。再次感谢!

Member
avatar
加入于:
帖子: 7
声望: 1

谢谢分享,对我这VNPY新手来说就是指明灯!

Member
avatar
加入于:
帖子: 24
声望: 2

感谢分享!希望能继续看到这样的精华帖

Member
avatar
加入于:
帖子: 419
声望: 170

这一句有问题:
self.bg_daily = BarGenerator(self.on_bar, 1, self.on_daily_bar, Interval.DAILY)
因为:BarGenerator没有实现interval = Interval.DAILY时的情况,需要另外实现一个 可以处理日线间隔的bar生成器。

Member
avatar
加入于:
帖子: 7
声望: 1

很棒 加油!

Member
avatar
加入于:
帖子: 10
声望: 0

这几天在看你的视频呢。加油!

Member
avatar
加入于:
帖子: 23
声望: 0

参考该案例修改了简化版的海龟,已在BG中写入DAILY合成代码,作者的策略可以正常运行,我的运行出来只有1手成交,刚入门不久,找不到问题所在,恳请大家帮我看看是哪里的问题。谢谢。
(一)代码
from vnpy.app.cta_strategy import (
CtaTemplate,
StopOrder,
Direction,
TickData,
BarData,
TradeData,
OrderData,
BarGenerator,
ArrayManager,
)

class HaiguiStrategy(CtaTemplate):
""""""
author = "用Python的交易员"

#参数定义
entry_window = 20
exit_window = 10
atr_window = 20
fixed_size = 1

#进出场变量
entry_up = 0
entry_down = 0
exit_up = 0
exit_down = 0
atr_value = 0

#止损变量
long_stop = 0
short_stop = 0

parameters = ["entry_window", "exit_window", "atr_window", "fixed_size"]
variables = ["entry_up", "entry_down", "exit_up", "exit_down", "atr_value","long_stop","short_stop"]

def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
    """"""
    super().__init__(cta_engine, strategy_name, vt_symbol, setting)

    self.bg = BarGenerator(self.on_bar)
    self.am = ArrayManager()

    self.bg_daily = BarGenerator(self.on_bar, 1, self.on_daily_bar, Interval.DAILY)
    self.am_daliy = ArrayManager()

def on_init(self):
    """
    Callback when strategy is inited.
    """
    self.write_log("策略初始化")
    self.load_bar(30)

def on_start(self):
    """
    Callback when strategy is started.
    """
    self.write_log("策略启动")

def on_stop(self):
    """
    Callback when strategy is stopped.
    """
    self.write_log("策略停止")

def on_tick(self, tick: TickData):
    """
    Callback of new tick data update.
    """
    self.bg.update_tick(tick)

def on_daily_bar(self, bar: BarData):

    self.cancel_all()

    self.am_daliy.update_bar(bar)
    if not self.am_daliy.inited:
        return

    #基于日线周期计算唐奇安通道以及ATR波动
    self.entry_up, self.entry_down = self.am_daliy.donchian(self.entry_window)
    self.exit_up, self.exit_down = self.am_daliy.donchian(self.exit_window)
    self.atr_value = self.am_daliy.atr(self.atr_window)

    #基于日线周期定义多头止损
    self.long_stop = self.entry_up - 2 * self.atr_value   #定义多头止损价
    self.short_stop = self.entry_down + 2 * self.atr_value   #定义空头止损价


def on_bar(self, bar: BarData):
    """
    Callback of new bar data update.
    """   
    #撤销之前的所有委托
    self.cancel_all()

    #保存K线数据
    self.am.update_bar(bar)
    if not self.am.inited:
        return

    #判断是否进行交易

    #没有仓位
    if self.pos==0:
        if bar.high_price >= self.entry_up:
            self.buy(self.entry_up,self.fixed_size,True)
        elif bar.low_price <= self.entry_down:
            self.short(self.entry_down,self.fixed_size,True)

    #持有多头仓位
    elif self.pos > 0:

        sell_price = max(self.long_stop, self.exit_down)

        if bar.close_price <= self.entry_down:
            self.sell(self.entry_down, abs(self.pos), True)
            self.short(self.entry_down, self.fixed_size, True)

        elif bar.close_price <= sell_price:
            self.sell(sell_price, abs(self.pos), True)

    elif self.pos < 0:

        cover_price = min(self.short_stop, self.exit_up)

        if bar.close_price >= self.entry_up:
            self.cover(self.entry_up, abs(self.pos), True)
            self.buy(self.entry_up, self.fixed_size, True)

        elif bar.close_price >= cover_price:
            self.cover(cover_price, abs(self.pos), True)

    self.put_event()       

def on_order(self, order: OrderData):
    """
    Callback of new order data update.
    """
    pass

def on_trade(self, trade: TradeData):
    """
    Callback of new trade data update.
    """
    self.put_event()

def on_stop_order(self, stop_order: StopOrder):
    """
    Callback of stop order update.
    """
    pass

(二)回测
#
engine = BacktestingEngine()
engine.set_parameters(
vt_symbol = "ru88.SHFE",
interval ="1m",
start = datetime(2018,1,1),
end = datetime(2020,10,26),
rate = 0,
slippage = 0,
size = 10,
pricetick = 1,
capital = 5_000_0,
)

engine.add_strategy(HaiguiStrategy,{})
engine.load_data()
engine.run_backtesting()
df = engine.calculate_result()
engine.calculate_statistics()
engine.show_chart()

(三)结果显示
2020-11-09 15:48:04.633257 开始加载历史数据
2020-11-09 15:48:04.635257 加载进度: [3%]
2020-11-09 15:48:04.635257 加载进度: [6%]
2020-11-09 15:48:04.635257 加载进度: [9%]
2020-11-09 15:48:04.636257 加载进度:# [12%]
2020-11-09 15:48:04.636257 加载进度:# [15%]
2020-11-09 15:48:04.637257 加载进度:# [17%]
2020-11-09 15:48:04.637257 加载进度:## [20%]
2020-11-09 15:48:04.638257 加载进度:## [23%]
2020-11-09 15:48:04.640257 加载进度:## [26%]
2020-11-09 15:48:04.641257 加载进度:## [29%]
2020-11-09 15:48:04.642257 加载进度:### [32%]
2020-11-09 15:48:04.643257 加载进度:### [35%]
2020-11-09 15:48:04.643257 加载进度:### [38%]
2020-11-09 15:48:04.644257 加载进度:#### [41%]
2020-11-09 15:48:04.645257 加载进度:#### [44%]
2020-11-09 15:48:04.645257 加载进度:#### [47%]
2020-11-09 15:48:04.646257 加载进度:#### [50%]
2020-11-09 15:48:04.646257 加载进度:##### [52%]
2020-11-09 15:48:04.647257 加载进度:##### [55%]
2020-11-09 15:48:04.648258 加载进度:##### [58%]
2020-11-09 15:48:04.648258 加载进度:###### [61%]
2020-11-09 15:48:04.649258 加载进度:###### [64%]
2020-11-09 15:48:04.649258 加载进度:###### [67%]
2020-11-09 15:48:04.650258 加载进度:###### [70%]
2020-11-09 15:48:04.650258 加载进度:####### [73%]
2020-11-09 15:48:04.650258 加载进度:####### [76%]
2020-11-09 15:48:04.651258 加载进度:####### [79%]
2020-11-09 15:48:04.652258 加载进度:######## [82%]
2020-11-09 15:48:04.652258 加载进度:######## [85%]
2020-11-09 15:48:04.652258 加载进度:######## [87%]
2020-11-09 15:48:04.654258 加载进度:######### [90%]
2020-11-09 15:48:04.654258 加载进度:######### [93%]
2020-11-09 15:48:04.654258 加载进度:######### [96%]
2020-11-09 15:48:04.655258 加载进度:######### [99%]
2020-11-09 15:48:04.655258 加载进度:########## [100%]
2020-11-09 15:48:04.655258 历史数据加载完成,数据量:253750
2020-11-09 15:48:04.732262 策略初始化完成
2020-11-09 15:48:04.732262 开始回放历史数据
2020-11-09 15:48:07.345412 历史数据回放结束
2020-11-09 15:48:07.346412 开始计算逐日盯市盈亏
2020-11-09 15:48:07.352412 逐日盯市盈亏计算完成
2020-11-09 15:48:07.353412 开始计算策略统计指标
2020-11-09 15:48:07.360413 ------------------------------
2020-11-09 15:48:07.360413 首个交易日: 2018-02-09
2020-11-09 15:48:07.360413 最后交易日: 2020-10-23
2020-11-09 15:48:07.360413 总交易日: 687
2020-11-09 15:48:07.360413 盈利交易日: 322
2020-11-09 15:48:07.360413 亏损交易日: 319
2020-11-09 15:48:07.360413 起始资金: 50,000.00
2020-11-09 15:48:07.360413 结束资金: 76,250.00
2020-11-09 15:48:07.360413 总收益率: 52.50%
2020-11-09 15:48:07.360413 年化收益: 18.34%
2020-11-09 15:48:07.360413 最大回撤: -38,400.00
2020-11-09 15:48:07.361413 百分比最大回撤: -63.52%
2020-11-09 15:48:07.361413 最长回撤天数: 77
2020-11-09 15:48:07.361413 总盈亏: 26,250.00
2020-11-09 15:48:07.361413 总手续费: 0.00
2020-11-09 15:48:07.361413 总滑点: 0.00
2020-11-09 15:48:07.361413 总成交金额: 122,700.00
2020-11-09 15:48:07.361413 总成交笔数: 1
2020-11-09 15:48:07.361413 日均盈亏: 38.21
2020-11-09 15:48:07.361413 日均手续费: 0.00
2020-11-09 15:48:07.361413 日均滑点: 0.00
2020-11-09 15:48:07.361413 日均成交金额: 178.60
2020-11-09 15:48:07.361413 日均成交笔数: 0.001455604075691412
2020-11-09 15:48:07.361413 日均收益率: 0.06%
2020-11-09 15:48:07.361413 收益标准差: 4.81%
2020-11-09 15:48:07.361413 Sharpe Ratio: 0.18
2020-11-09 15:48:07.361413 收益回撤比: 0.83
2020-11-09 15:48:07.362413 策略统计指标计算完成

description

Member
avatar
加入于:
帖子: 4618
声望: 284

应该是你把一分钟的逻辑写在on_bar里了,导致on_daily_bar里的指标全是0。要写多周期策略请参考示例策略multi_timeframe_strategy

Member
avatar
加入于:
帖子: 23
声望: 0

已解决,谢谢。

Member
avatar
加入于:
帖子: 1
声望: 0

为什么我回测只开多,不开空呢

Member
avatar
加入于:
帖子: 4618
声望: 284

那要自己去策略里print指标数值之类的看看了

Member
avatar
加入于:
帖子: 59
声望: 0

日线合成周线bar需要自己手工合成吗
还是这样写就可以了
self.bg5 = BarGenerator(self.on_bar, 5, self.on_5day_bar, interval=Interval.DAILY)
我的回测数据是日线的。
算了,的确得自己合成。

© 2015-2022 上海韦纳软件科技有限公司
备案服务号:沪ICP备18006526号

沪公网安备 31011502017034号

【用户协议】
【隐私政策】
【免责条款】