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,150 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Component 4: Bearish Signal Status (Weight: 15%)
4
+
5
+ Evaluates the backtested bearish signal flag and its context.
6
+ Also detects the "Pink Zone" (Bearish Region) from the source repository:
7
+ Pink Zone = Breadth_200MA_Trend == -1 AND 8MA < 200MA
8
+
9
+ Input: Bearish_Signal, Breadth_200MA_Trend, Breadth_Index_8MA,
10
+ Breadth_Index_200MA (latest row)
11
+
12
+ Scoring (100 = healthy):
13
+ Base:
14
+ Signal=False & Trend=1 -> 85 (all clear)
15
+ Signal=False & Trend=-1 -> 50 (no warning but downtrend)
16
+ Signal=True & Trend=1 -> 30 (warning in uptrend)
17
+ Signal=True & Trend=-1 -> 10 (fully bearish)
18
+
19
+ Context Adjustment:
20
+ Signal=True & 8MA > 0.50 -> +15 (weak signal amid strong breadth)
21
+ Signal=True & 8MA < 0.25 -> -5 (severe signal amid extreme weakness)
22
+
23
+ Pink Zone Adjustment:
24
+ In Pink Zone & Signal=False -> -10 (structural weakness even without signal)
25
+ """
26
+
27
+ from typing import Dict, List
28
+
29
+
30
+ def calculate_bearish_signal(rows: List[Dict]) -> Dict:
31
+ """
32
+ Calculate bearish signal status score.
33
+
34
+ Args:
35
+ rows: All detail rows sorted by date ascending.
36
+
37
+ Returns:
38
+ Dict with score, signal, and component details.
39
+ """
40
+ if not rows:
41
+ return {
42
+ "score": 50,
43
+ "signal": "NO DATA: No data available for bearish signal analysis",
44
+ "data_available": False,
45
+ }
46
+
47
+ latest = rows[-1]
48
+ signal_active = latest["Bearish_Signal"]
49
+ trend = latest["Breadth_200MA_Trend"]
50
+ ma8 = latest["Breadth_Index_8MA"]
51
+ ma200 = latest["Breadth_Index_200MA"]
52
+
53
+ # Pink Zone detection (from source repo: chart's pink background)
54
+ # Condition: 200MA trend declining AND 8MA below 200MA
55
+ in_pink_zone = (trend == -1) and (ma8 < ma200)
56
+
57
+ # Count consecutive pink zone days
58
+ pink_zone_days = 0
59
+ if in_pink_zone:
60
+ for i in range(len(rows) - 1, -1, -1):
61
+ r = rows[i]
62
+ if (r["Breadth_200MA_Trend"] == -1 and
63
+ r["Breadth_Index_8MA"] < r["Breadth_Index_200MA"]):
64
+ pink_zone_days += 1
65
+ else:
66
+ break
67
+
68
+ # Base score
69
+ base_score = _base_score(signal_active, trend)
70
+
71
+ # Context adjustment
72
+ context_adj = 0
73
+ if signal_active:
74
+ if ma8 > 0.50:
75
+ context_adj = +15 # Weak signal amid strong breadth
76
+ elif ma8 < 0.25:
77
+ context_adj = -5 # Severe signal amid extreme weakness
78
+
79
+ # Pink Zone adjustment: structural weakness even without bearish signal
80
+ pink_zone_adj = 0
81
+ if in_pink_zone and not signal_active:
82
+ pink_zone_adj = -10
83
+
84
+ score = round(base_score + context_adj + pink_zone_adj)
85
+ score = max(0, min(100, score))
86
+
87
+ signal_text = _generate_signal(signal_active, trend, ma8, in_pink_zone, score)
88
+
89
+ return {
90
+ "score": score,
91
+ "signal": signal_text,
92
+ "data_available": True,
93
+ "signal_active": signal_active,
94
+ "trend": trend,
95
+ "current_8ma": ma8,
96
+ "current_200ma": ma200,
97
+ "in_pink_zone": in_pink_zone,
98
+ "pink_zone_days": pink_zone_days,
99
+ "base_score": base_score,
100
+ "context_adjustment": context_adj,
101
+ "pink_zone_adjustment": pink_zone_adj,
102
+ "date": latest["Date"],
103
+ }
104
+
105
+
106
+ def _base_score(signal_active: bool, trend: int) -> int:
107
+ """Calculate base score from signal and trend combination."""
108
+ if not signal_active and trend == 1:
109
+ return 85 # All clear
110
+ elif not signal_active and trend == -1:
111
+ return 50 # No warning but downtrend
112
+ elif signal_active and trend == 1:
113
+ return 30 # Warning in uptrend
114
+ else: # signal_active and trend == -1
115
+ return 10 # Fully bearish
116
+
117
+
118
+ def _generate_signal(
119
+ signal_active: bool, trend: int, ma8: float,
120
+ in_pink_zone: bool, score: int,
121
+ ) -> str:
122
+ """Generate human-readable signal."""
123
+ trend_str = "uptrend" if trend == 1 else "downtrend"
124
+ pink_str = " [PINK ZONE]" if in_pink_zone else ""
125
+
126
+ if not signal_active:
127
+ if trend == 1:
128
+ return f"ALL CLEAR: No bearish signal, {trend_str}"
129
+ elif in_pink_zone:
130
+ return (
131
+ f"CAUTION: No bearish signal but in Pink Zone "
132
+ f"(downtrend + 8MA < 200MA){pink_str}"
133
+ )
134
+ else:
135
+ return f"CAUTION: No bearish signal but {trend_str}"
136
+ else:
137
+ if ma8 > 0.50:
138
+ return (
139
+ f"WARNING (muted): Bearish signal active in {trend_str}, "
140
+ f"but 8MA={ma8:.3f} still relatively strong{pink_str}"
141
+ )
142
+ elif ma8 < 0.25:
143
+ return (
144
+ f"CRITICAL: Bearish signal active in {trend_str}, "
145
+ f"8MA={ma8:.3f} extremely weak{pink_str}"
146
+ )
147
+ else:
148
+ return (
149
+ f"BEARISH: Signal active in {trend_str}, 8MA={ma8:.3f}{pink_str}"
150
+ )
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Component 3: Peak/Trough Cycle Position (Weight: 20%)
4
+
5
+ Determines where we are in the breadth cycle relative to recent peaks and troughs.
6
+
7
+ Input: Is_Peak, Is_Trough, Is_Trough_8MA_Below_04, Breadth_Index_8MA (last 120 days)
8
+
9
+ Scoring (100 = healthy):
10
+ Latest marker = TROUGH:
11
+ <= 20 days & 8MA rising -> 85 (early recovery)
12
+ <= 20 days & 8MA flat/falling -> 30 (failed reversal)
13
+ 21-60 days & 8MA rising -> 75 (sustained recovery)
14
+ > 60 days & 8MA rising -> 65 (mature recovery)
15
+
16
+ Latest marker = PEAK:
17
+ <= 20 days & 8MA falling -> 20 (post-peak decline)
18
+ <= 20 days & 8MA flat/rising -> 60 (high-level consolidation)
19
+ 21-60 days & 8MA falling -> 15 (sustained decline)
20
+ > 60 days & 8MA falling -> 10 (prolonged decline)
21
+ > 60 days & 8MA rising -> 50 (possible bottom formation)
22
+
23
+ Extreme Trough Bonus: Is_Trough_8MA_Below_04 == True -> +10
24
+ No marker within 120 days -> 50 (neutral)
25
+ """
26
+
27
+ from typing import Dict, List, Optional, Tuple
28
+
29
+
30
+ def calculate_cycle_position(rows: List[Dict]) -> Dict:
31
+ """
32
+ Calculate peak/trough cycle position score.
33
+
34
+ Args:
35
+ rows: All detail rows sorted by date ascending.
36
+
37
+ Returns:
38
+ Dict with score, signal, and component details.
39
+ """
40
+ if not rows or len(rows) < 10:
41
+ return {
42
+ "score": 50,
43
+ "signal": "NO DATA: Insufficient data for cycle analysis",
44
+ "data_available": False,
45
+ }
46
+
47
+ # Look at last 120 trading days
48
+ lookback = min(120, len(rows))
49
+ recent = rows[-lookback:]
50
+ latest = rows[-1]
51
+ current_8ma = latest["Breadth_Index_8MA"]
52
+
53
+ # Find latest peak or trough marker
54
+ marker_type, marker_idx, days_since = _find_latest_marker(recent)
55
+
56
+ # Determine 8MA trend (rising/falling) over last 5 days
57
+ if len(rows) >= 6:
58
+ ma8_trend = "rising" if current_8ma > rows[-6]["Breadth_Index_8MA"] else "falling"
59
+ else:
60
+ ma8_trend = "unknown"
61
+
62
+ # Check for extreme trough
63
+ extreme_trough = False
64
+ if marker_type == "TROUGH":
65
+ extreme_trough = recent[marker_idx]["Is_Trough_8MA_Below_04"]
66
+
67
+ # Calculate score
68
+ score = _calculate_score(marker_type, days_since, ma8_trend, extreme_trough)
69
+ score = max(0, min(100, score))
70
+
71
+ signal = _generate_signal(marker_type, days_since, ma8_trend, score)
72
+
73
+ return {
74
+ "score": score,
75
+ "signal": signal,
76
+ "data_available": True,
77
+ "latest_marker_type": marker_type,
78
+ "days_since_marker": days_since,
79
+ "ma8_trend": ma8_trend,
80
+ "current_8ma": current_8ma,
81
+ "extreme_trough": extreme_trough,
82
+ "date": latest["Date"],
83
+ }
84
+
85
+
86
+ def _find_latest_marker(recent: List[Dict]) -> Tuple[Optional[str], int, Optional[int]]:
87
+ """
88
+ Find the most recent peak or trough marker.
89
+
90
+ Returns (marker_type, index_in_recent, days_since).
91
+ """
92
+ for i in range(len(recent) - 1, -1, -1):
93
+ row = recent[i]
94
+ if row["Is_Peak"]:
95
+ days_since = len(recent) - 1 - i
96
+ return "PEAK", i, days_since
97
+ if row["Is_Trough"] or row["Is_Trough_8MA_Below_04"]:
98
+ days_since = len(recent) - 1 - i
99
+ return "TROUGH", i, days_since
100
+
101
+ return None, -1, None
102
+
103
+
104
+ def _calculate_score(
105
+ marker_type: Optional[str],
106
+ days_since: Optional[int],
107
+ ma8_trend: str,
108
+ extreme_trough: bool,
109
+ ) -> int:
110
+ """Calculate cycle position score based on marker and context."""
111
+ if marker_type is None or days_since is None:
112
+ return 50 # Neutral when no marker found
113
+
114
+ rising = ma8_trend == "rising"
115
+
116
+ if marker_type == "TROUGH":
117
+ if days_since <= 20:
118
+ base = 85 if rising else 30
119
+ elif days_since <= 60:
120
+ base = 75 if rising else 35
121
+ else:
122
+ base = 65 if rising else 40
123
+
124
+ # Extreme trough bonus (contrarian buy signal)
125
+ if extreme_trough:
126
+ base += 10
127
+
128
+ elif marker_type == "PEAK":
129
+ if days_since <= 20:
130
+ base = 60 if rising else 20
131
+ elif days_since <= 60:
132
+ base = 45 if rising else 15
133
+ else:
134
+ base = 50 if rising else 10
135
+
136
+ else:
137
+ base = 50
138
+
139
+ return base
140
+
141
+
142
+ def _generate_signal(
143
+ marker_type: Optional[str],
144
+ days_since: Optional[int],
145
+ ma8_trend: str,
146
+ score: int,
147
+ ) -> str:
148
+ """Generate human-readable signal."""
149
+ if marker_type is None:
150
+ return "NEUTRAL: No cycle marker in last 120 days"
151
+
152
+ if marker_type == "TROUGH":
153
+ if days_since <= 20:
154
+ phase = "early recovery" if ma8_trend == "rising" else "failed reversal attempt"
155
+ elif days_since <= 60:
156
+ phase = "sustained recovery" if ma8_trend == "rising" else "stalled recovery"
157
+ else:
158
+ phase = "mature recovery" if ma8_trend == "rising" else "weakening recovery"
159
+ return f"TROUGH ({days_since}d ago): {phase}, 8MA {ma8_trend}"
160
+
161
+ else: # PEAK
162
+ if days_since <= 20:
163
+ phase = "consolidation near highs" if ma8_trend == "rising" else "post-peak decline"
164
+ elif days_since <= 60:
165
+ phase = "gradual decline" if ma8_trend != "rising" else "recovery attempt"
166
+ else:
167
+ phase = "possible bottom formation" if ma8_trend == "rising" else "prolonged decline"
168
+ return f"PEAK ({days_since}d ago): {phase}, 8MA {ma8_trend}"
@@ -0,0 +1,119 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Component 6: S&P 500 vs Breadth Divergence (Weight: 10%)
4
+
5
+ Detects divergence between price action and breadth participation.
6
+
7
+ Input: S&P500_Price, Breadth_Index_8MA (last 60 days)
8
+
9
+ Scoring (100 = healthy):
10
+ 60-day changes:
11
+ sp500_pct = (latest - 60d_ago) / 60d_ago * 100
12
+ breadth_change = latest_8ma - 8ma_60d_ago
13
+
14
+ Both rising -> 70 (healthy rally)
15
+ Both falling -> 30 (consistent decline)
16
+ SP up(>3%) & Breadth down(<-0.05) -> 10 (dangerous divergence)
17
+ SP up(>1%) & Breadth down(<-0.03) -> 25
18
+ SP down(<-3%) & Breadth up(>+0.05) -> 80 (bullish divergence)
19
+ SP down(<-1%) & Breadth up(>+0.03) -> 65
20
+ Otherwise -> 50
21
+ """
22
+
23
+ from typing import Dict, List
24
+
25
+
26
+ def calculate_divergence(rows: List[Dict]) -> Dict:
27
+ """
28
+ Calculate S&P 500 vs breadth divergence score.
29
+
30
+ Args:
31
+ rows: All detail rows sorted by date ascending.
32
+
33
+ Returns:
34
+ Dict with score, signal, and component details.
35
+ """
36
+ if not rows or len(rows) < 60:
37
+ # Use whatever data is available, minimum 20 days
38
+ if not rows or len(rows) < 20:
39
+ return {
40
+ "score": 50,
41
+ "signal": "NO DATA: Insufficient data for divergence analysis",
42
+ "data_available": False,
43
+ }
44
+
45
+ lookback = min(60, len(rows))
46
+ latest = rows[-1]
47
+ past = rows[-lookback]
48
+
49
+ sp_latest = latest["S&P500_Price"]
50
+ sp_past = past["S&P500_Price"]
51
+ ma8_latest = latest["Breadth_Index_8MA"]
52
+ ma8_past = past["Breadth_Index_8MA"]
53
+
54
+ if sp_past <= 0:
55
+ return {
56
+ "score": 50,
57
+ "signal": "NO DATA: Invalid S&P 500 price data",
58
+ "data_available": False,
59
+ }
60
+
61
+ sp500_pct = (sp_latest - sp_past) / sp_past * 100
62
+ breadth_change = ma8_latest - ma8_past
63
+
64
+ # Determine divergence type and score
65
+ score, div_type = _score_divergence(sp500_pct, breadth_change)
66
+ score = max(0, min(100, score))
67
+
68
+ signal = _generate_signal(sp500_pct, breadth_change, div_type, score)
69
+
70
+ return {
71
+ "score": score,
72
+ "signal": signal,
73
+ "data_available": True,
74
+ "sp500_pct_change": round(sp500_pct, 2),
75
+ "breadth_change": round(breadth_change, 4),
76
+ "sp500_latest": sp_latest,
77
+ "sp500_past": sp_past,
78
+ "ma8_latest": ma8_latest,
79
+ "ma8_past": ma8_past,
80
+ "lookback_days": lookback,
81
+ "divergence_type": div_type,
82
+ "date": latest["Date"],
83
+ }
84
+
85
+
86
+ def _score_divergence(sp_pct: float, breadth_chg: float) -> tuple:
87
+ """Score based on price/breadth divergence. Returns (score, type_label)."""
88
+ sp_up = sp_pct > 0
89
+ breadth_up = breadth_chg > 0
90
+
91
+ # Dangerous divergence: SP up, breadth down
92
+ if sp_pct > 3.0 and breadth_chg < -0.05:
93
+ return 10, "Dangerous bearish divergence"
94
+ if sp_pct > 1.0 and breadth_chg < -0.03:
95
+ return 25, "Moderate bearish divergence"
96
+
97
+ # Bullish divergence: SP down, breadth up
98
+ if sp_pct < -3.0 and breadth_chg > 0.05:
99
+ return 80, "Strong bullish divergence"
100
+ if sp_pct < -1.0 and breadth_chg > 0.03:
101
+ return 65, "Moderate bullish divergence"
102
+
103
+ # Aligned movements
104
+ if sp_up and breadth_up:
105
+ return 70, "Healthy alignment (both rising)"
106
+ if not sp_up and not breadth_up:
107
+ return 30, "Consistent decline (both falling)"
108
+
109
+ return 50, "Mixed signals"
110
+
111
+
112
+ def _generate_signal(
113
+ sp_pct: float, breadth_chg: float, div_type: str, score: int
114
+ ) -> str:
115
+ """Generate human-readable signal."""
116
+ return (
117
+ f"{div_type}: S&P {sp_pct:+.1f}%, "
118
+ f"Breadth 8MA {breadth_chg:+.3f} over 60d"
119
+ )
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Component 5: Historical Percentile (Weight: 10%)
4
+
5
+ Determines where the current breadth level sits in the full historical distribution.
6
+
7
+ Input: Breadth_Index_8MA (all rows), Summary CSV (average peak/trough values)
8
+
9
+ Scoring (100 = healthy):
10
+ Percentile rank of current 8MA among all historical 8MA values:
11
+ >= 80th -> 90 >= 60th -> 70 >= 40th -> 50
12
+ >= 20th -> 30 < 20th -> 10
13
+
14
+ Adjustment:
15
+ Current >= avg_peak * 0.95 -> -10 (overheated caution)
16
+ Current <= avg_trough * 1.05 -> +10 (oversold zone)
17
+ """
18
+
19
+ from typing import Dict, List, Optional
20
+
21
+
22
+ def calculate_historical_percentile(
23
+ rows: List[Dict],
24
+ summary: Dict[str, str],
25
+ ) -> Dict:
26
+ """
27
+ Calculate historical percentile score.
28
+
29
+ Args:
30
+ rows: All detail rows sorted by date ascending.
31
+ summary: Summary CSV as dict {Metric: Value}.
32
+
33
+ Returns:
34
+ Dict with score, signal, and component details.
35
+ """
36
+ if not rows:
37
+ return {
38
+ "score": 50,
39
+ "signal": "NO DATA: No data available for historical analysis",
40
+ "data_available": False,
41
+ }
42
+
43
+ # Extract all 8MA values
44
+ all_8ma = [r["Breadth_Index_8MA"] for r in rows]
45
+ current_8ma = all_8ma[-1]
46
+
47
+ # Calculate percentile rank
48
+ below_count = sum(1 for v in all_8ma if v < current_8ma)
49
+ percentile = (below_count / len(all_8ma)) * 100
50
+
51
+ # Base score from percentile
52
+ base_score = _score_percentile(percentile)
53
+
54
+ # Parse summary values
55
+ avg_peak = _safe_float(summary.get("Average Peaks (200MA)", ""))
56
+ avg_trough = _safe_float(summary.get("Average Troughs (8MA < 0.4)", ""))
57
+
58
+ # Adjustment for extremes
59
+ adjustment = 0
60
+ if avg_peak is not None and current_8ma >= avg_peak * 0.95:
61
+ adjustment = -10 # Overheated
62
+ elif avg_trough is not None and current_8ma <= avg_trough * 1.05:
63
+ adjustment = +10 # Oversold
64
+
65
+ score = round(base_score + adjustment)
66
+ score = max(0, min(100, score))
67
+
68
+ signal = _generate_signal(percentile, adjustment, score)
69
+
70
+ return {
71
+ "score": score,
72
+ "signal": signal,
73
+ "data_available": True,
74
+ "current_8ma": current_8ma,
75
+ "percentile_rank": percentile,
76
+ "base_score": base_score,
77
+ "adjustment": adjustment,
78
+ "avg_peak": avg_peak,
79
+ "avg_trough": avg_trough,
80
+ "total_observations": len(all_8ma),
81
+ "date": rows[-1]["Date"],
82
+ }
83
+
84
+
85
+ def _score_percentile(percentile: float) -> int:
86
+ """Score from percentile rank."""
87
+ if percentile >= 80:
88
+ return 90
89
+ elif percentile >= 60:
90
+ return 70
91
+ elif percentile >= 40:
92
+ return 50
93
+ elif percentile >= 20:
94
+ return 30
95
+ else:
96
+ return 10
97
+
98
+
99
+ def _safe_float(val: str) -> Optional[float]:
100
+ """Safely parse float from string."""
101
+ try:
102
+ return float(val)
103
+ except (ValueError, TypeError):
104
+ return None
105
+
106
+
107
+ def _generate_signal(percentile: float, adjustment: int, score: int) -> str:
108
+ """Generate human-readable signal."""
109
+ adj_str = ""
110
+ if adjustment < 0:
111
+ adj_str = " (near historical peak - overheated)"
112
+ elif adjustment > 0:
113
+ adj_str = " (near historical trough - oversold)"
114
+
115
+ if score >= 70:
116
+ return f"HIGH: {percentile:.0f}th percentile - above average breadth{adj_str}"
117
+ elif score >= 40:
118
+ return f"AVERAGE: {percentile:.0f}th percentile - normal range{adj_str}"
119
+ else:
120
+ return f"LOW: {percentile:.0f}th percentile - below average breadth{adj_str}"
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Component 2: 8MA vs 200MA Crossover Dynamics (Weight: 20%)
4
+
5
+ Detects momentum and direction changes via the gap between fast and slow MAs.
6
+
7
+ Input: Breadth_Index_8MA, Breadth_Index_200MA (latest + 5-day lookback)
8
+
9
+ Scoring (100 = healthy):
10
+ Gap = 8MA - 200MA
11
+ >= +0.15 -> 95 >= +0.10 -> 80 >= +0.05 -> 65
12
+ >= 0.00 -> 50 >= -0.05 -> 35 >= -0.10 -> 20
13
+ < -0.10 -> 5
14
+
15
+ Direction Modifier (5-day 8MA trend):
16
+ Gap < 0 and 8MA rising -> +10 (recovery signal)
17
+ Gap > 0 and 8MA falling -> -10 (deterioration signal)
18
+ """
19
+
20
+ from typing import Dict, List
21
+
22
+
23
+ def calculate_ma_crossover(rows: List[Dict]) -> Dict:
24
+ """
25
+ Calculate 8MA vs 200MA crossover dynamics score.
26
+
27
+ Args:
28
+ rows: All detail rows sorted by date ascending.
29
+
30
+ Returns:
31
+ Dict with score, signal, and component details.
32
+ """
33
+ if not rows or len(rows) < 6:
34
+ return {
35
+ "score": 50,
36
+ "signal": "NO DATA: Insufficient data for crossover analysis",
37
+ "data_available": False,
38
+ }
39
+
40
+ latest = rows[-1]
41
+ ma8 = latest["Breadth_Index_8MA"]
42
+ ma200 = latest["Breadth_Index_200MA"]
43
+ gap = ma8 - ma200
44
+
45
+ # Gap score
46
+ gap_score = _score_gap(gap)
47
+
48
+ # 8MA direction over last 5 days
49
+ ma8_5d_ago = rows[-6]["Breadth_Index_8MA"]
50
+ ma8_rising = ma8 > ma8_5d_ago
51
+ ma8_direction = "rising" if ma8_rising else "falling"
52
+
53
+ # Direction modifier
54
+ direction_modifier = 0
55
+ if gap < 0 and ma8_rising:
56
+ direction_modifier = +10 # Recovery signal
57
+ elif gap > 0 and not ma8_rising:
58
+ direction_modifier = -10 # Deterioration signal
59
+
60
+ score = round(gap_score + direction_modifier)
61
+ score = max(0, min(100, score))
62
+
63
+ signal = _generate_signal(gap, ma8_direction, direction_modifier, score)
64
+
65
+ return {
66
+ "score": score,
67
+ "signal": signal,
68
+ "data_available": True,
69
+ "gap": gap,
70
+ "gap_score": gap_score,
71
+ "current_8ma": ma8,
72
+ "current_200ma": ma200,
73
+ "ma8_5d_ago": ma8_5d_ago,
74
+ "ma8_direction": ma8_direction,
75
+ "direction_modifier": direction_modifier,
76
+ "date": latest["Date"],
77
+ }
78
+
79
+
80
+ def _score_gap(gap: float) -> int:
81
+ """Score based on 8MA - 200MA gap."""
82
+ if gap >= 0.15:
83
+ return 95
84
+ elif gap >= 0.10:
85
+ return 80
86
+ elif gap >= 0.05:
87
+ return 65
88
+ elif gap >= 0.00:
89
+ return 50
90
+ elif gap >= -0.05:
91
+ return 35
92
+ elif gap >= -0.10:
93
+ return 20
94
+ else:
95
+ return 5
96
+
97
+
98
+ def _generate_signal(gap: float, direction: str, modifier: int, score: int) -> str:
99
+ """Generate human-readable signal."""
100
+ mod_str = ""
101
+ if modifier > 0:
102
+ mod_str = " (recovery signal)"
103
+ elif modifier < 0:
104
+ mod_str = " (deterioration signal)"
105
+
106
+ if score >= 80:
107
+ return f"BULLISH: 8MA well above 200MA (gap={gap:+.3f}, 8MA {direction}){mod_str}"
108
+ elif score >= 60:
109
+ return f"POSITIVE: 8MA above 200MA (gap={gap:+.3f}, 8MA {direction}){mod_str}"
110
+ elif score >= 40:
111
+ return f"NEUTRAL: Near crossover (gap={gap:+.3f}, 8MA {direction}){mod_str}"
112
+ elif score >= 20:
113
+ return f"NEGATIVE: 8MA below 200MA (gap={gap:+.3f}, 8MA {direction}){mod_str}"
114
+ else:
115
+ return f"BEARISH: 8MA far below 200MA (gap={gap:+.3f}, 8MA {direction}){mod_str}"