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,329 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Uptrend Analyzer - Report Generator
4
+
5
+ Generates JSON and Markdown reports for uptrend breadth analysis.
6
+ """
7
+
8
+ import json
9
+ from typing import Dict
10
+
11
+
12
+ def generate_json_report(analysis: Dict, output_file: str):
13
+ """Save full analysis as JSON"""
14
+ with open(output_file, 'w') as f:
15
+ json.dump(analysis, f, indent=2, default=str)
16
+ print(f"JSON report saved to: {output_file}")
17
+
18
+
19
+ def generate_markdown_report(analysis: Dict, output_file: str):
20
+ """Generate comprehensive Markdown report"""
21
+ lines = []
22
+ composite = analysis.get("composite", {})
23
+ components = analysis.get("components", {})
24
+ metadata = analysis.get("metadata", {})
25
+
26
+ score = composite.get("composite_score", 0)
27
+ zone = composite.get("zone", "Unknown")
28
+ exposure = composite.get("exposure_guidance", "N/A")
29
+
30
+ # Header
31
+ lines.append("# Uptrend Analyzer Report")
32
+ lines.append("")
33
+ lines.append(f"**Generated:** {metadata.get('generated_at', 'N/A')}")
34
+ lines.append(f"**Data Source:** Monty's Uptrend Ratio Dashboard (GitHub CSV)")
35
+ lines.append(f"**API Key Required:** No")
36
+ lines.append("")
37
+
38
+ # Overall Assessment
39
+ lines.append("---")
40
+ lines.append("")
41
+ lines.append("## Overall Assessment")
42
+ lines.append("")
43
+ zone_emoji = _zone_emoji(composite.get("zone_color", ""))
44
+ lines.append("| Metric | Value |")
45
+ lines.append("|--------|-------|")
46
+ lines.append(f"| **Composite Score** | **{score}/100** |")
47
+ lines.append(f"| **Zone** | {zone_emoji} {zone} |")
48
+ lines.append(f"| **Exposure Guidance** | {exposure} |")
49
+ lines.append(f"| **Strongest Component** | {composite.get('strongest_component', {}).get('label', 'N/A')} "
50
+ f"({composite.get('strongest_component', {}).get('score', 0)}/100) |")
51
+ lines.append(f"| **Weakest Component** | {composite.get('weakest_component', {}).get('label', 'N/A')} "
52
+ f"({composite.get('weakest_component', {}).get('score', 0)}/100) |")
53
+ dq = composite.get("data_quality", {})
54
+ if dq:
55
+ lines.append(f"| **Data Quality** | {dq.get('label', 'N/A')} |")
56
+ lines.append("")
57
+
58
+ # Guidance
59
+ lines.append(f"> **Guidance:** {composite.get('guidance', '')}")
60
+ lines.append("")
61
+
62
+ # Current Market Snapshot
63
+ breadth = components.get("market_breadth", {})
64
+ if breadth.get("data_available"):
65
+ lines.append("---")
66
+ lines.append("")
67
+ lines.append("## Current Market Snapshot")
68
+ lines.append("")
69
+ lines.append("| Metric | Value |")
70
+ lines.append("|--------|-------|")
71
+ lines.append(f"| Uptrend Ratio | {breadth.get('ratio_pct', 'N/A')}% |")
72
+ lines.append(f"| 10-Day MA | {breadth.get('ma_10_pct', 'N/A')}% |")
73
+ lines.append(f"| Trend | {breadth.get('trend', 'N/A')} |")
74
+ lines.append(f"| Slope | {_format_slope(breadth.get('slope'))} |")
75
+ lines.append(f"| Distance from 37% (Overbought) | {_format_distance(breadth.get('distance_from_upper'))} |")
76
+ lines.append(f"| Distance from 9.7% (Oversold) | {_format_distance(breadth.get('distance_from_lower'))} |")
77
+ lines.append(f"| Date | {breadth.get('date', 'N/A')} |")
78
+ lines.append("")
79
+
80
+ # Component Scores Table
81
+ lines.append("---")
82
+ lines.append("")
83
+ lines.append("## Component Scores")
84
+ lines.append("")
85
+ lines.append("| # | Component | Weight | Score | Contribution | Signal |")
86
+ lines.append("|---|-----------|--------|-------|--------------|--------|")
87
+
88
+ component_order = [
89
+ "market_breadth", "sector_participation", "sector_rotation",
90
+ "momentum", "historical_context",
91
+ ]
92
+
93
+ for i, key in enumerate(component_order, 1):
94
+ comp = composite.get("component_scores", {}).get(key, {})
95
+ detail = components.get(key, {})
96
+ signal = detail.get("signal", "N/A")
97
+ score_val = comp.get("score", 0)
98
+ weight_pct = f"{comp.get('weight', 0)*100:.0f}%"
99
+ contribution = comp.get("weighted_contribution", 0)
100
+ bar = _score_bar(score_val)
101
+
102
+ lines.append(f"| {i} | **{comp.get('label', key)}** | {weight_pct} | "
103
+ f"{bar} {score_val} | {contribution:.1f} | {signal} |")
104
+
105
+ lines.append("")
106
+
107
+ # Component Details
108
+ lines.append("---")
109
+ lines.append("")
110
+ lines.append("## Component Details")
111
+ lines.append("")
112
+
113
+ # 1. Market Breadth
114
+ lines.append("### 1. Market Breadth (Overall)")
115
+ lines.append("")
116
+ if breadth.get("data_available"):
117
+ lines.append(f"- **Uptrend Ratio:** {breadth.get('ratio_pct', 'N/A')}%")
118
+ lines.append(f"- **10-Day MA:** {breadth.get('ma_10_pct', 'N/A')}%")
119
+ lines.append(f"- **Trend:** {breadth.get('trend', 'N/A')}")
120
+ lines.append(f"- **Slope:** {_format_slope(breadth.get('slope'))}")
121
+ lines.append(f"- **Trend Adjustment:** {breadth.get('trend_adjustment', 0):+d}")
122
+ else:
123
+ lines.append("- Data unavailable")
124
+ lines.append("")
125
+
126
+ # 2. Sector Participation
127
+ participation = components.get("sector_participation", {})
128
+ lines.append("### 2. Sector Participation")
129
+ lines.append("")
130
+ if participation.get("data_available"):
131
+ lines.append(f"- **Uptrending Sectors:** {participation.get('uptrend_count', 0)}"
132
+ f"/{participation.get('total_sectors', 0)}")
133
+ lines.append(f"- **Count Score:** {participation.get('count_score', 0)}/100")
134
+ lines.append(f"- **Spread:** {participation.get('spread_pct', 'N/A')}% "
135
+ f"(score: {participation.get('spread_score', 0)}/100)")
136
+ lines.append(f"- **Overbought ({'>'}37%):** {participation.get('overbought_count', 0)} "
137
+ f"sectors ({', '.join(participation.get('overbought_sectors', []))})")
138
+ lines.append(f"- **Oversold ({'<'}9.7%):** {participation.get('oversold_count', 0)} "
139
+ f"sectors ({', '.join(participation.get('oversold_sectors', []))})")
140
+ else:
141
+ lines.append("- Data unavailable")
142
+ lines.append("")
143
+
144
+ # 3. Sector Rotation
145
+ rotation = components.get("sector_rotation", {})
146
+ lines.append("### 3. Sector Rotation")
147
+ lines.append("")
148
+ if rotation.get("data_available"):
149
+ lines.append(f"- **Cyclical Avg:** {rotation.get('cyclical_avg_pct', 'N/A')}%")
150
+ lines.append(f"- **Defensive Avg:** {rotation.get('defensive_avg_pct', 'N/A')}%")
151
+ lines.append(f"- **Commodity Avg:** {rotation.get('commodity_avg_pct', 'N/A')}%")
152
+ lines.append(f"- **Cyclical-Defensive Gap:** {rotation.get('difference_pct', 'N/A')}pp")
153
+ if rotation.get("late_cycle_flag"):
154
+ lines.append(f"- **Late Cycle Warning:** YES (commodity penalty: {rotation.get('commodity_penalty', 0)})")
155
+
156
+ # Group detail tables
157
+ for group_name, group_key in [("Cyclical", "cyclical_details"),
158
+ ("Defensive", "defensive_details"),
159
+ ("Commodity", "commodity_details")]:
160
+ details = rotation.get(group_key, [])
161
+ if details:
162
+ lines.append(f"\n**{group_name} Sectors:**")
163
+ lines.append("")
164
+ lines.append("| Sector | Ratio | Trend | Slope |")
165
+ lines.append("|--------|-------|-------|-------|")
166
+ for d in details:
167
+ lines.append(f"| {d.get('sector', '')} | "
168
+ f"{d.get('ratio_pct', 'N/A')}% | "
169
+ f"{d.get('trend', 'N/A')} | "
170
+ f"{_format_slope(d.get('slope'))} |")
171
+ lines.append("")
172
+ else:
173
+ lines.append("- Data unavailable")
174
+ lines.append("")
175
+
176
+ # 4. Momentum
177
+ momentum = components.get("momentum", {})
178
+ lines.append("### 4. Momentum")
179
+ lines.append("")
180
+ if momentum.get("data_available"):
181
+ lines.append(f"- **Current Slope:** {_format_slope(momentum.get('slope'))} "
182
+ f"(score: {momentum.get('slope_score', 0)}/100)")
183
+ lines.append(f"- **Acceleration:** {momentum.get('acceleration', 'N/A')} "
184
+ f"({momentum.get('acceleration_label', 'N/A')}, "
185
+ f"score: {momentum.get('acceleration_score', 0)}/100)")
186
+ lines.append(f"- **Sector Slope Breadth:** "
187
+ f"{momentum.get('sector_positive_slope_count', 0)}"
188
+ f"/{momentum.get('sector_total', 0)} positive "
189
+ f"(score: {momentum.get('sector_slope_breadth_score', 0)}/100)")
190
+ else:
191
+ lines.append("- Data unavailable")
192
+ lines.append("")
193
+
194
+ # 5. Historical Context
195
+ historical = components.get("historical_context", {})
196
+ lines.append("### 5. Historical Context")
197
+ lines.append("")
198
+ if historical.get("data_available"):
199
+ lines.append(f"- **Current Ratio:** {historical.get('current_ratio_pct', 'N/A')}%")
200
+ lines.append(f"- **Percentile Rank:** {historical.get('percentile', 'N/A')}th")
201
+ lines.append(f"- **Historical Range:** "
202
+ f"{historical.get('historical_min_pct', 'N/A')}% - "
203
+ f"{historical.get('historical_max_pct', 'N/A')}%")
204
+ lines.append(f"- **Historical Median:** {historical.get('historical_median_pct', 'N/A')}%")
205
+ lines.append(f"- **30-Day Avg:** {historical.get('avg_30d_pct', 'N/A')}%")
206
+ lines.append(f"- **90-Day Avg:** {historical.get('avg_90d_pct', 'N/A')}%")
207
+ lines.append(f"- **Data Points:** {historical.get('data_points', 0)} "
208
+ f"({historical.get('date_range', 'N/A')})")
209
+ else:
210
+ lines.append("- Data unavailable")
211
+ lines.append("")
212
+
213
+ # Sector Heatmap Table
214
+ sector_details = components.get("sector_participation", {}).get("sector_details", [])
215
+ if sector_details:
216
+ lines.append("---")
217
+ lines.append("")
218
+ lines.append("## Sector Heatmap")
219
+ lines.append("")
220
+ lines.append("| Rank | Sector | Ratio | 10MA | Trend | Slope | Status |")
221
+ lines.append("|------|--------|-------|------|-------|-------|--------|")
222
+ for i, s in enumerate(sector_details, 1):
223
+ lines.append(f"| {i} | {s.get('sector', '')} | "
224
+ f"{s.get('ratio_pct', 'N/A')}% | "
225
+ f"{round(s['ma_10'] * 100, 1) if s.get('ma_10') is not None else 'N/A'}% | "
226
+ f"{s.get('trend', '')} | "
227
+ f"{_format_slope(s.get('slope'))} | "
228
+ f"{s.get('status', '')} |")
229
+ lines.append("")
230
+
231
+ # Recommended Actions
232
+ lines.append("---")
233
+ lines.append("")
234
+ lines.append("## Recommended Actions")
235
+ lines.append("")
236
+ lines.append(f"**Zone:** {zone}")
237
+ lines.append(f"**Exposure Guidance:** {exposure}")
238
+ lines.append("")
239
+ for action in composite.get("actions", []):
240
+ lines.append(f"- {action}")
241
+ lines.append("")
242
+
243
+ # Active Warnings (overlay adjustments)
244
+ active_warnings = composite.get("active_warnings", [])
245
+ if active_warnings:
246
+ lines.append("### Active Warnings")
247
+ lines.append("")
248
+ for warning in active_warnings:
249
+ lines.append(f"**{warning.get('label', 'WARNING')}**")
250
+ lines.append(f"> {warning.get('description', '')}")
251
+ lines.append("")
252
+ for action in warning.get("actions", []):
253
+ lines.append(f"- {action}")
254
+ lines.append("")
255
+
256
+ # Methodology
257
+ lines.append("---")
258
+ lines.append("")
259
+ lines.append("## Methodology")
260
+ lines.append("")
261
+ lines.append("This analysis uses Monty's Uptrend Ratio Dashboard data to assess market breadth health.")
262
+ lines.append("The dashboard tracks ~2,800 US stocks across 11 sectors, measuring the percentage in uptrends.")
263
+ lines.append("")
264
+ lines.append("**5-Component Scoring System (0-100, higher = healthier):**")
265
+ lines.append("")
266
+ lines.append("1. **Market Breadth (30%):** Overall uptrend ratio level and trend direction")
267
+ lines.append("2. **Sector Participation (25%):** Number of uptrending sectors and spread uniformity")
268
+ lines.append("3. **Sector Rotation (15%):** Cyclical vs Defensive vs Commodity balance")
269
+ lines.append("4. **Momentum (20%):** Slope direction, acceleration, and sector slope breadth")
270
+ lines.append("5. **Historical Context (10%):** Percentile rank in historical distribution")
271
+ lines.append("")
272
+ lines.append("**Key Thresholds (Monty's Dashboard):** Overbought = 37%, Oversold = 9.7%")
273
+ lines.append("")
274
+ lines.append("For detailed methodology, see `references/uptrend_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
+ "light_green": "🟢",
296
+ "yellow": "🟡",
297
+ "orange": "🟠",
298
+ "red": "🔴",
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 "░░░░"
315
+
316
+
317
+ def _format_slope(value) -> str:
318
+ """Format slope value with 4 decimal places."""
319
+ if value is None:
320
+ return "N/A"
321
+ return f"{value:+.4f}"
322
+
323
+
324
+ def _format_distance(value) -> str:
325
+ """Format distance value as percentage points."""
326
+ if value is None:
327
+ return "N/A"
328
+ pct = round(value * 100, 1)
329
+ return f"{pct:+.1f}pp"
@@ -0,0 +1,276 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Uptrend Analyzer - Composite Scoring Engine
4
+
5
+ Combines 5 component scores into a weighted composite (0-100).
6
+ Higher score = healthier market (opposite of Market Top Detector).
7
+
8
+ Component Weights:
9
+ 1. Market Breadth (Overall): 30%
10
+ 2. Sector Participation: 25%
11
+ 3. Sector Rotation: 15%
12
+ 4. Momentum: 20%
13
+ 5. Historical Context: 10%
14
+ Total: 100%
15
+
16
+ Scoring Zones (higher = better):
17
+ 80-100: Strong Bull - Full Exposure (100%)
18
+ 60-79: Bull - Normal Exposure (80-100%)
19
+ 40-59: Neutral - Reduced Exposure (60-80%)
20
+ 20-39: Cautious - Defensive (30-60%)
21
+ 0-19: Bear - Capital Preservation (0-30%)
22
+ """
23
+
24
+ from typing import Dict, List, Optional
25
+
26
+
27
+ COMPONENT_WEIGHTS = {
28
+ "market_breadth": 0.30,
29
+ "sector_participation": 0.25,
30
+ "sector_rotation": 0.15,
31
+ "momentum": 0.20,
32
+ "historical_context": 0.10,
33
+ }
34
+
35
+ COMPONENT_LABELS = {
36
+ "market_breadth": "Market Breadth (Overall)",
37
+ "sector_participation": "Sector Participation",
38
+ "sector_rotation": "Sector Rotation",
39
+ "momentum": "Momentum",
40
+ "historical_context": "Historical Context",
41
+ }
42
+
43
+
44
+ def calculate_composite_score(component_scores: Dict[str, float],
45
+ data_availability: Optional[Dict[str, bool]] = None,
46
+ warning_flags: Optional[Dict[str, bool]] = None) -> Dict:
47
+ """
48
+ Calculate weighted composite market health score.
49
+
50
+ Args:
51
+ component_scores: Dict with keys matching COMPONENT_WEIGHTS,
52
+ each value 0-100
53
+ data_availability: Optional dict mapping component key -> bool indicating
54
+ if data was actually available (vs neutral default)
55
+ warning_flags: Optional dict of component warning flags, e.g.
56
+ {"late_cycle": True, "high_spread": True}
57
+
58
+ Returns:
59
+ Dict with composite_score, zone, exposure_guidance, guidance,
60
+ strongest/weakest components, component breakdown, data_quality,
61
+ and active_warnings
62
+ """
63
+ if data_availability is None:
64
+ data_availability = {}
65
+ if warning_flags is None:
66
+ warning_flags = {}
67
+
68
+ # Calculate weighted composite
69
+ composite = 0.0
70
+ for key, weight in COMPONENT_WEIGHTS.items():
71
+ score = component_scores.get(key, 0)
72
+ composite += score * weight
73
+
74
+ composite = round(composite, 1)
75
+
76
+ # Identify strongest and weakest components
77
+ valid_scores = {k: v for k, v in component_scores.items()
78
+ if k in COMPONENT_WEIGHTS}
79
+
80
+ if valid_scores:
81
+ strongest = max(valid_scores, key=valid_scores.get)
82
+ weakest = min(valid_scores, key=valid_scores.get)
83
+ else:
84
+ strongest = "N/A"
85
+ weakest = "N/A"
86
+
87
+ # Get zone interpretation
88
+ zone_info = _interpret_zone(composite)
89
+
90
+ # Overlay warning-specific adjustments
91
+ active_warnings = _apply_warning_overlays(zone_info, warning_flags)
92
+
93
+ # Calculate data quality
94
+ available_count = sum(
95
+ 1 for k in COMPONENT_WEIGHTS
96
+ if data_availability.get(k, True)
97
+ )
98
+ total_components = len(COMPONENT_WEIGHTS)
99
+ missing_components = [
100
+ COMPONENT_LABELS[k] for k in COMPONENT_WEIGHTS
101
+ if not data_availability.get(k, True)
102
+ ]
103
+
104
+ if available_count == total_components:
105
+ quality_label = f"Complete ({available_count}/{total_components} components)"
106
+ elif available_count >= total_components - 1:
107
+ quality_label = (f"Partial ({available_count}/{total_components} components)"
108
+ " - interpret with caution")
109
+ else:
110
+ quality_label = (f"Limited ({available_count}/{total_components} components)"
111
+ " - low confidence")
112
+
113
+ data_quality = {
114
+ "available_count": available_count,
115
+ "total_components": total_components,
116
+ "label": quality_label,
117
+ "missing_components": missing_components,
118
+ }
119
+
120
+ return {
121
+ "composite_score": composite,
122
+ "zone": zone_info["zone"],
123
+ "zone_color": zone_info["color"],
124
+ "exposure_guidance": zone_info["exposure_guidance"],
125
+ "guidance": zone_info["guidance"],
126
+ "actions": zone_info["actions"],
127
+ "active_warnings": active_warnings,
128
+ "strongest_component": {
129
+ "component": strongest,
130
+ "label": COMPONENT_LABELS.get(strongest, strongest),
131
+ "score": valid_scores.get(strongest, 0),
132
+ },
133
+ "weakest_component": {
134
+ "component": weakest,
135
+ "label": COMPONENT_LABELS.get(weakest, weakest),
136
+ "score": valid_scores.get(weakest, 0),
137
+ },
138
+ "data_quality": data_quality,
139
+ "component_scores": {
140
+ k: {
141
+ "score": component_scores.get(k, 0),
142
+ "weight": w,
143
+ "weighted_contribution": round(component_scores.get(k, 0) * w, 1),
144
+ "label": COMPONENT_LABELS[k],
145
+ }
146
+ for k, w in COMPONENT_WEIGHTS.items()
147
+ },
148
+ }
149
+
150
+
151
+ def _apply_warning_overlays(zone_info: Dict,
152
+ warning_flags: Dict[str, bool]) -> List[Dict]:
153
+ """Apply warning-driven adjustments to zone guidance and actions.
154
+
155
+ When component-level warnings (e.g. late_cycle, high_spread) are active,
156
+ tighten the exposure guidance and prepend cautionary actions even if the
157
+ composite score places the market in a bullish zone.
158
+
159
+ Returns:
160
+ List of active warning dicts with label, description, and extra actions
161
+ """
162
+ active = []
163
+
164
+ if warning_flags.get("late_cycle"):
165
+ active.append({
166
+ "flag": "late_cycle",
167
+ "label": "LATE CYCLE WARNING",
168
+ "description": ("Commodity sectors leading both cyclical and defensive groups. "
169
+ "Historically associated with late-cycle inflation or sector rotation "
170
+ "preceding broader market weakness."),
171
+ "actions": [
172
+ "Favor lower end of exposure range (e.g. 80% if guidance is 80-100%)",
173
+ "New entries limited to A-grade setups only",
174
+ "Tighten stops on commodity/cyclical positions",
175
+ "Monitor for commodity rollover as potential broad market lead indicator",
176
+ ],
177
+ })
178
+
179
+ if warning_flags.get("high_spread"):
180
+ active.append({
181
+ "flag": "high_spread",
182
+ "label": "HIGH SELECTIVITY WARNING",
183
+ "description": ("Wide spread between strongest and weakest sectors indicates "
184
+ "highly selective market. Breadth may be masking narrowing leadership."),
185
+ "actions": [
186
+ "Concentrate on sectors with ratio above 10MA",
187
+ "Avoid lagging sectors even if trend is nominally 'up'",
188
+ "Reduce position count to highest-conviction ideas",
189
+ ],
190
+ })
191
+
192
+ # If any warning is active, tighten exposure guidance for bullish zones
193
+ if active and zone_info["zone"] in ("Strong Bull", "Bull"):
194
+ original = zone_info["exposure_guidance"]
195
+ if zone_info["zone"] == "Strong Bull":
196
+ zone_info["exposure_guidance"] = "Full Exposure with Caution (90-100%)"
197
+ elif zone_info["zone"] == "Bull":
198
+ zone_info["exposure_guidance"] = "Normal Exposure, Lower End (80-90%)"
199
+ zone_info["guidance"] += (
200
+ f" However, active warnings suggest operating at the conservative "
201
+ f"end of the range."
202
+ )
203
+
204
+ return active
205
+
206
+
207
+ def _interpret_zone(composite: float) -> Dict:
208
+ """Map composite score to health zone (higher = healthier)"""
209
+ if composite >= 80:
210
+ return {
211
+ "zone": "Strong Bull",
212
+ "color": "green",
213
+ "exposure_guidance": "Full Exposure (100%)",
214
+ "guidance": "Broad market participation with strong momentum. Ideal environment for new positions.",
215
+ "actions": [
216
+ "Full equity exposure allowed",
217
+ "Aggressive position sizing for breakout entries",
218
+ "Add to winning positions on pullbacks",
219
+ "Minimal hedging needed",
220
+ ],
221
+ }
222
+ elif composite >= 60:
223
+ return {
224
+ "zone": "Bull",
225
+ "color": "light_green",
226
+ "exposure_guidance": "Normal Exposure (80-100%)",
227
+ "guidance": "Healthy market breadth supporting equity allocation. Standard position management.",
228
+ "actions": [
229
+ "Normal position sizing",
230
+ "New entries on quality setups",
231
+ "Standard stop-loss levels",
232
+ "Monitor sector rotation for early warnings",
233
+ ],
234
+ }
235
+ elif composite >= 40:
236
+ return {
237
+ "zone": "Neutral",
238
+ "color": "yellow",
239
+ "exposure_guidance": "Reduced Exposure (60-80%)",
240
+ "guidance": "Mixed signals. Participate selectively with tighter risk controls.",
241
+ "actions": [
242
+ "Reduce position sizes by 20-30%",
243
+ "Focus on strongest sectors only",
244
+ "Tighten stop-losses",
245
+ "Avoid low-quality setups",
246
+ "Increase cash allocation gradually",
247
+ ],
248
+ }
249
+ elif composite >= 20:
250
+ return {
251
+ "zone": "Cautious",
252
+ "color": "orange",
253
+ "exposure_guidance": "Defensive (30-60%)",
254
+ "guidance": "Weak breadth environment. Prioritize capital preservation over gains.",
255
+ "actions": [
256
+ "Significant cash allocation (40-70%)",
257
+ "Only hold strongest leaders in uptrending sectors",
258
+ "Tight stops on all positions",
259
+ "Consider defensive sector allocation",
260
+ "No new aggressive entries",
261
+ ],
262
+ }
263
+ else:
264
+ return {
265
+ "zone": "Bear",
266
+ "color": "red",
267
+ "exposure_guidance": "Capital Preservation (0-30%)",
268
+ "guidance": "Severe breadth deterioration. Maximum defensive posture.",
269
+ "actions": [
270
+ "Maximum cash (70-100%)",
271
+ "Exit most equity positions",
272
+ "Only ultra-high-conviction holdings",
273
+ "Consider hedges (inverse ETFs, puts)",
274
+ "Wait for breadth recovery before re-entry",
275
+ ],
276
+ }