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.
- package/.claude/skills/README.md +80 -0
- package/.claude/skills/backtest-expert/SKILL.md +206 -0
- package/.claude/skills/backtest-expert/references/failed_tests.md +236 -0
- package/.claude/skills/backtest-expert/references/methodology.md +227 -0
- package/.claude/skills/breadth-chart-analyst/SKILL.md +583 -0
- package/.claude/skills/breadth-chart-analyst/assets/SP500_Breadth_Index_200MA_8MA.jpeg +0 -0
- package/.claude/skills/breadth-chart-analyst/assets/US_Stock_Market_Uptrend_Ratio.jpeg +0 -0
- package/.claude/skills/breadth-chart-analyst/assets/breadth_analysis_template.md +558 -0
- package/.claude/skills/breadth-chart-analyst/references/breadth_chart_methodology.md +590 -0
- package/.claude/skills/canslim-screener/SKILL.md +599 -0
- package/.claude/skills/canslim-screener/references/canslim_methodology.md +606 -0
- package/.claude/skills/canslim-screener/references/fmp_api_endpoints.md +707 -0
- package/.claude/skills/canslim-screener/references/interpretation_guide.md +516 -0
- package/.claude/skills/canslim-screener/references/scoring_system.md +597 -0
- package/.claude/skills/canslim-screener/scripts/calculators/earnings_calculator.py +343 -0
- package/.claude/skills/canslim-screener/scripts/calculators/growth_calculator.py +334 -0
- package/.claude/skills/canslim-screener/scripts/calculators/institutional_calculator.py +347 -0
- package/.claude/skills/canslim-screener/scripts/calculators/leadership_calculator.py +380 -0
- package/.claude/skills/canslim-screener/scripts/calculators/market_calculator.py +244 -0
- package/.claude/skills/canslim-screener/scripts/calculators/new_highs_calculator.py +194 -0
- package/.claude/skills/canslim-screener/scripts/calculators/supply_demand_calculator.py +221 -0
- package/.claude/skills/canslim-screener/scripts/finviz_stock_client.py +227 -0
- package/.claude/skills/canslim-screener/scripts/fmp_client.py +393 -0
- package/.claude/skills/canslim-screener/scripts/report_generator.py +405 -0
- package/.claude/skills/canslim-screener/scripts/scorer.py +625 -0
- package/.claude/skills/canslim-screener/scripts/screen_canslim.py +361 -0
- package/.claude/skills/canslim-screener/scripts/test_institutional_endpoint.py +109 -0
- package/.claude/skills/chart/SKILL.md +20 -0
- package/.claude/skills/dividend-growth-pullback-screener/SKILL.md +322 -0
- package/.claude/skills/dividend-growth-pullback-screener/references/dividend_growth_compounding.md +400 -0
- package/.claude/skills/dividend-growth-pullback-screener/references/fmp_api_guide.md +642 -0
- package/.claude/skills/dividend-growth-pullback-screener/references/rsi_oversold_strategy.md +333 -0
- package/.claude/skills/dividend-growth-pullback-screener/scripts/screen_dividend_growth_rsi.py +1155 -0
- package/.claude/skills/earnings-calendar/SKILL.md +721 -0
- package/.claude/skills/earnings-calendar/assets/earnings_report_template.md +102 -0
- package/.claude/skills/earnings-calendar/references/fmp_api_guide.md +590 -0
- package/.claude/skills/earnings-calendar/scripts/fetch_earnings_fmp.py +443 -0
- package/.claude/skills/earnings-calendar/scripts/generate_report.py +366 -0
- package/.claude/skills/economic-calendar-fetcher/SKILL.md +365 -0
- package/.claude/skills/economic-calendar-fetcher/references/fmp_api_documentation.md +345 -0
- package/.claude/skills/economic-calendar-fetcher/scripts/get_economic_calendar.py +267 -0
- package/.claude/skills/ftd-detector/SKILL.md +147 -0
- package/.claude/skills/ftd-detector/references/ftd_methodology.md +188 -0
- package/.claude/skills/ftd-detector/references/post_ftd_guide.md +185 -0
- package/.claude/skills/ftd-detector/scripts/fmp_client.py +158 -0
- package/.claude/skills/ftd-detector/scripts/ftd_detector.py +280 -0
- package/.claude/skills/ftd-detector/scripts/post_ftd_monitor.py +404 -0
- package/.claude/skills/ftd-detector/scripts/rally_tracker.py +508 -0
- package/.claude/skills/ftd-detector/scripts/report_generator.py +341 -0
- package/.claude/skills/ftd-detector/scripts/tests/conftest.py +9 -0
- package/.claude/skills/ftd-detector/scripts/tests/helpers.py +107 -0
- package/.claude/skills/ftd-detector/scripts/tests/test_post_ftd_monitor.py +311 -0
- package/.claude/skills/ftd-detector/scripts/tests/test_rally_tracker.py +302 -0
- package/.claude/skills/institutional-flow-tracker/README.md +362 -0
- package/.claude/skills/institutional-flow-tracker/SKILL.md +357 -0
- package/.claude/skills/institutional-flow-tracker/references/13f_filings_guide.md +383 -0
- package/.claude/skills/institutional-flow-tracker/references/institutional_investor_types.md +580 -0
- package/.claude/skills/institutional-flow-tracker/references/interpretation_framework.md +573 -0
- package/.claude/skills/institutional-flow-tracker/scripts/analyze_single_stock.py +457 -0
- package/.claude/skills/institutional-flow-tracker/scripts/track_institution_portfolio.py +108 -0
- package/.claude/skills/institutional-flow-tracker/scripts/track_institutional_flow.py +450 -0
- package/.claude/skills/macro-regime-detector/SKILL.md +86 -0
- package/.claude/skills/macro-regime-detector/references/historical_regimes.md +124 -0
- package/.claude/skills/macro-regime-detector/references/indicator_interpretation_guide.md +144 -0
- package/.claude/skills/macro-regime-detector/references/regime_detection_methodology.md +138 -0
- package/.claude/skills/macro-regime-detector/scripts/calculators/__init__.py +1 -0
- package/.claude/skills/macro-regime-detector/scripts/calculators/concentration_calculator.py +165 -0
- package/.claude/skills/macro-regime-detector/scripts/calculators/credit_conditions_calculator.py +124 -0
- package/.claude/skills/macro-regime-detector/scripts/calculators/equity_bond_calculator.py +198 -0
- package/.claude/skills/macro-regime-detector/scripts/calculators/sector_rotation_calculator.py +123 -0
- package/.claude/skills/macro-regime-detector/scripts/calculators/size_factor_calculator.py +131 -0
- package/.claude/skills/macro-regime-detector/scripts/calculators/utils.py +347 -0
- package/.claude/skills/macro-regime-detector/scripts/calculators/yield_curve_calculator.py +279 -0
- package/.claude/skills/macro-regime-detector/scripts/fmp_client.py +134 -0
- package/.claude/skills/macro-regime-detector/scripts/macro_regime_detector.py +278 -0
- package/.claude/skills/macro-regime-detector/scripts/report_generator.py +327 -0
- package/.claude/skills/macro-regime-detector/scripts/scorer.py +574 -0
- package/.claude/skills/macro-regime-detector/scripts/tests/conftest.py +9 -0
- package/.claude/skills/macro-regime-detector/scripts/tests/test_concentration.py +78 -0
- package/.claude/skills/macro-regime-detector/scripts/tests/test_credit_conditions.py +59 -0
- package/.claude/skills/macro-regime-detector/scripts/tests/test_equity_bond.py +74 -0
- package/.claude/skills/macro-regime-detector/scripts/tests/test_helpers.py +90 -0
- package/.claude/skills/macro-regime-detector/scripts/tests/test_scorer.py +439 -0
- package/.claude/skills/macro-regime-detector/scripts/tests/test_sector_rotation.py +78 -0
- package/.claude/skills/macro-regime-detector/scripts/tests/test_size_factor.py +59 -0
- package/.claude/skills/macro-regime-detector/scripts/tests/test_utils.py +126 -0
- package/.claude/skills/macro-regime-detector/scripts/tests/test_yield_curve.py +64 -0
- package/.claude/skills/market-breadth-analyzer/SKILL.md +121 -0
- package/.claude/skills/market-breadth-analyzer/references/breadth_analysis_methodology.md +168 -0
- package/.claude/skills/market-breadth-analyzer/scripts/calculators/__init__.py +1 -0
- package/.claude/skills/market-breadth-analyzer/scripts/calculators/bearish_signal_calculator.py +150 -0
- package/.claude/skills/market-breadth-analyzer/scripts/calculators/cycle_calculator.py +168 -0
- package/.claude/skills/market-breadth-analyzer/scripts/calculators/divergence_calculator.py +119 -0
- package/.claude/skills/market-breadth-analyzer/scripts/calculators/historical_context_calculator.py +120 -0
- package/.claude/skills/market-breadth-analyzer/scripts/calculators/ma_crossover_calculator.py +115 -0
- package/.claude/skills/market-breadth-analyzer/scripts/calculators/trend_level_calculator.py +103 -0
- package/.claude/skills/market-breadth-analyzer/scripts/csv_client.py +225 -0
- package/.claude/skills/market-breadth-analyzer/scripts/market_breadth_analyzer.py +307 -0
- package/.claude/skills/market-breadth-analyzer/scripts/report_generator.py +330 -0
- package/.claude/skills/market-breadth-analyzer/scripts/scorer.py +271 -0
- package/.claude/skills/market-environment-analysis/SKILL.md +139 -0
- package/.claude/skills/market-environment-analysis/references/analysis_patterns.md +124 -0
- package/.claude/skills/market-environment-analysis/references/indicators.md +99 -0
- package/.claude/skills/market-environment-analysis/scripts/market_utils.py +127 -0
- package/.claude/skills/market-news-analyst/SKILL.md +714 -0
- package/.claude/skills/market-news-analyst/references/corporate_news_impact.md +446 -0
- package/.claude/skills/market-news-analyst/references/geopolitical_commodity_correlations.md +499 -0
- package/.claude/skills/market-news-analyst/references/market_event_patterns.md +393 -0
- package/.claude/skills/market-news-analyst/references/trusted_news_sources.md +510 -0
- package/.claude/skills/market-top-detector/SKILL.md +159 -0
- package/.claude/skills/market-top-detector/references/distribution_day_guide.md +100 -0
- package/.claude/skills/market-top-detector/references/historical_tops.md +142 -0
- package/.claude/skills/market-top-detector/references/market_top_methodology.md +167 -0
- package/.claude/skills/market-top-detector/scripts/calculators/__init__.py +17 -0
- package/.claude/skills/market-top-detector/scripts/calculators/breadth_calculator.py +116 -0
- package/.claude/skills/market-top-detector/scripts/calculators/defensive_rotation_calculator.py +127 -0
- package/.claude/skills/market-top-detector/scripts/calculators/distribution_day_calculator.py +161 -0
- package/.claude/skills/market-top-detector/scripts/calculators/index_technical_calculator.py +254 -0
- package/.claude/skills/market-top-detector/scripts/calculators/leading_stock_calculator.py +198 -0
- package/.claude/skills/market-top-detector/scripts/calculators/sentiment_calculator.py +213 -0
- package/.claude/skills/market-top-detector/scripts/fmp_client.py +158 -0
- package/.claude/skills/market-top-detector/scripts/market_top_detector.py +349 -0
- package/.claude/skills/market-top-detector/scripts/report_generator.py +314 -0
- package/.claude/skills/market-top-detector/scripts/scorer.py +473 -0
- package/.claude/skills/market-top-detector/scripts/tests/conftest.py +9 -0
- package/.claude/skills/market-top-detector/scripts/tests/helpers.py +49 -0
- package/.claude/skills/market-top-detector/scripts/tests/test_breadth.py +62 -0
- package/.claude/skills/market-top-detector/scripts/tests/test_defensive_rotation.py +56 -0
- package/.claude/skills/market-top-detector/scripts/tests/test_distribution_day.py +92 -0
- package/.claude/skills/market-top-detector/scripts/tests/test_index_technical.py +73 -0
- package/.claude/skills/market-top-detector/scripts/tests/test_leading_stock.py +57 -0
- package/.claude/skills/market-top-detector/scripts/tests/test_scorer.py +180 -0
- package/.claude/skills/market-top-detector/scripts/tests/test_sentiment.py +64 -0
- package/.claude/skills/options-strategy-advisor/README.md +469 -0
- package/.claude/skills/options-strategy-advisor/SKILL.md +959 -0
- package/.claude/skills/options-strategy-advisor/scripts/black_scholes.py +495 -0
- package/.claude/skills/pair-trade-screener/README.md +389 -0
- package/.claude/skills/pair-trade-screener/SKILL.md +622 -0
- package/.claude/skills/pair-trade-screener/references/cointegration_guide.md +745 -0
- package/.claude/skills/pair-trade-screener/references/methodology.md +853 -0
- package/.claude/skills/pair-trade-screener/scripts/analyze_spread.py +394 -0
- package/.claude/skills/pair-trade-screener/scripts/find_pairs.py +535 -0
- package/.claude/skills/portfolio-manager/README.md +394 -0
- package/.claude/skills/portfolio-manager/SKILL.md +750 -0
- package/.claude/skills/portfolio-manager/references/alpaca-mcp-setup.md +367 -0
- package/.claude/skills/portfolio-manager/references/asset-allocation.md +502 -0
- package/.claude/skills/portfolio-manager/references/diversification-principles.md +553 -0
- package/.claude/skills/portfolio-manager/references/portfolio-risk-metrics.md +603 -0
- package/.claude/skills/portfolio-manager/references/position-evaluation.md +477 -0
- package/.claude/skills/portfolio-manager/references/rebalancing-strategies.md +715 -0
- package/.claude/skills/portfolio-manager/references/risk-profile-questionnaire.md +608 -0
- package/.claude/skills/portfolio-manager/references/target-allocations.md +558 -0
- package/.claude/skills/portfolio-manager/scripts/test_alpaca_connection.py +286 -0
- package/.claude/skills/scenario-analyzer/SKILL.md +317 -0
- package/.claude/skills/scenario-analyzer/references/headline_event_patterns.md +264 -0
- package/.claude/skills/scenario-analyzer/references/scenario_playbooks.md +320 -0
- package/.claude/skills/scenario-analyzer/references/sector_sensitivity_matrix.md +217 -0
- package/.claude/skills/sector-analyst/SKILL.md +206 -0
- package/.claude/skills/sector-analyst/assets/industory_performance_1.jpeg +0 -0
- package/.claude/skills/sector-analyst/assets/industory_performance_2.jpeg +0 -0
- package/.claude/skills/sector-analyst/assets/sector_performance.jpeg +0 -0
- package/.claude/skills/sector-analyst/references/sector_rotation.md +170 -0
- package/.claude/skills/stanley-druckenmiller-investment/SKILL.md +84 -0
- package/.claude/skills/stanley-druckenmiller-investment/references/case-studies.md +148 -0
- package/.claude/skills/stanley-druckenmiller-investment/references/investment-philosophy.md +80 -0
- package/.claude/skills/stanley-druckenmiller-investment/references/market-analysis-guide.md +146 -0
- package/.claude/skills/stock/NOTION_SETUP.md +33 -0
- package/.claude/skills/stock/SKILL.md +38 -0
- package/.claude/skills/technical-analyst/SKILL.md +238 -0
- package/.claude/skills/technical-analyst/assets/analysis_template.md +183 -0
- package/.claude/skills/technical-analyst/references/technical_analysis_framework.md +282 -0
- package/.claude/skills/theme-detector/SKILL.md +320 -0
- package/.claude/skills/theme-detector/assets/report_template.md +155 -0
- package/.claude/skills/theme-detector/references/cross_sector_themes.md +252 -0
- package/.claude/skills/theme-detector/references/finviz_industry_codes.md +403 -0
- package/.claude/skills/theme-detector/references/thematic_etf_catalog.md +333 -0
- package/.claude/skills/theme-detector/references/theme_detection_methodology.md +430 -0
- package/.claude/skills/theme-detector/scripts/calculators/__init__.py +1 -0
- package/.claude/skills/theme-detector/scripts/calculators/heat_calculator.py +123 -0
- package/.claude/skills/theme-detector/scripts/calculators/industry_ranker.py +98 -0
- package/.claude/skills/theme-detector/scripts/calculators/lifecycle_calculator.py +172 -0
- package/.claude/skills/theme-detector/scripts/calculators/theme_classifier.py +195 -0
- package/.claude/skills/theme-detector/scripts/calculators/theme_discoverer.py +280 -0
- package/.claude/skills/theme-detector/scripts/config_loader.py +142 -0
- package/.claude/skills/theme-detector/scripts/default_theme_config.py +254 -0
- package/.claude/skills/theme-detector/scripts/etf_scanner.py +609 -0
- package/.claude/skills/theme-detector/scripts/finviz_performance_client.py +131 -0
- package/.claude/skills/theme-detector/scripts/report_generator.py +490 -0
- package/.claude/skills/theme-detector/scripts/representative_stock_selector.py +673 -0
- package/.claude/skills/theme-detector/scripts/scorer.py +87 -0
- package/.claude/skills/theme-detector/scripts/tests/README.md +21 -0
- package/.claude/skills/theme-detector/scripts/tests/conftest.py +9 -0
- package/.claude/skills/theme-detector/scripts/tests/test_config_loader.py +239 -0
- package/.claude/skills/theme-detector/scripts/tests/test_etf_scanner.py +810 -0
- package/.claude/skills/theme-detector/scripts/tests/test_heat_calculator.py +245 -0
- package/.claude/skills/theme-detector/scripts/tests/test_industry_ranker.py +256 -0
- package/.claude/skills/theme-detector/scripts/tests/test_lifecycle_calculator.py +301 -0
- package/.claude/skills/theme-detector/scripts/tests/test_report_generator.py +624 -0
- package/.claude/skills/theme-detector/scripts/tests/test_representative_stock_selector.py +898 -0
- package/.claude/skills/theme-detector/scripts/tests/test_scorer.py +185 -0
- package/.claude/skills/theme-detector/scripts/tests/test_theme_classifier.py +534 -0
- package/.claude/skills/theme-detector/scripts/tests/test_theme_detector_e2e.py +467 -0
- package/.claude/skills/theme-detector/scripts/tests/test_theme_discoverer.py +458 -0
- package/.claude/skills/theme-detector/scripts/tests/test_uptrend_client.py +76 -0
- package/.claude/skills/theme-detector/scripts/theme_detector.py +815 -0
- package/.claude/skills/theme-detector/scripts/themes.yaml +168 -0
- package/.claude/skills/theme-detector/scripts/uptrend_client.py +241 -0
- package/.claude/skills/uptrend-analyzer/SKILL.md +108 -0
- package/.claude/skills/uptrend-analyzer/references/uptrend_methodology.md +215 -0
- package/.claude/skills/uptrend-analyzer/scripts/calculators/__init__.py +1 -0
- package/.claude/skills/uptrend-analyzer/scripts/calculators/historical_context_calculator.py +122 -0
- package/.claude/skills/uptrend-analyzer/scripts/calculators/market_breadth_calculator.py +145 -0
- package/.claude/skills/uptrend-analyzer/scripts/calculators/momentum_calculator.py +183 -0
- package/.claude/skills/uptrend-analyzer/scripts/calculators/sector_participation_calculator.py +204 -0
- package/.claude/skills/uptrend-analyzer/scripts/calculators/sector_rotation_calculator.py +218 -0
- package/.claude/skills/uptrend-analyzer/scripts/data_fetcher.py +236 -0
- package/.claude/skills/uptrend-analyzer/scripts/report_generator.py +329 -0
- package/.claude/skills/uptrend-analyzer/scripts/scorer.py +276 -0
- package/.claude/skills/uptrend-analyzer/scripts/uptrend_analyzer.py +219 -0
- package/.claude/skills/us-market-bubble-detector/CHANGELOG.md +118 -0
- package/.claude/skills/us-market-bubble-detector/SKILL.md +545 -0
- package/.claude/skills/us-market-bubble-detector/references/bubble_framework.md +335 -0
- package/.claude/skills/us-market-bubble-detector/references/historical_cases.md +327 -0
- package/.claude/skills/us-market-bubble-detector/references/implementation_guide.md +473 -0
- package/.claude/skills/us-market-bubble-detector/references/quick_reference.md +354 -0
- package/.claude/skills/us-market-bubble-detector/references/quick_reference_en.md +342 -0
- package/.claude/skills/us-market-bubble-detector/scripts/bubble_scorer.py +309 -0
- package/.claude/skills/us-stock-analysis/SKILL.md +294 -0
- package/.claude/skills/us-stock-analysis/references/financial-metrics.md +172 -0
- package/.claude/skills/us-stock-analysis/references/fundamental-analysis.md +129 -0
- package/.claude/skills/us-stock-analysis/references/report-template.md +207 -0
- package/.claude/skills/us-stock-analysis/references/technical-analysis.md +93 -0
- package/.claude/skills/value-dividend-screener/SKILL.md +562 -0
- package/.claude/skills/value-dividend-screener/references/fmp_api_guide.md +348 -0
- package/.claude/skills/value-dividend-screener/references/screening_methodology.md +315 -0
- package/.claude/skills/value-dividend-screener/scripts/screen_dividend_stocks.py +1138 -0
- package/.claude/skills/vcp-screener/SKILL.md +79 -0
- package/.claude/skills/vcp-screener/references/fmp_api_endpoints.md +45 -0
- package/.claude/skills/vcp-screener/references/scoring_system.md +154 -0
- package/.claude/skills/vcp-screener/references/vcp_methodology.md +124 -0
- package/.claude/skills/vcp-screener/scripts/calculators/__init__.py +1 -0
- package/.claude/skills/vcp-screener/scripts/calculators/pivot_proximity_calculator.py +139 -0
- package/.claude/skills/vcp-screener/scripts/calculators/relative_strength_calculator.py +161 -0
- package/.claude/skills/vcp-screener/scripts/calculators/trend_template_calculator.py +228 -0
- package/.claude/skills/vcp-screener/scripts/calculators/vcp_pattern_calculator.py +322 -0
- package/.claude/skills/vcp-screener/scripts/calculators/volume_pattern_calculator.py +121 -0
- package/.claude/skills/vcp-screener/scripts/fmp_client.py +162 -0
- package/.claude/skills/vcp-screener/scripts/report_generator.py +317 -0
- package/.claude/skills/vcp-screener/scripts/scorer.py +155 -0
- package/.claude/skills/vcp-screener/scripts/screen_vcp.py +536 -0
- package/.claude/skills/vcp-screener/scripts/tests/__init__.py +0 -0
- package/.claude/skills/vcp-screener/scripts/tests/conftest.py +9 -0
- package/.claude/skills/vcp-screener/scripts/tests/test_vcp_screener.py +834 -0
- package/.claude/skills/weekly-trade-strategy/.claude/agents/druckenmiller-strategy-planner.md +300 -0
- package/.claude/skills/weekly-trade-strategy/.claude/agents/market-news-analyzer.md +239 -0
- package/.claude/skills/weekly-trade-strategy/.claude/agents/technical-market-analyst.md +187 -0
- package/.claude/skills/weekly-trade-strategy/.claude/agents/us-market-analyst.md +218 -0
- package/.claude/skills/weekly-trade-strategy/.claude/agents/weekly-trade-blog-writer.md +318 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/breadth-chart-analyst/SKILL.md +662 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/breadth-chart-analyst/assets/SP500_Breadth_Index_200MA_8MA.jpeg +0 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/breadth-chart-analyst/assets/US_Stock_Market_Uptrend_Ratio.jpeg +0 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/breadth-chart-analyst/assets/breadth_analysis_template.md +558 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/breadth-chart-analyst/references/breadth_chart_methodology.md +590 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/earnings-calendar/SKILL.md +721 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/earnings-calendar/assets/earnings_report_template.md +102 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/earnings-calendar/earnings_calendar_2025-11-02.md +447 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/earnings-calendar/references/fmp_api_guide.md +590 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/earnings-calendar/scripts/fetch_earnings_fmp.py +443 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/earnings-calendar/scripts/generate_report.py +366 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/economic-calendar-fetcher/SKILL.md +365 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/economic-calendar-fetcher/references/fmp_api_documentation.md +345 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/economic-calendar-fetcher/scripts/get_economic_calendar.py +267 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/market-environment-analysis/SKILL.md +139 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/market-environment-analysis/references/analysis_patterns.md +124 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/market-environment-analysis/references/indicators.md +99 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/market-environment-analysis/scripts/market_utils.py +127 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/market-news-analyst/SKILL.md +714 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/market-news-analyst/references/corporate_news_impact.md +446 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/market-news-analyst/references/geopolitical_commodity_correlations.md +499 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/market-news-analyst/references/market_event_patterns.md +393 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/market-news-analyst/references/trusted_news_sources.md +510 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/sector-analyst/SKILL.md +206 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/sector-analyst/assets/industory_performance_1.jpeg +0 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/sector-analyst/assets/industory_performance_2.jpeg +0 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/sector-analyst/assets/sector_performance.jpeg +0 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/sector-analyst/references/sector_rotation.md +170 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/stanley-druckenmiller-investment/SKILL.md +84 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/stanley-druckenmiller-investment/references/case-studies.md +148 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/stanley-druckenmiller-investment/references/investment-philosophy.md +80 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/stanley-druckenmiller-investment/references/market-analysis-guide.md +146 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/technical-analyst/SKILL.md +238 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/technical-analyst/assets/analysis_template.md +183 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/technical-analyst/references/technical_analysis_framework.md +282 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/us-market-bubble-detector/CHANGELOG.md +118 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/us-market-bubble-detector/SKILL.md +545 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/us-market-bubble-detector/references/bubble_framework.md +335 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/us-market-bubble-detector/references/historical_cases.md +327 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/us-market-bubble-detector/references/implementation_guide.md +473 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/us-market-bubble-detector/references/quick_reference.md +354 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/us-market-bubble-detector/references/quick_reference_en.md +342 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/us-market-bubble-detector/scripts/bubble_scorer.py +309 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/us-stock-analysis/SKILL.md +294 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/us-stock-analysis/references/financial-metrics.md +172 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/us-stock-analysis/references/fundamental-analysis.md +129 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/us-stock-analysis/references/report-template.md +207 -0
- package/.claude/skills/weekly-trade-strategy/.claude/skills/us-stock-analysis/references/technical-analysis.md +93 -0
- package/.claude/skills/weekly-trade-strategy/CLAUDE.md +454 -0
- package/.claude/skills/weekly-trade-strategy/README.md +287 -0
- package/.claude/skills/weekly-trade-strategy/blogs/.gitkeep +0 -0
- package/.claude/skills/weekly-trade-strategy/charts/.gitkeep +0 -0
- package/.claude/skills/weekly-trade-strategy/earnings_data.json +10054 -0
- package/.claude/skills/weekly-trade-strategy/skills/breadth-chart-analyst/SKILL.md +662 -0
- package/.claude/skills/weekly-trade-strategy/skills/breadth-chart-analyst/assets/SP500_Breadth_Index_200MA_8MA.jpeg +0 -0
- package/.claude/skills/weekly-trade-strategy/skills/breadth-chart-analyst/assets/US_Stock_Market_Uptrend_Ratio.jpeg +0 -0
- package/.claude/skills/weekly-trade-strategy/skills/breadth-chart-analyst/assets/breadth_analysis_template.md +558 -0
- package/.claude/skills/weekly-trade-strategy/skills/breadth-chart-analyst/references/breadth_chart_methodology.md +590 -0
- package/.claude/skills/weekly-trade-strategy/skills/earnings-calendar/SKILL.md +721 -0
- package/.claude/skills/weekly-trade-strategy/skills/earnings-calendar/assets/earnings_report_template.md +102 -0
- package/.claude/skills/weekly-trade-strategy/skills/earnings-calendar/earnings_calendar_2025-11-02.md +447 -0
- package/.claude/skills/weekly-trade-strategy/skills/earnings-calendar/references/fmp_api_guide.md +590 -0
- package/.claude/skills/weekly-trade-strategy/skills/earnings-calendar/scripts/fetch_earnings_fmp.py +443 -0
- package/.claude/skills/weekly-trade-strategy/skills/earnings-calendar/scripts/generate_report.py +366 -0
- package/.claude/skills/weekly-trade-strategy/skills/economic-calendar-fetcher/SKILL.md +365 -0
- package/.claude/skills/weekly-trade-strategy/skills/economic-calendar-fetcher/references/fmp_api_documentation.md +345 -0
- package/.claude/skills/weekly-trade-strategy/skills/economic-calendar-fetcher/scripts/get_economic_calendar.py +267 -0
- package/.claude/skills/weekly-trade-strategy/skills/market-environment-analysis/SKILL.md +139 -0
- package/.claude/skills/weekly-trade-strategy/skills/market-environment-analysis/references/analysis_patterns.md +124 -0
- package/.claude/skills/weekly-trade-strategy/skills/market-environment-analysis/references/indicators.md +99 -0
- package/.claude/skills/weekly-trade-strategy/skills/market-environment-analysis/scripts/market_utils.py +127 -0
- package/.claude/skills/weekly-trade-strategy/skills/market-news-analyst/SKILL.md +714 -0
- package/.claude/skills/weekly-trade-strategy/skills/market-news-analyst/references/corporate_news_impact.md +446 -0
- package/.claude/skills/weekly-trade-strategy/skills/market-news-analyst/references/geopolitical_commodity_correlations.md +499 -0
- package/.claude/skills/weekly-trade-strategy/skills/market-news-analyst/references/market_event_patterns.md +393 -0
- package/.claude/skills/weekly-trade-strategy/skills/market-news-analyst/references/trusted_news_sources.md +510 -0
- package/.claude/skills/weekly-trade-strategy/skills/sector-analyst/SKILL.md +206 -0
- package/.claude/skills/weekly-trade-strategy/skills/sector-analyst/assets/industory_performance_1.jpeg +0 -0
- package/.claude/skills/weekly-trade-strategy/skills/sector-analyst/assets/industory_performance_2.jpeg +0 -0
- package/.claude/skills/weekly-trade-strategy/skills/sector-analyst/assets/sector_performance.jpeg +0 -0
- package/.claude/skills/weekly-trade-strategy/skills/sector-analyst/references/sector_rotation.md +170 -0
- package/.claude/skills/weekly-trade-strategy/skills/stanley-druckenmiller-investment/SKILL.md +84 -0
- package/.claude/skills/weekly-trade-strategy/skills/stanley-druckenmiller-investment/references/case-studies.md +148 -0
- package/.claude/skills/weekly-trade-strategy/skills/stanley-druckenmiller-investment/references/investment-philosophy.md +80 -0
- package/.claude/skills/weekly-trade-strategy/skills/stanley-druckenmiller-investment/references/market-analysis-guide.md +146 -0
- package/.claude/skills/weekly-trade-strategy/skills/technical-analyst/SKILL.md +238 -0
- package/.claude/skills/weekly-trade-strategy/skills/technical-analyst/assets/analysis_template.md +183 -0
- package/.claude/skills/weekly-trade-strategy/skills/technical-analyst/references/technical_analysis_framework.md +282 -0
- package/.claude/skills/weekly-trade-strategy/skills/us-market-bubble-detector/CHANGELOG.md +118 -0
- package/.claude/skills/weekly-trade-strategy/skills/us-market-bubble-detector/SKILL.md +545 -0
- package/.claude/skills/weekly-trade-strategy/skills/us-market-bubble-detector/references/bubble_framework.md +335 -0
- package/.claude/skills/weekly-trade-strategy/skills/us-market-bubble-detector/references/historical_cases.md +327 -0
- package/.claude/skills/weekly-trade-strategy/skills/us-market-bubble-detector/references/implementation_guide.md +473 -0
- package/.claude/skills/weekly-trade-strategy/skills/us-market-bubble-detector/references/quick_reference.md +354 -0
- package/.claude/skills/weekly-trade-strategy/skills/us-market-bubble-detector/references/quick_reference_en.md +342 -0
- package/.claude/skills/weekly-trade-strategy/skills/us-market-bubble-detector/scripts/bubble_scorer.py +309 -0
- package/.claude/skills/weekly-trade-strategy/skills/us-stock-analysis/SKILL.md +294 -0
- package/.claude/skills/weekly-trade-strategy/skills/us-stock-analysis/references/financial-metrics.md +172 -0
- package/.claude/skills/weekly-trade-strategy/skills/us-stock-analysis/references/fundamental-analysis.md +129 -0
- package/.claude/skills/weekly-trade-strategy/skills/us-stock-analysis/references/report-template.md +207 -0
- package/.claude/skills/weekly-trade-strategy/skills/us-stock-analysis/references/technical-analysis.md +93 -0
- package/.mcp.json +3 -0
- package/cli.mjs +16 -16
- 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()
|