qmt软件里面的快速计算是在什么模式下使用的?

李魔佛 发表了文章 • 0 个评论 • 1247 次浏览 • 2023-02-25 13:35 • 来自相关话题

QMT 平台模型是根据行情驱动,逐 K 线运行的。
 
即点击运行模型时,模型是从第 0 根 K 线开始运行到最后一根 K 线(如想加快模型运行速度,可以策略编辑器 - 基本信息中设置快速计算,限制计算范围,只计算最新的指定数量的 K 线范围),每根 K 线调用一次 Python 模型中的 handlebar(ContextInfo) 函数。
 
也就是你点击“运行”按钮的时候,如果你的快速计算默认设置的是0,
 
handlebar里面的k线是从2005年1月1日运行的,即使你在代码里面设置了运行时间:
def init(ContextInfo):
print("==============start==========")
ContextInfo.start = '2023-02-20 10:00:00'
ContextInfo.end = '2023-02-23 10:00:00'或者在回测参数里面设置的时间:






都是不管用的。
 
需要在代码里添加一句:
if not ContextInfo.is_last_bar():
return 
 
或者 把快速计算的值设置为1, 就只会以最新的k线计算。也就是只会执行1次handlebar。





 
不得不说,qmt的说明文档很让人困惑的。笔者也多次吐槽了。
如果没有编程的朋友,不建议自己折腾了。不少编程大咖都惊呼这软件和文档入门太难,文档太扯淡。
如果需要qmt策略代码 和实盘代码 代写,可以在公众号后台留言:qmt代写
 
 
  查看全部
QMT 平台模型是根据行情驱动,逐 K 线运行的。
 
即点击运行模型时,模型是从第 0 根 K 线开始运行到最后一根 K 线(如想加快模型运行速度,可以策略编辑器 - 基本信息中设置快速计算,限制计算范围,只计算最新的指定数量的 K 线范围),每根 K 线调用一次 Python 模型中的 handlebar(ContextInfo) 函数。
 
也就是你点击“运行”按钮的时候,如果你的快速计算默认设置的是0,
 
handlebar里面的k线是从2005年1月1日运行的,即使你在代码里面设置了运行时间:
def init(ContextInfo):
print("==============start==========")
ContextInfo.start = '2023-02-20 10:00:00'
ContextInfo.end = '2023-02-23 10:00:00'
或者在回测参数里面设置的时间:

20230225001.jpg


都是不管用的。
 
需要在代码里添加一句:
    if not ContextInfo.is_last_bar():
return
 
 
或者 把快速计算的值设置为1, 就只会以最新的k线计算。也就是只会执行1次handlebar。

20230225002.jpg

 
不得不说,qmt的说明文档很让人困惑的。笔者也多次吐槽了。
如果没有编程的朋友,不建议自己折腾了。不少编程大咖都惊呼这软件和文档入门太难,文档太扯淡。
如果需要qmt策略代码 和实盘代码 代写,可以在公众号后台留言:qmt代写
 
 
 

qmt隔夜文件单(python代码实现)实盘代码

李魔佛 发表了文章 • 0 个评论 • 1722 次浏览 • 2023-02-24 14:17 • 来自相关话题

 代码基于iquant平台编写。可以拿去参考参考。# encoding:gbk
import logging
import pandas as pd
from datetime import datetime, timedelta, time
from decimal import InvalidOperation
from decimal import Decimal as D
from READFILE import read_file

logging.basicConfig(level=logging.INFO)
# 挂单失败后的等待时长,以秒计
TIMEOUT_ON_FAIL_SEC = 30
# 规避 account_callback 的 Racing Condition
RUN_TIME_DELAY = 10

# global FILEPATH, DIR, PRICE, VOL, START_TIME, account
SH_pattern = r'^[1-9]\d{5}\.(sh|SH)$'
SZ_pattern = r'^(?!39)\d{6}\.(sz|SZ)$'
SH_prefix = ['5', '6', '9', '11']
SZ_prefix = ['0', '2', '30', '12', '159']
COLNAMES = ['direction', 'vol', 'price', 'start_time']


def init(ContextInfo):
ContextInfo.accID = account
ContextInfo.set_account(ContextInfo.accID)

ContextInfo.can_order = False
ContextInfo.all_order_done = False

if not load_file_order(ContextInfo):
load_sys_order(ContextInfo)

# load_file_order(ContextInfo)
ContextInfo.run_time("place_order", "{0}nSecond".format(TIMEOUT_ON_FAIL_SEC),
(datetime.now() + timedelta(seconds=RUN_TIME_DELAY)).strftime('%Y-%m-%d %H:%M:%S'), 'SH')


def account_callback(ContextInfo, accountInfo):
if not ContextInfo.can_order:
ContextInfo.can_order = True


def handlebar(ContextInfo):
return


def load_file_order(ContextInfo):
def _price_vol_filtering(row):
if not isinstance(row.start_time, time):
logging.warning('读取{0}指令时间失败: {1}'.format(row.name, row.start_time))
return None
if row.direction not in ['买', '卖']:
logging.warning('读取{0}买卖方向失败: {1}'.format(row.name, row.direction))
return None
try:
# parse start_time
curr_start_time = datetime.now().strftime('%Y-%m-%d ') + row.start_time.strftime('%H:%M:%S')
curr_start_time = datetime.strptime(curr_start_time, '%Y-%m-%d %H:%M:%S')
# parse direction
curr_direction = 23 if row.direction == '买' else 24
# parse price and vol
price = D(row.price)
vol = int(row.vol)
return pd.Series([curr_direction, vol, price, curr_start_time])
except InvalidOperation:
logging.warning("读取 {0} 指令价格失败: {1}".format(row.name, row.price))
return None
except ValueError:
logging.warning('读取 {0} 下单总量失败: {1}'.format(row.name, row.vol))
return None

def _name_parser(asset_name):
# 目前默认用户输入.SH 或.SZ时标的名称正确
if '.SH' in asset_name or '.SZ' in asset_name:
# todo: SH/SZ_pattern regex check here?
return asset_name
else:
raise ValueError('{0} 标的代码不合法'.format(asset_name))

try:
tmp_df = read_file(FILEPATH, names=COLNAMES, index_col=0)
except:
logging.warning('读取挂单配置文件失败或挂单配置文件为空,尝试交易读取配置面板参数')
return None
if tmp_df.empty:
logging.warning('读取挂单配置文件失败或挂单配置文件为空,尝试交易读取配置面板参数')
return None

tmp_df.index = tmp_df.index.to_series().astype(str)
tmp_df.index = tmp_df.index.str.strip()
tmp_df.index = tmp_df.index.str.upper()
tmp_df.index = tmp_df.index.to_series().apply(_name_parser).dropna()
tmp_df = tmp_df.apply(_price_vol_filtering, axis=1, broadcast=True).dropna()
if tmp_df.empty:
logging.warning('读取挂单配置文件失败或挂单配置文件为空,尝试交易读取配置面板参数')
return False

tmp_df.set_axis(COLNAMES, axis='columns', inplace=True)
# 挂单成功Flag
tmp_df['finished'] = [False] * tmp_df.shape[0]
ContextInfo.order_df = tmp_df
ContextInfo.set_universe(ContextInfo.order_df.index.tolist())
return True


def load_sys_order(ContextInfo):
try:
asset_name = ContextInfo.stockcode + '.' + ContextInfo.market
ContextInfo.set_universe([asset_name])
direction = 23 if DIR == '买入' else 24
start_time = datetime.strptime(datetime.now().strftime('%Y%m%d') + str(START_TIME), '%Y%m%d%H%M%S')
price = D(PRICE)
vol = int(VOL)
except BaseException:
raise ValueError("读取策略面板交易配置失败。请尝试修正挂单配置文件或者策略面板参数。")

