热烈祝贺www.911sss.com服务器升级完毕,全固态硬盘,50G超大带宽,满足你的 一切数据查看需求!

公告:郑重承诺:资源永久免费,资源不含任何联盟富媒体弹窗广告,只有三次走马灯水印广告(承诺绝不影响用户体验)


当前位置
首页  »  格上私募圈  »  使用深度学习在A股量化交易上的大胆尝试

摘要: 格上财富:10年深度研究铸造专业品牌,甄选阳光私募、PE/VC、海外基金等高端理财产品,是您投资理财的明智之选。


作者:zipline

来源:知乎,转载已获授权。

原文链接:http://dwz.cn/6ExQBz


上一篇《zipline 因子和策略开发代码集》中提到了深度学习如何在zipline里实现的问题,不少朋友感兴趣,进一步问深度学习用于量化的一系列问题。 其实深度学习我也只懂皮毛,稍微能用点罢了,详细回答真是勉为其难,真不敢系统性回答,怕误人子弟。


但为了降低大家上手的学习曲线,我还是尽力把我以前做的一个例子分享给大家,这个例子较为完整的给出一个深度学习用于量化上的实践,有一定的理论和试用价值 —— 后续的文章我还会给出如何运用该算法和雪球组合自动对接已实现交易自动化的办法。


假设读者稍微了解深度学习概念,稍微了解量化知识,有些概念我不解释了,也解释不准 —— 百度或google上自行查找一下,专家们讲的比我清楚多了。


