VeighNa量化社区
你的开源社区量化交易平台 | vn.py | vnpy
Member
avatar
加入于:
帖子: 1
声望: 0

嗯,用户让我写一个vn.py框架的策略,选股逻辑和买卖条件都挺详细的。首先,我需要理解每个条件的具体含义,然后考虑如何在vn.py中实现。

首先,选股逻辑有两个部分。第一部分是筛选昨日首次涨停,排除前两日有涨停的股票,追求首板后次日连板。这里要注意的是,首次涨停意味着昨天是第一个涨停,而前两天没有涨停过。可能需要计算过去三天的涨停情况,确定昨天涨停,而前两日没有。

第二部分是昨日盘中触及涨停但未封板的股票,次日通过集合竞价强度验证。这时候需要判断股票昨天最高价达到了涨停价,但收盘价没有封住涨停。可能需要计算涨停价,然后比较最高价和收盘价。

买入条件方面,成交额、均价涨幅、流通市值、集合竞价放量这些都需要处理。成交额在5到202亿之间,均价涨幅超过7%,流通市值70到520亿。集合竞价的成交量要大于前日的3%。另外,前四日涨幅小于18%,昨日均价跌幅小于4%,集合竞价高开在0.98到1.07倍前收盘价。这些条件都需要在策略中逐一实现。

卖出规则有两个情况:如果未封板且有浮盈,就在11:25前卖出;另一个是在14:50前,跌破MA5则止损,否则止盈。需要处理时间判断和指标计算,比如MA5的计算,以及是否封板的判断。

接下来考虑vn.py的结构。vn.py的策略通常继承自CtaTemplate,包含on_init初始化,on_start启动,on_stop停止,on_tick处理tick数据,on_bar处理K线。但选股策略可能需要用到选股函数,或者使用定时任务每天执行选股逻辑。

可能需要使用到历史数据,比如获取前三天的涨停情况。这里可能需要访问历史K线数据,比如使用历史管理器或者直接通过数据库查询。但vn.py中可能需要通过load_bar来加载历史数据,但选股可能需要多只股票的数据,这里可能需要处理多标的的数据,这可能比较复杂,因为vn.py主要是为单标的设计的CTA策略,多标的可能需要结合其他模块,比如PortfolioStrategy。

不过用户可能希望的是一个单一的策略,可能需要在策略中维护一个股票池,然后每天处理这些股票的数据。或者可能需要使用到选股模块,比如定时每天收盘后筛选符合条件的股票,然后在次日进行交易。

另外,处理涨停的判断需要注意不同的股票有不同的涨停幅度,比如普通股票是10%,ST是5%,创业板、科创板可能不同。这里可能需要获取股票的前收盘价,然后计算涨停价。或者可能需要使用到股票的涨跌停价格,如果数据源中有的话。

对于时间处理,比如在集合竞价阶段检查条件,或者在特定时间执行卖出操作。需要处理时间判断,比如在9:25检查集合竞价数据,或者在11:25前执行卖出。

关于数据频率,可能需要日线数据来判断涨停情况,以及分钟或tick数据来处理实时交易信号。但vn.py的策略通常基于K线数据,所以可能需要日线、分钟线结合。

实现时可能需要:

  1. 在策略初始化时加载历史日线数据,或者每天收盘后分析前几天的数据,筛选符合条件的股票。

  2. 在交易日开始时,对筛选出的股票进行监控,检查集合竞价的条件。

  3. 在满足买入条件时,发送订单。

  4. 在交易时间内监控卖出条件,如价格是否封板、浮盈情况,以及时间条件。

可能的难点包括:

  • 多股票处理:vn.py的CtaTemplate通常是针对单一合约的,处理多股票可能需要多个实例或者使用组合策略模块。

  • 历史数据获取:需要访问多只股票的历史日线数据来判断涨停情况,可能需要预先加载或查询数据库。

  • 实时数据监控:需要同时监控多个股票的实时数据,如集合竞价的成交量和价格。

  • 涨停价的计算:需要准确计算每个股票的涨停价,考虑ST、新股等因素。

  • 时间判断:策略需要在特定时间执行操作,比如11:25前卖出,这需要准确处理时间戳。

