介绍
我一直觉得程序员都应该试试量化投资,因为在投资里最忌讳的就是情绪波动,而程序员们都相对理性一些,更加愿意相信规则与数据,而同时程序员们又有能力去实现自动交易,所以我这篇文章主要的目标是面向程序员,给大家介绍一些投资、交易里面比较初级的东西以及背后的原理,关于代码部分会少一些特别细节的说明。所以这篇文章需要的大概背景是会 Python 就行。
网格交易是一个很好的入门的策略。在交易里,我们会有像哲学三大问题一样的疑问:
- 买什么
- 什么时候买,什么时候卖
- 买多少,卖多少
而网格交易主要针对的就是后两个问题的,尝试性地给出一种答案
遇到的问题
废话不多说,先上一个例子。
这张图是黄金在 19年10月10日 到 23日左右1小时的一个走势图。之前是更高的位置,然后经历一次 0.9% 的下跌到了A的位置,然后又在B的位置跌了一个 0.77%,已知在这段时间里黄金都是一个振荡的趋势。这时候我们经常就会听见一些分析师大概会分享这么个说法(或者类似的说法):
-
在振荡盘中,A处是上方跌下来的,并且保持了一段时间,所以这个是一个阻力位,而B这个位置是一个支撑位,所以我们在这个振荡区间里,就可以做高抛低吸。
-
简单来说就是如果我们还有持仓,那我们在 C 这个位置就应该抛掉,如果我们错过了C,这个市场还给了我们 D,E两次机会,如果都没抛掉,那在F的时候就跌下来了。
-
而如果我们这时到了 F 这个位置后呢,因为有 B 这个支撑点,所以可以开始买入。
-
然后一直到 H 处,又来到了上方阻力位,然后我们就可以卖出
-
…
这个说法好像听起来不错,理论都是好的,但是实际情况是什么样的呢?假设对于处于振荡区间这点是确定的条件了,我们实际上会遇到的情况将会是这样的:[图2]
哪条才是阻力线? 又或者: [图3]
哪条才是支撑线?
如果图2里我们说用之前跌的最低点作为阻力的话,那在图2里最后一根大涨起来之前的一个绿十字星要卖么?还是冒险等一等真的上穿阻力了才卖?
同理如果图3里我们也用之前收盘最低点作为支撑线的话,那最后一根大跌的红线结束后我们要卖么?这时候可是还没跌破支撑哦。
所以这其实就是我们遇到的实际情况,看上去好像我们有一个很详细的操作策略,但到了实盘的时候,我们其实并没有很明确的标准,而最初第一张图里分析师的那些说法虽然没错,但却犯了一个很经典的量化里面的错误就是 “未来函数”,简单的说就是在画线的时候,我是根据未来一段时间的情况画的线,然后再来指导我们当下每个时间点的分析,相当于预知未来了。(这让我想起了小时候老师讲卷子,拿着答案给我们讲过程,讲的头头是道最后发现拿错了一份答案)
网格交易给出的答案
而网格交易的方案是这样的(先看个大概,再下一节有针对每一步的例子):
-
第一步,先估计出未来看盘时间的震荡范围,高值和低值都不用特别找某个特殊值,确定一个就可以。这个高值意味着如果价格超过这点时,我们仓位就会是空仓,而低值意味着如果价格掉到这个值以下,我们就是满仓。到现在为止和刚才的支撑止损线都还是一致的。
-
第二步,选定网格数量,这里我们为了方便计算,就选定为10条线分为11个格子,这样就可以每10%一档从空仓0% 到满仓 100% 全都覆盖了。然后我们为了避免在一条线附近快速震荡,我们在最下一条线之下再画一条线(本质上来说相当于11条线分为12个格子,但这里这么说主要为了更方便解释和操作)
-
第三步,确定初始仓位,我们就可以直接按照第一条 K 线的收盘价落在哪个区间来确定仓位
前面的准备工作做好之后,之后的调仓逻辑如下:
-
记穿越的是第n条线,线从上往下数字增大
-
如果价格上穿第 n-1 条线,卖 10%
-
如果价格下穿第 n+1 条线,买 10%
-
回到调仓逻辑第一步
它和我们普通的交易区别在于什么呢,表面上来看,它把原来的一次交易分成了很多次,实质里有如下一些好处:
-
越密的网格,交易机会越多(但要注意交易手续费),在前面对比的策略中,不到预计的点不交易,如果振荡幅度不够,就会错过很多次交易机会,而在网格交易里,只要振荡到了一个小的格子,就有一次的交易机会。
-
每一个格子其实就相当于交易的 “信心度”,价格涨跌信心不足的时候轻仓做,价格涨跌到信心很足的时候重仓做。在交易里,所谓的信心度也就是经常提到的“概率”:数学期望(预期收益)和方差(风险)之间有一个好的比率的时候,就要重仓,如果没有好的比率的时候,就要轻仓甚至空仓
手动操作网格交易
在这里我们把上面的流程举例说一下:
预备阶段
- 第一步,建立振荡范围,我们计划做黄金19年10月份的交易,下图是黄金19年9月份的走势,可以看出是一个振荡市场,最高在1535左右,最低在1460左右,我们可以随意一些,把高值定为1540,把低值定为 1450:
- 第二步,分为11个格子,以 10%间隔,分别覆盖从 0% 到 100%。下面多画一条绿色的辅助线,
- 第三步,我们来到10月份的图形,黑圈是10月份最初的价格,我们可以看到它是落在70%仓位区间,也就是第8条线之上,所以我们要建立 70% 的仓位(多头,这例子不涉及到做空,下同):
交易阶段
-
我们刚建仓,所以在这里 n = 8
-
第 n-1 条线也就是第7条线,如果收盘价到了这里,我们就以收盘价卖 10%
-
第 n+1 条线也就是第9条线,如果收盘价到了这里,里我们以收盘价买 10%
-
接下来的一个事件是一条长绿阳线上穿第7条线,所以我们卖10%(仓位:60%)此时 n = 7,重新计算买卖的价格线:第6条线卖10%,第8条线买10%
-
长绿阳线上穿第7条线之后有一小段回落到第7条线以下,但并没有碰到第6条线,所以我们不做操作,否则如果价格在第7条线上下抖动,那么我们来回买卖10%的仓位就会付出大量的手续费,这也是最下面的绿线存在的原因,我们可以模拟数一数最后是下穿绿线才会真正满仓
-
随后一直进行下去,如果掉一格就买,涨一格就卖
盈利来源
这样交易怎么赚到的钱呢?我们看下面的简图:
本质上来说,我们在A点建的10%仓是在H点卖掉的,B点建的仓是在G点卖掉的,所以每次我们都有”一格“的利润,所以如果价格在我们框定的范围振荡,那么我们就不断地在以低价买入,高价卖出。
风险点
有收益就会有风险,不讲风险的投资都是耍流氓,在网格交易里主要会遇到的风险有如下几点:
-
过高的交易手续费,我们每一格赚到的钱一定要能 cover 住相应手续费
-
价格滑点,我们如果买卖量比较大的话,很有可能没办法按我们希望的价格买到或者卖出
-
市场不再振荡而是进入趋势,如果市场大跌你就会发现网格交易是满仓跌,如果市场大涨你会发现网格交易是空仓
所以我们在实盘之前要模拟一下这些问题并做一些相应的策略调优,而处理大量数据做模拟盘对于我们程序员来说最好的方式就是:量化交易
BackTrader 介绍
BackTrader 是一个基础的量化框架,当然现在市面上也有很多的宽客平台可以使用,但如果我们做完测试想用真钱做交易的时候,就会遇到一些问题。有些平台可能不支持我们自己账号所在的 broker(比方说数字货币),或者做高频交易的时候没办法选到距离 exchange 最近的服务器来提升速度等,所以我们基于一个开源平台来搭建自己的回测框架和模型就是比较可控的,我们市面上常见的回测框架有 backtrader、zipline 等,在选择平台的时候我综合考虑了代码结构、可扩展性、易用性、性能、文档清晰全面、社区等各因素,最终我选择了 backtrader。
文档地址在这里:backtrader 文档
github项目:github
由于作者是一个严谨的德国人,服务器也放在了德国,所以文档访问起来还是有点慢,有梯子的上梯子,没梯子的我也会在之后的文章里找机会慢慢给大家介绍它的特性。
建立一个策略
安装 backtrader 库:pip install backtrader
建立一个空的策略框架:
import backtrader as btclass GridStrategy(bt.Strategy):def __init__(self):passdef next(self):pass
关于 Python 语言的部分我不过多介绍,如果对 Python 不熟悉的可以先去看看 Python 的文档,关于 Python 进阶一些的东西可以看一下我写的 Python 相关的文章。
在这里我们需要继承一下 bt.Strategy,然后定义自己的构造函数和 next 函数,我们可以在构造函数里初始化一些东西,比方说画格子什么的,但还不能做交易,要做交易的话我们需要在 next 函数里,而 next 函数会被 backtrader 调用,每产生一条新的 K 线就会调用一次。
思路
在上面的网格交易里我们是先确定最大值和最小值,在这里我编程的时候先算出中间值,然后从中间值往上下,价格每改变 0.5% 画一个格子,这样的好处是为了应对过高的手续费问题,如果我们的格子太小,就 cover 不住手续费,如果我们以格子宽度做为参数那我们可以通过调节宽度来一定程度上应对手续费问题。但整体逻辑是一样的,这就好比 距离(高低点差) = 速度(格子宽度) x 时间(格子数量),我们可以任选两个变量从而确定第三个值。
建立一个Indicator
首先我们要根据数据建立一个高点一个低点,然后求中间值,这里我直接用 backtrader 里提供的 indicator 来建立:
def __init__(self):# 1440个单位K线图高点的最高点self.highest = bt.indicators.Highest(self.data.high, period=1440, subplot=False)# 1440个单位K线图低点的最低点self.lowest = bt.indicators.Lowest(self.data.low, period=1440, subplot=False)mid = (self.highest + self.lowest)/2
建立网格
由于 Python 的 range 不支持小数,这里我们用 numpy 库里的 arange
#百分比区间
perc_levels = [x for x in np.arange(1 + 0.005 * 5,#这里多1/2,是因为 arange函数是左闭右开区间。1 - 0.005 * 5 - 0.005/2,-0.005)]
#价格区间
self.price_levels = [mid * x for x in perc_levels]
记录上一次穿越的网格
self.last_price_index = None
交易逻辑
开仓部分
if self.last_price_index == None:for i in range(len(self.price_levels)):if self.data.close > self.price_levels[i]:self.last_price_index = iself.order_target_percent(target=i/(len(self.price_levels) - 1))return
计算 n-1 和 n+1 条线
upper = None #n-1
lower = None #n+1
if self.last_price_index > 0:upper = self.price_levels[self.last_price_index - 1]
if self.last_price_index < len(self.price_levels) - 1:lower = self.price_levels[self.last_price_index + 1]if upper != None and self.data.close > upper:self.last_price_index = self.last_price_index - 1self.order_target_percent(target=self.last_price_index/(len(self.price_levels) - 1))
if lower != None and self.data.close < lower:self.last_price_index = self.last_price_index + 1self.order_target_percent(target=self.last_price_index/(len(self.price_levels) - 1))
其中 order_target_percent
函数是直接调整到目标仓位,相当好用,不用考虑买多少卖多少问题
这里有一个问题是有可能一条K线会穿过多个网格,所以我们要处理一下:
#调仓信号
signal = False
while True:upper = Nonelower = Noneif self.last_price_index > 0:upper = self.price_levels[self.last_price_index - 1]if self.last_price_index < len(self.price_levels) - 1:lower = self.price_levels[self.last_price_index + 1]# 还不是最轻仓,继续涨,就再卖一档if upper != None and self.data.close > upper:self.last_price_index = self.last_price_index - 1signal = Truecontinue# 还不是最重仓,继续跌,再买一档if lower != None and self.data.close < lower:self.last_price_index = self.last_price_index + 1signal = Truecontinuebreak
if signal:self.order_target_percent(target=self.last_price_index/(len(self.price_levels) - 1))
剩下的就是一些跑引擎的逻辑,我放在源码里了
回测数据
我这里使用了一段 Binance 的数据,因为数字货币这边提供给我们的数据更细一些,可以让我们有更多的可能性去做回测,然后在没有任何调优的情况下,策略收益:16.5%,持仓不动的收益:15%
数据时间长度是1个月,因为这里是分钟级别的数据,所以我把最高值最低值都按1440分钟的最高最低算,也就是过去一天里的最高最低值作为参考(实际会算成中间值)
小结
网格交易法是一个交易策略,但它更是一个仓位管理策略,在这篇文里没有特别涉及到仓位管理主要还是希望能先把网格交易先说明白,能让程序员们了解一下交易的基本东西,这个策略也没做什么优化,并没有加入大跌止损的逻辑,也没有考虑手续费问题,都是为了让内容更集中在网格交易本身。
P.S. 写代码比画图要快多了,作图不易,觉得好还请点个赞,留个言啥的
源码在这里
我的博客