price = float(price)
ContextInfo.order_df = pd.DataFrame(data=[direction, vol, price, start_time], index=COLNAMES,
columns=[asset_name]).T
ContextInfo.order_df['finished'] = False
return


def place_order(ContextInfo):
if not ContextInfo.can_order or ContextInfo.all_order_done:
return
for curr_asset in ContextInfo.get_universe():
if not ContextInfo.order_df.loc[curr_asset].finished \
and datetime.now() > ContextInfo.order_df.loc[curr_asset].start_time:
curr_order = ContextInfo.order_df.loc[curr_asset]
direction = int(curr_order.direction)
txt_direction = '买入' if direction == 23 else '卖出'
price = float(D(curr_order.price))
vol = int(curr_order.vol)
order_remark = '隔日文件挂单: 以 {0} {1} {2}'.format(price, txt_direction, curr_asset)
passorder(direction, 1101, ContextInfo.accID, curr_asset, 11, price, vol, order_remark, 1, order_remark,
ContextInfo)
ContextInfo.order_df.loc[curr_asset, 'finished'] = True

ContextInfo.all_order_done = all(ContextInfo.order_df['finished'].tolist())


def order_callback(ContextInfo, orderInfo):
curr_asset = orderInfo.m_strInstrumentID + '.' + orderInfo.m_strExchangeID
curr_remark = orderInfo.m_strRemark
curr_status = orderInfo.m_nOrderStatus
if '隔日文件挂单' in curr_remark and curr_status == 57:
ContextInfo.order_df.loc[curr_asset, 'finished'] = False
ContextInfo.all_order_done = False
logging.error('{0} 隔日文件挂单:报单废单 (柜台返回失败),原因:{1} 尝试重报'.format(curr_asset, orderInfo.m_strCancelInfo))
elif '隔日文件挂单' in curr_remark and curr_status == 50:
logging.info('{0} 隔日文件挂单:报单成功'.format(curr_asset))
return


def orderError_callback(ContextInfo, orderArgs, errMsg):
curr_asset = orderArgs.orderCode
if '隔日文件挂单' in orderArgs.strategyName:
ContextInfo.order_df.loc[curr_asset, 'finished'] = False
ContextInfo.all_order_done = False
logging.error('{0} 隔日文件挂单:账号下单异常 (COS/iQuant校验失败), 错误消息:{1} 尝试重报'.format(curr_asset, errMsg))
return






 把上述代码复制到iquant里面,然后部署到策略运行,运行策略,切换为实盘 查看全部
20230224003.jpg

 代码基于iquant平台编写。可以拿去参考参考。
# encoding:gbk
import logging
import pandas as pd
from datetime import datetime, timedelta, time
from decimal import InvalidOperation
from decimal import Decimal as D
from READFILE import read_file

logging.basicConfig(level=logging.INFO)
# 挂单失败后的等待时长,以秒计
TIMEOUT_ON_FAIL_SEC = 30
# 规避 account_callback 的 Racing Condition
RUN_TIME_DELAY = 10

# global FILEPATH, DIR, PRICE, VOL, START_TIME, account
SH_pattern = r'^[1-9]\d{5}\.(sh|SH)$'
SZ_pattern = r'^(?!39)\d{6}\.(sz|SZ)$'
SH_prefix = ['5', '6', '9', '11']
SZ_prefix = ['0', '2', '30', '12', '159']
COLNAMES = ['direction', 'vol', 'price', 'start_time']


def init(ContextInfo):
ContextInfo.accID = account
ContextInfo.set_account(ContextInfo.accID)

ContextInfo.can_order = False
ContextInfo.all_order_done = False

if not load_file_order(ContextInfo):
load_sys_order(ContextInfo)

# load_file_order(ContextInfo)
ContextInfo.run_time("place_order", "{0}nSecond".format(TIMEOUT_ON_FAIL_SEC),
(datetime.now() + timedelta(seconds=RUN_TIME_DELAY)).strftime('%Y-%m-%d %H:%M:%S'), 'SH')


def account_callback(ContextInfo, accountInfo):
if not ContextInfo.can_order:
ContextInfo.can_order = True


def handlebar(ContextInfo):
return


def load_file_order(ContextInfo):
def _price_vol_filtering(row):
if not isinstance(row.start_time, time):
logging.warning('读取{0}指令时间失败: {1}'.format(row.name, row.start_time))
return None
if row.direction not in ['买', '卖']:
logging.warning('读取{0}买卖方向失败: {1}'.format(row.name, row.direction))
return None
try:
# parse start_time
curr_start_time = datetime.now().strftime('%Y-%m-%d ') + row.start_time.strftime('%H:%M:%S')
curr_start_time = datetime.strptime(curr_start_time, '%Y-%m-%d %H:%M:%S')
# parse direction
curr_direction = 23 if row.direction == '买' else 24
# parse price and vol
price = D(row.price)
vol = int(row.vol)
return pd.Series([curr_direction, vol, price, curr_start_time])
except InvalidOperation:
logging.warning("读取 {0} 指令价格失败: {1}".format(row.name, row.price))
return None
except ValueError:
logging.warning('读取 {0} 下单总量失败: {1}'.format(row.name, row.vol))
return None

def _name_parser(asset_name):
# 目前默认用户输入.SH 或.SZ时标的名称正确
if '.SH' in asset_name or '.SZ' in asset_name:
# todo: SH/SZ_pattern regex check here?
return asset_name
else:
raise ValueError('{0} 标的代码不合法'.format(asset_name))

try:
tmp_df = read_file(FILEPATH, names=COLNAMES, index_col=0)
except:
logging.warning('读取挂单配置文件失败或挂单配置文件为空,尝试交易读取配置面板参数')
return None
if tmp_df.empty:
logging.warning('读取挂单配置文件失败或挂单配置文件为空,尝试交易读取配置面板参数')
return None

tmp_df.index = tmp_df.index.to_series().astype(str)
tmp_df.index = tmp_df.index.str.strip()
tmp_df.index = tmp_df.index.str.upper()
tmp_df.index = tmp_df.index.to_series().apply(_name_parser).dropna()
tmp_df = tmp_df.apply(_price_vol_filtering, axis=1, broadcast=True).dropna()
if tmp_df.empty:
logging.warning('读取挂单配置文件失败或挂单配置文件为空,尝试交易读取配置面板参数')
return False

tmp_df.set_axis(COLNAMES, axis='columns', inplace=True)
# 挂单成功Flag
tmp_df['finished'] = [False] * tmp_df.shape[0]
ContextInfo.order_df = tmp_df
ContextInfo.set_universe(ContextInfo.order_df.index.tolist())
return True


def load_sys_order(ContextInfo):
try:
asset_name = ContextInfo.stockcode + '.' + ContextInfo.market
ContextInfo.set_universe([asset_name])
direction = 23 if DIR == '买入' else 24
start_time = datetime.strptime(datetime.now().strftime('%Y%m%d') + str(START_TIME), '%Y%m%d%H%M%S')
price = D(PRICE)
vol = int(VOL)
except BaseException:
raise ValueError("读取策略面板交易配置失败。请尝试修正挂单配置文件或者策略面板参数。")

price = float(price)
ContextInfo.order_df = pd.DataFrame(data=[direction, vol, price, start_time], index=COLNAMES,
columns=[asset_name]).T
ContextInfo.order_df['finished'] = False
return


def place_order(ContextInfo):
if not ContextInfo.can_order or ContextInfo.all_order_done:
return
for curr_asset in ContextInfo.get_universe():
if not ContextInfo.order_df.loc[curr_asset].finished \
and datetime.now() > ContextInfo.order_df.loc[curr_asset].start_time:
curr_order = ContextInfo.order_df.loc[curr_asset]
direction = int(curr_order.direction)
txt_direction = '买入' if direction == 23 else '卖出'
price = float(D(curr_order.price))
vol = int(curr_order.vol)
order_remark = '隔日文件挂单: 以 {0} {1} {2}'.format(price, txt_direction, curr_asset)
passorder(direction, 1101, ContextInfo.accID, curr_asset, 11, price, vol, order_remark, 1, order_remark,
ContextInfo)
ContextInfo.order_df.loc[curr_asset, 'finished'] = True

