策略示例

在下面我们列举一些常用的算法范例,您可以通过QFF运行,方便您快速学习和掌握QFF框架。

双均线策略

金叉死叉策略其实就是双均线策略。策略思想是:当短期均线上穿长期均线时,形成金叉,此时买入股票。 当短期均线下穿长期均线时,形成死叉,此时卖出股票。研究表明,双均线系统虽然简单,但只要严格执行,也能长期盈利。

 1
 2# 双均线策略,当五日均线位于十日均线上方则买入,反之卖出。
 3from qff import *
 4
 5
 6# 初始化函数,设定要操作的股票、基准等等
 7def initialize(context):
 8    # 定义一个全局变量, 保存要操作的股票
 9    # 000002(股票:万科A)
10    g.security = '000002'
11    # 设定沪深300作为基准
12    set_benchmark('000300')
13    # 运行函数
14    run_daily(trade, 'every_bar')
15
16
17# 交易程序
18def trade(context, data):
19    security = g.security
20    # 设定均线窗口长度
21    n1 = 5
22    n2 = 10
23    # 获取股票的收盘价
24    close_data = attribute_history(security, n2+2, '1d', ['close'])
25    # 取得过去 ma_n1 天的平均价格
26    ma_n1 = close_data['close'][-n1:].mean()
27    # 取得过去 ma_n2 天的平均价格
28    ma_n2 = close_data['close'][-n2:].mean()
29    # 取得当前的现金
30    cash = context.portfolio.available_cash
31
32    # 如果当前有余额,并且n1日均线大于n2日均线
33    if ma_n1 > ma_n2:
34        # 用所有 cash 买入股票
35        order_value(security, cash)
36        # 记录这次买入
37        log.info("Buying %s" % security)
38
39    # 如果n1日均线小于n2日均线,并且目前有头寸
40    elif ma_n1 < ma_n2 and\
41            security in context.portfolio.positions.keys() and\
42            context.portfolio.positions[security].closeable_amount > 0:
43        # 全部卖出
44        order_target(security, 0)
45        # 记录这次卖出
46        log.info("Selling %s" % security)
47
48
49if __name__ == '__main__':
50    run_file(__file__)
51
52
53

MACD策略

以下是一个我们使用TALib编写的单股票MACD算法示例。

  1. macd 是长短均线的差值,signal是macd的均线,使用macd策略有几种不同的方法,我们这里采用macd线突破signal线的判断方法。

  2. talib是python的技术指标库,其中包含了很多150多种量化指标,所以talib是非常值得我们学习和使用的。 talib使用C语言实现,执行速度非常快,其安装方法也比较特殊,请自行搜索安装方法。

 1from qff import *
 2import talib as tl
 3import numpy as np
 4
 5
 6def initialize(context):
 7
 8    # 设置指数基准
 9    set_benchmark(security="000300")
10    # 定义一个全局变量, 保存要操作的股票
11    g.s1 = "000001"
12    # 定义运行函数,每日9点50分运行
13    run_daily(market_open, run_time='09:50')
14
15    log.info("initialize : 初始化运行")
16
17
18def market_open(context):
19    log.info("market_open函数,每天运行一次...")
20    # 读取历史数据,前100天的收盘价
21    close = history(100, '1d', 'close', g.s1).values
22    # 获取当前价格
23    current_price = get_current_data(g.s1).last_price
24    # 将当前价格与历史价格合并
25    close = np.append(close, current_price)
26
27    # 用Talib计算MACD取值,得到三个时间序列数组,
28    macd, signal, hist = tl.MACD(close, 12, 26, 9)
29
30    # 如果macd从上往下跌破macd_signal
31    if macd[-1] < signal[-1] and macd[-2] > signal[-2]:
32        # 进行清仓
33        if g.s1 in context.portfolio.positions.keys():
34            order_target(g.s1, 0)
35
36    # 如果短均线从下往上突破长均线,为入场信号
37    if macd[-1] > signal[-1] and macd[-2] < signal[-2]:
38        # 满仓买入
39        order_value(g.s1, context.portfolio.available_cash)
40
41
42if __name__ == '__main__':
43    run_file(__file__, start="2021-08-27", end="2022-03-25")
44
45

小市值策略