可能的解决方案:

  • 使用vn.py的PortfolioStrategy模块,或者创建多个策略实例,每个实例处理一个股票。

  • 在策略中使用定时任务,比如每天收盘后运行选股函数,筛选出符合条件的股票列表,然后在次日进行监控。

  • 使用历史管理器加载多日的数据,计算涨停情况。

  • 对于实时数据,在on_bar或on_tick中处理,但需要区分不同股票的数据。

另外,用户的条件中有一些指标需要详细处理,比如均价涨幅>7%,这里的均价可能是指当日的成交均价,或者开盘价?需要明确。同样,集合竞价的放量是相比前日的成交量,需要计算前日成交量,然后比较集合竞价的成交量是否超过3%。

可能还需要处理集合竞价的数据,比如在9:25获取集合竞价的成交量和价格,作为买入条件的判断依据。

对于卖出规则,浮盈情况下在11:25前卖出,需要判断当前持仓是否有盈利,并且当前时间是否早于11:25。另一个是14:50前根据MA5判断止损或止盈。需要计算MA5的值,并检查当前价格是否跌破。

在代码结构上,可能需要:

  • 在策略的on_init中加载足够的历史数据来计算指标,比如MA5。

  • 在on_start中,每天初始化选股列表。

  • 在on_bar中处理每个新K线的生成,比如日线结束时筛选股票,或者在分钟线中检查卖出条件。

可能需要使用到定时器,比如每天9:35执行选股,或者在特定时间检查卖出条件。

总之,这个策略需要综合处理多股票、历史数据筛选、实时条件判断以及时间驱动的交易动作。在vn.py中实现可能需要结合多个模块,并且可能需要一定的扩展,因为原生的CtaTemplate可能不太适合多标的情况。不过用户可能希望的是一个示例代码框架,所以可以给出一个基于CtaTemplate的结构,并在其中处理多股票的逻辑,或者指出需要结合组合策略模块。

以下是一个基于vn.py框架的策略示例,实现了您描述的选股逻辑和交易规则。请注意这需要根据您的数据接口和交易环境进行调整:

from vnpy.app.cta_strategy import (
    CtaTemplate,
    StopOrder,
    TickData,
    BarData,
    TradeData,
    OrderData,
    BarGenerator,
    ArrayManager,
    EngineType
)
from vnpy.trader.constant import Status, Interval
import pandas as pd
import numpy as np

