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,380 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ L Component - Leadership / Relative Strength Calculator
4
+
5
+ Calculates CANSLIM 'L' component score based on relative price performance vs market.
6
+
7
+ O'Neil's Rule: "The top-performing stocks, prior to major advances, have Relative Strength
8
+ Ratings averaging 87 at pivot points. You want to see 80+ RS Rank - meaning the stock is
9
+ outperforming 80% of all other stocks over the past 52 weeks."
10
+
11
+ Key Principles:
12
+ - RS Rank 90+: Market leader, top 10% performer
13
+ - RS Rank 80-89: Strong performer, top 20%
14
+ - RS Rank 70-79: Above average, top 30%
15
+ - RS Rank 60-69: Average performer
16
+ - RS Rank <60: Laggard, underperforming majority
17
+
18
+ Implementation Note:
19
+ Since IBD's official RS Rank is proprietary, we estimate RS using:
20
+ 1. 52-week price performance vs S&P 500
21
+ 2. Sector-relative performance (optional enhancement)
22
+
23
+ Scoring:
24
+ - 100 points: Relative outperformance > +30% vs S&P 500 (market leader)
25
+ - 90 points: Relative outperformance +20% to +30%
26
+ - 80 points: Relative outperformance +10% to +20% (strong)
27
+ - 60 points: Relative outperformance 0% to +10% (above average)
28
+ - 40 points: Relative performance -10% to 0% (lagging)
29
+ - 20 points: Relative performance -20% to -10% (weak)
30
+ - 0 points: Relative underperformance > -20% (avoid)
31
+ """
32
+
33
+ from typing import Dict, List, Optional
34
+
35
+
36
+ def calculate_leadership(
37
+ historical_prices: List[Dict],
38
+ sp500_historical: Optional[List[Dict]] = None,
39
+ sp500_performance: Optional[float] = None
40
+ ) -> Dict:
41
+ """
42
+ Calculate Leadership/Relative Strength score (L component)
43
+
44
+ Args:
45
+ historical_prices: List of daily price data for the stock (oldest to newest)
46
+ Each entry: {"date": str, "close": float, "volume": int}
47
+ sp500_historical: Optional list of S&P 500 daily prices (for RS calculation)
48
+ sp500_performance: Optional pre-calculated S&P 500 52-week performance (%)
49
+
50
+ Returns:
51
+ Dict with:
52
+ - score: 0-100 points
53
+ - stock_52w_performance: Stock's 52-week return (%)
54
+ - sp500_52w_performance: S&P 500's 52-week return (%)
55
+ - relative_performance: Stock return - S&P 500 return (%)
56
+ - rs_rank_estimate: Estimated RS Rank (0-99)
57
+ - interpretation: Human-readable interpretation
58
+ - quality_warning: Warning if data quality issues
59
+ - error: Error message if calculation failed
60
+
61
+ Example:
62
+ >>> prices = client.get_historical_prices("NVDA", days=365)
63
+ >>> sp500 = client.get_historical_prices("SPY", days=365)
64
+ >>> result = calculate_leadership(prices, sp500)
65
+ >>> print(f"L Score: {result['score']}, RS Estimate: {result['rs_rank_estimate']}")
66
+ """
67
+ # Validate input
68
+ if not historical_prices or len(historical_prices) < 50:
69
+ return {
70
+ "score": 0,
71
+ "error": "Insufficient historical price data (need 50+ days)",
72
+ "stock_52w_performance": None,
73
+ "sp500_52w_performance": None,
74
+ "relative_performance": None,
75
+ "rs_rank_estimate": None,
76
+ "interpretation": "Data unavailable"
77
+ }
78
+
79
+ # Calculate stock's 52-week (or available period) performance
80
+ # Prices should be sorted oldest to newest
81
+ try:
82
+ # Get start and end prices (handle both orderings)
83
+ if historical_prices[0].get("date", "") < historical_prices[-1].get("date", ""):
84
+ # Oldest first (ascending)
85
+ start_price = historical_prices[0].get("close", 0)
86
+ end_price = historical_prices[-1].get("close", 0)
87
+ else:
88
+ # Newest first (descending)
89
+ start_price = historical_prices[-1].get("close", 0)
90
+ end_price = historical_prices[0].get("close", 0)
91
+
92
+ if start_price <= 0:
93
+ return {
94
+ "score": 0,
95
+ "error": "Invalid start price (zero or negative)",
96
+ "stock_52w_performance": None,
97
+ "sp500_52w_performance": None,
98
+ "relative_performance": None,
99
+ "rs_rank_estimate": None,
100
+ "interpretation": "Data quality issue"
101
+ }
102
+
103
+ stock_performance = ((end_price - start_price) / start_price) * 100
104
+ days_analyzed = len(historical_prices)
105
+
106
+ except (KeyError, TypeError, ZeroDivisionError) as e:
107
+ return {
108
+ "score": 0,
109
+ "error": f"Price calculation error: {str(e)}",
110
+ "stock_52w_performance": None,
111
+ "sp500_52w_performance": None,
112
+ "relative_performance": None,
113
+ "rs_rank_estimate": None,
114
+ "interpretation": "Calculation error"
115
+ }
116
+
117
+ # Calculate S&P 500 performance for comparison
118
+ sp500_perf = None
119
+ quality_warning = None
120
+
121
+ if sp500_performance is not None:
122
+ # Use pre-calculated S&P 500 performance
123
+ sp500_perf = sp500_performance
124
+ elif sp500_historical and len(sp500_historical) >= 50:
125
+ # Calculate from provided S&P 500 data
126
+ try:
127
+ if sp500_historical[0].get("date", "") < sp500_historical[-1].get("date", ""):
128
+ sp500_start = sp500_historical[0].get("close", 0)
129
+ sp500_end = sp500_historical[-1].get("close", 0)
130
+ else:
131
+ sp500_start = sp500_historical[-1].get("close", 0)
132
+ sp500_end = sp500_historical[0].get("close", 0)
133
+
134
+ if sp500_start > 0:
135
+ sp500_perf = ((sp500_end - sp500_start) / sp500_start) * 100
136
+ except (KeyError, TypeError, ZeroDivisionError):
137
+ quality_warning = "S&P 500 performance calculation failed"
138
+ else:
139
+ quality_warning = "S&P 500 data unavailable - using absolute performance only"
140
+
141
+ # Calculate relative performance
142
+ if sp500_perf is not None:
143
+ relative_performance = stock_performance - sp500_perf
144
+ else:
145
+ # Fallback: use absolute performance with penalty
146
+ relative_performance = stock_performance
147
+ if quality_warning is None:
148
+ quality_warning = "Using absolute performance (S&P 500 comparison unavailable)"
149
+
150
+ # Score based on relative performance
151
+ score, rs_rank_estimate = score_leadership(relative_performance, sp500_perf is not None)
152
+
153
+ # Generate interpretation
154
+ interpretation = interpret_leadership(
155
+ score, stock_performance, sp500_perf, relative_performance, days_analyzed
156
+ )
157
+
158
+ return {
159
+ "score": score,
160
+ "stock_52w_performance": round(stock_performance, 2),
161
+ "sp500_52w_performance": round(sp500_perf, 2) if sp500_perf is not None else None,
162
+ "relative_performance": round(relative_performance, 2),
163
+ "rs_rank_estimate": rs_rank_estimate,
164
+ "days_analyzed": days_analyzed,
165
+ "interpretation": interpretation,
166
+ "quality_warning": quality_warning,
167
+ "error": None
168
+ }
169
+
170
+
171
+ def score_leadership(relative_performance: float, has_benchmark: bool) -> tuple:
172
+ """
173
+ Score leadership based on relative performance
174
+
175
+ Args:
176
+ relative_performance: Stock performance minus S&P 500 performance (%)
177
+ has_benchmark: True if S&P 500 comparison was available
178
+
179
+ Returns:
180
+ tuple: (score, rs_rank_estimate)
181
+ """
182
+ # Scoring thresholds for relative performance
183
+ if relative_performance >= 50:
184
+ score = 100
185
+ rs_rank_estimate = 99 # Top 1%
186
+ elif relative_performance >= 30:
187
+ score = 95
188
+ rs_rank_estimate = 95 # Top 5%
189
+ elif relative_performance >= 20:
190
+ score = 90
191
+ rs_rank_estimate = 90 # Top 10%
192
+ elif relative_performance >= 10:
193
+ score = 80
194
+ rs_rank_estimate = 80 # Top 20%
195
+ elif relative_performance >= 5:
196
+ score = 70
197
+ rs_rank_estimate = 70 # Top 30%
198
+ elif relative_performance >= 0:
199
+ score = 60
200
+ rs_rank_estimate = 60 # Top 40%
201
+ elif relative_performance >= -5:
202
+ score = 50
203
+ rs_rank_estimate = 50 # Average
204
+ elif relative_performance >= -10:
205
+ score = 40
206
+ rs_rank_estimate = 40 # Below average
207
+ elif relative_performance >= -20:
208
+ score = 20
209
+ rs_rank_estimate = 25 # Weak
210
+ else:
211
+ score = 0
212
+ rs_rank_estimate = 10 # Laggard
213
+
214
+ # Apply penalty if no benchmark comparison available
215
+ if not has_benchmark:
216
+ score = int(score * 0.8) # 20% penalty for absolute-only comparison
217
+ rs_rank_estimate = int(rs_rank_estimate * 0.9)
218
+
219
+ return score, rs_rank_estimate
220
+
221
+
222
+ def interpret_leadership(
223
+ score: int,
224
+ stock_performance: float,
225
+ sp500_performance: Optional[float],
226
+ relative_performance: float,
227
+ days_analyzed: int
228
+ ) -> str:
229
+ """
230
+ Generate human-readable interpretation
231
+
232
+ Args:
233
+ score: Leadership score (0-100)
234
+ stock_performance: Stock's period return (%)
235
+ sp500_performance: S&P 500's period return (%)
236
+ relative_performance: Stock - S&P 500 (%)
237
+ days_analyzed: Number of days analyzed
238
+
239
+ Returns:
240
+ str: Interpretation string
241
+ """
242
+ # Period description
243
+ if days_analyzed >= 250:
244
+ period = "52-week"
245
+ elif days_analyzed >= 180:
246
+ period = "9-month"
247
+ elif days_analyzed >= 90:
248
+ period = "quarterly"
249
+ else:
250
+ period = f"{days_analyzed}-day"
251
+
252
+ # Performance description
253
+ if stock_performance > 0:
254
+ stock_msg = f"+{stock_performance:.1f}%"
255
+ else:
256
+ stock_msg = f"{stock_performance:.1f}%"
257
+
258
+ # Relative performance description
259
+ if sp500_performance is not None:
260
+ if relative_performance > 0:
261
+ rel_msg = f"+{relative_performance:.1f}% vs S&P 500"
262
+ else:
263
+ rel_msg = f"{relative_performance:.1f}% vs S&P 500"
264
+ else:
265
+ rel_msg = "(absolute performance)"
266
+
267
+ # Rating based on score
268
+ if score >= 90:
269
+ rating = "Market Leader"
270
+ action = "Strong momentum, prime CANSLIM candidate"
271
+ elif score >= 80:
272
+ rating = "Strong Performer"
273
+ action = "Outperforming market significantly"
274
+ elif score >= 60:
275
+ rating = "Above Average"
276
+ action = "Slight outperformance"
277
+ elif score >= 40:
278
+ rating = "Average"
279
+ action = "Matching or slightly lagging market"
280
+ elif score >= 20:
281
+ rating = "Laggard"
282
+ action = "Underperforming market - caution"
283
+ else:
284
+ rating = "Weak"
285
+ action = "Significant underperformance - avoid"
286
+
287
+ return f"{rating} - {period} return {stock_msg} ({rel_msg}). {action}"
288
+
289
+
290
+ def calculate_sector_relative_strength(
291
+ stock_performance: float,
292
+ sector_stocks_performance: List[float]
293
+ ) -> Dict:
294
+ """
295
+ Calculate relative strength within sector (optional enhancement)
296
+
297
+ Args:
298
+ stock_performance: Target stock's period return (%)
299
+ sector_stocks_performance: List of sector peers' period returns (%)
300
+
301
+ Returns:
302
+ Dict with:
303
+ - sector_rank: Stock's rank within sector (1 = best)
304
+ - sector_percentile: Percentile within sector (0-100)
305
+ - is_sector_leader: True if top 20% in sector
306
+ """
307
+ if not sector_stocks_performance:
308
+ return {
309
+ "sector_rank": None,
310
+ "sector_percentile": None,
311
+ "is_sector_leader": False,
312
+ "error": "No sector data available"
313
+ }
314
+
315
+ # Add stock to list and sort (descending)
316
+ all_stocks = sector_stocks_performance + [stock_performance]
317
+ all_stocks_sorted = sorted(all_stocks, reverse=True)
318
+
319
+ # Find rank
320
+ rank = all_stocks_sorted.index(stock_performance) + 1
321
+ total = len(all_stocks)
322
+ percentile = ((total - rank) / total) * 100
323
+
324
+ return {
325
+ "sector_rank": rank,
326
+ "sector_total": total,
327
+ "sector_percentile": round(percentile, 1),
328
+ "is_sector_leader": percentile >= 80
329
+ }
330
+
331
+
332
+ # Example usage
333
+ if __name__ == "__main__":
334
+ print("Testing Leadership Calculator (L Component)...")
335
+ print()
336
+
337
+ # Test 1: Strong outperformer
338
+ sample_prices = [
339
+ {"date": "2024-01-01", "close": 100.0},
340
+ {"date": "2024-06-01", "close": 120.0},
341
+ {"date": "2025-01-01", "close": 180.0} # +80% YoY
342
+ ]
343
+ sample_sp500 = [
344
+ {"date": "2024-01-01", "close": 4500.0},
345
+ {"date": "2024-06-01", "close": 4700.0},
346
+ {"date": "2025-01-01", "close": 5400.0} # +20% YoY
347
+ ]
348
+
349
+ result = calculate_leadership(sample_prices, sample_sp500)
350
+ print("Test 1: Strong Outperformer (+80% stock vs +20% S&P)")
351
+ print(f" Stock Performance: {result['stock_52w_performance']}%")
352
+ print(f" S&P 500 Performance: {result['sp500_52w_performance']}%")
353
+ print(f" Relative Performance: {result['relative_performance']}%")
354
+ print(f" L Score: {result['score']}/100")
355
+ print(f" RS Rank Estimate: {result['rs_rank_estimate']}")
356
+ print(f" Interpretation: {result['interpretation']}")
357
+ print()
358
+
359
+ # Test 2: Underperformer
360
+ sample_prices_weak = [
361
+ {"date": "2024-01-01", "close": 100.0},
362
+ {"date": "2025-01-01", "close": 90.0} # -10% YoY
363
+ ]
364
+
365
+ result2 = calculate_leadership(sample_prices_weak, sample_sp500)
366
+ print("Test 2: Underperformer (-10% stock vs +20% S&P)")
367
+ print(f" Stock Performance: {result2['stock_52w_performance']}%")
368
+ print(f" Relative Performance: {result2['relative_performance']}%")
369
+ print(f" L Score: {result2['score']}/100")
370
+ print(f" Interpretation: {result2['interpretation']}")
371
+ print()
372
+
373
+ # Test 3: Without S&P 500 data (fallback)
374
+ result3 = calculate_leadership(sample_prices)
375
+ print("Test 3: Without S&P 500 Data (Fallback)")
376
+ print(f" L Score: {result3['score']}/100 (20% penalty applied)")
377
+ print(f" Warning: {result3['quality_warning']}")
378
+ print()
379
+
380
+ print("✓ All tests completed")
@@ -0,0 +1,244 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ M Component - Market Direction Calculator
4
+
5
+ Calculates CANSLIM 'M' component score based on overall market trend.
6
+
7
+ O'Neil's Rule: "You can be right about a stock but wrong about the market,
8
+ and still lose money. Three out of four stocks follow the market's trend."
9
+
10
+ Scoring:
11
+ - 100: Strong uptrend (S&P 500 >2% above 50-EMA) + VIX <15
12
+ - 80: Uptrend (S&P 500 above 50-EMA) + VIX <20
13
+ - 60: Early uptrend (S&P 500 just above 50-EMA)
14
+ - 40: Choppy/neutral (S&P 500 near 50-EMA ±2%)
15
+ - 20: Downtrend forming (S&P 500 below 50-EMA)
16
+ - 0: Bear market (S&P 500 well below 50-EMA OR VIX >30) - DO NOT BUY
17
+ """
18
+
19
+ from typing import Dict, List, Optional
20
+
21
+
22
+ def calculate_market_direction(sp500_quote: Dict,
23
+ sp500_prices: Optional[List[Dict]] = None,
24
+ vix_quote: Optional[Dict] = None) -> Dict:
25
+ """
26
+ Calculate M component score based on S&P 500 trend and VIX
27
+
28
+ Args:
29
+ sp500_quote: S&P 500 quote data (symbol ^GSPC)
30
+ sp500_prices: Optional historical prices for EMA calculation
31
+ vix_quote: Optional VIX quote (symbol ^VIX)
32
+
33
+ Returns:
34
+ Dict with:
35
+ - score: 0-100 points
36
+ - trend: "strong_uptrend", "uptrend", "choppy", "downtrend", "bear_market"
37
+ - sp500_price: Current S&P 500 level
38
+ - sp500_ema_50: 50-day EMA (if calculated)
39
+ - distance_from_ema_pct: Distance from 50-EMA (%)
40
+ - vix_level: Current VIX level
41
+ - interpretation: Human-readable interpretation
42
+ - warning: Warning message if bear market (M=0)
43
+ """
44
+ # Validate input
45
+ if not sp500_quote:
46
+ return {
47
+ "score": 50, # Default to neutral if data unavailable
48
+ "error": "S&P 500 quote data missing",
49
+ "trend": "unknown",
50
+ "interpretation": "Market data unavailable"
51
+ }
52
+
53
+ sp500_price = sp500_quote.get("price")
54
+ if not sp500_price:
55
+ return {
56
+ "score": 50,
57
+ "error": "S&P 500 price missing",
58
+ "trend": "unknown",
59
+ "interpretation": "Market data unavailable"
60
+ }
61
+
62
+ # Calculate or estimate 50-day EMA
63
+ sp500_ema_50 = None
64
+ if sp500_prices and len(sp500_prices) >= 50:
65
+ # Calculate from historical data
66
+ close_prices = [day.get("close") for day in sp500_prices if day.get("close")]
67
+ if len(close_prices) >= 50:
68
+ sp500_ema_50 = calculate_ema(close_prices, period=50)
69
+ else:
70
+ # Estimate: Assume EMA is ~2% below current price in uptrend, ~2% above in downtrend
71
+ # This is a simplified fallback when historical data unavailable
72
+ sp500_ema_50 = sp500_price * 0.98 # Conservative estimate
73
+
74
+ # Calculate distance from EMA
75
+ distance_from_ema_pct = ((sp500_price / sp500_ema_50) - 1) * 100 if sp500_ema_50 else 0
76
+
77
+ # Get VIX level
78
+ vix_level = None
79
+ if vix_quote:
80
+ vix_level = vix_quote.get("price")
81
+
82
+ # Determine trend
83
+ if distance_from_ema_pct >= 2.0:
84
+ trend = "strong_uptrend"
85
+ elif distance_from_ema_pct >= 0:
86
+ trend = "uptrend"
87
+ elif distance_from_ema_pct >= -2.0:
88
+ trend = "choppy"
89
+ elif distance_from_ema_pct >= -5.0:
90
+ trend = "downtrend"
91
+ else:
92
+ trend = "bear_market"
93
+
94
+ # Calculate score
95
+ score = score_market_direction(trend, vix_level)
96
+
97
+ # Generate interpretation
98
+ interpretation = interpret_market_score(score, trend, distance_from_ema_pct, vix_level)
99
+
100
+ # Warning for bear market
101
+ warning = None
102
+ if score == 0:
103
+ warning = ("⚠️ BEAR MARKET - Do not buy stocks regardless of C, A, N scores. "
104
+ "Raise 80-100% cash and wait for market recovery.")
105
+
106
+ return {
107
+ "score": score,
108
+ "trend": trend,
109
+ "sp500_price": sp500_price,
110
+ "sp500_ema_50": sp500_ema_50,
111
+ "distance_from_ema_pct": round(distance_from_ema_pct, 2),
112
+ "vix_level": vix_level,
113
+ "interpretation": interpretation,
114
+ "warning": warning,
115
+ "error": None
116
+ }
117
+
118
+
119
+ def calculate_ema(prices: List[float], period: int = 50) -> float:
120
+ """
121
+ Calculate Exponential Moving Average
122
+
123
+ Args:
124
+ prices: List of prices (most recent first)
125
+ period: EMA period (default 50)
126
+
127
+ Returns:
128
+ EMA value
129
+
130
+ Formula: EMA = Price * k + EMA_prev * (1-k), where k = 2/(period+1)
131
+ """
132
+ if len(prices) < period:
133
+ return sum(prices) / len(prices) # Fallback to simple average
134
+
135
+ # Reverse to oldest-first for calculation
136
+ prices_reversed = prices[::-1]
137
+
138
+ # Initialize with SMA
139
+ sma = sum(prices_reversed[:period]) / period
140
+ ema = sma
141
+
142
+ # Calculate EMA
143
+ k = 2 / (period + 1)
144
+ for price in prices_reversed[period:]:
145
+ ema = price * k + ema * (1 - k)
146
+
147
+ return ema
148
+
149
+
150
+ def score_market_direction(trend: str, vix_level: Optional[float]) -> int:
151
+ """
152
+ Score M component based on trend and VIX
153
+
154
+ Args:
155
+ trend: Market trend classification
156
+ vix_level: Current VIX level
157
+
158
+ Returns:
159
+ Score (0-100)
160
+ """
161
+ # Base score from trend
162
+ if trend == "strong_uptrend":
163
+ base_score = 90
164
+ elif trend == "uptrend":
165
+ base_score = 70
166
+ elif trend == "choppy":
167
+ base_score = 40
168
+ elif trend == "downtrend":
169
+ base_score = 20
170
+ else: # bear_market
171
+ base_score = 0
172
+
173
+ # VIX adjustments
174
+ if vix_level:
175
+ if vix_level < 15:
176
+ base_score += 10 # Low fear, bullish
177
+ elif vix_level > 30:
178
+ base_score = 0 # Panic mode - override trend
179
+
180
+ return min(max(base_score, 0), 100)
181
+
182
+
183
+ def interpret_market_score(score: int,
184
+ trend: str,
185
+ distance: float,
186
+ vix: Optional[float]) -> str:
187
+ """
188
+ Generate human-readable market interpretation
189
+
190
+ Args:
191
+ score: M component score
192
+ trend: Trend classification
193
+ distance: Distance from 50-EMA (%)
194
+ vix: VIX level
195
+
196
+ Returns:
197
+ Interpretation string
198
+ """
199
+ vix_text = f", VIX {vix:.1f}" if vix else ""
200
+
201
+ if score >= 90:
202
+ return f"Strong bull market - Aggressive buying recommended (S&P 500 {distance:+.1f}% from 50-EMA{vix_text})"
203
+ elif score >= 70:
204
+ return f"Bull market - Standard position sizing (S&P 500 {distance:+.1f}% from 50-EMA{vix_text})"
205
+ elif score >= 50:
206
+ return f"Early uptrend - Small initial positions (S&P 500 {distance:+.1f}% from 50-EMA{vix_text})"
207
+ elif score >= 30:
208
+ return f"Choppy/neutral - Reduce exposure, be selective (S&P 500 {distance:+.1f}% from 50-EMA{vix_text})"
209
+ elif score >= 10:
210
+ return f"Downtrend forming - Defensive posture (S&P 500 {distance:+.1f}% from 50-EMA{vix_text})"
211
+ else:
212
+ return f"Bear market - Raise 80-100% cash, DO NOT BUY (S&P 500 {distance:+.1f}% from 50-EMA{vix_text})"
213
+
214
+
215
+ # Example usage
216
+ if __name__ == "__main__":
217
+ print("Testing Market Calculator (M Component)...\n")
218
+
219
+ # Test 1: Strong uptrend (score 100)
220
+ test_sp500_1 = {"symbol": "^GSPC", "price": 4900}
221
+ test_vix_1 = {"symbol": "^VIX", "price": 12.5}
222
+ result1 = calculate_market_direction(test_sp500_1, vix_quote=test_vix_1)
223
+ print(f"Test 1: Strong Uptrend - Score: {result1['score']}")
224
+ print(f" {result1['interpretation']}\n")
225
+
226
+ # Test 2: Choppy market (score 40)
227
+ test_sp500_2 = {"symbol": "^GSPC", "price": 4700}
228
+ # EMA estimated at 4750 (price 1% below)
229
+ test_vix_2 = {"symbol": "^VIX", "price": 18.0}
230
+ result2 = calculate_market_direction(test_sp500_2, vix_quote=test_vix_2)
231
+ print(f"Test 2: Choppy Market - Score: {result2['score']}")
232
+ print(f" {result2['interpretation']}\n")
233
+
234
+ # Test 3: Bear market (score 0)
235
+ test_sp500_3 = {"symbol": "^GSPC", "price": 4200}
236
+ # EMA estimated at 4600 (price 8.7% below)
237
+ test_vix_3 = {"symbol": "^VIX", "price": 32.0}
238
+ result3 = calculate_market_direction(test_sp500_3, vix_quote=test_vix_3)
239
+ print(f"Test 3: Bear Market - Score: {result3['score']}")
240
+ print(f" {result3['interpretation']}")
241
+ if result3['warning']:
242
+ print(f" {result3['warning']}\n")
243
+
244
+ print("✓ All tests completed")