筛选出市值介于20-30亿的股票,选取其中市值最小的三只股票,每天开盘买入,持有五个交易日,然后调仓。 等权重买入,无单只股票仓位上限控制、无止盈止损。小市值策略曾经在15年期间有非常好的收益,未来有可能还能重现。

 1from qff import *
 2
 3
 4# 初始化函数,设定要操作的股票、基准等等
 5def initialize(context):
 6    # 设定沪深300作为基准
 7    set_benchmark('000300')
 8    # 持仓数量
 9    g.stock_num = 3
10    # 交易日计时器
11    g.days = 0
12    # 调仓频率
13    g.refresh_rate = 5
14
15
16def before_trading_start(context):
17    log.info("before_trading_start函数运行...")
18
19
20def check_stocks(context):
21    # 选出小市值股票
22
23    filter = {'date': context.previous_date, 'market_cap': {'$gt': 20, '$lt': 30}}
24    projection = {'market_cap': 1}
25    df = query_valuation(filter, projection)
26    df = df.sort_values('market_cap').reset_index()
27    buy_list = list(df['code'])[:g.stock_num*2]
28
29    # 过滤停牌股票
30    paused_code = get_paused_stock(buy_list, context.previous_date)
31    filter_paused = [x for x in buy_list if x not in paused_code]
32
33    return filter_paused[:g.stock_num]
34
35
36# 交易函数
37def handle_data(context, data):
38    if g.days % g.refresh_rate == 0:
39
40        # 获取持仓列表
41        sell_list = list(context.portfolio.positions.keys())
42        # 如果有持仓,则卖出
43        if len(sell_list) > 0:
44            for stock in sell_list:
45                order_target_value(stock, 0)
46
47        # 分配资金
48        if len(context.portfolio.positions) < g.stock_num:
49            Num = g.stock_num - len(context.portfolio.positions)
50            Cash = context.portfolio.available_cash / Num
51        else:
52            Cash = 0
53
54        # 选股
55        stock_list = check_stocks(context)
56
57        # 买入股票
58        for stock in stock_list:
59            if len(context.portfolio.positions.keys()) < g.stock_num:
60                order_value(stock, Cash)
61
62        # 天计数加一
63        g.days = 1
64    else:
65        g.days += 1
66
67
68if __name__ == '__main__':
69    run_file(__file__, start="2021-08-27", end="2022-03-25")
70

海龟策略

海龟交易系统是非常经典的一种策略,类似的成熟策略体系还有很多种,例如羊驼,鳄鱼等等。 关于海龟策略的原理介绍可以参照 这篇帖子

  1
  2# 海归策略
  3# 2012-01-01 到 2016-03-10, ¥1000000, 分钟
  4
  5# 海龟策略
  6
  7from qff import *
  8import numpy as np
  9
 10# ================================================================================
 11# 总体回测前
 12# ================================================================================
 13
 14
 15# 总体回测前要做的事情
 16def initialize(context):
 17    set_params()  # 1设置策参数
 18    set_variables()  # 2设置中间变量
 19    set_backtest()  # 3设置回测条件
 20
 21
 22# 1
 23# 设置策略参数
 24def set_params():
 25    g.security = '000001'
 26    # 系统1入市的trailing date
 27    g.short_in_date = 20
 28    # 系统2入市的trailing date
 29    g.long_in_date = 55
 30    # 系统1 exiting market trailing date
 31    g.short_out_date = 10
 32    # 系统2 exiting market trailing date
 33    g.long_out_date = 20
 34    # g.dollars_per_share是标的股票每波动一个最小单位,1手股票的总价格变化量。
 35    # 在国内最小变化量是0.01元,所以就是0.01×100=1
 36    g.dollars_per_share = 1
 37    # 可承受的最大损失率
 38    g.loss = 0.1
 39    # 若超过最大损失率,则调整率为:
 40    g.adjust = 0.8
 41    # 计算N值的天数
 42    g.number_days = 20
 43    # 最大允许单元
 44    g.unit_limit = 4
 45    # 系统1所配金额占总金额比例
 46    g.ratio = 0.8
 47
 48
 49# 2
 50# 设置中间变量
 51def set_variables():
 52    # 初始单元
 53    g.unit = 1000
 54    # A list storing info of N
 55    g.N = []
 56    # Record the number of days for this trading system
 57    g.days = 0
 58    # 系统1的突破价格
 59    g.break_price1 = 0
 60    # 系统2的突破价格
 61    g.break_price2 = 0
 62    # 系统1建的仓数
 63    g.sys1 = 0
 64    # 系统2建的仓数
 65    g.sys2 = 0
 66    # 系统1执行且系统2不执行
 67    g.system1 = True
 68
 69
 70# 3
 71# 设置回测条件
 72def set_backtest():
 73    # 作为判断策略好坏和一系列风险值计算的基准
 74    set_benchmark(g.security)
 75    log.set_level('info')  # 设置报错等级
 76
 77
 78'''
 79================================================================================
 80每天开盘前
 81================================================================================
 82'''
 83
 84
 85# 每天开盘前要做的事情
 86def before_trading_start(context):
 87    set_slip_fee(context)
 88
 89
 90# 4 根据不同的时间段设置滑点与手续费
 91def set_slip_fee(context):
 92    # 将滑点设置为0
 93    set_slippage(0)
 94    # 根据不同的时间段设置手续费
 95    dt = context.current_dt
 96
 97    if dt > '2013-01-01':
 98        set_order_cost(open_commission=0.0003, close_commission=0.0013, min_commission=5)
 99    elif dt > '2011-01-01':
