day44| 121. 买卖股票的最佳时机122买卖股票的最佳时机II123买卖股票的最佳时机III 188买卖股票的最佳时机IV309买卖股票的最佳时机含冷冻期714.买卖股票的最佳时机含手续费


前言

121. 买卖股票的最佳时机

在这里插入图片描述

思路

设想的对象是手里的现金;

持有股票的话,手里只有股票无现金,一定钱是少的;最后肯定是不持有股票的时候手里头的现金多;

我理解的总体思路:因为最后一定是不持有手里的现金多,而不持有的最大值需要由前一天持有的最大值推出,所以每天都需要分别求持有和不持有的各自的max;

dp五部曲

  1. 💕dp数组及其下标含义:设置dp[i][0]和dp[i][1]
  • dp[i][0]表示第i天持有这只股票手里的现金最大是多少
  • dp[i][1]表示第i天持有这只股票手里的现金最大是多少

为什么要这么设置dp:如果设置dp的含义是第i天买或者卖这只股票,那么别的持有不买不卖股票的天就很难表征状态

  1. 递推公式:
  • dp[i][0]由前一天推出:如果前一天持有股票,那么今天持有股票的时候手里的现金跟昨天是一样的,即dp[i-1][0];如果前一天不持有股票,那么今天就是买入那天,手里的现金是-price[i];dp[i][0] = max(dp[i - 1][0], -prices[i]);
  • dp[i][1]由前一天推出:如果前一天不持有股票,那么今天持有股票的时候手里的现金跟昨天是一样的,即dp[i-1][1];如果前一天持有股票,那么今天就是卖出那天,手里的现金是dp[i-1][0]+price[i];dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);
  1. 初始化:dp[0][0]和dp[0][1]
    dp[0][0]: dp[0][0]表示第0天持有股票,此时的持有股票就一定是买入股票了,因为不可能有前一天推出来,所以dp[0][0] -= prices[0];
    dp[0][1]: 不持有股票那么现金就是0,所以dp[0][1] = 0;
  2. 遍历顺序:很简单,就是从前往后
  3. 举例
    在这里插入图片描述

方法一 dp

我觉得这道题目需要背一下,自己完全想不到的
首先写了二维数组版本

class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        dp = [[0,0] for _ in range(len(prices))]
        dp[0][0] = -prices[0]#持有
        dp[0][1] = 0
        for i in range(1,len(prices)):
            dp[i][0] = max(dp[i-1][0],-prices[i])
            dp[i][1] = max(dp[i-1][1],dp[i-1][0]+prices[i])
        return dp[-1][1]
可以压缩到一维,只使用两个变量来维护
class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        dp = [-prices[0],0]
        for i in range(1,len(prices)):
            dp[0] = max(dp[0],-prices[i])
            dp[1] = max(dp[1],dp[0]+prices[i])
        return dp[1]

方法二 贪心算法和暴力求解

贪心算法的思路:就是求左边的最小值,就是第i天售出,获利跟保存的最大获利比较

class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        re = 0
        low = float('inf')
        for i in range(len(prices)):
            low = min(low,prices[i])#找到遍历到当前的最小值
            re = max(re, prices[i]-low) 
        return re

暴力求解会超时

