聚宽(JoinQuant)、优矿(Uqer)等等都是非常好的量化交易平台,本文记录在JoinQuant上进行基础的策略框架。
首先,肯定先要注册一个JoinQuant账号,这个不废话。
在聚宽选择【策略研究】->【策略列表】
接下来,点击【新建策略】
接下来,就是这样一个页面:
- 左边是代码区
- 右边的设定包括回测的时间、初始资金、显示频率、Python版本
现在把里面内容都删了,写自己的东西。
最简单的交易策略
首先,要执行一个非常简单的交易策略
初始资金设置为10万,每天买100股平安银行
为了这个策略能执行,首先策略要符合“初始化+周期循环”框架:
初始化:选定要交易的股票为平安银行
每天循环:买100股的平安银行
初始化与循环,可以用以下方法写成代码(这是最新的方法):
def initialize(context):
run_daily(period,time='every_bar')
这里是用来写初始化代码的地方,例子中就是选定要交易的股票为平安银行
def period(context):
这里是用来写周期循环代码的地方,例子中就是买100股的平安银行
关于context
和period
的内容,会在后面讲到。
接下来,如何基于这个框架来写代码?。
首先,要选定要交易的股票是平安银行:
g.security = '000001.XSHE'
买入100股平安银行(市价单写法):
order(g.security, 100)
完整代码为:
# 导入函数库
from jqdata import *
def initialize(context):
run_daily(period,time='every_bar')
g.security = '000001.XSHE'
def period(context):
order(g.security, 100)
第一次回测
接下来,把右边的频率设置成天,点击右边的回测按钮,就可以进行回测了,结果如下
其中,红线是基准收益(默认是沪深300指数,代表整个市场增长水平),蓝线是我们跑的。
周期循环具体是什么时候开始的呢?
- 如果策略频率为天,是每个交易日开始生效,从9:30直到15:00(从股市开市到收市),所以例子中是每个交易日9:30开市循环就开始,一天一次地循环执行买入股票的操作。
- 如果策略频率为分钟,是每个分钟开始时执行,所以例子中的买入股票的操作是每个交易日从9:30:00开始,然后9:31:00,直到14:59:00。接着下一天9:30:00,如此一分钟一次地循环执行的。
下单、函数、API
下面,来深入理解这些框架:
想了解它们怎么用,可以在API文档中查找,比如order - API按股价下单)
可以看到,order(security, amount, style=None, side='long', pindex=0, close_today=False)
,一共可以接收6个参数,其中:
security
是标的代码;amount
是数量,单位为100,负数为卖出;style
决定下的是市价单还是限价单,默认None
是市价单;side
决定是开空单还是开多单,股票只能开多pindex
是在多资金仓位时选择资金仓位,股票一般用不到
order
函数是有返回值的,如果创建订单成功则返回Order对象,失败则返回None。
常见下单函数:
order(security,amount)
,这个刚才说了,就不讲了order_target(security,amount)
,通过买卖,将股票仓位调到一定数量(单位:股)order_value(security,value)
,买卖一定价值量(单位:元)股票。order_target_value(security,value)
,通过买卖,将股票仓位调整至一定价值量(单位:元)
股票的拆分合并、分红,交易的税费,下单导致成交价向不利的方向波动,这些JoinQuant都考虑并且仿真处理了,这些暂时不关心。
context
首先,什么是context?
context顾名思义,就是上下文对象,存储了当前策略运行的时间点、所持股票、数量、持仓成本等等数据。
摘一张非常好的图片:
常用的context数据写法如下。
- 当前时间
context.current_dt
- 当前时间的“年-月-日”的字符串格式
context.current_dt.strftime("%Y-%m-%d")
- 前一个交易日
context.previous_date
- 当前可用资金
context.portfolio.available_cash
- 持仓价值
context.portfolio.positions_value
- 累计收益
context.portfolio.returns
- 当前持有股票
context.portfolio.positions.keys()
- 当前持有的某股票的开仓均价
context.portfolio.positions['xxxxxx.xxxx'].avg_cost
- 当前持有的某股票的可卖持仓量
context.portfolio.positions['xxxxxx.xxxx'].closeable_amount
止损
当亏损到一定程度,就需要卖出止损,减少进一步的损失。
可以先根据context
的数据得到持有股票的成本和现价,计算出该股票的盈亏程度,根据盈亏情况决定是否卖出股票,下面是个例子(跌出10%就止损):
# 导入函数库
from jqdata import *
def initialize(context):
run_daily(period,time='every_bar')
g.security = '000001.XSHE'
def period(context):
# 买入股票
order(g.security, 100)
#获得持仓成本
cost = context.portfolio.positions['000001.XSHE'].avg_cost
#获得股票现价
price = context.portfolio.positions['000001.XSHE'].price
ret = price / cost - 1
print('成本价:', cost)
print('现价',price)
if ret < - 0.05:
order_target('000001.XSHE', 0)
print('触发止损')
再做个试验,在2016年6月13日买入,在2016年12月12日卖出
# 导入函数库
from jqdata import *
def initialize(context):
run_daily(period,time='every_bar')
g.security = '000001.XSHE'
def period(context):
current_dt = context.current_dt.strftime("%Y%m%d")
if current_dt == '20160613':
order_target_value(g.security, 100000)
if current_dt == '20161212':
order_target_value(g.security, 0)
循环、多股票策略
用list数据类型存储多个股票
可以用之前所学的内容,写多个股票的策略,把g.security
弄成列表形式就OK了。
# 导入函数库
from jqdata import *
def initialize(context):
run_daily(period,time='every_bar')
g.security = '000001.XSHE'
def period(context):
order(g.security, 100)
因此,可以写一个简单的多股票策略:
# 导入函数库
from jqdata import *
def initialize(context):
run_daily(period,time='every_bar')
g.security = ['000001.XSHE','000002.XSHE']
def period(context):
for stk in g.security:
order(stk, 100)
加上止损:
# 导入函数库
from jqdata import *
def initialize(context):
run_daily(period,time='every_bar')
g.security = ['000001.XSHE','000002.XSHE']
def period(context):
for stk in g.security:
order(stk, 100)
cost=context.portfolio.positions[stk].avg_cost
price=context.portfolio.positions[stk].price
if cost == 0:
continue
ret=price/cost-1
if ret<-0.05:
order_target(stk,0)
print('触发止损')
学习交流:
微信:ElijahMingLiu