Skip to content

Latest commit

 

History

History
381 lines (256 loc) · 13 KB

strategysample.md

File metadata and controls

381 lines (256 loc) · 13 KB

两个量化交易策略样例

作者:brz, lli, PKUJohnson

quantOS的策略系统JAQS系统有两大神器——阿尔法选股框架和事件驱动框架。下面我们分别以"板块内股票动量轮动策略"和"期货双均线穿越策略"为例详细介绍。

特别说明

  • 这只是一个演示策略,不代表能真正赚钱,使用此策略投资,风险自负。
  • 历史研究不代表未来,仅供参考。

安装JAQS

本例需要要用到JAQS系统,请根据这里的安装指南,安装好JAQS系统。

阿尔法选股框架

在这里,我们展示一个“板块内股票动量轮动策略”是如何通过JAQS阿尔法选股框架实现的。可在这里直接下载策略源代码。

一、策略描述

策略的主要思想如下:

1、选定某一个板块(中证白酒399997.SZ

2、每天将板块内所有股票按过去20天的收益率从大到小排序,挑选出排名靠前的10%的股票。

3、使用等市值权重构建投资组合,之后与策略持仓进行对比,进行调仓。

4、给定初始资金金额1亿,期间不增减资金。

这是一个典型的动量轮动策略,选个表现更好的股票,剔除表现一般的股票。

二、策略实现和回测

在jaqs平台中,这是一个典型的Alpha选股策略,即通过一定的条件,选出理想的股票,然后按照某种方式进行资产配置。具体做法如下:

步骤1、选出中证白酒指数成份股,计算股票20天的收益率,并进行排序。

在Alpha选股策略中,数据的操作是通过DataView对象实现的,具体代码如下:

from jaqs.data import RemoteDataService
from jaqs.data import DataView

data_config= {
  "remote.data.address": "tcp://data.tushare.org:8910",
  "remote.data.username": "手机号",
  "remote.data.password": "token"
}

BENCHMARK = '399997.SZ'

ds = RemoteDataService()
ds.init_from_config(data_config)
dv = DataView()

props = {'start_date': 20170101, 'end_date': 20171001, 'universe': BENCHMARK,
    'fields': 'close,volume,sw1',
    'freq': 1}

dv.init_from_config(props, ds)
dv.prepare_data()

dv.add_formula('ret', 'Return(close_adj, 20)', is_quarterly=False)
dv.add_formula('rank_ret', 'Rank(ret)', is_quarterly=False)

dv.save_dataview(folder_path=dataview_dir_path)

其中RemoteDataService是一个从远程服务器通过DataApi读取数据的对象,DataView根据props里面的配置信息,通过RemoteDataService读取相应的基础数据,如本例中的closevolumesw1

这里我们使用了quantOS官方数据源data.tushare.org.

DataView对象中通过add_formula方法,可以通过公式定义衍生数据,比如本例中的Return(close_adj, 20)表示通过复权的收盘价计算的20天收益率,并被命名为ret

add_formula还可以基于衍生数据定义新的衍生数据,比如本例中的Rank(ret)表示基于ret数据的排序,并被命名为rank_ret(在中证白酒指数成份股中的排序,从小到大,返回值范围是(0,1])。

最后通过save_dataview把准备的数据保存到本地文件,供后续回测使用。

步骤2、选出收益率最高的前10%的股票。

在Alpha选股策略中,是通过定义股票筛选函数来实现的,代码如下:

def my_selector(context, user_options=None):
    rank_ret = context.snapshot['rank_ret']

    return rank_ret >= 0.9

这里的context是策略的上下文环境,从中可以提取到之前定义的数据。

步骤3、策略回测

代码如下:

dv = DataView()
dv.load_dataview(folder_path=dataview_dir_path)

data_config = {
  "remote.data.address": "tcp://data.tushare.org:8910",
  "remote.data.username": "手机号",
  "remote.data.password": "token"
}
props = {
    "benchmark": BENCHMARK,
    "universe": ','.join(dv.symbol),

    "start_date": dv.start_date,
    "end_date": dv.end_date,

    "period": "day",
    "days_delay": 0,

    "init_balance": 1e8,
    "position_ratio": 1.0,
}
props.update(data_config)
#props.update(trade_config)

trade_api = AlphaTradeApi()

stock_selector = model.StockSelector()
stock_selector.add_filter(name='rank_ret_top10', func=my_selector)

strategy = AlphaStrategy(stock_selector=stock_selector, pc_method='equal_weight')
pm = PortfolioManager()

bt = AlphaBacktestInstance()

context = model.Context(dataview=dv, instance=bt, strategy=strategy, trade_api=trade_api, pm=pm)
stock_selector.register_context(context)

bt.init_from_config(props)
bt.run_alpha()

bt.save_results(folder_path=backtest_result_dir_path)

回测过程包括:

  1. 从本地数据中加载DataView对象,设置回测参数。
  2. 指定一个交易接口对象AlphaTradeApi,用于回测过程发单。
  3. 将之前定义的规则加入股票筛选器stock_selector,并命名为rank_ret_top10。
  4. 构造一个AlphaStrategy策略对象,其中需要指定stock_selector和组合构建方法,这里使用的是equal_weight(等市值权重)
  5. 构造一个仓位管理对象PortfolioManager
  6. 构造一个回测实例对象AlphaBacktestInstance
  7. 构造一个上下文对象Context,需要给定dataview,回测实例对象,策略对象,交易接口,仓位管理对象
  8. 回测实例对象AlphaBacktestInstance初始化参数,并执行run_alpha进行回测,保存回测结果。

步骤4、回测结果分析

代码如下:

import jaqs.trade.analyze as ana

ta = ana.AlphaAnalyzer()
dv = DataView()
dv.load_dataview(folder_path=dataview_dir_path)

ta.initialize(dataview=dv, file_folder=backtest_result_dir_path)

ta.do_analyze(result_dir=backtest_result_dir_path, selected_sec=list(ta.universe)[:3], brinson_group='sw1')

通过AlphaAnalyzer对象,可对回测结果进行分析,查看策略回测情况。

三、仿真交易

我们使用TradeSim平台进行仿真交易,其代码如下:

四、策略实盘交易

我们使用vn.py进行实盘交易.

事件驱动框架

这里我将以股指期货的双均线穿越策略为例,展示如何使用JAQS的事件驱动策略框架. 完整的策略代码,可以在这里下载.

一、策略描述

双均线穿越策略总共分几步?答:两步。

  1. 首先,确定交易标的。根据历史价格数据计算快速均线和慢速均线。在实盘交易中,数据周期为1 tick(500毫秒),在回测中,周期为1分钟。
  2. 当快线上穿慢线时,进行做多操作,或开多或平空;反之进行做空操作,或开空或平多。 我们可将上穿和下穿视为两个“事件”,利用我们JAQS的事件驱动策略框架实现起来易如反掌。

二、策略实现

古人云:工欲善其事,必先利其器。首先让我带大家熟悉一下事件驱动策略框架的组成部分。该框架由类DoubleMaStrategy()实现,完成了“数据采集-信号生成-发单”的整个交易逻辑。

class DoubleMaStrategy(EventDrivenStrategy):
    """"""
    def __init__(self):
        super(DoubleMaStrategy, self).__init__()
        pass

    def init_from_config(self, props):
        super(DoubleMaStrategy, self).init_from_config(props)
        pass

    def buy(self, quote, size=1):
        pass

    def sell(self, quote, size=1):
        pass

    def on_tick(self, quote):
        pass

    def on_quote(self, quote_dic):
        pass

    def on_trade(self, ind):
        pass

其中,

  • init_from_config负责通过props接收策略所需参数,进行策略初始化;
  • buy/sell负责发单;
  • on_tick和on_quote是框架核心,策略的逻辑都在其中实现,区别在于前者接收tick输入,后者接收bar数据;
  • on_trade负责监督发单成交情况。

下面请听我一一介绍。

1. 参数初始化

首先在函数init中初始化策略所需变量

def __init__(self):
    super(DoubleMaStrategy, self).__init__()
    self.symbol = ''

    self.fast_ma_len = 0   # 快线周期
    self.slow_ma_len = 0   # 慢线周期

    self.window_count = 0  # 价格序列长度
    self.window = self.slow_ma_len + 1

    self.price_arr = np.zeros(self.window)   # 价格序列保存最近n个价格
    self.fast_ma = 0       # 快线均值
    self.slow_ma = 0       # 慢线均值
    self.pos = 0           # 仓位

    self.buy_size_unit = 1
    self.output = True

该策略的关键参数有三个,交易标的(symbol),快线周期(fast_length)和慢线周期(slow_length),通过props字典传入。

def init_from_config(self, props):
    super(DoubleMaStrategy, self).init_from_config(props)
    self.symbol = props.get('symbol')
    self.init_balance = props.get('init_balance')
    self.fast_ma_len = props.get('fast_length')
    self.slow_ma_len = props.get('slow_length')

2. 逻辑实现

策略逻辑的实现在on_tick函数中完成。在实盘中,每隔500毫秒就会有一个quote传入,主要包括当前的bid price, ask price,在回测中,每隔1分钟会有一个quote传入,主要包括open, close, high和low。下面我们以实盘为例。
第一步:根据历史价格数据计算快速均线和慢速均线。
每当一个quote传入,我们计算当前的midprice并更新价格序列。

if hasattr(quote, 'bidprice1'):
    mid = (quote.bidprice1 + quote.askprice1) / 2.0
else:
    mid = quote.close
self.price_arr[0: self.window - 1] = self.price_arr[1: self.window]
self.price_arr[-1] = mid

接着计算快速均线和慢速均线。

self.fast_ma = np.mean(self.price_arr[-self.fast_ma_len - 1:])
self.slow_ma = np.mean(self.price_arr[-self.slow_ma_len - 1:])

第二步:当快线上穿慢线时,进行做多操作,或开多或平空;反之进行做空操作,或开空或平多。

if self.fast_ma > self.slow_ma:
    if self.pos == 0:
        self.buy(quote, 1)
    elif self.pos < 0:
        self.buy(quote, 2)

elif self.fast_ma < self.slow_ma:
    if self.pos == 0:
        self.sell(quote, 1)
    elif self.pos > 0:
        self.sell(quote, 2)

至此,策略实现,就是这么简单!

三、回测与仿真交易

现在我们需要做的就是启动策略,这一步在run_strategy函数中完成。

trade_config = {
  "remote.trade.address": "tcp://gw.quantos.org:8901",
  "remote.trade.username": "手机号",
  "remote.trade.password": "token"
}

def run_strategy():
    if is_backtest:
        props = {"symbol": "IH1712.CFE",
                 "start_date": 20170510,
                 "end_date": 20170930,
                 "fast_length": 5,
                 "slow_length": 20,
                 "bar_type": "1M",  # '1d'
                 "init_balance": 2e4}

        tapi = BacktestTradeApi()
        ins = EventBacktestInstance()

    else:
        props = {'symbol': 'IH1712.CFE',
                 "fast_length": 5,
                 "slow_length": 20,
                 'strategy.no': 64}
        tapi = RealTimeTradeApi(trade_config)
        ins = EventRealTimeInstance()

你所需要做的只是输入props中的参数,并通过设置全局变量is_backtest确定是仿真交易还是回测。

四、回测结果及分析

回测结果还让你满意么?Nani,居然亏钱了?别慌,让我做个分析。策略的分析模块在analyze函数中完成。

def analyze():
    ta = ana.EventAnalyzer()

    ds = RemoteDataService()
    ds.init_from_config(data_config)

    ta.initialize(data_server_=ds, file_folder=result_dir_path)

    ta.do_analyze(result_dir=result_dir_path, selected_sec=[])

五、仿真交易展示

恭喜你已经完成了上面所有步骤,下面我们就要真刀真枪得干了。所有你所需要做的就是把is_backtest设置为False,然后启动程序,就可以在我们的网站上看到交易结果了。

具体位置在“开源项目-Tradesim-仿真交易”,选择对应的策略,比如在本例中为股指期货。

订单状态如下

持仓和pnl如下

成交情况如下

总结

至此,我带大家完整地体验一次JAQS的两大策略开发模型。虽然有所体验,但相信您还有很多疑问,比如:研究方法是否科学?数据怎么来的?策略回测的调度逻辑是什么?在实盘交易前,我还需要做那些验证?我怎么能利用quantOS提供的工具,构建自己的交易事业?

在随后的章节里,这些答案将会一一呈现在您面前。