VeighNa量化社区
你的开源社区量化交易平台
hxxjava's Avatar
Member
离线
419 帖子
声望: 155

把策略的文件名称从 “课时03CTA策略.py” 改为英文的名称,如“fast_slow_ma.py"试试。

论坛对交易理论的讨论有点少

大家总是在讨论编程,就没有人谈谈如何才能够盈利,如果不能够盈利,干嘛还要编程!

让K线图表说话

description

做多5手

description

后势怎么看?

如果不能够有效上破5070,rb2110还得跌!
一家之言,不喜勿喷。

vnpytrader wrote:

大神,# 策略账户引擎 self.sam_engine:SamEngine = cta_engine.main_engine.get_engine('sam') 是自定义的?

是的,策略账户引擎现在测试中。

1. 连接UFT接口后,报如下错误

选择恒生UFT网关,连接后就报如下错误:

KeyError: ('TradeDate',)

At:
  D:\ProgramFiles\VnStudio\lib\site-packages\vnpy\gateway\uft\uft_gateway.py(728): update_trade
  D:\ProgramFiles\VnStudio\lib\site-packages\vnpy\gateway\uft\uft_gateway.py(502): onRspQryTrade

2. 错误的位置

错误位置是gateway\uft\uft_gateway.py的728行:

    def update_trade(self, data: dict) -> None:
        """"""
        symbol = data["InstrumentID"]
        exchange = EXCHANGE_UFT2VT[data["ExchangeID"]]
        sessionid = data["SessionID"]
        order_ref = data["OrderRef"]
        orderid = f"{sessionid}_{order_ref}"

        order = self.orders.get(orderid, None)
        if order:
            order.traded += data["TradeVolume"]

            if order.traded < order.volume:
                order.status = Status.PARTTRADED
            else:
                order.status = Status.ALLTRADED

            self.gateway.on_order(order)

        trade_time = generate_time(data["TradeTime"])
        timestamp = f"{data['TradeDate']} {trade_time}"         # 就是这里的TradeDate出错,意思是没有该字典项目
        dt = datetime.strptime(timestamp, "%H:%M:%S")
        dt = CHINA_TZ.localize(dt)

        trade = TradeData(
            symbol=symbol,
            exchange=exchange,
            orderid=orderid,
            tradeid=data["TradeID"],
            direction=DIRECTION_UFT2VT[data["Direction"]],
            offset=OFFSET_UFT2VT[data["OffsetFlag"]],
            price=data["TradePrice"],
            volume=data["TradeVolume"],
            datetime=dt,
            gateway_name=self.gateway_name
        )
        self.gateway.on_trade(trade)

3. CHSTradeField中的交易日叫’TradingDay‘不叫’TradeDate‘

找到api\uft\include\HSStruct.h中的CHSTradeField,发现CHSTradeField结构中没有TradeDate字段,应该是TradingDay字段。

///成交信息
struct CHSTradeField
{
    /// 账号
    HSAccountID                   AccountID;
    /// 成交编码
    HSTradeID                     TradeID;
    /// 报单编码
    HSOrderSysID                  OrderSysID;
    /// 经纪公司报单编码
    HSBrokerOrderID               BrokerOrderID;
    /// 会话编码
    HSSessionID                   SessionID;
    /// 报单引用
    HSRef                         OrderRef;
    /// 交易所代码
    HSExchangeID                  ExchangeID;
    /// 合约代码
    HSInstrumentID                InstrumentID;
    /// 买卖方向
    HSDirection                   Direction;
    /// 开平标志
    HSOffsetFlag                  OffsetFlag;
    /// 投机/套保/备兑类型
    HSHedgeType                   HedgeType;
    /// 成交数量
    HSVolume                      TradeVolume;
    /// 成交价格
    HSPrice                       TradePrice;
    /// 交易日
    HSDate                        TradingDay;   # 这里是交易日 !!!
    /// 成交时间
    HSTime                        TradeTime;
    /// 期权对应的标的合约代码
    HSInstrumentID                UnderlyingInstrID;
};

4. 如何修改错误

