迅投qmt入门教程(一)

QMT
很早想写一个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教程的,无奈平时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界面的运行和回测按钮功能有什么不同?

QMT

20230206004.jpg

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

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

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

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

继续阅读 »

20230206004.jpg

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

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

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

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

收起阅读 »

Ptrade基本期货策略

ptrade本身支持期货交易,开通账户绑定就可以了。
新建策略的时候选择:期货即可。

20230204005.jpg

 
 
 1. 买入开仓
 
不同期货品种每一跳的价格变动都不一样,limit_price入参的时候要参考对应品种的价格变动规则,如limit_price不做入参则会以交易的行情快照最新价或者回测的分钟最新价进行报单;

根据交易所规则,每天结束时会取消所有未完成交易;
 
 
def initialize(context):
g.security = ['IF1712.CCFX', 'CU1806.XSGE']
set_universe(g.security)

def handle_data(context, data):
#买入开仓
buy_open('IF1712.CCFX', 1)

#买入开仓(限定点数为52220)
buy_open('CU1806.XSGE', 1, limit_price=52220)

2. 卖出平仓
def initialize(context):
g.security = ['IF1712.CCFX', 'CU1806.XSGE']
set_universe(g.security)

def handle_data(context, data):
#卖出平仓
sell_close('IF1712.CCFX', 1)
#卖出平今仓(限定点数为52220)
sell_close ('CU1806.XSGE', 1, limit_price=52220, close_today=True)
#卖出平仓(限定点数为52220)
sell_close ('CU1806.XSGE', 1, limit_price=52220)

3. 获取合约信息
get_instruments- 获取合约信息
 
get_instruments(contract)


返回

FutureParams对象,主要返回的字段为:

contract_code -- 合约代码,str类型;
contract_name -- 合约名称,str类型;
exchange -- 交易所:大商所、郑商所、上期所、中金所,str类型;
trade_unit -- 交易单位,int类型;
contract_multiplier -- 合约乘数,float类型;
delivery_date -- 交割日期,str类型;
listing_date -- 上市日期,str类型;
trade_code -- 交易代码,str类型;
margin_rate -- 保证金比例,float类型;


 
代码示例:
def initialize(context):
g.security = ["CU2112.XSGE", "IF2112.CCFX"]
set_universe(g.security)

def before_trading_start(context, data):
# 获取股票池代码合约信息
for security in g.security:
info = get_instruments(security)
log.info(info)

def handle_data(context, data):
pass

继续阅读 »
ptrade本身支持期货交易,开通账户绑定就可以了。
新建策略的时候选择:期货即可。

20230204005.jpg

 
 
 1. 买入开仓
 
不同期货品种每一跳的价格变动都不一样,limit_price入参的时候要参考对应品种的价格变动规则,如limit_price不做入参则会以交易的行情快照最新价或者回测的分钟最新价进行报单;

根据交易所规则,每天结束时会取消所有未完成交易;
 
 
def initialize(context):
g.security = ['IF1712.CCFX', 'CU1806.XSGE']
set_universe(g.security)

def handle_data(context, data):
#买入开仓
buy_open('IF1712.CCFX', 1)

#买入开仓(限定点数为52220)
buy_open('CU1806.XSGE', 1, limit_price=52220)

2. 卖出平仓
def initialize(context):
g.security = ['IF1712.CCFX', 'CU1806.XSGE']
set_universe(g.security)

def handle_data(context, data):
#卖出平仓
sell_close('IF1712.CCFX', 1)
#卖出平今仓(限定点数为52220)
sell_close ('CU1806.XSGE', 1, limit_price=52220, close_today=True)
#卖出平仓(限定点数为52220)
sell_close ('CU1806.XSGE', 1, limit_price=52220)

3. 获取合约信息
get_instruments- 获取合约信息
 
get_instruments(contract)


返回

FutureParams对象,主要返回的字段为:

contract_code -- 合约代码,str类型;
contract_name -- 合约名称,str类型;
exchange -- 交易所:大商所、郑商所、上期所、中金所,str类型;
trade_unit -- 交易单位,int类型;
contract_multiplier -- 合约乘数,float类型;
delivery_date -- 交割日期,str类型;
listing_date -- 上市日期,str类型;
trade_code -- 交易代码,str类型;
margin_rate -- 保证金比例,float类型;


 
代码示例:
def initialize(context):
g.security = ["CU2112.XSGE", "IF2112.CCFX"]
set_universe(g.security)

def before_trading_start(context, data):
# 获取股票池代码合约信息
for security in g.security:
info = get_instruments(security)
log.info(info)

def handle_data(context, data):
pass

收起阅读 »

ptrade获取分时成交数据-LEVEL2数据逐笔数据

在接口文档 http://ptradeapi.com/#get_tick_direction
中提供了获取分时成交的数据。
 
使用场景
该函数在交易模块可用

接口说明
该接口用于获取当日分时成交行情数据。

注意事项:

1、沪深市场都有分时成交数据;

2、分时成交数据需开通level2行情才有数据推送,否则无数据返回;

返回字段:
返回
返回一个OrderedDict对象,包含每只代码的分时成交行情数据。(OrderedDict([(),()...]))

返回结果字段介绍:

time_stamp: 时间戳毫秒级(str:numpy.int64);
hq_px: 价格(str:numpy.float64);
hq_px64: 价格(str:numpy.int64)(行情暂不支持,返回均为0);
business_amount: 成交数量(str:numpy.int64);
business_balance: 成交金额(str:numpy.int64);
business_count: 成交笔数(str:numpy.int64);
business_direction: 成交方向(0:卖,1:买,2:平盘)(str:numpy.int64);
amount: 持仓量(str:numpy.int64)(行情暂不支持,返回均为0);
start_index: 分笔关联的逐笔开始序号(str:numpy.int64)(行情暂不支持,返回均为0);
end_index: 分笔关联的逐笔结束序号(str:numpy.int64)(行情暂不支持,返回均为0);

示例代码:
def initialize(context):
g.security = '000001.SZ'
set_universe(g.security)

def handle_data(context, data):
#获取000001.SZ的分时成交数据
direction_data = get_tick_direction(g.security)
log.info(direction_data)
#获取指定股票列表分时成交数据
direction_data = get_tick_direction(['000002.SZ','000032.SZ'])
log.info(direction_data)
#获取成交量
business_amount = direction_data['000002.SZ']['business_amount']
log.info('分时成交的成交量为:%s' % business_amount)

不过在handle_bar中或者tick_data中,实际行情推送最快也要3s,所以拿到的level2的是切片数据,即使拿到很多数据,可是行情获取时间间隔还是3s, 无法做到和qmt那样的level2逐笔订阅驱动。还有level2数据需要收费。ptrade目前常用的几个券商都不支持level2的。
 
目前有万一免五的qmt ptrade量化交易接口的券商吗?
 

 
 
继续阅读 »
在接口文档 http://ptradeapi.com/#get_tick_direction
中提供了获取分时成交的数据。
 
使用场景
该函数在交易模块可用

接口说明
该接口用于获取当日分时成交行情数据。

注意事项:

1、沪深市场都有分时成交数据;

2、分时成交数据需开通level2行情才有数据推送,否则无数据返回;

返回字段:
返回
返回一个OrderedDict对象,包含每只代码的分时成交行情数据。(OrderedDict([(),()...]))

返回结果字段介绍:

time_stamp: 时间戳毫秒级(str:numpy.int64);
hq_px: 价格(str:numpy.float64);
hq_px64: 价格(str:numpy.int64)(行情暂不支持,返回均为0);
business_amount: 成交数量(str:numpy.int64);
business_balance: 成交金额(str:numpy.int64);
business_count: 成交笔数(str:numpy.int64);
business_direction: 成交方向(0:卖,1:买,2:平盘)(str:numpy.int64);
amount: 持仓量(str:numpy.int64)(行情暂不支持,返回均为0);
start_index: 分笔关联的逐笔开始序号(str:numpy.int64)(行情暂不支持,返回均为0);
end_index: 分笔关联的逐笔结束序号(str:numpy.int64)(行情暂不支持,返回均为0);

示例代码:
def initialize(context):
g.security = '000001.SZ'
set_universe(g.security)

def handle_data(context, data):
#获取000001.SZ的分时成交数据
direction_data = get_tick_direction(g.security)
log.info(direction_data)
#获取指定股票列表分时成交数据
direction_data = get_tick_direction(['000002.SZ','000032.SZ'])
log.info(direction_data)
#获取成交量
business_amount = direction_data['000002.SZ']['business_amount']
log.info('分时成交的成交量为:%s' % business_amount)

不过在handle_bar中或者tick_data中,实际行情推送最快也要3s,所以拿到的level2的是切片数据,即使拿到很多数据,可是行情获取时间间隔还是3s, 无法做到和qmt那样的level2逐笔订阅驱动。还有level2数据需要收费。ptrade目前常用的几个券商都不支持level2的。
 
目前有万一免五的qmt ptrade量化交易接口的券商吗?
 

 
  收起阅读 »

ptrade移除当前ST股

 
下面代码移除创业板,科创板还有当前被ST的股票。也可以任意组合,移除。
可以参考上一篇:http://30daydo.com/article/44569
 
def remove_st_stock(all_stock_list):
st_dict = get_stock_status(all_stock_list, query_type='ST', query_date=None)
st_list = []
for k, v in st_dict.items():
if v:
st_list.append(k)
return st_list

MARKET_DICT = {0: '科创板', 1: '创业板', }
IGNORE_MARKET = [0, 1]

def all_codes_in_market():
all_stock_set = set(get_Ashares(date=None))
for ignore_code in IGNORE_MARKET:
market = MARKET_DICT.get(ignore_code)
if market == '科创板':
all_stock_set = all_stock_set - set(filter(lambda x: x.startswith('68'), all_stock_set))
if market == '创业板':
all_stock_set = all_stock_set - set(filter(lambda x: x.startswith('3'), all_stock_set))

return all_stock_set


def create_target(context):
all_stock_set = all_codes_in_market()
st_list = remove_st_stock(list(all_stock_set))
all_stock_set = all_stock_set - set(st_list)
return all_stock_set

调用方式:

stock_target = create_target(None)
 
这样返回的股票就被排除了科创板,创业板,ST股票。
 

 
 
继续阅读 »
 
下面代码移除创业板,科创板还有当前被ST的股票。也可以任意组合,移除。
可以参考上一篇:http://30daydo.com/article/44569
 
def remove_st_stock(all_stock_list):
st_dict = get_stock_status(all_stock_list, query_type='ST', query_date=None)
st_list = []
for k, v in st_dict.items():
if v:
st_list.append(k)
return st_list

MARKET_DICT = {0: '科创板', 1: '创业板', }
IGNORE_MARKET = [0, 1]

