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,194 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ N Component - Newness / New Highs Calculator
4
+
5
+ Calculates CANSLIM 'N' component score based on price position relative to
6
+ 52-week high and momentum indicators.
7
+
8
+ O'Neil's Rule: "Stocks making new price highs have no overhead supply.
9
+ New products, services, or management catalyze major moves."
10
+
11
+ Scoring:
12
+ - 100: Within 5% of 52-week high + breakout + new product catalyst
13
+ - 80: Within 10% of 52-week high + breakout
14
+ - 60: Within 15% of 52-week high OR breakout
15
+ - 40: Within 25% of 52-week high
16
+ - 20: >25% from 52-week high (lacks momentum)
17
+ """
18
+
19
+ from typing import Dict, List, Optional
20
+
21
+
22
+ def calculate_newness(quote: Dict, historical_prices: Optional[Dict] = None) -> Dict:
23
+ """
24
+ Calculate N component score based on price position and momentum
25
+
26
+ Args:
27
+ quote: Stock quote data from FMP API (contains yearHigh, price, volume)
28
+ historical_prices: Optional historical price data for detailed analysis
29
+
30
+ Returns:
31
+ Dict with:
32
+ - score: 0-100 points
33
+ - distance_from_high_pct: Distance from 52-week high (%)
34
+ - current_price: Current stock price
35
+ - week_52_high: 52-week high price
36
+ - week_52_low: 52-week low price
37
+ - breakout_detected: Boolean
38
+ - interpretation: Human-readable interpretation
39
+ """
40
+ # Validate input
41
+ if not quote:
42
+ return {
43
+ "score": 0,
44
+ "error": "Quote data missing",
45
+ "distance_from_high_pct": None,
46
+ "interpretation": "Data unavailable"
47
+ }
48
+
49
+ # Extract price data
50
+ current_price = quote.get("price")
51
+ week_52_high = quote.get("yearHigh")
52
+ week_52_low = quote.get("yearLow")
53
+ current_volume = quote.get("volume")
54
+ avg_volume = quote.get("avgVolume")
55
+
56
+ if not current_price or not week_52_high:
57
+ return {
58
+ "score": 0,
59
+ "error": "Price or 52-week high data missing",
60
+ "distance_from_high_pct": None,
61
+ "interpretation": "Data unavailable"
62
+ }
63
+
64
+ # Calculate distance from 52-week high
65
+ distance_from_high_pct = ((current_price / week_52_high) - 1) * 100
66
+
67
+ # Detect breakout (new high on elevated volume)
68
+ breakout_detected = False
69
+ if current_volume and avg_volume:
70
+ # Within 0.5% of high AND volume 40%+ above average
71
+ if current_price >= week_52_high * 0.995 and current_volume > avg_volume * 1.4:
72
+ breakout_detected = True
73
+
74
+ # Calculate score
75
+ score = score_newness(distance_from_high_pct, breakout_detected)
76
+
77
+ # Generate interpretation
78
+ interpretation = interpret_newness_score(score, distance_from_high_pct, breakout_detected)
79
+
80
+ return {
81
+ "score": score,
82
+ "distance_from_high_pct": round(distance_from_high_pct, 1),
83
+ "current_price": current_price,
84
+ "week_52_high": week_52_high,
85
+ "week_52_low": week_52_low,
86
+ "breakout_detected": breakout_detected,
87
+ "current_volume": current_volume,
88
+ "avg_volume": avg_volume,
89
+ "volume_ratio": round(current_volume / avg_volume, 2) if avg_volume else None,
90
+ "interpretation": interpretation,
91
+ "error": None
92
+ }
93
+
94
+
95
+ def score_newness(distance_from_high_pct: float, breakout_detected: bool) -> int:
96
+ """
97
+ Score N component based on price position and breakout
98
+
99
+ Args:
100
+ distance_from_high_pct: Distance from 52-week high (negative = below high)
101
+ breakout_detected: Boolean indicating volume-confirmed breakout
102
+
103
+ Returns:
104
+ Score (0-100)
105
+
106
+ Scoring Logic:
107
+ - Within 5% of high + breakout: 100
108
+ - Within 10% of high + breakout: 80
109
+ - Within 15% of high OR breakout: 60
110
+ - Within 25% of high: 40
111
+ - >25% from high: 20
112
+ """
113
+ if distance_from_high_pct >= -5 and breakout_detected:
114
+ return 100 # Perfect setup - at new highs with volume
115
+ elif distance_from_high_pct >= -10 and breakout_detected:
116
+ return 80 # Strong momentum
117
+ elif distance_from_high_pct >= -15 or breakout_detected:
118
+ return 60 # Acceptable
119
+ elif distance_from_high_pct >= -25:
120
+ return 40 # Weak momentum
121
+ else:
122
+ return 20 # Too far from highs, lacks sponsorship
123
+
124
+
125
+ def interpret_newness_score(score: int, distance: float, breakout: bool) -> str:
126
+ """
127
+ Generate human-readable interpretation
128
+
129
+ Args:
130
+ score: Component score
131
+ distance: Distance from 52-week high (%)
132
+ breakout: Breakout detected flag
133
+
134
+ Returns:
135
+ Interpretation string
136
+ """
137
+ breakout_text = " with volume breakout" if breakout else ""
138
+
139
+ if score >= 90:
140
+ return f"Exceptional - At new highs{breakout_text} ({distance:+.1f}% from high)"
141
+ elif score >= 70:
142
+ return f"Strong - Near 52-week high{breakout_text} ({distance:+.1f}% from high)"
143
+ elif score >= 50:
144
+ return f"Acceptable - Within 15% of high ({distance:+.1f}% from high)"
145
+ elif score >= 30:
146
+ return f"Weak - Lacks momentum ({distance:+.1f}% from high)"
147
+ else:
148
+ return f"Poor - Too far from highs, overhead resistance ({distance:+.1f}% from high)"
149
+
150
+
151
+ # Example usage
152
+ if __name__ == "__main__":
153
+ print("Testing New Highs Calculator (N Component)...\n")
154
+
155
+ # Test 1: At new high with breakout (score 100)
156
+ test_quote_1 = {
157
+ "symbol": "NVDA",
158
+ "price": 495.50,
159
+ "yearHigh": 496.00,
160
+ "yearLow": 280.00,
161
+ "volume": 60000000,
162
+ "avgVolume": 40000000
163
+ }
164
+ result1 = calculate_newness(test_quote_1)
165
+ print(f"Test 1: New High + Breakout - Score: {result1['score']}")
166
+ print(f" {result1['interpretation']}\n")
167
+
168
+ # Test 2: Within 10% of high, no breakout (score 60)
169
+ test_quote_2 = {
170
+ "symbol": "META",
171
+ "price": 450.00,
172
+ "yearHigh": 490.00,
173
+ "yearLow": 320.00,
174
+ "volume": 15000000,
175
+ "avgVolume": 16000000
176
+ }
177
+ result2 = calculate_newness(test_quote_2)
178
+ print(f"Test 2: Near High, No Breakout - Score: {result2['score']}")
179
+ print(f" {result2['interpretation']}\n")
180
+
181
+ # Test 3: Far from high (score 20)
182
+ test_quote_3 = {
183
+ "symbol": "XYZ",
184
+ "price": 50.00,
185
+ "yearHigh": 80.00,
186
+ "yearLow": 45.00,
187
+ "volume": 1000000,
188
+ "avgVolume": 1200000
189
+ }
190
+ result3 = calculate_newness(test_quote_3)
191
+ print(f"Test 3: Far from High - Score: {result3['score']}")
192
+ print(f" {result3['interpretation']}\n")
193
+
194
+ print("✓ All tests completed")
@@ -0,0 +1,221 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ S Component - Supply and Demand Calculator
4
+
5
+ Calculates CANSLIM 'S' component score based on volume accumulation/distribution patterns.
6
+
7
+ O'Neil's Rule: "Volume is the gas in the tank of a stock. Without gas, the car doesn't
8
+ go anywhere. Look for stocks where volume expands on up days and contracts on down days."
9
+
10
+ Key Principle:
11
+ - UP-DAY VOLUME > DOWN-DAY VOLUME = Accumulation (institutions buying)
12
+ - DOWN-DAY VOLUME > UP-DAY VOLUME = Distribution (institutions selling)
13
+
14
+ Scoring:
15
+ - 100 points: Up/down ratio ≥ 2.0 (Strong Accumulation)
16
+ - 80 points: Ratio 1.5-2.0 (Accumulation)
17
+ - 60 points: Ratio 1.0-1.5 (Neutral/Weak Accumulation)
18
+ - 40 points: Ratio 0.7-1.0 (Neutral/Weak Distribution)
19
+ - 20 points: Ratio 0.5-0.7 (Distribution)
20
+ - 0 points: Ratio < 0.5 (Strong Distribution)
21
+ """
22
+
23
+ from typing import Dict, List
24
+
25
+
26
+ def calculate_supply_demand(historical_prices: Dict) -> Dict:
27
+ """
28
+ Calculate supply/demand dynamics based on volume patterns
29
+
30
+ Args:
31
+ historical_prices: Historical price data from FMP API
32
+ Dict with keys: 'historical' (list of daily price/volume data)
33
+ Each entry should have: date, close, volume
34
+
35
+ Returns:
36
+ Dict with:
37
+ - score: 0-100 points
38
+ - up_down_ratio: Average up-day volume / average down-day volume
39
+ - accumulation_detected: True if ratio ≥ 1.5
40
+ - avg_up_volume: Average volume on up days
41
+ - avg_down_volume: Average volume on down days
42
+ - up_days_count: Number of up days in analysis period
43
+ - down_days_count: Number of down days
44
+ - interpretation: Human-readable interpretation
45
+ - quality_warning: Warning if data quality issues
46
+ - error: Error message if calculation failed
47
+
48
+ Example:
49
+ >>> prices = client.get_historical_prices("NVDA", days=90)
50
+ >>> result = calculate_supply_demand(prices)
51
+ >>> print(f"S Score: {result['score']}, Ratio: {result['up_down_ratio']:.2f}")
52
+ """
53
+ # Validate input
54
+ if not historical_prices or "historical" not in historical_prices:
55
+ return {
56
+ "score": 0,
57
+ "error": "No historical price data provided",
58
+ "up_down_ratio": None,
59
+ "accumulation_detected": False,
60
+ "avg_up_volume": None,
61
+ "avg_down_volume": None,
62
+ "up_days_count": 0,
63
+ "down_days_count": 0,
64
+ "interpretation": "Data unavailable"
65
+ }
66
+
67
+ prices = historical_prices["historical"]
68
+
69
+ if not prices or len(prices) < 60:
70
+ return {
71
+ "score": 0,
72
+ "error": f"Insufficient data (need 60+ days, got {len(prices)})",
73
+ "up_down_ratio": None,
74
+ "accumulation_detected": False,
75
+ "avg_up_volume": None,
76
+ "avg_down_volume": None,
77
+ "up_days_count": 0,
78
+ "down_days_count": 0,
79
+ "interpretation": "Data unavailable",
80
+ "quality_warning": "Less than 60 days of historical data available"
81
+ }
82
+
83
+ # Analyze last 60 days (prices are typically newest first, so take first 60)
84
+ analysis_period = prices[:60]
85
+
86
+ # Classify days as up or down
87
+ up_days = []
88
+ down_days = []
89
+ previous_close = None
90
+
91
+ for day in reversed(analysis_period): # Process chronologically
92
+ current_close = day.get("close")
93
+ current_volume = day.get("volume")
94
+
95
+ if current_close is None or current_volume is None:
96
+ continue # Skip incomplete data
97
+
98
+ if previous_close is not None:
99
+ if current_close > previous_close:
100
+ up_days.append(current_volume)
101
+ elif current_close < previous_close:
102
+ down_days.append(current_volume)
103
+ # Unchanged days are ignored
104
+
105
+ previous_close = current_close
106
+
107
+ # Validate we have enough classified days
108
+ if len(up_days) < 10 or len(down_days) < 10:
109
+ return {
110
+ "score": 0,
111
+ "error": "Insufficient up/down days for analysis",
112
+ "up_down_ratio": None,
113
+ "accumulation_detected": False,
114
+ "avg_up_volume": None,
115
+ "avg_down_volume": None,
116
+ "up_days_count": len(up_days),
117
+ "down_days_count": len(down_days),
118
+ "interpretation": "Data quality issues",
119
+ "quality_warning": f"Too few classified days (up: {len(up_days)}, down: {len(down_days)})"
120
+ }
121
+
122
+ # Calculate average volumes
123
+ avg_up_volume = sum(up_days) / len(up_days)
124
+ avg_down_volume = sum(down_days) / len(down_days)
125
+
126
+ # Calculate accumulation/distribution ratio
127
+ if avg_down_volume == 0:
128
+ # Edge case: no volume on down days (extremely bullish, but rare)
129
+ ratio = 5.0 # Cap at 5.0 to avoid infinity
130
+ else:
131
+ ratio = avg_up_volume / avg_down_volume
132
+
133
+ # Determine accumulation status
134
+ accumulation_detected = ratio >= 1.5
135
+
136
+ # Score based on ratio
137
+ score = score_supply_demand(ratio)
138
+
139
+ # Generate interpretation
140
+ interpretation = interpret_supply_demand(ratio, accumulation_detected)
141
+
142
+ return {
143
+ "score": score,
144
+ "up_down_ratio": ratio,
145
+ "accumulation_detected": accumulation_detected,
146
+ "avg_up_volume": int(avg_up_volume),
147
+ "avg_down_volume": int(avg_down_volume),
148
+ "up_days_count": len(up_days),
149
+ "down_days_count": len(down_days),
150
+ "interpretation": interpretation
151
+ }
152
+
153
+
154
+ def score_supply_demand(ratio: float) -> int:
155
+ """
156
+ Score accumulation/distribution ratio
157
+
158
+ Args:
159
+ ratio: Up-day volume / down-day volume
160
+
161
+ Returns:
162
+ int: Score from 0-100
163
+ """
164
+ if ratio >= 2.0:
165
+ return 100 # Strong Accumulation
166
+ elif ratio >= 1.5:
167
+ return 80 # Accumulation
168
+ elif ratio >= 1.0:
169
+ return 60 # Neutral/Weak Accumulation
170
+ elif ratio >= 0.7:
171
+ return 40 # Neutral/Weak Distribution
172
+ elif ratio >= 0.5:
173
+ return 20 # Distribution
174
+ else:
175
+ return 0 # Strong Distribution
176
+
177
+
178
+ def interpret_supply_demand(ratio: float, accumulation: bool) -> str:
179
+ """
180
+ Generate human-readable interpretation of supply/demand dynamics
181
+
182
+ Args:
183
+ ratio: Up-day volume / down-day volume
184
+ accumulation: True if accumulation detected
185
+
186
+ Returns:
187
+ str: Interpretation string
188
+ """
189
+ if ratio >= 2.0:
190
+ return f"Strong Accumulation (ratio: {ratio:.2f}x) - Institutions aggressively buying. Volume precedes price."
191
+ elif ratio >= 1.5:
192
+ return f"Accumulation (ratio: {ratio:.2f}x) - Bullish volume pattern. Institutional interest building."
193
+ elif ratio >= 1.0:
194
+ return f"Neutral/Weak Accumulation (ratio: {ratio:.2f}x) - Slightly positive volume trend."
195
+ elif ratio >= 0.7:
196
+ return f"Neutral/Weak Distribution (ratio: {ratio:.2f}x) - Slightly negative volume trend. Monitor closely."
197
+ elif ratio >= 0.5:
198
+ return f"Distribution (ratio: {ratio:.2f}x) - Bearish volume pattern. Institutions may be selling."
199
+ else:
200
+ return f"Strong Distribution (ratio: {ratio:.2f}x) - Heavy selling pressure. Volume leads price lower."
201
+
202
+
203
+ # Example usage
204
+ if __name__ == "__main__":
205
+ # Test with sample data
206
+ sample_data = {
207
+ "historical": [
208
+ {"date": "2024-12-10", "close": 150.0, "volume": 80000000}, # up day
209
+ {"date": "2024-12-09", "close": 148.0, "volume": 50000000}, # down day
210
+ {"date": "2024-12-06", "close": 149.0, "volume": 75000000}, # up day
211
+ {"date": "2024-12-05", "close": 147.0, "volume": 45000000}, # down day
212
+ ] * 15 # Repeat to get 60 days
213
+ }
214
+
215
+ result = calculate_supply_demand(sample_data)
216
+
217
+ print("S Component Test Results:")
218
+ print(f" Score: {result['score']}/100")
219
+ print(f" Up/Down Ratio: {result['up_down_ratio']:.2f}")
220
+ print(f" Accumulation: {result['accumulation_detected']}")
221
+ print(f" Interpretation: {result['interpretation']}")
@@ -0,0 +1,227 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Finviz Stock Data Client
4
+
5
+ Fetches individual stock data from Finviz including institutional ownership.
6
+ Uses simple web scraping with BeautifulSoup (no API key required).
7
+
8
+ Install: pip install beautifulsoup4 requests lxml
9
+
10
+ Usage:
11
+ from finviz_stock_client import FinvizStockClient
12
+
13
+ client = FinvizStockClient()
14
+ data = client.get_institutional_ownership("AAPL")
15
+ print(f"Inst Own: {data['inst_own_pct']}%")
16
+ """
17
+
18
+ import sys
19
+ import time
20
+ import re
21
+ from typing import Dict, Optional
22
+
23
+ try:
24
+ import requests
25
+ from bs4 import BeautifulSoup
26
+ except ImportError:
27
+ print("ERROR: required libraries not found. Install with: pip install beautifulsoup4 requests lxml", file=sys.stderr)
28
+ sys.exit(1)
29
+
30
+
31
+ class FinvizStockClient:
32
+ """Client for fetching stock data from Finviz"""
33
+
34
+ BASE_URL = "https://finviz.com/quote.ashx"
35
+
36
+ def __init__(self, rate_limit_seconds: float = 2.0):
37
+ """
38
+ Initialize Finviz client
39
+
40
+ Args:
41
+ rate_limit_seconds: Delay between requests to avoid rate limiting (default: 2.0s)
42
+ """
43
+ self.rate_limit_seconds = rate_limit_seconds
44
+ self.last_request_time = 0.0
45
+ self.cache = {}
46
+ self.session = requests.Session()
47
+ # Use a realistic user agent to avoid blocking
48
+ self.session.headers.update({
49
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'
50
+ })
51
+
52
+ def _rate_limited_fetch(self, symbol: str) -> Optional[Dict]:
53
+ """
54
+ Fetch stock data with rate limiting
55
+
56
+ Args:
57
+ symbol: Stock ticker symbol
58
+
59
+ Returns:
60
+ Dict with stock data, or None if fetch failed
61
+ """
62
+ # Respect rate limit
63
+ elapsed = time.time() - self.last_request_time
64
+ if elapsed < self.rate_limit_seconds:
65
+ time.sleep(self.rate_limit_seconds - elapsed)
66
+
67
+ try:
68
+ url = f"{self.BASE_URL}?t={symbol}"
69
+ response = self.session.get(url, timeout=15)
70
+ self.last_request_time = time.time()
71
+
72
+ if response.status_code == 200:
73
+ return self._parse_finviz_page(response.text)
74
+ else:
75
+ print(f"WARNING: Finviz request failed with status {response.status_code} for {symbol}", file=sys.stderr)
76
+ return None
77
+
78
+ except Exception as e:
79
+ print(f"WARNING: Failed to fetch Finviz data for {symbol}: {e}", file=sys.stderr)
80
+ self.last_request_time = time.time()
81
+ return None
82
+
83
+ def _parse_finviz_page(self, html: str) -> Dict:
84
+ """
85
+ Parse Finviz stock page HTML
86
+
87
+ Args:
88
+ html: HTML content
89
+
90
+ Returns:
91
+ Dict with extracted data fields
92
+ """
93
+ soup = BeautifulSoup(html, 'lxml')
94
+
95
+ # Finviz uses a table structure with label-value pairs
96
+ data = {}
97
+
98
+ # Find all table rows containing data
99
+ tables = soup.find_all('table', {'class': 'snapshot-table2'})
100
+
101
+ for table in tables:
102
+ rows = table.find_all('tr')
103
+ for row in rows:
104
+ cells = row.find_all('td')
105
+ # Each row has pairs of (label, value, label, value, ...)
106
+ for i in range(0, len(cells), 2):
107
+ if i + 1 < len(cells):
108
+ label = cells[i].get_text(strip=True)
109
+ value = cells[i + 1].get_text(strip=True)
110
+ data[label] = value
111
+
112
+ return data
113
+
114
+ def get_institutional_ownership(self, symbol: str) -> Dict:
115
+ """
116
+ Get institutional ownership data from Finviz
117
+
118
+ Args:
119
+ symbol: Stock ticker symbol
120
+
121
+ Returns:
122
+ Dict with:
123
+ - inst_own_pct: Institutional ownership percentage (float, or None)
124
+ - inst_trans_pct: Institutional transactions percentage (float, or None)
125
+ - error: Error message if data unavailable
126
+
127
+ Example:
128
+ >>> client = FinvizStockClient()
129
+ >>> data = client.get_institutional_ownership("AAPL")
130
+ >>> print(f"Inst Own: {data['inst_own_pct']}%")
131
+ Inst Own: 60.5%
132
+ """
133
+ # Check cache first
134
+ cache_key = f"finviz_inst_{symbol}"
135
+ if cache_key in self.cache:
136
+ return self.cache[cache_key]
137
+
138
+ # Fetch from Finviz
139
+ stock_data = self._rate_limited_fetch(symbol)
140
+
141
+ if not stock_data:
142
+ result = {
143
+ "inst_own_pct": None,
144
+ "inst_trans_pct": None,
145
+ "error": f"Failed to fetch data from Finviz for {symbol}"
146
+ }
147
+ self.cache[cache_key] = result
148
+ return result
149
+
150
+ # Extract institutional data
151
+ # Finviz returns strings like "60.50%" or "-" if unavailable
152
+ inst_own_str = stock_data.get("Inst Own", "-")
153
+ inst_trans_str = stock_data.get("Inst Trans", "-")
154
+
155
+ # Parse percentage strings
156
+ inst_own_pct = self._parse_percentage(inst_own_str)
157
+ inst_trans_pct = self._parse_percentage(inst_trans_str)
158
+
159
+ result = {
160
+ "inst_own_pct": inst_own_pct,
161
+ "inst_trans_pct": inst_trans_pct,
162
+ "error": None
163
+ }
164
+
165
+ # Cache result
166
+ self.cache[cache_key] = result
167
+ return result
168
+
169
+ @staticmethod
170
+ def _parse_percentage(pct_str: str) -> Optional[float]:
171
+ """
172
+ Parse percentage string from Finviz
173
+
174
+ Args:
175
+ pct_str: Percentage string like "60.50%" or "-"
176
+
177
+ Returns:
178
+ Float percentage value (60.50), or None if unavailable
179
+ """
180
+ if not pct_str or pct_str == "-":
181
+ return None
182
+
183
+ try:
184
+ # Remove '%' and convert to float
185
+ return float(pct_str.rstrip('%'))
186
+ except (ValueError, AttributeError):
187
+ return None
188
+
189
+ def get_stock_data(self, symbol: str) -> Optional[Dict]:
190
+ """
191
+ Get full stock data dict from Finviz
192
+
193
+ Args:
194
+ symbol: Stock ticker symbol
195
+
196
+ Returns:
197
+ Dict with all Finviz data fields, or None if unavailable
198
+ """
199
+ cache_key = f"finviz_full_{symbol}"
200
+ if cache_key in self.cache:
201
+ return self.cache[cache_key]
202
+
203
+ stock_data = self._rate_limited_fetch(symbol)
204
+ if stock_data:
205
+ self.cache[cache_key] = stock_data
206
+
207
+ return stock_data
208
+
209
+
210
+ # Example usage
211
+ if __name__ == "__main__":
212
+ import argparse
213
+
214
+ parser = argparse.ArgumentParser(description="Test Finviz institutional ownership fetcher")
215
+ parser.add_argument("symbol", help="Stock ticker symbol")
216
+ args = parser.parse_args()
217
+
218
+ client = FinvizStockClient()
219
+ data = client.get_institutional_ownership(args.symbol)
220
+
221
+ print("\nFinviz Institutional Ownership Data:")
222
+ print(f" Symbol: {args.symbol}")
223
+ print(f" Inst Own: {data['inst_own_pct']}%" if data['inst_own_pct'] is not None else " Inst Own: N/A")
224
+ print(f" Inst Trans: {data['inst_trans_pct']}%" if data['inst_trans_pct'] is not None else " Inst Trans: N/A")
225
+
226
+ if data.get('error'):
227
+ print(f" Error: {data['error']}")