打开gateway\uft\uft_gateway.py中UftTdApi的这个update_trade()函数做如下修改就OK了:

    def update_trade(self, data: dict) -> None:
        """"""
        symbol = data["InstrumentID"]
        exchange = EXCHANGE_UFT2VT[data["ExchangeID"]]
        sessionid = data["SessionID"]
        order_ref = data["OrderRef"]
        orderid = f"{sessionid}_{order_ref}"

        order = self.orders.get(orderid, None)
        if order:
            order.traded += data["TradeVolume"]

            if order.traded < order.volume:
                order.status = Status.PARTTRADED
            else:
                order.status = Status.ALLTRADED

            self.gateway.on_order(order)

        trade_time = generate_time(data["TradeTime"])
        # timestamp = f"{data['TradeDate']} {trade_time}"   # hxxjava debug 不是TradeDate
        timestamp = f"{data['TradingDay']} {trade_time}"    # hxxjava debug 是TradingDay
        # dt = datetime.strptime(timestamp, "%H:%M:%S")     # hxxjava 日期提取格式不对
        dt = datetime.strptime(timestamp, "%Y%m%d %H:%M:%S") # hxxjava 日期提取格式 %Y%m%d %H:%M:%S
        dt = CHINA_TZ.localize(dt)

        trade = TradeData(
            symbol=symbol,
            exchange=exchange,
            orderid=orderid,
            tradeid=data["TradeID"],
            direction=DIRECTION_UFT2VT[data["Direction"]],
            offset=OFFSET_UFT2VT[data["OffsetFlag"]],
            price=data["TradePrice"],
            volume=data["TradeVolume"],
            datetime=dt,
            gateway_name=self.gateway_name
        )
        self.gateway.on_trade(trade)

青青子荆 wrote:

OutManOrz wrote:

清风 wrote:

tcp://101.71.12.144:9006

linhertz wrote:

请教一下,按照您的步骤申请了账号,为什么连不上?

description

description
我和你的情况一样,应该是之前让下载的license.dat许可文件的问题,我目前将这个文件放入Vnstudio\Licenses\下,仍然没有解决,不知道怎么使用这个认证文件=。=!

license.dat放的位置改成".vntrader"文件夹的上级目录试试。

参考第一贴中的第4步,图中②运行目录是哪个目录就把license.dat放在那个目录中,就可以了。

公告:SimNow系统切换更新通告

  1. SIMNOW将于下周一(5月17日)夜盘上线一套新的环境,该环境可以进行全市场全部期货合约和上期所全部期权合约的交易。同时,老环境将关闭。在新环境内将沿用之前注册的账号密码,重置资金和持仓,初始资金为2000万。

  2. 新环境采用新的前置地址,如下:

第一组:Trade Front:180.168.146.187:10201,Market Front:180.168.146.187:10211;【电信】(看穿式前置,使用监控中心生产秘钥)

第二组:Trade Front:180.168.146.187:10202,Market Front:180.168.146.187:10212;【电信】(看穿式前置,使用监控中心生产秘钥)

第三组:Trade Front:218.202.237.33:10203, Market Front:218.202.237.33:10213; 【移动】(看穿式前置,使用监控中心生产秘钥)

  1. 新的交易终端目前只提供快期,后续会增加更多终端软件,SIMNOW官网也将在同一时间更新,请在网站更新后到“终端下载”进$下载。simnow官网将更新可下载到的API,版本为v6.5.1_20200908。

  2. 7*24环境将于该新环境上线后两周上线。

  3. 网站更新后,入金逻辑不变,每日仍然可以入金三次,但是增加单次入金上限,提高至单次200万。

升级过程中给广大SimNow用户造成的不便,深表歉意!

发布日期:2021/05/13

小天下 wrote:

hxxjava wrote:

黄裳 wrote:

也就是说,想要得到随时更新的5分钟线,30分钟线,就要从新写自己的BarGenerator,非常感谢

是的,就要重新写自己的BarGenerator。
你所说的随时更新的5分钟线,30分钟线其实是临时K线,而目前的BarGenerator没有直接提供你要的临时K线机制。
不过你可以扩展一个BarGenerator类,实现一个属性或者方法,如gen_temp_bar()和temp_bar(),具体办法:

def gen_temp_bar(self)
     self.temp_bar = self.window_bar与self.bar的合成

当然这只是伪代码,自己实现下,应该不难。

def temp_bar(self)
     return self.temp_bar

其中gen_temp_bar()需要在策略的on_tick()的函数中最后被执行,

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

这样你就可以得到window_bar的临时K线了。

大神,这样需不需要同时改进class ArrayManager(object)或者重新 定制一个全新的或者扩展的ArrayManager函数来配套使用,因为每次推出新的bar,它也将新的close,high等数据存储进去了!

扩展BarGenerator(K线合成器)和ArrayManager(数组管理器)没有关系,
因为ArrayManager使用输入的是扩展BarGenerator的输出结果bar,只要
扩展BarGenerator没有改变bar的内容构成就不影响ArrayManager。

