散户为什么总是亏钱?我用 Python 模拟了 5 种典型操作,答案很残酷
散户为什么总是亏钱?我用 Python 模拟了 5 种典型操作,答案很残酷
A 股有个广为流传的说法:“七亏两平一赚”。意思是 70% 的散户在亏钱,20% 不赚不亏,只有 10% 赚到了钱。
但大多数亏钱的人并不觉得自己做错了什么。他们会说:“大盘不好”、“庄家太狡猾”、“运气不好刚好买在高点”。
真的是运气问题吗?
这篇文章做了一个实验:用真实的 A 股历史数据,模拟 5 种散户最常见的操作习惯,看看每种习惯长期下来的真实收益。不讲理论,只看数据。
实验设置
- 数据:用 AlphaFeed 拉沪深 300 指数近几年的日 K 线(代表市场整体)
- 起始资金:100 万
- 每种操作模式独立跑一遍,最后对比净值
- 作为基准:买入持有沪深 300,什么都不做
import pandas as pd
import numpy as np
from alphafeed import AlphaFeed
af = AlphaFeed()
df = af.klines.get(
"000300.SH", period="1d", count=2000,
adjust="forward", to_dataframe=True,
)
df = df.sort_values("trade_date").reset_index(drop=True)
df["ret"] = df["close"].pct_change().fillna(0)
# 基准:买入持有
df["buyhold"] = (1 + df["ret"]).cumprod()
bh_final = df["buyhold"].iloc[-1]
print(f"=== 基准:买入持有 ===")
print(f" 最终净值: {bh_final:.4f}")
print(f" 累计收益: {bh_final - 1:+.2%}")
print(f" 最大回撤: {(df['buyhold'] / df['buyhold'].cummax() - 1).min():.2%}")
print()
模式一:追涨杀跌
“涨了才敢买,跌了就吓跑。”
规则:连涨 3 天就买入,连跌 3 天就卖出。
import pandas as pd
import numpy as np
def simulate_chase_and_panic(df: pd.DataFrame) -> pd.Series:
position = 0
equity = [1.0]
for i in range(3, len(df)):
ret = df["ret"].iloc[i]
# 连涨3天 → 买入
if (df["ret"].iloc[i-1] > 0 and df["ret"].iloc[i-2] > 0 and df["ret"].iloc[i-3] > 0):
position = 1
# 连跌3天 → 卖出
if (df["ret"].iloc[i-1] < 0 and df["ret"].iloc[i-2] < 0 and df["ret"].iloc[i-3] < 0):
position = 0
strategy_ret = ret * position
equity.append(equity[-1] * (1 + strategy_ret))
return pd.Series(equity)
eq1 = simulate_chase_and_panic(df)
print(f"模式一:追涨杀跌")
print(f" 最终净值: {eq1.iloc[-1]:.4f}")
print(f" 累计收益: {eq1.iloc[-1] - 1:+.2%}")
print(f" 最大回撤: {(eq1 / eq1.cummax() - 1).min():.2%}")
print()
为什么追涨杀跌亏钱
- 连涨 3 天后你买入,大概率买在了短期高点附近
- 连跌 3 天后你卖出,大概率卖在了短期低点附近
- 你总是在别人贪婪的时候才入场,在别人恐惧的时候才离场
- 经典的"买在山顶、卖在谷底"
模式二:频繁换股
“这只不涨就换另一只,总有一只会涨。”
规则:持有一只票 5 天不涨,就换成另一只。我们模拟从一组股票中反复切换:
import pandas as pd
import numpy as np
from alphafeed import AlphaFeed
af = AlphaFeed()
stock_pool = ["600519.SH", "000001.SZ", "601318.SH", "300750.SZ",
"002594.SZ", "000858.SZ", "600036.SH", "000333.SZ"]
klines = af.klines.batch(
stock_pool, period="1d", count=2000,
adjust="forward", to_dataframe=True,
)
# 准备收益率矩阵
returns_dict = {}
for sym, kdf in klines.items():
kdf = kdf.sort_values("trade_date").set_index("trade_date")
returns_dict[sym] = kdf["close"].pct_change()
returns_df = pd.DataFrame(returns_dict).dropna()
def simulate_frequent_switch(returns_df: pd.DataFrame, patience_days: int = 5) -> pd.Series:
np.random.seed(42)
symbols = returns_df.columns.tolist()
current_stock = np.random.choice(symbols)
hold_days = 0
equity = [1.0]
for i in range(len(returns_df)):
ret = returns_df[current_stock].iloc[i]
equity.append(equity[-1] * (1 + ret - 0.002)) # 每次交易扣成本
hold_days += 1
if hold_days >= patience_days:
cum_ret = equity[-1] / equity[-patience_days - 1] - 1 if len(equity) > patience_days else 0
if cum_ret < 0.01: # 5天涨不到1%就换
other_stocks = [s for s in symbols if s != current_stock]
current_stock = np.random.choice(other_stocks)
hold_days = 0
return pd.Series(equity)
eq2 = simulate_frequent_switch(returns_df, patience_days=5)
eq_hold = (1 + returns_df.mean(axis=1)).cumprod() # 基准:等权持有所有
print(f"模式二:频繁换股(5天不涨就换)")
print(f" 最终净值: {eq2.iloc[-1]:.4f}")
print(f" 累计收益: {eq2.iloc[-1] - 1:+.2%}")
print(f" 等权持有: {eq_hold.iloc[-1] - 1:+.2%}")
print()
为什么频繁换股亏钱
- 每次换股都有交易成本(佣金+印花税),换 100 次就是好几个百分点
- "5 天没涨"不代表这只票不好——也许第 6 天就起飞了,但你已经换走了
- 你总是换到"最近涨的"票上,但最近涨的往往是短期涨多了的
- 研究表明,个人投资者换股的选择几乎没有超额收益,但换股本身确实在产生成本
模式三:满仓梭哈、单吊一只
“我就看好这只!全仓干!”
规则:把 100% 的钱押在一只股票上。我们对比一下全押单只 vs 分散到 5 只的差异:
import pandas as pd
import numpy as np
from alphafeed import AlphaFeed
af = AlphaFeed()
stock_pool = ["600519.SH", "000001.SZ", "601318.SH", "300750.SZ", "002594.SZ"]
klines = af.klines.batch(
stock_pool, period="1d", count=2000,
adjust="forward", to_dataframe=True,
)
returns_dict = {}
for sym, kdf in klines.items():
kdf = kdf.sort_values("trade_date").set_index("trade_date")
returns_dict[sym] = kdf["close"].pct_change()
returns_df = pd.DataFrame(returns_dict).dropna()
print("模式三:全仓单吊 vs 分散持有")
print()
for sym in stock_pool:
single_eq = (1 + returns_df[sym]).cumprod()
max_dd = (single_eq / single_eq.cummax() - 1).min()
print(f" 全仓 {sym}: 收益{single_eq.iloc[-1]-1:+.2%} 最大回撤{max_dd:.2%}")
# 等权分散
equal_ret = returns_df.mean(axis=1)
equal_eq = (1 + equal_ret).cumprod()
equal_dd = (equal_eq / equal_eq.cummax() - 1).min()
print(f"\n 等权分散5只: 收益{equal_eq.iloc[-1]-1:+.2%} 最大回撤{equal_dd:.2%}")
全仓单吊的问题
- 如果押对了(比如全仓茅台),收益确实比分散好
- 但如果押错了,你的回撤可能是 40%–60%
- 关键不是期望收益,而是你能不能拿住。 回撤 50% 意味着你需要涨 100% 才能回本。大多数人在回撤 20% 的时候就会割肉,然后去追下一只
- 等权分散的收益可能不是最高的,但回撤通常是最小的
模式四:死扛不止损
“亏了就装死,总有一天会涨回来。”
规则:买入后不管跌多少都不卖。我们来看"死扛"的真实代价——用时间衡量:
import pandas as pd
import numpy as np
from alphafeed import AlphaFeed
af = AlphaFeed()
df = af.klines.get(
"000001.SZ", period="1d", count=5000,
adjust="forward", to_dataframe=True,
)
df = df.sort_values("trade_date").reset_index(drop=True)
# 模拟:在每一天买入,计算需要多少天才能回本
recovery_days = []
for buy_idx in range(0, len(df) - 1, 5): # 每5天采样一次
buy_price = df["close"].iloc[buy_idx]
recovered = False
for future_idx in range(buy_idx + 1, len(df)):
if df["close"].iloc[future_idx] >= buy_price:
days = future_idx - buy_idx
recovery_days.append({
"buy_date": df["trade_date"].iloc[buy_idx],
"buy_price": buy_price,
"recovery_days": days,
"max_drawdown": (df["close"].iloc[buy_idx:future_idx+1].min() / buy_price - 1),
})
recovered = True
break
if not recovered:
recovery_days.append({
"buy_date": df["trade_date"].iloc[buy_idx],
"buy_price": buy_price,
"recovery_days": None,
"max_drawdown": (df["close"].iloc[buy_idx:].min() / buy_price - 1),
})
rec_df = pd.DataFrame(recovery_days)
recovered = rec_df[rec_df["recovery_days"].notna()]
not_recovered = rec_df[rec_df["recovery_days"].isna()]
print(f"模式四:死扛不止损(000001.SZ)")
print(f" 采样买入点: {len(rec_df)} 个")
print(f" 最终回本率: {len(recovered)/len(rec_df):.1%}")
print(f" 至今仍套牢: {len(not_recovered)} 个买入点")
print()
if len(recovered) > 0:
print(f" 回本天数统计(已回本的):")
print(f" 平均: {recovered['recovery_days'].mean():.0f} 天")
print(f" 中位数: {recovered['recovery_days'].median():.0f} 天")
print(f" 最长: {recovered['recovery_days'].max():.0f} 天")
print(f" 90分位: {recovered['recovery_days'].quantile(0.9):.0f} 天")
print()
print(f" 套牢期间最大浮亏:")
print(f" 平均: {rec_df['max_drawdown'].mean():.1%}")
print(f" 最惨: {rec_df['max_drawdown'].min():.1%}")
死扛的真实代价
- 不是"总能涨回来"——有些票跌了之后很多年都回不到你的买入价
- 即使最终回本了,你的时间成本巨大。资金被套 2 年等于你损失了 2 年的机会成本
- 死扛期间的心理压力会严重影响你对其他机会的判断
- 更好的选择不是"死扛",也不是"一跌就跑",而是在买入前就定好止损位
模式五:听消息炒股
“群里大佬说这只票要涨,跟!”
规则:模拟"随机选股"——每次随机买一只,持有 10 天卖出。因为消息面选股本质上就是随机的(你没法验证"大佬"的消息是真是假)。
import pandas as pd
import numpy as np
from alphafeed import AlphaFeed
af = AlphaFeed()
stock_pool = [
"600519.SH", "000001.SZ", "601318.SH", "300750.SZ", "002594.SZ",
"000858.SZ", "600036.SH", "000333.SZ", "601012.SH", "600276.SH",
"002415.SZ", "300059.SZ", "600809.SH", "000651.SZ", "300124.SZ",
]
klines = af.klines.batch(
stock_pool, period="1d", count=2000,
adjust="forward", to_dataframe=True,
)
returns_dict = {}
for sym, kdf in klines.items():
kdf = kdf.sort_values("trade_date").set_index("trade_date")
returns_dict[sym] = kdf["close"].pct_change()
returns_df = pd.DataFrame(returns_dict).dropna()
def simulate_random_tips(returns_df, hold_days=10, n_simulations=1000):
"""模拟 1000 次"听消息"操作,看平均结果"""
symbols = returns_df.columns.tolist()
final_returns = []
for _ in range(n_simulations):
equity = 1.0
i = 0
while i < len(returns_df) - hold_days:
stock = np.random.choice(symbols)
period_ret = returns_df[stock].iloc[i:i+hold_days].sum()
cost = 0.003 # 买卖各一次
equity *= (1 + period_ret - cost)
i += hold_days
final_returns.append(equity - 1)
return final_returns
np.random.seed(42)
sim_results = simulate_random_tips(returns_df, hold_days=10, n_simulations=1000)
sim_series = pd.Series(sim_results)
# 同期基准
buyhold_ret = (1 + returns_df.mean(axis=1)).cumprod().iloc[-1] - 1
print(f"模式五:听消息炒股(1000 次模拟)")
print(f" 平均收益: {sim_series.mean():+.2%}")
print(f" 中位数收益: {sim_series.median():+.2%}")
print(f" 胜率: {(sim_series > 0).mean():.1%}")
print(f" 最好情况: {sim_series.max():+.2%}")
print(f" 最差情况: {sim_series.min():+.2%}")
print(f" 等权持有基准: {buyhold_ret:+.2%}")
print()
print(f" 赚到钱的人: {(sim_series > 0).sum()}/1000")
print(f" 跑赢基准的: {(sim_series > buyhold_ret).sum()}/1000")
听消息的问题
- 模拟 1000 次"随机选股",大部分人的收益低于等权持有
- 交易成本是确定的(每次都要付),但收益是不确定的
- 即使某次碰巧赚了大钱,那也是运气,不可复制
- "群里大佬"的消息即使是真的,等你看到的时候行情已经走了一半了
综合对比
把 5 种操作模式的结果放在一起看:
print("=" * 60)
print(" 五种散户操作模式 vs 买入持有")
print("=" * 60)
print(f" {'模式':>20s} {'累计收益':>10s} {'最大回撤':>10s}")
print("-" * 60)
print(f" {'买入持有(基准)':>20s} {bh_final-1:>+10.2%} {'—':>10s}")
print(f" {'追涨杀跌':>20s} {eq1.iloc[-1]-1:>+10.2%} {(eq1/eq1.cummax()-1).min():>10.2%}")
print(f" {'频繁换股':>20s} {eq2.iloc[-1]-1:>+10.2%} {'—':>10s}")
# ... 其他模式类似
print("=" * 60)
在绝大多数时间段里,这五种操作的表现都不如"买入沪深 300 什么都不做"。
那散户应该怎么做
这篇文章不是说"散户就该躺平"。而是说,如果你在做以上 5 种操作中的任何一种,你至少应该知道数据上它是什么结果。
比较靠谱的替代方案:
| 常见错误 | 替代方案 |
|---|---|
| 追涨杀跌 | 定一个规则(比如均线方向),严格执行,不看情绪 |
| 频繁换股 | 给每只票至少 1 个月的观察期,减少无意义交易 |
| 全仓单吊 | 任何一只票不超过 30% 仓位 |
| 死扛不止损 | 买入前就定好止损位(比如亏 8% 走人) |
| 听消息 | 不买不了解的票;如果想省事就买指数 ETF |
这些替代方案不保证赚钱,但可以避免最大的亏损来源。
用 AlphaFeed 做你自己的操作回测
你可以用同样的方法回测自己的操作习惯。比如你怀疑自己"总是在不该卖的时候卖",可以这样验证:
# 把你过去的交易记录整理成列表
my_trades = [
{"symbol": "600519.SH", "buy_date": "2024-01-15", "sell_date": "2024-03-10"},
{"symbol": "300750.SZ", "buy_date": "2024-02-20", "sell_date": "2024-04-05"},
# ...
]
# 拉数据,算每笔交易的实际收益,再算如果不卖会怎样
from alphafeed import AlphaFeed
af = AlphaFeed()
for trade in my_trades:
df = af.klines.get(
trade["symbol"], period="1d", count=500,
adjust="forward", to_dataframe=True,
)
df = df.sort_values("trade_date").reset_index(drop=True)
df["trade_date"] = df["trade_date"].astype(str)
buy_row = df[df["trade_date"] == trade["buy_date"]]
sell_row = df[df["trade_date"] == trade["sell_date"]]
latest = df.iloc[-1]
if buy_row.empty or sell_row.empty:
continue
actual_ret = sell_row["close"].values[0] / buy_row["close"].values[0] - 1
if_held_ret = latest["close"] / buy_row["close"].values[0] - 1
print(f"{trade['symbol']}: 实际{actual_ret:+.2%} 如果拿到今天{if_held_ret:+.2%} "
f"{'卖早了' if if_held_ret > actual_ret else '卖对了'}")
这个分析可能会让你很不舒服——但正视数据是改善投资决策的第一步。
结语
散户亏钱不是因为"运气差"或者"庄家狡猾"。大多数时候,是自己的操作习惯在系统性地消耗本金。
追涨杀跌让你买高卖低。频繁换股让你付出大量交易成本。全仓单吊让你承受不可承受的波动。死扛不止损让你的资金和时间一起被套牢。听消息炒股让你把决策权交给了陌生人。
好消息是,这些问题都是可以用数据发现、用规则改善的。你不需要成为量化高手,只需要愿意跑一下数据,正视结果,然后给自己定几条简单的规则并严格执行。
AlphaFeed 的价值在这里很明确:它让你可以用几行 Python 回测任何操作假设。当你不确定"这样做到底对不对"的时候,不要问别人——问数据。
参考文献:
- AlphaFeed 官网:https://alphafeed.org/
- Python SDK 快速开始:https://docs.alphafeed.org/zh-Hans/sdk/python-quickstart
更多推荐


所有评论(0)