基于 William J. O'Neil,《24条投资成功必备教训》(2000年)
William J. O'Neil 是一位股票交易员、企业家兼作家,创办了《投资者商业日报》(IBD)和机构研究公司 William O'Neil + Co.。他的业绩记录包括:
O'Neil的研究方法详尽且实证。他不是从理论出发并寻找确认证据。他研究了1880年代以来每个市场周期中每只表现最佳的股票,记录了这些股票在重大上涨之前共享的共同特征,并从出现的模式中构建了可重复的系统。
《24条投资成功必备教训》是O'Neil最精简、最易读的作品。《如何靠股票赚钱》是综合参考,而本书则将CAN SLIM系统提炼为24条重点的、可操作的教训——每条都短到可以一次读完,每条都旨在教授一个投资者可以立即应用的具体概念。
本书以工作手册的形式组织。每条教训以复习题结尾。语气直接且实用:O'Neil不在金融理论、有效市场论点或对冲语言上浪费篇幅。他说明什么有效,什么无效,以及投资者必须做什么。
O'Neil的哲学建立在三个支柱上:
1. 事实胜于观点 — 根据实际发生的事情(盈利、价格、成交量)买入股票,
而非你认为应该发生的事或分析师预测的事。
2. 历史重演 — 百年来赢家股票的特征非常一致。
模式是可学习的和可重复的。
3. 止损持盈 — 最重要的单一规则。
系统中每条其他规则的存在,都是为了让你进入那些很少触发这条规则的股票——
但当它被触发时,你必须毫无例外地遵守。
O'Neil反复强调,股票市场不是智力练习。它是一门实践学科。研究最多历史模型、遵守最严格规则、控制情绪最有效的投资者,将超越拥有最高智商或最复杂金融理论的投资者。
CAN SLIM是一个七因素检查表。每个字母代表最强股票在实现最大价格涨幅之前表现出的一个基本特征。O'Neil从研究数十年间每只上涨200%或更多的股票中得出这些因素。
该系统不是加分模型——不是将分数加起来。每个股票必须通过所有七个标准才能合格作为买入候选。任何单个因素的弱点都是拒绝的理由。
规则:当前季度EPS必须比去年同期同一季度增长至少25%。
最好的股票显示50%-100%+的季度盈利增长。
详情:
- 与去年同期同一季度比较(今年Q3 vs 去年Q3)以消除季节性扭曲。
- 增长必须来自核心业务,而非一次性收益、税收优惠或会计变更。检查脚注。
- 加速增长优于稳定增长。显示15% -> 20% -> 35% -> 60%季度增长的股票,
比显示30% -> 30% -> 30% -> 30%的股票更有说服力。
- 营收增长应确认盈利增长。如果EPS上涨40%但营收持平,
盈利增长来自成本削减或回购——这较不可持续。
- 预期很重要:股票应该超越分析师预期,而非仅仅达到预期。
不合格情形:
- 季度盈利增长低于25%。
- 盈利增长完全由成本 Reduction且无营收增长驱动。
- 盈利趋势减速(例如50% -> 40% -> 30% -> 20%)。
- 一次性收益夸大盈利数字。
规则:年度EPS应在过去5年中每年显示25%或以上的增长率。
或者,最近2-3年可以显示强劲加速。
详情:
- 理想的股票同时显示强劲的5年盈利增长率和加速的近期季度增长。
这种组合表示公司进入扩张新阶段。
- 净资产收益率(ROE)应达到17%或更高。高ROE表示公司在其使用的资本上产生优越回报。
- 年度盈利应显示一致的上升轨迹。像$1.00、$1.50、$2.00、$1.20、$3.00这样的模式
是不稳定和不可靠的——第4年的下降提出了问题。
- 寻找达到或接近新高的年度盈利。盈利低于峰值年份的公司处于恢复中,而非增长中。
不合格情形:
- 过去3-5年年度增长率低于25%。
- 中间有大幅下降的不稳定年度盈利。
- ROE低于17%。
- 盈利严重依赖单一产品、客户或合同。
规则:股票应该有"新"的催化剂——新产品或服务、新管理层、新行业条件——
并且股票应该正在创新高或接近新高。
详情:
- O'Neil的历史研究发现,95%的最大赢家有真正推动其增长的新事物。
这不是模糊的乐观——而是一个具体的、可识别的催化剂。
- 历史例子:苹果的iPhone、Google的搜索广告主导地位、
思科在互联网建设期间的网络设备、家得宝的仓库零售概念。
- "新高"部分违反直觉但至关重要:创新高的股票往往继续走高。
创新低的股票往往继续走低。大多数投资者搞反了——
他们在一堆下跌的股票中寻找"便宜货"。
- 股票从适当的基底形态中走出并进入新高区域、伴随强劲成交量,
正是重大上涨之前的精确行为。
不合格情形:
- 没有可识别的催化剂或竞争优势。
- 股票远低于52周高点且趋势向下。
- "转折"故事没有实际改善的数字证据。
规则:寻找流通股数合理的股票。监控成交量以发现机构积累的迹象。
详情:
- 流通股:流通股较少的股票(低于2-3亿股)往往产生更大波动,
因为推动价格所需的买入压力较小。
- 这并不意味着你只应该买小型股。O'Neil最好的例子中许多有5000-20000万股流通股——
对机构持有来说足够大,但对有意义的价格变动来说足够小。
- 成交量是供需动态的关键指标:
- 积累:价格在上证均量上涨=机构正在买入。
- 分销:价格在下沉均量下跌=机构正在卖出。
- 突破日成交量应至少比50日平均量高50%。
- 寻找股票在价格上涨时成交量增加、在基底内价格回调时成交量减少的模式。
关键信号:
- 突破时成交量比平均量高50%以上 -> 需求强劲,买入信号
- 价格下跌时成交量沉重 -> 供应压倒需求,谨慎
- 价格上升时成交量下降 -> 反弹失去确信度,谨慎
- 基底内价格走势紧密、成交量极低 -> 供应已枯竭,准备变动
规则:买入行业领导者群体中排名第1或第2的股票。
股票的相对价格强度(RS)评级应达到80或更高——理想情况下90+。
详情:
- 相对强度衡量股票在过去12个月中与市场中所有其他股票相比的价格表现,
以1-99等级表示。RS为90意味着该股票在过去一年中跑赢了90%的所有股票。
- O'Neil发现,最大赢家在主要上涨开始前的平均RS评级为87。
这些已经是强劲的股票,然后变得更强。
- 行业群体强度非常重要。O'Neil的研究表明,
约37%的股票价格变动可归因于其行业群体,另有12%归因于整体板块。
这意味着近一半的股票变动是由公司自身以外的因素决定的。
- 买领导者,卖落后者。如果同一行业的两只股票满足所有其他CAN SLIM标准,
买RS评级较高的那只。
不合格情形:
- RS评级低于80。
- 处于落后行业群体(后四分位)。
- 股票一直跑输其行业同行。
- "同情游戏"——因为领导者看起来"太贵"而购买组内排名第4或第5的股票。
规则:股票应该获得优质机构越来越多的所有权。
至少一些具有顶级业绩记录的共同基金应该持有该股票。
详情:
- 机构买家(共同基金、养老基金、对冲基金)提供推动股票显著上涨所需的持续买入力量。
仅靠个人投资者无法创造重大价格变动。
- 寻找机构所有者数量较上一季度增加的情况。
股票从150个增加到200个再到260个机构持有者,显示积累。
- 质量比数量更重要。十家顶级表现共同基金持有股票,
比500家中等基金持有股票是更强的信号。
- 检查近年来表现最好的成长型基金是否一直在增持该股票。
持续跑赢市场的基金经理,更可能有识别未来赢家的那种研究。
- 避免被太广泛持有的股票。如果每个主要机构已经持有该股票,
谁还有可能买入?持有股票的最佳时机是机构所有权在增加但尚未普遍的时候。
不合格情形:
- 没有机构所有权或机构持有者数量下降。
- 只有低质量或表现不佳的基金持有该股票。
- 机构所有权较上一季度下降(分销)。
- 股票已被实际上每个主要基金持有(饱和)。
规则:四分之三的股票跟随总体市场方向。
在做出任何买入决定之前,你必须评估总体市场。
详情:
- 即使是最好的CAN SLIM股票在下跌市场中也会挣扎。
O'Neil估计,在熊市或重大调整期间,75%的所有股票都会下跌。
- 每天跟踪主要指数(标普500、纳斯达克综合指数)的价格和成交量行动。
这是必须的。
- 分销日:当主要指数成交量高于前一天地下跌时,发生分销日。
这表示机构卖出。
- 在2-3周内出现4-5个分销日是严重警告信号。
- 在确认的分销日群之后,减少敞口并停止新买入。
- 跟进日:市场调整后,跟进日确认新的上升趋势已经开始。
它发生在反弹尝试的第4天或更晚,当主要指数在成交量高于前一天的情况下
上涨至少1.5%-2%。
- 并非每个跟进日都导致持续反弹,但每个持续反弹都从一个跟进日开始。
- 在确认的上升趋势中:当CAN SLIM股票设置时,积极买入。
- 在确认的下降趋势中:持有现金。不要试图"抄底"。
市场敞口指南:
确认上升趋势 -> 80-100%已投资,积极买入突破
上升趋势承压 -> 50-70%已投资,不新买入,收紧止损
市场调整中 -> 0-25%已投资mostly现金,卖出弱势持仓
确认下降趋势 -> 0%已投资Fully现金,等待跟进日
O'Neil强调,图表阅读不是算命——它是可视化股票供需动态的一种方式。价格和成交量告诉你机构实际上在用他们的钱做什么,无论他们在公开场合说什么。
杯柄是 最常见和最可靠的基底形态。O'Neil在大多数最大赢家股票的主要上涨之前发现了它。
杯柄形态解剖:
价格
|
| 左杯边 右杯边
| \ /
| \ / ___ <- 柄(浅回调)
| \ / _/ \___
| \ / / | <- 买入点(支点)
| \ / / |
| \_______/ <- 底部 |
| 杯 |
| |
+-------------------------------+-----> 时间
7至65周
规格:
- 杯深度: 从峰值到谷底的12%至33%(理想情况下低于25%)
深于33% = 损伤太大,不太可能成功
- 杯长度: 最短7周,可延长至65周或更长
短于7周 = 没有足够时间淘汰弱势持有者
- 柄: 在杯的上半部形成
应该略微向下漂移(不是向上——向上是"楔形")
深度应最多8-12%
持续时间:至少1-2周
- 成交量: 应该在杯底和柄部干涸
这表示卖出压力已耗尽
- 买入点: 柄的最高价加上$0.10
突破必须发生在成交量至少比平均量高50%的日子
双底形态解剖:
价格
|
| 第一次 第二次
| 下跌 下跌
| \ /\ \
| \ / \ \ /
| \/ \ \ / <- 突破
| 第一次 \ \ /
| 底部 \ \/
| \ 第二次底部
| \ (略低于第一次底部)
| 中间峰值
+-------------------------------------> 时间
规格:
- 形状: 像字母"W"
- 持续时间: 最短7周
- 深度: 与杯类似——12%至33%的回调
- 第二次底部: 应该略低于第一次底部(1-2%)
这淘汰弱势持有者的震荡是形态工作的原因
第二次底部在较低成交量上是理想的
- 买入点: "W"的中间峰值加上$0.10
- 成交量: 突破时必须沉重(比平均量高50%+)
平底解剖:
价格
|
| ___________________________________
| | 紧密交易区间 | <- 买入点(区间顶部 + $0.10)
| | _ __ _ __ __ _ |
| |__/ \__/ \___/ \_/ \__/ \_/ \__|
|
| 最短5周
+---------------------------------------------> 时间
规格:
- 回调: 从基底高点到低点的不超过10-15%
- 持续时间: 最短5周
- 背景: 通常形成于股票已经从先前突破上涨20%+之后——
它是现有上升趋势中的停顿
- 成交量: 在基底内应该安静且有序
- 买入点: 平底的高点加上$0.10
- 意义:平底之所以强大,是因为它们表明,即使在显著上涨之后,
实际上没有卖出压力。持有者不获利了结——他们预期更多上涨。
在任何基底内观察的内容:
健康的基底:
- 成交量在价格向下回调时收缩 = 卖出压力干涸
- 成交量在基底内的上涨日扩张 = 安静积累
- 柄/震荡区域成交量极低 = 最终弱势持有者已离开
- 突破日成交量比50日平均量高50-100%+ = 机构承诺
不健康的基底:
- 成交量在基底内的下跌日扩张 = 主动分销
- 成交量在基底内的上涨日收缩 = 没有机构兴趣
- 宽幅、松散的价格波动在沉重成交量上 = 太多波动性
- 在低于平均量的成交量上突破 = 没有确信度,可能失败
O'Neil以数学精度定义买入点(支点)。没有模糊性。
买入点规则:
1. 支点价格 = 柄的最高价(杯柄)
或中间峰值(双底)
或区间顶部(平底)
加上$0.10
2. 买入区 = 从支点价格到支点价格以上5%
在支点以上超过5%买入 = "追逐" = 更高风险
在延伸位置买入的股票更可能触发止损
3. 成交量 = 突破日必须至少比50日平均量高50%
如果股票在低于平均量的成交量上穿越支点,不要买入
等待——它可能回调提供另一个入场点,或者可能失败
4. 市场 = 总体市场必须在确认的上升趋势中
不要在市场调整期间买入突破
5. 时机 = 尽可能接近支点买入
最佳时机是突破日的最初1-2小时交易
如果股票大幅跳空高于买入区,等待回调——不要追逐
金字塔规则:
- 你最大的持仓应该是支点处的初始买入。
- 只有在股票走势对你有利时才加仓。
- 绝不要向亏损持仓加仓。这是均价向下,是禁止的。
金字塔示例:
初始买入在支点: 占预期总持仓的50%
第一次加仓(支点以上+2.5%): 占预期总持仓的30%
第二次加仓(支点以上+5%): 占预期总持仓的20%
随着股票通过走高确认而建立总持仓。
平均成本保持在支点附近。
如果股票在初始买入后立即失败,你只亏损50%,而非100%。
这是整个CAN SLIM系统中最重要的单一规则。O'Neil毫无保留地声明: 卖出任何从买入价下跌7-8%的股票,毫无疑问。
绝对止损规则:
如果股票价格 <= (买入价 * 0.92)
则立即卖出全部持仓
没有例外。没有希望。没有等待反弹。没有查看新闻。
为什么是7-8%?
- O'Neil的研究表明,从适当基底突破然后跌至支点以下7-8%的股票,
很少恢复成为大赢家。基底已经失败。
- 在7-8%止损,三次连续亏损只消耗约23%的资本。
这是可恢复的。50%的亏损需要100%的收益才能恢复。
- 该规则让你保持在游戏中。允许一次亏损变成25%、40%或60%的投资者,
可能永远无法恢复——财务上或心理上。
亏损计算:
亏损 需要恢复的收益
-7% +7.5%
-8% +8.7%
-10% +11.1%
-20% +25.0%
-33% +50.0%
-50% +100.0%
-75% +300.0%
执行:
- 买入时设定心理或实际止损单。
- 如果使用心理止损,至少在交易日内检查一次股票。
- 当阈值被触发时,按市价卖出。不要在止损时使用限价单——你想要成交,而非特定价格。
- 永远不要基于新闻、分析师升级或"直觉"覆盖此规则。
7-8%规则是最大可容忍亏损。经验丰富的投资者往往更早卖出:
如果出现以下情况更早卖出:
- 股票在低于平均量的成交量上突破然后停滞 -> 在-3%至-4%卖出
- 股票在突破后5-7个交易日内没有向上进展 -> 重新评估
- 总体市场从"确认上升趋势"转变为"上升趋势承压"
- 股票的相对强度线下降,即使价格保持 -> 失去领导地位
- 发生沉重成交量卖出(股票本身的分销日)
规则:当股票从买入点获得20-25%收益时,卖出大部分股票。
为什么?
- O'Neil观察到,许多股票从适当基底突破后上涨20-25%,
然后建立新基底(整合)或回调。
在此水平获利了结,可以在不可避免的回调之前锁定收益。
- 这与7-8%止损规则配对,创造有利的风险/回报比:
如果你在赢家常赚20%而在输家常亏7%,
你错4次中的3次仍可大致持平。
如果你的胜率是50%或更好,你就在复利财富。
例外——8周持有规则:
- 如果股票在前3周内上涨20%或更多,它是特别强劲的。
至少从突破日起持有8周后再做任何卖出决定。
- 这些快速移动者是潜在的"大领导者"——
可能上涨100%、200%或更多的股票。
过早卖出是成长型投资者最昂贵的错误。
- 8周后,评估:如果股票表现良好(保持收益、强劲RS、没有沉重分销),
继续持有并使用更宽的追踪止损。
卖出或收紧止损当:
- 股票达到20-25%收益(标准利润目标)
- 高潮顶部:股票在数月上涨后在1-3周内飙升25-50%(耗尽)
- 从上涨开始以来的最大单日点数增益(高潮走势)
- 股票在接近顶部时成交量扩大、价格spread扩大而上涨
- 股票已上涨数月并建立宽幅、松散的晚期基底
(第4阶段基底或更晚 = 高失败率)
- 盈利增长连续2个季度减速
- 股票在漫长上涨后在沉重成交量上跌破10周移动均线
- 相对强度线从其自身上升趋势中破位
- 总体市场进入调整(主动卖出,不要等待止损)
O'Neil创办《投资者商业日报》,专门提供CAN SLIM投资者所需的数据。关键评级是:
EPS评级(1-99):
衡量公司过去3-5年的盈利增长率加上最近季度的盈利稳定性。
95的评级意味着该公司的盈利记录优于95%的所有公开交易公司。
最低:80。理想情况下85+。
相对强度(RS)评级(1-99):
衡量股票过去12个月与所有其他股票相比的价格表现。
RS为90意味着该股票跑赢了90%的所有股票。
最低:80。理想情况下85+。最好的股票在买入点通常有RS 90+。
行业群体相对强度(A至E):
将股票的产业群体与IBD跟踪的所有197个产业群体进行排名。
A = 前20%,B = 20-40%,C = 40-60%,D = 60-80%,E = 后20%。
最低:A或B。
积累/分销评级(A至E):
基于过去13周的价格和成交量分析。
A或B = 净积累(机构买入多于卖出)。
D或E = 净分销(卖出多于买入)。
最低:A、B或C。
综合评级(1-99):
EPS、RS和其他因素的加权组合成单一分数。
最低:90+。
SMR评级(A至E):
销售额增长、利润率和净资产收益率的组合。
A = 前20%。最低:A或B。
O'Neil用整节课专门阅读市场的每日价格和成交量行动。 IBD的大局栏跟踪市场状态:
市场状态类别:
"确认上升趋势" -> 积极买入CAN SLIM突破
"上升趋势承压" -> 停止新买入,收紧现有持仓止损
"市场调整中" -> 持有现金,不买入,卖出弱势持仓
"反弹尝试" -> 寻找跟进日,准备买入清单
每日例程:
1. 检查市场状态(大局)——决定你是买入还是防守。
2. 审查IBD 50 / IBD大蓝筹20,寻找符合CAN SLIM标准的股票。
3. 筛选具有以下条件的股票:EPS >= 80,RS >= 80,综合评级 >= 90,群体排名A或B。
4. 对于每个候选,拉出周线图:
a. 它处于第2阶段上升趋势吗(高于上升的30周移动均线)?
b. 它正在形成可识别的基底形态(杯、双底、平底)吗?
c. 它离买入点多近?
5. 将候选放入观察清单,并注明计算的买入点。
6. 当市场处于确认上升趋势且观察清单股票在成交量上达到支点时,执行买入。
O'Neil用多节课讨论导致大多数投资者失败的错误。他直言不讳:大多数投资者亏损,不是因为市场不公平,而是因为他们反复违反基本规则。
错误:在一只股票下跌时买入更多股票,以"降低平均成本"。
为什么是致命的:
- 你在向亏损持仓追加资金。股票下跌是有原因的——
机构在卖出,盈利故事在恶化,或市场疲软。
- 均值向下摊平将小亏损变成灾难性亏损。
如果投资者在$50买入,在$45加仓,在$40再加仓,
最后在$30清仓,他比在$46简单卖出第一笔的人破坏得多得多资本。
- O'Neil:"在股票市场赢钱的全部秘密是,当你不正确时,损失尽可能少。"
- 规则:无论什么情况,绝不要向亏损持仓加仓。
错误:因为股票从高点下跌30%、40%或50%而买入,因此看起来"便宜"或"打折"。
为什么失败:
- 下跌50%的股票不是"打折"。它是破裂的。基本面发生了变化——
机构持有者在清算,盈利在恶化,或行业在衰退。
- 曾经是$100的$50股票很容易跌到$25,然后$10,然后$5。
没有任何法律说股票必须恢复。
- O'Neil的数据显示,最大赢家几乎从未在低点买入。
它们是在接近新高时从基底中走出时买入的。
- 规则:上涨时买入,而非下跌时买入。买强势,而非弱势。
错误:卖出盈利股票以"锁定利润",同时持有亏损股票希望它们"回来"。
为什么是本末倒置:
- 这种行为保证组合越来越集中于输家。
- 心理学:卖出赢家感觉很好(你实现了利润)。
卖出输家感觉糟糕(你承认了错误)。所以大多数投资者做感觉好的事。
- O'Neil:这完全是本末倒置。你的赢家在上涨因为它们是好股票。
你的输家在下跌因为它们是坏股票。你应该持有前者,卖出后者。
- 规则:快速止损。让赢家奔跑。
- 基于消息、谣言或分析师推荐而非自己做分析来买入。
- 购买低价股票(低于$10-15)。低价股票通常有低价的原因。
O'Neil最大的赢家在买入点通常是$30-$80。
- 过度分散化。持有25-30只股票意味着你有共同基金,而非投资组合。
O'Neil建议大多数投资者4-8只股票。
- 因为股票看起来"太高"而拒绝买入股票。
如果它满足所有CAN SLIM标准且处于正确买入点,价格不是"太高"。
- 交易太频繁。CAN SLIM不是日内交易。让赢家持有数周或数月。
- 不研究历史模型。O'Neil坚持投资者至少研究100个赢家股票的历史例子以训练模式识别。
- 忽略市场方向。在下降趋势中买入是亏钱最快的方式。
O'Neil的24条教训每条都映射到特定的CAN SLIM组件和交易纪律:
教训 标题/主题 CAN SLIM链接
------ ------------------------------------------------ ----------------
1 如何找到赢家股票(CAN SLIM介绍) 所有因素
2 "C" — 当前季度盈利 C
3 "A" — 年度盈利增长 A
4 "N" — 新产品、高点、管理层 N
5 "S" — 供需分析 S
6 "L" — 领导者 vs 落后者 L
7 "I" — 机构支持 I
8 "M" — 市场方向 M
9 何时卖出并止损 风险管理
10 何时卖出并获利 获利了结
11 如何阅读图表:杯柄 技术分析
12 如何阅读图表:其他基底形态 技术分析
13 如何使用IBD SmartSelect评级 工具
14 如何使用行业群体分析 L、I
15 如何评估总体市场 M
16 如何避免最常见的投资者错误 心理学
17 应该分散还是集中? 仓位确定
18 如何使用IBD屏幕寻找新想法 筛选
19 应该买期权、场外股票或新股吗? 特殊情况
20 分析共同基金业绩 I
21 改进每日投资例程 流程
22 需要关注的重要经济指标 M、宏观
23 如何选择最佳市场板块 L、板块轮动
24 回顾并整合一切 系统整合
本示例完整走通从初步识别到退出的CAN SLIM交易。
=== 阶段1:市场评估 ===
日期:3月初(假设)
市场状态:"确认上升趋势"——跟进日在6个交易日前发生。
在过去25个交易日中,标普500只有1个分销日。
纳斯达克创新高。涨跌线改善。
决策:市场环境有利。积极寻找CAN SLIM买入候选。
=== 阶段2:股票识别 ===
股票:TECH Corp(代码:TECH),交易价格$62
来源:连续两周出现在IBD 50名单上。
基本面检查(CAN SLIM:C、A、I):
当前季度EPS: +58% vs去年同期同一季度 (C = 通过,>25%)
上季度EPS: +43% (加速)
年度EPS增长: 5年来+32% (A = 通过,>25%)
ROE: 24% (通过,>17%)
营收增长: 最近季度+35% (确认盈利)
EPS评级: 96 (通过,>80)
SMR评级: A (通过)
机构赞助商: 385只基金,较上季度从310只增加 (I = 通过,增加中)
顶级基金持有: 富达反向基金增持股份 (质量确认)
技术检查(CAN SLIM:N、S、L):
催化剂: 上季度推出新型AI驱动产品,推动营收加速 (N = 通过)
RS评级: 94 (L = 通过,>80)
行业群体排名: 197个群体中第8名(前5%) (L = 通过)
积累/分销评级: A (S = 通过)
基底形态: 14周杯柄
杯深度:22%(可接受,低于33%)
柄在下降成交量上向下漂移
买入点: $64.10(柄高点$64.00 + $0.10)
决策:TECH通过所有7个CAN SLIM因素。添加到观察清单,买入点$64.10。
=== 阶段3:入场 ===
日期:3月18日
行动:TECH在上午10:15以成交量达到50日平均量2.3倍的情况下穿越$64.10。
买入400股,价格$64.25(在支点5%以内)
总投资:$25,700
止损设定:$59.11(买入价$64.25以下7.8%,四舍五入)
每股风险:$64.25 - $59.11 = $5.14
总风险:400 × $5.14 = $2,056
组合:$100,000总资本。此持仓 = 25.7%的组合。
金字塔计划:
如果TECH达到$65.85(+2.5%):加仓250股
如果TECH达到$67.46(+5.0%):加仓150股(补足总预期持仓800股)
=== 阶段4:持仓管理 ===
第1周(3月18-22日):
TECH周五收于$66.80。成交量整周保持在平均量以上。
RS线创新高。股票上没有分销日。
金字塔:当股票穿越$65.85时,以$65.90加仓250股。
新平均成本:(400 × $64.25 + 250 × $65.90) / 650 = $64.89
调整止损:保持在$59.11(原始买入价以下7.8%)。
第2周(3月25-29日):
TECH达到$67.50。以$67.50加仓最终150股。
总持仓:800股,平均成本$65.52
总投资:$52,416
止损仍在$59.11 = 风险($65.52 - $59.11) × 800 = $5,128
第3-6周:TECH继续走高。第6周五收于$76.20。
相对于平均成本收益:+16.3%
没有卖出信号。持有。
=== 阶段5:退出 ===
情景A — 标准利润目标:
第8周:TECH达到$81.75
相对于支点($64.25)的收益:+27.2%(超过20-25%目标)
相对于平均成本($65.52)的收益:+24.8%
在$81.75卖出800股
收益:$65,400
利润:$65,400 - $52,416 = $12,984(+24.8%)
情景B — 8周持有(快速移动者):
如果TECH在前3周内达到+20%($77.10),应用8周持有规则。
从突破日起持有满8周。
8周后,将止损追踪到10周移动均线下方。
可能结果:如果TECH在接下来几个月继续到$95-$120,
收益是45-85%而非25%。
情景C — 止损:
第1周:TECH未能守住支点。跌至$59.11。
在$59.11卖出400股(只初始持仓——金字塔从未触发)
亏损:(64.25 - 59.11) × 400 = $2,056
亏损占组合百分比:2.1%
保留资本:$97,944。准备好下一个设置。
def canslim_screen(universe):
"""
Screen a universe of stocks for CAN SLIM criteria.
Returns a list of candidates that pass all seven factors.
"""
candidates = []
for stock in universe:
fundamentals = get_fundamentals(stock)
technicals = get_technicals(stock)
institutional = get_institutional_data(stock)
# ---- C: Current Quarterly Earnings ----
quarterly_eps_growth = fundamentals.current_quarter_eps_growth_yoy
if quarterly_eps_growth < 0.25:
continue # Must be 25%+ growth
# Check for acceleration
prior_quarter_growth = fundamentals.prior_quarter_eps_growth_yoy
eps_accelerating = quarterly_eps_growth > prior_quarter_growth
# Revenue confirmation
quarterly_revenue_growth = fundamentals.current_quarter_revenue_growth_yoy
if quarterly_revenue_growth < 0.10:
continue # Revenue should confirm earnings growth
# ---- A: Annual Earnings Growth ----
annual_eps_growth_5yr = fundamentals.annual_eps_cagr_5yr
if annual_eps_growth_5yr < 0.25:
continue # Must show 25%+ annual growth over 5 years
roe = fundamentals.return_on_equity
if roe < 0.17:
continue # ROE must be 17%+
# Check for consistency (no negative EPS years in past 5)
if any(eps <= 0 for eps in fundamentals.annual_eps_last_5_years):
continue
# ---- N: New Products / New Highs ----
# Require stock to be within 15% of its 52-week high
pct_from_high = technicals.pct_below_52_week_high
if pct_from_high > 0.15:
continue # Stock must be near new highs, not in a deep decline
# ---- S: Supply and Demand ----
# Check for accumulation pattern in recent weeks
acc_dist_rating = technicals.accumulation_distribution_rating
if acc_dist_rating in ['D', 'E']:
continue # Must show net accumulation, not distribution
# ---- L: Leader or Laggard ----
rs_rating = technicals.relative_strength_rating
if rs_rating < 80:
continue # Must be a market leader
group_rank = technicals.industry_group_rank_percentile
if group_rank < 60:
continue # Industry group should be in top 40%
# ---- I: Institutional Sponsorship ----
inst_owners_current = institutional.num_institutional_owners_current
inst_owners_prior = institutional.num_institutional_owners_prior_quarter
if inst_owners_current <= inst_owners_prior:
continue # Institutional ownership must be INCREASING
if inst_owners_current < 10:
continue # Minimum institutional presence required
# ---- All factors passed ----
candidates.append({
'ticker': stock.ticker,
'eps_growth_qtr': quarterly_eps_growth,
'eps_accelerating': eps_accelerating,
'annual_growth': annual_eps_growth_5yr,
'roe': roe,
'rs_rating': rs_rating,
'group_rank_pct': group_rank,
'inst_owners': inst_owners_current,
'inst_change': inst_owners_current - inst_owners_prior,
'pct_from_high': pct_from_high,
'acc_dist': acc_dist_rating,
})
# Sort by composite score: RS rating + EPS growth acceleration
candidates.sort(key=lambda x: (x['rs_rating'], x['eps_growth_qtr']), reverse=True)
return candidates
def detect_cup_with_handle(prices, volumes, min_weeks=7, max_weeks=65):
"""
Detect cup-with-handle patterns in weekly price/volume data.
Returns buy point (pivot) if a valid pattern is found, else None.
"""
n = len(prices)
if n < min_weeks:
return None
# Step 1: Find the left-side peak (start of cup)
left_peak_idx = None
left_peak_price = 0
for i in range(n - min_weeks):
if prices[i] > left_peak_price:
left_peak_price = prices[i]
left_peak_idx = i
if left_peak_idx is None:
return None
# Step 2: Find the cup bottom (lowest point after left peak)
search_end = min(left_peak_idx + max_weeks, n - 2)
cup_bottom_idx = left_peak_idx
cup_bottom_price = left_peak_price
for i in range(left_peak_idx + 1, search_end):
if prices[i] < cup_bottom_price:
cup_bottom_price = prices[i]
cup_bottom_idx = i
# Check cup depth: should be 12-33%
cup_depth = (left_peak_price - cup_bottom_price) / left_peak_price
if cup_depth < 0.12 or cup_depth > 0.33:
return None
# Step 3: Find right side recovery (price approaching left peak level)
right_peak_idx = None
right_peak_price = 0
for i in range(cup_bottom_idx + 1, min(cup_bottom_idx + max_weeks, n)):
if prices[i] > right_peak_price:
right_peak_price = prices[i]
right_peak_idx = i
# Right side should recover to within 10-15% of left peak
if right_peak_price >= left_peak_price * 0.85:
break
if right_peak_idx is None or right_peak_price < left_peak_price * 0.85:
return None
# Step 4: Identify handle (shallow pullback from right peak)
handle_start = right_peak_idx
handle_low = right_peak_price
handle_low_idx = right_peak_idx
for i in range(right_peak_idx + 1, min(right_peak_idx + 8, n)):
if prices[i] < handle_low:
handle_low = prices[i]
handle_low_idx = i
# Handle depth should be <= 12%
handle_depth = (right_peak_price - handle_low) / right_peak_price
if handle_depth > 0.12:
return None
# Handle must form in upper half of cup
cup_midpoint = cup_bottom_price + (left_peak_price - cup_bottom_price) / 2
if handle_low < cup_midpoint:
return None
# Step 5: Check volume signature
# Volume should contract in handle vs. average
handle_volumes = volumes[handle_start:handle_low_idx + 1]
avg_volume_50 = sum(volumes[max(0, handle_start-50):handle_start]) / 50
avg_handle_volume = sum(handle_volumes) / max(len(handle_volumes), 1)
volume_contraction = avg_handle_volume < avg_volume_50 # Want lower volume in handle
# Step 6: Calculate buy point
handle_high = max(prices[handle_start:handle_low_idx + 2]) if handle_low_idx + 2 <= n else right_peak_price
buy_point = handle_high + 0.10 # Pivot = handle high + $0.10
# Cup duration check
cup_duration_weeks = right_peak_idx - left_peak_idx
if cup_duration_weeks < min_weeks:
return None
return {
'pattern': 'cup_with_handle',
'buy_point': buy_point,
'cup_depth_pct': round(cup_depth * 100, 1),
'handle_depth_pct': round(handle_depth * 100, 1),
'cup_duration_weeks': cup_duration_weeks,
'volume_contracts': volume_contraction,
'left_peak': left_peak_price,
'cup_bottom': cup_bottom_price,
'handle_low': handle_low,
}
def detect_double_bottom(prices, volumes, min_weeks=7):
"""
Detect double-bottom (W-shaped) patterns.
"""
n = len(prices)
if n < min_weeks:
return None
# Find first bottom
first_bottom_idx = 0
first_bottom = prices[0]
for i in range(1, n // 2):
if prices[i] < first_bottom:
first_bottom = prices[i]
first_bottom_idx = i
# Find middle peak between bottoms
middle_peak = 0
middle_peak_idx = first_bottom_idx
for i in range(first_bottom_idx + 1, n - 2):
if prices[i] > middle_peak:
middle_peak = prices[i]
middle_peak_idx = i
if prices[i] < prices[i-1] and middle_peak > first_bottom * 1.05:
break # Found the middle peak, now price turning down
# Find second bottom (should undercut first bottom slightly)
second_bottom = middle_peak
second_bottom_idx = middle_peak_idx
for i in range(middle_peak_idx + 1, n):
if prices[i] < second_bottom:
second_bottom = prices[i]
second_bottom_idx = i
if prices[i] > second_bottom * 1.03:
break # Price recovering from second bottom
# Second bottom should undercut first by 1-3%
undercut = (first_bottom - second_bottom) / first_bottom
if undercut < -0.01 or undercut > 0.05:
return None # Second bottom should be at or slightly below first
# Check pattern duration
pattern_weeks = second_bottom_idx - first_bottom_idx
if pattern_weeks < min_weeks - 2:
return None
# Buy point = middle peak + $0.10
buy_point = middle_peak + 0.10
return {
'pattern': 'double_bottom',
'buy_point': buy_point,
'first_bottom': first_bottom,
'second_bottom': second_bottom,
'middle_peak': middle_peak,
'undercut_pct': round(undercut * 100, 1),
'duration_weeks': pattern_weeks,
}
class CANSLIMPortfolio:
"""
Portfolio management system implementing O'Neil's rules for
position sizing, loss cutting, profit taking, and market exposure.
"""
def __init__(self, total_capital, max_positions=8):
self.total_capital = total_capital
self.cash = total_capital
self.max_positions = max_positions
self.positions = {} # ticker -> Position
self.closed_trades = []
self.market_status = 'CONFIRMED_UPTREND'
self.distribution_days = 0
# ---- Market Assessment ----
def update_market_status(self, index_data):
"""
Track distribution days and update market status.
"""
today_close = index_data['close']
today_volume = index_data['volume']
prev_close = index_data['prev_close']
prev_volume = index_data['prev_volume']
# Distribution day: index down on higher volume
if today_close < prev_close and today_volume > prev_volume:
self.distribution_days += 1
if self.distribution_days >= 5:
self.market_status = 'MARKET_IN_CORRECTION'
elif self.distribution_days >= 3:
self.market_status = 'UPTREND_UNDER_PRESSURE'
else:
self.market_status = 'CONFIRMED_UPTREND'
def check_follow_through(self, rally_day_count, index_gain_pct, volume_vs_prior):
"""
After a correction, check for a follow-through day to confirm new uptrend.
"""
if (rally_day_count >= 4 and
index_gain_pct >= 1.5 and
volume_vs_prior > 1.0):
self.market_status = 'CONFIRMED_UPTREND'
self.distribution_days = 0
return True
return False
# ---- Position Sizing ----
def calculate_position_size(self, buy_price, stop_price):
"""
Size position so maximum loss is ~2% of total capital.
Also enforce maximum position = 25% of total capital.
"""
max_risk_per_trade = self.total_capital * 0.02 # 2% portfolio risk
risk_per_share = buy_price - stop_price
shares_by_risk = int(max_risk_per_trade / risk_per_share)
max_position_value = self.total_capital * 0.25 # 25% max position
shares_by_position = int(max_position_value / buy_price)
shares = min(shares_by_risk, shares_by_position)
cost = shares * buy_price
if cost > self.cash:
shares = int(self.cash / buy_price)
return shares
# ---- Trade Execution ----
def buy(self, ticker, price, stop_price, volume_ratio):
"""
Execute a buy if all conditions are met.
"""
# Market check
if self.market_status != 'CONFIRMED_UPTREND':
return {'action': 'BLOCKED', 'reason': f'Market status: {self.market_status}'}
# Position count check
if len(self.positions) >= self.max_positions:
return {'action': 'BLOCKED', 'reason': 'Maximum positions reached'}
# Volume check
if volume_ratio < 1.5:
return {'action': 'BLOCKED', 'reason': f'Volume ratio {volume_ratio:.1f}x < 1.5x required'}
# Already holding check
if ticker in self.positions:
return {'action': 'BLOCKED', 'reason': 'Already holding this stock'}
shares = self.calculate_position_size(price, stop_price)
if shares <= 0:
return {'action': 'BLOCKED', 'reason': 'Insufficient capital'}
cost = shares * price
self.cash -= cost
self.positions[ticker] = {
'shares': shares,
'entry_price': price,
'stop_price': stop_price,
'entry_date': get_current_date(),
'highest_close': price,
'weeks_held': 0,
'pyramid_count': 0,
}
return {
'action': 'BOUGHT',
'ticker': ticker,
'shares': shares,
'price': price,
'cost': cost,
'stop': stop_price,
'risk_amt': (price - stop_price) * shares,
}
def daily_review(self, market_prices):
"""
Review all positions daily. Execute sells as needed.
"""
actions = []
for ticker, pos in list(self.positions.items()):
current_price = market_prices.get(ticker)
if current_price is None:
continue
# Update highest close
if current_price > pos['highest_close']:
pos['highest_close'] = current_price
gain_pct = (current_price - pos['entry_price']) / pos['entry_price']
# ---- RULE 1: Cut loss at 7-8% ----
if current_price <= pos['stop_price']:
actions.append(self._sell(ticker, current_price, 'STOP_LOSS'))
continue
# ---- RULE 2: Take profit at 20-25% ----
if gain_pct >= 0.20:
weeks = pos['weeks_held']
# Check 8-week hold rule for fast movers
if weeks >= 8 or gain_pct >= 0.20:
actions.append(self._sell(ticker, current_price, 'PROFIT_TARGET'))
continue
# Increment weeks held
pos['weeks_held'] += 1
return actions
"在股票市场赚大钱的秘密,用一句话说,就是:你必须学会迅速认输。一旦股票对你不利,就毫不犹豫地止损。不要抱有任何幻想,不要等待,不要视而不见。"
"我的研究显示,在主要上涨之前,几乎每只大赢家股票都有一些共同特征。这就是CAN SLIM系统的基础。"
"买入正处于强劲上升趋势的股票。不要试图猜测底部或买入便宜的股票。强势股往往继续走强,弱势股往往继续走弱。"
"最重要的规则是7-8%止损规则。如果你严格遵守这一规则,三次连续亏损只会让你损失约23%的资本。而一次50%的亏损则需要100%的收益才能恢复。"
"我研究了过去100年中的所有主要市场赢家。它们的特征惊人地一致。这就是为什么CAN SLIM有效——它基于历史事实,而非理论。"
"不要爱上任何股票。如果股票触发了止损,就卖出。不要问为什么。不要希望它会反弹。市场的涨跌与你无关。"
"成交量是股票涨跌的燃料。突破日需要成交量确认。如果成交量不足,突破往往会失败。"
"20-25%的收益目标意味着你可以在4次交易中对3次,仅需保持50%的胜率就能盈利。结合7-8%的止损,风险/回报比对你有利。"
"市场方向先于个股选择。在确认的熊市中,即使最好的CAN SLIM股票也会挣扎。"
"成长股投资的全部秘密是:买最好的股票,在它们still上涨时持有,在它们停止上涨时卖出。"
本规范从William J. O'Neil的《24条投资成功必备教训》中提炼,用于实际应用。完整阅读原著作补充。
(全文完——共1465行)