应该是你没有历史数据,先用vnpy中的数据管理应用去米筐下载你要显示的历史K线,比如rb2110的,规定好起始和结束日期。
有了历史K线数据,再来运行if name == "main":之后的测试程序。

1. 启动投资组合管理应用的界面

description

2. 投资组合管理应用不应该是可选启动项

设想一下今天用户的启动情况:

  • 20:55-23:35 启动了投资组合管理应用
  • 8:55-11:35 没有启动投资组合管理应用
  • 13:25-15:35 又启动了投资组合管理应用

9:00-11:30 期间进行的各个应用进行的操作就无法被PortfolioManager截获,当然也就无法被记录到下面的两个json文件中:

    portfolio_manager_data.json
    portfolio_manager_order.json

当我们再次打开投资组合管理应用的时,看到的组合列表和各个组合列表的成交记录就可能是有遗漏的!
那么这样的成交记录查询结果就会给用户有种莫名其妙的感觉。

因此,投资组合管理应用应该是自动启动应用。

3. 投资组合管理应用缺失的功能

  1. 只能显示当前交易日的成交记录,无法显示历史交易记录,也没有盈亏统计。这里显示的交易记录是调用的self.main_engine.get_all_trades()——不包含历史成交记录。
  2. 没有当前交易日的委托记录查询,也没有历史交易日的委托记录查询。
  3. 按照当前设计,取名“投资组合管理”有点名不副实,应该叫“成交记录查询”更合适些。
  4. 既然叫“投资组合管理”,那么应该可以有可以干预的功能在里面,比如:
    • 如果手动平掉的是策略自动开仓的仓位,可以在这里把它们关连起来。
    • 允许用户对记录进行删减或者修改,以便与当前系统各个应用的实际运行情况一致,而不是打开一个个json文件进行手工修改,那样对用户的要求高不说,也容易出错。

linhertz wrote:

请教一下,按照您的步骤申请了账号,为什么连不上?

description

description

答复:

注意:
第3步里下载的license,dat(原来是压缩文件,解压后得到)一定要放在第4步里的②运行目录下;
你的连接界面里的用户名和密码,一定要是第2步里查询到的期货测试账号和测试密码。

用Python的交易员 wrote:

WOW,感谢分享,必须加个精华。

另外我们想在微信社区公众号分享下,是否可以?

没有问题,只要对社区有帮助,岂不是好事一桩 !

longgerchen wrote:

description

description

我在复制如上代码之后,会碰到这两个问题,不知如何解决。

答复:
第一个是需要修改为OrderData,
原来的是错误的,不过好在这只是个注解,不会引擎出错。
第二个是需要在该文件的前面:

from vnpy.trader.constant import Exchange

VNPY如何连接UFX对接恒生云接口?且随我一步一步往下做。

1. 注册恒生云期货模拟

在浏览器中输入http://ufx.hs.net,进入如下界面:
description

点击注册,进入恒生云测试平台的用户注册,①输入手机号,③密码,④密码确认和②验证码,勾选下面的同意,就可以注册恒生云测试平台的用户了。

description

2. 查询模拟期货账户及密码

完成了第1步之后,你已经有一个恒生云测试平台的用户,再次在浏览器中输入http://ufx.hs.net,输入您的手机号和密码,就可以登录进入下面的界面:
description
点击图中的申请期货、证券或者股票期权下的按钮,就可以申请你想要的模拟市场,其中不同模拟市场的账户或者密码可能不一样。如下图所示。
记住这里测试账号和密码,这是我们vnpy要登录的UFT网关中的用户名与密码!
点击图中的④接口下载,进入下一步。

3. 下载接口认证文件lisence.dat及行情和交易服务器地址

description
点击1下载,下载一个包含认证文件lisence.dat的rar文件,同时记录②站点ip和端口号,这是vnpy要登录的UFT网关中行情和交易服务器的ip和端口号。
记录④,⑤,分别为直连模式下的接入方ID和授权码,这是vnpy要登录的UFT网关中产品名称和授权码。

4. 登录vnpy

启动vnpy时,在配置VN Trader界面中,如图所示,勾选恒生UFT,记住图中的②运行目录,一定把步骤3中下载的认证文件lisence.dat复制到该目录。然后点击③启动VN Trader。
description

5. 连接UFX恒生云模拟接口

进入VN Trader主界面后,选择系统>连接UFT,进入如下的UFT连接参数设置。
其中①-⑦各项在第2步和第3步中都已经获得,只是注意图中的⑧,委托类型必须填7,这是恒生云客服告诉我的!
然后点击连接按钮就可以成功登录UFX恒生云模拟账户了。
description

6. 订阅行情

