quantwise 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (362) hide show
  1. package/.claude/skills/README.md +80 -0
  2. package/.claude/skills/backtest-expert/SKILL.md +206 -0
  3. package/.claude/skills/backtest-expert/references/failed_tests.md +236 -0
  4. package/.claude/skills/backtest-expert/references/methodology.md +227 -0
  5. package/.claude/skills/breadth-chart-analyst/SKILL.md +583 -0
  6. package/.claude/skills/breadth-chart-analyst/assets/SP500_Breadth_Index_200MA_8MA.jpeg +0 -0
  7. package/.claude/skills/breadth-chart-analyst/assets/US_Stock_Market_Uptrend_Ratio.jpeg +0 -0
  8. package/.claude/skills/breadth-chart-analyst/assets/breadth_analysis_template.md +558 -0
  9. package/.claude/skills/breadth-chart-analyst/references/breadth_chart_methodology.md +590 -0
  10. package/.claude/skills/canslim-screener/SKILL.md +599 -0
  11. package/.claude/skills/canslim-screener/references/canslim_methodology.md +606 -0
  12. package/.claude/skills/canslim-screener/references/fmp_api_endpoints.md +707 -0
  13. package/.claude/skills/canslim-screener/references/interpretation_guide.md +516 -0
  14. package/.claude/skills/canslim-screener/references/scoring_system.md +597 -0
  15. package/.claude/skills/canslim-screener/scripts/calculators/earnings_calculator.py +343 -0
  16. package/.claude/skills/canslim-screener/scripts/calculators/growth_calculator.py +334 -0
  17. package/.claude/skills/canslim-screener/scripts/calculators/institutional_calculator.py +347 -0
  18. package/.claude/skills/canslim-screener/scripts/calculators/leadership_calculator.py +380 -0
  19. package/.claude/skills/canslim-screener/scripts/calculators/market_calculator.py +244 -0
  20. package/.claude/skills/canslim-screener/scripts/calculators/new_highs_calculator.py +194 -0
  21. package/.claude/skills/canslim-screener/scripts/calculators/supply_demand_calculator.py +221 -0
  22. package/.claude/skills/canslim-screener/scripts/finviz_stock_client.py +227 -0
  23. package/.claude/skills/canslim-screener/scripts/fmp_client.py +393 -0
  24. package/.claude/skills/canslim-screener/scripts/report_generator.py +405 -0
  25. package/.claude/skills/canslim-screener/scripts/scorer.py +625 -0
  26. package/.claude/skills/canslim-screener/scripts/screen_canslim.py +361 -0
  27. package/.claude/skills/canslim-screener/scripts/test_institutional_endpoint.py +109 -0
  28. package/.claude/skills/chart/SKILL.md +20 -0
  29. package/.claude/skills/dividend-growth-pullback-screener/SKILL.md +322 -0
  30. package/.claude/skills/dividend-growth-pullback-screener/references/dividend_growth_compounding.md +400 -0
  31. package/.claude/skills/dividend-growth-pullback-screener/references/fmp_api_guide.md +642 -0
  32. package/.claude/skills/dividend-growth-pullback-screener/references/rsi_oversold_strategy.md +333 -0
  33. package/.claude/skills/dividend-growth-pullback-screener/scripts/screen_dividend_growth_rsi.py +1155 -0
  34. package/.claude/skills/earnings-calendar/SKILL.md +721 -0
  35. package/.claude/skills/earnings-calendar/assets/earnings_report_template.md +102 -0
  36. package/.claude/skills/earnings-calendar/references/fmp_api_guide.md +590 -0
  37. package/.claude/skills/earnings-calendar/scripts/fetch_earnings_fmp.py +443 -0
  38. package/.claude/skills/earnings-calendar/scripts/generate_report.py +366 -0
  39. package/.claude/skills/economic-calendar-fetcher/SKILL.md +365 -0
  40. package/.claude/skills/economic-calendar-fetcher/references/fmp_api_documentation.md +345 -0
  41. package/.claude/skills/economic-calendar-fetcher/scripts/get_economic_calendar.py +267 -0
  42. package/.claude/skills/ftd-detector/SKILL.md +147 -0
  43. package/.claude/skills/ftd-detector/references/ftd_methodology.md +188 -0
  44. package/.claude/skills/ftd-detector/references/post_ftd_guide.md +185 -0
  45. package/.claude/skills/ftd-detector/scripts/fmp_client.py +158 -0
  46. package/.claude/skills/ftd-detector/scripts/ftd_detector.py +280 -0
  47. package/.claude/skills/ftd-detector/scripts/post_ftd_monitor.py +404 -0
  48. package/.claude/skills/ftd-detector/scripts/rally_tracker.py +508 -0
  49. package/.claude/skills/ftd-detector/scripts/report_generator.py +341 -0
  50. package/.claude/skills/ftd-detector/scripts/tests/conftest.py +9 -0
  51. package/.claude/skills/ftd-detector/scripts/tests/helpers.py +107 -0
  52. package/.claude/skills/ftd-detector/scripts/tests/test_post_ftd_monitor.py +311 -0
  53. package/.claude/skills/ftd-detector/scripts/tests/test_rally_tracker.py +302 -0
  54. package/.claude/skills/institutional-flow-tracker/README.md +362 -0
  55. package/.claude/skills/institutional-flow-tracker/SKILL.md +357 -0
  56. package/.claude/skills/institutional-flow-tracker/references/13f_filings_guide.md +383 -0
  57. package/.claude/skills/institutional-flow-tracker/references/institutional_investor_types.md +580 -0
  58. package/.claude/skills/institutional-flow-tracker/references/interpretation_framework.md +573 -0
  59. package/.claude/skills/institutional-flow-tracker/scripts/analyze_single_stock.py +457 -0
  60. package/.claude/skills/institutional-flow-tracker/scripts/track_institution_portfolio.py +108 -0
  61. package/.claude/skills/institutional-flow-tracker/scripts/track_institutional_flow.py +450 -0
  62. package/.claude/skills/macro-regime-detector/SKILL.md +86 -0
  63. package/.claude/skills/macro-regime-detector/references/historical_regimes.md +124 -0
  64. package/.claude/skills/macro-regime-detector/references/indicator_interpretation_guide.md +144 -0
  65. package/.claude/skills/macro-regime-detector/references/regime_detection_methodology.md +138 -0
  66. package/.claude/skills/macro-regime-detector/scripts/calculators/__init__.py +1 -0
  67. package/.claude/skills/macro-regime-detector/scripts/calculators/concentration_calculator.py +165 -0
  68. package/.claude/skills/macro-regime-detector/scripts/calculators/credit_conditions_calculator.py +124 -0
  69. package/.claude/skills/macro-regime-detector/scripts/calculators/equity_bond_calculator.py +198 -0
  70. package/.claude/skills/macro-regime-detector/scripts/calculators/sector_rotation_calculator.py +123 -0
  71. package/.claude/skills/macro-regime-detector/scripts/calculators/size_factor_calculator.py +131 -0
  72. package/.claude/skills/macro-regime-detector/scripts/calculators/utils.py +347 -0
  73. package/.claude/skills/macro-regime-detector/scripts/calculators/yield_curve_calculator.py +279 -0
  74. package/.claude/skills/macro-regime-detector/scripts/fmp_client.py +134 -0
  75. package/.claude/skills/macro-regime-detector/scripts/macro_regime_detector.py +278 -0
  76. package/.claude/skills/macro-regime-detector/scripts/report_generator.py +327 -0
  77. package/.claude/skills/macro-regime-detector/scripts/scorer.py +574 -0
  78. package/.claude/skills/macro-regime-detector/scripts/tests/conftest.py +9 -0
  79. package/.claude/skills/macro-regime-detector/scripts/tests/test_concentration.py +78 -0
  80. package/.claude/skills/macro-regime-detector/scripts/tests/test_credit_conditions.py +59 -0
  81. package/.claude/skills/macro-regime-detector/scripts/tests/test_equity_bond.py +74 -0
  82. package/.claude/skills/macro-regime-detector/scripts/tests/test_helpers.py +90 -0
  83. package/.claude/skills/macro-regime-detector/scripts/tests/test_scorer.py +439 -0
  84. package/.claude/skills/macro-regime-detector/scripts/tests/test_sector_rotation.py +78 -0
  85. package/.claude/skills/macro-regime-detector/scripts/tests/test_size_factor.py +59 -0
  86. package/.claude/skills/macro-regime-detector/scripts/tests/test_utils.py +126 -0
  87. package/.claude/skills/macro-regime-detector/scripts/tests/test_yield_curve.py +64 -0
  88. package/.claude/skills/market-breadth-analyzer/SKILL.md +121 -0
  89. package/.claude/skills/market-breadth-analyzer/references/breadth_analysis_methodology.md +168 -0
  90. package/.claude/skills/market-breadth-analyzer/scripts/calculators/__init__.py +1 -0
  91. package/.claude/skills/market-breadth-analyzer/scripts/calculators/bearish_signal_calculator.py +150 -0
  92. package/.claude/skills/market-breadth-analyzer/scripts/calculators/cycle_calculator.py +168 -0
  93. package/.claude/skills/market-breadth-analyzer/scripts/calculators/divergence_calculator.py +119 -0
  94. package/.claude/skills/market-breadth-analyzer/scripts/calculators/historical_context_calculator.py +120 -0
  95. package/.claude/skills/market-breadth-analyzer/scripts/calculators/ma_crossover_calculator.py +115 -0
  96. package/.claude/skills/market-breadth-analyzer/scripts/calculators/trend_level_calculator.py +103 -0
  97. package/.claude/skills/market-breadth-analyzer/scripts/csv_client.py +225 -0
  98. package/.claude/skills/market-breadth-analyzer/scripts/market_breadth_analyzer.py +307 -0
  99. package/.claude/skills/market-breadth-analyzer/scripts/report_generator.py +330 -0
  100. package/.claude/skills/market-breadth-analyzer/scripts/scorer.py +271 -0
  101. package/.claude/skills/market-environment-analysis/SKILL.md +139 -0
  102. package/.claude/skills/market-environment-analysis/references/analysis_patterns.md +124 -0
  103. package/.claude/skills/market-environment-analysis/references/indicators.md +99 -0
  104. package/.claude/skills/market-environment-analysis/scripts/market_utils.py +127 -0
  105. package/.claude/skills/market-news-analyst/SKILL.md +714 -0
  106. package/.claude/skills/market-news-analyst/references/corporate_news_impact.md +446 -0
  107. package/.claude/skills/market-news-analyst/references/geopolitical_commodity_correlations.md +499 -0
  108. package/.claude/skills/market-news-analyst/references/market_event_patterns.md +393 -0
  109. package/.claude/skills/market-news-analyst/references/trusted_news_sources.md +510 -0
  110. package/.claude/skills/market-top-detector/SKILL.md +159 -0
  111. package/.claude/skills/market-top-detector/references/distribution_day_guide.md +100 -0
  112. package/.claude/skills/market-top-detector/references/historical_tops.md +142 -0
  113. package/.claude/skills/market-top-detector/references/market_top_methodology.md +167 -0
  114. package/.claude/skills/market-top-detector/scripts/calculators/__init__.py +17 -0
  115. package/.claude/skills/market-top-detector/scripts/calculators/breadth_calculator.py +116 -0
  116. package/.claude/skills/market-top-detector/scripts/calculators/defensive_rotation_calculator.py +127 -0
  117. package/.claude/skills/market-top-detector/scripts/calculators/distribution_day_calculator.py +161 -0
  118. package/.claude/skills/market-top-detector/scripts/calculators/index_technical_calculator.py +254 -0
  119. package/.claude/skills/market-top-detector/scripts/calculators/leading_stock_calculator.py +198 -0
  120. package/.claude/skills/market-top-detector/scripts/calculators/sentiment_calculator.py +213 -0
  121. package/.claude/skills/market-top-detector/scripts/fmp_client.py +158 -0
  122. package/.claude/skills/market-top-detector/scripts/market_top_detector.py +349 -0
  123. package/.claude/skills/market-top-detector/scripts/report_generator.py +314 -0
  124. package/.claude/skills/market-top-detector/scripts/scorer.py +473 -0
  125. package/.claude/skills/market-top-detector/scripts/tests/conftest.py +9 -0
  126. package/.claude/skills/market-top-detector/scripts/tests/helpers.py +49 -0
  127. package/.claude/skills/market-top-detector/scripts/tests/test_breadth.py +62 -0
  128. package/.claude/skills/market-top-detector/scripts/tests/test_defensive_rotation.py +56 -0
  129. package/.claude/skills/market-top-detector/scripts/tests/test_distribution_day.py +92 -0
  130. package/.claude/skills/market-top-detector/scripts/tests/test_index_technical.py +73 -0
  131. package/.claude/skills/market-top-detector/scripts/tests/test_leading_stock.py +57 -0
  132. package/.claude/skills/market-top-detector/scripts/tests/test_scorer.py +180 -0
  133. package/.claude/skills/market-top-detector/scripts/tests/test_sentiment.py +64 -0
  134. package/.claude/skills/options-strategy-advisor/README.md +469 -0
  135. package/.claude/skills/options-strategy-advisor/SKILL.md +959 -0
  136. package/.claude/skills/options-strategy-advisor/scripts/black_scholes.py +495 -0
  137. package/.claude/skills/pair-trade-screener/README.md +389 -0
  138. package/.claude/skills/pair-trade-screener/SKILL.md +622 -0
  139. package/.claude/skills/pair-trade-screener/references/cointegration_guide.md +745 -0
  140. package/.claude/skills/pair-trade-screener/references/methodology.md +853 -0
  141. package/.claude/skills/pair-trade-screener/scripts/analyze_spread.py +394 -0
  142. package/.claude/skills/pair-trade-screener/scripts/find_pairs.py +535 -0
  143. package/.claude/skills/portfolio-manager/README.md +394 -0
  144. package/.claude/skills/portfolio-manager/SKILL.md +750 -0
  145. package/.claude/skills/portfolio-manager/references/alpaca-mcp-setup.md +367 -0
  146. package/.claude/skills/portfolio-manager/references/asset-allocation.md +502 -0
  147. package/.claude/skills/portfolio-manager/references/diversification-principles.md +553 -0
  148. package/.claude/skills/portfolio-manager/references/portfolio-risk-metrics.md +603 -0
  149. package/.claude/skills/portfolio-manager/references/position-evaluation.md +477 -0
  150. package/.claude/skills/portfolio-manager/references/rebalancing-strategies.md +715 -0
  151. package/.claude/skills/portfolio-manager/references/risk-profile-questionnaire.md +608 -0
  152. package/.claude/skills/portfolio-manager/references/target-allocations.md +558 -0
  153. package/.claude/skills/portfolio-manager/scripts/test_alpaca_connection.py +286 -0
  154. package/.claude/skills/scenario-analyzer/SKILL.md +317 -0
  155. package/.claude/skills/scenario-analyzer/references/headline_event_patterns.md +264 -0
  156. package/.claude/skills/scenario-analyzer/references/scenario_playbooks.md +320 -0
  157. package/.claude/skills/scenario-analyzer/references/sector_sensitivity_matrix.md +217 -0
  158. package/.claude/skills/sector-analyst/SKILL.md +206 -0
  159. package/.claude/skills/sector-analyst/assets/industory_performance_1.jpeg +0 -0
  160. package/.claude/skills/sector-analyst/assets/industory_performance_2.jpeg +0 -0
  161. package/.claude/skills/sector-analyst/assets/sector_performance.jpeg +0 -0
  162. package/.claude/skills/sector-analyst/references/sector_rotation.md +170 -0
  163. package/.claude/skills/stanley-druckenmiller-investment/SKILL.md +84 -0
  164. package/.claude/skills/stanley-druckenmiller-investment/references/case-studies.md +148 -0
  165. package/.claude/skills/stanley-druckenmiller-investment/references/investment-philosophy.md +80 -0
  166. package/.claude/skills/stanley-druckenmiller-investment/references/market-analysis-guide.md +146 -0
  167. package/.claude/skills/stock/NOTION_SETUP.md +33 -0
  168. package/.claude/skills/stock/SKILL.md +38 -0
  169. package/.claude/skills/technical-analyst/SKILL.md +238 -0
  170. package/.claude/skills/technical-analyst/assets/analysis_template.md +183 -0
  171. package/.claude/skills/technical-analyst/references/technical_analysis_framework.md +282 -0
  172. package/.claude/skills/theme-detector/SKILL.md +320 -0
  173. package/.claude/skills/theme-detector/assets/report_template.md +155 -0
  174. package/.claude/skills/theme-detector/references/cross_sector_themes.md +252 -0
  175. package/.claude/skills/theme-detector/references/finviz_industry_codes.md +403 -0
  176. package/.claude/skills/theme-detector/references/thematic_etf_catalog.md +333 -0
  177. package/.claude/skills/theme-detector/references/theme_detection_methodology.md +430 -0
  178. package/.claude/skills/theme-detector/scripts/calculators/__init__.py +1 -0
  179. package/.claude/skills/theme-detector/scripts/calculators/heat_calculator.py +123 -0
  180. package/.claude/skills/theme-detector/scripts/calculators/industry_ranker.py +98 -0
  181. package/.claude/skills/theme-detector/scripts/calculators/lifecycle_calculator.py +172 -0
  182. package/.claude/skills/theme-detector/scripts/calculators/theme_classifier.py +195 -0
  183. package/.claude/skills/theme-detector/scripts/calculators/theme_discoverer.py +280 -0
  184. package/.claude/skills/theme-detector/scripts/config_loader.py +142 -0
  185. package/.claude/skills/theme-detector/scripts/default_theme_config.py +254 -0
  186. package/.claude/skills/theme-detector/scripts/etf_scanner.py +609 -0
  187. package/.claude/skills/theme-detector/scripts/finviz_performance_client.py +131 -0
  188. package/.claude/skills/theme-detector/scripts/report_generator.py +490 -0
  189. package/.claude/skills/theme-detector/scripts/representative_stock_selector.py +673 -0
  190. package/.claude/skills/theme-detector/scripts/scorer.py +87 -0
  191. package/.claude/skills/theme-detector/scripts/tests/README.md +21 -0
  192. package/.claude/skills/theme-detector/scripts/tests/conftest.py +9 -0
  193. package/.claude/skills/theme-detector/scripts/tests/test_config_loader.py +239 -0
  194. package/.claude/skills/theme-detector/scripts/tests/test_etf_scanner.py +810 -0
  195. package/.claude/skills/theme-detector/scripts/tests/test_heat_calculator.py +245 -0
  196. package/.claude/skills/theme-detector/scripts/tests/test_industry_ranker.py +256 -0
  197. package/.claude/skills/theme-detector/scripts/tests/test_lifecycle_calculator.py +301 -0
  198. package/.claude/skills/theme-detector/scripts/tests/test_report_generator.py +624 -0
  199. package/.claude/skills/theme-detector/scripts/tests/test_representative_stock_selector.py +898 -0
  200. package/.claude/skills/theme-detector/scripts/tests/test_scorer.py +185 -0
  201. package/.claude/skills/theme-detector/scripts/tests/test_theme_classifier.py +534 -0
  202. package/.claude/skills/theme-detector/scripts/tests/test_theme_detector_e2e.py +467 -0
  203. package/.claude/skills/theme-detector/scripts/tests/test_theme_discoverer.py +458 -0
  204. package/.claude/skills/theme-detector/scripts/tests/test_uptrend_client.py +76 -0
  205. package/.claude/skills/theme-detector/scripts/theme_detector.py +815 -0
  206. package/.claude/skills/theme-detector/scripts/themes.yaml +168 -0
  207. package/.claude/skills/theme-detector/scripts/uptrend_client.py +241 -0
  208. package/.claude/skills/uptrend-analyzer/SKILL.md +108 -0
  209. package/.claude/skills/uptrend-analyzer/references/uptrend_methodology.md +215 -0
  210. package/.claude/skills/uptrend-analyzer/scripts/calculators/__init__.py +1 -0
  211. package/.claude/skills/uptrend-analyzer/scripts/calculators/historical_context_calculator.py +122 -0
  212. package/.claude/skills/uptrend-analyzer/scripts/calculators/market_breadth_calculator.py +145 -0
  213. package/.claude/skills/uptrend-analyzer/scripts/calculators/momentum_calculator.py +183 -0
  214. package/.claude/skills/uptrend-analyzer/scripts/calculators/sector_participation_calculator.py +204 -0
  215. package/.claude/skills/uptrend-analyzer/scripts/calculators/sector_rotation_calculator.py +218 -0
  216. package/.claude/skills/uptrend-analyzer/scripts/data_fetcher.py +236 -0
  217. package/.claude/skills/uptrend-analyzer/scripts/report_generator.py +329 -0
  218. package/.claude/skills/uptrend-analyzer/scripts/scorer.py +276 -0
  219. package/.claude/skills/uptrend-analyzer/scripts/uptrend_analyzer.py +219 -0
  220. package/.claude/skills/us-market-bubble-detector/CHANGELOG.md +118 -0
  221. package/.claude/skills/us-market-bubble-detector/SKILL.md +545 -0
  222. package/.claude/skills/us-market-bubble-detector/references/bubble_framework.md +335 -0
  223. package/.claude/skills/us-market-bubble-detector/references/historical_cases.md +327 -0
  224. package/.claude/skills/us-market-bubble-detector/references/implementation_guide.md +473 -0
  225. package/.claude/skills/us-market-bubble-detector/references/quick_reference.md +354 -0
  226. package/.claude/skills/us-market-bubble-detector/references/quick_reference_en.md +342 -0
  227. package/.claude/skills/us-market-bubble-detector/scripts/bubble_scorer.py +309 -0
  228. package/.claude/skills/us-stock-analysis/SKILL.md +294 -0
  229. package/.claude/skills/us-stock-analysis/references/financial-metrics.md +172 -0
  230. package/.claude/skills/us-stock-analysis/references/fundamental-analysis.md +129 -0
  231. package/.claude/skills/us-stock-analysis/references/report-template.md +207 -0
  232. package/.claude/skills/us-stock-analysis/references/technical-analysis.md +93 -0
  233. package/.claude/skills/value-dividend-screener/SKILL.md +562 -0
  234. package/.claude/skills/value-dividend-screener/references/fmp_api_guide.md +348 -0
  235. package/.claude/skills/value-dividend-screener/references/screening_methodology.md +315 -0
  236. package/.claude/skills/value-dividend-screener/scripts/screen_dividend_stocks.py +1138 -0
  237. package/.claude/skills/vcp-screener/SKILL.md +79 -0
  238. package/.claude/skills/vcp-screener/references/fmp_api_endpoints.md +45 -0
  239. package/.claude/skills/vcp-screener/references/scoring_system.md +154 -0
  240. package/.claude/skills/vcp-screener/references/vcp_methodology.md +124 -0
  241. package/.claude/skills/vcp-screener/scripts/calculators/__init__.py +1 -0
  242. package/.claude/skills/vcp-screener/scripts/calculators/pivot_proximity_calculator.py +139 -0
  243. package/.claude/skills/vcp-screener/scripts/calculators/relative_strength_calculator.py +161 -0
  244. package/.claude/skills/vcp-screener/scripts/calculators/trend_template_calculator.py +228 -0
  245. package/.claude/skills/vcp-screener/scripts/calculators/vcp_pattern_calculator.py +322 -0
  246. package/.claude/skills/vcp-screener/scripts/calculators/volume_pattern_calculator.py +121 -0
  247. package/.claude/skills/vcp-screener/scripts/fmp_client.py +162 -0
  248. package/.claude/skills/vcp-screener/scripts/report_generator.py +317 -0
  249. package/.claude/skills/vcp-screener/scripts/scorer.py +155 -0
  250. package/.claude/skills/vcp-screener/scripts/screen_vcp.py +536 -0
  251. package/.claude/skills/vcp-screener/scripts/tests/__init__.py +0 -0
  252. package/.claude/skills/vcp-screener/scripts/tests/conftest.py +9 -0
  253. package/.claude/skills/vcp-screener/scripts/tests/test_vcp_screener.py +834 -0
  254. package/.claude/skills/weekly-trade-strategy/.claude/agents/druckenmiller-strategy-planner.md +300 -0
  255. package/.claude/skills/weekly-trade-strategy/.claude/agents/market-news-analyzer.md +239 -0
  256. package/.claude/skills/weekly-trade-strategy/.claude/agents/technical-market-analyst.md +187 -0
  257. package/.claude/skills/weekly-trade-strategy/.claude/agents/us-market-analyst.md +218 -0
  258. package/.claude/skills/weekly-trade-strategy/.claude/agents/weekly-trade-blog-writer.md +318 -0
  259. package/.claude/skills/weekly-trade-strategy/.claude/skills/breadth-chart-analyst/SKILL.md +662 -0
  260. package/.claude/skills/weekly-trade-strategy/.claude/skills/breadth-chart-analyst/assets/SP500_Breadth_Index_200MA_8MA.jpeg +0 -0
  261. package/.claude/skills/weekly-trade-strategy/.claude/skills/breadth-chart-analyst/assets/US_Stock_Market_Uptrend_Ratio.jpeg +0 -0
  262. package/.claude/skills/weekly-trade-strategy/.claude/skills/breadth-chart-analyst/assets/breadth_analysis_template.md +558 -0
  263. package/.claude/skills/weekly-trade-strategy/.claude/skills/breadth-chart-analyst/references/breadth_chart_methodology.md +590 -0
  264. package/.claude/skills/weekly-trade-strategy/.claude/skills/earnings-calendar/SKILL.md +721 -0
  265. package/.claude/skills/weekly-trade-strategy/.claude/skills/earnings-calendar/assets/earnings_report_template.md +102 -0
  266. package/.claude/skills/weekly-trade-strategy/.claude/skills/earnings-calendar/earnings_calendar_2025-11-02.md +447 -0
  267. package/.claude/skills/weekly-trade-strategy/.claude/skills/earnings-calendar/references/fmp_api_guide.md +590 -0
  268. package/.claude/skills/weekly-trade-strategy/.claude/skills/earnings-calendar/scripts/fetch_earnings_fmp.py +443 -0
  269. package/.claude/skills/weekly-trade-strategy/.claude/skills/earnings-calendar/scripts/generate_report.py +366 -0
  270. package/.claude/skills/weekly-trade-strategy/.claude/skills/economic-calendar-fetcher/SKILL.md +365 -0
  271. package/.claude/skills/weekly-trade-strategy/.claude/skills/economic-calendar-fetcher/references/fmp_api_documentation.md +345 -0
  272. package/.claude/skills/weekly-trade-strategy/.claude/skills/economic-calendar-fetcher/scripts/get_economic_calendar.py +267 -0
  273. package/.claude/skills/weekly-trade-strategy/.claude/skills/market-environment-analysis/SKILL.md +139 -0
  274. package/.claude/skills/weekly-trade-strategy/.claude/skills/market-environment-analysis/references/analysis_patterns.md +124 -0
  275. package/.claude/skills/weekly-trade-strategy/.claude/skills/market-environment-analysis/references/indicators.md +99 -0
  276. package/.claude/skills/weekly-trade-strategy/.claude/skills/market-environment-analysis/scripts/market_utils.py +127 -0
  277. package/.claude/skills/weekly-trade-strategy/.claude/skills/market-news-analyst/SKILL.md +714 -0
  278. package/.claude/skills/weekly-trade-strategy/.claude/skills/market-news-analyst/references/corporate_news_impact.md +446 -0
  279. package/.claude/skills/weekly-trade-strategy/.claude/skills/market-news-analyst/references/geopolitical_commodity_correlations.md +499 -0
  280. package/.claude/skills/weekly-trade-strategy/.claude/skills/market-news-analyst/references/market_event_patterns.md +393 -0
  281. package/.claude/skills/weekly-trade-strategy/.claude/skills/market-news-analyst/references/trusted_news_sources.md +510 -0
  282. package/.claude/skills/weekly-trade-strategy/.claude/skills/sector-analyst/SKILL.md +206 -0
  283. package/.claude/skills/weekly-trade-strategy/.claude/skills/sector-analyst/assets/industory_performance_1.jpeg +0 -0
  284. package/.claude/skills/weekly-trade-strategy/.claude/skills/sector-analyst/assets/industory_performance_2.jpeg +0 -0
  285. package/.claude/skills/weekly-trade-strategy/.claude/skills/sector-analyst/assets/sector_performance.jpeg +0 -0
  286. package/.claude/skills/weekly-trade-strategy/.claude/skills/sector-analyst/references/sector_rotation.md +170 -0
  287. package/.claude/skills/weekly-trade-strategy/.claude/skills/stanley-druckenmiller-investment/SKILL.md +84 -0
  288. package/.claude/skills/weekly-trade-strategy/.claude/skills/stanley-druckenmiller-investment/references/case-studies.md +148 -0
  289. package/.claude/skills/weekly-trade-strategy/.claude/skills/stanley-druckenmiller-investment/references/investment-philosophy.md +80 -0
  290. package/.claude/skills/weekly-trade-strategy/.claude/skills/stanley-druckenmiller-investment/references/market-analysis-guide.md +146 -0
  291. package/.claude/skills/weekly-trade-strategy/.claude/skills/technical-analyst/SKILL.md +238 -0
  292. package/.claude/skills/weekly-trade-strategy/.claude/skills/technical-analyst/assets/analysis_template.md +183 -0
  293. package/.claude/skills/weekly-trade-strategy/.claude/skills/technical-analyst/references/technical_analysis_framework.md +282 -0
  294. package/.claude/skills/weekly-trade-strategy/.claude/skills/us-market-bubble-detector/CHANGELOG.md +118 -0
  295. package/.claude/skills/weekly-trade-strategy/.claude/skills/us-market-bubble-detector/SKILL.md +545 -0
  296. package/.claude/skills/weekly-trade-strategy/.claude/skills/us-market-bubble-detector/references/bubble_framework.md +335 -0
  297. package/.claude/skills/weekly-trade-strategy/.claude/skills/us-market-bubble-detector/references/historical_cases.md +327 -0
  298. package/.claude/skills/weekly-trade-strategy/.claude/skills/us-market-bubble-detector/references/implementation_guide.md +473 -0
  299. package/.claude/skills/weekly-trade-strategy/.claude/skills/us-market-bubble-detector/references/quick_reference.md +354 -0
  300. package/.claude/skills/weekly-trade-strategy/.claude/skills/us-market-bubble-detector/references/quick_reference_en.md +342 -0
  301. package/.claude/skills/weekly-trade-strategy/.claude/skills/us-market-bubble-detector/scripts/bubble_scorer.py +309 -0
  302. package/.claude/skills/weekly-trade-strategy/.claude/skills/us-stock-analysis/SKILL.md +294 -0
  303. package/.claude/skills/weekly-trade-strategy/.claude/skills/us-stock-analysis/references/financial-metrics.md +172 -0
  304. package/.claude/skills/weekly-trade-strategy/.claude/skills/us-stock-analysis/references/fundamental-analysis.md +129 -0
  305. package/.claude/skills/weekly-trade-strategy/.claude/skills/us-stock-analysis/references/report-template.md +207 -0
  306. package/.claude/skills/weekly-trade-strategy/.claude/skills/us-stock-analysis/references/technical-analysis.md +93 -0
  307. package/.claude/skills/weekly-trade-strategy/CLAUDE.md +454 -0
  308. package/.claude/skills/weekly-trade-strategy/README.md +287 -0
  309. package/.claude/skills/weekly-trade-strategy/blogs/.gitkeep +0 -0
  310. package/.claude/skills/weekly-trade-strategy/charts/.gitkeep +0 -0
  311. package/.claude/skills/weekly-trade-strategy/earnings_data.json +10054 -0
  312. package/.claude/skills/weekly-trade-strategy/skills/breadth-chart-analyst/SKILL.md +662 -0
  313. package/.claude/skills/weekly-trade-strategy/skills/breadth-chart-analyst/assets/SP500_Breadth_Index_200MA_8MA.jpeg +0 -0
  314. package/.claude/skills/weekly-trade-strategy/skills/breadth-chart-analyst/assets/US_Stock_Market_Uptrend_Ratio.jpeg +0 -0
  315. package/.claude/skills/weekly-trade-strategy/skills/breadth-chart-analyst/assets/breadth_analysis_template.md +558 -0
  316. package/.claude/skills/weekly-trade-strategy/skills/breadth-chart-analyst/references/breadth_chart_methodology.md +590 -0
  317. package/.claude/skills/weekly-trade-strategy/skills/earnings-calendar/SKILL.md +721 -0
  318. package/.claude/skills/weekly-trade-strategy/skills/earnings-calendar/assets/earnings_report_template.md +102 -0
  319. package/.claude/skills/weekly-trade-strategy/skills/earnings-calendar/earnings_calendar_2025-11-02.md +447 -0
  320. package/.claude/skills/weekly-trade-strategy/skills/earnings-calendar/references/fmp_api_guide.md +590 -0
  321. package/.claude/skills/weekly-trade-strategy/skills/earnings-calendar/scripts/fetch_earnings_fmp.py +443 -0
  322. package/.claude/skills/weekly-trade-strategy/skills/earnings-calendar/scripts/generate_report.py +366 -0
  323. package/.claude/skills/weekly-trade-strategy/skills/economic-calendar-fetcher/SKILL.md +365 -0
  324. package/.claude/skills/weekly-trade-strategy/skills/economic-calendar-fetcher/references/fmp_api_documentation.md +345 -0
  325. package/.claude/skills/weekly-trade-strategy/skills/economic-calendar-fetcher/scripts/get_economic_calendar.py +267 -0
  326. package/.claude/skills/weekly-trade-strategy/skills/market-environment-analysis/SKILL.md +139 -0
  327. package/.claude/skills/weekly-trade-strategy/skills/market-environment-analysis/references/analysis_patterns.md +124 -0
  328. package/.claude/skills/weekly-trade-strategy/skills/market-environment-analysis/references/indicators.md +99 -0
  329. package/.claude/skills/weekly-trade-strategy/skills/market-environment-analysis/scripts/market_utils.py +127 -0
  330. package/.claude/skills/weekly-trade-strategy/skills/market-news-analyst/SKILL.md +714 -0
  331. package/.claude/skills/weekly-trade-strategy/skills/market-news-analyst/references/corporate_news_impact.md +446 -0
  332. package/.claude/skills/weekly-trade-strategy/skills/market-news-analyst/references/geopolitical_commodity_correlations.md +499 -0
  333. package/.claude/skills/weekly-trade-strategy/skills/market-news-analyst/references/market_event_patterns.md +393 -0
  334. package/.claude/skills/weekly-trade-strategy/skills/market-news-analyst/references/trusted_news_sources.md +510 -0
  335. package/.claude/skills/weekly-trade-strategy/skills/sector-analyst/SKILL.md +206 -0
  336. package/.claude/skills/weekly-trade-strategy/skills/sector-analyst/assets/industory_performance_1.jpeg +0 -0
  337. package/.claude/skills/weekly-trade-strategy/skills/sector-analyst/assets/industory_performance_2.jpeg +0 -0
  338. package/.claude/skills/weekly-trade-strategy/skills/sector-analyst/assets/sector_performance.jpeg +0 -0
  339. package/.claude/skills/weekly-trade-strategy/skills/sector-analyst/references/sector_rotation.md +170 -0
  340. package/.claude/skills/weekly-trade-strategy/skills/stanley-druckenmiller-investment/SKILL.md +84 -0
  341. package/.claude/skills/weekly-trade-strategy/skills/stanley-druckenmiller-investment/references/case-studies.md +148 -0
  342. package/.claude/skills/weekly-trade-strategy/skills/stanley-druckenmiller-investment/references/investment-philosophy.md +80 -0
  343. package/.claude/skills/weekly-trade-strategy/skills/stanley-druckenmiller-investment/references/market-analysis-guide.md +146 -0
  344. package/.claude/skills/weekly-trade-strategy/skills/technical-analyst/SKILL.md +238 -0
  345. package/.claude/skills/weekly-trade-strategy/skills/technical-analyst/assets/analysis_template.md +183 -0
  346. package/.claude/skills/weekly-trade-strategy/skills/technical-analyst/references/technical_analysis_framework.md +282 -0
  347. package/.claude/skills/weekly-trade-strategy/skills/us-market-bubble-detector/CHANGELOG.md +118 -0
  348. package/.claude/skills/weekly-trade-strategy/skills/us-market-bubble-detector/SKILL.md +545 -0
  349. package/.claude/skills/weekly-trade-strategy/skills/us-market-bubble-detector/references/bubble_framework.md +335 -0
  350. package/.claude/skills/weekly-trade-strategy/skills/us-market-bubble-detector/references/historical_cases.md +327 -0
  351. package/.claude/skills/weekly-trade-strategy/skills/us-market-bubble-detector/references/implementation_guide.md +473 -0
  352. package/.claude/skills/weekly-trade-strategy/skills/us-market-bubble-detector/references/quick_reference.md +354 -0
  353. package/.claude/skills/weekly-trade-strategy/skills/us-market-bubble-detector/references/quick_reference_en.md +342 -0
  354. package/.claude/skills/weekly-trade-strategy/skills/us-market-bubble-detector/scripts/bubble_scorer.py +309 -0
  355. package/.claude/skills/weekly-trade-strategy/skills/us-stock-analysis/SKILL.md +294 -0
  356. package/.claude/skills/weekly-trade-strategy/skills/us-stock-analysis/references/financial-metrics.md +172 -0
  357. package/.claude/skills/weekly-trade-strategy/skills/us-stock-analysis/references/fundamental-analysis.md +129 -0
  358. package/.claude/skills/weekly-trade-strategy/skills/us-stock-analysis/references/report-template.md +207 -0
  359. package/.claude/skills/weekly-trade-strategy/skills/us-stock-analysis/references/technical-analysis.md +93 -0
  360. package/.mcp.json +3 -0
  361. package/cli.mjs +16 -16
  362. package/package.json +4 -2