def all_codes_in_market():
all_stock_set = set(get_Ashares(date=None))
for ignore_code in IGNORE_MARKET:
market = MARKET_DICT.get(ignore_code)
if market == '科创板':
all_stock_set = all_stock_set - set(filter(lambda x: x.startswith('68'), all_stock_set))
if market == '创业板':
all_stock_set = all_stock_set - set(filter(lambda x: x.startswith('3'), all_stock_set))

return all_stock_set


def create_target(context):
all_stock_set = all_codes_in_market()
st_list = remove_st_stock(list(all_stock_set))
all_stock_set = all_stock_set - set(st_list)
return all_stock_set

调用方式:

stock_target = create_target(None)
 
这样返回的股票就被排除了科创板,创业板,ST股票。
 

 
  收起阅读 »

ptrade排除A股创业板,科创板的股票

由于创业板和科创板的股票波动会比沪深主板的要大,所以如果想要按照某些策略,排除这两个板块的股票,可以使用下面的方法:
 
MARKET_DICT = {0: '科创板', 1: '创业板', }
IGNORE_MARKET = [0, 1]

def create_target(context):

all_stock_set = set(get_Ashares(date=None))
for ignore_code in IGNORE_MARKET:
market = MARKET_DICT.get(ignore_code)
if market == '科创板':
all_stock_set = all_stock_set - set(filter(lambda x:x.startswith('68'),all_stock_set))
if market == '创业板':
all_stock_set = all_stock_set - set(filter(lambda x:x.startswith('3'),all_stock_set))

return all_stock_set

返回的all_stock_set就是排除了创业板,科创板的股票列表。
 
ptrade接口文档:http://ptradeapi.com
 
继续阅读 »
由于创业板和科创板的股票波动会比沪深主板的要大,所以如果想要按照某些策略,排除这两个板块的股票,可以使用下面的方法:
 
MARKET_DICT = {0: '科创板', 1: '创业板', }
IGNORE_MARKET = [0, 1]

def create_target(context):

all_stock_set = set(get_Ashares(date=None))
for ignore_code in IGNORE_MARKET:
market = MARKET_DICT.get(ignore_code)
if market == '科创板':
all_stock_set = all_stock_set - set(filter(lambda x:x.startswith('68'),all_stock_set))
if market == '创业板':
all_stock_set = all_stock_set - set(filter(lambda x:x.startswith('3'),all_stock_set))

return all_stock_set

返回的all_stock_set就是排除了创业板,科创板的股票列表。
 
ptrade接口文档:http://ptradeapi.com
 
收起阅读 »

ptrade如何获取某天的全市场股票代码?

ptrade如何获取全市场股票代码?
 
在ptrade的接口文档http://ptradeapi.com 里面可以查到,
 
get_Ashares – 获取指定日期A股代码列表
 
get_Ashares(date=None)

 
如果不指定日期,则获取单天的A股所有股票的股票代码。
 
如果在回测的时候,获取的是回测单天额所有股票代码; 如果指定日期,则获取的是指定日期的所有A股股票代码。


get_Ashares – 获取指定日期A股代码列表get_Ashares(date=None)使用场景

该函数在研究、回测、交易模块可用

接口说明

该接口用于获取指定日期沪深市场的所有A股代码列表

注意事项:

1、在回测中,date不入参默认取回测日期,默认值会随着回测日期变化而变化,等于context.current_dt

2、在研究中,date不入参默认取当天日期

3、在交易中,date不入参默认取当天日期

参数

date:格式为YYYYmmdd

返回

股票代码列表,list类型(list[str,...])


 
让我们来测试一下:

20230204002.jpg

 
拿到的股票个数是4912个。
 
然后我对着通达信的所有A股数据比较了一下,get_Ashares 获取的数据不包括北交所,新三板创新创业的股票,也就是不包括4和8开头的股票数据,但包含沪深主板,创业板,科创板的股票数据。
 

 
继续阅读 »
ptrade如何获取全市场股票代码?
 
在ptrade的接口文档http://ptradeapi.com 里面可以查到,
 
get_Ashares – 获取指定日期A股代码列表
 
get_Ashares(date=None)

 
如果不指定日期,则获取单天的A股所有股票的股票代码。
 
如果在回测的时候,获取的是回测单天额所有股票代码; 如果指定日期,则获取的是指定日期的所有A股股票代码。


get_Ashares – 获取指定日期A股代码列表get_Ashares(date=None)使用场景

该函数在研究、回测、交易模块可用

接口说明

该接口用于获取指定日期沪深市场的所有A股代码列表

注意事项:

1、在回测中,date不入参默认取回测日期,默认值会随着回测日期变化而变化,等于context.current_dt

2、在研究中,date不入参默认取当天日期

3、在交易中,date不入参默认取当天日期

参数

date:格式为YYYYmmdd

返回

股票代码列表,list类型(list[str,...])


 
让我们来测试一下:

20230204002.jpg

 
拿到的股票个数是4912个。
 
然后我对着通达信的所有A股数据比较了一下,get_Ashares 获取的数据不包括北交所,新三板创新创业的股票,也就是不包括4和8开头的股票数据,但包含沪深主板,创业板,科创板的股票数据。
 

  收起阅读 »

国信可以使用miniqmt吗?


20230220003.jpg

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

具体情况,具体分析。



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

 


 
具体的申请表如下:

20230121154931708.png

 
不过申请了这个权限后,只能进行拉取数据,并没有交易权限。。交易权限需要机构才能开通。郁闷,看来国信的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。

 
  收起阅读 »

github首页Maximum retries exceeded Please add an env variable called PAT_1 with your token

git
最近登录github后,发现之前显示的状态都不正常了。
正常的时候是这样的:

20230118001.jpg

但现在提示是这样的:

20230118002.jpg


Something went wrong! file an issue at https://tiny.one/readme-statsMaximum retries exceeded Please add an env variable called PAT_1 with your github token in vercel


 
有两个办法可以解决。
1. Set it up yourself using Vercel (requires some step)
2. Use URL provided by others (The easiest step)
 
翻译成中文:
1. 自己到vercel上部署一个这样的app (复杂)
2. 换一个其他人部署好的链接 (容易)
 
一般人就别折腾第一种方法了,直接使用第二种方法就好了。
 
笔者找了一些其他人部署好的链接:
 
拿去用就可以了:
https://github-readme-stats-git-masterrstaa-rickstaa.vercel.app
By Unknown

https://github-readme-stats-ruby-one.vercel.app
By Claudio Tancredi

https://github-readme-stats-ten-gilt.vercel.app
By Unknown

https://github-readme-stats-61 ... l.app
By Fanwang Meng

https://github-readme-stats.zohan.tech
By Zohan Subhash

https://readme-stats.clckblog.space
By Kevin Zhong


 
继续阅读 »
最近登录github后,发现之前显示的状态都不正常了。
正常的时候是这样的:

20230118001.jpg

但现在提示是这样的:

20230118002.jpg


Something went wrong! file an issue at https://tiny.one/readme-statsMaximum retries exceeded Please add an env variable called PAT_1 with your github token in vercel


 
有两个办法可以解决。
1. Set it up yourself using Vercel (requires some step)
2. Use URL provided by others (The easiest step)
 
翻译成中文:
1. 自己到vercel上部署一个这样的app (复杂)
2. 换一个其他人部署好的链接 (容易)
 
一般人就别折腾第一种方法了,直接使用第二种方法就好了。
 
笔者找了一些其他人部署好的链接:
 
拿去用就可以了:
https://github-readme-stats-git-masterrstaa-rickstaa.vercel.app
By Unknown

https://github-readme-stats-ruby-one.vercel.app
By Claudio Tancredi

https://github-readme-stats-ten-gilt.vercel.app
By Unknown

https://github-readme-stats-61 ... l.app
By Fanwang Meng

https://github-readme-stats.zohan.tech
By Zohan Subhash

https://readme-stats.clckblog.space
By Kevin Zhong


 
收起阅读 »

成为全栈工程师的技能与学习网站。(推特上推荐的)

HTML [➡️] learn-html.org 
CSS [➡️] web.dev/learn/css 
JavaScript [➡️] javascript .info 
Git [➡️] atlassian.com/git 
React [➡️] beta.reactjs.org 
Node [➡️] nodejs.dev/en/learn 
Postgres [➡️] postgresqltutorial.com 
API [➡️] rapidapi.com/learn
 当然上面资料应该是英文的。当作练习练习英文吧。
继续阅读 »
HTML [➡️] learn-html.org 
CSS [➡️] web.dev/learn/css 
JavaScript [➡️] javascript .info 
Git [➡️] atlassian.com/git 
React [➡️] beta.reactjs.org 
Node [➡️] nodejs.dev/en/learn 
Postgres [➡️] postgresqltutorial.com 
API [➡️] rapidapi.com/learn
 当然上面资料应该是英文的。当作练习练习英文吧。 收起阅读 »

目前有万一免五的qmt ptrade量化交易接口的券商吗?

==== 2023-02-03 更新 ======
目前量化qmt 有可以免五的券商了,国信,国元
 
国元证券万一免五,最低0元起步,免五门槛入金1w,不过开通qmt的门槛是100w
 
国信证券万一免五,门槛是100门槛,不过如果只是开通qmt(iquant),它的门槛是比较低的,入金2w就可以了
 
综合来看,如要要用qmt+万一免五,入金要100w的。
 目前(截至2023-01-08日) 市场上已经没有可以免五的支持qmt,ptrade的量化交易券商。 
当然动辄入金500w-5000W的券商就忽略了,大部分散户是达不到这个资金要求的。 
 
在正常条件下,入金1W 到 50W的范围内,目前多家券商已经停止股票免五了,费率最低是万一, 不过可以在开户后2-6月申请免五。【国金】 
 
或者先把户开了,把量化接口的权限也开了,等到券商可以重新申请免五的时候申请即可。 目前多家券商的营业部老板也承诺了,只要后续免五开放,就可以帮有量化交易权限的用户申请免五。
(最晚大概3月低可以免5)【国盛】
v2-d4f747d1d49a1121c68bee2d54a5a37a_r.jpg

当然如果如果有券商营业部可以支持免五,也可以联系微信自荐,诚意合作。

20230108002.jpg



 
 
 
继续阅读 »
==== 2023-02-03 更新 ======
目前量化qmt 有可以免五的券商了,国信,国元
 
国元证券万一免五,最低0元起步,免五门槛入金1w,不过开通qmt的门槛是100w
 
国信证券万一免五,门槛是100门槛,不过如果只是开通qmt(iquant),它的门槛是比较低的,入金2w就可以了
 
综合来看,如要要用qmt+万一免五,入金要100w的。
 目前(截至2023-01-08日) 市场上已经没有可以免五的支持qmt,ptrade的量化交易券商。 
当然动辄入金500w-5000W的券商就忽略了,大部分散户是达不到这个资金要求的。 
 
在正常条件下,入金1W 到 50W的范围内,目前多家券商已经停止股票免五了,费率最低是万一, 不过可以在开户后2-6月申请免五。【国金】 
 