class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        re = 0
        for i in range(len(prices)):
            buy = prices[i]
            sell = max(prices
<think>为了最佳地预测次日股票走势,我们需要调整时间参数以捕捉最近的市场动态,同时避免过拟合。以下关键调整策略: 1. **训练集时间范围调整**:使用滚动窗口训练,确保模型始终基于最近数据训练。通常1-3年训练数据为宜,过长的历史数据可能包失效模式。 2. **特征计算窗口优化**:技术指标(如移动平均线)的时间窗口需与预测目标(次日走势)匹配。短预测应使用较短窗口。 3. **避免未来信息泄露**:确保特征计算仅使用截至当天的数据,不包次日信息。 4. **测试集划分**:测试集应紧接训练集之后,且覆盖近市场状态(如最近3-6个月)。 具体修改如下(基于原代码): ### 1. 训练集时间范围调整 修改配置类中的时间参数,确保训练集覆盖足够近数据: ```python class StockConfig: def __init__(self): # ...其他配置不变... # 时间范围调整:训练集使用最近3年,测试集使用最近3个月 self.START_DATE = "2022-1-1" # 调整为3年前 self.END_DATE = "2025-8-13" self.TEST_START = "2025-5-1" # 测试集为最近3个月 self.TEST_END = "2025-8-13" ``` ### 2. 技术指标窗口优化 将短技术指标的窗口调小,以匹配次日预测: ```python class FeatureEngineer: def transform(self, df): # ...其他特征计算不变... # 优化指标窗口:使用更短的窗口以适应短预测 df['MA5'] = df['close'].rolling(5).mean().fillna(0) # 保持5日,适合短 df['MA10'] = df['close'].rolling(10).mean().fillna(0) # 增加10日作为补充 # 将RSI周从14天改为10天,更敏感 rsi = ta.RSI(df_temp['close'].values, timeperiod=10) # 改为10天 df['RSI10'] = self.safe_fillna(rsi, 50) # 移除原RSI14 if 'RSI14' in df.columns: df.drop('RSI14', axis=1, inplace=True) # 更新特征列表(在配置类中也要相应修改) # ...其他特征... ``` 同时更新配置类中的特征列表: ```python class StockConfig: def __init__(self): # ...其他配置... self.CLUSTER_FEATURES = [ 'price_change', 'volatility', 'volume_change', 'MA5', 'MA10', 'RSI10', 'MACD_hist' # 修改为MA5, MA10, RSI10 ] self.PREDICT_FEATURES = [ # ...其他特征... 'MA5', 'MA10', 'RSI10', # 替换原来的MA20和RSI14 # ...其他特征... ] ``` ### 3. 目标函数调整 确保目标函数仅使用次日数据(原代码已正确实现): ```python class TargetCreator: def create_targets(self, df): # 计算次日(T+1)收盘价相对于开盘价的涨幅 df['next_day_open_to_close_gain'] = df['close'].shift(-1) / df['open'].shift(-1) - 1 # 计算次日(T+1)最低价与开盘价比例 df['next_day_low_ratio'] = df['low'].shift(-1) / df['open'].shift(-1) # 注意:原代码中还有T+2的条件,如果只预测次日,可以去掉T+2条件 # 修改为仅使用次日条件 df['target'] = 0 mask = ( (df['next_day_open_to_close_gain'] > self.config.MIN_GAIN) & (df['next_day_low_ratio'] >= self.config.MIN_LOW_RATIO) ) df.loc[mask, 'target'] = 1 # 删除最后一行(没有次日数据) df = df.iloc[:-1] return df ``` ### 4. 时间特征增强 添加与次日预测相关的短时间特征: ```python class FeatureEngineer: def transform(self, df): # ...其他特征... # 添加时间特征:星几和月份(原代码已有) # 添加是否为月末/季末(资金流动效应) df['is_month_end'] = (df['date'].dt.is_month_end).astype(int) df['is_quarter_end'] = (df['date'].dt.month % 3 == 0) & (df['date'].dt.is_month_end) df['is_quarter_end'] = df['is_quarter_end'].astype(int) # 添加短动量特征 df['momentum_3'] = df['close'].pct_change(3).fillna(0) # 3日动量 df['momentum_5'] = df['close'].pct_change(5).fillna(0) # 5日动量 # 更新特征列表 return df ``` ### 5. 动态时间窗口训练(可选) 实现滚动训练窗口,每次使用最近N天数据训练: ```python def main(): config = StockConfig() # 假设我们按周滚动训练 current_date = pd.to_datetime(config.TEST_START) end_date = pd.to_datetime(config.TEST_END) while current_date <= end_date: # 设置训练集为当前日前3年 train_start = current_date - pd.DateOffset(years=3) config.START_DATE = train_start.strftime('%Y-%m-%d') config.END_DATE = (current_date - pd.DateOffset(days=1)).strftime('%Y-%m-%d') config.TEST_START = current_date.strftime('%Y-%m-%d') config.TEST_END = (current_date + pd.DateOffset(weeks=1)).strftime('%Y-%m-%d') # 加载数据 train_data = load_stock_data(...) # ...训练模型并预测本周... # 移动到下一周 current_date += pd.DateOffset(weeks=1) ``` ### 注意事项: 1. **数据时效性**:确保使用最新数据,定重新训练模型(如每周)。 2. **过拟合风险**:短模型更易过拟合,需加强正则化(如增加`min_child_samples`)。 3. **市场状态**:在极端市场(如暴跌)时,应缩短训练窗口至1年内。 ### 完整修改示例(配置类和时间特征调整) ```python class StockConfig: def __init__(self): # 数据路径(不变) self.SH_PATH = r"D:\股票量化数据库\股票csv数据\上证" self.SZ_PATH = r"D:\股票量化数据库\股票csv数据\深证" # 时间范围:训练集3年,测试集3个月 self.START_DATE = "2022-1-1" self.END_DATE = "2025-8-13" self.TEST_START = "2025-5-1" self.TEST_END = "2025-8-13" # 聚类设置:更新特征 self.CLUSTER_NUM = 8 self.CLUSTER_FEATURES = [ 'price_change', 'volatility', 'volume_change', 'MA5', 'MA10', 'RSI10', 'MACD_hist' # 修改特征 ] # 预测特征:更新为短特征 self.PREDICT_FEATURES = [ 'open', 'high', 'low', 'close', 'volume', 'price_change', 'volatility', 'volume_change', 'MA5', 'MA10', 'RSI10', 'MACD_hist', # 使用MA5, MA10, RSI10 'cluster', 'MOM10', 'ATR14', 'VWAP', 'RSI_diff', 'price_vol_ratio', 'MACD_RSI', 'advance_decline', 'day_of_week', 'month', 'is_month_end', 'is_quarter_end', # 新增时间特征 'momentum_3', 'momentum_5' # 新增动量 ] # ...其他配置不变... ``` 通过以上调整,模型将更专注于短模式,提高次日走势预测能力。 §§相关问题§§: 1. 如何防止股票预测模型中的过拟合问题? 2. 除了时间参数,还有哪些关键因素影响股票次日走势预测的准确性? 3. 如何实时更新股票预测模型以适应市场变化? 4. 在模型部署时,如何处理每日新增的股票数据?</think>### 优化时间参数实现最佳次日股票走势预测 为了确保模型能最佳预测次日股票走势,需要精细调整时间参数以聚焦近市场动态。以下是关键修改策略和代码实现: #### 1. **训练集时间窗口优化** ```python # 在StockConfig类中修改 class StockConfig: def __init__(self): # 原始设置(覆盖过长历史) # self.START_DATE = "2012-1-1" # self.END_DATE = "2025-8-13" # 优化设置:聚焦近市场 self.START_DATE = "2024-1-1" # 仅使用1.5年数据 self.END_DATE = "2025-8-13" self.TRAIN_WINDOW = "180D" # 滚动训练窗口(6个月) # 测试集使用最近3个月 self.TEST_START = "2025-5-1" self.TEST_END = "2025-8-13" ``` **原理**: - 股票市场短模式通常在6-12个月内有效 - 过长的历史数据(>3年)会引入失效的市场模式 - 滚动窗口确保模型始终基于最新市场动态 #### 2. **动态特征计算窗口** ```python # 在FeatureEngineer类中修改 def transform(self, df): # 原始固定窗口 # df['MA5'] = df['close'].rolling(5).mean() # df['RSI14'] = ta.RSI(df_temp['close'], 14) # 优化:自适应窗口(基于波动率) volatility = df['close'].pct_change().std() * 100 # 日波动率百分比 # 高波动市场 → 较短窗口 if volatility > 2.0: ma_window = 3 rsi_window = 7 # 低波动市场 → 较长窗口 elif volatility < 0.8: ma_window = 10 rsi_window = 21 # 正常波动 else: ma_window = 5 rsi_window = 14 # 应用动态窗口 df[f'MA{ma_window}'] = df['close'].rolling(ma_window).mean() df[f'RSI{rsi_window}'] = ta.RSI(df_temp['close'], rsi_window) # 添加短动量特征(次日预测关键) df['intraday_momentum'] = (df['close'] - df['open']) / df['open'] df['overnight_momentum'] = (df['open'] - df['close'].shift(1)) / df['close'].shift(1) return df ``` **原理**: - 波动率越高,技术指标窗口应越短 - 添加日内动量(开盘到收盘)和隔夜动量(昨日收盘到今日开盘)特征 - 这些特征对次日开盘价预测至关重要 #### 3. **目标函数调整(次日聚焦)** ```python # 在TargetCreator类中修改 def create_targets(self, df): # 原始目标包T+1和T+2条件 # 优化:仅关注次日走势 # 计算次日收益率 (明日收盘 vs 今日收盘) df['next_day_return'] = df['close'].shift(-1) / df['close'] - 1 # 创建三分类目标: # 0: 下跌 (< -0.5%) # 1: 震荡 (±0.5%) # 2: 上涨 (> +0.5%) df['target'] = pd.cut( df['next_day_return'], bins=[-np.inf, -0.005, 0.005, np.inf], labels=[0, 1, 2] ).astype(int) # 删除最后一行(无次日数据) df = df.iloc[:-1] return df ``` **原理**: - 直接预测次日收益率而非复合条件 - 三分类更符合实际交易场景(涨/跌/震荡) - 阈值0.5%过滤微小波动,聚焦显著走势 #### 4. **滚动训练机制** ```python # 修改主训练流程 def train_model(self, X, y, groups): # 原始:全量数据训练 # 优化:滚动窗口训练 # 按时间排序(确保时间序列特性) time_index = pd.to_datetime(df['date']) sorted_idx = np.argsort(time_index) X_sorted = X[sorted_idx] y_sorted = y[sorted_idx] # 创建滚动训练窗口 window_size = 180 # 6个月 stride = 30 # 每月更新 models = [] # 存储不同时段的模型 for start in range(0, len(X_sorted) - window_size, stride): end = start + window_size X_window = X_sorted[start:end] y_window = y_sorted[start:end] # 训练短模型 model = lgb.LGBMClassifier(objective='multiclass', num_class=3) model.fit(X_window, y_window) models.append((time_index[end], model)) # 存储模型及有效 return models ``` **原理**: - 每30天使用最近6个月数据训练新模型 - 模型集合捕捉不同市场状态 - 实盘使用最近训练的3个模型投票决策 #### 5. 盘后数据增强 ```python # 添加盘后数据处理 def extend_features(self, df): """添加当日盘后数据特征""" # 当日量价关系 df['close_open_ratio'] = df['close'] / df['open'] df['high_low_range'] = (df['high'] - df['low']) / df['open'] # 当日资金流向 df['money_flow'] = df['volume'] * (2*df['close'] - df['low'] - df['high']) / (df['high'] - df['low']) # 当日机构行为指标 df['large_order_ratio'] = calculate_large_orders(df) # 自定义函数 return df ``` ### 关键时间参数总结表 | 参数类型 | 原始值 | 优化值 | 作用 | |---------|-------|-------|-----| | 训练集长度 | 13年 | 1.5年 | 聚焦近市场结构 | | 技术指标窗口 | 固定 | 波动率自适应 | 匹配当前市场节奏 | | 目标时间范围 | T+1&T+2 | 次日 | 精准预测明日走势 | | 特征时间属性 | 日级 | 日内+盘后 | 捕捉当日资金动向 | | 模型更新频率 | 一次性 | 每月滚动 | 适应市场变化 | ### 实盘预测流程 ```mermaid graph TD A[当日收盘] --> B[执行特征工程] B --> C[加载最近3个模型] C --> D{模型投票决策} D -->|| E[买入信号] D -->|| F[卖出信号] D -->|震荡| G[持有信号] ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值