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

1. 每个价差策略都会在on_init()时调用load_bar()

1.1 价差策略的on_init()

例如下面的DemoStrategy价差策略的代码:

class DemoStrategy(SpreadStrategyTemplate):
    """
    利用BOLL通道进行套利的一种价差交易
    """

    author = "hxxjava"

    bar_window = 5      # K线周期
    boll_window = 26    # BOLL参数1
    boll_dev = 2        # BOLL参数2        
    target_pos = 1      # 开仓数量
    payup = 10          
    interval = 5

    spread_pos = 0.0
    boll_mid = None
    boll_up = None
    boll_down = None

    sk_algoid:str = ""
    bp_algoid:str = ""
    bk_algoid:str = ""
    sp_algoid:str = ""

    parameters = [
        "bar_window",
        "boll_window",
        "boll_dev",
        "target_pos",
        "payup",
        "interval"
    ]

    variables = [
        "spread_pos",
        "boll_mid",
        "boll_up",
        "boll_down",
        "sk_algoid",
        "bp_algoid",
        "bk_algoid",
        "sp_algoid"
    ]

    def __init__(
        self,
        strategy_engine,
        strategy_name: str,
        spread: SpreadData,
        setting: dict
    ):
        """"""
        super().__init__(
            strategy_engine, strategy_name, spread, setting
        )

        self.bg = BarGenerator(self.on_spread_bar,self.bar_window,self.on_xmin_spread_bar)
        self.am = ArrayManager()

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

        self.load_bar(days=10,callback=self.on_spread_bar)

    def on_spread_bar(self,bar:BarData):
        """
        Callback when 1 min spread bar data is generated.
        """
        print(f"on_spread_bar bar={bar}")       # 看看价差策略的bar长的是什么样子
        self.bg.update_bar(bar)

    。。。其他省略

2. 假如没有下载过数据到本地数据库,load_bar(10)将加载不了1分钟价差K线数据!

2.1 发现问题的过程

  • 编写好了价差交易策略之后;
  • 启动vnpy,连接到CTP接口;
  • 进入价差交易模块;
  • 创建一个跨期价差:rb2109&rb2201,主动腿为rb2109.SHFE,被动腿为rb2201.SHFE。价格乘数分别为1,-1,交易乘数分别为1,-1,没有问题;
  • 用DemoStrategy创建一个价差策略实例:DS_rb2109&rb2201,也是成功的。
  • 初始化DS_rb2109&rb2201,除了策略的inited=True之外,没有任何反应,看不到有任何1分钟价差K线数据被打印出来!

2.2 为什么加载不到任何1分钟价差K线数据?

进去查找,原来问题出在这里:
DemoStrategy调用的load_bar()是从SpreadStrateyTemplate继承的,而SpreadStrateyTemplate是load_bar()又调用了strategy_engine的load_bar()。
strategy_engine的load_bar()的代码如下:

    def load_bar(
        self, spread: SpreadData, days: int, interval: Interval, callback: Callable
    ):
        """"""
        end = datetime.now()
        start = end - timedelta(days)

        bars = load_bar_data(spread, interval, start, end)

        for bar in bars:
            callback(bar)

load_bar_data()的代码:

@lru_cache(maxsize=999)
def load_bar_data(
    spread: SpreadData,
    interval: Interval,
    start: datetime,
    end: datetime,
    pricetick: float = 0
):
    """"""
    # Load bar data of each spread leg
    leg_bars: Dict[str, Dict] = {}

    for vt_symbol in spread.legs.keys():
        symbol, exchange = extract_vt_symbol(vt_symbol)

        bar_data: List[BarData] = database_manager.load_bar_data(
            symbol, exchange, interval, start, end
        )

        bars: Dict[datetime, BarData] = {bar.datetime: bar for bar in bar_data}
        leg_bars[vt_symbol] = bars

    # Calculate spread bar data
    spread_bars: List[BarData] = []

    for dt in bars.keys():
        spread_price = 0
        spread_value = 0
        spread_available = True

        for leg in spread.legs.values():
            leg_bar = leg_bars[leg.vt_symbol].get(dt, None)

            if leg_bar:
                price_multiplier = spread.price_multipliers[leg.vt_symbol]
                spread_price += price_multiplier * leg_bar.close_price
                spread_value += abs(price_multiplier) * leg_bar.close_price
            else:
                spread_available = False

        if spread_available:
            if pricetick:
                spread_price = round_to(spread_price, pricetick)

            spread_bar = BarData(
                symbol=spread.name,
                exchange=exchange.LOCAL,
                datetime=dt,
                interval=interval,
                open_price=spread_price,
                high_price=spread_price,
                low_price=spread_price,
                close_price=spread_price,
                gateway_name="SPREAD",
            )
            spread_bar.value = spread_value
            spread_bars.append(spread_bar)

    return spread_bars

原来load_bar_data()中只考虑了从本地数据库加载1分钟价差K线数据(当然是用价差组合中的多个合约的1分钟K线数据合成的)。
而我因为没有事先下载过rb2109.SHFE和rb2201.SHFE的合约的1分钟K线数据,所以加载不到这10天中的任何1分钟价差K线数据!

3. 为什么不优先从米筐接口rqdatac加载1分钟价差K线?

就算加载不到1分钟价差K线的原因已经找到,可是这样的设计仍然是有问题的:

  • 1 要求不停地下载价差策略相关的合约数据不合理,因为这很容易忘记
  • 2 就算你昨天已经下载了价差策略相关的合约数据,今天你没有下载最新的数据,重新启动了价差策略,策略加载的数据就会缺少最新的数据