下图中显示的是成功登录UFX恒生云模拟账户后,可以看到①接口登录日志、②资金账户都已经显示正确信息。
接下来你可以在③交易模块中的输入交易所和代码,回车后就可以看到④行情模块中已经成功订阅了MA109.CZCE和rb2110.SHFE的行情了。
到此VNPY成功连接UFX对接恒生云模拟接口!
description

7. 交易测试

成功连接恒生云模拟接口,免不了要测试下能否交易,——结果见下图:

description

8. 这哪里是模拟接口,其实是个测试接口!

连接恒生云UFX,目前还问题多多!

  1. 经常连不上
  2. 连上了,报授权码无效或者过期
  3. 休市期间恒生云接口会重演行情数据,这个最让人不能理解!
  4. 休市期间重演行情数据的时候,还可以模拟交易!
  5. 感觉恒生云模拟接口的定位是测试而非模拟,他们的目标是为了调试程序接口是否正常,而不是为用户的交易策略的运行提供一个贴近实盘的交易环境吧,重演就说明了这个问题。

8.1 去掉休市行情重演和休市交易功能

目前,经过和恒生云急速API客服团队的沟通,要求他们把恒生云去掉休市行情重演和休市可以交易功能,意见已经被采纳了。
他们打算提供提两套接口,一个是模拟环境接口(无休市行情重演和交易),一个是测试接口(有休市行情重演和交易)。

8.2 目前已经和CTP一样,是一个没有休市重演和休市交易了

恒生云的还是快速做出调整,目前模拟接口已经没有了休市行情重演和休市交易功能,给一个大大的赞!

9. CTP接口又可以使用了!

鉴于恒生云接口目前问题不少,同时CTP模拟接口又可以用了,不愿意折腾恒生云接口的小伙伴们又可以重新使用CTP了。
只是需要重新修改下新的行情和交易服务端口号就可以了,沿用之前的账户和密码,不过以前的资金回复为2000万,同时以前交易记录也都清零了。
具体设置见 公告:SimNow系统切换更新通告

无论如何,小伙伴们现在已经可以有三个解决模拟的接口:1、CTP模拟交易;2、恒生模拟交易;3、PaperAccount。选择哪一个?自己决定吧!

10. 恒生UFT网关成交单查询应答错误的解决