ContextInfo.all_order_done = all(ContextInfo.order_df['finished'].tolist())


def order_callback(ContextInfo, orderInfo):
curr_asset = orderInfo.m_strInstrumentID + '.' + orderInfo.m_strExchangeID
curr_remark = orderInfo.m_strRemark
curr_status = orderInfo.m_nOrderStatus
if '隔日文件挂单' in curr_remark and curr_status == 57:
ContextInfo.order_df.loc[curr_asset, 'finished'] = False
ContextInfo.all_order_done = False
logging.error('{0} 隔日文件挂单:报单废单 (柜台返回失败),原因:{1} 尝试重报'.format(curr_asset, orderInfo.m_strCancelInfo))
elif '隔日文件挂单' in curr_remark and curr_status == 50:
logging.info('{0} 隔日文件挂单:报单成功'.format(curr_asset))
return


def orderError_callback(ContextInfo, orderArgs, errMsg):
curr_asset = orderArgs.orderCode
if '隔日文件挂单' in orderArgs.strategyName:
ContextInfo.order_df.loc[curr_asset, 'finished'] = False
ContextInfo.all_order_done = False
logging.error('{0} 隔日文件挂单:账号下单异常 (COS/iQuant校验失败), 错误消息:{1} 尝试重报'.format(curr_asset, errMsg))
return






 把上述代码复制到iquant里面,然后部署到策略运行,运行策略,切换为实盘

qmt的文档写的有点稀烂

李魔佛 发表了文章 • 0 个评论 • 1242 次浏览 • 2023-02-21 12:41 • 来自相关话题

在部署QMT 在线文档的时候,还是忍不住吐槽下它的文档。
 这也是当时不用QMT的一个重要原因。
 
不仅仅是文档,而且还有它的函数接口的涉及。
 
比如最常用的交易函数,passorder综合交易下单 passorder()
用法: passorder(opType, orderType, accountid, orderCode, prType, modelprice, volume[, strategyName, quickTrade, userOrderId], ContextInfo)
里面有11个参数,可选参数有2个。 对于一个常用函数来说,这个参数有点多了。
 
而更为令人费解的,是它参数里面额设定值
比如第一个opType,操作类型。
 
里面有59个数字:期货六键:

0:开多

1:平昨多

2:平今多

3:开空

4:平昨空

5:平今空

期货四键:

6:平多,优先平今

7:平多,优先平昨

8:平空,优先平今

9:平空,优先平昨

期货两键:

10:卖出,如有多仓,优先平仓,优先平今,如有余量,再开空

11:卖出,如有多仓,优先平仓,优先平昨,如有余量,再开空

12:买入,如有空仓,优先平仓,优先平今,如有余量,再开多

13:买入,如有空仓,优先平仓,优先平昨,如有余量,再开多

14:买入,不优先平仓

15:卖出,不优先平仓

股票买卖:

23:股票买入,或沪港通、深港通股票买入

24:股票卖出,或沪港通、深港通股票卖出

融资融券:

27:融资买入

28:融券卖出

29:买券还券

30:直接还券

31:卖券还款

32:直接还款

33:信用账号股票买入

34:信用账号股票卖出

组合交易:

25:组合买入,或沪港通、深港通的组合买入

26:组合卖出,或沪港通、深港通的组合卖出

27:融资买入

28:融券卖出

29:买券还券

31:卖券还款

33:信用账号股票买入

34:信用账号股票卖出

40:期货组合开多

43:期货组合开空

46:期货组合平多,优先平今

47:期货组合平多,优先平昨

48:期货组合平空,优先平今

49:期货组合平空,优先平昨

期权交易:

50:买入开仓

51:卖出平仓

52:卖出开仓

53:买入平仓

54:备兑开仓

55:备兑平仓

56:认购行权

57:认沽行权

58:证券锁定

59:证券解锁
它把期货,股票,期权所有品种压缩到一起,通过参数数字来辨认交易类别。
 
那么我们来看一看一个例子,就拿一个官网的一个例子来说:
 
最简单的例子:passorder(23,1101,account,s,11,14.00,100,2,ContextInfo)
一般人看了上面的代码,里面全部是数字,简直就像灾难一样。 但是我要明白它的交易品种和交易逻辑,
就得对着文档去查,编号23是啥,1101是啥,11,14又是啥。
 
如果我是代码reviewer,底下的员工这种文档,或者写代码的人,提交上这样的代码,绝对100%是要reject这个提交的。
 
然后orderType 更加让人吐血。。
 1101:单股、单账号、普通、股/手方式下单

1102:单股、单账号、普通、金额(元)方式下单(只支持股票)

1113:单股、单账号、总资产、比例 [0 ~ 1] 方式下单

1123:单股、单账号、可用、比例[0 ~ 1]方式下单

1201:单股、账号组(无权重)、普通、股/手方式下单

1202:单股、账号组(无权重)、普通、金额(元)方式下单(只支持股票)

1213:单股、账号组(无权重)、总资产、比例 [0 ~ 1] 方式下单

1223:单股、账号组(无权重)、可用、比例 [0 ~ 1] 方式下单

2101:组合、单账号、普通、按组合股票数量(篮子中股票设定的数量)方式下单 > 对应 volume 的单位为篮子的份

2102:组合、单账号、普通、按组合股票权重(篮子中股票设定的权重)方式下单 > 对应 volume 的单位为元

2103:组合、单账号、普通、按账号可用方式下单 > (底层篮子股票怎么分配?答:按可用资金比例后按篮子中股票权重分配,如用户没填权重则按相等权重分配)只对股票篮子支持

2201:组合、账号组(无权重)、普通、按组合股票数量方式下单

2202:组合、账号组(无权重)、普通、按组合股票权重方式下单



这排列组合,谁能记得住,逼着你去查文档,或者每次复制粘贴旧代码。
 
然后下单 类型,继续来各种枚举:
 -1:无效(实际下单时,需要用交易面板交易函数那设定的选价类型)

0:卖5价

1:卖4价

2:卖3价

3:卖2价

4:卖1价

5:最新价

6:买1价

7:买2价(组合不支持)

8:买3价(组合不支持)

9:买4价(组合不支持)

10:买5价(组合不支持)

11:(指定价)模型价(只对单股情况支持,对组合交易不支持)

12:涨跌停价

13:挂单价

14:对手价

27:市价即成剩撤(仅对股票期权申报有效)

28:市价即全成否则撤(仅对股票期权申报有效)

29:市价剩转限价(仅对股票期权申报有效)

42:最优五档即时成交剩余撤销申报(仅对上交所申报有效)

43:最优五档即时成交剩转限价申报(仅对上交所申报有效)

44:对手方最优价格委托(仅对深交所申报有效)

45:本方最优价格委托(仅对深交所申报有效)

46:即时成交剩余撤销委托(仅对深交所申报有效)

47:最优五档即时成交剩余撤销委托(仅对深交所申报有效)

48:全额成交或撤销委托(仅对深交所申报有效)

49:科创板盘后定价
会不会用常量定义个值给客户调用呢?
 passorder(
ContextInfo.TYPE_STOCK,

ContextInfo.TYPE_BUY,
100,
ContextInfo.LIMIT_PRICE,
ContextInfo)
没有标注类型:
虽然python是若类型的语言,可是qmt底层是c++,有些参数不对,就会导致异常:
比如交易软件:
 
比如下单函数passorder的参数列表:
volume,下单数量(股 / 手 / 元 / %)