或者先把户开了,把量化接口的权限也开了,等到券商可以重新申请免五的时候申请即可。 目前多家券商的营业部老板也承诺了,只要后续免五开放,就可以帮有量化交易权限的用户申请免五。
(最晚大概3月低可以免5)【国盛】
v2-d4f747d1d49a1121c68bee2d54a5a37a_r.jpg

当然如果如果有券商营业部可以支持免五,也可以联系微信自荐,诚意合作。

20230108002.jpg



 
 
  收起阅读 »

ptrade生产环境在开盘交易时间无法回测,有什么办法可以解决?


2023-01-06 10:41:46 Error: 回测运行失败, 错误码:2 错误信息: 当前时间不允许创建回测 


20230106002.jpg

 在正式环境下,多个券商的ptrade都无法进行回测。
 
但也有办法解决。 就是使用ptrade的仿真客户端。 仿真客户端并连接实盘交易。所以没有这个时间的限制。
笔者在几个券商的上的仿真客户端都可以在交易时间使用回测功能。
可能部分仿真客户端需要申请才给开通的。
 
继续阅读 »


2023-01-06 10:41:46 Error: 回测运行失败, 错误码:2 错误信息: 当前时间不允许创建回测 


20230106002.jpg

 在正式环境下,多个券商的ptrade都无法进行回测。
 
但也有办法解决。 就是使用ptrade的仿真客户端。 仿真客户端并连接实盘交易。所以没有这个时间的限制。
笔者在几个券商的上的仿真客户端都可以在交易时间使用回测功能。
可能部分仿真客户端需要申请才给开通的。
 
收起阅读 »

ptrade回测结束后执行某个函数,比如保存回测结果

http://ptradeapi.com/#after_trading_end
官方文档只提供一个每天盘后执行的函数,没有函数可以在回测结束后,固定执行某些操作。
 
比如我回测过程保存的历史交易记录,收益率等,要如何保存? 虽然可以在回测的时候,每个交易日保存一次。
但是这样就需要在回测的时候按照天打开文件,盘后写入一次。 使用一个全局对象操作,显得很啰嗦。
 
那么有没有办法可以做在回测结束后一次性 保存操作呢?
 
答案是有的。也很简单。 适用于ptrade,qmt。
https://t.zsxq.com/09yigu5dy

 

 
 
继续阅读 »
http://ptradeapi.com/#after_trading_end
官方文档只提供一个每天盘后执行的函数,没有函数可以在回测结束后,固定执行某些操作。
 
比如我回测过程保存的历史交易记录,收益率等,要如何保存? 虽然可以在回测的时候,每个交易日保存一次。
但是这样就需要在回测的时候按照天打开文件,盘后写入一次。 使用一个全局对象操作,显得很啰嗦。
 
那么有没有办法可以做在回测结束后一次性 保存操作呢?
 
答案是有的。也很简单。 适用于ptrade,qmt。
https://t.zsxq.com/09yigu5dy

 

 
  收起阅读 »

ptrade回测 获取回测当天的分时数据

http://ptradeapi.com/#get_price
ptrade api的文档第3条表明,
 


3、数据返回内容不包括当天数据。


也就是用get_price是拿不到回测当天的数据。
 
比如下面的例子:
def initialize(context):
# 初始化策略
run_daily(context, execute, '09:36')

def handle_data(context, data):
pass


def execute(context):
current = context.blotter.current_dt.strftime('%Y-%m-%d')
log.info(current)
security='128025.SZ'
df = get_price(security, start_date=None, end_date=None, frequency='1m', fields=None, fq=None, count=10)
log.info(df)

返回的数据:
2023-01-01 15:31:21 开始运行回测, 策略名称: 四叶草-指定时间价格
2022-12-01 09:36:00 - INFO - 2022-12-01
2022-12-01 09:36:00 - INFO - open high low close volume \
2022-11-30 14:51:00 276.510 276.560 274.626 275.500 217510.0
2022-11-30 14:52:00 275.240 278.638 275.205 278.398 363820.0
2022-11-30 14:53:00 278.485 278.895 276.660 277.479 307570.0
2022-11-30 14:54:00 277.337 278.440 276.660 278.440 239370.0
2022-11-30 14:55:00 279.900 287.113 279.900 287.113 853170.0
2022-11-30 14:56:00 287.113 288.526 286.533 288.125 581860.0
2022-11-30 14:57:00 288.126 291.800 287.909 291.361 523580.0
2022-11-30 14:58:00 291.500 292.980 291.500 291.800 36480.0
2022-11-30 14:59:00 291.800 291.800 291.800 291.800 0.0
2022-11-30 15:00:00 292.510 292.510 292.510 292.510 421398.0

回测日期是2022-12-01日,每天09:36运行,那10根数据。
但返回的数据是昨天的收盘前的10根分时数据。并非当天9:36分开始拿10根bar。
 
如果把日期数据也固定,
    df = get_price(security, start_date='2022-12-01', end_date=None, frequency='1m', fields=None, fq=None, count=10)

实际拿到的数据是空的,也就是无法拿到当天的数据。
 
正确的用法:
 
def initialize(context):
# 初始化策略
run_daily(context, execute, '09:36')

def handle_data(context, data):
pass


def execute(context):
current = context.blotter.current_dt.strftime('%Y-%m-%d')
log.info(current)
security='128025.SZ'
count=6
df=get_history(count, frequency='1m', field='close', security_list=security, fq=None, include=False, fill='nan')
log.info(df)

返回的数据:

20230101154423810.png

 
欢迎关注公众号
继续阅读 »
http://ptradeapi.com/#get_price
ptrade api的文档第3条表明,
 


3、数据返回内容不包括当天数据。


也就是用get_price是拿不到回测当天的数据。
 
比如下面的例子:
def initialize(context):
# 初始化策略
run_daily(context, execute, '09:36')

def handle_data(context, data):
pass


def execute(context):
current = context.blotter.current_dt.strftime('%Y-%m-%d')
log.info(current)
security='128025.SZ'
df = get_price(security, start_date=None, end_date=None, frequency='1m', fields=None, fq=None, count=10)
log.info(df)

返回的数据:
2023-01-01 15:31:21 开始运行回测, 策略名称: 四叶草-指定时间价格
2022-12-01 09:36:00 - INFO - 2022-12-01
2022-12-01 09:36:00 - INFO - open high low close volume \
2022-11-30 14:51:00 276.510 276.560 274.626 275.500 217510.0
2022-11-30 14:52:00 275.240 278.638 275.205 278.398 363820.0
2022-11-30 14:53:00 278.485 278.895 276.660 277.479 307570.0
2022-11-30 14:54:00 277.337 278.440 276.660 278.440 239370.0
2022-11-30 14:55:00 279.900 287.113 279.900 287.113 853170.0
2022-11-30 14:56:00 287.113 288.526 286.533 288.125 581860.0
2022-11-30 14:57:00 288.126 291.800 287.909 291.361 523580.0
2022-11-30 14:58:00 291.500 292.980 291.500 291.800 36480.0
2022-11-30 14:59:00 291.800 291.800 291.800 291.800 0.0
2022-11-30 15:00:00 292.510 292.510 292.510 292.510 421398.0

回测日期是2022-12-01日,每天09:36运行,那10根数据。
但返回的数据是昨天的收盘前的10根分时数据。并非当天9:36分开始拿10根bar。
 
如果把日期数据也固定,
    df = get_price(security, start_date='2022-12-01', end_date=None, frequency='1m', fields=None, fq=None, count=10)

实际拿到的数据是空的,也就是无法拿到当天的数据。
 
正确的用法:
 
def initialize(context):
# 初始化策略
run_daily(context, execute, '09:36')

def handle_data(context, data):
pass


def execute(context):
current = context.blotter.current_dt.strftime('%Y-%m-%d')
log.info(current)
security='128025.SZ'
count=6
df=get_history(count, frequency='1m', field='close', security_list=security, fq=None, include=False, fill='nan')
log.info(df)

返回的数据:

20230101154423810.png

 
欢迎关注公众号
收起阅读 »

Ptrade获取可转债强赎数据

对于强赎的可转债而言,期权价值归0,叠加一个转股压力。类似于股票解禁的效果,导致转债和正股一起下跌。

haerzuanzai.jpeg

 
所以在可转债的策略里面,把强赎的转债排除掉,是一个不错的因子。
 
但内置的ptrade接口数据并无提供任何转债相关的数据。
 
不过笔者这里提供了一个自研的数据接口。
 
http://ptradeapi.com/#%E5%8F%AF%E8%BD%AC%E5%80%BA%E5%BC%BA%E8%B5%8E%E4%B8%8E%E6%95%B0%E6%97%A5%E5%AD%90
 
方便在ptrade里面调用。
 

20221221003.jpg

继续阅读 »
对于强赎的可转债而言,期权价值归0,叠加一个转股压力。类似于股票解禁的效果,导致转债和正股一起下跌。

haerzuanzai.jpeg

 
所以在可转债的策略里面,把强赎的转债排除掉,是一个不错的因子。
 
但内置的ptrade接口数据并无提供任何转债相关的数据。
 
不过笔者这里提供了一个自研的数据接口。
 
http://ptradeapi.com/#%E5%8F%AF%E8%BD%AC%E5%80%BA%E5%BC%BA%E8%B5%8E%E4%B8%8E%E6%95%B0%E6%97%A5%E5%AD%90
 
方便在ptrade里面调用。
 

20221221003.jpg

收起阅读 »

Ptrade API 文档

部署一个ptrade APi文档的小站,便于平时做量化的朋友查询。
 
http://ptradeapi.com/

20221218005.jpg

 

20221218006.jpg

 
以后再也不用在ptrade的编辑页面与编辑页面来回切换了。
继续阅读 »
部署一个ptrade APi文档的小站,便于平时做量化的朋友查询。
 
http://ptradeapi.com/

20221218005.jpg

 

20221218006.jpg

 
以后再也不用在ptrade的编辑页面与编辑页面来回切换了。 收起阅读 »

ubuntu snap 配置不更新新软件

它默认会自动更新软件,非常的烦。
 
网上的禁用方法
$ snap refresh --hold
error: unknown flag `hold'

在ubuntu20.04上是没有用的。因为它的snap不是最新的。要在ubuntu22.04上才可以。
 
在ubuntu 20.04上,
 
$ sudo snap set system refresh.hold="2030-01-01T01:00:00-01:00"
$ sudo snap set system refresh.metered=hold

网上说 snap最多只能设置60天内不更新,所以还需要配合crontab,在计划任务里面设置时间。
 
 
继续阅读 »
它默认会自动更新软件,非常的烦。
 
网上的禁用方法
$ snap refresh --hold
error: unknown flag `hold'

在ubuntu20.04上是没有用的。因为它的snap不是最新的。要在ubuntu22.04上才可以。
 
在ubuntu 20.04上,
 
