从零实现一个支持深度强化学习的量化投资系统

发布于: 雪球转发:0回复:1喜欢:1

开始关注量化投资有几年了,断断续续的,认知上也在持续进化。

研究过外汇自动交易,期货CTA,然后是股票的量化投资,当然本质上没有太大区别。

开源量化系统也用过几个,数据格式融合的问题,有些不方便,而且客观讲,实现一个简单的量化系统,其实非常简单。

这一次算是重构,希望它可以:

一、支持传统基于“规则”也就是技术分析为主的策略,包括投资组合、单支证券择时,多支证券轮动。

二、支持机器学习,深度强化学习等算法量化。

由于深度强化学习的环境是有openai确定规范的接口,所以,我们就按照强化学习的规则,实现一个A股基金的交易环境。

环境继承自gym.Env, env_data是从数据库加载数据并做好指标预计算备查,config就是相关的交易参数

class MarketEnv(gym.Env):
    def __init__(self,env_data,config):
        self.config = config
        self.env_data = env_data
self.reset()

reset函数就是把环境初始化,下标零0,使用config里的benchmark做交易基准。数据我们是加载全量,calendar里去加载日期子集。这里有一个todo:可以判断universe里,日期最大的,以此为起点。

def reset(self):
    self.i = 0
    benchmark = self.config.benchmark
    df = self.env_data.get_df_by_code(benchmark)
    self.calendar = df.index[df.index>=self.config.start]
    print('交易天数:{}'.format(len(self.calendar)))

    #初始有组合权重,未分配均为持仓占比为0,组合值为初始资金
    self.last_weights = [0.0 for code in self.config.universe]
    self.last_portfolio = self.config.init_cash

    self.weights_memory = []
    self.portfolio_memory = []

给传统策略一个函数的接口(强化学习不使用这个接口)。它其实就是对calendar里每一个tick进行轮询,每个tick都会使用策略去取一个动作,动作如果是None则忽略,然后调用step去执行这个action。而强化学习是自己会调这个action。

def run(self,strategy):
    self.strategy = strategy
    done = False
    while (not done):
        action = strategy.get_action(self)
        _, _, done, _ = self.step(action)
    print('回测完成!')
    self.analysis()

step函数会做三件事:

最后一件get_status是按强化学习的要求,返回obv,reward,done及info。如果i大于的calendar的长度,就是遍历结果。

def step(self,action=None):
    #收盘后执行交易
    # 根据收益率更新组合情况
    #self.__update_portfolio()
    #self.__do_action()
    observation, reward, done, info = self.__get_status()
return observation,reward,done,info


先是根据universe里tick的收益率变化,更新组合净值。

def __update_portfolio(self):
    df = self.env_data.get_data(self.calendar[self.i],self.config.universe)
    rates = df['rate'].values

    #计算组合总的收益率 = 收益率加权求和
    port_rate = sum(np.array(self.last_weights)*rates)
    new_portfolio = self.last_portfolio * (1+port_rate)
    new_weights = np.array(self.last_weights) * (1+rates) / (1+port_rate)

    #保存值
    self.last_weights = new_weights
    self.last_portfolio = new_portfolio

    self.weights_memory.append(new_weights)
    self.portfolio_memory.append(new_portfolio)

然后执行Action,其实就是配置新的组合。这里没有计算交易手续费及滑点(todo)。

def __do_action(self,action):
    if not action:
        return
    self.last_weights = action

第三步,返回状态。

def __get_status(self):
    observation = self.env_data.get_data(self.calendar[self.i],self.config.universe)
    #print(observation)
    reward = None
    info = {}
    self.i += 1
    done = False
    if self.i > (len(self.calendar) - 1):
        done = True
    return observation, reward, done, info

这里reward是给强化学习使用的。后续再给出。

“买入并持有”的策略这样写:

class BuyHold(object):
    def __init__(self,fix,rebalance='yearly'):
        self.fix = fix
        self.vars = None
        self.rebalance = rebalance
        #self.universe = universe
    def get_action(self,env):
        if env.i == 0:
            return self.fix

        if self.rebalance:
            if self.rebalance == 'yearly':
                if env.i % 252 == 0:
                    return self.fix
        return None

就是第一步的时候,把权重配置为[0.2,0.8]即股债二八。

运行一下,结果长这样:

(公众号:七年实现财富自由(ailabx),用数字说基金,用基金做投资组合,践行财富自由之路)

全部讨论

2021-03-03 20:43

看着很牛,可惜我不懂