passorder(opType, orderType, accountID, orderCode, prType, price, volume, ContextInfo)

根据 orderType 值最后一位确定 volume 的单位:

单股下单时:

1:股 / 手

2:金额(元)

3:比例(%)这个类型如果用了浮点,前面类型用了以股为但是,是会报错的,因为不存在100.11 股这样的非100单位的下单数据。
 
看完不想吐槽了,一群文科生设计的软件。。。





  查看全部
在部署QMT 在线文档的时候,还是忍不住吐槽下它的文档。
 这也是当时不用QMT的一个重要原因。
 
不仅仅是文档,而且还有它的函数接口的涉及。
 
比如最常用的交易函数,passorder
综合交易下单 passorder()
用法: passorder(opType, orderType, accountid, orderCode, prType, modelprice, volume[, strategyName, quickTrade, userOrderId], ContextInfo)

里面有11个参数,可选参数有2个。 对于一个常用函数来说,这个参数有点多了。
 
而更为令人费解的,是它参数里面额设定值
比如第一个opType,操作类型。
 
里面有59个数字:
期货六键:

0:开多

1:平昨多

2:平今多

3:开空

4:平昨空

5:平今空

期货四键:

6:平多,优先平今

7:平多,优先平昨

8:平空,优先平今

9:平空,优先平昨

期货两键:

10:卖出,如有多仓,优先平仓,优先平今,如有余量,再开空

11:卖出,如有多仓,优先平仓,优先平昨,如有余量,再开空

12:买入,如有空仓,优先平仓,优先平今,如有余量,再开多

13:买入,如有空仓,优先平仓,优先平昨,如有余量,再开多

14:买入,不优先平仓

15:卖出,不优先平仓

股票买卖:

23:股票买入,或沪港通、深港通股票买入

24:股票卖出,或沪港通、深港通股票卖出

融资融券:

27:融资买入

28:融券卖出

29:买券还券

30:直接还券

31:卖券还款

32:直接还款

33:信用账号股票买入

34:信用账号股票卖出

组合交易:

25:组合买入,或沪港通、深港通的组合买入

26:组合卖出,或沪港通、深港通的组合卖出

27:融资买入

28:融券卖出

29:买券还券

31:卖券还款

33:信用账号股票买入

34:信用账号股票卖出

40:期货组合开多

43:期货组合开空

46:期货组合平多,优先平今

47:期货组合平多,优先平昨

48:期货组合平空,优先平今

49:期货组合平空,优先平昨

期权交易:

50:买入开仓

51:卖出平仓

52:卖出开仓

53:买入平仓

54:备兑开仓

55:备兑平仓

56:认购行权

57:认沽行权

58:证券锁定

59:证券解锁

它把期货,股票,期权所有品种压缩到一起,通过参数数字来辨认交易类别。
 
那么我们来看一看一个例子,就拿一个官网的一个例子来说:
 
最简单的例子:
passorder(23,1101,account,s,11,14.00,100,2,ContextInfo)

一般人看了上面的代码,里面全部是数字,简直就像灾难一样。 但是我要明白它的交易品种和交易逻辑,
就得对着文档去查,编号23是啥,1101是啥,11,14又是啥。
 
如果我是代码reviewer,底下的员工这种文档,或者写代码的人,提交上这样的代码,绝对100%是要reject这个提交的。
 
然后orderType 更加让人吐血。。
 
1101:单股、单账号、普通、股/手方式下单

1102:单股、单账号、普通、金额(元)方式下单(只支持股票)

1113:单股、单账号、总资产、比例 [0 ~ 1] 方式下单

1123:单股、单账号、可用、比例[0 ~ 1]方式下单

1201:单股、账号组(无权重)、普通、股/手方式下单

1202:单股、账号组(无权重)、普通、金额(元)方式下单(只支持股票)

1213:单股、账号组(无权重)、总资产、比例 [0 ~ 1] 方式下单

1223:单股、账号组(无权重)、可用、比例 [0 ~ 1] 方式下单

2101:组合、单账号、普通、按组合股票数量(篮子中股票设定的数量)方式下单 > 对应 volume 的单位为篮子的份

2102:组合、单账号、普通、按组合股票权重(篮子中股票设定的权重)方式下单 > 对应 volume 的单位为元

2103:组合、单账号、普通、按账号可用方式下单 > (底层篮子股票怎么分配?答:按可用资金比例后按篮子中股票权重分配,如用户没填权重则按相等权重分配)只对股票篮子支持

2201:组合、账号组(无权重)、普通、按组合股票数量方式下单

2202:组合、账号组(无权重)、普通、按组合股票权重方式下单



这排列组合,谁能记得住,逼着你去查文档,或者每次复制粘贴旧代码。
 
然后下单 类型,继续来各种枚举:
 
-1:无效(实际下单时,需要用交易面板交易函数那设定的选价类型)

0:卖5价

1:卖4价

2:卖3价

3:卖2价

4:卖1价

5:最新价

6:买1价

7:买2价(组合不支持)

8:买3价(组合不支持)

9:买4价(组合不支持)

10:买5价(组合不支持)

11:(指定价)模型价(只对单股情况支持,对组合交易不支持)

12:涨跌停价

13:挂单价

14:对手价

27:市价即成剩撤(仅对股票期权申报有效)

28:市价即全成否则撤(仅对股票期权申报有效)

29:市价剩转限价(仅对股票期权申报有效)

42:最优五档即时成交剩余撤销申报(仅对上交所申报有效)

43:最优五档即时成交剩转限价申报(仅对上交所申报有效)

44:对手方最优价格委托(仅对深交所申报有效)

45:本方最优价格委托(仅对深交所申报有效)

46:即时成交剩余撤销委托(仅对深交所申报有效)

47:最优五档即时成交剩余撤销委托(仅对深交所申报有效)

48:全额成交或撤销委托(仅对深交所申报有效)

49:科创板盘后定价

会不会用常量定义个值给客户调用呢?
 
passorder(
ContextInfo.TYPE_STOCK,

ContextInfo.TYPE_BUY,
100,
ContextInfo.LIMIT_PRICE,
ContextInfo)

没有标注类型:
虽然python是若类型的语言,可是qmt底层是c++,有些参数不对,就会导致异常:
比如交易软件:
 
比如下单函数passorder的参数列表:
volume,下单数量(股 / 手 / 元 / %)

passorder(opType, orderType, accountID, orderCode, prType, price, volume, ContextInfo)

根据 orderType 值最后一位确定 volume 的单位:

单股下单时:

1:股 / 手

2:金额(元)

3:比例(%)
这个类型如果用了浮点,前面类型用了以股为但是,是会报错的,因为不存在100.11 股这样的非100单位的下单数据。
 
看完不想吐槽了,一群文科生设计的软件。。。

3fc3af7e327ac67c475223f8424004fd.jpg

 

为什么我的QMT安装目录下没有miniqmt的包xtquant

李魔佛 发表了文章 • 0 个评论 • 1216 次浏览 • 2023-02-20 21:00 • 来自相关话题

今天有位群友,qmt新手,说看了我的公众号,想搞miniQMT,结果发现安装了国盛的QMT之后没有发现xtquant的目录。





 



 
他安装的也是实盘正式版本的QMT。
 
那么问题出现在哪里呢?
 
主要问题在于它没有在qmt内部 下载内置的python库。
 





 
经过这个下载过程后。
 
然后就可以看到有site-packages了





 

 
  查看全部
今天有位群友,qmt新手,说看了我的公众号,想搞miniQMT,结果发现安装了国盛的QMT之后没有发现xtquant的目录。

20230220185141623.png

 
20230220012.jpg

 
他安装的也是实盘正式版本的QMT。
 
那么问题出现在哪里呢?
 