先来说一下量化中怎么使用深度学习,也就是在哪里个环节加入深度学习技术 —— 这个问题没有固定答案,我只能给出我个人建议 —— 若按照Quantopian 主席的总结流程 (https://blog.quantopian.com/a-professional-quant-equity-workflow/ 量化实施分为如下几个阶段 : Data, Universe Definition, Alpha Discovery, Alpha Combination, Portfolio Construction, and Trading)来讲,深度学习介入的环节应该处于“因子发现”(alpha discover)阶段 , 而发现因子后,“因子合并”(Alpha Combination)阶段我们使用朴素机器学习做回归计算即可产生预测超因子(mega-alpha)—— 这两个步骤我都会在Tensorflow框架下实现。



“因子发现”— 传统上我们依靠自己的先验知识,结合一些数据方法猜测、构造我们的因子(factor),这无疑是整个量化工程里最累的体力活,费时费力,枯燥无味。而深度学习则有可能帮助我们自动发现潜在因子(有兴趣可看看autoencoder 和 embedding 算法,原则上讲深度网络的隐藏层都能起到类似作用),从而解放宽客们。


我们就是沿着这个思路,下面看看怎么让深度学习帮助我们发现一组比原始特征(股票里就是OHLCV)更有表现力(预测性)的特征集。


在给出具体实现前,我先简要的介绍一些该实现中有必要重点阐述的地方。


数据预处理


  • OHLCV 原始的绝对刚量数据我们不直接使用,而是转化成比值使用(后面有详细代码) —— 绝对数据意义不大,分布一搬也是不符合正态原则的非平稳的数据,而比值就会好很多。


  • 比值数据我们也不直接使用,而是横向再对比计算其在universe中的zscore —— 横向就是cross stock 计算,和所有股票的对应值一起计算 , 这种使用类排序(rank或zscore)的的而不是使用源值的做法,其目的也是为了数据平稳,不受异常值干扰;而且这种cross stock的值更能反应相互之间的关系。


算法选型


我们要找一个能面向时许序列的神经网模型,最简单、最直接的无疑属于RNN网络,尤其是其记忆能力更强的变种LSTM。


全数据


全时间,全股票参与计算。这是最核心的一个思路 —— 所有股票, 一个都不能少!谁知道它们有啥关系啦(明的关系也许有同板块,同概念关系,暗关系也行同一个后台老板,同一个操盘手,相互有关联交易,以及我们根本说不清的关联),因此我们的分析应该囊括所有股票 —— 所有股票个体在一起相互作用,共同决定当天市场上的供求关系,所以要使用全部股票数据做特征, 这是我们思路中很重要的一点 —— 这很有别于预测单个股票的涨跌。


程序细节简要说明


整个计算我模拟的 ziplien research platform 环境下进行 ,指标计算都有 pipeline机制实现(数据加工变得尤为简单和迅速)。


深度学习框架使用Tensorflow,使用静态模型tf.nn.static_rnn训练 —— 理论上动态rnn更灵活,但运行速度慢数倍。


代码中tf.nn.rnn_cell.BasicLSTMCell(RNN_HIDDEN_SIZE, state_is_tuple=True) 中的RNN_HIDDEN_SIZE = 100,其实就是要深度学习帮忙寻找的潜在因子 —— 因子发现就是靠它。


有了100个因子后,我们使用线型回归计算出predict值,即是“因子合并” - predictions = tf.contrib.layers.fully_connected(output, num_stocks, None)


原始特征需简单变化为


h2o = USEquityPricing.high.latest /USEquityPricing.open.latest

l2o = USEquityPricing.low.latest / USEquityPricing.open.latest

c2o = USEquityPricing.close.latest / USEquityPricing.open.latest

h2c = USEquityPricing.high.latest / USEquityPricing.close.latest

l2c = USEquityPricing.low.latest / USEquityPricing.close.latest

h2l = USEquityPricing.high.latest / USEquityPricing.low.latest


vol = USEquityPricing.volume.latest

outstanding = Fundamental(asset_finder).outstanding

outstanding.window_safe = True

turnover_rate = vol/Latest([outstanding])

returns = Returns(inputs=[USEquityPricing.close], window_length=5)

pipe_columns = {

h2o: h2o.log1p().zscore(),

l2o: l2o.log1p().zscore(),

c2o: c2o.log1p().zscore(),

h2c: h2c.log1p().zscore(),

l2c: l2c.log1p().zscore(),

h2l: h2l.log1p().zscore(),

vol: vol.zscore(),

turnover_rate: turnover_rate.log1p().zscore(),

return:returns.log1p(),

}


上述部分指标我借鉴了https://github.com/talolard/MarketVectors/blob/master/preparedata.ipynb 它的方法,但注意一点zipline的zscore是cross stock 而不是文中cross time的


训练时间长度 : 自己定把,大于21天最少 —— 我使用1年的数据大约一个GPU情况下跑20小时吧 要是有计算能力,跑个2年数据比较稳妥。


训练epoch : 200 轮,loss基本收敛不动了。


批次大小 : 21天,一个月


预测时间 : 5天,预测期一周


代码所在:https://github.com/kanghua309/strategy/edit/master/campaign/deeplearn/training.py


tensorflow版本: 我是 1.2.1 ,其他版本应该也成


运行方式:python ./training.py -s 2015-01-12 -e 2017-09-09 #s 开始时间,e 结束时间 ,要求两个时间都在开盘时间 (前提是你已经把我前文《zipline的A股改造版本》所描述的A股数据注入bundle了)


结果:漫长的训练后,其结果会写入到当前目录下predict.cvs中


提醒:


1. 如果等不及


如果没有GPU,缩短计算时间跨度吧,也可缩小参数


RNN_HIDDEN_SIZE = 100 # 我们寄希望发现100个隐藏特征

NUM_LAYERS = 2

BATCH_SIZE = 21 NUM_EPOCHS = 200 # 200

lr = 0.003


2. 如果提示资源耗尽


如果内存太小,估计会跑出OOM来 ,那样就自己尝试缩小股票集合(虽然我们强调全集合)


要是还跑不动,最后一招,挥刀自宫:注释掉下面attention 和 dropout 节点吧


if len(cells) == 0:


# Add attention wrapper to first layer.

cell = tf.contrib.rnn.AttentionCellWrapper(

cell, attn_length=ATTN_LENGTH, state_is_tuple=True)

cell = tf.nn.rnn_cell.DropoutWrapper(cell, output_keep_prob=dropout_keep_prob)


3. 如果提示存储空间不够


训练会在/tmp/下生成模型文件,挺大的,确保你/tmp下空间够大;如果/tmp没空间则修改代码中这行 —— model_dir="Models/model_0" —— 放开注释,指定到你有空间的地方。但记路径固定后,修改时长、参数等重新运行需要删除它。


4. 如果当前目录有名为predict.cvs的文件,小心把你的文件覆盖了。呵呵。


最后重申


上述算法是实验性质,没有严格进行残渣检查(找机会专门说这个问题),因子相关性,共线性等检测,所以是给大家演示深度学习暴力而实现的 —— 很可能有不少错误(尤其数据缺失值处理方面),但大思路是可取的


我个人是做数据相关互联网工作的,机器学习相比统计学还用的更多一些,但仍然认为在量化交易中统计学那些验证方法是必须了解的,是有必然价值的,更值得相信的。所以在量化世界里让我选常规武器,我还是优先选择严谨优雅的统计学方法论,机器学习作为辅助手段 —— 这次仅仅展示深度学习的暴力美,而并非代表深度学习适合量化,仅仅是探索中。


代码也贴上吧


# -*- coding: utf-8 -*-

import click

import numpy as np

import pandas as pd

from zipline.pipeline import Pipeline

from zipline.pipeline.data import USEquityPricing

from zipline.pipeline.factors import Latest

from zipline.pipeline.factors import Returns

from zipline.utils.cli import Date


from me.helper.research_env import Research

from me.pipeline.factors.tsfactor import Fundamental


pd.set_option(display.width, 800)


def make_pipeline(asset_finder):


h2o = USEquityPricing.high.latest / USEquityPricing.open.latest

l2o = USEquityPricing.low.latest / USEquityPricing.open.latest

c2o = USEquityPricing.close.latest / USEquityPricing.open.latest

h2c = USEquityPricing.high.latest / USEquityPricing.close.latest

l2c = USEquityPricing.low.latest / USEquityPricing.close.latest

h2l = USEquityPricing.high.latest / USEquityPricing.low.latest


vol = USEquityPricing.volume.latest

outstanding = Fundamental(asset_finder).outstanding

outstanding.window_safe = True

turnover_rate = vol / Latest([outstanding])

returns = Returns(inputs=[USEquityPricing.close], window_length=5)  # 预测一周数据


pipe_columns = {

h2o: h2o.log1p().zscore(),

l2o: l2o.log1p().zscore(),

c2o: c2o.log1p().zscore(),

h2c: h2c.log1p().zscore(),

l2c: l2c.log1p().zscore(),

h2l: h2l.log1p().zscore(),

vol: vol.zscore(),

turnover_rate: turnover_rate.log1p().zscore(),

return: returns.log1p(),

}


# pipe_screen = (low_returns | high_returns)

pipe = Pipeline(columns=pipe_columns)

return pipe


def make_input(result):

# start = 2014-9-1

# end   = 2017-9-11

P = result.pivot_table(index=result[level_0], columns=level_1,

values=[h2o, l2o, c2o, h2c, l2c, vol, turnover_rate,

return])  # Make a pivot table from the data


mi = P.columns.tolist()


new_ind = pd.Index(e[1].symbol + _ + e[0] for e in mi)

P.columns = new_ind

P = P.sort_index(axis=1)  # Sort by columns

P.index.name = date

clean_and_flat = P  # 去掉0列


print clean_and_flat

print "*" * 50, "flat result", "*" * 50


target_cols = list(filter(lambda x: return in x, clean_and_flat.columns.values))

input_cols = list(filter(lambda x: return not in x, clean_and_flat.columns.values))

size = len(clean_and_flat)

InputDF = clean_and_flat[input_cols][:size]

TargetDF = clean_and_flat[target_cols][:size]

return InputDF, TargetDF


#############################################################################


import tensorflow as tf


tf.logging.set_verbosity(tf.logging.INFO)

from tensorflow.contrib.learn.python.learn.estimators.estimator import SKCompat


learn = tf.contrib.learn


RNN_HIDDEN_SIZE = 100  # 我们寄希望发现100个隐藏特征

NUM_LAYERS = 2

BATCH_SIZE = 21  # PARM

NUM_EPOCHS = 200  # 200   #PARM

lr = 0.003


def train(InputDF, TargetDF):

print "*" * 50, "Training a rnn network", "*" * 50

num_features = len(InputDF.columns)

num_stocks = len(TargetDF.columns)

print "num stocks %s,last train data %s,first train data %s" % (num_stocks, TargetDF.index[-1], TargetDF.index[0])


# 生成数据

used_size = (len(InputDF) // BATCH_SIZE) * BATCH_SIZE  # 要BATCH_SIZE整数倍

train_X, train_y = InputDF[-used_size:].values, TargetDF[-used_size:].values

test_X, test_y = InputDF[-BATCH_SIZE:].values, TargetDF[-BATCH_SIZE:].values  # TODO

train_X = train_X.astype(np.float32)

train_y = train_y.astype(np.float32)

test_X = test_X.astype(np.float32)

test_y = test_y.astype(np.float32)

print np.shape(train_X), np.shape(train_y)

print "Train Set <X:y> shape"

print "Train Data Count:%s , Feather Count:%s , Stock Count:%s" % (

len(train_X), num_features, num_stocks)


NUM_TRAIN_BATCHES = int(len(train_X) / BATCH_SIZE)

ATTN_LENGTH = 10

dropout_keep_prob = 0.5


def LstmCell():

lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(RNN_HIDDEN_SIZE, state_is_tuple=True)

return lstm_cell


def makeGRUCells():

cells = []

for i in range(NUM_LAYERS):

cell = tf.nn.rnn_cell.GRUCell(num_units=RNN_HIDDEN_SIZE)

if len(cells) == 0:

# Add attention wrapper to first layer.

cell = tf.contrib.rnn.AttentionCellWrapper(

cell, attn_length=ATTN_LENGTH, state_is_tuple=True)

cell = tf.nn.rnn_cell.DropoutWrapper(cell, output_keep_prob=dropout_keep_prob)

cells.append(cell)

attn_cell = tf.nn.rnn_cell.MultiRNNCell(cells,

state_is_tuple=True)  # GRUCell必须false,True 比错 ,如果是BasicLSTMCell 必须True

return attn_cell


def lstm_model(X, y):

cell = makeGRUCells()

#理论dynnamic rnn 首选,但计算速度相比静态慢很多,不知何故


output, _ = tf.nn.dynamic_rnn(

cell,

inputs=tf.expand_dims(X, -1),

dtype=tf.float32,

time_major=False

)

split_inputs = tf.reshape(X, shape=[1, BATCH_SIZE,

num_features])  # Each item in the batch is a time step, iterate through them

# print split_inputs

split_inputs = tf.unstack(split_inputs, axis=1, name="unpack_l1")


output, _ = tf.nn.static_rnn(cell,

inputs=split_inputs,

dtype=tf.float32

)


output = tf.transpose(output, [1, 0, 2])

output = output[-1]

# 通过无激活函数的全连接层,计算就是线性回归,并将数据压缩成一维数组结构

predictions = tf.contrib.layers.fully_connected(output, num_stocks, None)

labels = y

loss = tf.losses.mean_squared_error(predictions, labels)

# print "lost:",loss

train_op = tf.contrib.layers.optimize_loss(loss, tf.contrib.framework.get_global_step(),

optimizer="Adagrad",

learning_rate=lr)

return predictions, loss, train_op


PRINT_STEPS = 100

validation_monitor = learn.monitors.ValidationMonitor(test_X, test_y,

every_n_steps=PRINT_STEPS,

early_stopping_rounds=1000)


# 进行训练

regressor = SKCompat(learn.Estimator(model_fn=lstm_model,

#model_dir="Models/model_0",

config=tf.contrib.learn.RunConfig(

save_checkpoints_steps=100,

save_checkpoints_secs=None,

save_summary_steps=100,

)))


print "Total Train Step: ", NUM_TRAIN_BATCHES * NUM_EPOCHS

print "*" * 50, "Training a rnn regress task now", "*" * 50

regressor.fit(train_X, train_y, batch_size=BATCH_SIZE,

steps=NUM_TRAIN_BATCHES * NUM_EPOCHS)  # steps=train_labels.shape[0]/batch_size * epochs,


print "*" * 50, "Predict tomorrow stock price now", "*" * 50

pred = regressor.predict(test_X[-BATCH_SIZE:])  # 使用最后21天预测 未来5天的股票价格


return pred


def save_to_sqlite(date, pred, target_cols):

print date, target_cols

print pred

date = date  # +1?  +5? 获取最后一天的数据

df = pd.DataFrame(pred[-1:], index=[date], columns=target_cols)  # 所有的股票数据一次性预测

df.index.name = "date"

print "*" * 50, "Predicted stock price from last trade day", "*" * 50

print df

df.to_csv("predict.csv", encoding="utf-8")


@click.command()

@click.option(

-s,

--start,

type=Date(tz=utc, as_timestamp=True),

help=The start date of the train.,

)

@click.option(

-e,

--end,

type=Date(tz=utc, as_timestamp=True),

help=The start date of the train.,

)

def execute(start, end):

research = Research()

my_pipe = make_pipeline(research.get_engine()._finder)

result = research.run_pipeline(my_pipe, start, end)


InputDF, TargetDF = make_input(result.reset_index())

predict = train(InputDF.fillna(0), TargetDF.fillna(0))

save_to_sqlite(end, predict, TargetDF.columns)

# save_to_sqlite(end,TargetDF[-1:].values,TargetDF.columns)


if __name__ == __main__:

print "Lets go …… "

execute()


权声明:部分文章推送时未能与原作者取得联系。若涉及版权问题,敬请原作者联系我们。联系方式:010-65983413。

复制下列地址至浏览器地址栏即可观看,本站不提供在线正版。备注:如有地址错误,请点击→ 我要报错 向我们报错!我们将在第一时间处理!谢谢!
  • 本网站所有内容均收集于互联网主流视频网站,不提供在线正版播放。
  • Copyright ©2017 All Rights Reserved www.911sss.com