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,322 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ VCP Pattern Calculator - Core Volatility Contraction Pattern Detection
4
+
5
+ Implements Mark Minervini's VCP detection algorithm:
6
+ 1. Find swing highs and lows within a 120-day lookback window
7
+ 2. Identify successive contractions (T1, T2, T3, T4)
8
+ 3. Validate that each contraction is tighter than the previous
9
+ 4. Score based on number of contractions, tightness, and depth ratios
10
+
11
+ VCP Characteristics:
12
+ - T1 (first correction): 8-35% depth for S&P 500 large-caps
13
+ - Each successive contraction should be 25%+ tighter than the previous
14
+ - Minimum 2 contractions required for valid VCP
15
+ - Successive highs should be within 5% of each other
16
+ - Pattern duration: 15-325 trading days
17
+ """
18
+
19
+ from typing import Dict, List, Optional, Tuple
20
+
21
+
22
+ def calculate_vcp_pattern(
23
+ historical_prices: List[Dict],
24
+ lookback_days: int = 120,
25
+ ) -> Dict:
26
+ """
27
+ Detect Volatility Contraction Pattern in price data.
28
+
29
+ Args:
30
+ historical_prices: Daily OHLCV data (most recent first), need 30+ days
31
+ lookback_days: Number of days to look back for pattern (default 120)
32
+
33
+ Returns:
34
+ Dict with score (0-100), contractions list, pattern validity, pivot point
35
+ """
36
+ if not historical_prices or len(historical_prices) < 30:
37
+ return {
38
+ "score": 0,
39
+ "valid_vcp": False,
40
+ "contractions": [],
41
+ "num_contractions": 0,
42
+ "pivot_price": None,
43
+ "error": "Insufficient data (need 30+ days)",
44
+ }
45
+
46
+ # Work in chronological order (oldest first)
47
+ prices = list(reversed(historical_prices[:lookback_days]))
48
+ n = len(prices)
49
+
50
+ if n < 30:
51
+ return {
52
+ "score": 0,
53
+ "valid_vcp": False,
54
+ "contractions": [],
55
+ "num_contractions": 0,
56
+ "pivot_price": None,
57
+ "error": "Insufficient data in lookback window",
58
+ }
59
+
60
+ # Step A: Find swing points
61
+ highs = [d.get("high", d.get("close", 0)) for d in prices]
62
+ lows = [d.get("low", d.get("close", 0)) for d in prices]
63
+ closes = [d.get("close", 0) for d in prices]
64
+ dates = [d.get("date", f"day-{i}") for i, d in enumerate(prices)]
65
+
66
+ swing_highs = _find_swing_highs(highs, window=5)
67
+ swing_lows = _find_swing_lows(lows, window=5)
68
+
69
+ if len(swing_highs) < 1 or len(swing_lows) < 1:
70
+ return {
71
+ "score": 0,
72
+ "valid_vcp": False,
73
+ "contractions": [],
74
+ "num_contractions": 0,
75
+ "pivot_price": None,
76
+ "error": "Insufficient swing points detected",
77
+ }
78
+
79
+ # Step B: Identify contractions
80
+ contractions = _identify_contractions(swing_highs, swing_lows, highs, lows, dates)
81
+
82
+ if len(contractions) < 2:
83
+ return {
84
+ "score": 0,
85
+ "valid_vcp": False,
86
+ "contractions": contractions,
87
+ "num_contractions": len(contractions),
88
+ "pivot_price": _get_pivot_price(contractions, highs, swing_highs),
89
+ "error": "Fewer than 2 contractions found" if len(contractions) < 2 else None,
90
+ }
91
+
92
+ # Step C: Validate VCP
93
+ validation = _validate_vcp(contractions, n)
94
+
95
+ # Pivot price = high of the last contraction
96
+ pivot_price = _get_pivot_price(contractions, highs, swing_highs)
97
+
98
+ # Calculate pattern duration
99
+ if len(contractions) >= 2:
100
+ first_idx = contractions[0]["high_idx"]
101
+ last_low_idx = contractions[-1]["low_idx"]
102
+ pattern_duration = last_low_idx - first_idx
103
+ else:
104
+ pattern_duration = 0
105
+
106
+ # Score the pattern
107
+ score = _score_vcp(contractions, validation)
108
+
109
+ return {
110
+ "score": score,
111
+ "valid_vcp": validation["valid"],
112
+ "contractions": contractions,
113
+ "num_contractions": len(contractions),
114
+ "pivot_price": round(pivot_price, 2) if pivot_price else None,
115
+ "pattern_duration_days": pattern_duration,
116
+ "validation": validation,
117
+ "error": None,
118
+ }
119
+
120
+
121
+ def _find_swing_highs(highs: List[float], window: int = 5) -> List[Tuple[int, float]]:
122
+ """Find swing high points. Returns list of (index, value)."""
123
+ swing_highs = []
124
+ for i in range(window, len(highs) - window):
125
+ is_high = True
126
+ for j in range(1, window + 1):
127
+ if highs[i] <= highs[i - j] or highs[i] <= highs[i + j]:
128
+ is_high = False
129
+ break
130
+ if is_high:
131
+ swing_highs.append((i, highs[i]))
132
+ return swing_highs
133
+
134
+
135
+ def _find_swing_lows(lows: List[float], window: int = 5) -> List[Tuple[int, float]]:
136
+ """Find swing low points. Returns list of (index, value)."""
137
+ swing_lows = []
138
+ for i in range(window, len(lows) - window):
139
+ is_low = True
140
+ for j in range(1, window + 1):
141
+ if lows[i] >= lows[i - j] or lows[i] >= lows[i + j]:
142
+ is_low = False
143
+ break
144
+ if is_low:
145
+ swing_lows.append((i, lows[i]))
146
+ return swing_lows
147
+
148
+
149
+ def _identify_contractions(
150
+ swing_highs: List[Tuple[int, float]],
151
+ swing_lows: List[Tuple[int, float]],
152
+ highs: List[float],
153
+ lows: List[float],
154
+ dates: List[str],
155
+ ) -> List[Dict]:
156
+ """
157
+ Identify successive contractions from swing points.
158
+ Each contraction is defined by a swing high followed by a swing low.
159
+ """
160
+ if not swing_highs:
161
+ return []
162
+
163
+ # Start from the highest swing high in the lookback
164
+ h1_idx, h1_val = max(swing_highs, key=lambda x: x[1])
165
+
166
+ contractions = []
167
+ current_high_idx = h1_idx
168
+ current_high_val = h1_val
169
+
170
+ # Find successive contraction pairs
171
+ for _ in range(4): # Max 4 contractions
172
+ # Find next swing low after current high
173
+ next_low = None
174
+ for idx, val in swing_lows:
175
+ if idx > current_high_idx:
176
+ next_low = (idx, val)
177
+ break
178
+
179
+ if next_low is None:
180
+ break
181
+
182
+ low_idx, low_val = next_low
183
+ depth_pct = (current_high_val - low_val) / current_high_val * 100 if current_high_val > 0 else 0
184
+
185
+ contractions.append({
186
+ "label": f"T{len(contractions) + 1}",
187
+ "high_idx": current_high_idx,
188
+ "high_price": round(current_high_val, 2),
189
+ "high_date": dates[current_high_idx] if current_high_idx < len(dates) else "N/A",
190
+ "low_idx": low_idx,
191
+ "low_price": round(low_val, 2),
192
+ "low_date": dates[low_idx] if low_idx < len(dates) else "N/A",
193
+ "depth_pct": round(depth_pct, 2),
194
+ })
195
+
196
+ # Find next swing high after this low (for the next contraction)
197
+ next_high = None
198
+ for idx, val in swing_highs:
199
+ if idx > low_idx:
200
+ next_high = (idx, val)
201
+ break
202
+
203
+ if next_high is None:
204
+ break
205
+
206
+ current_high_idx, current_high_val = next_high
207
+
208
+ return contractions
209
+
210
+
211
+ def _validate_vcp(contractions: List[Dict], total_days: int) -> Dict:
212
+ """Validate whether the contraction pattern qualifies as a VCP."""
213
+ issues = []
214
+ valid = True
215
+
216
+ if len(contractions) < 2:
217
+ return {"valid": False, "issues": ["Need at least 2 contractions"]}
218
+
219
+ # Check T1 depth (8-35% for large-caps)
220
+ t1_depth = contractions[0]["depth_pct"]
221
+ if t1_depth < 8:
222
+ issues.append(f"T1 depth too shallow ({t1_depth:.1f}%, need >= 8%)")
223
+ valid = False
224
+ elif t1_depth > 35:
225
+ issues.append(f"T1 depth too deep ({t1_depth:.1f}%, prefer <= 35%)")
226
+ # Don't invalidate, just flag
227
+
228
+ # Check contraction tightening (each T should be <= 75% of previous)
229
+ contraction_ratios = []
230
+ for i in range(1, len(contractions)):
231
+ prev_depth = contractions[i - 1]["depth_pct"]
232
+ curr_depth = contractions[i]["depth_pct"]
233
+ if prev_depth > 0:
234
+ ratio = curr_depth / prev_depth
235
+ contraction_ratios.append(ratio)
236
+ if ratio > 0.75:
237
+ issues.append(
238
+ f"{contractions[i]['label']} ({curr_depth:.1f}%) does not contract "
239
+ f"25%+ vs {contractions[i-1]['label']} ({prev_depth:.1f}%), "
240
+ f"ratio={ratio:.2f} (need <= 0.75)"
241
+ )
242
+ valid = False
243
+
244
+ # Check successive highs within 5% of each other
245
+ for i in range(1, len(contractions)):
246
+ prev_high = contractions[i - 1]["high_price"]
247
+ curr_high = contractions[i]["high_price"] if i < len(contractions) else contractions[-1]["high_price"]
248
+ # The high of subsequent contraction should be near the first
249
+ if prev_high > 0:
250
+ pct_diff = abs(curr_high - contractions[0]["high_price"]) / contractions[0]["high_price"] * 100
251
+ if pct_diff > 5:
252
+ issues.append(
253
+ f"{contractions[i]['label']} high ${curr_high:.2f} is "
254
+ f"{pct_diff:.1f}% from H1 ${contractions[0]['high_price']:.2f}"
255
+ )
256
+
257
+ # Pattern duration check (15-325 trading days)
258
+ if len(contractions) >= 2:
259
+ duration = contractions[-1]["low_idx"] - contractions[0]["high_idx"]
260
+ if duration < 15:
261
+ issues.append(f"Pattern too short ({duration} days, need >= 15)")
262
+ valid = False
263
+ elif duration > 325:
264
+ issues.append(f"Pattern too long ({duration} days, prefer <= 325)")
265
+
266
+ return {
267
+ "valid": valid,
268
+ "issues": issues,
269
+ "contraction_ratios": [round(r, 3) for r in contraction_ratios],
270
+ "t1_depth": t1_depth,
271
+ }
272
+
273
+
274
+ def _get_pivot_price(
275
+ contractions: List[Dict],
276
+ highs: List[float],
277
+ swing_highs: List[Tuple[int, float]],
278
+ ) -> Optional[float]:
279
+ """Get the pivot (breakout) price - high of the last contraction."""
280
+ if contractions:
281
+ return contractions[-1]["high_price"]
282
+ elif swing_highs:
283
+ return swing_highs[-1][1]
284
+ return None
285
+
286
+
287
+ def _score_vcp(contractions: List[Dict], validation: Dict) -> int:
288
+ """Score the VCP pattern quality (0-100)."""
289
+ if not validation["valid"]:
290
+ # Even invalid patterns get partial credit for structure
291
+ return min(40, len(contractions) * 15)
292
+
293
+ num = len(contractions)
294
+
295
+ # Base score by contraction count
296
+ if num >= 4:
297
+ base = 90
298
+ elif num >= 3:
299
+ base = 80
300
+ elif num >= 2:
301
+ base = 60
302
+ else:
303
+ return 20
304
+
305
+ score = base
306
+
307
+ # Bonus: tight final contraction (< 5% depth)
308
+ final_depth = contractions[-1]["depth_pct"]
309
+ if final_depth < 5:
310
+ score += 10
311
+
312
+ # Bonus: good contraction ratio (avg < 0.4 of T1)
313
+ ratios = validation.get("contraction_ratios", [])
314
+ if ratios and sum(ratios) / len(ratios) < 0.4:
315
+ score += 10
316
+
317
+ # Penalty: deep T1 (> 30%)
318
+ t1_depth = validation.get("t1_depth", 0)
319
+ if t1_depth > 30:
320
+ score -= 10
321
+
322
+ return max(0, min(100, score))
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Volume Pattern Calculator - Volume Dry-Up Analysis
4
+
5
+ Analyzes volume behavior near the pivot point of a VCP pattern.
6
+ Key principle: Volume should contract (dry up) as the pattern tightens,
7
+ then expand on breakout.
8
+
9
+ Key Metric: Volume dry-up ratio = avg volume (last 10 bars near pivot) / 50-day avg volume
10
+
11
+ Scoring:
12
+ - Dry-up ratio < 0.30: 90 (exceptional volume contraction)
13
+ - 0.30-0.50: 75 (strong dry-up)
14
+ - 0.50-0.70: 60 (moderate dry-up)
15
+ - 0.70-1.00: 40 (weak dry-up)
16
+ - > 1.00: 20 (no dry-up, not ideal)
17
+
18
+ Modifiers:
19
+ - Breakout on 1.5x+ volume: +10
20
+ - Net accumulation > 3 days: +10
21
+ - Net distribution > 3 days: -10
22
+ """
23
+
24
+ from typing import Dict, List, Optional
25
+
26
+
27
+ def calculate_volume_pattern(
28
+ historical_prices: List[Dict],
29
+ pivot_price: Optional[float] = None,
30
+ ) -> Dict:
31
+ """
32
+ Analyze volume behavior near the VCP pivot point.
33
+
34
+ Args:
35
+ historical_prices: Daily OHLCV data (most recent first), need 50+ days
36
+ pivot_price: The pivot (breakout) price level. If None, uses recent high.
37
+
38
+ Returns:
39
+ Dict with score (0-100), dry_up_ratio, volume details
40
+ """
41
+ if not historical_prices or len(historical_prices) < 20:
42
+ return {
43
+ "score": 0,
44
+ "dry_up_ratio": None,
45
+ "error": "Insufficient data (need 20+ days)",
46
+ }
47
+
48
+ volumes = [d.get("volume", 0) for d in historical_prices]
49
+ closes = [d.get("close", d.get("adjClose", 0)) for d in historical_prices]
50
+
51
+ # 50-day average volume (or available)
52
+ vol_period = min(50, len(volumes))
53
+ avg_volume_50d = sum(volumes[:vol_period]) / vol_period if vol_period > 0 else 0
54
+
55
+ if avg_volume_50d <= 0:
56
+ return {
57
+ "score": 0,
58
+ "dry_up_ratio": None,
59
+ "error": "No volume data available",
60
+ }
61
+
62
+ # Recent volume (last 10 bars, representing area near pivot)
63
+ recent_period = min(10, len(volumes))
64
+ avg_volume_recent = sum(volumes[:recent_period]) / recent_period if recent_period > 0 else 0
65
+
66
+ # Volume dry-up ratio
67
+ dry_up_ratio = avg_volume_recent / avg_volume_50d if avg_volume_50d > 0 else 1.0
68
+
69
+ # Base score from dry-up ratio
70
+ if dry_up_ratio < 0.30:
71
+ base_score = 90
72
+ elif dry_up_ratio < 0.50:
73
+ base_score = 75
74
+ elif dry_up_ratio < 0.70:
75
+ base_score = 60
76
+ elif dry_up_ratio <= 1.00:
77
+ base_score = 40
78
+ else:
79
+ base_score = 20
80
+
81
+ score = base_score
82
+
83
+ # Modifier: Check for breakout volume (most recent day)
84
+ breakout_volume = False
85
+ if len(volumes) >= 2 and volumes[0] > avg_volume_50d * 1.5:
86
+ current_price = closes[0] if closes else 0
87
+ if pivot_price and current_price > pivot_price:
88
+ breakout_volume = True
89
+ score += 10
90
+
91
+ # Modifier: Net accumulation/distribution in last 20 days
92
+ # Count up-volume vs down-volume days
93
+ up_vol_days = 0
94
+ down_vol_days = 0
95
+ analysis_period = min(20, len(closes) - 1)
96
+
97
+ for i in range(analysis_period):
98
+ if i + 1 < len(closes) and closes[i] > closes[i + 1]:
99
+ up_vol_days += 1
100
+ elif i + 1 < len(closes) and closes[i] < closes[i + 1]:
101
+ down_vol_days += 1
102
+
103
+ net_accumulation = up_vol_days - down_vol_days
104
+ if net_accumulation > 3:
105
+ score += 10
106
+ elif net_accumulation < -3:
107
+ score -= 10
108
+
109
+ score = max(0, min(100, score))
110
+
111
+ return {
112
+ "score": score,
113
+ "dry_up_ratio": round(dry_up_ratio, 3),
114
+ "avg_volume_50d": int(avg_volume_50d),
115
+ "avg_volume_recent_10d": int(avg_volume_recent),
116
+ "breakout_volume_detected": breakout_volume,
117
+ "up_volume_days_20d": up_vol_days,
118
+ "down_volume_days_20d": down_vol_days,
119
+ "net_accumulation": net_accumulation,
120
+ "error": None,
121
+ }
@@ -0,0 +1,162 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ FMP API Client for VCP Screener
4
+
5
+ Provides rate-limited access to Financial Modeling Prep API endpoints
6
+ for VCP (Volatility Contraction Pattern) screening.
7
+
8
+ Features:
9
+ - Rate limiting (0.3s between requests)
10
+ - Automatic retry on 429 errors
11
+ - Session caching for duplicate requests
12
+ - Batch quote support
13
+ - S&P 500 constituents fetching
14
+ """
15
+
16
+ import os
17
+ import sys
18
+ import time
19
+ from typing import Dict, List, Optional
20
+
21
+ try:
22
+ import requests
23
+ except ImportError:
24
+ print("ERROR: requests library not found. Install with: pip install requests", file=sys.stderr)
25
+ sys.exit(1)
26
+
27
+
28
+ class FMPClient:
29
+ """Client for Financial Modeling Prep API with rate limiting and caching"""
30
+
31
+ BASE_URL = "https://financialmodelingprep.com/api/v3"
32
+ RATE_LIMIT_DELAY = 0.3 # 300ms between requests
33
+
34
+ def __init__(self, api_key: Optional[str] = None):
35
+ self.api_key = api_key or os.getenv("FMP_API_KEY")
36
+ if not self.api_key:
37
+ raise ValueError(
38
+ "FMP API key required. Set FMP_API_KEY environment variable "
39
+ "or pass api_key parameter."
40
+ )
41
+ self.session = requests.Session()
42
+ self.cache = {}
43
+ self.last_call_time = 0
44
+ self.rate_limit_reached = False
45
+ self.retry_count = 0
46
+ self.max_retries = 1
47
+ self.api_calls_made = 0
48
+
49
+ def _rate_limited_get(self, url: str, params: Optional[Dict] = None) -> Optional[Dict]:
50
+ if self.rate_limit_reached:
51
+ return None
52
+
53
+ if params is None:
54
+ params = {}
55
+ params['apikey'] = self.api_key
56
+
57
+ elapsed = time.time() - self.last_call_time
58
+ if elapsed < self.RATE_LIMIT_DELAY:
59
+ time.sleep(self.RATE_LIMIT_DELAY - elapsed)
60
+
61
+ try:
62
+ response = self.session.get(url, params=params, timeout=30)
63
+ self.last_call_time = time.time()
64
+ self.api_calls_made += 1
65
+
66
+ if response.status_code == 200:
67
+ self.retry_count = 0
68
+ return response.json()
69
+ elif response.status_code == 429:
70
+ self.retry_count += 1
71
+ if self.retry_count <= self.max_retries:
72
+ print("WARNING: Rate limit exceeded. Waiting 60 seconds...", file=sys.stderr)
73
+ time.sleep(60)
74
+ return self._rate_limited_get(url, params)
75
+ else:
76
+ print("ERROR: Daily API rate limit reached.", file=sys.stderr)
77
+ self.rate_limit_reached = True
78
+ return None
79
+ else:
80
+ print(f"ERROR: API request failed: {response.status_code} - {response.text[:200]}",
81
+ file=sys.stderr)
82
+ return None
83
+ except requests.exceptions.RequestException as e:
84
+ print(f"ERROR: Request exception: {e}", file=sys.stderr)
85
+ return None
86
+
87
+ def get_sp500_constituents(self) -> Optional[List[Dict]]:
88
+ """Fetch S&P 500 constituent list.
89
+
90
+ Returns:
91
+ List of dicts with keys: symbol, name, sector, subSector
92
+ or None on failure.
93
+ """
94
+ cache_key = "sp500_constituents"
95
+ if cache_key in self.cache:
96
+ return self.cache[cache_key]
97
+
98
+ url = f"{self.BASE_URL}/sp500_constituent"
99
+ data = self._rate_limited_get(url)
100
+ if data:
101
+ self.cache[cache_key] = data
102
+ return data
103
+
104
+ def get_quote(self, symbols: str) -> Optional[List[Dict]]:
105
+ """Fetch real-time quote data for one or more symbols (comma-separated)"""
106
+ cache_key = f"quote_{symbols}"
107
+ if cache_key in self.cache:
108
+ return self.cache[cache_key]
109
+
110
+ url = f"{self.BASE_URL}/quote/{symbols}"
111
+ data = self._rate_limited_get(url)
112
+ if data:
113
+ self.cache[cache_key] = data
114
+ return data
115
+
116
+ def get_historical_prices(self, symbol: str, days: int = 365) -> Optional[Dict]:
117
+ """Fetch historical daily OHLCV data"""
118
+ cache_key = f"prices_{symbol}_{days}"
119
+ if cache_key in self.cache:
120
+ return self.cache[cache_key]
121
+
122
+ url = f"{self.BASE_URL}/historical-price-full/{symbol}"
123
+ params = {"timeseries": days}
124
+ data = self._rate_limited_get(url, params)
125
+ if data:
126
+ self.cache[cache_key] = data
127
+ return data
128
+
129
+ def get_batch_quotes(self, symbols: List[str]) -> Dict[str, Dict]:
130
+ """Fetch quotes for a list of symbols, batching up to 5 per request"""
131
+ results = {}
132
+ batch_size = 5
133
+ for i in range(0, len(symbols), batch_size):
134
+ batch = symbols[i:i+batch_size]
135
+ batch_str = ",".join(batch)
136
+ quotes = self.get_quote(batch_str)
137
+ if quotes:
138
+ for q in quotes:
139
+ results[q['symbol']] = q
140
+ return results
141
+
142
+ def get_batch_historical(self, symbols: List[str], days: int = 260) -> Dict[str, List[Dict]]:
143
+ """Fetch historical prices for multiple symbols"""
144
+ results = {}
145
+ for symbol in symbols:
146
+ data = self.get_historical_prices(symbol, days=days)
147
+ if data and 'historical' in data:
148
+ results[symbol] = data['historical']
149
+ return results
150
+
151
+ def calculate_sma(self, prices: List[float], period: int) -> float:
152
+ """Calculate Simple Moving Average from a list of prices (most recent first)"""
153
+ if len(prices) < period:
154
+ return sum(prices) / len(prices)
155
+ return sum(prices[:period]) / period
156
+
157
+ def get_api_stats(self) -> Dict:
158
+ return {
159
+ "cache_entries": len(self.cache),
160
+ "api_calls_made": self.api_calls_made,
161
+ "rate_limit_reached": self.rate_limit_reached,
162
+ }