主要问题在于它没有在qmt内部 下载内置的python库。
 

mmexport1676896038788.png

 
经过这个下载过程后。
 
然后就可以看到有site-packages了

20230220013.jpg

 

 
 

qmt iquant最新接口文档

李魔佛 发表了文章 • 0 个评论 • 1493 次浏览 • 2023-02-19 15:16 • 来自相关话题

申请了个二级域名,作为QMT iQuant的接口文档。懒得再去搞新的域名了,凑合这用,和ptrade的接口文档拼在一个根域名下面
 
http://qmt.ptradeapi.com





 





 





 
除了官方的接口文档,还加入了一些个人平时编写的写法与回测,实盘代码。 不定期更新。
 
欢迎关注收藏。 查看全部
申请了个二级域名,作为QMT iQuant的接口文档。懒得再去搞新的域名了,凑合这用,和ptrade的接口文档拼在一个根域名下面
 
http://qmt.ptradeapi.com

20230219003.jpg

 

20230219004.jpg

 

20230219005.jpg

 
除了官方的接口文档,还加入了一些个人平时编写的写法与回测,实盘代码。 不定期更新。
 
欢迎关注收藏。

迅投qmt入门教程(一)

李魔佛 发表了文章 • 0 个评论 • 8532 次浏览 • 2023-02-06 19:43 • 来自相关话题

很早想写一个qmt教程的,无奈平时90%时间都用的ptrade。之前想把教程写好了再发出来,不过这样只会越拖越久,为了让自己填这个坑,先把文章发出来,按照平时正常的量化学习的路径,从简单到复杂。 慢慢记录,形成个系列教程。 有疑问的朋友可以到笔者的公众号或者知识星球去提问吧。(见文末)
 
1. 准备:
 
首先得开一个支持qmt的券商,目前市面上支持qmt的券商越来越丰富了。
 
初学者可以开一个门槛第一点的,一般入金1w-2w 不等,就可以申请开通了。 
 
鉴于以学习为目的,真正投入到实盘中的资金不会很大,所以初始阶段也不一定就找万一免五的券商,毕竟目前要给免五,资金门槛比较高,一般要100w甚至以上。
 
笔者推荐国信,国金的qmt, 门槛只要1-2w就足够了,股票费率在万一,可转债万0.4-万0.5。适合初学者,这两家也可以在虚拟机运行,适合苹果mac的用户。 需要的朋友也可以在公众号后台留言: qmt开通





 
2. 假设已经在券商那里开通了qmt功能,接下来就开始进入教学:
 
这里以国信的qmt(iquant)为例:
 
首先要做的就是下载python库。 这个python库指的是qmt的python库,它的版本是3.6.8; 如果你只用qmt内置的python,你就不用自己到网上下载python安装程序,只需要在qmt的设置里面,点一下按钮,就可以安装python库。这里用默认的系统路径就可以了。
 





 
3. 第一个量化程序 hello world
 
新建策略后:
在编辑器里面输入下面的代码:#encoding:gbk

def init(ContextInfo):
print('hello world')

def handlebar(ContextInfo):
#计算当前主图的cci
print("handle bar")
点击回测:
得到输出结果




 
这里介绍2个概念:(3)Handlebar

handlebar 是整个 Python 模型中的核心执行函数。当模型从数据层获取到运行所需要的数据之后,会对数据集上的每一根 bar,调用一次 handlebar 函数,处理当前这根 bar 上的数据。也就是说,用户模型的核心逻辑都是写在该函数中的,如获取数据,设置下单条件等。在 handlebar 中处理完数据后,用户可以通过 paint 方法将需要绘图输出的结果返回给界面。界面会将输出结果如实的展示出来。 (4)ContextInfo

ContextInfo 是整个 Python 框架中的一个核心对象。它包含了各种与 Python 底层框架交互的 API 方法,也是一个全局的上下文环境,可以在 init 以及 handlebar 这两个函数中自由地传递用户创建的各种自定义数据。





文绉绉的,实际写一个策略,必须包含下面两个函数,而且参数也要一致,参数名随意,不过用默认的就好了。你随便改成没有意义的字符,后面自己看代码也是很麻烦。def init(ContextInfo):
pass

def handlebar(ContextInfo):
pass
 
init 和 handlebar 是 Python 模型中最重要的方法,也是唯二由 C++ 直接调用的方法,所有的执行代码都尽量写在这两个方法中或由其中的函数调用。【个人不太喜欢这样】
 
回测时间设置,在右边的菜单栏,有个回测参数,里面设置时间;在菜单“基本信息”里面 ,可以设置回测的时间间隔,可以使用分钟线,日线,小时等等不同周期,不过无法做到tick的回测。最小的只能到分钟。
 
但是如果你有秒的tick数据,自己写个回测框架也是可以做到秒级的tick级别的回测。很早前笔者就在星球上提供了完整的源码和数据,初学者也可以拿着去改,只要后续更新tick数据,就可以不断的回测策略的最新状态。
 
你写的回测实盘python代码,是保存在本地的文件夹的:
C:\iquant_gx\python, 前面的C:\iquant_gx 是你的iquant安装路径。
 
而且底下也有很多的现成的代码:





 
部分代码可以直接用pycharm就可以打开,没有加密的,但也有一些是加密了的。
比如这个自动逆回购是现成的:





对,这里就有,很多人还到处找人写;# encoding:gbk
import logging
from datetime import datetime, timedelta
from decimal import Decimal as D
from decimal import InvalidOperation

logging.basicConfig(level=logging.INFO)

# 挂单失败后的等待时长,以秒计
TIMEOUT_ON_FAIL_SEC = 30
# 等待account_callback的时长
# RUN_TIME_DELAY = 30

# how is this not defined in package??
MORNING_START = datetime.strptime(datetime.now().strftime('%Y%m%d') + '093000', '%Y%m%d%H%M%S')
MORNING_END = datetime.strptime(datetime.now().strftime('%Y%m%d') + '113000', '%Y%m%d%H%M%S')
NOON_START = datetime.strptime(datetime.now().strftime('%Y%m%d') + '130000', '%Y%m%d%H%M%S')
NOON_END = datetime.strptime(datetime.now().strftime('%Y%m%d') + '153000', '%Y%m%d%H%M%S')

# for SH only
TRANS_COST_1D = D('5e-6')
TRANS_COST_LONG = D('1.5e-7')
TRANS_COST_MAX = 100

# ORDER LIMITS
SH_UPPER = 1e7
SH_LOWER = 1e5
SZ_UPPER = 1e8
SZ_LOWER = 1e3

# ASSET NAME DICT
SH_REV_REPO = {'上交所1天': '204001.SH', '上交所2天': '204002.SH', '上交所3天': '204003.SH',
'上交所4天': '204004.SH', '上交所7天': '204007.SH', '上交所14天': '204014.SH',
'上交所28天': '204028.SH', '上交所91天': '204091.SH', '上交所182天': '204182.SH',
}

SZ_REV_REPO = {'深交所3天': '131800.SZ', '深交所7天': '131801.SZ', '深交所14天': '131802.SZ',
'深交所28天': '131803.SZ', '深交所91天': '131805.SZ', '深交所182天': '131806.SZ',
'深交所4天': '131809.SZ', '深交所1天': '131810.SZ', '深交所2天': '131811.SZ',
}


def init(ContextInfo):
ContextInfo.accID = account
ContextInfo.set_account(ContextInfo.accID)
ContextInfo.use_all_cap = False if ALL_CAP == '否' else True

# global trading control, set to False if detected error on user's side
# stop() does not halt strat
ContextInfo.order_control = False

if not ContextInfo.use_all_cap:
try:
ContextInfo.dollar_vol = float(D(DOLLAR_VOL))
except InvalidOperation:
ContextInfo.order_control = True
raise ValueError('读取资金量失败')
else:
if DOLLAR_VOL != '':
logging.warning('已设定使用全部账户资金,忽略所设置资金量')

