1.Lib\site-packages\vnpy路径下新建user_tools文件夹,放入pricetick.py
pricetick.py内容如下:
import json
import os
class PRICETICK(object):
PATH = os.environ["USERPROFILE"] + "\\.vntrader\\unit.json"
PRICE_TICK = 0.0 #最小跳动点 float
UNIT = 0 #合约乘数 int
def get_unit(self,symbol):
with open(self.PATH,"r",encoding="utf_8") as f:
result = json.load(f)
for key_symbol in result:
if key_symbol == symbol:
self.PRICE_TICK = float(result[symbol][0]) #json读取最小跳动点
self.UNIT = int(result[symbol][1]) #json读取合约乘数
break
return self.PRICE_TICK,self.UNIT
2. .vntrader\unit.json 在.\vntrader文件夹下,新建unit.json, json文件存储所有标的最小跳动点,合约乘数
unit.json内容如下:
{
"IF":["0.2","300"],
"IC":["0.2","200"],
"IH":["0.2","300"],
"IM":["0.2","200"],
"T":["0.005","10000"],
"TF":["0.005","10000"],
"TS":["0.005","20000"],
"TL":["0.01","10000"],
"AU":["0.02","1000"],
"AG":["1","15"],
"CU":["10","5"],
"AL":["5","5"],
"ZN":["5","5"],
"PB":["5","5"],
"NI":["10","1"],
"SN":["10","1"],
"RB":["1","10"],
"WR":["1","10"],
"I":["0.5","100"],
"HC":["1","10"],
"SS":["5","5"],
"SF":["2","5"],
"SM":["2","5"],
"JM":["0.5","60"],
"J":["0.5","100"],
"ZC":["0.2","100"],
"FG":["1","20"],
"SP":["2","10"],
"FU":["1","10"],
"LU":["1","10"],
"SC":["0.1","1000"],
"BC":["10","5"],
"EC":["0.1","50"],
"BU":["1","10"],
"PG":["1","20"],
"RU":["5","10"],
"NR":["5","10"],
"L":["1","5"],
"TA":["2","5"],
"V":["1","5"],
"EG":["1","10"],
"MA":["1","10"],
"PP":["1","5"],
"EB":["1","5"],
"UR":["1","20"],
"SA":["1","20"],
"C":["1","10"],
"A":["1","10"],
"CS":["1","10"],
"B":["1","10"],
"M":["1","10"],
"Y":["2","10"],
"RM":["1","10"],
"OI":["1","10"],
"P":["2","10"],
"CF":["5","5"],
"SR":["1","10"],
"JD":["1","10"],
"AP":["1","10"],
"CJ":["5","5"],
"PF":["2","5"],
"PK":["2","5"],
"AO":["1","20"],
"LC":["50","1"],
"SI":["5","5"],
"BR":["5","5"],
"RR":["1","10"],
"LH":["5","16"],
"PX":["2","5"],
"SH":["1","30"]
}
3.在自己的策略中调用
from vnpy.user_tools.pricetick import PRICETICK
初始化时候def __init__():
实例化 self.contract = PRICETICK()
获取标的 self.symbol = "".join(re.findall(r"\D+",self.get_data()["vt_symbol"].split(".")[0])).upper()
提取最小变动价格,合约乘数 self.PRICE_TICK,self.UNIT = self.contract.get_unit(self.symbol) #从json提取最小变动价位,合约乘数
ahren wrote:
xiaohe wrote:
- 要load_bar完成后(初始化完成后),trading状态才会变为true。
- 可以去engine.py看一下load_bar函数。默认use_database为false,会先去接口拿数据,没有的话会去rqdata找,最后才会去database。
数据库存储了足够的日线历史数据,没有存储分钟数据,然后self.load_bar(days = 40,use_database=True),40个自然日足够策略计算指标,在cta策略界面执行初始化,看了下初始化inited=True,启动后trading=True,但显示窗口列出的指标都没有数据,就能不通过rqdata实现实盘交易吗?
不能的话,只想通过数据库历史数据,不要rqdata的数据,数据库是不是还需要历史分钟数据才可以,至少需要多少分钟数据?
验证了下,load_bar(10), 实盘是load 10个自然日的1分钟线去初始化(开始以为是10个自然日日线数据就可以初始化),那么不用rqdata的历史数据,数据库必须写入的是满足load_bar天数的1分钟线,如果天数不满足依然可以初始化,只是指标可能就计算不出来;我的策略指标都是日线计算,但cta策略合成日线又都是用分钟数据,在想数据库存日线数据容易获取,直接用日线数据计算指标而不通过分钟合成日线在计算指标,不就可以实现入库的是日线历史数据,而实现策略初始化
xiaohe wrote:
- 要load_bar完成后(初始化完成后),trading状态才会变为true。
- 可以去engine.py看一下load_bar函数。默认use_database为false,会先去接口拿数据,没有的话会去rqdata找,最后才会去database。
数据库存储了足够的日线历史数据,没有存储分钟数据,然后self.load_bar(days = 40,use_database=True),40个自然日足够策略计算指标,在cta策略界面执行初始化,看了下初始化inited=True,启动后trading=True,但显示窗口列出的指标都没有数据,就能不通过rqdata实现实盘交易吗?
不能的话,只想通过数据库历史数据,不要rqdata的数据,数据库是不是还需要历史分钟数据才可以,至少需要多少分钟数据?
CtaTemplate中,cta_engine.get_size()这个合约乘数获取,是从接入的数据源获得的嘛?比如接入rqdata,这个get_size()数据就是rqdata的数据
搞清楚了,记录下过程,本人爬到的数据以dataframe形式缓存,其中日期是str形式存储,按照论坛内容转换df["date"] = pd.to_datetime(df["date"],format = '%Y%m%d %H:%M:%S'),此时日期时间存储形式是Timestamp(时间戳),且没有时区属性, 而bardata 入sqlite 需要以带有时间戳的datetime形式存储,所以需要增加时区属性,同时将Timestamp形式变成datetime形式;合成bardata时处理如下:
datetime = datetime.fromtimestamp(int(row.datetime.timestamp()),pytz.timezone('Asia/Shanghai'));
因为sqlite 版本不同,时间存储Timestamp,datatime形式有区别,参考 https://baijiahao.baidu.com/s?id=1671113933155288895&wfr=spider&for=pc
我的数据是交易所每个交易日所有合约开盘、最高、最低、收盘等数据,而不是按照单一合约按照交易日排序的数据,直接用下面的方法入库,看了下数据库dbbardata入了所有数据,但映射到dbbaroverview 就不是每个合约按照日期排序,清空dbbaroverview数据,在数据管理窗口刷新下,才能映射成功,
1.有什么方法可以直接入库映射成功呢?
2.本地sqlite数据库存储了数据,且映射成功,在cta策略窗口,在不接入rqdata情况下,执行初始化依然没有历史数据初始化,这个初始化难道不是先从sqlite数据库读历史数据?
3.接入的rqdata数据,通过vnstation界面,cta策略初始化的数据,在数据库里面都没找到,这个初始化数据存储在哪儿呢?
from vnpy.trader.database import BaseDatabase, get_database
database: BaseDatabase = get_database()
database.save_bar_data(bars)
按照这个 Debug过程请参考 https://github.com/Yiran13/Strategies/tree/master/debug_process ,bardata中datetime要储存为str形式,可把datetime变成str形式,database.save_bar_data(bars)入库sqlite时候,报错AttributeError: 'str' object has no attribute 'astimezone'; 如果datetime变成Timestamp形式存储,报错InterfaceError: Error binding parameter 2 - probably unsupported type.
xiaohe wrote:
是你datetime类型的问题,可参考https://www.vnpy.com/forum/topic/4137-qing-jiao-:-csvwen-jian-yong-jiao-ben-dao-ru-sqlitebao-cuo-shi-qu-fang-mian
按照原贴12,13楼添加了时区,如下图
from vnpy.trader.database import BaseDatabase, get_database
insert into database
database: BaseDatabase = get_database()
database.save_bar_data(bars)
但按照这种方法入库,报错,Error binding parameter 2 - probably unsupported type.检查了下只有枚举类型,时间戳,float数据类型都能对得上;
现在这个sqlite_manager怎么创建的?我换成sqlite_manager.save_bar_data(bars)试试
dataframe 数据,如上图,需要数据入库SqliteDatabase,先已经将dataframe数据整理成BarData
入库如下:
_from vnpy.trader.database import BaseDatabase, get_database
database: BaseDatabase = get_database()
database.save_bardata(bars)
遇到这个时间戳转换问题,如何解决呢?
这样是不是复杂了啊?或许试试,在每一次on_trade里关注trade的开平状态,只要是开仓的话就发出止损单。不需要查看之前是否发出止损单,只要是最新on_trade的开仓,之前一定是没有发过的。
按照你的方法,可以的,不用判断直接发止损单,谢啦
alvinli512 wrote:
“先判断没发出止损单”这个判断是通过之前的order id吗。或许可以用trade id来判断?
是的,之前按照orderid来判断的,想了下tradeid 可以,你看看这么实现有没有漏洞
on_trade()里面先long_tradeid = [],short_tradeid = []缓存tradeid,接着
self.pos != 0时候获取trade.price,不同的方向上把trade.tradeid 分别加入对应的tradeid 列表,即:long_tradeid.extend(trade.tradeid)
self.pos > 0 时候 判断有long_tradeid,计算long_stop,然后遍历long_tradeid,发出止损单
即:self.sell(self.long_stop,abs(trade.volume),stop=True);反之,self.pos<0同样;
这样的话,就是循环成交笔次数挂止损单,没有剔除tradeid list ;
xiaohe wrote:
on_trade/on_tick都可以自己打印看看是否符合你的猜想
on_trade 里面打印trade.volume ,1单被拆成2笔成交,trade.volume回报的是2笔,时间戳毫秒有点区别;第一笔成交后,立即就发出了第一笔的止损单;因为在发出止损单前,有先判断没发出止损单,才计算止损价格发出止损单,导致第二笔止损单发不出去;目前不清楚删掉判断有没有发止损单这个条件,会不会影响其他部分
xiaohe wrote:
正常分次成交情况下,应该每一次都会收到on_trade推送,应该首先在on_trade下打印排查这个问题
不清楚你策略的具体逻辑,如果想开仓后立即挂单,可以在on_trade下进行委托
on_trade里面就是仓位大于0,判断没有挂止损单就发出trade.volume量的止损单,self.sell(self.long_stop,abs(trade.volume),stop=True)(仓位小于0,判断没挂止损,发trade.volume量的止损);
我怀疑是因为在on_tick里面 把tick数据推送到on_bar,导致on_bar时时刻刻都在接受tick数据,而不是每分钟合成好k线才有数据
alvinli512 wrote:
ahren wrote:
alvinli512 wrote:
如果只成交了33,那就只下了两个停止单,量分别是33,因为on_trade只被调用了两次。
这样依然不行,用trade.volume 发出的止损,也是首次成交的单子,也就是您讲的只发出了3;
alvinli512 wrote:
可以在每次on_trade下查看仓位,如果大于0,用trade.volume来下停止单。就只下每次成交量的停止单数量。
就是说我在仓位不等于0时候,缓存trade.volume,来替换止损单中的self.pos?
在on_trade函数下,判断self.pos>0,且没有挂止损单,发出止损self.sell(stop_price,abs(self.pos),stop=True),最后发现发出去的止损单和仓位不一致;观察了下,可能是一个仓位分了多次成交,on_trade只收到第一单成交的仓位,这个如何解决?
vnpy自带的python3.10.2 版本,对以前自己写的程序不兼容,怎么简单更换vnpy自带的版本不影响vnstation现有的程序?