sunnyswag / StockRL

在A股(股票)市场上训练强化学习交易智能体
GNU General Public License v3.0
190 stars 81 forks source link

utils/env.py 所定义的股票环境存在明显的未来函数 #9

Closed linchunquan closed 5 months ago

linchunquan commented 2 years ago

FinRL的各种例子都有未来函数的问题,你的代码里也把这部分错误逻辑copy过来了。详见

            self.date_index += 1
            state = (
                [coh] + list(holdings_updated) + self.get_date_vector(self.date_index)
            )

注意啊,date_index应该是要在state更新之后才能递增,因为在T日,只能看到T-1日的股价。去除未来函数之后,再看看你的算法是否还·有效果吧 :)

sunnyswag commented 2 years ago

首先,step() 时,所处的环境是根据历史股票数据构建出来的股票交易环境 可以先了解一下 env 相关的东西,获取的 next_state 是环境给出的。这里是第二天股票的 state,是客观的事实,不是预测出来的,这里有什么问题吗?

linchunquan commented 2 years ago

问题很严重也很明显:今天只能看到昨天股票的高、开、低、收价格及成交量。你当前的代码逻辑,在T日的step结束时,把T+1的股价作为下一步的状态了,这相当于在T+1日的step里,agent已经提前感知了T+1日的股价,这就是未来函数产生的原因。

Zero1366166516 commented 2 years ago

我认为T日获得T+1股价其实对算法(train)来说是没有用的,因为每天只操作一次并且T+1日的操作其实T日就已经给出了,T+1日其实是给T+2日的action。 我的问题是在T+1日时,trade的算法是怎么操作的,action的产生是否与当日(T+1)的状态相关,train和trade的机制是一样的吗? 我记得action应该是随机概率的生成,然后给定方向,根据reward调整。

linchunquan commented 2 years ago

现实当中T日怎能获得T+1股价的股价呢?这就叫做未来函数,是错误。train和trade的业务模型需要保持一致,如果train期间应用了带未来函数的方法训练,实盘trade是不可能成功的。

SernRounder commented 2 years ago

首先,step() 时,所处的环境是根据历史股票数据构建出来的股票交易环境 可以先了解一下 env 相关的东西,获取的 next_state 是环境给出的。这里是第二天股票的 state,是客观的事实,不是预测出来的,这里有什么问题吗?

你在step里更新date的时机不对。我们理一理你的代码逻辑:在dayT-1,step结束后通过getenv返回了环境e,然后Agent根据e确定action1并提交给env,env处理action1,执行交易股票等操作,之后进入dayT,通过getenv返回新的环境e+1。

但在这个过程中,env处理action时使用的close收盘价格等一系列指标已经在dayT-1交给Agent了。等于是Agent在已知dayT的所有情况下做出了action1并立即处理了。所以收敛速度十分快速。当把day+1的指令放到处理action之前就能发现模型不再收敛。希望作者复现一下。

这里附上原始代码: ` begin_cash = self.cash_on_hand assert min(self.holdings) >= 0

       #### 使用dayT的数据开始处理action #####
        assert_value = np.dot(self.holdings, self.closings)
        self.account_information["cash"].append(begin_cash)
        self.account_information["asset_value"].append(assert_value)
        self.account_information["total_assets"].append(begin_cash + assert_value)
        reward = self.get_reward()
        self.account_information["reward"].append(reward)

        transactions = self.get_transactions(actions)
        ######一些省略的action处理代码############

        #### action处理结束, 进入day T+1 #####

        self.date_index += 1  ###<---至少这一句应该放到处理action之前执行,因为不可能在已知dayT的全部状态的情况下操作dayT
        state = (
            [coh] + list(holdings_updated) + self.get_date_vector(self.date_index)
        )
        self.state_memory.append(state)

       #### 返回 day T+1的状态 #####
        return state, reward, False, {}`
SernRounder commented 2 years ago

顺便再附上我修改前后的模型收敛情况, 这是不做任何修改的收敛情况: image 可以看到20多轮就能在训练集上收敛出不错的结果

而这是将day+1提前到处理action之前的结果 image 在30多轮后在训练集上依旧看不到明显的收敛

SernRounder commented 2 years ago

我又用第三方平台跑了一下训练出来的模型, 这个模型大概在5800x+3080的平台上训练了两个小时. 总之是效果一言难尽. 没有使用任何未来数据的情况下训练和执行的. 训练集是11年到21年, 测试集是过去一年到现在. 具体看图吧, 虽然强化学习每次执行结果不一样, 但是都会收敛到-30~-40%的样子, 这已经是我跑10次里最好的一次了 image

下面的是跑20年7月到21年7月的结果, 模型在训练集上表现出了在你的测试集上的效果, 我认为可以初步排除我自己的回测代码写错的问题 image

Zero1366166516 commented 2 years ago

好的,数据集和我用的一样,我也跑一下,试试。

sunnyswag commented 5 months ago

但在这个过程中,env处理action时使用的close收盘价格等一系列指标已经在dayT-1交给Agent了。等于是Agent在已知dayT的所有情况下做出了action1并立即处理了

强化学习的相关机制和原理,暂时先关闭这个 Issue