守望长城2020-6-11-艾瑞巴蒂 wrote:
测试成功.
反馈一个bug, 在图表最左边(最初始的部分K线图,那个时候macd值还未产生)用鼠标右键缩放的时候,会报错,报错信息如下:
Traceback (most recent call last):
File "C:\Users\lx\Desktop\vnpy2022-9-20-在线可运行K线版 - 副本\vnpy\chart\widget.py", line 217, in _update_y_range
y_range = item.get_y_range(min_ix, max_ix)
File "C:\Users\lx\Desktop\vnpy2022-9-20-在线可运行K线版 - 副本\vnpy\usertools\kx_chart.py", line 428, in get_y_range
max_price = np.nanmax(ndarray)
File "<__array_function__ internals>", line 6, in nanmax
File "C:\vnstudio\lib\site-packages\numpy\lib\nanfunctions.py", line 434, in nanmax
res = np.fmax.reduce(a, axis=axis, out=out, **kwargs)
ValueError: zero-size array to reduction operation fmax which has no identity
答复:
既然发现了问题,动动您发财的手帮大家改一下吧 。。。 :)
守望长城2020-6-11-艾瑞巴蒂 wrote:
还有一个问题请大神解答下, 我自己编写了一个chartItem, 用于显示数据点的, 用的是drawPoint绘制方式, 随着不同数据范围下, drawPoint被拉得老长了, 变成了一个长的柱子, 有没有办法就让它一直显示为一个方形的点, 不要自适应拉长
答复:
这个是不可能的,因为你画的矩形的宽度与高度的单位是不同的,宽度单位是时间(多少个K线),高度是价格,怎么能够一样呢?
如果你硬是想显示为正方形,建议使用PyQtGraph的ScutterItem,可以参考回测界面里的K线图表显示开仓和平仓成交单的方法来显示,那是用上下三角形来表达的,不会因为K线图标的缩放而改变大小。
文韬武略 wrote:
配置的是5
文件D:\ProgramFiles\vnstudio\lib\site-packages\vnpy\trader\utility.py"中BarGenerator的update_bar_minute_window(bar)有bug,这样修改:
def update_bar_minute_window(self, bar: BarData) -> None:
""""""
# If not inited, create window bar object
if not self.window_bar:
dt = bar.datetime.replace(second=0, microsecond=0)
self.window_bar = BarData(
symbol=bar.symbol,
exchange=bar.exchange,
datetime=dt,
gateway_name=bar.gateway_name,
open_price=bar.open_price,
high_price=bar.high_price,
low_price=bar.low_price
)
# Otherwise, update high/low price into window bar
else:
self.window_bar.high_price = max(
self.window_bar.high_price,
bar.high_price
)
self.window_bar.low_price = min(
self.window_bar.low_price,
bar.low_price
)
# Update close price/volume/turnover into window bar
self.window_bar.close_price = bar.close_price
self.window_bar.volume += bar.volume
self.window_bar.turnover += bar.turnover
self.window_bar.open_interest = bar.open_interest
# Check if window bar completed
if self.on_window_bar and self.window: # hxxjava add
if not (bar.datetime.minute + 1) % self.window:
self.on_window_bar(self.window_bar)
self.window_bar = None
from vnpy_ctastrategy import (
CtaTemplate,
StopOrder,
TickData,
BarData,
TradeData,
OrderData,
BarGenerator,
ArrayManager,
)
# from my_strategies_tool import NewBarGennerator
from vnpy.trader.constant import Interval
class DemoStrategy(CtaTemplate):
"""演示用的简单双均线"""
# 策略作者
author = "Smart Trader"
# 定义参数
fast_window = 10
slow_window = 20
fast_window1 = 10
slow_window1 = 20
k=5
# 定义变量
fast_ma0 = 0.0
fast_ma1 = 0.0
slow_ma0 = 0.0
slow_ma1 = 0.0
fast_ma01 = 0.0
fast_ma11 = 0.0
slow_ma01 = 0.0
slow_ma11 = 0.0
cross_over2=True
cross_below2=True
# 添加参数和变量名到对应的列表
parameters = ["fast_window", "slow_window","k","fast_window", "slow_window"]
variables = ["fast_ma0", "fast_ma1", "slow_ma0", "slow_ma1","fast_ma01", "fast_ma11", "slow_ma01", "slow_ma11","cross_over2","cross_below2"]
def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
""""""
super().__init__(cta_engine, strategy_name, vt_symbol, setting)
# K线合成器:从Tick合成分钟K线用xzsde
self.bg = BarGenerator(self.on_bar)
self.bg15=BarGenerator(self.on_bar, window=self.k, on_window_bar=self.on_5min_bar, interval=Interval.MINUTE)
# 时间序列容器:计算技术指标用
self.am = ArrayManager()
self.am2=ArrayManager()
self.bars_num = 0 # hxxjava add
self.bars15_num = 0 # hxxjava add
def on_init(self):
"""
当策略被初始化时调用该函数。
"""
# 输出个日志信息,下同
self.write_log("策略初始化")
# 加载10天的历史数据用于初始化回放
self.load_bar(10)
def on_start(self):
"""
当策略被启动时调用该函数。
"""
self.write_log("策略启动")
# 通知图形界面更新(策略最新状态)
# 不调用该函数则界面不会变化
self.put_event()
def on_stop(self):
"""
当策略被停止时调用该函数。
"""
self.write_log("策略停止")
self.put_event()
def on_tick(self, tick: TickData):
"""
通过该函数收到Tick推送。
"""
self.bg.update_tick(tick)
# self.bg15.update_tick(tick)
def on_bar(self, bar: BarData):
"""
通过该函数收到新的1分钟K线推送。
"""
self.bars_num += 1 # hxxjava add
self.bg.update_bar(bar)
self.bg15.update_bar(bar)
am = self.am
am.update_bar(bar)
if not am.inited:
return
fast_ma1 = am.sma(self.fast_window1, array=True)
self.fast_ma01 = fast_ma1[-1] # T时刻数值
self.fast_ma11 = fast_ma1[-2] # T-1时刻数值
# 计算慢速均线
slow_ma1 = am.sma(self.slow_window1, array=True)
self.slow_ma01 = slow_ma1[-1]
self.slow_ma11 = slow_ma1[-2]
# 判断是否金叉
self.cross_over2 = (self.fast_ma01 > self.slow_ma01 and
self.fast_ma11 < self.slow_ma11)
# 判断是否死叉
self.cross_below2 = (self.fast_ma01 < self.slow_ma01 and
self.fast_ma11 > self.slow_ma11)
def on_5min_bar(self, bar: BarData):
self.bars15_num += 1 # hxxjava add
print(f"1分钟bar到数量={self.bars_num} k分钟bar到数量={self.bars15_num}") # hxxjava add
am2=self.am2
# 更新K线到时间序列容器中
am2.update_bar(bar)
# 若缓存的K线数量尚不够计算技术指标,则直接返回
if not am2.inited:
return
# 计算快速均线
fast_ma = am2.sma(self.fast_window, array=True)
self.fast_ma0 = fast_ma[-1] # T时刻数值
self.fast_ma1 = fast_ma[-2] # T-1时刻数值
# 计算慢速均线
slow_ma = am2.sma(self.slow_window, array=True)
self.slow_ma0 = slow_ma[-1]
self.slow_ma1 = slow_ma[-2]
# 判断是否金叉
cross_over = (self.fast_ma0 > self.slow_ma0 and
self.fast_ma1 < self.slow_ma1)
# 判断是否死叉
cross_below = (self.fast_ma0 < self.slow_ma0 and
self.fast_ma1 > self.slow_ma1)
# 如果发生了金叉
if cross_over and self.cross_over2:
# 为了保证成交,在K线收盘价上加5发出限价单
price = bar.close_price + 5
# 当前无仓位,则直接开多
if self.pos == 0:
self.buy(price, 1)
# 当前持有空头仓位,则先平空,再开多
elif self.pos < 0:
self.cover(price, 1)
self.buy(price, 1)
# 如果发生了死叉
elif cross_below and self.cross_below2:
price = bar.close_price - 5
# 当前无仓位,则直接开空
if self.pos == 0:
self.short(price, 1)
# 当前持有空头仓位,则先平多,再开空
elif self.pos > 0:
self.sell(price, 1)
self.short(price, 1)
self.put_event()
def on_order(self, order: OrderData):
"""
通过该函数收到委托状态更新推送。
"""
pass
def on_trade(self, trade: TradeData):
"""
通过该函数收到成交推送。
"""
# 成交后策略逻辑仓位发生变化,需要通知界面更新。
self.put_event()
def on_stop_order(self, stop_order: StopOrder):
"""
通过该函数收到本地停止单推送。
"""
pass
1分钟bar的数量=10 k分钟bar的数量=2
2022-08-27 08:56:53 1分钟bar的数量=15 k分钟bar的数量=3
1分钟bar的数量=20 k分钟bar的数量=4
1分钟bar的数量=25 k分钟bar的数量=5
1分钟bar的数量=30 k分钟bar的数量=6
1分钟bar的数量=35 k分钟bar的数量=7
1分钟bar的数量=40 k分钟bar的数量=8
1分钟bar的数量=45 k分钟bar的数量=9
1分钟bar的数量=50 k分钟bar的数量=10
2022-08-27 08:56:53 1分钟bar的数量=55 k分钟bar的数量=11
1分钟bar的数量=60 k分钟bar的数量=12
1分钟bar的数量=65 k分钟bar的数量=13
1分钟bar的数量=70 k分钟bar的数量=14
1分钟bar的数量=75 k分钟bar的数量=15
1分钟bar的数量=80 k分钟bar的数量=16
1分钟bar的数量=85 k分钟bar的数量=17
1分钟bar的数量=90 k分钟bar的数量=18
1分钟bar的数量=95 k分钟bar的数量=19
1分钟bar的数量=100 k分钟bar的数量=20
1分钟bar的数量=105 k分钟bar的数量=21
1分钟bar的数量=110 k分钟bar的数量=22
1分钟bar的数量=115 k分钟bar的数量=23
1分钟bar的数量=120 k分钟bar的数量=24
1分钟bar的数量=125 k分钟bar的数量=25
1分钟bar的数量=130 k分钟bar的数量=26
1分钟bar的数量=135 k分钟bar的数量=27
1分钟bar的数量=140 k分钟bar的数量=28
1分钟bar的数量=145 k分钟bar的数量=29
1分钟bar的数量=150 k分钟bar的数量=30
1分钟bar的数量=155 k分钟bar的数量=31
1分钟bar的数量=160 k分钟bar的数量=32
1分钟bar的数量=165 k分钟bar的数量=33
1分钟bar的数量=170 k分钟bar的数量=34
。。。 。。。
self.k 配置的时候是多少,不能够是0
错误已经提示了:
···
File "C:\veighna_studio\lib\site-packages\vnpy\trader\utility.py", line 302, in update_bar_minute_window
if not (bar.datetime.minute + 1) % self.window:
ZeroDivisionError: integer division or modulo by zero
···
逆火 wrote:
在add_gateway()中的这个语句跟哪里有联系没看出来,self.tick_filters 这个字典在哪里用到了??
def on_tick(self, tick: TickData):
"""
通过该函数收到Tick推送。
"""
self.bg.update_tick(tick)
# self.bg15.update_tick(tick) # —— 先注释掉这一句
self.bg和self.bg15共用了同一个on_bar()函数,在tick到来的时候on_tick()只要调用周期较小self.bg的update_tick就可以生成1分钟bar了。无需两个都更新tick来得到两个相同的1分钟bar,然后又两次调用on_bar()函数,因为self.bg生成1分钟bar时会调用on_bar()函数,self.bg15也会生成一个相同的1分钟bar,它也会调用on_bar()函数,这不符合你的设计本意,对吧?
先这么改下看看。
感觉你的问题与TickFilter的过滤无关,因为:
由此可见:你的问题与TickFilter的过滤无关。
逆火 wrote:
大佬 在用价差策略的时候,状态不对,请教
王不同 wrote:
请教,怎么获取一根未完成的k线的指标值
逆火 wrote:
交易所推送status的时间是多久一次
答复:
- 通常一个品种一个交易日有1个集合竞价时间段,n个连续竞价时间段的话,那么会收到:
所以正常情况下会收到2n+3个交易状态非交易时间状态——1个 集合竞价开始状态——1个 集合竞价匹配状态——1个 第一个连续竞价状态——1个 第一次休市状态——1个 第二个连续竞价状态——1个 第二次休市状态 ——1个 ... 第n个连续竞价状态——1个 交易日收盘状态 ——1个
- 如果某个合约盘中因为极端行情导致停盘,延迟一段时间后再次回复交易,则每停盘1次就又会多收到1个停盘交易状态+1个连续交易状态。如果一个交易日如果停盘m次,则整个交易日会收到2n+2m+3个交易状态。
- 交易所推送交易状态根据不同品种或合约分别推送的,例如你可能在交易日的晚21:00收到约60多个连续竞价交易状态信息,这其中大部分是品种,也可能还包含一些具体合约的交易状态信息。
逆火 wrote:
请教大佬 tick过滤器的实例化在这里 ,没找到 在哪里调用tick数据的传输路径是什么样的
def on_tick(self, tick: TickData) -> None:
"""
Tick event push.
Tick event of a specific vt_symbol is also pushed.
"""
self.on_event(EVENT_ORIGIN_TICK, tick) # hxxjava add
# if tick.first_time: # hxxjava add
# return
# self.on_event(EVENT_TICK, tick)
# self.on_event(EVENT_TICK + tick.vt_symbol, tick)
这里将EVENT_TICK消息类型替换为新定义的EVENT_ORIGIN_TICK消息。TickFilter都有注册了EVENT_ORIGIN_TICK消息,并且在其处理函数process_tick_event()中进行原始tick有效性判断和过滤,之后再将有效的tick转发到vnpy系统中供其他应用使用的,下面是TickFilter的tick过滤处理函数,其最后两句与原来的BaseGateway的on_tick()的作用是相同的。
def process_tick_event(self,event:Event):
""" 对原始tick进行过滤 """
tick:TickData = event.data
# 检查tick合约的经验状态是否位有效交易状态
status:StatusData = self.statuses.get(tick.vt_symbol,None)
if not status:
vt_instrument = get_vt_instrument(tick.vt_symbol)
status = self.statuses.get(vt_instrument,None)
if not status:
# 未收到交易状态,返回
return
if status.instrument_status not in VALID_TRADE_STATUSES:
# 不在有效交易状态,返回
return
key = (tick.gateway_name,tick.vt_symbol)
_,oldtick = self.last_ticks.get(key,(None,None))
valid_tick = False
if not oldtick:
# 没有该合约的历史tick
self.last_ticks[key] = (True,tick)
valid_tick = True
elif tick.datetime > oldtick.datetime:
#
self.last_ticks[key] = (True,tick)
valid_tick = True
# else:
# print(f"【特别tick = {tick}】")
if valid_tick == True:
# 如果是有效的tick
if status.instrument_status != InstrumentStatus.CONTINOUS:
# 发送集合竞价tic消息到系统中
self.event_engine.put(Event(EVENT_AUCTION_TICK,tick))
self.event_engine.put(Event(EVENT_AUCTION_TICK + tick.vt_symbol, tick))
else:
# 发送连续竞价tic消息到系统中
self.event_engine.put(Event(EVENT_TICK,tick))
self.event_engine.put(Event(EVENT_TICK + tick.vt_symbol, tick))
逆火 wrote:
执行的时候 default_name找不到 请问这个是哪里定义的
在vnpy_ctp\gateway\ctp_gateway.py中:
class CtpGateway(BaseGateway):
"""
vn.py用于对接期货CTP柜台的交易接口。
"""
default_name: str = "CTP"
default_setting: Dict[str, str] = {
"用户名": "",
"密码": "",
"经纪商代码": "",
"交易服务器": "",
"行情服务器": "",
"产品名称": "",
"授权编码": ""
}
exchanges: List[str] = list(EXCHANGE_CTP2VT.values())
def __init__(self, event_engine: EventEngine, gateway_name: str) -> None:
"""构造函数"""
super().__init__(event_engine, gateway_name)
self.td_api: "CtpTdApi" = CtpTdApi(self)
self.md_api: "CtpMdApi" = CtpMdApi(self)
对于10:15:00还是15:00:00之后推过来的tick强行加回上一分钟,倒也是一种处理方法,也可以办到。只是tick的时间戳在休市或收盘之后一分钟后也是存在的,杜绝不完的。而且采用这种方式策略必须考虑处理休市或收盘之后可能发出委托的问题了。
如果生成的K先只是用来显示,不用考虑交易,怎么做都很自由。
c787297017 wrote:
多谢大佬深夜耐心回复,那果断在您的TickFilter的基础上增加时间校验,抛弃掉这些虽然有效但处于交易时间外的tick,以防产生额外的k线
顺便问下:你之前遇到的PTA合约在一秒内收到的tick时间戳均没有秒以下毫秒数的情况,现在还是这样吗?
c787297017 wrote:
将大神的tick_filter应用到实盘后发现存在如上datetime为10:15:00和15:00:00的tick,首先不知道他们还属于有效tick吗?它们的状态是属于VALID_TRADE_STATUSES的,否则会被tick_filter过滤掉,那可能是什么原因导致的这种情况呢?是否考虑增加时间过滤?
上述过程的第3步是需要时间的,有时也会因为一些结算及审查机制,使得用户在已经收到休市和收盘状态,仍然能够收到tick数据推送。
就是说——用户委托必须是在有效交易时间段内,但行情推送可能会超出连续交易时间段的结束时间!
综上所述:抛弃这些tick是比较好的做法。
逆火 wrote:
实盘中双边仓位一直在扩大,不明白,,,如果持仓中为多2手 空2手,然后新发指令为多开2手,我的持仓会变成多4手 空2手,为什么持仓不是多2手 空0手
这是期货基本常识,准确的说法:
要保证交易信号生成机制与交易执行机制的一致性:
zlengine wrote:
目前vnpy返回的持仓数据字段只有一个持仓均价,就是昨日的结算价。
有什么办法获取开仓均价呢?挨个交易日去查询交易记录不太有效率啊,难道一个仓位如果我已经持仓3个月,要查到3月前的成交记录才能计算获得开仓均价?CTP返回的持仓数据中是否包含这一数据呢?
有没有大神了解的,谢谢
要实现你的需求,需要自己保存交易后返回的TradeData,按照reference进行统计,按照逐笔方式统计出多、空两个方向上的持仓均价。
dmz wrote:
另外想请教大神一个问题
def process_tick_event(self,event:Event): """ 对原始tick进行过滤 """ tick:TickData = event.data # 检查tick合约的经验状态是否位有效交易状态 status:StatusData = self.statuses.get(tick.vt_symbol,None) if not status: vt_instrument = get_vt_instrument(tick.vt_symbol) status = self.statuses.get(vt_instrument,None) if not status: # 未收到交易状态,返回 return
这段代码我看了很久,如果我的理解没错的话, 判断一个tick是否有效,是用的上一个该品种的tick的 StatusData, 为什么要这样做呢?为什么不能直接用这个tick的状态信息呢?