100        set_order_cost(open_commission=0.001, close_commission=0.002, min_commission=5)
101    elif dt > '2009-01-01':
102        set_order_cost(open_commission=0.002, close_commission=0.003, min_commission=5)
103    else:
104        set_order_cost(open_commission=0.003, close_commission=0.004, min_commission=5)
105
106# ================================================================================
107# 每天交易时
108# ================================================================================
109
110
111# 按分钟回测
112def handle_data(context, data):
113    dt = context.current_dt  # 当前日期
114    if dt[:10] == '2020-04-20':
115        log.info(dt)
116    data = get_current_data(g.security)
117    current_price = data.last_price  # 当前价格N
118    if dt[11:15] == '09:30':
119        g.days += 1
120        calculate_N()  # 计算N的值
121    if g.days > g.number_days:
122        # 当前持有的股票和现金的总价值
123        value = context.portfolio.total_assets
124        # 可花费的现金
125        cash = context.portfolio.available_cash
126        if g.sys1 == 0 and g.sys2 == 0:
127            # 若损失率大于g.loss,则调整(减小)可持有现金和总价值
128            if value < (1 - g.loss) * context.portfolio.starting_cash:
129                cash *= g.adjust
130                value *= g.adjust
131
132        # 计算美元波动的价格
133        dollar_volatility = g.dollars_per_share * g.N[-1]
134        # 依本策略,计算买卖的单位
135        g.unit = value * 0.01 / dollar_volatility
136
137        # 系统1的操作
138        g.system1 = True
139        if g.sys1 == 0:
140            market_in(current_price, g.ratio * cash, g.short_in_date)
141        else:
142            stop_loss(current_price)
143            market_add(current_price, g.ratio * cash, g.short_in_date)
144            market_out(current_price, g.short_out_date)
145
146        # 系统2的操作
147        g.system1 = False
148        if g.sys2 == 0:
149            market_in(current_price, (1 - g.ratio) * cash, g.long_in_date)
150        else:
151            stop_loss(current_price)
152            market_add(current_price, (1 - g.ratio) * cash, g.long_in_date)
153            market_out(current_price, g.long_out_date)
154
155        # 5
156
157
158# 计算当前N的值
159# 输入:none
160# 输出:N的值的更新列表-list类型
161def calculate_N():
162    # 如果交易天数小于等于20天
163    if g.days <= g.number_days:
164        price = attribute_history(g.security, g.days+1, '1d', ['high', 'low', 'close'])
165        price['pre_close'] = price['close'].shift(1)
166        lst = []
167        for i in range(0, g.days):
168            h_l = price['high'][i] - price['low'][i]
169            h_c = price['high'][i] - price['pre_close'][i]
170            c_l = price['pre_close'][i] - price['low'][i]
171            # 计算 True Range
172            True_Range = max(h_l, h_c, c_l)
173            lst.append(True_Range)
174        # 计算前g.days(小于等于20)天的True_Range平均值,即当前N的值:
175        current_N = np.mean(np.array(lst))
176        g.N.append(current_N)
177
178    # 如果交易天数超过20天
179    else:
180        price = attribute_history(g.security, 2, '1d', ['high', 'low', 'close'])
181        price['pre_close'] = price['close'].shift(1)
182        h_l = price['high'][-1] - price['low'][-1]
183        h_c = price['high'][-1] - price['pre_close'][-1]
184        c_l = price['pre_close'][-1] - price['low'][-1]
185        # Calculate the True Range
186        True_Range = max(h_l, h_c, c_l)
187        # 计算前g.number_days(大于20)天的True_Range平均值,即当前N的值:
188        current_N = (True_Range + (g.number_days - 1) * g.N[-1]) / g.number_days
189        g.N.append(current_N)
190
191
192# 6
193# 入市:决定系统1、系统2是否应该入市,更新系统1和系统2的突破价格
194# 海龟将所有资金分为2部分:一部分资金按系统1执行,一部分资金按系统2执行
195# 输入:当前价格-float, 现金-float, 天数-int
196# 输出:none
197def market_in(current_price, cash, in_date):
198    # Get the price for the past "in_date" days
199    price = attribute_history(g.security, in_date, '1d', ['close'])
200    # Build position if current price is higher than highest in past
201    if current_price > max(price['close']):
202        # 计算可以买该股票的股数
203        num_of_shares = cash / current_price
204        if num_of_shares >= g.unit:
205            print("买入")
206            print(current_price)
207            print(max(price['close']))
208            if g.system1:
209                if g.sys1 < int(g.unit_limit * g.unit):
210                    order(g.security, int(g.unit))
211                    g.sys1 += int(g.unit)
212                    g.break_price1 = current_price
213            else:
214                if g.sys2 < int(g.unit_limit * g.unit):
215                    order(g.security, int(g.unit))
216                    g.sys2 += int(g.unit)
217                    g.break_price2 = current_price
218
219
220# 7
221# 加仓函数
222# 输入:当前价格-float, 现金-float, 天数-int
223# 输出:none
224def market_add(current_price, cash, in_date):
225    if g.system1:
226        break_price = g.break_price1
227    else:
228        break_price = g.break_price2
229    # 每上涨0.5N,加仓一个单元
230    if current_price >= break_price + 0.5 * g.N[-1]:
231        num_of_shares = cash / current_price
232        # 加仓
233        if num_of_shares >= g.unit:
234            print("加仓")
235            print(g.sys1)
236            print(g.sys2)
237            print(current_price)
238            print(break_price + 0.5 * g.N[-1])
239
240            if g.system1:
241                if g.sys1 < int(g.unit_limit * g.unit):
242                    order(g.security, int(g.unit))
243                    g.sys1 += int(g.unit)
244                    g.break_price1 = current_price
245            else:
246                if g.sys2 < int(g.unit_limit * g.unit):
247                    order(g.security, int(g.unit))
248                    g.sys2 += int(g.unit)
249                    g.break_price2 = current_price
250
251
252# 8
253# 离场函数
254# 输入:当前价格-float, 天数-int
255# 输出:none
256def market_out(current_price, out_date):
257    # Function for leaving the market
258    price = attribute_history(g.security, out_date, '1d', ['close'])
259    # 若当前价格低于前out_date天的收盘价的最小值, 则卖掉所有持仓
260    if current_price < min(price['close']):
261        print("离场")
262        print(current_price)
263        print(min(price['close']))
264        if g.system1:
265            if g.sys1 > 0:
266                order(g.security, -g.sys1)
267                g.sys1 = 0
268        else:
269            if g.sys2 > 0:
270                order(g.security, -g.sys2)
271                g.sys2 = 0
272
273
274# 9
275# 止损函数
276# 输入:当前价格-float
277# 输出:none
278def stop_loss(current_price):
279    # 损失大于2N,卖出股票
280    if g.system1:
281        break_price = g.break_price1
282    else:
283        break_price = g.break_price2
284    # If the price has decreased by 2N, then clear all position
285    if current_price < (break_price - 2 * g.N[-1]):
286        print("止损")
287        print(current_price)
288        print(break_price - 2 * g.N[-1])
289        if g.system1:
290            order(g.security, -g.sys1)
291            g.sys1 = 0
292        else:
293            order(g.security, -g.sys2)
294            g.sys2 = 0
295
296
297# ================================================================================
298# 每天收盘后
299# ================================================================================
300
301# 每日收盘后要做的事情(本策略中不需要)
302def after_trading_end(context):
303    return
304
305