$ sudo snap set system refresh.hold="2030-01-01T01:00:00-01:00"
$ sudo snap set system refresh.metered=hold

网上说 snap最多只能设置60天内不更新,所以还需要配合crontab,在计划任务里面设置时间。
 
  收起阅读 »

pycharm 最新版2022.03 无法使用ida-eval-resetter 插件重置试用日期

 
0x5. 新试用机制

最新的IDE试用需要登录,我们可以任选以下方式中的一种来继续使用重置插件:

使用网络上热心大佬收集总结的key,进入IDE后使用重置插件。
登录账号试用IDE,安装设置好本插件,退出登录账号重启IDE即可。
先安装旧版本IDE,安装设置好本插件,升级IDE到最新版本即可。

不管哪种方法原理都是为了让你进入IDE,以便重置插件接管试用。
2021.3已经彻底不支持离线试用,本重置插件已失效。可以考虑暂缓升级至2021.3!
 

20221217001.jpg


如果要使用重置日期插件,那么得要把你的pycharm降级到2021.3版本或者以下。
 
继续阅读 »
 
0x5. 新试用机制

最新的IDE试用需要登录,我们可以任选以下方式中的一种来继续使用重置插件:

使用网络上热心大佬收集总结的key,进入IDE后使用重置插件。
登录账号试用IDE,安装设置好本插件,退出登录账号重启IDE即可。
先安装旧版本IDE,安装设置好本插件,升级IDE到最新版本即可。

不管哪种方法原理都是为了让你进入IDE,以便重置插件接管试用。
2021.3已经彻底不支持离线试用,本重置插件已失效。可以考虑暂缓升级至2021.3!
 

20221217001.jpg


如果要使用重置日期插件,那么得要把你的pycharm降级到2021.3版本或者以下。
  收起阅读 »

ubuntu snap 下载指定版本的pycharm goland

之前用的pycharm,goland被snap自动更新,更新到2022.3 , 导致之前的ida-eval-resetter 无法使用。
 
所以只好回滚到指定的pycharm版本。
 
使用snap可以下载指定版本的pycharm和goland
 
sudo snap install pycharm-professional --channel=2021.2/stable --classic
 
上面的命令下载  2021.2/stable的版本的pycharm
 
那么怎么查看pycharm的版本?
snap info pycharm-professional
name:      pycharm-professional
summary: PyCharm Professional Edition
publisher: jetbrains✓
store-url: https://snapcraft.io/pycharm-professional
contact: https://www.jetbrains.com/pycharm/documentation/
license: unset
description: |
Python IDE for professional developers. Save time while PyCharm takes care of the r
on bigger things and embrace the keyboard-centric approach to get the most of PyCha
productivity features.
commands:
- pycharm-professional
snap-id: Uqpw0ZWqy6Wh4mgaWE0rxgM5tAGCwf4D
tracking: latest/stable
refresh-date: 15 days ago, at 09:57 CST
channels:
latest/stable: 2022.3 2022-12-01 (314) 779MB classic
latest/candidate: 2022.3.1-RC 2022-12-12 (315) 793MB classic
latest/beta: 2022.3.1-RC 2022-12-12 (315) 793MB classic
latest/edge: 2022.3.1-RC 2022-12-12 (315) 793MB classic
2022.3/stable: 2022.3 2022-12-01 (314) 779MB classic
2022.3/candidate: 2022.3.1-RC 2022-12-12 (315) 793MB classic
2022.3/beta: 2022.3.1-RC 2022-12-12 (315) 793MB classic
2022.3/edge: 2022.3.1-RC 2022-12-12 (315) 793MB classic
2022.2/stable: 2022.2.4 2022-11-17 (311) 764MB classic
2022.2/candidate: 2022.2.4 2022-11-17 (311) 764MB classic
2022.2/beta: 2022.2.4 2022-11-17 (311) 764MB classic
2022.2/edge: 2022.2.4 2022-11-17 (311) 764MB classic
2022.1/stable: 2022.1.4 2022-07-21 (295) 759MB classic
2022.1/candidate: 2022.1.4 2022-07-21 (295) 759MB classic
2022.1/beta: 2022.1.4 2022-07-21 (295) 759MB classic
2022.1/edge: 2022.1.4 2022-07-21 (295) 759MB classic
2021.3/stable: 2021.3.3 2022-03-17 (278) 702MB classic
2021.3/candidate: 2021.3.3 2022-03-17 (278) 702MB classic
2021.3/beta: 2021.3.3 2022-03-17 (278) 702MB classic
2021.3/edge: 2021.3.3 2022-03-17 (278) 702MB classic
2021.2/stable: 2021.2.4 2021-12-22 (268) 565MB classic
2021.2/candidate: 2021.2.4 2021-12-22 (268) 565MB classic
2021.2/beta: 2021.2.4 2021-12-22 (268) 565MB classic
2021.2/edge: 2021.2.4 2021-12-22 (268) 565MB classic
2021.1/stable: 2021.1.3 2021-06-30 (248) 530MB classic
2021.1/candidate: 2021.1.3 2021-06-30 (248) 530MB classic
2021.1/beta: 2021.1.3 2021-06-30 (248) 530MB classic
2021.1/edge: 2021.1.3 2021-06-30 (248) 530MB classic
2020.3/stable: 2020.3.5 2021-03-26 (237) 559MB classic
2020.3/candidate: 2020.3.5 2021-03-26 (237) 559MB classic
2020.3/beta: 2020.3.5 2021-03-26 (237) 559MB classic
2020.3/edge: 2020.3.5 2021-03-26 (237) 559MB classic
2020.2/stable: 2020.2.5 2020-12-02 (225) 479MB classic
2020.2/candidate: 2020.2.5 2020-12-02 (225) 479MB classic
2020.2/beta: 2020.2.5 2020-12-02 (225) 479MB classic
2020.2/edge: 2020.2.5 2020-12-02 (225) 479MB classic
2020.1/stable: 2020.1.5 2020-12-02 (224) 465MB classic
2020.1/candidate: 2020.1.5 2020-12-02 (224) 465MB classic
2020.1/beta: 2020.1.5 2020-12-02 (224) 465MB classic
2020.1/edge: 2020.1.5 2020-12-02 (224) 465MB classic
2019.3/stable: 2019.3.5 2020-05-08 (199) 473MB classic
2019.3/candidate: ↑
2019.3/beta: ↑
2019.3/edge: ↑
2019.2/stable: 2019.2.6 2020-02-10 (184) 445MB classic
2019.2/candidate: ↑
2019.2/beta: ↑
2019.2/edge: ↑
2019.1/stable: 2019.1.4 2019-07-30 (148) 394MB classic
2019.1/candidate: ↑
2019.1/beta: ↑
2019.1/edge: ↑
2018.3/stable: 2018.3.7 2019-07-10 (142) 355MB classic
2018.3/candidate: ↑
2018.3/beta: ↑
2018.3/edge: ↑
2018.2/stable: 2018.2.8 2019-04-12 (128) 313MB classic
2018.2/candidate: ↑
2018.2/beta: ↑
2018.2/edge: ↑
2018.1/stable: 2018.1.6 2018-11-15 (101) 314MB classic
2018.1/candidate: ↑
2018.1/beta: ↑
2018.1/edge: ↑
2017.3/stable: 2017.3.7 2018-11-15 (100) 344MB classic
2017.3/candidate: ↑
2017.3/beta: ↑
2017.3/edge: ↑
installed: 2022.3 (314) 779MB classic

只要在里面挑一个好一点的版本就好了。
 
 
继续阅读 »
之前用的pycharm,goland被snap自动更新,更新到2022.3 , 导致之前的ida-eval-resetter 无法使用。
 
所以只好回滚到指定的pycharm版本。
 
使用snap可以下载指定版本的pycharm和goland
 
sudo snap install pycharm-professional --channel=2021.2/stable --classic
 
上面的命令下载  2021.2/stable的版本的pycharm
 
那么怎么查看pycharm的版本?
snap info pycharm-professional
name:      pycharm-professional
summary: PyCharm Professional Edition
publisher: jetbrains✓
store-url: https://snapcraft.io/pycharm-professional
contact: https://www.jetbrains.com/pycharm/documentation/
license: unset
description: |
Python IDE for professional developers. Save time while PyCharm takes care of the r
on bigger things and embrace the keyboard-centric approach to get the most of PyCha
productivity features.
commands:
- pycharm-professional
snap-id: Uqpw0ZWqy6Wh4mgaWE0rxgM5tAGCwf4D
tracking: latest/stable
refresh-date: 15 days ago, at 09:57 CST
channels:
latest/stable: 2022.3 2022-12-01 (314) 779MB classic
latest/candidate: 2022.3.1-RC 2022-12-12 (315) 793MB classic
latest/beta: 2022.3.1-RC 2022-12-12 (315) 793MB classic
latest/edge: 2022.3.1-RC 2022-12-12 (315) 793MB classic
2022.3/stable: 2022.3 2022-12-01 (314) 779MB classic
2022.3/candidate: 2022.3.1-RC 2022-12-12 (315) 793MB classic
2022.3/beta: 2022.3.1-RC 2022-12-12 (315) 793MB classic
2022.3/edge: 2022.3.1-RC 2022-12-12 (315) 793MB classic
2022.2/stable: 2022.2.4 2022-11-17 (311) 764MB classic
2022.2/candidate: 2022.2.4 2022-11-17 (311) 764MB classic
2022.2/beta: 2022.2.4 2022-11-17 (311) 764MB classic
2022.2/edge: 2022.2.4 2022-11-17 (311) 764MB classic
2022.1/stable: 2022.1.4 2022-07-21 (295) 759MB classic
2022.1/candidate: 2022.1.4 2022-07-21 (295) 759MB classic
2022.1/beta: 2022.1.4 2022-07-21 (295) 759MB classic
2022.1/edge: 2022.1.4 2022-07-21 (295) 759MB classic
2021.3/stable: 2021.3.3 2022-03-17 (278) 702MB classic
2021.3/candidate: 2021.3.3 2022-03-17 (278) 702MB classic
2021.3/beta: 2021.3.3 2022-03-17 (278) 702MB classic
2021.3/edge: 2021.3.3 2022-03-17 (278) 702MB classic
2021.2/stable: 2021.2.4 2021-12-22 (268) 565MB classic
2021.2/candidate: 2021.2.4 2021-12-22 (268) 565MB classic
2021.2/beta: 2021.2.4 2021-12-22 (268) 565MB classic
2021.2/edge: 2021.2.4 2021-12-22 (268) 565MB classic
2021.1/stable: 2021.1.3 2021-06-30 (248) 530MB classic
2021.1/candidate: 2021.1.3 2021-06-30 (248) 530MB classic
2021.1/beta: 2021.1.3 2021-06-30 (248) 530MB classic
2021.1/edge: 2021.1.3 2021-06-30 (248) 530MB classic
2020.3/stable: 2020.3.5 2021-03-26 (237) 559MB classic
2020.3/candidate: 2020.3.5 2021-03-26 (237) 559MB classic
2020.3/beta: 2020.3.5 2021-03-26 (237) 559MB classic
2020.3/edge: 2020.3.5 2021-03-26 (237) 559MB classic
2020.2/stable: 2020.2.5 2020-12-02 (225) 479MB classic
2020.2/candidate: 2020.2.5 2020-12-02 (225) 479MB classic
2020.2/beta: 2020.2.5 2020-12-02 (225) 479MB classic
2020.2/edge: 2020.2.5 2020-12-02 (225) 479MB classic
2020.1/stable: 2020.1.5 2020-12-02 (224) 465MB classic
2020.1/candidate: 2020.1.5 2020-12-02 (224) 465MB classic
2020.1/beta: 2020.1.5 2020-12-02 (224) 465MB classic
2020.1/edge: 2020.1.5 2020-12-02 (224) 465MB classic
2019.3/stable: 2019.3.5 2020-05-08 (199) 473MB classic
2019.3/candidate: ↑
2019.3/beta: ↑
2019.3/edge: ↑
2019.2/stable: 2019.2.6 2020-02-10 (184) 445MB classic
2019.2/candidate: ↑
2019.2/beta: ↑
2019.2/edge: ↑
2019.1/stable: 2019.1.4 2019-07-30 (148) 394MB classic
2019.1/candidate: ↑
2019.1/beta: ↑
2019.1/edge: ↑
2018.3/stable: 2018.3.7 2019-07-10 (142) 355MB classic
2018.3/candidate: ↑
2018.3/beta: ↑
2018.3/edge: ↑
2018.2/stable: 2018.2.8 2019-04-12 (128) 313MB classic
2018.2/candidate: ↑
2018.2/beta: ↑
2018.2/edge: ↑
2018.1/stable: 2018.1.6 2018-11-15 (101) 314MB classic
2018.1/candidate: ↑
2018.1/beta: ↑
2018.1/edge: ↑
2017.3/stable: 2017.3.7 2018-11-15 (100) 344MB classic
2017.3/candidate: ↑
2017.3/beta: ↑
2017.3/edge: ↑
installed: 2022.3 (314) 779MB classic