class BreakStrategy(CtaTemplate):
    author = "AI Trader"

    parameters = [
        "capital",
        "position_ratio",
        "volume_ratio_threshold",
        "ma_window"
    ]

    variables = [
        "target_list",
        "current_positions"
    ]

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

        self.bg_daily = BarGenerator(self.on_bar, interval=Interval.DAILY)
        self.am_daily = ArrayManager(40)  # 保留40天数据

        self.target_list = []  # 候选股票池
        self.current_positions = {}  # 当前持仓

        # 参数设置
        self.capital = 1000000
        self.position_ratio = 0.2
        self.volume_ratio_threshold = 0.03
        self.ma_window = 5

    def on_init(self):
        self.load_bar(40)  # 加载40天数据

    def on_start(self):
        self.put_event()

    def on_stop(self):
        self.put_event()

    def filter_stocks(self):
        """每日选股逻辑"""
        # 获取历史数据
        history = self.am_daily.snapshot

        # 条件1:昨日首次涨停
        cond1 = (
            (history['close'].iloc[-1] == history['high_limit'].iloc[-1]) &  # 昨日涨停
            (history['close'].iloc[-2] < history['high_limit'].iloc[-2]) &   # 前日未涨停
            (history['close'].iloc[-3] < history['high_limit'].iloc[-3])     # 大前日未涨停
        )

        # 条件2:昨日触板未封
        cond2 = (
            (history['high'].iloc[-1] == history['high_limit'].iloc[-1]) &   # 最高价达到涨停
            (history['close'].iloc[-1] < history['high_limit'].iloc[-1])     # 未封住涨停
        )

        # 综合筛选
        selected = history[cond1 | cond2]

        # 其他过滤条件
        filtered = selected[
            (selected['turnover'].between(5e8, 2.02e9)) &                 # 成交额
            (selected['float_mv'].between(7e9, 5.2e10)) &                 # 流通市值
            (selected['pre_4d_pct'] < 0.18) &                             # 前四日涨幅
            (selected['last_close']*0.96 < selected['avg_price'])        # 昨日均价跌幅
        ]

        self.target_list = filtered.index.tolist()

    def on_tick(self, tick: TickData):
        # 处理集合竞价数据
        if tick.datetime.time().hour == 9 and tick.datetime.time().minute == 25:
            self.check_auction(tick)

        # 执行卖出逻辑
        self.execute_sell(tick)

    def check_auction(self, tick):
        """集合竞价验证"""
        for symbol in self.target_list:
            # 获取前日收盘价
            pre_close = self.am_daily.close[-2]

            # 高开验证
            open_ratio = tick.open_price / pre_close
            if not (0.98 <= open_ratio <= 1.07):
                continue

            # 放量验证
            auction_volume = tick.volume
            pre_volume = self.am_daily.volume[-2]
            if auction_volume < pre_volume * 1.03:
                continue

            # 执行买入
            target_price = tick.open_price * 1.01  # 略高挂单
            self.buy(symbol, target_price, self.capital*self.position_ratio)

    def execute_sell(self, tick):
        """执行卖出规则"""
        if tick.datetime.time() < time(11,25):
            if self.check_floating_profit() and not self.is_limit_up(tick):
                self.sell(tick)

        elif tick.datetime.time() < time(14,50):
            ma5 = self.am.close[-5:].mean()
            if tick.last_price < ma5:
                self.sell(tick, stop=True)
            else:
                self.sell(tick, profit=True)

    def is_limit_up(self, tick):
        """判断是否涨停"""
        return abs(tick.last_price - tick.limit_up) < 1e-5

    def check_floating_profit(self):
        """检查浮动盈亏"""
        return self.pos > 0 and (self.last_price/self.avg_cost - 1) > 0

    def on_bar(self, bar: BarData):
        self.bg_daily.update_bar(bar)
        self.am_daily.update_bar(bar)

        # 每日收盘后执行选股
        if bar.datetime.time() == time(15,0):
            self.filter_stocks()
            self.put_event()

    def notify_order(self, order: OrderData):
        """处理订单回报"""
        if order.status == Status.ALLTRADED:
            if order.direction == Direction.LONG:
                self.current_positions[order.vt_symbol] = {
                    'entry_price': order.price,
                    'position': order.volume
                }

        self.put_event()

# 策略参数配置示例
strategy_setting = {
    "capital": 1000000,
    "position_ratio": 0.2,
    "volume_ratio_threshold": 0.03,
    "ma_window": 5
}

需要特别注意的几个关键点:

  1. 数据要求:
  2. 需要获取股票的每日涨停价数据(high_limit)
  3. 需要流通市值(float_mv)、成交额(turnover)等字段
  4. 需要前4日涨幅(pre_4d_pct)等衍生指标

  5. 功能扩展建议:

    # 在ArrayManager中增加以下指标计算
    class EnhancedArrayManager(ArrayManager):
     def __init__(self, size=100):
         super().__init__(size)
    
     def update_bar(self, bar: BarData):
         super().update_bar(bar)
    
         # 计算4日涨幅
         if self.size >= 5:
             self.pre_4d_pct = (self.close[-1]/self.close[-5] - 1) * 100
    
         # 计算均价指标
         self.avg_price = self.turnover[-1]/self.volume[-1] if self.volume[-1] > 0 else 0
    
  6. 实际使用前需要:

  7. 验证各交易所的涨跌停计算规则
  8. 完善异常处理机制
  9. 增加风险控制模块
  10. 根据实际数据接口调整字段名称
  11. 优化订单执行逻辑(当前为简化示例)

建议先用历史数据进行充分回测,再逐步实盘验证。需要注意不同市场(A股/港股/美股)的涨跌停规则差异,本策略主要针对A股市场设计。

Member
avatar
加入于:
帖子: 1821
声望: 138

代码里有多处幻觉错误,小心哦

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

沪公网安备 31011502017034号

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