JoinQuant-TWist 发布于2017-03-17
回复 29
浏览 31495
81
上一篇文章:[十行代码带你量化交易入门](https://www.joinquant.com/post/3616)
下一篇文章:[【新手入门教程】简单小市值轮动策略](https://www.joinquant.com/post/6596)
>学习内容:
>- 学会使用for语句和list数据类型
>- 学会写多股票策略
### 1 确定策略内容
前文中,我们写的单股票的均线策略的策略内容是这样的:
>若昨日收盘价高出过去20日平均价今天开盘买入股票
>若昨日收盘价低于过去20日平均价今天开盘卖出股票
现在,我们想利用计算机强大的数据处理能力,**同时监视市场上多只股票**,如果满足条件就进行相应交易。简言之,**对多个股票分别实行原本的单股票策略**,策略内容应该是这样的:
>若**多只股票某只**昨日收盘价高出过去20日平均价今天开盘买入**该**股票
>若**多只股票某只**昨日收盘价低于过去20日平均价今天开盘卖出**该**股票
那怎么用代码说给计算机听呢?老办法,先想清楚人要做的话要怎么做,再一点点翻译成代码。
1. 多股票究竟是都是哪些?即**要确定股票的范围**。
1. **每天**看看**每一只股票**昨日收盘价是否高出过去20日平均价,是的话开盘就买入,不是开盘就卖出。**每天都这么做,循环下去**。
接下来就按之前讲得基本框架的套路出牌就好了,即**初始化**加**周期循环**。
### 2 用list数据类型初始化股票列表
我们要确定股票的选择范围,此处举例就简单点只选两个了,比如 兔宝宝(002043) 和 好想你(002582)。代码如下:
``` python
def initialize(context):
g.security = ['002043.XSHE','002582.XSHE']# 存入兔宝宝、好想你 的股票代码
```
对比下,单股票策略中初始化代码是这样的:
``` python
def initialize(context):
g.security = '002043.XSHE'# 存入兔宝宝 的股票代码
```
可以看到,多个股票代码之间用**逗号隔开**了,并且两侧被**中括号**包在了一起。这种被中括号包在一起的数据的类型叫做**list**。形如:
```
[x1,x2,x3,...,xn]
```
当多个股票代码被包在一起成为一个list后就是一个整体,我们就可以给他们一起命名(例如此处命名是g.security),就好像我们把多个股票代码包在一个盒子里,在盒子外面写上名字,如此以后你想让计算机把那些股票代码拿到哪里去计算,或是怎么样的时候,只要跟她讲g.security,她就知道是那个盒子了,而不必一一地把每个股票名字再交代一遍。
> 答疑与延伸:
>- **关于list的详细介绍?**:请看 [Python入门(2)- 数据类型之列表](https://www.joinquant.com/post/1970)
初始化完成,开始周期循环的部分。
### 3 for语句
之前我们已经发现了,所谓的多股票策略,就是对多个股票逐个地实行单股票策略,所以对于所选股票只有两个的时候,只要把原本单股票策略对每个股票再写一遍就好了,比如这样:
```
def initialize(context):
g.security1 = '002043.XSHE'
g.security2 = '002582.XSHE'
def handle_data(context, data):
last_price = data[g.security1].close
average_price = data[g.security1].mavg(20, 'close')
cash = context.portfolio.cash
if last_price > average_price:
order_value(g.security1, cash)
elif last_price < average_price:
order_target(g.security1, 0)
last_price = data[g.security2].close
average_price = data[g.security2].mavg(20, 'close')
cash = context.portfolio.cash
if last_price > average_price:
order_value(g.security2, cash)
elif last_price < average_price:
order_target(g.security2, 0)
```
(不是很重要,也避免代码太长,注释就省了)
基本上就是原来的单股票代码写两遍,两只股票写两遍,上千只股票的话还不写死。。。
所以我们要用到**for**,来告诉计算机,对多只股票都**逐个进行相同的一系列的操作**。
for的用法如下:
```
# 把x中的数据依次取出暂时放入i中
for i in x:
描述操作的代码
# 其中x的数据类型需要是list。
```
这段代码的含义可以理解成面试:
(x像一队的等待的面试者,i就是面试的房间,操作就是面试的过程)
取出x中的第一个数据放到i中,进行操作。
(排在最前面的人进去面试然后出来)
然后从x中再取出第二个数据覆盖掉i中原本的数据,进行操作。
(第二个人进去面试然后出来)
如此继续,直到x中最后一个数据取到并进行操作。
(直到最后一个人面试完)
> 答疑与延伸:
>- **从x中取出的次序?**:x的数据类型是list,list类型里面的数据是有次序的,所以从x中取出的次序就是x里自带的次序。如for i in [2,7,3]的取出次序就是,如所见到的从左到右2,7,3的次序。([2,7,3]是个list)
>- **关于for的更多内容?**:请看文章 [Python入门(5)- 条件与循环:if、while、for](https://www.joinquant.com/post/1973)中关于for的部分。
for应用在我们的多股票策略中后,我们的策略就是这样的:
```
def initialize(context):
# 存入兔宝宝、好想你 的股票代码
g.security = ['002043.XSHE','002582.XSHE']
def handle_data(context, data):
# 把g.security中的股票代码依次取出,逐个进行单股票均线策略
for i in g.security:
# 获取取得最近日收盘价,命名为last_price
last_price = data[i].close
# 获取近二十日股票收盘价的平均价,命名为average_price
average_price = data[i].mavg(20, 'close')
# 获取当前现金数量,命名为cash
cash = context.portfolio.cash
# 如果昨日收盘价高出二十日平均价, 则买入,否则卖出。
if last_price > average_price:
order_value(i, cash)# 用per_cash的资金量买入股票i
elif last_price < average_price:
order_target(i, 0)# 将股票仓位调整到0,即全卖出
```
至此,已经是一个完整的可运行的策略了。
**但是**,我们应用原本单股票策略的买入卖出方法到多股票后,逻辑上会出现这样一种情况,只要多只股票中一只股票满足买入条件了,就用**所有资金**买入了,从而没有资金买别的股票了,即便余下的股票也有满足条件的,即策略一直最多持有一只股票。
当然,这种买入卖出逻辑并没什么错,但一般来说,多股票策略相比单股票的策略的优势,除了可以**更大范围内的寻找机会**外,能同时持有多只股票能帮助我们**分散风险**。
故,我们有必要继续研究下每次花多少钱去买股票,使策略可同时持有多只股票。
### 4 各个股票买多少?
每次交易信号发生,不全额买卖,该买卖多少额度呢?这是个复杂的问题,每个人对于每个策略都可能有不同的看法,并无定法。此处只做简单的处理,即将资金按股票数量分配预留,哪只股票发出信号,就将该股票的那份资金全额交易。
详细表述与代码如下:
1. 将资金平分成两份("兔宝宝"一份,"好想你"一份),每份资金量为per_cash.
``` python
# cash除以g.security中的股票数,得到per_cash
per_cash = cash/len(g.security)
```
>- 答疑与延伸:
**len(g.security)什么意思?**:len()是用来求list长度,即list中包含多少个东西。本例中len(g.security)就是求g.security中的股票数,结果为2.
2. 如果昨日收盘价高出二十日平均价, 则用per_cash的资金量买入的该股票;
否则卖出全部该股票。
翻译成代码:
``` python
if last_price > average_price:
order_value(i, per_cash)# 用per_cash的资金量买入股票i
elif last_price < average_price:
order_target(i, 0)# 将股票i持有量调整到0,即全卖出
```
### 5 策略代码写完,进行回测
把买入卖出的代码写好,策略就写完了,如下
``` python
def initialize(context):
# 存入兔宝宝、好想你 的股票代码
g.security = ['002043.XSHE','002582.XSHE']
def handle_data(context, data):
# 把g.security中的股票代码依次取出,逐个进行单股票均线策略
for i in g.security:
# 获取取得最近日收盘价,命名为last_price
last_price = data[i].close
# 获取近二十日股票收盘价的平均价,命名为average_price
average_price = data[i].mavg(20, 'close')
# 获取当前现金数量,命名为cash
cash = context.portfolio.cash
# cash除以g.security中的股票数,得到per_cash
per_cash = cash/len(g.security)
# 如果昨日收盘价高出二十日平均价, 则买入,否则卖出。
if last_price > average_price:
order_value(i, per_cash)# 用资金买入股票
elif last_price < average_price:
order_target(i, 0)# 将股票仓位调整到0,即全卖出
```
现在,点击运行回测,如果你代码没有问题,就会顺利的进行回测,回测结果见下文
至此,你就完成了一个简单策略的回测了。
>- 答疑与延伸:
>- **代码编辑区上方的编译运行按钮是什么?**:编译是简化版的回测,相比回测少做了很多统计工作,比如每日持仓,交易详情等,所以运行会快很多。所以策略前期需要反复调试的时候,点编译运行,而策略完善后需要生成详细的报告,就点运行回测。两个都试试就知道了。
### 自测与自学
1. 能否理解并学会使用list数据类型。
2. 能否理解并学会使用for语句。
3. 试着调整多股票的数量,比如将选股范围调整为沪深300指数的成分股。(提示:使用获取指数成份股的API)
4. 试着调整买入卖出条件,比如将买买卖条件变为:如果昨日收盘价高出二十日平均价5%, 则买入;如果昨日收盘价低出二十日平均价5%,则卖出。(提示:乘法的代码是" \* ",a的5%用代码表示为:a \* 0.05)
[1]: https://image.joinquant.com/59c77164844e5769bb76ec50d00423fb
评论
多股策略中, 用当前所有资金买入股票应该为per_cash吧?
# 如果昨日收盘价高出二十日平均价, 则买入,否则卖出。
if last_price > average_price:
order_value(i, per_cash)# 用当前所有资金买入股票
2017-05-21
最后四行写错了。。
另外假设总共股票池是5个。。。如果我希望符合买入条件的股票,可以把资金买完,比如说其中两个符合买入条件,那么我希望这两个,每个买入50%,该怎么设置呢?我试了好几次都没成功。。
另外,如果如果希望能够设置调仓周期,不像现在这样每天都在调仓该如何设置呢?
谢谢各位大神。。
2017-06-03
```
def handle_data(context, data):
h={}
for i in g.security:
last_price=data[i].close
average_price=data[i].mavg(20,'close')
if last_price>average_price:
h[i]=True
elif last_price<average_price:
h[i]=False
snum=h.values().count(True)
cash = context.portfolio.cash
if snum!=0:
per_amount=cash/snum
else:
per_amount=0
for i in h.items():
if i[1]==True:
order_value(i[0],per_amount)
else:
order_value(i[0],0)
```
@强大的河蟹
2017-06-15
@8072Click 谢谢啊,我来研究一下。。
2017-06-19
楼主,按你的代码,是将现有资金分成两份,每只股票各买一份。但如果在这之前已经买了一只股票了。只剩下一半资金了。程序还是会把剩下的资金分成两份,用来买另外一只股票。这个时候另一只股票的仓位就只有近四分之一了。不知道我理解得对不对?
2017-06-23
为什么同样的设置回测出来的结果不一样呢?
2017-07-10
作者说了半天,小白还是不懂,真崩溃了,能不能用那个向导式的策略填写来描述20日均线的操作方法啊?!!!!!!
2017-07-14
@狮大拿 这个问题我也注意到了,按照文字的意思正确的代码应该是这样的吧
```
def initialize(context):
g.security = ['002043.XSHE','002582.XSHE']
def handle_data(context,data):
cash = context.portfolio.cash
per_cash = cash/len(g.security)
for i in g.security:
last_price = data[i].close
average_price = data[i].mavg(20,'close')
if last_price > average_price:
order_value(i,per_cash)
elif last_price < average_price:
order_target(i,0)
```
2017-07-15
@Aaron1992 话说楼主这个策略可能是只想起到策略示范作用,并没有考虑那么多逻辑问题。按说这个策略本身就有问题。因为收盘价高于20日均价极可能是一个连续的过程,这样程序就会在一波上涨趋势中,每天都买进这只股票。只不过买进的资金量逐渐减小。
2017-07-18
最后四行确实写错了。另外,如果单位时间内的价格持续在20日均线上,则会造成所有资金all in的局面,没有做风险控制。不过作为模板学习,还是很不错的,感谢楼主分享!!!
2017-08-22
@Shaw918 @狮大拿 是的,会出现资金分配很不平均的情况。这是为了让策略简单易学,突出核心重点,所做出的让步。如果处理这个问题可能会让代码变得不太友好。
2017-08-23
@Aaron1992 但是i的值怎么确定 ? 直接赋值2?
2017-08-31
那我要怎么设置一只股票买20%,另一只买80%?
ps.我是极限小白。。。
2017-10-01
@李知恩的男朋友 分成10份,一个两份,一个8份。推荐你看下python基础,事半功倍的。
2017-10-17
@JoinQuant-TWist 谢谢!我现在在学!
2017-11-05
@李知恩的男朋友 cash*0.2这样就行了
2017-11-28