只要在里面挑一个好一点的版本就好了。
 
  收起阅读 »

golang mysql count(*) 的写法

mysql最为常用的语句。
 
使用golang标准库 database/sql 实现:
 
 
package main

import (
"database/sql"
"fmt"
"log"

_ "github.com/lib/pq"
)

func main() {
var count int

db, err := sql.Open("postgres", "user=test password=test dbname=foo sslmode=disable")
if err != nil {
log.Fatal(err)
}
defer db.Close()

row := db.QueryRow("SELECT COUNT(*) FROM table_name")
err := row.Scan(&count)
if err != nil {
log.Fatal(err)
}

fmt.Println(count)
}
 
即可。
 
 
继续阅读 »
mysql最为常用的语句。
 
使用golang标准库 database/sql 实现:
 
 
package main

import (
"database/sql"
"fmt"
"log"

_ "github.com/lib/pq"
)

func main() {
var count int

db, err := sql.Open("postgres", "user=test password=test dbname=foo sslmode=disable")
if err != nil {
log.Fatal(err)
}
defer db.Close()

row := db.QueryRow("SELECT COUNT(*) FROM table_name")
err := row.Scan(&count)
if err != nil {
log.Fatal(err)
}

fmt.Println(count)
}
 
即可。
 
  收起阅读 »

golang mysql包 database/sql的基本操作:增删改查 (go sql操作 保存这篇文章就足够了)

如果不是使用gorm,直接操作底层mysql命令,那么最常使用的是 database/sql 这个包了。

下面的是一些常见的操作,如果需要写底层sql语句,使用频率极高,值得收藏。
 
/*
* @Author: 30daydo
* @FilePath: /go110/go-012/g012.go
* @Description: Go 数据库基本操作
*/

package main

import (
"database/sql"
"fmt"
"runtime"

_ "github.com/go-sql-driver/mysql"
)

/**
// 创建 user 表
CREATE TABLE `user` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`name` varchar(50) COLLATE utf8mb4_general_ci NOT NULL COMMENT '姓名',
`age` int NOT NULL COMMENT '年龄',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户信息表'
**/

// 全局对象 db
var db *sql.DB

// 定义一个 user 结构体接收数据:
type user struct {
id int
age int
name string
}

// 始化数据库的函数
func initDB() (err error) {

// 构建连接的 dsn 格式是:"用户名:密码@tcp(IP:端口)/数据库?charset=utf8"
dsn := "user:password@tcp(127.0.0.1:3306)/sql_test?charset=utf8mb4&parseTime=True"

// 给全局变量赋值, 注意这里不要使用 :=
db, err = sql.Open("mysql", dsn)
if err != nil {
fmt.Println("初始化数据库失败")
return err
}

// 校验dsn是否正确
fmt.Println("尝试与数据库建立连接...")
err = db.Ping()
if err != nil {
fmt.Println("连接失败")
return err
}
fmt.Println("连接成功")

db.SetMaxOpenConns(2000) // 设置最大打开连接数
db.SetMaxIdleConns(10) // 设置最大空闲连接数

return nil
}

// 插入数据
func insertRow(name string, age int) int64 {

sqlStr := "INSERT INTO user(name, age) VALUES (?,?)"

ret, err := db.Exec(sqlStr, name, age)

if err != nil {
fmt.Printf("插入失败, err: %v\n", err)
return 0
}

// 新插入数据的 id
insertId, err := ret.LastInsertId()
if err != nil {
fmt.Printf("获取新插入数据的 ID 失败, err:%v\n", err)
return 0
}
fmt.Printf("插入成功, ID 为 %d.\n", insertId)

return insertId
}

// 查询单条数据记录
func queryRow(rowId int64) {
sqlStr := "SELECT id, name, age FROM user WHERE id = ?"
var u user
// 确保 QueryRow 之后调用 Scan 方法,释放持有的数据库链接
err := db.QueryRow(sqlStr, rowId).Scan(&u.id, &u.name, &u.age)
if err != nil {
fmt.Printf("未找到记录, 查询失败, err: %v\n", err)
return
}
fmt.Printf("查询数据成功, id: %d name: %s age: %d \n", u.id, u.name, u.age)
}

// 更新数据
func updateRow(rowId int64, newAge int) {
sqlStr := "UPDATE user SET age=? WHERE id = ?"
ret, err := db.Exec(sqlStr, newAge, rowId)
if err != nil {
fmt.Printf("更新失败 , err:%v\n", err)
return
}
// 操作影响的行数
n, err := ret.RowsAffected()
if err != nil {
fmt.Printf("获取影响函数失败, err: %v\n", err)
return
}
fmt.Printf("更新成功, 影响行数为: %d\n", n)
}

// 删除数据
func deleteRow(rowId int64) {
sqlStr := "DELETE FROM user WHERE id = ?"
ret, err := db.Exec(sqlStr, rowId)
if err != nil {
fmt.Printf("删除失败, err: %v\n", err)
return
}
n, err := ret.RowsAffected() // 操作影响的行数
if err != nil {
fmt.Printf("获取影响函数失败, err: %v\n", err)
return
}
fmt.Printf("删除成功, 影响行数为: %d\n", n)
}

// 查询多行
func queryMultiRow() {
sqlStr := "SELECT id, name, age FROM user WHERE id > ?"
rows, err := db.Query(sqlStr, 0)
if err != nil {
fmt.Printf("查询失败, err:%v\n", err)
return
}
// 重要:关闭 rows, 释放持有的数据库链接
defer rows.Close()

// 循环读取结果集中的数据
for rows.Next() {
var u user
err := rows.Scan(&u.id, &u.name, &u.age)
if err != nil {
fmt.Printf("查询多行数据失败, err:%v\n", err)
return
}
fmt.Printf("当前数据 id: %d name: %s age: %d\n", u.id, u.name, u.age)
}
}

func main() {
// 使用内置函数打印
println("Hello", "菜鸟实战")

// 初始化数据库
initDB()

// 插入数据
var id1 = insertRow("唐遇春", 17)
var id2 = insertRow("冯显", 38)
var id3 = insertRow("花千里", 20)

// 查询多行数据
queryMultiRow()

// 更新数据
updateRow(id2, 35)

// 查询单行数据
queryRow(id2)

// 删除数据
deleteRow(id1)

// 查询多行数据
queryMultiRow()
queryRow(id3)

// 当前版本
fmt.Printf("版本: %s \n", runtime.Version())
}

 
当然,上面的语句也可以不用Prepare的写法,直接插入进去:
 
