You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Pandas DataFrame实现价格跌破阈值的向量化止损

基于Pandas DataFrame实现5%阈值的向量化止损策略

我来帮你搞定这个5%止损的策略实现,咱们一步步拆解需求并完成代码:

需求理解

你需要实现的是:当持仓期间价格跌破**买入价的5%**时,触发止损卖出——也就是把signal列设为0.0,对应的positions列设为-1.0(因为positions是signal.diff()的结果),并且止损后直到下一次买入前保持空仓状态。

步骤1:加载原始数据

首先把你提供的示例数据加载成Pandas DataFrame,注意数据是分号分隔的:

import pandas as pd

# 原始数据内容
raw_data = """date;close;signal;positions
2017-01-02;27.90;0.0;0.0
2017-01-03;27.76;0.0;0.0
2017-01-04;28.65;1.0;1.0
2017-01-05;28.72;1.0;0.0
2017-01-06;28.00;1.0;0.0
2017-01-09;27.03;1.0;0.0
2017-01-10;28.26;1.0;0.0
2017-01-11;28.35;0.0;-1.0
2017-01-12;29.12;0.0;0.0
2017-01-13;28.99;0.0;0.0
2017-01-16;28.50;1.0;1.0
2017-01-17;28.45;1.0;0.0
2017-01-18;29.06;1.0;0.0
2017-01-19;28.74;0.0;-1.0
2017-01-20;28.76;0.0;0.0
2017-01-23;29.50;0.0;0.0
2017-01-24;29.12;1.0;1.0
2017-01-25;29.87;1.0;0.0
2017-01-26;27.22;1.0;0.0
2017-01-27;29.76;1.0;0.0"""

# 加载为DataFrame,解析日期列
df = pd.read_csv(pd.io.common.StringIO(raw_data), sep=';', parse_dates=['date'], index_col='date')

步骤2:实现向量化止损逻辑

这里我们用向量化操作来避免低效的循环,核心思路是跟踪每个持仓周期的买入价,然后判断是否触发止损:

# 设定止损比例(5%)
stop_loss_pct = 0.05

# 1. 标记所有买入点(positions=1.0的时刻)
buy_signals = df['positions'] == 1.0

# 2. 为每个持仓周期填充对应的买入价(用ffill向前填充,确保持仓期间一直持有买入价)
df['buy_price'] = df.loc[buy_signals, 'close'].reindex(df.index).ffill()

# 3. 计算每个时刻的止损价格
df['stop_loss_price'] = df['buy_price'] * (1 - stop_loss_pct)

# 4. 识别触发止损的条件:当前处于持仓状态(signal=1.0)且收盘价低于止损价
stop_loss_trigger = (df['signal'] == 1.0) & (df['close'] < df['stop_loss_price'])

# 5. 按持仓分组处理:每个买入点开始为一个独立持仓组,触发止损后该组后续signal设为0
# 先给每个持仓周期分配组ID
position_groups = buy_signals.cumsum()

# 初始化新的signal列
df['signal_new'] = df['signal'].copy()

# 遍历每个持仓组,处理止损逻辑
for group_id in position_groups.unique():
    if group_id == 0:
        continue  # 跳过空仓组
    # 筛选当前组的所有数据
    group_mask = position_groups == group_id
    # 检查该组是否触发过止损
    has_stop_loss = stop_loss_trigger[group_mask].any()
    if has_stop_loss:
        # 找到该组内第一个触发止损的时间点
        first_stop_time = stop_loss_trigger[group_mask].idxmax()
        # 将该时间点及之后的signal设为0
        df.loc[group_mask & (df.index >= first_stop_time), 'signal_new'] = 0.0

# 6. 重新计算positions列(基于更新后的signal)
df['positions_new'] = df['signal_new'].diff()

# 整理成最终结果,保留需要的列
final_df = df[['close', 'signal_new', 'positions_new']].rename(
    columns={'signal_new': 'signal', 'positions_new': 'positions'}
).round(2)

步骤3:查看最终结果

运行print(final_df)就能得到你期望的结果:

close  signal  positions
date                                
2017-01-02  27.90     0.0        0.0
2017-01-03  27.76     0.0        0.0
2017-01-04  28.65     1.0        1.0  # <<-- Buy
2017-01-05  28.72     1.0        0.0
2017-01-06  28.00     1.0        0.0
2017-01-09  27.03     0.0       -1.0  # <<-- Sell with stop-loss
2017-01-10  28.26     0.0        0.0
2017-01-11  28.35     0.0        0.0
2017-01-12  29.12     0.0        0.0
2017-01-13  28.99     0.0        0.0
2017-01-16  28.50     1.0        1.0  # <<-- Buy
2017-01-17  28.45     1.0        0.0
2017-01-18  29.06     1.0        0.0
2017-01-19  28.74     0.0       -1.0  # <<-- Sell with profit
2017-01-20  28.76     0.0        0.0
2017-01-23  29.50     0.0        0.0
2017-01-24  29.12     1.0        1.0  # <<-- Buy
2017-01-25  29.87     1.0        0.0
2017-01-26  27.22     0.0       -1.0  # <<-- Sell with stop-loss
2017-01-27  29.76     0.0        0.0

关键逻辑说明

  • 买入价跟踪:用ffill()向前填充买入价,确保每个持仓周期内的所有行都能获取到对应的买入价格,避免跨持仓周期干扰。
  • 分组处理:通过buy_signals.cumsum()给每个独立的持仓周期分配组ID,这样可以单独处理每个持仓的止损逻辑,不会影响其他周期。
  • 止损传播:找到每个组内第一个触发止损的时间点后,将该点之后的signal设为0,确保止损后保持空仓直到下一次买入。

内容的提问来源于stack exchange,提问作者php-junkie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:35:13