try:
ContextInfo.start_time = datetime.strptime(datetime.now().strftime('%Y%m%d') + str(START_TIME), '%Y%m%d%H%M%S')
ContextInfo.asset_name = SH_REV_REPO[ASSET_NAME]
except KeyError:
ContextInfo.asset_name = SZ_REV_REPO[ASSET_NAME]
except ValueError as error:
if 'unconverted data remains' in str(error):
ContextInfo.order_control = True
raise ValueError('读取挂单时间失败')

if not (MORNING_END > ContextInfo.start_time >= MORNING_START) \
and not (NOON_END > ContextInfo.start_time >= NOON_START):
ContextInfo.order_control = True
raise ValueError('挂单时间不在可交易时间内')

ContextInfo.can_order = False
ContextInfo.order_done = False

if not ContextInfo.order_control:
ContextInfo.run_time("place_order", "{0}nSecond".format(TIMEOUT_ON_FAIL_SEC),
ContextInfo.start_time.strftime('%Y-%m-%d %H:%M:%S'), 'SH')


def account_callback(ContextInfo, accountInfo):
if not ContextInfo.can_order:
ContextInfo.can_order = True
if ContextInfo.use_all_cap:
ContextInfo.dollar_vol = accountInfo.m_dAvailable
else:
if ContextInfo.dollar_vol > accountInfo.m_dAvailable:
ContextInfo.order_control = True
raise ValueError('下单额度大于账户可用资金')

# check if order satisfies lower limit for each exchange
if ('SH' in ContextInfo.asset_name and ContextInfo.dollar_vol < SH_LOWER) \
or ('SZ' in ContextInfo.asset_name and ContextInfo.dollar_vol < SZ_LOWER):
ContextInfo.order_control = True
raise ValueError('下单额度低于交易所最低限额')