func insertDirect() {
db, err := sql.Open("mysql",
"username:password@tcp(127.0.0.1:3306)/test_db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
sql := "INSERT INTO user(id, name, pwd) VALUES(?, ?, ?)"
res, err := db.Exec(sql, 13, "Jobs", "444555")
checkErr(err)
fmt.Println(res.LastInsertId())

}
继续阅读 »
如果不是使用gorm,直接操作底层mysql命令,那么最常使用的是 database/sql 这个包了。

下面的是一些常见的操作,如果需要写底层sql语句,使用频率极高,值得收藏。
 
/*
* @Author: 30daydo
* @FilePath: /go110/go-012/g012.go
* @Description: Go 数据库基本操作
*/

package main

import (
"database/sql"
"fmt"
"runtime"

_ "github.com/go-sql-driver/mysql"
)

/**
// 创建 user 表
CREATE TABLE `user` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`name` varchar(50) COLLATE utf8mb4_general_ci NOT NULL COMMENT '姓名',
`age` int NOT NULL COMMENT '年龄',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户信息表'
**/

// 全局对象 db
var db *sql.DB

// 定义一个 user 结构体接收数据:
type user struct {
id int
age int
name string
}

// 始化数据库的函数
func initDB() (err error) {

// 构建连接的 dsn 格式是:"用户名:密码@tcp(IP:端口)/数据库?charset=utf8"
dsn := "user:password@tcp(127.0.0.1:3306)/sql_test?charset=utf8mb4&parseTime=True"

// 给全局变量赋值, 注意这里不要使用 :=
db, err = sql.Open("mysql", dsn)
if err != nil {
fmt.Println("初始化数据库失败")
return err
}

// 校验dsn是否正确
fmt.Println("尝试与数据库建立连接...")
err = db.Ping()
if err != nil {
fmt.Println("连接失败")
return err
}
fmt.Println("连接成功")

db.SetMaxOpenConns(2000) // 设置最大打开连接数
db.SetMaxIdleConns(10) // 设置最大空闲连接数

return nil
}

// 插入数据
func insertRow(name string, age int) int64 {

sqlStr := "INSERT INTO user(name, age) VALUES (?,?)"

ret, err := db.Exec(sqlStr, name, age)

if err != nil {
fmt.Printf("插入失败, err: %v\n", err)
return 0
}

// 新插入数据的 id
insertId, err := ret.LastInsertId()
if err != nil {
fmt.Printf("获取新插入数据的 ID 失败, err:%v\n", err)
return 0
}
fmt.Printf("插入成功, ID 为 %d.\n", insertId)

return insertId
}

// 查询单条数据记录
func queryRow(rowId int64) {
sqlStr := "SELECT id, name, age FROM user WHERE id = ?"
var u user
// 确保 QueryRow 之后调用 Scan 方法,释放持有的数据库链接
err := db.QueryRow(sqlStr, rowId).Scan(&u.id, &u.name, &u.age)
if err != nil {
fmt.Printf("未找到记录, 查询失败, err: %v\n", err)
return
}
fmt.Printf("查询数据成功, id: %d name: %s age: %d \n", u.id, u.name, u.age)
}

// 更新数据
func updateRow(rowId int64, newAge int) {
sqlStr := "UPDATE user SET age=? WHERE id = ?"
ret, err := db.Exec(sqlStr, newAge, rowId)
if err != nil {
fmt.Printf("更新失败 , err:%v\n", err)
return
}
// 操作影响的行数
n, err := ret.RowsAffected()
if err != nil {
fmt.Printf("获取影响函数失败, err: %v\n", err)
return
}
fmt.Printf("更新成功, 影响行数为: %d\n", n)
}

// 删除数据
func deleteRow(rowId int64) {
sqlStr := "DELETE FROM user WHERE id = ?"
ret, err := db.Exec(sqlStr, rowId)
if err != nil {
fmt.Printf("删除失败, err: %v\n", err)
return
}
n, err := ret.RowsAffected() // 操作影响的行数
if err != nil {
fmt.Printf("获取影响函数失败, err: %v\n", err)
return
}
fmt.Printf("删除成功, 影响行数为: %d\n", n)
}

// 查询多行
func queryMultiRow() {
sqlStr := "SELECT id, name, age FROM user WHERE id > ?"
rows, err := db.Query(sqlStr, 0)
if err != nil {
fmt.Printf("查询失败, err:%v\n", err)
return
}
// 重要:关闭 rows, 释放持有的数据库链接
defer rows.Close()

// 循环读取结果集中的数据
for rows.Next() {
var u user
err := rows.Scan(&u.id, &u.name, &u.age)
if err != nil {
fmt.Printf("查询多行数据失败, err:%v\n", err)
return
}
fmt.Printf("当前数据 id: %d name: %s age: %d\n", u.id, u.name, u.age)
}
}

func main() {
// 使用内置函数打印
println("Hello", "菜鸟实战")

// 初始化数据库
initDB()

// 插入数据
var id1 = insertRow("唐遇春", 17)
var id2 = insertRow("冯显", 38)
var id3 = insertRow("花千里", 20)

// 查询多行数据
queryMultiRow()

// 更新数据
updateRow(id2, 35)

// 查询单行数据
queryRow(id2)

// 删除数据
deleteRow(id1)

// 查询多行数据
queryMultiRow()
queryRow(id3)

// 当前版本
fmt.Printf("版本: %s \n", runtime.Version())
}

 
当然,上面的语句也可以不用Prepare的写法,直接插入进去:
 
func insertDirect() {
db, err := sql.Open("mysql",
"username:password@tcp(127.0.0.1:3306)/test_db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
sql := "INSERT INTO user(id, name, pwd) VALUES(?, ?, ?)"
res, err := db.Exec(sql, 13, "Jobs", "444555")
checkErr(err)
fmt.Println(res.LastInsertId())

}
收起阅读 »

宁稳网可转债弹性因子 python回测

利用文章: 
https://t.zsxq.com/08779XPlk

的数据和代码,修改原来的代码
 
df=df.sort_values('税前收益率',ascending=False)
改为
df=df.sort_values('弹性',ascending=False) # 倒序


df=df.sort_values('弹性',ascending=True) # 正序

还有这里:
 
df = pd.read_excel(full_path)

df['税前收益率'] = df['税前收益率'].map(convert_float)
df['转股价格']=df['转股价格'].astype(float)
df=df[['转债代码','转债名称','转债价格','税前收益率']]

改为:

df['弹性'] = df['弹性'].map(convert_float)
df['转股价格']=df['转股价格'].astype(float)
df=df[['转债代码','转债名称','转债价格','弹性']]

如果修改持有个数,天数,修改HOLD_NUM ,和FREQ 即可

得到下面的图:
 

20221204003-name.jpg
点击查看大图
 
"1天,大到小" 收益率 -30%,最大回撤34%

"1天,小到大" 收益率15.74%,最大回撤10.43%

'5天,大到小' 收益率-25%,最大回撤30.71%

'5天,小到大' 收益率7.36%, 最大回撤10.47%


可见弹性按照单天弹性值排名,买入最大的10只,无论按1天还是5天轮动,今天收益率是亏损的;

可见弹性按照单天弹性值排名,买入最小的10只,无论按1天还是5天轮动,今天收益率是盈利的;
 
如果对数据有疑问,可以到公众号后台留言~
继续阅读 »
利用文章: 
https://t.zsxq.com/08779XPlk

的数据和代码,修改原来的代码
 
df=df.sort_values('税前收益率',ascending=False)
改为
df=df.sort_values('弹性',ascending=False) # 倒序


df=df.sort_values('弹性',ascending=True) # 正序

还有这里:
 
df = pd.read_excel(full_path)

df['税前收益率'] = df['税前收益率'].map(convert_float)
df['转股价格']=df['转股价格'].astype(float)
df=df[['转债代码','转债名称','转债价格','税前收益率']]

改为:

df['弹性'] = df['弹性'].map(convert_float)
df['转股价格']=df['转股价格'].astype(float)
df=df[['转债代码','转债名称','转债价格','弹性']]

如果修改持有个数,天数,修改HOLD_NUM ,和FREQ 即可

得到下面的图:
 

20221204003-name.jpg
点击查看大图
 
"1天,大到小" 收益率 -30%,最大回撤34%

"1天,小到大" 收益率15.74%,最大回撤10.43%

'5天,大到小' 收益率-25%,最大回撤30.71%

'5天,小到大' 收益率7.36%, 最大回撤10.47%


可见弹性按照单天弹性值排名,买入最大的10只,无论按1天还是5天轮动,今天收益率是亏损的;

可见弹性按照单天弹性值排名,买入最小的10只,无论按1天还是5天轮动,今天收益率是盈利的;
 
如果对数据有疑问,可以到公众号后台留言~
收起阅读 »

python父类如何判断子类时候实现了某个方法或者属性赋值

用hasattr内置函数即可
 
看看下面的例子
 class Parent:

def __init__(self):
self.name='parent'
self.age=10

def run(self):
if hasattr(self,'get_salary'):
print('has func')
print(self.get_salary())

class Child(Parent):

def __init__(self):
# self.name='child'
Parent.__init__(self)
self.salary=100


def get_salary(self):
return self.salary

obj = Child()
obj.run()

obj.run调用的是parent里面的方法。
而parent的run里面调用一个hasattr, 来判断self 是否有get_salary这个函数。
因为self是从子类传进去的,所以self实际是 child的实例。
 
因为child里面是有get_salary方法(属性)的,所以hasatrr 是返回true, 然后调用子类的self.get_salary
从而程序没有报错。打印正确的返回数据
 
继续阅读 »
用hasattr内置函数即可
 
看看下面的例子
 class Parent:

def __init__(self):
self.name='parent'
self.age=10

def run(self):
if hasattr(self,'get_salary'):
print('has func')
print(self.get_salary())

class Child(Parent):

def __init__(self):
# self.name='child'
Parent.__init__(self)
self.salary=100


def get_salary(self):
return self.salary

obj = Child()
obj.run()

obj.run调用的是parent里面的方法。
而parent的run里面调用一个hasattr, 来判断self 是否有get_salary这个函数。
因为self是从子类传进去的,所以self实际是 child的实例。
 
因为child里面是有get_salary方法(属性)的,所以hasatrr 是返回true, 然后调用子类的self.get_salary
从而程序没有报错。打印正确的返回数据
  收起阅读 »

Ptrade QMT 哪家费率最便宜,门槛最低?

最近咨询的朋友很多。这里做个汇总,可以节省大家的时间。 
笔者只会推荐合适的券商和量化接口给你,而不会推荐坑爹的券商给你。
 
如果你不信任,绕路即可,莫浪费大家时间。或者你有好的资源或者券商,你也可以推荐给我,可以给你发红包,奖励你提供有用的情报信息。
 
目前支持ptrade,qmt可以股票可以免五(0.1元起步)的券商,只有国盛,需要的可以找我开。 但是有入金门槛,ptrade门槛30w, qmt 的门槛50w。 国盛的ptrade可以链接外网。其他家的不行。但国盛的qmt不支持运行在云服务器。
 
其他家的qmt是支持的云服务器的,但国金的qmt会每天自动退出,需要每次手动点击登录,我也写了一个自动登录的小脚本,开完户的朋友可以拿去用用。
 
门槛低的国信,适合新手使用,但是没有miniqmt,这个东西也不一定适合你。 真的需要这个功能,在qmt里面可以写一段代码,让它把下单接口变成http指令,把下单功能独立出来,也是可以的。不过这个属于进阶功能。 需要的也可以把代码给你。
 
笔者用过大部分券商的ptrade qmt,所以个人的指导与推荐还是很有指导意见的。 写过的策略也大部分人(99%)多, 平台好坏,坑的多寡,哪里有雷,都已经摸清摸楚。 可以大大节省你们的时间。 当然你也可以忽略,自己去折腾折腾。选ptrade 还是qmt,然后哪个券商,什么费率。
 
mmexport1669547412034.png


 
需要开通ptrade - qmt 低门槛 低费率 的 或者可以提供更低费用的资源的可以加微信:
 

ptrade 接口文档: http://ptradeapi.com
 
继续阅读 »
最近咨询的朋友很多。这里做个汇总,可以节省大家的时间。 
笔者只会推荐合适的券商和量化接口给你,而不会推荐坑爹的券商给你。
 
如果你不信任,绕路即可,莫浪费大家时间。或者你有好的资源或者券商,你也可以推荐给我,可以给你发红包,奖励你提供有用的情报信息。
 
目前支持ptrade,qmt可以股票可以免五(0.1元起步)的券商,只有国盛,需要的可以找我开。 但是有入金门槛,ptrade门槛30w, qmt 的门槛50w。 国盛的ptrade可以链接外网。其他家的不行。但国盛的qmt不支持运行在云服务器。
 
其他家的qmt是支持的云服务器的,但国金的qmt会每天自动退出,需要每次手动点击登录,我也写了一个自动登录的小脚本,开完户的朋友可以拿去用用。
 
门槛低的国信,适合新手使用,但是没有miniqmt,这个东西也不一定适合你。 真的需要这个功能,在qmt里面可以写一段代码,让它把下单接口变成http指令,把下单功能独立出来,也是可以的。不过这个属于进阶功能。 需要的也可以把代码给你。
 
笔者用过大部分券商的ptrade qmt,所以个人的指导与推荐还是很有指导意见的。 写过的策略也大部分人(99%)多, 平台好坏,坑的多寡,哪里有雷,都已经摸清摸楚。 可以大大节省你们的时间。 当然你也可以忽略,自己去折腾折腾。选ptrade 还是qmt,然后哪个券商,什么费率。
 
mmexport1669547412034.png


 
需要开通ptrade - qmt 低门槛 低费率 的 或者可以提供更低费用的资源的可以加微信:
 

ptrade 接口文档: http://ptradeapi.com
  收起阅读 »

Linux 运行resilio (一个跨平台的资源共享类软件)【附pro license】

什么是resilio ?

只需要知道一个分享密钥key,即可同步整个文件夹了,不需要第三方服务器,不受存储空间和流量限制,Resilio Sync的数据不在云端,而是存储在本地,传输速度基本上只受用户网络带宽的制约,它可以支持多人同时同步,而且,越多人对文件夹进行同步,则同步速度越快,没有审查,不过现在已经属于404 的产品了,不过如果找到神key,资源还是蛮丰富的。

安装过程:

1、打开官网下载安装Resilio Sync,国内用户需要自备网络环境。
对于linux , ubuntu,解压后实际只有一个可执行文件,猜测这个很可能是golang写的。因为大部分golang的程序都可以编译成一个文件。
 $ ./rslsync  
By using this application, you agree to our Privacy Policy, Terms of Use and End User License Agreement.
https://www.resilio.com/legal/privacy
https://www.resilio.com/legal/terms-of-use
https://www.resilio.com/legal/eula
 

运行后rslsync是在后台运行的。 不会返回访问界面,当然linux下也不会有界面。 需要你打开浏览器, 输入 http://127.0.0.1:8888
就可以看到resilio的内容了。
 

20221127001.jpg

 

2、下载Resilio Sync授权文件:ResilioSyncPro.btskey. 附件里面

3、然后解压,解压后文件后缀是btskey,打开安装好的Resilio Sync,打开设置的License界面,点击Apply License

这个时候页面是空的,没有任何空的内容。 这个时候就需要一些神key。因为上面也说了,resilio是通过分享key,来获取资源的。
比如zlibrary,有人共享了不少电子书在某个文件夹里面。 只要找到一个key,你就可以找到一个资源分享的组织。
 

20221127003.jpg

 
需要电子书的key,可以关注公众号:

 
回复:电子书key
可以获取zibibrary 几个T的电子书。

 
继续阅读 »
什么是resilio ?

只需要知道一个分享密钥key,即可同步整个文件夹了,不需要第三方服务器,不受存储空间和流量限制,Resilio Sync的数据不在云端,而是存储在本地,传输速度基本上只受用户网络带宽的制约,它可以支持多人同时同步,而且,越多人对文件夹进行同步,则同步速度越快,没有审查,不过现在已经属于404 的产品了,不过如果找到神key,资源还是蛮丰富的。

安装过程:

1、打开官网下载安装Resilio Sync,国内用户需要自备网络环境。
对于linux , ubuntu,解压后实际只有一个可执行文件,猜测这个很可能是golang写的。因为大部分golang的程序都可以编译成一个文件。
 $ ./rslsync  
By using this application, you agree to our Privacy Policy, Terms of Use and End User License Agreement.
https://www.resilio.com/legal/privacy
https://www.resilio.com/legal/terms-of-use
https://www.resilio.com/legal/eula
 

运行后rslsync是在后台运行的。 不会返回访问界面,当然linux下也不会有界面。 需要你打开浏览器, 输入 http://127.0.0.1:8888
就可以看到resilio的内容了。
 

20221127001.jpg

 

2、下载Resilio Sync授权文件:ResilioSyncPro.btskey. 附件里面

3、然后解压,解压后文件后缀是btskey,打开安装好的Resilio Sync,打开设置的License界面,点击Apply License

这个时候页面是空的,没有任何空的内容。 这个时候就需要一些神key。因为上面也说了,resilio是通过分享key,来获取资源的。
比如zlibrary,有人共享了不少电子书在某个文件夹里面。 只要找到一个key,你就可以找到一个资源分享的组织。
 

20221127003.jpg

 
需要电子书的key,可以关注公众号:

 
回复:电子书key
可以获取zibibrary 几个T的电子书。

  收起阅读 »

可转债到期收益率因子回测 YTM回测 vs 双低 低溢价 低价策略

最近看到有人发的一些可转债高YTM的回测结果图,说高YTM轮动收益达到年化20%。嗯?高TYM实际上大部分是和低价转债是重叠的,理论和实际收益率都应该不会有这么高的。

自从优矿转为收费后,基本就没有登录过了。现在的回测在本地进行了,宁稳网的可转债数据上有YTM这因子数据,所以这次使用宁稳网的数据回测。也不使用什么第三方框架了,直接手写回测,模拟交易过程。
 
逻辑就是按照交易日期,读取所有excel数据。保存到一个dataframe里面,然后按照日期对YTM排名。 选出排名N名的进行买入,掉出N名的进行卖出。
 
数据源
 
采用宁稳的全表数据,里面有YTM因子

20221126001.jpg

 
20221126002.jpg


 
YTM收益率曲线
本地使用python编写回测代码,纯手工,不使用第三方框架。 平时我也多次力荐大家手写,别依赖那些第三方框架,什么vnpy,backtrade,一是特别难用,数据要适配,二是不好调试,除非你对它们的源码特别熟悉。本回测代码和宁稳数据放在个人知识星球,大部分人其实只关心回测结果就够了。

下图是2022年1月1日至11月25日的回测数据。持有10个标的,红色的是1天调仓,蓝色的是5天调仓
 

20221126006.jpg

 
5天轮动,2022年的收益率为4.29%,最大回撤6.98% 。

1天轮动,2022年的收益率为9.33%,最大回撤5.32% 。

1天轮动的整体收益率要比5天轮动的高5个点,并且最大回撤也小一点。

持仓日志
 

20221126009.jpg


从持仓以及调仓日志来看,买入的大部分是低价转债,且部分也是之前有瑕疵,爆过雷的转债。
 
高YTM轮动 vs 双低,低溢价,低价

接着对比一下几个常规的转债策略,双低,低溢价,低价。

回测条件相同,起始时间2022-01-01,结束时间2022-11-25。

持有10只,5天轮动。
 

20221126005.jpg

 
在今年的可转债偏弱的行情下,高YTM轮动在这4个策略里面的收益率是最高的。

低溢价一如既然的高波动,一会ICU,一会蹦迪。但笔者觉得跌到前期最大回撤的位置,低溢价可以考虑介入的。
 

20221126010.jpg

 
YTM和低价格的今年回测收益率接近,YTM高了一个点左右,最大回撤接近。 而低溢价还在回撤的途中,目前今年的收益率为-25%,如果轮动频率高一些,低溢价收益率会高一些,强赎的转债基本会被轮入低溢价标的,因为最近半年强赎后的转债像进入了死亡螺旋一样,正股转债一起跌;因此排除强赎转债会对低溢价收益率有不少的提升。
 
完整代码以及数据请开通星球查收。
f088492c7da0ce8b90cbdb7aafe3b191.png

 
 
 
 
 
继续阅读 »
最近看到有人发的一些可转债高YTM的回测结果图,说高YTM轮动收益达到年化20%。嗯?高TYM实际上大部分是和低价转债是重叠的,理论和实际收益率都应该不会有这么高的。

自从优矿转为收费后,基本就没有登录过了。现在的回测在本地进行了,宁稳网的可转债数据上有YTM这因子数据,所以这次使用宁稳网的数据回测。也不使用什么第三方框架了,直接手写回测,模拟交易过程。
 
逻辑就是按照交易日期,读取所有excel数据。保存到一个dataframe里面,然后按照日期对YTM排名。 选出排名N名的进行买入,掉出N名的进行卖出。
 
数据源
 
采用宁稳的全表数据,里面有YTM因子

20221126001.jpg

 
20221126002.jpg


 
YTM收益率曲线
本地使用python编写回测代码,纯手工,不使用第三方框架。 平时我也多次力荐大家手写,别依赖那些第三方框架,什么vnpy,backtrade,一是特别难用,数据要适配,二是不好调试,除非你对它们的源码特别熟悉。本回测代码和宁稳数据放在个人知识星球,大部分人其实只关心回测结果就够了。

下图是2022年1月1日至11月25日的回测数据。持有10个标的,红色的是1天调仓,蓝色的是5天调仓
 

20221126006.jpg

 
5天轮动,2022年的收益率为4.29%,最大回撤6.98% 。

1天轮动,2022年的收益率为9.33%,最大回撤5.32% 。

1天轮动的整体收益率要比5天轮动的高5个点,并且最大回撤也小一点。

持仓日志
 

20221126009.jpg


从持仓以及调仓日志来看,买入的大部分是低价转债,且部分也是之前有瑕疵,爆过雷的转债。
 
高YTM轮动 vs 双低,低溢价,低价

接着对比一下几个常规的转债策略,双低,低溢价,低价。

回测条件相同,起始时间2022-01-01,结束时间2022-11-25。

持有10只,5天轮动。
 

20221126005.jpg

 
在今年的可转债偏弱的行情下,高YTM轮动在这4个策略里面的收益率是最高的。

低溢价一如既然的高波动,一会ICU,一会蹦迪。但笔者觉得跌到前期最大回撤的位置,低溢价可以考虑介入的。
 

20221126010.jpg

 
YTM和低价格的今年回测收益率接近,YTM高了一个点左右,最大回撤接近。 而低溢价还在回撤的途中,目前今年的收益率为-25%,如果轮动频率高一些,低溢价收益率会高一些,强赎的转债基本会被轮入低溢价标的,因为最近半年强赎后的转债像进入了死亡螺旋一样,正股转债一起跌;因此排除强赎转债会对低溢价收益率有不少的提升。
 
完整代码以及数据请开通星球查收。
f088492c7da0ce8b90cbdb7aafe3b191.png

 
 
 
 
  收起阅读 »

Ptrade多策略如何编写?

多策略需要解决的一个最主要的问题,就是仓位管理。
 
系统自带的读取仓位函数需要你重写。
 
目前使用一个类来管理仓位:
 
初始化部分:
class PositionManager():

def __init__(self):
if SINGLE_FACTOR not in [1, 2, 3, 4]:
raise ValueError('策略数字有误')

self.strategy = SINGLE_FACTOR
NOTEBOOK_PATH = '/home/fly/notebook/'
# self.filename = NOTEBOOK_PATH + 'S-{}.txt'.format(self.strategy)
self.filename = NOTEBOOK_PATH + personal_define_filename
self.portfolio = self.read()
log.info(self.portfolio)
self.write()

def init_data(self):
js_data = {
'cash': CASH,
'strategy': self.strategy,
'positions': ,
'portfolio_value': None,
'positions_value': None,
'capital_used': None,
'start_date': datetime.datetime.now().strftime('%Y%m%d%H%M%S'),
'current_day': 0,
}

数据需要收盘后保存到文件,数据库也行;不过考虑到大部分ptrade(除了国盛,需要开通的可以联系公众号:可转债量化分析)都没有连接外网功能,所以最简单的方式就是写入文件,纯粹的文本文件。
 
目前笔者使用json存储

20221116001.jpg

 点击查看大图
 
这样存储有一个好处,就是如果你想中途修改策略持仓,可以直接修改这个文本文件。比如你的策略不小心买入了一只强赎的转债,你想手动卖掉,那么很简单,你只要在这个json文件里面把对应的持仓删除,再把他的市值加到可用资金里面去即可。 用法是相当灵活。
 
需要完整代码或者指导的朋友可以关注下面公众号和知识星球。
 
 

 

 
 
继续阅读 »
多策略需要解决的一个最主要的问题,就是仓位管理。
 
系统自带的读取仓位函数需要你重写。
 
目前使用一个类来管理仓位:
 
初始化部分:
class PositionManager():

def __init__(self):
if SINGLE_FACTOR not in [1, 2, 3, 4]:
raise ValueError('策略数字有误')

self.strategy = SINGLE_FACTOR
NOTEBOOK_PATH = '/home/fly/notebook/'
# self.filename = NOTEBOOK_PATH + 'S-{}.txt'.format(self.strategy)
self.filename = NOTEBOOK_PATH + personal_define_filename
self.portfolio = self.read()
log.info(self.portfolio)
self.write()

def init_data(self):
js_data = {
'cash': CASH,
'strategy': self.strategy,
'positions': ,
'portfolio_value': None,
'positions_value': None,
'capital_used': None,
'start_date': datetime.datetime.now().strftime('%Y%m%d%H%M%S'),
'current_day': 0,
}

数据需要收盘后保存到文件,数据库也行;不过考虑到大部分ptrade(除了国盛,需要开通的可以联系公众号:可转债量化分析)都没有连接外网功能,所以最简单的方式就是写入文件,纯粹的文本文件。
 
目前笔者使用json存储

20221116001.jpg

 点击查看大图
 
这样存储有一个好处,就是如果你想中途修改策略持仓,可以直接修改这个文本文件。比如你的策略不小心买入了一只强赎的转债,你想手动卖掉,那么很简单,你只要在这个json文件里面把对应的持仓删除,再把他的市值加到可用资金里面去即可。 用法是相当灵活。
 
需要完整代码或者指导的朋友可以关注下面公众号和知识星球。
 
 

 

 
  收起阅读 »

Ptrade拆单 分批下单 python代码 可转债/股票

在转债市场,部分转债的流动性很差,有时候连成交100张也需要等待一段时间。

所以如果资金量大,就需要拆单操作。

交易代码部分,如果设置 SPLIT_ORDER_ENABLE = True 即进行拆单操作:


在交易部分:(省略部分不相关代码)
                if SPLIT_ORDER_ENABLE:
split_order(code, BUY_DIRECTION, amount)
else:
buy_price = round(buy_price, 3)
ret = order(code, amount, limit_price=buy_price)
BUY_DIRECTION 为买,值是1,一个常量

SELL_DIRECTION为卖,值为-1,也是一个常量,传入拆单函数中
 
 
拆单函数提取出来:
def split_order(code, direction, target_count):
'''
拆单
:param code: 股票代码
:param direction: 买:1 卖:-1
:param target_count: 总共要卖的股数
:return:
'''

count = int(target_count / EACH_ORDER_COUNT)
# 例如:560张, 200 张一单, 2次 + 最后一次160 张

remain_count = target_count % EACH_ORDER_COUNT

for i in range(count):
ret = order(code, direction * EACH_ORDER_COUNT)
time.sleep(SPLIT_ORDER_DELAY) # 拆开的单子等待一个时间,再下另外一单

if direction == 1: # 买的时候需要整数,卖则不需要
remain_count = int(remain_count / 10) * 10

if remain_count > 0:
ret = order(code, direction * remain_count)
常用的操作都类似,写成模块方便下次调用。写多了就是套模块。
 
在可转债实盘中,拆单后每笔下单200张,就是每次200张下一次单,因为有可能不是马上成交,所以还需要一段等待延时,再去下单;不然你的拆单也变得没有意义,因为委托那里都是挂的你的单,并没有被消化掉。
 

20221110002.jpg



20221110003.jpg


可以看到实盘交易日志,即使拆单为200张一笔,外加一段延时,成交张数也是稀稀拉拉的,出现了不少的部分成交;也就是一次连200张都未成交完成;一笔200张的,部分成交10张,20张,都是有可能的,这也足以说明,可转债的流动性问题,滑点是很难被忽略的。

当然,这是轮动调仓的时候,金额较大的情况下拆单。如果高频交易下就不能这么操作了。 
具体怎么写,可以关注个人公众号与知识星球。

知识星球原文:

20221110001.jpg

 
如需要代写量化策略实盘代码,可以到个人公众号【可转债量化分析】后台留言。
 
 
 
 
继续阅读 »
在转债市场,部分转债的流动性很差,有时候连成交100张也需要等待一段时间。

所以如果资金量大,就需要拆单操作。

交易代码部分,如果设置 SPLIT_ORDER_ENABLE = True 即进行拆单操作:


在交易部分:(省略部分不相关代码)
                if SPLIT_ORDER_ENABLE:
split_order(code, BUY_DIRECTION, amount)
else:
buy_price = round(buy_price, 3)
ret = order(code, amount, limit_price=buy_price)
BUY_DIRECTION 为买,值是1,一个常量

SELL_DIRECTION为卖,值为-1,也是一个常量,传入拆单函数中
 
 
拆单函数提取出来:
def split_order(code, direction, target_count):
'''
拆单
:param code: 股票代码
:param direction: 买:1 卖:-1
:param target_count: 总共要卖的股数
:return:
'''

count = int(target_count / EACH_ORDER_COUNT)
# 例如:560张, 200 张一单, 2次 + 最后一次160 张

remain_count = target_count % EACH_ORDER_COUNT

for i in range(count):
ret = order(code, direction * EACH_ORDER_COUNT)
time.sleep(SPLIT_ORDER_DELAY) # 拆开的单子等待一个时间,再下另外一单

if direction == 1: # 买的时候需要整数,卖则不需要
remain_count = int(remain_count / 10) * 10

if remain_count > 0:
ret = order(code, direction * remain_count)
常用的操作都类似,写成模块方便下次调用。写多了就是套模块。
 
在可转债实盘中,拆单后每笔下单200张,就是每次200张下一次单,因为有可能不是马上成交,所以还需要一段等待延时,再去下单;不然你的拆单也变得没有意义,因为委托那里都是挂的你的单,并没有被消化掉。
 

20221110002.jpg



20221110003.jpg


可以看到实盘交易日志,即使拆单为200张一笔,外加一段延时,成交张数也是稀稀拉拉的,出现了不少的部分成交;也就是一次连200张都未成交完成;一笔200张的,部分成交10张,20张,都是有可能的,这也足以说明,可转债的流动性问题,滑点是很难被忽略的。

当然,这是轮动调仓的时候,金额较大的情况下拆单。如果高频交易下就不能这么操作了。 
具体怎么写,可以关注个人公众号与知识星球。

知识星球原文:

20221110001.jpg

 
如需要代写量化策略实盘代码,可以到个人公众号【可转债量化分析】后台留言。
 
 
 
  收起阅读 »

akshare获取reits数据,搞笑,数据源测试过没有呀,没有集思录会员还没获取?


描述: 集思录-实时数据-REITs-A股 REITs

限量: 单次返回所有 REITs 的基本信息数据
 



20221107004.jpg

估计是旧的,以前游客就可以获取到数据的了。现在就不行了。
 

20221107003.jpg

 
然后另外一个获取东财的函数,获取了20个就完事了。
结果人间现在超过20家,获取的数据都不全的。
 
akshare,用的还是有点糟心。长期来看,还是自己写靠谱,有些坑,你第一次用就可以发现,但是有些坑,却是埋在那里,像个定时炸弹。
 
 
20221108003.jpg


akshare的代码:

20221108004.jpg


 

 
继续阅读 »


描述: 集思录-实时数据-REITs-A股 REITs

限量: 单次返回所有 REITs 的基本信息数据
 



20221107004.jpg

估计是旧的,以前游客就可以获取到数据的了。现在就不行了。
 

20221107003.jpg

 
然后另外一个获取东财的函数,获取了20个就完事了。
结果人间现在超过20家,获取的数据都不全的。
 
akshare,用的还是有点糟心。长期来看,还是自己写靠谱,有些坑,你第一次用就可以发现,但是有些坑,却是埋在那里,像个定时炸弹。
 
 
20221108003.jpg


akshare的代码:

20221108004.jpg


 

  收起阅读 »

Ptrade里写策略坑比较多的地方(一)

总结一些,给过来人少踩些坑
 1. 后缀符号不统一
这个是天煞的产品涉及的问题。 好好地代码后缀,比如 深圳市场的 有时候出现 300333.SZ , 有时候结构体里面却会是 300333.XSHE,
 
比如返回的orders 字典,里面用的是 00333.XSHE,而仓位的结构体 position 里面用的缺失 .sz

20221103001.jpg

 
类似这样的问题在很多函数里面都有。
 
2. 部分成交 的主推函数
如果一个订单,部分成交,会先触发部分成交主推; 然后最后一个部分成交,反而会触发全部成交主推。
细想,似乎也是合理的,只是,你在全部成交里面返回的成交数量,实际只是最后一次部分成交的量。
 
3. 想到再写
更多更新 可以参看个人知识星球或者公众号。

 
 
 
继续阅读 »
总结一些,给过来人少踩些坑
 1. 后缀符号不统一
这个是天煞的产品涉及的问题。 好好地代码后缀,比如 深圳市场的 有时候出现 300333.SZ , 有时候结构体里面却会是 300333.XSHE,
 
比如返回的orders 字典,里面用的是 00333.XSHE,而仓位的结构体 position 里面用的缺失 .sz

20221103001.jpg

 
类似这样的问题在很多函数里面都有。
 
2. 部分成交 的主推函数
如果一个订单,部分成交,会先触发部分成交主推; 然后最后一个部分成交,反而会触发全部成交主推。
细想,似乎也是合理的,只是,你在全部成交里面返回的成交数量,实际只是最后一次部分成交的量。
 
3. 想到再写
更多更新 可以参看个人知识星球或者公众号。

 
 
  收起阅读 »