4. 修改成优先从米筐接口rqdatac加载1分钟价差K线!

鉴于以上的分析,把app\spread_trading\base.py做如下修改:

4.1 加入从rqdatac读取历史数据的query_bar_from_rq()函数,

# hxxjava debug spread_trading
def query_bar_from_rq(
    symbol: str, exchange: Exchange, interval: Interval, start: datetime, end: datetime
):
    """
    Query bar data from RQData.
    """
    from vnpy.trader.rqdata import rqdata_client
    from vnpy.trader.object import HistoryRequest

    if not rqdata_client.inited:
        rqdata_client.init()

    req = HistoryRequest(
        symbol=symbol,
        exchange=exchange,
        interval=interval,
        start=start,
        end=end
    )
    data = rqdata_client.query_history(req)
    return data

4.2 修改价差K线的读取函数load_bar_data()的读取优先顺序,优先从米筐接口rqdatac加载1分钟价差K线,修改如下:

@lru_cache(maxsize=999)
def load_bar_data(
    spread: SpreadData,
    interval: Interval,
    start: datetime,
    end: datetime,
    pricetick: float = 0
):
    """"""
    # Load bar data of each spread leg
    leg_bars: Dict[str, Dict] = {}

    for vt_symbol in spread.legs.keys():
        symbol, exchange = extract_vt_symbol(vt_symbol)

        # hxxjava debug spread_trading
        bar_data = query_bar_from_rq(
            symbol=symbol, exchange=exchange,
            interval=interval,start=start,end=end
            )

        # if bar_data:
        #     print(f"load {symbol}.{exchange} {interval} bar_data, len of = {len(bar_data)}")

        if not bar_data:
            bar_data: List[BarData] = database_manager.load_bar_data(
                symbol, exchange, interval, start, end
            )

        bars: Dict[datetime, BarData] = {bar.datetime: bar for bar in bar_data}
        leg_bars[vt_symbol] = bars

    # Calculate spread bar data
    spread_bars: List[BarData] = []

    for dt in bars.keys():
        spread_price = 0
        spread_value = 0
        spread_available = True

        for leg in spread.legs.values():
            leg_bar = leg_bars[leg.vt_symbol].get(dt, None)

            if leg_bar:
                price_multiplier = spread.price_multipliers[leg.vt_symbol]
                spread_price += price_multiplier * leg_bar.close_price
                spread_value += abs(price_multiplier) * leg_bar.close_price
            else:
                spread_available = False

        if spread_available:
            if pricetick:
                spread_price = round_to(spread_price, pricetick)

            spread_bar = BarData(
                symbol=spread.name,
                exchange=exchange.LOCAL,
                datetime=dt,
                interval=interval,
                open_price=spread_price,
                high_price=spread_price,
                low_price=spread_price,
                close_price=spread_price,
                gateway_name="SPREAD",
            )
            spread_bar.value = spread_value
            spread_bars.append(spread_bar)

    return spread_bars

5. load_bar()从本地数据库加载的1分钟价差K线数据只有K线

看看 load_bar()从本地数据库加载的1分钟价差K线数据,如下所示:
你会发现其的成交量,volume=0,在使用过程从必须加以注意!
也就是说,需要成交量参与计算的指标是不可利用价差K线序列来计算的!

bar=BarData(gateway_name='SPREAD', symbol='rb2110&rb2201', exchange=<Exchange.LOCAL: 'LOCAL'>, datetime=datetime.datetime(2021, 6, 17, 11, 15, tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>), interval=<Interval.MINUTE: '1m'>, volume=0, open_interest=0, open_price=109.0, high_price=109.0, low_price=109.0, close_price=109.0)
bar=BarData(gateway_name='SPREAD', symbol='rb2110&rb2201', exchange=<Exchange.LOCAL: 'LOCAL'>, datetime=datetime.datetime(2021, 6, 17, 11, 16, tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>), interval=<Interval.MINUTE: '1m'>, volume=0, open_interest=0, open_price=108.0, high_price=108.0, low_price=108.0, close_price=108.0)
bar=BarData(gateway_name='SPREAD', symbol='rb2110&rb2201', exchange=<Exchange.LOCAL: 'LOCAL'>, datetime=datetime.datetime(2021, 6, 17, 11, 17, tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>), interval=<Interval.MINUTE: '1m'>, volume=0, open_interest=0, open_price=106.0, high_price=106.0, low_price=106.0, close_price=106.0)
Administrator
avatar
加入于:
帖子: 4502
声望: 321

收到,我们来改下

Member
avatar
加入于:
帖子: 420
声望: 173

用Python的交易员 wrote:

收到,我们来改下

感谢回复!
我在帖子里的第4部分已经提供了修改的方法,是否合适?可以参考一下。

Administrator
avatar
加入于:
帖子: 4502
声望: 321

是的,理论上按照:RQData、交易接口、数据库的优先级顺序,逐步查询即可

Administrator
avatar
加入于:
帖子: 4502
声望: 321

已在dev分支修改

Member
avatar
加入于:
帖子: 420
声望: 173

用Python的交易员 wrote:

已在dev分支修改

老师,dev分支在哪里?我升级后查了代码,load_bar()的处理逻辑任然没有任何改动。

Member
avatar
加入于:
帖子: 4680
声望: 285

description

Member
avatar
加入于:
帖子: 420
声望: 173

好的,谢谢!

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

沪公网安备 31011502017034号

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