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,536 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ VCP Stock Screener - Main Orchestrator
4
+
5
+ Screens S&P 500 stocks for Mark Minervini's Volatility Contraction Pattern (VCP).
6
+ Uses a 3-phase pipeline: Pre-filter -> Trend Template -> VCP Detection & Scoring.
7
+
8
+ Usage:
9
+ # Default (S&P 500, top 100 candidates, free tier API)
10
+ python3 screen_vcp.py --api-key YOUR_KEY
11
+
12
+ # Custom universe
13
+ python3 screen_vcp.py --universe AAPL NVDA MSFT AMZN META
14
+
15
+ # Full S&P 500 (requires paid API tier)
16
+ python3 screen_vcp.py --full-sp500
17
+
18
+ Output:
19
+ - JSON: vcp_screener_YYYY-MM-DD_HHMMSS.json
20
+ - Markdown: vcp_screener_YYYY-MM-DD_HHMMSS.md
21
+ """
22
+
23
+ import argparse
24
+ import os
25
+ import sys
26
+ from datetime import datetime
27
+ from typing import Dict, List, Optional
28
+
29
+ # Add parent directory to path for imports
30
+ sys.path.insert(0, os.path.dirname(__file__))
31
+
32
+ from fmp_client import FMPClient
33
+ from calculators.trend_template_calculator import calculate_trend_template
34
+ from calculators.vcp_pattern_calculator import calculate_vcp_pattern
35
+ from calculators.volume_pattern_calculator import calculate_volume_pattern
36
+ from calculators.pivot_proximity_calculator import calculate_pivot_proximity
37
+ from calculators.relative_strength_calculator import calculate_relative_strength
38
+ from scorer import calculate_composite_score
39
+ from report_generator import generate_json_report, generate_markdown_report
40
+
41
+
42
+ def parse_arguments():
43
+ parser = argparse.ArgumentParser(
44
+ description="VCP Stock Screener - Minervini Volatility Contraction Pattern"
45
+ )
46
+
47
+ parser.add_argument(
48
+ "--api-key",
49
+ help="FMP API key (defaults to FMP_API_KEY environment variable)"
50
+ )
51
+ parser.add_argument(
52
+ "--max-candidates", type=int, default=100,
53
+ help="Max stocks for full VCP analysis after pre-filter (default: 100)"
54
+ )
55
+ parser.add_argument(
56
+ "--top", type=int, default=20,
57
+ help="Top results to include in report (default: 20)"
58
+ )
59
+ parser.add_argument(
60
+ "--output-dir", default=".",
61
+ help="Output directory for reports"
62
+ )
63
+ parser.add_argument(
64
+ "--universe", nargs="+",
65
+ help="Custom symbols to screen (overrides S&P 500)"
66
+ )
67
+ parser.add_argument(
68
+ "--full-sp500", action="store_true",
69
+ help="Screen all S&P 500 stocks (requires paid API tier, ~350 calls)"
70
+ )
71
+ parser.add_argument(
72
+ "--mode", choices=["all", "prebreakout"], default="all",
73
+ help="Output mode: 'all' shows everything, 'prebreakout' shows entry_ready only (default: all)"
74
+ )
75
+ parser.add_argument(
76
+ "--max-above-pivot", type=float, default=3.0,
77
+ help="Max %% above pivot for entry_ready (default: 3.0)"
78
+ )
79
+ parser.add_argument(
80
+ "--max-risk", type=float, default=15.0,
81
+ help="Max risk %% for entry_ready (default: 15.0)"
82
+ )
83
+ parser.add_argument(
84
+ "--no-require-valid-vcp", action="store_true",
85
+ help="Do not require valid_vcp=True for entry_ready"
86
+ )
87
+ parser.add_argument(
88
+ "--min-atr-pct", type=float, default=1.0,
89
+ help="Min avg daily range %% to exclude stale/acquired stocks (default: 1.0)"
90
+ )
91
+ parser.add_argument(
92
+ "--ext-threshold", type=float, default=8.0,
93
+ help="SMA50 distance %% where extended penalty starts (default: 8.0)"
94
+ )
95
+
96
+ return parser.parse_args()
97
+
98
+
99
+ def pre_filter_stock(quote: Dict) -> tuple:
100
+ """
101
+ Cheap pre-filter using quote data only.
102
+
103
+ Criteria:
104
+ - Price > $10
105
+ - At least 20% above 52-week low
106
+ - Within 30% of 52-week high
107
+ - Average volume > 200,000
108
+
109
+ Returns:
110
+ (passed: bool, stage2_likelihood_score: float)
111
+ """
112
+ price = quote.get("price", 0)
113
+ year_high = quote.get("yearHigh", 0)
114
+ year_low = quote.get("yearLow", 0)
115
+ avg_volume = quote.get("avgVolume", 0)
116
+
117
+ if price <= 10:
118
+ return False, 0
119
+ if avg_volume < 200000:
120
+ return False, 0
121
+
122
+ # Check distance from 52w low
123
+ if year_low <= 0:
124
+ return False, 0
125
+ pct_above_low = (price - year_low) / year_low
126
+ if pct_above_low < 0.20:
127
+ return False, 0
128
+
129
+ # Check distance from 52w high
130
+ if year_high <= 0:
131
+ return False, 0
132
+ pct_below_high = (year_high - price) / year_high
133
+ if pct_below_high > 0.30:
134
+ return False, 0
135
+
136
+ # Stage 2 likelihood score (higher = more likely in uptrend)
137
+ # Combines proximity to high and distance from low
138
+ score = pct_above_low * 50 + (1 - pct_below_high) * 50
139
+
140
+ return True, score
141
+
142
+
143
+ def analyze_stock(
144
+ symbol: str,
145
+ historical: List[Dict],
146
+ quote: Dict,
147
+ sp500_history: List[Dict],
148
+ sector: str = "Unknown",
149
+ company_name: str = "",
150
+ ext_threshold: float = 8.0,
151
+ ) -> Optional[Dict]:
152
+ """
153
+ Full VCP analysis for a single stock (Phase 3).
154
+ No additional API calls needed - uses pre-fetched data.
155
+ """
156
+ price = quote.get("price", 0)
157
+ market_cap = quote.get("marketCap", 0)
158
+
159
+ # 1. Relative Strength (needed for Trend Template criterion 7)
160
+ rs_result = calculate_relative_strength(historical, sp500_history)
161
+ rs_rank = rs_result.get("rs_rank_estimate", 0)
162
+
163
+ # 2. Trend Template
164
+ tt_result = calculate_trend_template(
165
+ historical, quote, rs_rank=rs_rank, ext_threshold=ext_threshold
166
+ )
167
+
168
+ # 3. VCP Pattern Detection
169
+ vcp_result = calculate_vcp_pattern(historical, lookback_days=120)
170
+
171
+ # 4. Volume Pattern
172
+ pivot_price = vcp_result.get("pivot_price")
173
+ vol_result = calculate_volume_pattern(historical, pivot_price=pivot_price)
174
+
175
+ # 5. Pivot Proximity
176
+ last_low = None
177
+ contractions = vcp_result.get("contractions", [])
178
+ if contractions:
179
+ last_low = contractions[-1].get("low_price")
180
+
181
+ piv_result = calculate_pivot_proximity(
182
+ current_price=price,
183
+ pivot_price=pivot_price,
184
+ last_contraction_low=last_low,
185
+ breakout_volume=vol_result.get("breakout_volume_detected", False),
186
+ )
187
+
188
+ # 6. Composite Score
189
+ valid_vcp = vcp_result.get("valid_vcp", False)
190
+ composite = calculate_composite_score(
191
+ trend_score=tt_result.get("score", 0),
192
+ contraction_score=vcp_result.get("score", 0),
193
+ volume_score=vol_result.get("score", 0),
194
+ pivot_score=piv_result.get("score", 0),
195
+ rs_score=rs_result.get("score", 0),
196
+ valid_vcp=valid_vcp,
197
+ )
198
+
199
+ return {
200
+ "symbol": symbol,
201
+ "company_name": company_name,
202
+ "sector": sector,
203
+ "price": price,
204
+ "market_cap": market_cap,
205
+ "composite_score": composite["composite_score"],
206
+ "rating": composite["rating"],
207
+ "rating_description": composite["rating_description"],
208
+ "guidance": composite["guidance"],
209
+ "valid_vcp": valid_vcp,
210
+ "distance_from_pivot_pct": piv_result.get("distance_from_pivot_pct"),
211
+ "weakest_component": composite["weakest_component"],
212
+ "weakest_score": composite["weakest_score"],
213
+ "strongest_component": composite["strongest_component"],
214
+ "strongest_score": composite["strongest_score"],
215
+ "trend_template": tt_result,
216
+ "vcp_pattern": vcp_result,
217
+ "volume_pattern": vol_result,
218
+ "pivot_proximity": piv_result,
219
+ "relative_strength": rs_result,
220
+ }
221
+
222
+
223
+ def is_stale_price(
224
+ historical: List[Dict],
225
+ lookback: int = 10,
226
+ threshold: float = 1.0,
227
+ ) -> bool:
228
+ """Detect acquired/pinned stocks with abnormally flat price action.
229
+
230
+ Args:
231
+ historical: Price data (most-recent-first)
232
+ lookback: Number of recent days to check
233
+ threshold: Max average daily range % to be considered stale
234
+
235
+ Returns:
236
+ True if the stock's price action is stale (likely acquired/pinned)
237
+ """
238
+ if len(historical) < lookback:
239
+ return False
240
+
241
+ recent = historical[:lookback]
242
+ ranges = []
243
+ for bar in recent:
244
+ high = bar.get("high", 0)
245
+ low = bar.get("low", 0)
246
+ close = bar.get("close", 0)
247
+ if close > 0:
248
+ ranges.append((high - low) / close * 100)
249
+
250
+ if not ranges:
251
+ return False
252
+
253
+ avg_range_pct = sum(ranges) / len(ranges)
254
+ return avg_range_pct < threshold
255
+
256
+
257
+ def compute_entry_ready(
258
+ result: Dict,
259
+ max_above_pivot: float = 3.0,
260
+ max_risk: float = 15.0,
261
+ require_valid_vcp: bool = True,
262
+ ) -> bool:
263
+ """Determine if a stock is entry-ready based on configurable thresholds.
264
+
265
+ Args:
266
+ result: Analysis result dict from analyze_stock()
267
+ max_above_pivot: Max % above pivot for entry readiness
268
+ max_risk: Max risk % for entry readiness
269
+ require_valid_vcp: Whether valid_vcp=True is required
270
+ """
271
+ valid_vcp = result.get("valid_vcp", False)
272
+ distance = result.get("distance_from_pivot_pct")
273
+ dry_up_ratio = result.get("volume_pattern", {}).get("dry_up_ratio")
274
+ risk_pct = result.get("pivot_proximity", {}).get("risk_pct")
275
+
276
+ if require_valid_vcp and not valid_vcp:
277
+ return False
278
+ if distance is None:
279
+ return False
280
+ if not (-8.0 <= distance <= max_above_pivot):
281
+ return False
282
+ if dry_up_ratio is None or dry_up_ratio > 1.0:
283
+ return False
284
+ if risk_pct is None or risk_pct > max_risk:
285
+ return False
286
+ return True
287
+
288
+
289
+ def main():
290
+ args = parse_arguments()
291
+
292
+ if not (0 < args.ext_threshold < 50):
293
+ print("ERROR: --ext-threshold must be between 0 and 50 (exclusive)",
294
+ file=sys.stderr)
295
+ sys.exit(1)
296
+
297
+ print("=" * 70)
298
+ print("VCP Stock Screener")
299
+ print("Mark Minervini's Volatility Contraction Pattern")
300
+ print("=" * 70)
301
+ print()
302
+
303
+ # Initialize FMP client
304
+ try:
305
+ client = FMPClient(api_key=args.api_key)
306
+ print("FMP API client initialized")
307
+ except ValueError as e:
308
+ print(f"ERROR: {e}", file=sys.stderr)
309
+ sys.exit(1)
310
+
311
+ # ========================================================================
312
+ # Phase 1: Pre-Filter (API-efficient)
313
+ # ========================================================================
314
+ print()
315
+ print("Phase 1: Pre-Filter")
316
+ print("-" * 70)
317
+
318
+ # Determine universe
319
+ if args.universe:
320
+ symbols = args.universe
321
+ universe_desc = f"Custom ({len(symbols)} stocks)"
322
+ print(f" Using custom universe: {len(symbols)} stocks")
323
+ else:
324
+ print(" Fetching S&P 500 constituents...", end=" ", flush=True)
325
+ constituents = client.get_sp500_constituents()
326
+ if not constituents:
327
+ print("FAILED")
328
+ print("ERROR: Unable to fetch S&P 500 constituents", file=sys.stderr)
329
+ sys.exit(1)
330
+ symbols = [c["symbol"] for c in constituents]
331
+ universe_desc = f"S&P 500 ({len(symbols)} stocks)"
332
+ print(f"OK ({len(symbols)} stocks)")
333
+
334
+ # Build sector/name lookup
335
+ sector_map = {}
336
+ name_map = {}
337
+ if not args.universe and constituents:
338
+ for c in constituents:
339
+ sector_map[c["symbol"]] = c.get("sector", "Unknown")
340
+ name_map[c["symbol"]] = c.get("name", c["symbol"])
341
+
342
+ # Batch fetch quotes
343
+ print(f" Fetching quotes...", end=" ", flush=True)
344
+ all_quotes = client.get_batch_quotes(symbols)
345
+ print(f"OK ({len(all_quotes)} quotes)")
346
+
347
+ # Apply pre-filter
348
+ print(f" Applying pre-filter...", end=" ", flush=True)
349
+ pre_filtered = []
350
+ for sym in symbols:
351
+ quote = all_quotes.get(sym)
352
+ if not quote:
353
+ continue
354
+ passed, likelihood = pre_filter_stock(quote)
355
+ if passed:
356
+ pre_filtered.append((sym, likelihood, quote))
357
+
358
+ # Sort by Stage 2 likelihood, take top candidates
359
+ pre_filtered.sort(key=lambda x: x[1], reverse=True)
360
+ max_candidates = len(pre_filtered) if args.full_sp500 else args.max_candidates
361
+ candidates = pre_filtered[:max_candidates]
362
+
363
+ print(f"{len(pre_filtered)} passed, taking top {len(candidates)}")
364
+ print()
365
+
366
+ # ========================================================================
367
+ # Phase 2: Trend Template Filter
368
+ # ========================================================================
369
+ print("Phase 2: Trend Template Filter")
370
+ print("-" * 70)
371
+
372
+ # Fetch SPY historical for RS calculation
373
+ print(" Fetching SPY 260-day history...", end=" ", flush=True)
374
+ spy_data = client.get_historical_prices("SPY", days=260)
375
+ sp500_history = spy_data.get("historical", []) if spy_data else []
376
+ if sp500_history:
377
+ print(f"OK ({len(sp500_history)} days)")
378
+ else:
379
+ print("WARN - SPY data unavailable, RS calculations will be limited")
380
+
381
+ # Fetch historical data for candidates
382
+ candidate_symbols = [c[0] for c in candidates]
383
+ print(f" Fetching 260-day histories for {len(candidate_symbols)} candidates...")
384
+
385
+ candidate_histories = {}
386
+ for i, sym in enumerate(candidate_symbols):
387
+ if (i + 1) % 20 == 0 or i == len(candidate_symbols) - 1:
388
+ print(f" Progress: {i + 1}/{len(candidate_symbols)}", flush=True)
389
+ data = client.get_historical_prices(sym, days=260)
390
+ if data and 'historical' in data:
391
+ candidate_histories[sym] = data['historical']
392
+
393
+ # Apply Trend Template filter
394
+ print(f" Applying 7-point Trend Template...", end=" ", flush=True)
395
+ trend_passed = []
396
+
397
+ for sym, likelihood, quote in candidates:
398
+ hist = candidate_histories.get(sym, [])
399
+ if not hist or len(hist) < 50:
400
+ continue
401
+
402
+ # Quick RS calculation for criterion 7
403
+ rs_result = calculate_relative_strength(hist, sp500_history)
404
+ rs_rank = rs_result.get("rs_rank_estimate", 0)
405
+
406
+ tt_result = calculate_trend_template(
407
+ hist, quote, rs_rank=rs_rank, ext_threshold=args.ext_threshold
408
+ )
409
+ if tt_result.get("passed"):
410
+ trend_passed.append((sym, quote))
411
+
412
+ print(f"{len(trend_passed)} passed")
413
+ print()
414
+
415
+ # ========================================================================
416
+ # Phase 3: VCP Detection & Scoring
417
+ # ========================================================================
418
+ print("Phase 3: VCP Detection & Scoring")
419
+ print("-" * 70)
420
+
421
+ results = []
422
+ for sym, quote in trend_passed:
423
+ hist = candidate_histories.get(sym, [])
424
+ sector = sector_map.get(sym, "Unknown")
425
+ name = name_map.get(sym, sym)
426
+
427
+ # For custom universe, try to get name from quote
428
+ if not name or name == sym:
429
+ name = quote.get("name", sym)
430
+ if not sector or sector == "Unknown":
431
+ sector = quote.get("sector", "Unknown")
432
+
433
+ # Skip stale/acquired stocks
434
+ if is_stale_price(hist, threshold=args.min_atr_pct):
435
+ print(f" Skipping {sym} (stale price - likely acquired/pinned)")
436
+ continue
437
+
438
+ print(f" Analyzing {sym}...", end=" ", flush=True)
439
+ analysis = analyze_stock(
440
+ sym, hist, quote, sp500_history, sector, name,
441
+ ext_threshold=args.ext_threshold,
442
+ )
443
+
444
+ if analysis:
445
+ score = analysis["composite_score"]
446
+ print(f"Score: {score:.1f} ({analysis['rating']})")
447
+ results.append(analysis)
448
+ else:
449
+ print("FAILED")
450
+
451
+ print()
452
+
453
+ # Compute entry_ready using CLI thresholds
454
+ require_vcp = not args.no_require_valid_vcp
455
+ for r in results:
456
+ r["entry_ready"] = compute_entry_ready(
457
+ r,
458
+ max_above_pivot=args.max_above_pivot,
459
+ max_risk=args.max_risk,
460
+ require_valid_vcp=require_vcp,
461
+ )
462
+
463
+ # Sort by composite score
464
+ results.sort(key=lambda x: x["composite_score"], reverse=True)
465
+
466
+ # Apply prebreakout filter if requested
467
+ if args.mode == "prebreakout":
468
+ total_before = len(results)
469
+ results = [r for r in results if r.get("entry_ready", False)]
470
+ print(f" Pre-breakout filter: {total_before} -> {len(results)} candidates")
471
+ print()
472
+
473
+ # ========================================================================
474
+ # Generate Reports
475
+ # ========================================================================
476
+ print("Generating Reports")
477
+ print("-" * 70)
478
+
479
+ timestamp = datetime.now().strftime("%Y-%m-%d_%H%M%S")
480
+ json_file = os.path.join(args.output_dir, f"vcp_screener_{timestamp}.json")
481
+ md_file = os.path.join(args.output_dir, f"vcp_screener_{timestamp}.md")
482
+
483
+ api_stats = client.get_api_stats()
484
+
485
+ metadata = {
486
+ "generated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
487
+ "universe_description": universe_desc,
488
+ "max_candidates": max_candidates,
489
+ "ext_threshold": args.ext_threshold,
490
+ "funnel": {
491
+ "universe": len(symbols),
492
+ "pre_filter_passed": len(pre_filtered),
493
+ "trend_template_passed": len(trend_passed),
494
+ "vcp_candidates": len(results),
495
+ },
496
+ "api_stats": api_stats,
497
+ }
498
+
499
+ top_results = results[:args.top]
500
+
501
+ generate_json_report(top_results, metadata, json_file, all_results=results)
502
+ generate_markdown_report(top_results, metadata, md_file, all_results=results)
503
+
504
+ # ========================================================================
505
+ # Summary
506
+ # ========================================================================
507
+ print()
508
+ print("=" * 70)
509
+ print("VCP Screening Complete")
510
+ print("=" * 70)
511
+
512
+ # Top 5 display
513
+ if results:
514
+ print()
515
+ print(f"Top {min(5, len(results))} Results:")
516
+ for i, s in enumerate(results[:5], 1):
517
+ pivot = s.get("vcp_pattern", {}).get("pivot_price")
518
+ pivot_str = f"Pivot: ${pivot:.2f}" if pivot else ""
519
+ print(f" {i}. {s['symbol']:6} Score: {s['composite_score']:5.1f} "
520
+ f"({s['rating']}) {pivot_str}")
521
+ else:
522
+ print()
523
+ print(" No VCP candidates found in this screening run.")
524
+
525
+ print()
526
+ print(f" JSON Report: {json_file}")
527
+ print(f" Markdown Report: {md_file}")
528
+ print()
529
+ print(f"API Usage:")
530
+ print(f" API calls made: {api_stats['api_calls_made']}")
531
+ print(f" Cache entries: {api_stats['cache_entries']}")
532
+ print()
533
+
534
+
535
+ if __name__ == "__main__":
536
+ main()
@@ -0,0 +1,9 @@
1
+ """Shared fixtures for VCP Screener tests"""
2
+
3
+ import os
4
+ import sys
5
+
6
+ # Add scripts directory to path so modules can be imported
7
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
8
+ # Add tests directory to path so helpers can be imported
9
+ sys.path.insert(0, os.path.dirname(__file__))