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,185 @@
1
+ # Post-FTD Monitoring Guide
2
+
3
+ ## Overview
4
+
5
+ A confirmed Follow-Through Day (FTD) shifts the probability toward a new uptrend, but approximately 75% of FTDs ultimately fail. Post-FTD monitoring is essential for:
6
+ 1. Confirming the signal is working (exposure increase)
7
+ 2. Detecting early failure (exposure reduction)
8
+ 3. Identifying Power Trend confirmation (maximum conviction)
9
+
10
+ ---
11
+
12
+ ## Distribution Day Monitoring After FTD
13
+
14
+ ### What Is a Post-FTD Distribution Day?
15
+ A distribution day after an FTD is defined as:
16
+ - Index declines >= 0.2% from the prior day's close
17
+ - Volume is higher than the previous day's volume
18
+ - Occurs within the first 5 trading days after the FTD
19
+
20
+ ### Failure Rate by Distribution Timing
21
+
22
+ | Distribution Timing | Failure Rate | Quality Score Impact | Action |
23
+ |--------------------|-------------|---------------------|--------|
24
+ | **Day 1 after FTD** | ~85% fail | -30 points | Immediately reduce exposure |
25
+ | **Day 2 after FTD** | ~80% fail | -30 points | Reduce to defensive levels |
26
+ | **Day 3 after FTD** | ~65% fail | -15 points | Tighten stops significantly |
27
+ | **Day 4 after FTD** | ~50% fail | -5 points | Moderate caution |
28
+ | **Day 5 after FTD** | ~45% fail | -5 points | Normal monitoring |
29
+ | **No distribution (5 days)** | ~35% fail | +10 points | Increase conviction |
30
+
31
+ **Key Insight:** The earlier distribution appears after an FTD, the more likely the FTD will fail. Distribution within the first 2 days is a near-certain failure signal.
32
+
33
+ ### Multiple Distribution Days
34
+ - 2+ distribution days within 5 days of FTD: ~90% failure rate
35
+ - Even if individual days are in the "moderate" timing zone (Day 4-5), accumulation of distribution is bearish
36
+
37
+ ---
38
+
39
+ ## FTD Invalidation
40
+
41
+ ### Invalidation Criteria
42
+ An FTD is formally invalidated when:
43
+ - **Index closes below the FTD day's intraday low**
44
+ - This is a hard stop - the FTD signal is no longer valid
45
+
46
+ ### What to Do After Invalidation
47
+ 1. Reduce equity exposure to defensive levels (0-25%)
48
+ 2. Do NOT try to average down or hold through
49
+ 3. Wait for a new swing low and fresh rally attempt
50
+ 4. The previous FTD failure provides no information about the next attempt
51
+
52
+ ### Soft Warnings (Not Yet Invalidated)
53
+ - Close approaches but doesn't breach FTD low: heightened caution
54
+ - Intraday breach but close above: technically valid but weak
55
+ - Slow grinding decline toward FTD low: consider preemptive reduction
56
+
57
+ ---
58
+
59
+ ## Power Trend Confirmation
60
+
61
+ ### Definition
62
+ A Power Trend is the strongest bullish condition in O'Neil's framework. It occurs when three conditions are simultaneously true:
63
+
64
+ 1. **21-day EMA > 50-day SMA** (short-term momentum above medium-term trend)
65
+ 2. **50-day SMA slope is positive** (rising over the last 5 trading days)
66
+ 3. **Price above 21-day EMA** (current price confirming the trend)
67
+
68
+ ### Significance
69
+ - Power Trend + FTD = highest conviction bottom signal
70
+ - Historically, markets in Power Trend have very low probability of immediate failure
71
+ - Power Trend typically develops 2-4 weeks after a successful FTD
72
+ - Not required for FTD validity, but serves as strong confirmation
73
+
74
+ ### Power Trend Conditions Breakdown
75
+
76
+ | Conditions Met | Interpretation |
77
+ |---------------|---------------|
78
+ | 3/3 | Full Power Trend - maximum conviction |
79
+ | 2/3 | Developing trend - monitor for completion |
80
+ | 1/3 | No Power Trend - rely on other signals |
81
+ | 0/3 | Bearish structure - be cautious despite FTD |
82
+
83
+ ---
84
+
85
+ ## FTD Success vs Failure Patterns
86
+
87
+ ### Characteristics of Successful FTDs
88
+
89
+ | Factor | Successful Pattern |
90
+ |--------|--------------------|
91
+ | **Day Timing** | Day 4-7 (prime window) |
92
+ | **Gain** | 2.0%+ on heavy volume |
93
+ | **Volume** | Above 50-day average |
94
+ | **Dual Index** | Both S&P 500 and NASDAQ confirm |
95
+ | **Post-FTD** | Clean first 3-5 days (no distribution) |
96
+ | **Leading Stocks** | Many breakouts from proper bases |
97
+ | **Sector Breadth** | Multiple sectors participating |
98
+ | **Catalyst** | Identifiable positive catalyst (Fed pivot, earnings surprise) |
99
+ | **Power Trend** | Develops within 2-4 weeks |
100
+
101
+ ### Characteristics of Failed FTDs
102
+
103
+ | Factor | Failure Pattern |
104
+ |--------|--------------------|
105
+ | **Day Timing** | Day 8-10 (late window) |
106
+ | **Gain** | Minimum qualifying (1.25-1.49%) |
107
+ | **Volume** | Below 50-day average |
108
+ | **Dual Index** | Only one index confirms |
109
+ | **Post-FTD** | Distribution within first 2 days |
110
+ | **Leading Stocks** | Few/no quality breakouts |
111
+ | **Sector Breadth** | Narrow participation (1-2 sectors) |
112
+ | **Catalyst** | No clear catalyst, or hostile macro backdrop |
113
+ | **Power Trend** | Never develops, 50 SMA continues declining |
114
+
115
+ ---
116
+
117
+ ## Exposure Management After FTD
118
+
119
+ ### Graduated Exposure Model
120
+
121
+ The O'Neil approach uses progressive exposure increase, not all-at-once buying:
122
+
123
+ **Phase 1: Initial (FTD Day)**
124
+ - Start at 25% of target exposure
125
+ - Buy 1-2 leading stocks breaking out of bases
126
+ - Use FTD day's low as initial stop reference
127
+
128
+ **Phase 2: Confirmation (Days 1-5 post-FTD)**
129
+ - If no distribution: increase to 50% exposure
130
+ - Add positions in additional leaders
131
+ - Tighten stops on initial positions to breakeven
132
+
133
+ **Phase 3: Acceleration (Days 5-15 post-FTD)**
134
+ - If trend confirms (clean action, breakouts working): increase to 75%
135
+ - Pyramid into winning positions
136
+ - Look for Power Trend development
137
+
138
+ **Phase 4: Full Exposure (2-4 weeks post-FTD)**
139
+ - If Power Trend develops: full 100% exposure
140
+ - Focus on strongest leaders
141
+ - Normal stop-loss management
142
+
143
+ ### Exposure Reduction Triggers
144
+
145
+ | Trigger | Action |
146
+ |---------|--------|
147
+ | Distribution Day 1-2 post-FTD | Cut to 0-25% |
148
+ | Distribution Day 3 post-FTD | Cut to 25-50% |
149
+ | FTD invalidated | Cut to 0-25% |
150
+ | Breakouts failing (stocks reversing after breakout) | Reduce by 25% |
151
+ | No quality setups forming | Don't force increase |
152
+
153
+ ---
154
+
155
+ ## Interaction with Market Top Detector
156
+
157
+ The FTD Detector and Market Top Detector are complementary:
158
+
159
+ ### During Correction (Top Detector score 60+):
160
+ 1. Top Detector signals defensive posture
161
+ 2. FTD Detector watches for bottom signals
162
+ 3. When FTD confirms, begin transitioning from defensive to offensive
163
+
164
+ ### During FTD Confirmed:
165
+ 1. FTD Detector guides exposure increase
166
+ 2. Top Detector should show declining score (improving conditions)
167
+ 3. If Top Detector score remains high despite FTD, exercise extra caution
168
+
169
+ ### Signal Conflict Resolution:
170
+ - FTD confirmed but Top Detector still 60+: proceed with caution, use smaller position sizes
171
+ - FTD confirmed and Top Detector below 40: higher conviction signal
172
+ - FTD invalidated: defer to Top Detector for defensive guidance
173
+
174
+ ---
175
+
176
+ ## Historical Success Rate Context
177
+
178
+ Based on IBD historical analysis of FTDs since 1900:
179
+
180
+ - **Overall FTD success rate:** ~25% (1 in 4 leads to sustained uptrend)
181
+ - **FTDs with quality score 80+:** ~45-50% success rate
182
+ - **FTDs with quality score 60-79:** ~30-35% success rate
183
+ - **FTDs with quality score below 60:** ~10-15% success rate
184
+
185
+ The quality scoring system effectively filters the ~75% failure rate down to a more manageable ~50-55% for high-quality signals. Combined with proper stop-loss management, this creates a positive expected value system despite the sub-50% win rate, because winners significantly outperform losers when properly managed.
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ FMP API Client for Market Top Detector
4
+
5
+ Provides rate-limited access to Financial Modeling Prep API endpoints
6
+ for market top detection analysis.
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 for ETF baskets
13
+ """
14
+
15
+ import os
16
+ import sys
17
+ import time
18
+ from typing import Dict, List, Optional
19
+
20
+ try:
21
+ import requests
22
+ except ImportError:
23
+ print("ERROR: requests library not found. Install with: pip install requests", file=sys.stderr)
24
+ sys.exit(1)
25
+
26
+
27
+ class FMPClient:
28
+ """Client for Financial Modeling Prep API with rate limiting and caching"""
29
+
30
+ BASE_URL = "https://financialmodelingprep.com/api/v3"
31
+ RATE_LIMIT_DELAY = 0.3 # 300ms between requests
32
+
33
+ def __init__(self, api_key: Optional[str] = None):
34
+ self.api_key = api_key or os.getenv("FMP_API_KEY")
35
+ if not self.api_key:
36
+ raise ValueError(
37
+ "FMP API key required. Set FMP_API_KEY environment variable "
38
+ "or pass api_key parameter."
39
+ )
40
+ self.session = requests.Session()
41
+ self.cache = {}
42
+ self.last_call_time = 0
43
+ self.rate_limit_reached = False
44
+ self.retry_count = 0
45
+ self.max_retries = 1
46
+ self.api_calls_made = 0
47
+
48
+ def _rate_limited_get(self, url: str, params: Optional[Dict] = None) -> Optional[Dict]:
49
+ if self.rate_limit_reached:
50
+ return None
51
+
52
+ if params is None:
53
+ params = {}
54
+ params['apikey'] = self.api_key
55
+
56
+ elapsed = time.time() - self.last_call_time
57
+ if elapsed < self.RATE_LIMIT_DELAY:
58
+ time.sleep(self.RATE_LIMIT_DELAY - elapsed)
59
+
60
+ try:
61
+ response = self.session.get(url, params=params, timeout=30)
62
+ self.last_call_time = time.time()
63
+ self.api_calls_made += 1
64
+
65
+ if response.status_code == 200:
66
+ self.retry_count = 0
67
+ return response.json()
68
+ elif response.status_code == 429:
69
+ self.retry_count += 1
70
+ if self.retry_count <= self.max_retries:
71
+ print("WARNING: Rate limit exceeded. Waiting 60 seconds...", file=sys.stderr)
72
+ time.sleep(60)
73
+ return self._rate_limited_get(url, params)
74
+ else:
75
+ print("ERROR: Daily API rate limit reached.", file=sys.stderr)
76
+ self.rate_limit_reached = True
77
+ return None
78
+ else:
79
+ print(f"ERROR: API request failed: {response.status_code} - {response.text[:200]}",
80
+ file=sys.stderr)
81
+ return None
82
+ except requests.exceptions.RequestException as e:
83
+ print(f"ERROR: Request exception: {e}", file=sys.stderr)
84
+ return None
85
+
86
+ def get_quote(self, symbols: str) -> Optional[List[Dict]]:
87
+ """Fetch real-time quote data for one or more symbols (comma-separated)"""
88
+ cache_key = f"quote_{symbols}"
89
+ if cache_key in self.cache:
90
+ return self.cache[cache_key]
91
+
92
+ url = f"{self.BASE_URL}/quote/{symbols}"
93
+ data = self._rate_limited_get(url)
94
+ if data:
95
+ self.cache[cache_key] = data
96
+ return data
97
+
98
+ def get_historical_prices(self, symbol: str, days: int = 365) -> Optional[Dict]:
99
+ """Fetch historical daily OHLCV data"""
100
+ cache_key = f"prices_{symbol}_{days}"
101
+ if cache_key in self.cache:
102
+ return self.cache[cache_key]
103
+
104
+ url = f"{self.BASE_URL}/historical-price-full/{symbol}"
105
+ params = {"timeseries": days}
106
+ data = self._rate_limited_get(url, params)
107
+ if data:
108
+ self.cache[cache_key] = data
109
+ return data
110
+
111
+ def get_batch_quotes(self, symbols: List[str]) -> Dict[str, Dict]:
112
+ """Fetch quotes for a list of symbols, batching up to 5 per request"""
113
+ results = {}
114
+ # FMP supports comma-separated symbols in quote endpoint
115
+ batch_size = 5
116
+ for i in range(0, len(symbols), batch_size):
117
+ batch = symbols[i:i+batch_size]
118
+ batch_str = ",".join(batch)
119
+ quotes = self.get_quote(batch_str)
120
+ if quotes:
121
+ for q in quotes:
122
+ results[q['symbol']] = q
123
+ return results
124
+
125
+ def get_batch_historical(self, symbols: List[str], days: int = 50) -> Dict[str, List[Dict]]:
126
+ """Fetch historical prices for multiple symbols"""
127
+ results = {}
128
+ for symbol in symbols:
129
+ data = self.get_historical_prices(symbol, days=days)
130
+ if data and 'historical' in data:
131
+ results[symbol] = data['historical']
132
+ return results
133
+
134
+ def calculate_ema(self, prices: List[float], period: int) -> float:
135
+ """Calculate Exponential Moving Average from a list of prices (most recent first)"""
136
+ if len(prices) < period:
137
+ return sum(prices) / len(prices)
138
+
139
+ prices_reversed = prices[::-1]
140
+ sma = sum(prices_reversed[:period]) / period
141
+ ema = sma
142
+ k = 2 / (period + 1)
143
+ for price in prices_reversed[period:]:
144
+ ema = price * k + ema * (1 - k)
145
+ return ema
146
+
147
+ def calculate_sma(self, prices: List[float], period: int) -> float:
148
+ """Calculate Simple Moving Average from a list of prices (most recent first)"""
149
+ if len(prices) < period:
150
+ return sum(prices) / len(prices)
151
+ return sum(prices[:period]) / period
152
+
153
+ def get_api_stats(self) -> Dict:
154
+ return {
155
+ "cache_entries": len(self.cache),
156
+ "api_calls_made": self.api_calls_made,
157
+ "rate_limit_reached": self.rate_limit_reached,
158
+ }
@@ -0,0 +1,280 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ FTD Detector - Main Orchestrator
4
+
5
+ Detects Follow-Through Day (FTD) signals for market bottom confirmation
6
+ using William O'Neil's methodology with dual-index tracking.
7
+
8
+ Usage:
9
+ python3 ftd_detector.py --api-key YOUR_KEY
10
+ python3 ftd_detector.py # uses FMP_API_KEY env var
11
+
12
+ Output:
13
+ - JSON: ftd_detector_YYYY-MM-DD_HHMMSS.json
14
+ - Markdown: ftd_detector_YYYY-MM-DD_HHMMSS.md
15
+ """
16
+
17
+ import argparse
18
+ import os
19
+ import sys
20
+ from datetime import datetime
21
+ from typing import Dict
22
+
23
+ # Add parent directory to path for imports
24
+ sys.path.insert(0, os.path.dirname(__file__))
25
+
26
+ from fmp_client import FMPClient
27
+ from rally_tracker import get_market_state
28
+ from post_ftd_monitor import assess_post_ftd_health
29
+ from report_generator import generate_json_report, generate_markdown_report
30
+
31
+
32
+ def parse_arguments():
33
+ parser = argparse.ArgumentParser(
34
+ description="FTD Detector - Follow-Through Day Bottom Confirmation"
35
+ )
36
+ parser.add_argument(
37
+ "--api-key",
38
+ help="FMP API key (defaults to FMP_API_KEY environment variable)"
39
+ )
40
+ parser.add_argument(
41
+ "--output-dir", default=".",
42
+ help="Output directory for reports (default: current directory)"
43
+ )
44
+ return parser.parse_args()
45
+
46
+
47
+ def main():
48
+ args = parse_arguments()
49
+
50
+ print("=" * 70)
51
+ print("FTD Detector - Follow-Through Day Bottom Confirmation")
52
+ print("O'Neil Rally Attempt + FTD State Machine (Dual Index)")
53
+ print("=" * 70)
54
+ print()
55
+
56
+ # Initialize FMP client
57
+ try:
58
+ client = FMPClient(api_key=args.api_key)
59
+ print("FMP API client initialized")
60
+ except ValueError as e:
61
+ print(f"ERROR: {e}", file=sys.stderr)
62
+ sys.exit(1)
63
+
64
+ # ========================================================================
65
+ # Step 1: Fetch Market Data (4 API calls)
66
+ # ========================================================================
67
+ print()
68
+ print("Step 1: Fetching Market Data")
69
+ print("-" * 70)
70
+
71
+ # S&P 500 history (60 trading days)
72
+ print(" Fetching S&P 500 history...", end=" ", flush=True)
73
+ sp500_history_data = client.get_historical_prices("^GSPC", days=80)
74
+ sp500_history = sp500_history_data.get("historical", []) if sp500_history_data else []
75
+ if sp500_history:
76
+ print(f"OK ({len(sp500_history)} days)")
77
+ else:
78
+ print("FAILED")
79
+ print("ERROR: Cannot proceed without S&P 500 data", file=sys.stderr)
80
+ sys.exit(1)
81
+
82
+ # NASDAQ/QQQ history (60 trading days)
83
+ print(" Fetching NASDAQ (QQQ) history...", end=" ", flush=True)
84
+ qqq_history_data = client.get_historical_prices("QQQ", days=80)
85
+ qqq_history = qqq_history_data.get("historical", []) if qqq_history_data else []
86
+ if qqq_history:
87
+ print(f"OK ({len(qqq_history)} days)")
88
+ else:
89
+ print("WARN - NASDAQ data unavailable, using S&P 500 only")
90
+
91
+ # S&P 500 quote (for current price)
92
+ print(" Fetching S&P 500 quote...", end=" ", flush=True)
93
+ sp500_quote_list = client.get_quote("^GSPC")
94
+ sp500_quote = sp500_quote_list[0] if sp500_quote_list else None
95
+ if sp500_quote:
96
+ print(f"OK (${sp500_quote.get('price', 0):.2f})")
97
+ else:
98
+ print("WARN - Using historical close as current price")
99
+
100
+ # QQQ quote
101
+ print(" Fetching QQQ quote...", end=" ", flush=True)
102
+ qqq_quote_list = client.get_quote("QQQ")
103
+ qqq_quote = qqq_quote_list[0] if qqq_quote_list else None
104
+ if qqq_quote:
105
+ print(f"OK (${qqq_quote.get('price', 0):.2f})")
106
+ else:
107
+ print("WARN - Using historical close as current price")
108
+
109
+ print()
110
+
111
+ # ========================================================================
112
+ # Step 2: Run State Machine (Rally Tracker)
113
+ # ========================================================================
114
+ print("Step 2: Analyzing Market State")
115
+ print("-" * 70)
116
+
117
+ market_state = get_market_state(sp500_history, qqq_history)
118
+
119
+ sp500_state = market_state["sp500"]["state"]
120
+ nasdaq_state = market_state["nasdaq"]["state"]
121
+ combined = market_state["combined_state"]
122
+
123
+ print(f" S&P 500 State: {sp500_state}")
124
+ print(f" NASDAQ State: {nasdaq_state}")
125
+ print(f" Combined: {combined}")
126
+
127
+ # Print swing low info if found
128
+ for label, idx_data in [("S&P 500", market_state["sp500"]),
129
+ ("NASDAQ", market_state["nasdaq"])]:
130
+ swing = idx_data.get("swing_low")
131
+ if swing:
132
+ print(f" {label} Swing Low: {swing['swing_low_date']} "
133
+ f"(${swing['swing_low_price']:.2f}, "
134
+ f"{swing['decline_pct']:.1f}% decline)")
135
+ rally = idx_data.get("rally_attempt")
136
+ if rally and rally.get("day1_date"):
137
+ print(f" {label} Rally Day 1: {rally['day1_date']} "
138
+ f"(Day {rally['current_day_count']})")
139
+
140
+ print()
141
+
142
+ # ========================================================================
143
+ # Step 3: Post-FTD Health Assessment
144
+ # ========================================================================
145
+ print("Step 3: Post-FTD Health Assessment")
146
+ print("-" * 70)
147
+
148
+ # Convert to chronological for post-FTD analysis
149
+ sp500_chrono = list(reversed(sp500_history))
150
+ nasdaq_chrono = list(reversed(qqq_history)) if qqq_history else []
151
+
152
+ market_state = assess_post_ftd_health(market_state, sp500_chrono, nasdaq_chrono)
153
+
154
+ quality = market_state.get("quality_score", {})
155
+ print(f" Quality Score: {quality.get('total_score', 0)}/100")
156
+ print(f" Signal: {quality.get('signal', 'N/A')}")
157
+ print(f" Guidance: {quality.get('guidance', 'N/A')}")
158
+ print(f" Exposure Range: {quality.get('exposure_range', 'N/A')}")
159
+
160
+ # Power trend
161
+ pt = market_state.get("power_trend", {})
162
+ if pt:
163
+ print(f" Power Trend: {'YES' if pt.get('power_trend') else 'No'} "
164
+ f"({pt.get('conditions_met', 0)}/3 conditions)")
165
+
166
+ # Post-FTD distribution
167
+ dist = market_state.get("post_ftd_distribution", {})
168
+ if dist:
169
+ print(f" Post-FTD Distribution Days: {dist.get('distribution_count', 0)} "
170
+ f"(monitored {dist.get('days_monitored', 0)} days)")
171
+
172
+ # Invalidation
173
+ inv = market_state.get("ftd_invalidation", {})
174
+ if inv and inv.get("invalidated"):
175
+ print(f" FTD INVALIDATED on {inv.get('invalidation_date')} "
176
+ f"({inv.get('days_after_ftd')} days after FTD)")
177
+
178
+ print()
179
+
180
+ # ========================================================================
181
+ # Step 4: Generate Reports
182
+ # ========================================================================
183
+ print("Step 4: Generating Reports")
184
+ print("-" * 70)
185
+
186
+ analysis = {
187
+ "metadata": {
188
+ "generated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
189
+ "api_calls": client.get_api_stats(),
190
+ "index_prices": {
191
+ "sp500": sp500_quote.get("price", 0) if sp500_quote else (
192
+ sp500_history[0].get("close", 0) if sp500_history else None
193
+ ),
194
+ "qqq": qqq_quote.get("price", 0) if qqq_quote else (
195
+ qqq_history[0].get("close", 0) if qqq_history else None
196
+ ),
197
+ },
198
+ },
199
+ "market_state": {
200
+ "combined_state": market_state["combined_state"],
201
+ "dual_confirmation": market_state["dual_confirmation"],
202
+ "ftd_index": market_state.get("ftd_index"),
203
+ },
204
+ "sp500": _serialize_index(market_state["sp500"]),
205
+ "nasdaq": _serialize_index(market_state["nasdaq"]),
206
+ "quality_score": quality,
207
+ "post_ftd_distribution": market_state.get("post_ftd_distribution", {}),
208
+ "ftd_invalidation": market_state.get("ftd_invalidation", {}),
209
+ "power_trend": market_state.get("power_trend", {}),
210
+ }
211
+
212
+ timestamp = datetime.now().strftime("%Y-%m-%d_%H%M%S")
213
+ json_file = os.path.join(args.output_dir, f"ftd_detector_{timestamp}.json")
214
+ md_file = os.path.join(args.output_dir, f"ftd_detector_{timestamp}.md")
215
+
216
+ generate_json_report(analysis, json_file)
217
+ generate_markdown_report(analysis, md_file)
218
+
219
+ print()
220
+ print("=" * 70)
221
+ print("FTD Detection Complete")
222
+ print("=" * 70)
223
+ print(f" Combined State: {market_state['combined_state']}")
224
+ print(f" Quality Score: {quality.get('total_score', 0)}/100 ({quality.get('signal', 'N/A')})")
225
+ print(f" JSON Report: {json_file}")
226
+ print(f" Markdown Report: {md_file}")
227
+ print()
228
+
229
+ stats = client.get_api_stats()
230
+ print(f"API Usage:")
231
+ print(f" API calls made: {stats['api_calls_made']}")
232
+ print(f" Cache entries: {stats['cache_entries']}")
233
+ print()
234
+
235
+
236
+ def _serialize_index(idx_data: Dict) -> Dict:
237
+ """Serialize index analysis for JSON output, removing large rally_days lists."""
238
+ result = {
239
+ "state": idx_data.get("state"),
240
+ "current_price": idx_data.get("current_price"),
241
+ "lookback_high": idx_data.get("lookback_high"),
242
+ "correction_depth_pct": idx_data.get("correction_depth_pct"),
243
+ }
244
+
245
+ swing = idx_data.get("swing_low")
246
+ if swing:
247
+ result["swing_low"] = {
248
+ "date": swing.get("swing_low_date"),
249
+ "price": swing.get("swing_low_price"),
250
+ "decline_pct": swing.get("decline_pct"),
251
+ "down_days": swing.get("down_days"),
252
+ "recent_high_date": swing.get("recent_high_date"),
253
+ "recent_high_price": swing.get("recent_high_price"),
254
+ }
255
+
256
+ rally = idx_data.get("rally_attempt")
257
+ if rally:
258
+ result["rally_attempt"] = {
259
+ "day1_date": rally.get("day1_date"),
260
+ "current_day_count": rally.get("current_day_count"),
261
+ "invalidated": rally.get("invalidated"),
262
+ "invalidation_reason": rally.get("invalidation_reason"),
263
+ }
264
+
265
+ ftd = idx_data.get("ftd")
266
+ if ftd:
267
+ result["ftd"] = {
268
+ "ftd_detected": ftd.get("ftd_detected"),
269
+ "ftd_date": ftd.get("ftd_date"),
270
+ "ftd_day_number": ftd.get("ftd_day_number"),
271
+ "gain_pct": ftd.get("gain_pct"),
272
+ "gain_tier": ftd.get("gain_tier"),
273
+ "volume_above_avg": ftd.get("volume_above_avg"),
274
+ }
275
+
276
+ return result
277
+
278
+
279
+ if __name__ == "__main__":
280
+ main()