@@ -0,0 +1,349 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Market Top Detector - Main Orchestrator
4
+
5
+ Integrates O'Neil (Distribution Days), Minervini (Leading Stock Deterioration),
6
+ and Monty (Defensive Sector Rotation) approaches to detect market top probability.
7
+
8
+ Usage:
9
+ # With FMP API (recommended):
10
+ python3 market_top_detector.py --api-key YOUR_KEY \\
11
+ --breadth-200dma 62.26 --breadth-50dma 55.0 \\
12
+ --put-call 0.67 --vix-term contango
13
+
14
+ # Using environment variable:
15
+ export FMP_API_KEY=YOUR_KEY
16
+ python3 market_top_detector.py --breadth-200dma 62.26
17
+
18
+ # Minimal (VIX from API, rest from CLI):
19
+ python3 market_top_detector.py --api-key YOUR_KEY \\
20
+ --breadth-200dma 62.26 --put-call 0.67
21
+
22
+ Output:
23
+ - JSON: market_top_YYYY-MM-DD_HHMMSS.json
24
+ - Markdown: market_top_YYYY-MM-DD_HHMMSS.md
25
+ """
26
+
27
+ import argparse
28
+ import os
29
+ import sys
30
+ from datetime import datetime
31
+ from typing import Dict, List, Optional
32
+
33
+ # Add parent directory to path for imports
34
+ sys.path.insert(0, os.path.dirname(__file__))
35
+
36
+ from fmp_client import FMPClient
37
+ from calculators.distribution_day_calculator import calculate_distribution_days
38
+ from calculators.leading_stock_calculator import calculate_leading_stock_health, LEADING_ETFS
39
+ from calculators.defensive_rotation_calculator import (
40
+ calculate_defensive_rotation, DEFENSIVE_ETFS, OFFENSIVE_ETFS
41
+ )
42
+ from calculators.breadth_calculator import calculate_breadth_divergence
43
+ from calculators.index_technical_calculator import calculate_index_technical
44
+ from calculators.sentiment_calculator import calculate_sentiment
45
+ from scorer import calculate_composite_score, detect_follow_through_day
46
+ from report_generator import generate_json_report, generate_markdown_report
47
+
48
+
49
+ def parse_arguments():
50
+ parser = argparse.ArgumentParser(
51
+ description="Market Top Detector - O'Neil/Minervini/Monty Integration"
52
+ )
53
+
54
+ # API key
55
+ parser.add_argument(
56
+ "--api-key",
57
+ help="FMP API key (defaults to FMP_API_KEY environment variable)"
58
+ )
59
+
60
+ # WebSearch-sourced data (provided by Claude before script execution)
61
+ parser.add_argument(
62
+ "--breadth-200dma", type=float, default=None,
63
+ help="Percent of S&P 500 stocks above 200DMA (e.g., 62.26)"
64
+ )
65
+ parser.add_argument(
66
+ "--breadth-50dma", type=float, default=None,
67
+ help="Percent of S&P 500 stocks above 50DMA (e.g., 55.0)"
68
+ )
69
+ parser.add_argument(
70
+ "--put-call", type=float, default=None,
71
+ help="CBOE equity put/call ratio (e.g., 0.67)"
72
+ )
73
+ parser.add_argument(
74
+ "--vix-term", choices=["steep_contango", "contango", "flat", "backwardation"],
75
+ default=None,
76
+ help="VIX term structure state"
77
+ )
78
+ parser.add_argument(
79
+ "--margin-debt-yoy", type=float, default=None,
80
+ help="Margin debt year-over-year change percent (e.g., 36.0)"
81
+ )
82
+
83
+ # Additional context (not scored, but included in report)
84
+ parser.add_argument(
85
+ "--context", nargs="*", default=[],
86
+ help="Additional context items in 'key=value' format (e.g., 'Consumer Confidence=57.3')"
87
+ )
88
+
89
+ # Output
90
+ parser.add_argument(
91
+ "--output-dir", default=".",
92
+ help="Output directory for reports"
93
+ )
94
+
95
+ return parser.parse_args()
96
+
97
+
98
+ def main():
99
+ args = parse_arguments()
100
+
101
+ print("=" * 70)
102
+ print("Market Top Detector")
103
+ print("O'Neil (Distribution) + Minervini (Leadership) + Monty (Rotation)")
104
+ print("=" * 70)
105
+ print()
106
+
107
+ # Initialize FMP client
108
+ try:
109
+ client = FMPClient(api_key=args.api_key)
110
+ print("FMP API client initialized")
111
+ except ValueError as e:
112
+ print(f"ERROR: {e}", file=sys.stderr)
113
+ sys.exit(1)
114
+
115
+ # ========================================================================
116
+ # Step 1: Fetch shared data (indices, ETFs)
117
+ # ========================================================================
118
+ print()
119
+ print("Step 1: Fetching Market Data")
120
+ print("-" * 70)
121
+
122
+ # S&P 500 data
123
+ print(" Fetching S&P 500 data...", end=" ", flush=True)
124
+ sp500_quote_list = client.get_quote("^GSPC")
125
+ sp500_quote = sp500_quote_list[0] if sp500_quote_list else None
126
+ sp500_history_data = client.get_historical_prices("^GSPC", days=260)
127
+ sp500_history = sp500_history_data.get("historical", []) if sp500_history_data else []
128
+ if sp500_quote and sp500_history:
129
+ print(f"OK (${sp500_quote.get('price', 0):.2f}, {len(sp500_history)} days)")
130
+ else:
131
+ print("FAILED")
132
+ print("ERROR: Cannot proceed without S&P 500 data", file=sys.stderr)
133
+ sys.exit(1)
134
+
135
+ # NASDAQ/QQQ data
136
+ print(" Fetching NASDAQ (QQQ) data...", end=" ", flush=True)
137
+ qqq_quote_list = client.get_quote("QQQ")
138
+ qqq_quote = qqq_quote_list[0] if qqq_quote_list else None
139
+ qqq_history_data = client.get_historical_prices("QQQ", days=260)
140
+ qqq_history = qqq_history_data.get("historical", []) if qqq_history_data else []
141
+ if qqq_quote and qqq_history:
142
+ print(f"OK (${qqq_quote.get('price', 0):.2f}, {len(qqq_history)} days)")
143
+ else:
144
+ print("WARN - NASDAQ data unavailable, using S&P 500 only")
145
+
146
+ # VIX
147
+ print(" Fetching VIX...", end=" ", flush=True)
148
+ vix_quote_list = client.get_quote("^VIX")
149
+ vix_quote = vix_quote_list[0] if vix_quote_list else None
150
+ vix_level = vix_quote.get("price", None) if vix_quote else None
151
+ if vix_level:
152
+ print(f"OK ({vix_level:.2f})")
153
+ else:
154
+ print("WARN - VIX unavailable")
155
+
156
+ # Leading ETFs
157
+ print(" Fetching Leading ETFs...", end=" ", flush=True)
158
+ leading_quotes = client.get_batch_quotes(LEADING_ETFS)
159
+ leading_historical = client.get_batch_historical(LEADING_ETFS, days=60)
160
+ print(f"OK ({len(leading_quotes)} quotes, {len(leading_historical)} histories)")
161
+
162
+ # Sector ETFs
163
+ all_sector_etfs = list(set(DEFENSIVE_ETFS + OFFENSIVE_ETFS))
164
+ # Remove QQQ from sector fetching if already fetched
165
+ sector_etfs_to_fetch = [e for e in all_sector_etfs if e != "QQQ"]
166
+ print(" Fetching Sector ETFs...", end=" ", flush=True)
167
+ sector_historical = client.get_batch_historical(sector_etfs_to_fetch, days=30)
168
+ # Add QQQ history if available
169
+ if qqq_history:
170
+ sector_historical["QQQ"] = qqq_history[:30]
171
+ print(f"OK ({len(sector_historical)} ETFs)")
172
+
173
+ print()
174
+
175
+ # ========================================================================
176
+ # Step 2: Calculate Components
177
+ # ========================================================================
178
+ print("Step 2: Calculating Components")
179
+ print("-" * 70)
180
+
181
+ # Component 1: Distribution Days (25%)
182
+ print(" [1/6] Distribution Day Count...", end=" ", flush=True)
183
+ comp1 = calculate_distribution_days(sp500_history, qqq_history)
184
+ print(f"Score: {comp1['score']} ({comp1['signal']})")
185
+
186
+ # Component 2: Leading Stock Health (20%)
187
+ print(" [2/6] Leading Stock Health...", end=" ", flush=True)
188
+ comp2 = calculate_leading_stock_health(leading_quotes, leading_historical)
189
+ print(f"Score: {comp2['score']} ({comp2['signal']})")
190
+
191
+ # Component 3: Defensive Rotation (15%)
192
+ print(" [3/6] Defensive Sector Rotation...", end=" ", flush=True)
193
+ comp3 = calculate_defensive_rotation(sector_historical)
194
+ print(f"Score: {comp3['score']} ({comp3['signal']})")
195
+
196
+ # Component 4: Breadth Divergence (15%)
197
+ print(" [4/6] Market Breadth Divergence...", end=" ", flush=True)
198
+ # Calculate index distance from 52-week high
199
+ sp500_year_high = sp500_quote.get("yearHigh", 0)
200
+ sp500_price = sp500_quote.get("price", 0)
201
+ if sp500_year_high > 0:
202
+ index_dist = (sp500_price - sp500_year_high) / sp500_year_high * 100
203
+ else:
204
+ index_dist = 0
205
+
206
+ comp4 = calculate_breadth_divergence(
207
+ breadth_200dma=args.breadth_200dma,
208
+ breadth_50dma=args.breadth_50dma,
209
+ index_distance_from_high_pct=index_dist
210
+ )
211
+ print(f"Score: {comp4['score']} ({comp4['signal']})")
212
+
213
+ # Component 5: Index Technical (15%)
214
+ print(" [5/6] Index Technical Condition...", end=" ", flush=True)
215
+ comp5 = calculate_index_technical(
216
+ sp500_history, qqq_history,
217
+ sp500_quote=sp500_quote, nasdaq_quote=qqq_quote
218
+ )
219
+ print(f"Score: {comp5['score']} ({comp5['signal']})")
220
+
221
+ # Component 6: Sentiment (10%)
222
+ print(" [6/6] Sentiment & Speculation...", end=" ", flush=True)
223
+ comp6 = calculate_sentiment(
224
+ vix_level=vix_level,
225
+ put_call_ratio=args.put_call,
226
+ vix_term_structure=args.vix_term,
227
+ margin_debt_yoy_pct=args.margin_debt_yoy
228
+ )
229
+ print(f"Score: {comp6['score']} ({comp6['signal']})")
230
+
231
+ print()
232
+
233
+ # ========================================================================
234
+ # Step 3: Composite Score
235
+ # ========================================================================
236
+ print("Step 3: Calculating Composite Score")
237
+ print("-" * 70)
238
+
239
+ component_scores = {
240
+ "distribution_days": comp1["score"],
241
+ "leading_stocks": comp2["score"],
242
+ "defensive_rotation": comp3["score"],
243
+ "breadth_divergence": comp4["score"],
244
+ "index_technical": comp5["score"],
245
+ "sentiment": comp6["score"],
246
+ }
247
+
248
+ data_availability = {
249
+ "distribution_days": True, # Always available (requires S&P data to run)
250
+ "leading_stocks": comp2.get("data_available", True),
251
+ "defensive_rotation": comp3.get("data_available", True),
252
+ "breadth_divergence": comp4.get("data_available", True),
253
+ "index_technical": comp5.get("data_available", True),
254
+ "sentiment": comp6.get("data_available", True),
255
+ }
256
+
257
+ composite = calculate_composite_score(component_scores, data_availability)
258
+
259
+ print(f" Composite Score: {composite['composite_score']}/100")
260
+ print(f" Risk Zone: {composite['zone']}")
261
+ print(f" Risk Budget: {composite['risk_budget']}")
262
+ print(f" Strongest Warning: {composite['strongest_warning']['label']} "
263
+ f"({composite['strongest_warning']['score']})")
264
+ print()
265
+
266
+ # ========================================================================
267
+ # Step 4: Follow-Through Day Check
268
+ # ========================================================================
269
+ ftd = detect_follow_through_day(sp500_history, composite['composite_score'])
270
+ if ftd.get("applicable"):
271
+ print("Step 4: Follow-Through Day Monitor")
272
+ print("-" * 70)
273
+ print(f" {ftd['reason']}")
274
+ print()
275
+
276
+ # ========================================================================
277
+ # Step 5: Generate Reports
278
+ # ========================================================================
279
+ print("Step 5: Generating Reports")
280
+ print("-" * 70)
281
+
282
+ # Parse additional context
283
+ additional_context = {}
284
+ for item in args.context:
285
+ if "=" in item:
286
+ key, value = item.split("=", 1)
287
+ additional_context[key.strip()] = value.strip()
288
+
289
+ # Build full analysis
290
+ analysis = {
291
+ "metadata": {
292
+ "generated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
293
+ "data_mode": "FMP API + CLI inputs",
294
+ "api_calls": client.get_api_stats(),
295
+ "cli_inputs": {
296
+ "breadth_200dma": args.breadth_200dma,
297
+ "breadth_50dma": args.breadth_50dma,
298
+ "put_call_ratio": args.put_call,
299
+ "vix_term_structure": args.vix_term,
300
+ "margin_debt_yoy_pct": args.margin_debt_yoy,
301
+ },
302
+ "index_data": {
303
+ "sp500_price": sp500_price,
304
+ "sp500_year_high": sp500_year_high,
305
+ "sp500_distance_from_high_pct": round(index_dist, 2),
306
+ "qqq_price": qqq_quote.get("price", 0) if qqq_quote else None,
307
+ "vix_level": vix_level,
308
+ },
309
+ },
310
+ "composite": composite,
311
+ "components": {
312
+ "distribution_days": comp1,
313
+ "leading_stocks": comp2,
314
+ "defensive_rotation": comp3,
315
+ "breadth_divergence": comp4,
316
+ "index_technical": comp5,
317
+ "sentiment": comp6,
318
+ },
319
+ "follow_through_day": ftd,
320
+ "additional_context": additional_context,
321
+ }
322
+
323
+ timestamp = datetime.now().strftime("%Y-%m-%d_%H%M%S")
324
+ json_file = os.path.join(args.output_dir, f"market_top_{timestamp}.json")
325
+ md_file = os.path.join(args.output_dir, f"market_top_{timestamp}.md")
326
+
327
+ generate_json_report(analysis, json_file)
328
+ generate_markdown_report(analysis, md_file)
329
+
330
+ print()
331
+ print("=" * 70)
332
+ print("Market Top Detection Complete")
333
+ print("=" * 70)
334
+ print(f" Composite Score: {composite['composite_score']}/100")
335
+ print(f" Risk Zone: {composite['zone']}")
336
+ print(f" Risk Budget: {composite['risk_budget']}")
337
+ print(f" JSON Report: {json_file}")
338
+ print(f" Markdown Report: {md_file}")
339
+ print()
340
+
341
+ stats = client.get_api_stats()
342
+ print(f"API Usage:")
343
+ print(f" API calls made: {stats['api_calls_made']}")
344
+ print(f" Cache entries: {stats['cache_entries']}")
345
+ print()
346
+
347
+
348
+ if __name__ == "__main__":
349
+ main()
@@ -0,0 +1,314 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Market Top Detector - Report Generator
4
+
5
+ Generates JSON and Markdown reports for market top detection analysis.
6
+ """
7
+
8
+ import json
9
+ from datetime import datetime
10
+ from typing import Dict, List, Optional
11
+
12
+
13
+ def generate_json_report(analysis: Dict, output_file: str):
14
+ """Save full analysis as JSON"""
15
+ with open(output_file, 'w') as f:
16
+ json.dump(analysis, f, indent=2, default=str)
17
+ print(f"JSON report saved to: {output_file}")
18
+
19
+
20
+ def generate_markdown_report(analysis: Dict, output_file: str):
21
+ """Generate comprehensive Markdown report"""
22
+ lines = []
23
+ composite = analysis.get("composite", {})
24
+ components = analysis.get("components", {})
25
+ metadata = analysis.get("metadata", {})
26
+ ftd = analysis.get("follow_through_day", {})
27
+
28
+ score = composite.get("composite_score", 0)
29
+ zone = composite.get("zone", "Unknown")
30
+ risk_budget = composite.get("risk_budget", "N/A")
31
+
32
+ # Header
33
+ lines.append("# Market Top Detector Report")
34
+ lines.append("")
35
+ lines.append(f"**Generated:** {metadata.get('generated_at', 'N/A')}")
36
+ lines.append(f"**Data Mode:** {metadata.get('data_mode', 'N/A')}")
37
+ lines.append("")
38
+
39
+ # Overall Assessment Box
40
+ lines.append("---")
41
+ lines.append("")
42
+ lines.append("## Overall Assessment")
43
+ lines.append("")
44
+ zone_emoji = _zone_emoji(composite.get("zone_color", ""))
45
+ lines.append(f"| Metric | Value |")
46
+ lines.append(f"|--------|-------|")
47
+ lines.append(f"| **Composite Score** | **{score}/100** |")
48
+ lines.append(f"| **Risk Zone** | {zone_emoji} {zone} |")
49
+ lines.append(f"| **Risk Budget** | {risk_budget} |")
50
+ lines.append(f"| **Strongest Warning** | {composite.get('strongest_warning', {}).get('label', 'N/A')} "
51
+ f"({composite.get('strongest_warning', {}).get('score', 0)}/100) |")
52
+ lines.append(f"| **Weakest Warning** | {composite.get('weakest_warning', {}).get('label', 'N/A')} "
53
+ f"({composite.get('weakest_warning', {}).get('score', 0)}/100) |")
54
+ dq = composite.get("data_quality", {})
55
+ if dq:
56
+ lines.append(f"| **Data Quality** | {dq.get('label', 'N/A')} |")
57
+ lines.append("")
58
+
59
+ # Guidance
60
+ lines.append(f"> **Guidance:** {composite.get('guidance', '')}")
61
+ lines.append("")
62
+
63
+ # Component Scores Table
64
+ lines.append("---")
65
+ lines.append("")
66
+ lines.append("## Component Scores")
67
+ lines.append("")
68
+ lines.append("| # | Component | Weight | Score | Contribution | Signal |")
69
+ lines.append("|---|-----------|--------|-------|--------------|--------|")
70
+
71
+ component_order = [
72
+ "distribution_days", "leading_stocks", "defensive_rotation",
73
+ "breadth_divergence", "index_technical", "sentiment"
74
+ ]
75
+
76
+ for i, key in enumerate(component_order, 1):
77
+ comp = composite.get("component_scores", {}).get(key, {})
78
+ detail = components.get(key, {})
79
+ signal = detail.get("signal", "N/A")
80
+ score_val = comp.get("score", 0)
81
+ weight_pct = f"{comp.get('weight', 0)*100:.0f}%"
82
+ contribution = comp.get("weighted_contribution", 0)
83
+ bar = _score_bar(score_val)
84
+
85
+ lines.append(f"| {i} | **{comp.get('label', key)}** | {weight_pct} | "
86
+ f"{bar} {score_val} | {contribution:.1f} | {signal} |")
87
+
88
+ lines.append("")
89
+
90
+ # Detailed Component Breakdowns
91
+ lines.append("---")
92
+ lines.append("")
93
+ lines.append("## Component Details")
94
+ lines.append("")
95
+
96
+ # Component 1: Distribution Days
97
+ dist = components.get("distribution_days", {})
98
+ lines.append("### 1. Distribution Day Count")
99
+ lines.append("")
100
+ if dist:
101
+ sp = dist.get("sp500", {})
102
+ nq = dist.get("nasdaq", {})
103
+ lines.append(f"- **Effective Count:** {dist.get('effective_count', 0)} "
104
+ f"(Primary: {dist.get('primary_index', 'N/A')})")
105
+ lines.append(f"- **S&P 500:** {sp.get('distribution_days', 0)} distribution + "
106
+ f"{sp.get('stalling_days', 0)} stalling")
107
+ lines.append(f"- **NASDAQ:** {nq.get('distribution_days', 0)} distribution + "
108
+ f"{nq.get('stalling_days', 0)} stalling")
109
+ # Distribution day details
110
+ for idx_name, idx_data in [("S&P 500", sp), ("NASDAQ", nq)]:
111
+ details = idx_data.get("details", [])
112
+ if details:
113
+ lines.append(f"\n**{idx_name} Distribution Events:**")
114
+ for d in details[:5]:
115
+ lines.append(f" - {d.get('date', '?')}: {d.get('type', '?')} "
116
+ f"({d.get('pct_change', 0):+.2f}%, vol {d.get('volume_change', 0):+.1f}%)")
117
+ lines.append("")
118
+
119
+ # Component 2: Leading Stock Health
120
+ lead = components.get("leading_stocks", {})
121
+ lines.append("### 2. Leading Stock Health")
122
+ lines.append("")
123
+ if lead:
124
+ lines.append(f"- **ETFs Evaluated:** {lead.get('etfs_evaluated', 0)}")
125
+ lines.append(f"- **ETFs Deteriorating:** {lead.get('etfs_deteriorating', 0)} "
126
+ f"({lead.get('deteriorating_pct', 0):.0f}%)")
127
+ lines.append(f"- **Amplification Applied:** {'Yes (60%+ deteriorating)' if lead.get('amplified') else 'No'}")
128
+ etf_details = lead.get("etf_details", {})
129
+ if etf_details:
130
+ lines.append("")
131
+ lines.append("| ETF | Score | Distance from High | Flags |")
132
+ lines.append("|-----|-------|--------------------|-------|")
133
+ for sym, det in sorted(etf_details.items(),
134
+ key=lambda x: x[1].get("deterioration_score", 0),
135
+ reverse=True):
136
+ flags = det.get("flags", [])
137
+ flags_joined = "; ".join(flags)
138
+ flags_str = flags_joined if len(flags_joined) <= 80 else flags_joined[:77] + "..."
139
+ lines.append(f"| {sym} | {det.get('deterioration_score', 0)} | "
140
+ f"{det.get('distance_from_high_pct', 0):+.1f}% | {flags_str} |")
141
+ lines.append("")
142
+
143
+ # Component 3: Defensive Rotation
144
+ defr = components.get("defensive_rotation", {})
145
+ lines.append("### 3. Defensive Sector Rotation")
146
+ lines.append("")
147
+ if defr:
148
+ lines.append(f"- **Relative Performance (Def - Off):** "
149
+ f"{defr.get('relative_performance', 0):+.2f}% "
150
+ f"(over {defr.get('lookback_days', 20)} days)")
151
+ lines.append(f"- **Defensive Avg Return:** {defr.get('defensive_avg_return', 0):+.2f}%")
152
+ lines.append(f"- **Offensive Avg Return:** {defr.get('offensive_avg_return', 0):+.2f}%")
153
+
154
+ def_det = defr.get("defensive_details", {})
155
+ off_det = defr.get("offensive_details", {})
156
+ if def_det or off_det:
157
+ lines.append("")
158
+ lines.append("| Defensive ETF | Return | | Offensive ETF | Return |")
159
+ lines.append("|---------------|--------|-|---------------|--------|")
160
+ def_items = list(def_det.items())
161
+ off_items = list(off_det.items())
162
+ max_len = max(len(def_items), len(off_items))
163
+ for j in range(max_len):
164
+ d_sym = def_items[j][0] if j < len(def_items) else ""
165
+ d_ret = f"{def_items[j][1]:+.2f}%" if j < len(def_items) else ""
166
+ o_sym = off_items[j][0] if j < len(off_items) else ""
167
+ o_ret = f"{off_items[j][1]:+.2f}%" if j < len(off_items) else ""
168
+ lines.append(f"| {d_sym} | {d_ret} | | {o_sym} | {o_ret} |")
169
+ lines.append("")
170
+
171
+ # Component 4: Breadth
172
+ brd = components.get("breadth_divergence", {})
173
+ lines.append("### 4. Market Breadth Divergence")
174
+ lines.append("")
175
+ if brd:
176
+ lines.append(f"- **200DMA Breadth:** {brd.get('breadth_200dma', 'N/A')}%")
177
+ lines.append(f"- **50DMA Breadth:** {brd.get('breadth_50dma', 'N/A')}%")
178
+ lines.append(f"- **Index Near Highs:** {'Yes' if brd.get('index_near_highs') else 'No'} "
179
+ f"({brd.get('index_distance_from_high_pct', 0):+.1f}% from 52wk high)")
180
+ lines.append(f"- **Divergence Detected:** {'YES' if brd.get('divergence_detected') else 'No'}")
181
+ lines.append("")
182
+
183
+ # Component 5: Index Technical
184
+ tech = components.get("index_technical", {})
185
+ lines.append("### 5. Index Technical Condition")
186
+ lines.append("")
187
+ if tech:
188
+ for idx_name in ["sp500", "nasdaq"]:
189
+ idx = tech.get(idx_name, {})
190
+ if idx:
191
+ label = "S&P 500" if idx_name == "sp500" else "NASDAQ"
192
+ lines.append(f"**{label}:** (Score: {idx.get('raw_score', 0)})")
193
+ for flag in idx.get("flags", []):
194
+ lines.append(f" - {flag}")
195
+ mas = idx.get("mas", {})
196
+ if mas:
197
+ ma_str = ", ".join([f"{k}: ${v}" for k, v in mas.items()])
198
+ lines.append(f" - Moving Averages: {ma_str}")
199
+ lines.append("")
200
+ lines.append("")
201
+
202
+ # Component 6: Sentiment
203
+ sent = components.get("sentiment", {})
204
+ lines.append("### 6. Sentiment & Speculation")
205
+ lines.append("")
206
+ if sent:
207
+ details = sent.get("details", {})
208
+ pc = details.get("put_call_ratio", {})
209
+ vix = details.get("vix_level", {})
210
+ vts = details.get("vix_term_structure", {})
211
+ md = details.get("margin_debt", {})
212
+
213
+ lines.append(f"- **Put/Call Ratio:** {pc.get('value', 'N/A')} "
214
+ f"(+{pc.get('points', 0)}pt) - {pc.get('interpretation', '')}")
215
+ lines.append(f"- **VIX Level:** {vix.get('value', 'N/A')} "
216
+ f"({vix.get('points', 0):+d}pt) - {vix.get('interpretation', '')}")
217
+ lines.append(f"- **VIX Term Structure:** {vts.get('value', 'N/A')} "
218
+ f"({vts.get('points', 0):+d}pt) - {vts.get('interpretation', '')}")
219
+ if md:
220
+ lines.append(f"- **Margin Debt YoY:** {md.get('yoy_pct', 'N/A')}% "
221
+ f"- {md.get('interpretation', '')} *(context only, not scored)*")
222
+ lines.append("")
223
+
224
+ # Follow-Through Day Monitor
225
+ if ftd and ftd.get("applicable"):
226
+ lines.append("---")
227
+ lines.append("")
228
+ lines.append("## Follow-Through Day Monitor")
229
+ lines.append("")
230
+ if ftd.get("ftd_detected"):
231
+ lines.append(f"**FTD DETECTED** on {ftd.get('ftd_day', 'N/A')}")
232
+ else:
233
+ lines.append(f"- Rally Day Count: {ftd.get('rally_day_count', 0)}")
234
+ lines.append(f"- {ftd.get('reason', '')}")
235
+ lines.append("")
236
+
237
+ # Recommended Actions
238
+ lines.append("---")
239
+ lines.append("")
240
+ lines.append("## Recommended Actions")
241
+ lines.append("")
242
+ lines.append(f"**Risk Zone:** {zone}")
243
+ lines.append(f"**Risk Budget:** {risk_budget}")
244
+ lines.append("")
245
+ for action in composite.get("actions", []):
246
+ lines.append(f"- {action}")
247
+ lines.append("")
248
+
249
+ # Additional Context
250
+ extra = analysis.get("additional_context", {})
251
+ if extra:
252
+ lines.append("---")
253
+ lines.append("")
254
+ lines.append("## Additional Context (Not Scored)")
255
+ lines.append("")
256
+ for key, value in extra.items():
257
+ lines.append(f"- **{key}:** {value}")
258
+ lines.append("")
259
+
260
+ # Methodology
261
+ lines.append("---")
262
+ lines.append("")
263
+ lines.append("## Methodology")
264
+ lines.append("")
265
+ lines.append("This analysis integrates three complementary market top detection approaches:")
266
+ lines.append("")
267
+ lines.append("1. **O'Neil (Distribution Days):** Institutional selling pressure via volume-confirmed declines")
268
+ lines.append("2. **Minervini (Leading Stock Deterioration):** Growth leadership breakdown pattern")
269
+ lines.append("3. **Monty (Defensive Rotation):** Capital flow from offensive to defensive sectors")
270
+ lines.append("")
271
+ lines.append("Additional components (Breadth, Technical, Sentiment) provide confirmation signals.")
272
+ lines.append("Composite score is a weighted average of all 6 components (0-100 scale).")
273
+ lines.append("")
274
+ lines.append("For detailed methodology, see `references/market_top_methodology.md`.")
275
+ lines.append("")
276
+
277
+ # Disclaimer
278
+ lines.append("---")
279
+ lines.append("")
280
+ lines.append("**Disclaimer:** This analysis is for educational and informational purposes only. "
281
+ "Not investment advice. Past patterns may not predict future outcomes. "
282
+ "Conduct your own research and consult a financial advisor before making "
283
+ "investment decisions.")
284
+ lines.append("")
285
+
286
+ with open(output_file, 'w') as f:
287
+ f.write('\n'.join(lines))
288
+
289
+ print(f"Markdown report saved to: {output_file}")
290
+
291
+
292
+ def _zone_emoji(color: str) -> str:
293
+ mapping = {
294
+ "green": "🟢",
295
+ "yellow": "🟡",
296
+ "orange": "🟠",
297
+ "red": "🔴",
298
+ "critical": "⚫",
299
+ }
300
+ return mapping.get(color, "⚪")
301
+
302
+
303
+ def _score_bar(score: int) -> str:
304
+ """Simple text bar for score visualization"""
305
+ if score >= 80:
306
+ return "████"
307
+ elif score >= 60:
308
+ return "███░"
309
+ elif score >= 40:
310
+ return "██░░"
311
+ elif score >= 20:
312
+ return "█░░░"
313
+ else:
314
+ return "░░░░"