JoinQuant-TWist 发布于2018-06-13
回复 678
浏览 180922
595
本文是[量化交易零基础入门教程](https://www.joinquant.com/post/13149)中的一篇,点击蓝字链接可查看该系列详情。
------
### 摘要
- context的含义
- context的结构
- context的读取方法
- 条件判断语句
- 止损的含义及其实现方法
- 自测与自学
---
- 通过前文的讲解,我们已经能理解最开始的那个简单的策略例子,如下:
```
def initialize(context):
run_daily(period,time='every_bar')
g.security = '000001.XSHE'
def period(context):
order(g.security, 100)
```
- 接下来,我们将在此基础上进行改进与举例,学习新内容。
### context的结构
- context是一个回测系统建立的Context类型的对象,其中存储了如当前策略运行的时间点、所持有的股票、数量、持仓成本等数据。
- 对象可以理解为特殊类型的变量,对象的结构往往比我们之前见过的list与dict更复杂,被定义好的对象是有名字的,比如context是一个变量,它的变量类型是一个Context类型的对象,就像dict包括key与value,Context类型的对象也包括很多属性,而且可以嵌套另一个种类型的对象,结构见下图。图中只包括了主要与常用的内容,详细介绍可以看API文档:[Context对象](https://www.joinquant.com/api#context)

- 关于对象的知识非常复杂繁多,目前我们只需学习如何取用context中的数据就好。
### context中的数据取用方法
- 获取对象类型变量内包含的数据方法是用英文句号隔开,而当包含的是另一个对象时,只需在应用英文句号隔开即可,例子如下:
# 打印可用资金
print(context.portfolio.available_cash)
# 打印运行频率
print(context.run_params.frequency)
# 打印当前单位时间的开始时间
print(context.current_dt)
# 执行后日志内容如下
# 1000000.0
# day
# 2016-06-01 09:30:00

- 当要获取的对象内的数据是另一种有结构的变量类型时,比如dict或list,正常按照该变量类型进一步取用数据即可。例如context.portfolio.positions是一个dict,我们就可以应用之前讲过的dict 的用法来使用它,例子如下,这次给出了完整代码。
# context.portfolio.positions的含义是仓位信息,所以为了让它有数据,需要在取之前买入并持有股票。
def initialize(context):
run_daily(period,time='every_bar')
g.security = '000001.XSHE'
def period(context):
order(g.security, 100)
# 打印所有键
print(context.portfolio.positions.keys())
# 打印所有值
print(context.portfolio.positions.values())
# 打印g.security的开仓均价
print(context.portfolio.positions[g.security].avg_cost)
# 执行后日志内容如下
# ['000001.XSHE']
# [UserPosition({'avg_cost': 8.539999999999997, 'security': '000001.XSHE', 'closeable_amount': 0, 'price': 8.53, 'total_amount': 100})]
# 8.54
- 常用的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的数据后,我们会考虑利用这些数据丰富策略的逻辑,但在此之前我们还要学习if条件判断语句,如下:
# 如果 条件1成立为 True 将执行代码块1
# 如果 条件1不成立为False,将判断条件2
# 如果 条件2成立为 True 将执行代码块2
# 如果 条件2还不成立为False,将执行代码块3
if 条件1:
代码块1
elif 条件2:
代码块2
else:
代码块3
# 注意
# elif 可以有多个连续写
# 且elif和else都可以省略
# 条件判断语句中可以嵌套条件判断语句
- 举几个例子:
# 打印a、b中最大值
if a>=b:
print(a)
else:
print(b)
# 判断a的正负性
if a>0:
print('正')
elif a<0:
print('负')
elif a==0:
print('零')
# 如果当前是2018-05-04,则下单买入100股平安银行
date=context.current_dt.strftime("%Y-%m-%d")
if date=='2018-05-04':
order('000001.XSHE',100)
# 判断a大小情况
if a>0:
if a<1:
print('a大于0且小于1')
else:
print('a大于等于1')
else:
print('a小于等于0')
- 条件判断语句比较简单,但还需说明的是条件的写法中用到的运算符:
# 写条件常用运算符:
# < 小于
# > 大于
# <= 小于等于
# >= 大于等于
# == 等于
# != 不等于
# and 与,即and两边条件同为真,则真
# or 或,即or两边条件任意一个为真,则真
# not 非,即not右侧条件为真,则假,not右侧条件为假,则真
# 以判断a是否为0的几个写法为例
# 写法1
if a!=0:
print('否')
else:
print('是')
# 写法2
if a>0 or a<0:
print('否')
else:
print('是')
# 写法2
if a>=0 and a=<0:
print('是')
else:
print('否')
# 写法3
if not a==0:
print('否')
else:
print('是')
### 止损
- 狭义的止损是指当亏损达到一定幅度后下单卖出该股票的操作,目的是减少进一步的亏损。广义则指在狭义的思路上衍生的复杂的减少亏损的方法。更多的情况下指狭义的止损。综合运用前文的讲过的内容我们已经可以实现当亏损达到一定幅度后下单卖出该股票的止损操作了,不妨先自己思考下再继续学习。
- 通过context的数据可以得到持有股票的成本和现价,从而可以算出该股票的盈亏情况,运用条件判断语句根据盈亏情况从而决定是否卖出股票,从而实现止损操作,代码如下:
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('成本价:%s' % cost)
print('现价:%s' % price)
print('收益率:%s' % ret)
# 如果收益率小于-0.01,即亏损达到1%则卖出股票,幅度可以自己调,一般10%
if ret<-0.01:
order_target('000001.XSHE',0)
print('触发止损')
- 设置回测时间为从2017-03-01到2017-03-31,初始资金为100000,频率为天。回测发现会在2017-03-20触发止损。
### 自测与自学
- 实践下文中的例子。
- 阅读API文档中Context对象,了解下其中的结构与内容。
- 写一个策略,内容为在20180301买入一个股票,在20180321卖出一个股票。股票可以自己定。
- 试着根据止损的例子实现止盈,即指当盈利达到一定幅度后下单卖出股票。
- 写一个自定义函数,功能是判断一个年份是否是闰年。闰年定义为:普通年(不能被100整除的年份)能被4整除的为闰年。(如2004年就是闰年,1999年不是闰年);世纪年(能被100整除的年份)能被400整除的是闰年。(如2000年是闰年,1900年不是闰年);【提示:利用取余运算(%)判断是否整除】
---
[查看下一篇](https://joinquant.com/post/13305)
评论
一直对context理解很模糊,现在清晰多了
2018-06-13
非常好的教程,有助于新手的理解,期待更新
2018-06-28
结构不懂,请教。
比如,本文中的代码:
![QQ浏览器截图_20180630075921_66600F2665EC41a0BEB02F5DD2EF7F78.jpg][1]
上述代码只是定义了函数,没有执行语句啊?为什么通过平台右侧的“运行回测”能运行?
[1]: https://image.joinquant.com/984d169a22551143e7e48d2697d03911
2018-06-30
@cd6003 这件事情你可以这样去接受它,虽然不一定对,你写的代码只是庞大代码的一部分,在你看不见的地方有很多系统代码,通过各种API与你写的代码联系着
2018-06-30
@cd6003你可以这样想,你定义了函数,执行语句默认由平台进行,运行回测会根据你的要求,回测开始时运行一次inititalize,每天(或定义的其他周期)运行一次period函数,这个是平台的主要功能
2018-07-05
运行止损代码后一直报错:
NameError: name 'context' is not defined
请问什么原因?
2018-07-07
谢谢作者 写的很好 希望能多写点 教大家!!
2018-07-11
@JoinQuant-TWist
自己摸索着写了几个乱七八糟的差不多是废品的代码。然后再回过头来看您的回复,非常清楚了。谢谢。。
2018-07-13
@lun
自己摸索着写了几个乱七八糟的差不多是废品的代码。然后再回过头来看您的回复,非常清楚了。谢谢。。
2018-07-13
请教
# 获得股票持仓成本
cost=context.portfolio.positions['000001.XSHE'].avg_cost
还有一个持仓成本,hold_cost, 跟avg_cost 适用的区别在哪里? 先谢
2018-07-31
好生气啊,教程跟不上语言的更新了,原本的关系符合在现在的程序被判定为语言错误.大于、小于、大于等于、小于等于、用>、<、>=、<=这样简单的符号就可以表示了。
2018-08-22
@默默桑 显示有bug,小于号没显示对,聚宽已经再改了。
2018-08-22
@JoinQuant-TWist 把教程里止损的程序复制下来然后运行试试看行不行,我稍微改了下才能运行。
2018-08-23
context.portfolio.positions.value属性是没有的,只有'avg_cost', 'security','closeable_amount', 'price','total_amount'五个key。
2018-09-02
为什么不能同时买入两支股票
g.security1=
g.security2=
order()
order()
回测不出来是什么原因呀~security难道不是随便定义的变量嘛~
2018-09-02
@陈住气 我的意思是positions是dict,有key,有value
2018-09-03
@泥石流短腿柯基 可能你写错了。。
2018-09-03
@JoinQuant-TWist 好像是的hhh谢谢解答~
2018-09-03
闰年平年~
```
def runnian1(year):
if year%4 == 0:
if year%100 == 0:
if year%400 == 0:
print "%s年是闰年" %(year)
else:
print "%s年是平年" %(year)
else:
print "%s年是闰年" %(year)
else:
print "%s年是平年" %(year)
```
2018-09-04