70 lines
7.5 KiB
Python
70 lines
7.5 KiB
Python
import json
|
||
import shutil
|
||
import zipfile
|
||
from pathlib import Path
|
||
|
||
ROOT=Path(__file__).resolve().parent
|
||
OUT=ROOT/'outputs'/'20261019'
|
||
a=json.loads((ROOT/'analysis.json').read_text())
|
||
lines=['# 纳斯达克综合与标普500:日线回撤及解套时间','',
|
||
'抓取日期:2026-10-19(北京时间)。行情截至美国交易日 **2026-10-16**。NASDAQ 在本报告指纳斯达克综合指数 ^IXIC,不是纳斯达克100。','',
|
||
'## 数据范围','',
|
||
'| 指数 | 起点 | 终点 | 有效日线 | 回撤段数 | 已解套 | 未解套 |',
|
||
'|---|---|---|---:|---:|---:|---:|']
|
||
for x in a['indices'].values():
|
||
lines.append(f"| {x['name']} | {x['start']} | {x['end']} | {x['observations']:,} | {len(x['events'])} | {len(x['events'])-len(x['open_events'])} | {len(x['open_events'])} |")
|
||
lines += ['', '标普500于1957-03-04正式推出;更早数据是数据商提供的前身/回溯序列,不代表当时已有现行500只股票指数。早期日线没有逐日与独立交易所日历核对,可能未含当年的周六交易。因此“交易日”严格指本数据集的有效日线间隔数;自然日数直接由日期相减。','',
|
||
'## 计算口径','',
|
||
'- 价格:名义指数日收盘价,不含分红再投资、税费、交易成本、汇率或通胀。价格比较统一到0.01点,消除Yahoo浮点表示噪声。',
|
||
'- 回撤:日收盘低于截至此前的历史最高收盘后开始,首次收盘大于或等于该前高时结束。所有严格低于前高的事件均保留,没有跌幅或时长门槛。',
|
||
'- 前高日:下跌前最后一个达到该历史最高收盘的日期。谷底日:该事件最低收盘首次出现的日期。',
|
||
'- 总解套自然日 = 解套日 − 前高日。下跌自然日 = 谷底日 − 前高日。谷底后恢复自然日 = 解套日 − 谷底日。',
|
||
'- 交易日:对应日期在有效日线中的序号相减,不含起点、包含终点。不填充休市日,不插值。',
|
||
'- 未解套:解套日期、最终总时长和恢复时长留空;“已观察天数”只累计至2026-10-16。它是右删失的未完成事件,不参加已解套时长均值/中位数。',
|
||
'- 一段长回撤包含期间所有反弹,不重复拆分。例如纳指2000—2015年算一段,内部2007年的局部高点不另起历史新高回撤。',
|
||
'- 为补充任意日期买入的情况,日线表另列每个买入日之后,首个收盘不低于该日买入价的日期。若次日即盈利,也会列出该日期,但“期间曾浮亏”为否。这列不是历史新高回撤时长。最后一个观测日标为“最后观测日”,不能直接视为亏损。',
|
||
'- 事件边界和首次回本日期由脚本识别,是抓取时点快照。Excel可重算简单比率与日期差,但修改价格或新增行情后,须重跑分析脚本以重新识别边界。','',
|
||
'## 最大跌幅至少10%的全部回撤','',
|
||
'下面仍按“历史新高到收复该前高”的不重叠口径。完整微小回撤也已保存在Excel和CSV。','',
|
||
'| 指数 | 前高日 | 谷底日 | 解套日 | 最大回撤 | 下跌自然日 | 谷底后恢复自然日 | 总解套自然日 | 总解套交易日 |',
|
||
'|---|---|---|---|---:|---:|---:|---:|---:|']
|
||
for x in a['indices'].values():
|
||
for e in x['events']:
|
||
if e['max_drawdown'] <= -.10:
|
||
f=lambda v:'未解套' if v is None else str(v)
|
||
lines.append(f"| {x['name']} | {e['peak_date']} | {e['trough_date']} | {f(e['recovery_date'])} | {e['max_drawdown']:.2%} | {e['decline_calendar_days']} | {f(e['rebound_calendar_days'])} | {f(e['total_calendar_days'])} | {f(e['total_trading_days'])} |")
|
||
lines += ['', '## 目前尚未解套','', '| 指数 | 前高日 | 截止日 | 期间最大回撤 | 已观察自然日 | 已观察交易日 |','|---|---|---|---:|---:|---:|']
|
||
for x in a['indices'].values():
|
||
for e in x['open_events']:
|
||
lines.append(f"| {x['name']} | {e['peak_date']} | {e['end_date']} | {e['max_drawdown']:.2%} | {e['observed_calendar_days']} | {e['observed_trading_days']} |")
|
||
lines += ['', '## 数据来源与交叉核验','',
|
||
'两项指数均以Yahoo同指数日线的有效观测日期为日期基准。在这些日期上,FRED有有效收盘价时优先采用FRED(其上游为指数公司),FRED没有时使用Yahoo。不是总回报指数,不拼接ETF,不把纳指100混入综合指数。','']
|
||
for x in a['indices'].values():
|
||
lines.append(f"- {x['name']}:{x['fred_rows']:,}条FRED,{x['yahoo_rows']:,}条Yahoo。两源重叠{x['source_overlap']:,}条,按0.01点比较有{x['source_mismatches']:,}条不同,全部保留在source_discrepancies.csv,最终选FRED。")
|
||
lines += ['','FRED的纳指序列另含2019-04-19一条有效数值,但该日为Good Friday休市且Yahoo无观测。本报告排除此异常行,避免多计交易日。原始响应原样保留。', '',
|
||
'FRED的SP500依协议仅提供最近10年数据,因此更早标普日线依赖Yahoo,未获得独立全历史权威源逐日交叉核验。纳指末日2026-10-16仅Yahoo有观测。数据未来可能修订。','',
|
||
'- [FRED NASDAQCOM:来源、日收盘定义](https://fred.stlouisfed.org/series/NASDAQCOM)',
|
||
'- [FRED SP500:来源、仅10年限制、价格指数不含分红](https://fred.stlouisfed.org/series/SP500)',
|
||
'- [Yahoo纳指历史页](https://finance.yahoo.com/quote/%5EIXIC/history/)',
|
||
'- [Yahoo标普历史页](https://finance.yahoo.com/quote/%5EGSPC/history/)',
|
||
'- [标普500官方说明、推出日期](https://www.spglobal.com/spdji/en/indices/equity/sp-500/)',
|
||
'- [NYSE官方2019年休市日历](https://ir.theice.com/press/news-details/2018/NYSE-Group-Announces-2019-2020-and-2021-Holiday-and-Early-Closings-Calendar/default.aspx)',
|
||
'', '## 检查与文件','',
|
||
'检查了日期递增、重复日期、正数收盘、事件数与独立累计最高值算法一致、每段最低点、收复日首次达标、不重叠、日期差可加和。逐日回本使用O(n)单调栈,并对抽样日期及所有事件前高/谷底用独立逐日查找核对。另检查同价前高、重复谷底、未解套与没有回撤等人工边界样本。检查结果见validation.json。','',
|
||
'- nasdaq_sp500_drawdown_recovery.xlsx:概览、全部回撤、日线与逐日回本三个工作表。',
|
||
'- nasdaq_drawdowns.csv / sp500_drawdowns.csv:每次回撤的完整明细。',
|
||
'- nasdaq_daily.csv / sp500_daily.csv:逐条来源、每日回撤及买入回本日期。',
|
||
'- source_discrepancies.csv:两来源不一致的观测。',
|
||
'- source_manifest.json:下载URL、时间戳、字节数和SHA-256校验。raw/为未经改写的响应。',
|
||
'- analyze.py:可用Python标准库复算。先运行fetch_data.py更新固定截止日期的行情,再运行analyze.py;Excel用build_workbook.mjs重建。','']
|
||
(OUT/'分析说明.md').write_text('\n'.join(lines))
|
||
shutil.copy2(ROOT/'source_manifest.json',OUT/'source_manifest.json')
|
||
archive=OUT/'nasdaq_sp500_data_and_analysis.zip'
|
||
with zipfile.ZipFile(archive,'w',zipfile.ZIP_DEFLATED) as z:
|
||
for p in OUT.iterdir():
|
||
if p.suffix in ('.csv','.json','.md','.xlsx'):
|
||
z.write(p,p.name)
|
||
for p in (ROOT/'raw').iterdir():z.write(p,'raw/'+p.name)
|
||
for name in ['fetch_data.py','analyze.py','build_workbook.mjs','package_results.py']:z.write(ROOT/name,'scripts/'+name)
|
||
print(archive)
|