如果细心的话,您也许会发现[7. 交易测试】的图中没有成交单,这是因为UFT网关成交单查询应答出错误了!不过本人经找把问题已到并且解决了。
详细见:恒生UFT网关成交单查询应答错误

用Python的交易员 wrote:

mango wrote:

没有simnow,可以选择在VNPY中通过UFX对接恒生云7*24小时环境
详情如下:
https://mp.weixin.qq.com/s/-Yrxqa34cSXqZe0CjCJ_Tw

第一时间通过文中链接申请了,但是申请完成后没有直接给账号和服务器信息,查了邮箱也没收到任何邮件呢

请问,您的UFX对接恒生云搞好了吗?
我申请完成后也没有直接给账号和服务器信息,查了邮箱也没收到任何邮件。

黄裳 wrote:

你调用talib.MACD和文化的macd图形能对上吗??

K线序列相同的情况下,是相同的,不用怀疑。
我的意思是K线的产生方式是一样的,那么MACD指标是一样的。
如果不相同,请检查K线产生方式是否有区别。

1. 升级了,MACD不正常了!

很久没有升级vnpy了,前几天升级了,K线图表MACD,为什么?
差了下,MacdItem和ChartWighet,都没有修改,为什么不正常了呢?原来是pyqtgraph升级了,研究了半天也是一头雾水。

2. 修改了可以了,代码如下:

class MacdItem(ChartItem):
    """"""
    _values_ranges: Dict[Tuple[int, int], Tuple[float, float]] = {}

    last_range:Tuple[int, int] = (-1,-1)    # 最新显示K线索引范围

    def __init__(self, manager: BarManager):
        """"""
        super().__init__(manager)

        self.white_pen: QtGui.QPen = pg.mkPen(color=(255, 255, 255), width=1)
        self.yellow_pen: QtGui.QPen = pg.mkPen(color=(255, 255, 0), width=1)
        self.red_pen: QtGui.QPen = pg.mkPen(color=(255, 0, 0), width=1)
        self.green_pen: QtGui.QPen = pg.mkPen(color=(0, 255, 0), width=1)

        self.short_window = 12
        self.long_window = 26
        self.M = 9

        self.macd_data: Dict[int, Tuple[float,float,float]] = {}

    def get_macd_value(self, ix: int) -> Tuple[float,float,float]:
        """"""

        if ix < 0:
            return (0.0,0.0,0.0)

        # When initialize, calculate all macd value
        if not self.macd_data:
            bars = self._manager.get_all_bars()
            close_data = [bar.close_price for bar in bars]

            diffs,deas,macds = talib.MACD(np.array(close_data), 
                                    fastperiod=self.short_window, 
                                    slowperiod=self.long_window, 
                                    signalperiod=self.M)

            for n in range(0,len(diffs)):
                self.macd_data[n] = (diffs[n],deas[n],macds[n])

        # Return if already calcualted
        if ix in self.macd_data:
            return self.macd_data[ix]

        # Else calculate new value
        close_data = []
        for n in range(ix-self.long_window-self.M+1, ix + 1):
            bar = self._manager.get_bar(n)
            close_data.append(bar.close_price)

        diffs,deas,macds = talib.MACD(np.array(close_data), 
                                            fastperiod=self.short_window, 
                                            slowperiod=self.long_window, 
                                            signalperiod=self.M) 
        diff,dea,macd = diffs[-1],deas[-1],macds[-1]
        self.macd_data[ix] = (diff,dea,macd)

        return (diff,dea,macd)

    def _draw_bar_picture(self, ix: int, bar: BarData) -> QtGui.QPicture:
        """"""
        macd_value = self.get_macd_value(ix)
        last_macd_value = self.get_macd_value(ix - 1)

        # # Create objects
        picture = QtGui.QPicture()
        painter = QtGui.QPainter(picture)

        # # Draw macd lines
        if np.isnan(macd_value[0]) or np.isnan(last_macd_value[0]):
            # print("略过macd lines0")
            pass
        else:
            end_point0 = QtCore.QPointF(ix, macd_value[0])
            start_point0 = QtCore.QPointF(ix - 1, last_macd_value[0])
            painter.setPen(self.white_pen)
            painter.drawLine(start_point0, end_point0)

        if np.isnan(macd_value[1]) or np.isnan(last_macd_value[1]):
            # print("略过macd lines1")
            pass
        else:
            end_point1 = QtCore.QPointF(ix, macd_value[1])
            start_point1 = QtCore.QPointF(ix - 1, last_macd_value[1])
            painter.setPen(self.yellow_pen)
            painter.drawLine(start_point1, end_point1)

        if not np.isnan(macd_value[2]):
            if (macd_value[2]>0):
                painter.setPen(self.red_pen)
                painter.setBrush(pg.mkBrush(255,0,0))
            else:
                painter.setPen(self.green_pen)
                painter.setBrush(pg.mkBrush(0,255,0))
            painter.drawRect(QtCore.QRectF(ix-0.3,0,0.6,macd_value[2]))
        else:
            # print("略过macd lines2")
            pass

        painter.end()
        return picture

    def boundingRect(self) -> QtCore.QRectF:
        """"""
        min_y, max_y = self.get_y_range()
        rect = QtCore.QRectF(
            0,
            min_y,
            len(self._bar_picutures),
            max_y
        )
        return rect

    def get_y_range(self, min_ix: int = None, max_ix: int = None) -> Tuple[float, float]:
        #   获得3个指标在y轴方向的范围   
        #   hxxjava 修改,2020-6-29
        #   当显示范围改变时,min_ix,max_ix的值不为None,当显示范围不变时,min_ix,max_ix的值不为None,

        offset = max(self.short_window,self.long_window) + self.M - 1

        if not self.macd_data or len(self.macd_data) < offset:
            # print(f'(min_ix,max_ix){(min_ix,max_ix)} offset={offset},len(self.macd_data)={len(self.macd_data)}')
            # hxxjava 修改,2021-5-8,因为升级vnpy,其依赖的pyqtgraph版本也升级了,原来为return 0,1
            return -100, 100

        # print("len of range dict:",len(self._values_ranges),",macd_data:",len(self.macd_data),(min_ix,max_ix))

        if min_ix != None:          # 调整最小K线索引
            min_ix = max(min_ix,offset)

        if max_ix != None:          # 调整最大K线索引
            max_ix = min(max_ix, len(self.macd_data)-1)

        last_range = (min_ix,max_ix)    # 请求的最新范围   

        if last_range == (None,None):   # 当显示范围不变时
            if self.last_range in self._values_ranges:  
                # 如果y方向范围已经保存
                # 读取y方向范围
                result = self._values_ranges[self.last_range]
                # print("1:",self.last_range,result)
                return adjust_range(result)
            else:
                # 如果y方向范围没有保存
                # 从macd_data重新计算y方向范围
                min_ix,max_ix = 0,len(self.macd_data)-1

                macd_list = list(self.macd_data.values())[min_ix:max_ix + 1]
                ndarray = np.array(macd_list)           
                max_price = np.nanmax(ndarray)
                min_price = np.nanmin(ndarray)

                # 保存y方向范围,同时返回结果
                result = (min_price, max_price)
                self.last_range = (min_ix,max_ix)
                self._values_ranges[self.last_range] = result
                # print("2:",self.last_range,result)
                return adjust_range(result)

        """ 以下为显示范围变化时 """

        if last_range in self._values_ranges:
            # 该范围已经保存过y方向范围
            # 取得y方向范围,返回结果
            result = self._values_ranges[last_range]
            # print("3:",last_range,result)
            return adjust_range(result)

        # 该范围没有保存过y方向范围
        # 从macd_data重新计算y方向范围
        macd_list = list(self.macd_data.values())[min_ix:max_ix + 1]
        ndarray = np.array(macd_list) 
        max_price = np.nanmax(ndarray)
        min_price = np.nanmin(ndarray)

        # 取得y方向范围,返回结果
        result = (min_price, max_price)

        self.last_range = last_range
        self._values_ranges[self.last_range] = result
        # print("4:",self.last_range,result)

        return adjust_range(result)


    def get_info_text(self, ix: int) -> str:
        # """"""
        if ix in self.macd_data:
            diff,dea,macd = self.macd_data[ix]
            words = [
                f"diff {diff:.3f}"," ",
                f"dea {dea:.3f}"," ",
                f"macd {macd:.3f}"
                ]
            text = "\n".join(words)
        else:
            text = "diff - \ndea - \nmacd -"

        return text

3. 修改后显示效果

description

一、利用米筐找到有集合竞价的交易时间段

1.1 代码

import datetime
from rqdatac.utils import to_date
import rqdatac as rq

if __name__ == "__main__":
     def test():
        symbols = ['rb2110','sr2109','ag2106','ap2110','IF2105']
        date0 = datetime.date(2021,4,29)
        for symbol in symbols:
            ths = rq.get_trading_hours(order_book_id=symbol.upper(),date=date0,frequency='tick',expected_fmt='datetime')
            print(f"{symbol} 的交易时间段:")
            for th in ths:
                print(f"    ths={th}")

    # 初始化米筐接口 
    rq.init('xxxxxx','xxxxxx',("rqdatad-pro.ricequant.com",16011)) # 用自己的米筐账户和密码

    test()

1.2 执行结果

各个合约的交易时间段:

rb2110 的交易时间段:
    ths=[datetime.datetime(2021, 4, 28, 21, 0), datetime.datetime(2021, 4, 28, 23, 0)]
    ths=[datetime.datetime(2021, 4, 29, 9, 0), datetime.datetime(2021, 4, 29, 10, 15)]
    ths=[datetime.datetime(2021, 4, 29, 10, 30), datetime.datetime(2021, 4, 29, 11, 30)]
    ths=[datetime.datetime(2021, 4, 29, 13, 30), datetime.datetime(2021, 4, 29, 15, 0)]
sr2109 的交易时间段:
    ths=[datetime.datetime(2021, 4, 28, 21, 0), datetime.datetime(2021, 4, 28, 23, 0)]
    ths=[datetime.datetime(2021, 4, 29, 9, 0), datetime.datetime(2021, 4, 29, 10, 15)]
    ths=[datetime.datetime(2021, 4, 29, 10, 30), datetime.datetime(2021, 4, 29, 11, 30)]
    ths=[datetime.datetime(2021, 4, 29, 13, 30), datetime.datetime(2021, 4, 29, 15, 0)]
ag2106 的交易时间段:
    ths=[datetime.datetime(2021, 4, 28, 21, 0), datetime.datetime(2021, 4, 29, 2, 30)]
    ths=[datetime.datetime(2021, 4, 29, 9, 0), datetime.datetime(2021, 4, 29, 10, 15)]
    ths=[datetime.datetime(2021, 4, 29, 10, 30), datetime.datetime(2021, 4, 29, 11, 30)]
    ths=[datetime.datetime(2021, 4, 29, 13, 30), datetime.datetime(2021, 4, 29, 15, 0)]
ap2110 的交易时间段:
    ths=[datetime.datetime(2021, 4, 29, 9, 0), datetime.datetime(2021, 4, 29, 10, 15)]
    ths=[datetime.datetime(2021, 4, 29, 10, 30), datetime.datetime(2021, 4, 29, 11, 30)]
    ths=[datetime.datetime(2021, 4, 29, 13, 30), datetime.datetime(2021, 4, 29, 15, 0)]
IF2105 的交易时间段:
    ths=[datetime.datetime(2021, 4, 29, 9, 30), datetime.datetime(2021, 4, 29, 11, 30)]
    ths=[datetime.datetime(2021, 4, 29, 13, 0), datetime.datetime(2021, 4, 29, 15, 0)]

1.3 找到各个合约的前面带有集合竞价的交易时间段

看看上面各个合约的所有交易时间段,其中第一个交易时间段开始前就有交易时间段。
rb2110 的带有集合竞价的交易时间段:[datetime.datetime(2021, 4, 28, 21, 0), datetime.datetime(2021, 4, 28, 23, 0)]
sr2109 的带有集合竞价的交易时间段:[datetime.datetime(2021, 4, 28, 21, 0), datetime.datetime(2021, 4, 28, 23, 0)]
ag2106 的带有集合竞价的交易时间段:[datetime.datetime(2021, 4, 28, 21, 0), datetime.datetime(2021, 4, 29, 2, 30)]
ap2110 的带有集合竞价的交易时间段:[datetime.datetime(2021, 4, 29, 9, 0), datetime.datetime(2021, 4, 29, 10, 15)]
IF2105 的带有集合竞价的交易时间段:[datetime.datetime(2021, 4, 29, 9, 30), datetime.datetime(2021, 4, 29, 11, 30)]

二、找到有集合竞价交易时间段的意义

2.1 多出来的1根1分钟K线

我们知道每个合约每个交易日开盘前都带有集合竞价时段,日盘在上午,夜盘在晚上,不能统一规定。
集合竞价时段发生在开盘前,结束后CTP行情接口会收到一个tick数据,而这个数据的时间戳不是9:00也不是21:00,所以会导致vnpy的客户端的
BarGenerator产生一根1分钟K线。而这根1分钟K线本应该属于9:00后者21:00的那根1分钟K线的!

2.2 特别的A股股票集合竞价

注意到A股股票1日有两个集合竞价时段,上午的9:15-9:25和下午的14:57-15:00。可是下午的14:57-15:00的集合竞价不会对BarGenerator产生K线产生任何影响。
其中上午的9:15-9:25又分为竞价委托9:15-9:20和集合竞价9:20-9:25两部分。这一部分结束后会在 9:25产生一个tick数据并推送到客户端,会对目前的BarGenerator产生K线产生影响,因为BarGenerator无法把这个tick数据合并到9:30到那个1分钟K线中!

2.3 修改vnpy的BarGenerator,使得可以正确处理集合竞价

有了上一贴的讨论和第一部分的测试,我们可以对vnpy的BarGenerator进行修改,使之能够正确处理集合竞价!

期货集合竞价时间期货集合竞价规则
大多数投资者每天的交易都是从集合竞价开始的,并且很多投资者还会参与到其中来,那么期货集合竞价时间和集合竞价规则到是什么呢?

一、什么是期货的集合竞价

集合竞价指在每个期货交易日开市前的规定时间里,由期货交易者按照自己所能接受的价格进行买卖申报。

description

二、期货集合竞价的时间和规则

每一交易日开市前5分钟内,前4分钟为投资者对期货合约买、卖价格指令申报时间,后1分钟为集合竞价撮合时间,集合竞价产生的价格即为开盘价,随即在行情栏中显示。
这里需要注意: .
①集合竞价只能使用限价指令申报,不能使用市价指令。
②日盘品种(指无夜盘的品种)的集合竞价时间是8:55-8:59.夜盘品种的集合竞价时间是20:55-20:59.有夜盘的品种日盘不再进行集合竞价。
③集合竞价期间的申报价格盘面是不显示的,只能根据自己的预判进行申报,申报价格范围为上一交易日结算价的涨跌停板价。

description

                                                股指期货时间轴

description

                                                 国债期货时间轴

三、集合竞价产生的原理

国内期货交易所均采用计算机撮合成交方式,指交易所的计算机交易系统对交易双方的交易指令进行配对的过程,按价格优先、时间优先的原则进行配对,以此价格成交能够
得到最大成交量。
首先,交易系统分别对所有有效的买入申报按申报价由高到低的顺序排列,申报价相同的按照进入系统的时间先后排列;所有有效的卖出申报按申报价由低到高的顺序排列,申报价相同的按照进入系统的时间先后排列。
接下来,交易系统依此逐步将排在前面的买入申报和卖出申报配对成交,直到不能成交为止。

四、集合竞价举例详解

假定,某合约申报排序如下表所示(最小变动价为1元)。

description

集合竞价申报顺序表
配对情况为:
①排序为1的买进价高于排序为1的卖出价,成交30手;
②排序为1的买进价还有20手没成交,由于比排序为2的卖出价高,继续成交20手;
③排序为2的卖出价还有40手没成交,由于比排序为2的买进价低,继续成交40手;
④排序为2的买进价还有50手没成交,由于比排序为3的卖出价高,成交50手;
⑤排序为3的卖出价还有70手没成交,由于比排序为3的买进价高,显然无法成交。
这样,最终的开盘价就是2588元,在此价位上成交140手(30+20+40+50=140.如按双边计算则是280手)。
申报指令的撮合成交原理为:
①高于集合竞价产生的价格的买入申报全部成交;
②低于集合竞价产生的价格的卖出申报全部成交;
③等于集合竞价产生的价格的买入或卖出申报,根据买入申报量和卖出申报量的多少,按少的一方的申报量成交。
拿玉米C1901来举例:

description

玉米合约申报价格列表
假设集合竞价最后产生的开盘价是1460.
则对于买单,根据“高于集合竞价产生的价格的买入申报全部成交”原则,1461和1462的买入单都成交;
对于卖单,根据“低于集合竞价产生的价格的卖出申报全部成交”原则,1459和1458的卖出单都成交;
而等于1460的买入或卖出单,原则是:根据买入申报量和卖出申报量的多少,按少的一方的申报量成交。该例子中,1460买入 是20手,卖出是30手,故撮合成交20手,卖出单
有10手没有撮合成交。
集合竞价撮合成交期间的未成交申报单在开市后自动参与连续竞价交易,当天若未成交,结算时则会自动撤单。

五、另附:A股的竞价规则

A股的竞价分为集合竞价和连续竞价两个部分,集合竞价是决定开盘和深市收盘两个价格的重要部分,正因为如此,根据《上市公司股东、董监高减持股份的若干规定》,持股5%以上的大股东进行减持的,三个月内通过集合竞价交易减持的不得超过公司总股本的1%、半年2%,一年不得超过4%,这是一个量的规定。

5.1 准确的交易时间

沪市:上午9:15-9:25集合竞价,上午9:30-11:30和下午13:00-14:57连续交易时间,14:57-15:00为收盘集合竞价时间;

深市:上午9:15-9:25集合竞价,上午9:30-11:30和下午13:00-14:57连续交易时间,14:57-15:00为收盘集合竞价时间。

5.2 在A股市场,开盘价是由集合竞价确定,具体的竞价时间是开盘前的:

1.隔夜委托22:00-9:15
交易日前一晚上22:00-9:15之间的委托叫隔夜委托,即第一个交易日晚上规定时间之后就可以进行第二交易日的委托,委托会暂存在证券公司系统内,在第二天早上9:15分时报送到交易所主机,是由券商统一在集合竞价时间上报到交易所,这个上报的顺序理论上是不分时间,只管价格,但如果股票是涨跌停的,尤其是连续涨跌停的股票,比如新股和被执行风险警示的股票,价格是确定的,总还是有个时间的优先次序,这个时候股票委托按照“时间优先、数量优先、价格优先、大单先成交”的原则,所以如果第二天无论如何都要出来或者进去的,建议隔夜委托,特别是对于确定涨停的股票,在券商交易结算后,一般是22:00以后就下单委托。

2.竞价委托9:15-9:20
这个竞价时间是可以委托也是可以撤单的,其中隔夜委托的单子在这个时间也是可以撤掉的,但时间也就是这五分钟,所以要特别留意。

3.集合竞价9:20-9:25
这个竞价时间只能委托,但不能撤单了,截止到9:25分00秒,确定一个成交量最大的一个价格,就是开盘价,开盘价影响一天的价格,会记录在K线上面,所以非常重要。

4.封闭竞价9:25-9:30/11:30-13:00
这个时间表面上是可以委托和撤单的,但实际上你的所有委托都是暂时保存在券商系统里面,交易所在9:30统一接收,所以这个时间是封闭竞价时间,最终的开市价格由委托的单子撮合成交得出,所以开盘价格有两个:一个是集合竞价确定的开盘价,一个是撮合成交得出的开市价。而下午13:00的开市价格也是这么来的。

5.收盘竞价14:57-15:00
沪市和深市的开盘和收盘前都有一个竞价时间,以同一价格成交量最大化为确定原则。

© 2015-2022 微信 18391752892
备案服务号:沪ICP备18006526号

沪公网安备 31011502017034号

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