# checks dollar_vol and rounds the total amount
if 'SH' in ContextInfo.asset_name and ContextInfo.dollar_vol % SH_LOWER != 0:
ContextInfo.dollar_vol = (ContextInfo.dollar_vol // SH_LOWER) * SH_LOWER
logging.warning('下单额度已规整为:{0}'.format(ContextInfo.dollar_vol))
elif 'SZ' in ContextInfo.asset_name and ContextInfo.dollar_vol % SZ_LOWER != 0:
ContextInfo.dollar_vol = (ContextInfo.dollar_vol // SZ_LOWER) * SZ_LOWER
logging.warning('下单额度已规整为:{0}'.format(ContextInfo.dollar_vol))

'''
if 'SH' in ContextInfo.asset_name:
num_batch_order = int(ContextInfo.dollar_vol // SH_UPPER)
remain_order = ContextInfo.dollar_vol - num_batch_order * SH_UPPER
if ContextInfo.asset_name == '204001.SH':
transaction_cost = TRANS_COST_MAX * num_batch_order + remain_order * TRANS_COST_1D
else:
transaction_cost = TRANS_COST_MAX * num_batch_order + remain_order * TRANS_COST_LONG
if transaction_cost + ContextInfo.dollar_vol > accountInfo.m_dAvailable:
ContextInfo.order_control = True
raise ValueError('可用资金不足以垫付交易金额与手续费')
'''

ContextInfo.remain_vol = ContextInfo.dollar_vol


def handlebar(ContextInfo):
return


def place_order(ContextInfo):
if not ContextInfo.can_order or ContextInfo.order_control:
return

if not ContextInfo.order_done:
if 'SH' in ContextInfo.asset_name:
num_batch_order = int(ContextInfo.remain_vol // SH_UPPER)
remain_order = ContextInfo.remain_vol - num_batch_order * SH_UPPER
for _ in range(num_batch_order):
order_remark = '国债逆回购:尝试报单{0}元 {1}'.format(SH_UPPER, ContextInfo.asset_name)
passorder(24, 1102, ContextInfo.accID, ContextInfo.asset_name, 5, -1, SH_UPPER, order_remark, 1,
order_remark, ContextInfo)
else:
num_batch_order = int(ContextInfo.remain_vol // SZ_UPPER)
remain_order = ContextInfo.remain_vol - num_batch_order * SZ_UPPER
for _ in range(num_batch_order):
order_remark = '国债逆回购:尝试报单{0}元 {1}'.format(SZ_UPPER, ContextInfo.asset_name)
passorder(24, 1102, ContextInfo.accID, ContextInfo.asset_name, 5, -1, SZ_UPPER, order_remark, 1,
order_remark, ContextInfo)

order_remark = '国债逆回购:尝试报单{0}元 {1}'.format(remain_order, ContextInfo.asset_name)
passorder(24, 1102, ContextInfo.accID, ContextInfo.asset_name, 5, -1, remain_order, order_remark, 1,
order_remark, ContextInfo)

ContextInfo.remain_vol = 0
ContextInfo.order_done = True


def order_callback(ContextInfo, orderInfo):
curr_remark = orderInfo.m_strRemark
curr_status = orderInfo.m_nOrderStatus

if '国债逆回购' in curr_remark and ContextInfo.asset_name in curr_remark and curr_status == 57:
ContextInfo.order_done = False
# up the leftover dollar vol by failed amount
# logging.info('reported trade amount:{0}, reported_trade_volume:{1}'.format(orderInfo.m_dTradeAmount, orderInfo.m_nVolumeTotal))
# 单张100元
ContextInfo.remain_vol += orderInfo.m_nVolumeTotal * 100
if '交易时间不合法' in orderInfo.m_strCancelInfo:
ContextInfo.order_control = True
raise ValueError('国债逆回购:未能在交易时间内完成下单,停止报单。余量{0}元未报'.format(ContextInfo.remain_vol))
logging.warning('国债逆回购:报单废单,原因:\"{0}\",尝试重报'.format(orderInfo.m_strCancelInfo))
elif '国债逆回购' in curr_remark and ContextInfo.asset_name in curr_remark and curr_status == 50:
logging.info('国债逆回购:报单{0}元成功'.format(orderInfo.m_nVolumeTotal * 100))
return




待续,不定期更新


 
 
公众号:

星球:

 
 
 
  查看全部
很早想写一个qmt教程的,无奈平时90%时间都用的ptrade。之前想把教程写好了再发出来,不过这样只会越拖越久,为了让自己填这个坑,先把文章发出来,按照平时正常的量化学习的路径,从简单到复杂。 慢慢记录,形成个系列教程。 有疑问的朋友可以到笔者的公众号或者知识星球去提问吧。(见文末)
 
1. 准备:
 
首先得开一个支持qmt的券商,目前市面上支持qmt的券商越来越丰富了。
 
初学者可以开一个门槛第一点的,一般入金1w-2w 不等,就可以申请开通了。 
 
鉴于以学习为目的,真正投入到实盘中的资金不会很大,所以初始阶段也不一定就找万一免五的券商,毕竟目前要给免五,资金门槛比较高,一般要100w甚至以上。
 
笔者推荐国信,国金的qmt, 门槛只要1-2w就足够了,股票费率在万一,可转债万0.4-万0.5。适合初学者,这两家也可以在虚拟机运行,适合苹果mac的用户。 需要的朋友也可以在公众号后台留言: qmt开通

20230206005.jpg

 
2. 假设已经在券商那里开通了qmt功能,接下来就开始进入教学:
 
这里以国信的qmt(iquant)为例:
 
首先要做的就是下载python库。 这个python库指的是qmt的python库,它的版本是3.6.8; 如果你只用qmt内置的python,你就不用自己到网上下载python安装程序,只需要在qmt的设置里面,点一下按钮,就可以安装python库。这里用默认的系统路径就可以了。
 

20230206006.jpg

 
3. 第一个量化程序 hello world
 
新建策略后:
在编辑器里面输入下面的代码:
#encoding:gbk

def init(ContextInfo):
print('hello world')

def handlebar(ContextInfo):
#计算当前主图的cci
print("handle bar")

点击回测:
得到输出结果
20230206007.jpg

 
这里介绍2个概念:
(3)Handlebar

handlebar 是整个 Python 模型中的核心执行函数。当模型从数据层获取到运行所需要的数据之后,会对数据集上的每一根 bar,调用一次 handlebar 函数,处理当前这根 bar 上的数据。也就是说,用户模型的核心逻辑都是写在该函数中的,如获取数据,设置下单条件等。在 handlebar 中处理完数据后,用户可以通过 paint 方法将需要绘图输出的结果返回给界面。界面会将输出结果如实的展示出来。
 
(4)ContextInfo

ContextInfo 是整个 Python 框架中的一个核心对象。它包含了各种与 Python 底层框架交互的 API 方法,也是一个全局的上下文环境,可以在 init 以及 handlebar 这两个函数中自由地传递用户创建的各种自定义数据。





文绉绉的,实际写一个策略,必须包含下面两个函数,而且参数也要一致,参数名随意,不过用默认的就好了。你随便改成没有意义的字符,后面自己看代码也是很麻烦。
def init(ContextInfo):
pass

def handlebar(ContextInfo):
pass

 
init 和 handlebar 是 Python 模型中最重要的方法,也是唯二由 C++ 直接调用的方法,所有的执行代码都尽量写在这两个方法中或由其中的函数调用。【个人不太喜欢这样】
 
回测时间设置,在右边的菜单栏,有个回测参数,里面设置时间;在菜单“基本信息”里面 ,可以设置回测的时间间隔,可以使用分钟线,日线,小时等等不同周期,不过无法做到tick的回测。最小的只能到分钟。
 
但是如果你有秒的tick数据,自己写个回测框架也是可以做到秒级的tick级别的回测。很早前笔者就在星球上提供了完整的源码和数据,初学者也可以拿着去改,只要后续更新tick数据,就可以不断的回测策略的最新状态。
 
你写的回测实盘python代码,是保存在本地的文件夹的:
C:\iquant_gx\python, 前面的C:\iquant_gx 是你的iquant安装路径。
 
而且底下也有很多的现成的代码:

20230206008.jpg

 
部分代码可以直接用pycharm就可以打开,没有加密的,但也有一些是加密了的。
比如这个自动逆回购是现成的:

20230206010.jpg

对,这里就有,很多人还到处找人写;
# encoding:gbk
import logging
from datetime import datetime, timedelta
from decimal import Decimal as D
from decimal import InvalidOperation

logging.basicConfig(level=logging.INFO)

# 挂单失败后的等待时长,以秒计
TIMEOUT_ON_FAIL_SEC = 30
# 等待account_callback的时长
# RUN_TIME_DELAY = 30

# how is this not defined in package??
MORNING_START = datetime.strptime(datetime.now().strftime('%Y%m%d') + '093000', '%Y%m%d%H%M%S')
MORNING_END = datetime.strptime(datetime.now().strftime('%Y%m%d') + '113000', '%Y%m%d%H%M%S')
NOON_START = datetime.strptime(datetime.now().strftime('%Y%m%d') + '130000', '%Y%m%d%H%M%S')
NOON_END = datetime.strptime(datetime.now().strftime('%Y%m%d') + '153000', '%Y%m%d%H%M%S')

# for SH only
TRANS_COST_1D = D('5e-6')
TRANS_COST_LONG = D('1.5e-7')
TRANS_COST_MAX = 100

# ORDER LIMITS
SH_UPPER = 1e7
SH_LOWER = 1e5
SZ_UPPER = 1e8
SZ_LOWER = 1e3

# ASSET NAME DICT
SH_REV_REPO = {'上交所1天': '204001.SH', '上交所2天': '204002.SH', '上交所3天': '204003.SH',
'上交所4天': '204004.SH', '上交所7天': '204007.SH', '上交所14天': '204014.SH',
'上交所28天': '204028.SH', '上交所91天': '204091.SH', '上交所182天': '204182.SH',
}

SZ_REV_REPO = {'深交所3天': '131800.SZ', '深交所7天': '131801.SZ', '深交所14天': '131802.SZ',
'深交所28天': '131803.SZ', '深交所91天': '131805.SZ', '深交所182天': '131806.SZ',
'深交所4天': '131809.SZ', '深交所1天': '131810.SZ', '深交所2天': '131811.SZ',
}


def init(ContextInfo):
ContextInfo.accID = account
ContextInfo.set_account(ContextInfo.accID)
ContextInfo.use_all_cap = False if ALL_CAP == '否' else True

# global trading control, set to False if detected error on user's side
# stop() does not halt strat
ContextInfo.order_control = False

if not ContextInfo.use_all_cap:
try:
ContextInfo.dollar_vol = float(D(DOLLAR_VOL))
except InvalidOperation:
ContextInfo.order_control = True
raise ValueError('读取资金量失败')
else:
if DOLLAR_VOL != '':
logging.warning('已设定使用全部账户资金,忽略所设置资金量')

try:
ContextInfo.start_time = datetime.strptime(datetime.now().strftime('%Y%m%d') + str(START_TIME), '%Y%m%d%H%M%S')
ContextInfo.asset_name = SH_REV_REPO[ASSET_NAME]
except KeyError:
ContextInfo.asset_name = SZ_REV_REPO[ASSET_NAME]
except ValueError as error:
if 'unconverted data remains' in str(error):
ContextInfo.order_control = True
raise ValueError('读取挂单时间失败')

if not (MORNING_END > ContextInfo.start_time >= MORNING_START) \
and not (NOON_END > ContextInfo.start_time >= NOON_START):
ContextInfo.order_control = True
raise ValueError('挂单时间不在可交易时间内')

ContextInfo.can_order = False
ContextInfo.order_done = False

if not ContextInfo.order_control:
ContextInfo.run_time("place_order", "{0}nSecond".format(TIMEOUT_ON_FAIL_SEC),
ContextInfo.start_time.strftime('%Y-%m-%d %H:%M:%S'), 'SH')


def account_callback(ContextInfo, accountInfo):
if not ContextInfo.can_order:
ContextInfo.can_order = True
if ContextInfo.use_all_cap:
ContextInfo.dollar_vol = accountInfo.m_dAvailable
else:
if ContextInfo.dollar_vol > accountInfo.m_dAvailable:
ContextInfo.order_control = True
raise ValueError('下单额度大于账户可用资金')

# check if order satisfies lower limit for each exchange
if ('SH' in ContextInfo.asset_name and ContextInfo.dollar_vol < SH_LOWER) \
or ('SZ' in ContextInfo.asset_name and ContextInfo.dollar_vol < SZ_LOWER):
ContextInfo.order_control = True
raise ValueError('下单额度低于交易所最低限额')

# checks dollar_vol and rounds the total amount
if 'SH' in ContextInfo.asset_name and ContextInfo.dollar_vol % SH_LOWER != 0:
ContextInfo.dollar_vol = (ContextInfo.dollar_vol // SH_LOWER) * SH_LOWER
logging.warning('下单额度已规整为:{0}'.format(ContextInfo.dollar_vol))
elif 'SZ' in ContextInfo.asset_name and ContextInfo.dollar_vol % SZ_LOWER != 0:
ContextInfo.dollar_vol = (ContextInfo.dollar_vol // SZ_LOWER) * SZ_LOWER
logging.warning('下单额度已规整为:{0}'.format(ContextInfo.dollar_vol))

'''
if 'SH' in ContextInfo.asset_name:
num_batch_order = int(ContextInfo.dollar_vol // SH_UPPER)
remain_order = ContextInfo.dollar_vol - num_batch_order * SH_UPPER
if ContextInfo.asset_name == '204001.SH':
transaction_cost = TRANS_COST_MAX * num_batch_order + remain_order * TRANS_COST_1D
else:
transaction_cost = TRANS_COST_MAX * num_batch_order + remain_order * TRANS_COST_LONG
if transaction_cost + ContextInfo.dollar_vol > accountInfo.m_dAvailable:
ContextInfo.order_control = True
raise ValueError('可用资金不足以垫付交易金额与手续费')
'''

ContextInfo.remain_vol = ContextInfo.dollar_vol


def handlebar(ContextInfo):
return


def place_order(ContextInfo):
if not ContextInfo.can_order or ContextInfo.order_control:
return

if not ContextInfo.order_done:
if 'SH' in ContextInfo.asset_name:
num_batch_order = int(ContextInfo.remain_vol // SH_UPPER)
remain_order = ContextInfo.remain_vol - num_batch_order * SH_UPPER
for _ in range(num_batch_order):
order_remark = '国债逆回购:尝试报单{0}元 {1}'.format(SH_UPPER, ContextInfo.asset_name)
passorder(24, 1102, ContextInfo.accID, ContextInfo.asset_name, 5, -1, SH_UPPER, order_remark, 1,
order_remark, ContextInfo)
else:
num_batch_order = int(ContextInfo.remain_vol // SZ_UPPER)
remain_order = ContextInfo.remain_vol - num_batch_order * SZ_UPPER
for _ in range(num_batch_order):
order_remark = '国债逆回购:尝试报单{0}元 {1}'.format(SZ_UPPER, ContextInfo.asset_name)
passorder(24, 1102, ContextInfo.accID, ContextInfo.asset_name, 5, -1, SZ_UPPER, order_remark, 1,
order_remark, ContextInfo)

order_remark = '国债逆回购:尝试报单{0}元 {1}'.format(remain_order, ContextInfo.asset_name)
passorder(24, 1102, ContextInfo.accID, ContextInfo.asset_name, 5, -1, remain_order, order_remark, 1,
order_remark, ContextInfo)

ContextInfo.remain_vol = 0
ContextInfo.order_done = True


def order_callback(ContextInfo, orderInfo):
curr_remark = orderInfo.m_strRemark
curr_status = orderInfo.m_nOrderStatus

if '国债逆回购' in curr_remark and ContextInfo.asset_name in curr_remark and curr_status == 57:
ContextInfo.order_done = False
# up the leftover dollar vol by failed amount
# logging.info('reported trade amount:{0}, reported_trade_volume:{1}'.format(orderInfo.m_dTradeAmount, orderInfo.m_nVolumeTotal))
# 单张100元
ContextInfo.remain_vol += orderInfo.m_nVolumeTotal * 100
if '交易时间不合法' in orderInfo.m_strCancelInfo:
ContextInfo.order_control = True
raise ValueError('国债逆回购:未能在交易时间内完成下单,停止报单。余量{0}元未报'.format(ContextInfo.remain_vol))
logging.warning('国债逆回购:报单废单,原因:\"{0}\",尝试重报'.format(orderInfo.m_strCancelInfo))
elif '国债逆回购' in curr_remark and ContextInfo.asset_name in curr_remark and curr_status == 50:
logging.info('国债逆回购:报单{0}元成功'.format(orderInfo.m_nVolumeTotal * 100))
return




待续,不定期更新


 
 
公众号:

星球:

 
 
 
 

qmt界面的运行和回测按钮功能有什么不同?

李魔佛 发表了文章 • 0 个评论 • 1189 次浏览 • 2023-02-06 19:00 • 来自相关话题

他们二者的区别:
 
在模型编辑器中,有“回测”和“运行”两个按钮,分别代表两种模式,它们之间的区别如下:
(1)回测模式指策略以历史行情为依据,以回测参数中的开始时间、结束时间为回测时间区间进行运
算,投资者可观察该策略在历史行情所获得的年化收益率、夏普比率、最大回撤、信息比率等指标表
现。

(2)运行模式指策略根据实时行情信号进行运算,以主图行情开始时间到当前时间为运行区间,进行策
略的模拟运行,但不进行真实的委托。

注:如果需要向模拟/实盘柜台发送真实的委托,请将策略加入到“模型交易”中。
盘后运行可能会有抽风现象。

回测的时候日期问题,只能选副图,不知道为何 查看全部

20230206004.jpg

他们二者的区别:
 
在模型编辑器中,有“回测”和“运行”两个按钮,分别代表两种模式,它们之间的区别如下:
(1)回测模式指策略以历史行情为依据,以回测参数中的开始时间、结束时间为回测时间区间进行运
算,投资者可观察该策略在历史行情所获得的年化收益率、夏普比率、最大回撤、信息比率等指标表
现。

(2)运行模式指策略根据实时行情信号进行运算,以主图行情开始时间到当前时间为运行区间,进行策
略的模拟运行,但不进行真实的委托。

注:如果需要向模拟/实盘柜台发送真实的委托,请将策略加入到“模型交易”中。
盘后运行可能会有抽风现象。

回测的时候日期问题,只能选副图,不知道为何

国信可以使用miniqmt吗?

李魔佛 发表了文章 • 0 个评论 • 2838 次浏览 • 2023-01-21 15:52 • 来自相关话题

 之前群里有国信的小伙伴说,国信的mini qmt无法使用的。
 
所以笔者特意去问了下国信的好友兼营业部经理,但他回复说,个人只要申请,就可以开通mini qmt。如果不申请,是无法使用的,无法连接上去。
 
但因为开通这个是不用门槛的,可能会有部分不懂的或者不愿意的经理会和客户说不支持,或者需要机构这样话语。

具体情况,具体分析。


1. 国信证券iQuant策略交易平台精简版是指国信证券iQuant策略交易平台更专业快速且简洁的版本,满足股票、期货、期权、基金等全品种交易需求。
2. 风险等级:R4
 投资期限:0-1年
投资品种:权益类投资品种如股票、混合型基金、偏股型基金、股票型基金等。
3. 平台使用不收取费用。

 

 
具体的申请表如下:





 
不过申请了这个权限后,只能进行拉取数据,并没有交易权限。。交易权限需要机构才能开通。郁闷,看来国信的miniqmt是无法进行交易的了,只能白嫖点数据。

如果需要文字word版本,
可以到公众号后台回复:  国信mini申请 
获取word版本。

 

或者加微信开通指定的营业部的国信qmt(iquant), 也可以帮你开通mini qmt。

 
  查看全部

20230220003.jpg

 之前群里有国信的小伙伴说,国信的mini qmt无法使用的。
 
所以笔者特意去问了下国信的好友兼营业部经理,但他回复说,个人只要申请,就可以开通mini qmt。如果不申请,是无法使用的,无法连接上去。
 
但因为开通这个是不用门槛的,可能会有部分不懂的或者不愿意的经理会和客户说不支持,或者需要机构这样话语。

具体情况,具体分析。



1. 国信证券iQuant策略交易平台精简版是指国信证券iQuant策略交易平台更专业快速且简洁的版本,满足股票、期货、期权、基金等全品种交易需求。
2. 风险等级:R4
 投资期限:0-1年
投资品种:权益类投资品种如股票、混合型基金、偏股型基金、股票型基金等。
3. 平台使用不收取费用。

 


 
具体的申请表如下:

20230121154931708.png

 
不过申请了这个权限后,只能进行拉取数据,并没有交易权限。。交易权限需要机构才能开通。郁闷,看来国信的miniqmt是无法进行交易的了,只能白嫖点数据。

如果需要文字word版本,
可以到公众号后台回复:  国信mini申请 
获取word版本。

 

或者加微信开通指定的营业部的国信qmt(iquant), 也可以帮你开通mini qmt。