You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于Keras和TensorFlow的LSTM股票交易策略自定义损失函数咨询

Hey Sam, 我完全懂你现在的困惑——把传统的股价预测LSTM改成交易决策模型,确实是个思路上的大转变,毕竟目标从“预测数值”直接跳到了“最大化最终资金”,核心逻辑完全不一样了。我来给你拆解下可行的方向,帮你理清思路:

核心思路:直接让模型输出交易决策,而非股价

你不需要先训练一个股价预测模型再套决策层,这是绕远路。你的目标是收益最大化,所以应该让LSTM直接输出每一步的交易指令(比如仓位比例、买卖动作),然后用收益的相反数作为损失函数(因为模型要最小化损失,等价于最大化收益)。

第一步:设计模型的输出

首先得明确模型每一步输出什么,常见的两种方案:

  • 仓位比例:输出一个0到1之间的数值(用sigmoid激活),代表当前总资金中投入股票的比例(如果允许做空,可以用tanh输出-1到1,-1是空仓全卖,1是满仓全买)。这种方式更灵活,模型可以学习到不同市场环境下的仓位调整。
  • 离散动作:输出三个概率(买/卖/持仓),用softmax激活,然后根据概率选择动作。这种更贴近人类的交易决策,但可能不如连续仓位灵活。

通常推荐第一种,因为连续仓位能让模型更精细地调整策略。

第二步:自定义损失函数的关键——跟踪交易状态

这是最棘手的部分:交易是有状态的,当前的现金、持仓数量会影响下一步的决策,而普通的Keras损失函数是逐样本独立计算的,没法处理这种序列依赖。这里有两种解决方式:

方式1:用TensorFlow自定义训练循环(更灵活)

放弃Keras的model.fit(),自己写训练循环,用tf.GradientTape跟踪梯度,同时在循环里维护每个样本的现金、持仓状态。大致流程是:

  1. 遍历每个训练样本的时间步
  2. 根据模型输出的仓位比例,计算当前能买/卖的最大份额(不能超过现有现金或持仓)
  3. 更新现金和持仓数量,加入交易成本(比如千分之一的手续费,一定要加,不然模型会疯狂交易)
  4. 计算每个样本的最终资金(现金+最后一天持仓的市值),损失就是所有样本最终资金的平均值的相反数(因为要最小化损失,相当于最大化平均收益)

给你个简化的代码框架参考:

import tensorflow as tf
from tensorflow.keras.layers import Input, LSTM, Dense

# 构建模型:输入是时间窗口+特征(股价+技术指标),输出每一步的仓位比例
def build_trading_model(window_size, feature_num):
    inputs = Input(shape=(window_size, feature_num))
    x = LSTM(64, return_sequences=True)(inputs)  # return_sequences=True,输出每一步的决策
    outputs = Dense(1, activation='sigmoid')(x)  # 输出0-1的仓位比例
    return tf.keras.Model(inputs=inputs, outputs=outputs)

model = build_trading_model(window_size=10, feature_num=5)
optimizer = tf.keras.optimizers.Adam(learning_rate=1e-4)
transaction_cost = 0.001  # 千分之一手续费

@tf.function
def train_step(x_batch, price_batch):
    with tf.GradientTape() as tape:
        positions = model(x_batch, training=True)  # (batch_size, window_size, 1)
        
        # 用tf.scan处理序列状态更新,代替Python循环(效率更高)
        def update_state(state, step_data):
            cash, shares = state
            price_t, pos_t = step_data
            
            # 计算当前总市值
            total_value = cash + shares * price_t
            # 目标持仓数量
            target_shares = (pos_t * total_value) / price_t
            # 交易数量
            delta = target_shares - shares
            
            # 处理买入:不能花超现金,扣手续费
            if delta > 0:
                cost = delta * price_t * (1 + transaction_cost)
                if cost > cash:
                    delta = cash / (price_t * (1 + transaction_cost))
                    cash -= delta * price_t * (1 + transaction_cost)
                else:
                    cash -= cost
            # 处理卖出:不能卖超持仓,扣手续费
            elif delta < 0:
                sell_shares = tf.abs(delta)
                if sell_shares > shares:
                    sell_shares = shares
                cash += sell_shares * price_t * (1 - transaction_cost)
            
            shares += delta
            return (cash, shares)
        
        # 初始状态:现金100,持仓0
        initial_state = (tf.constant(100.0, dtype=tf.float32), tf.constant(0.0, dtype=tf.float32))
        
        # 对每个样本计算最终状态
        final_states = []
        for i in range(tf.shape(x_batch)[0]):
            step_data = (price_batch[i], tf.squeeze(positions[i], axis=-1))
            final_cash, final_shares = tf.scan(update_state, elems=step_data, initializer=initial_state)
            # 取最后一步的状态
            final_cash = final_cash[-1]
            final_shares = final_shares[-1]
            final_total = final_cash + final_shares * price_batch[i][-1]
            final_states.append(final_total)
        
        final_states = tf.stack(final_states)
        loss = -tf.reduce_mean(final_states)  # 最小化负的平均收益
    
    grads = tape.gradient(loss, model.trainable_variables)
    optimizer.apply_gradients(zip(grads, model.trainable_variables))
    return loss, tf.reduce_mean(final_states)

方式2:用强化学习框架(更贴合场景)

其实你的需求完美匹配强化学习(RL)的场景:序列决策、最大化累积奖励(收益)。你可以把LSTM作为RL的策略网络,输入是当前的股价窗口、现金、持仓等状态,输出动作(仓位或买卖指令),然后用DQN、PPO等算法训练。

这类框架会帮你处理奖励累积、策略更新的细节,不需要自己写复杂的状态跟踪逻辑。这种方式的优势是RL天生就考虑了长期收益,而不是只看最终资金,能避免模型短视。

第三步:关键注意事项

  • 数据预处理:一定要对股价和技术指标做归一化(比如MinMaxScaler),LSTM对数值范围很敏感。除了收盘价,还可以加入MA、RSI、成交量等技术指标,帮助模型捕捉市场信号。
  • 交易成本:千万不要忽略手续费、滑点,否则模型会学出频繁交易的策略,实际收益会惨不忍睹。
  • 回测验证:训练完模型后,一定要用未见过的测试数据做回测,看实际收益曲线,避免过拟合。

总结

你不需要修改MSE这类传统损失函数,因为你的目标不是预测数值而是最大化收益。优先考虑两种路径:要么用自定义训练循环实现基于收益的损失函数,要么直接用强化学习框架结合LSTM做策略网络。后者可能更省心,也更贴合交易决策的本质。

内容的提问来源于stack exchange,提问作者Samantha

火山引擎 最新活动