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
package/.claude/skills/market-breadth-analyzer/scripts/calculators/bearish_signal_calculator.py
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Component 4: Bearish Signal Status (Weight: 15%)
|
|
4
|
+
|
|
5
|
+
Evaluates the backtested bearish signal flag and its context.
|
|
6
|
+
Also detects the "Pink Zone" (Bearish Region) from the source repository:
|
|
7
|
+
Pink Zone = Breadth_200MA_Trend == -1 AND 8MA < 200MA
|
|
8
|
+
|
|
9
|
+
Input: Bearish_Signal, Breadth_200MA_Trend, Breadth_Index_8MA,
|
|
10
|
+
Breadth_Index_200MA (latest row)
|
|
11
|
+
|
|
12
|
+
Scoring (100 = healthy):
|
|
13
|
+
Base:
|
|
14
|
+
Signal=False & Trend=1 -> 85 (all clear)
|
|
15
|
+
Signal=False & Trend=-1 -> 50 (no warning but downtrend)
|
|
16
|
+
Signal=True & Trend=1 -> 30 (warning in uptrend)
|
|
17
|
+
Signal=True & Trend=-1 -> 10 (fully bearish)
|
|
18
|
+
|
|
19
|
+
Context Adjustment:
|
|
20
|
+
Signal=True & 8MA > 0.50 -> +15 (weak signal amid strong breadth)
|
|
21
|
+
Signal=True & 8MA < 0.25 -> -5 (severe signal amid extreme weakness)
|
|
22
|
+
|
|
23
|
+
Pink Zone Adjustment:
|
|
24
|
+
In Pink Zone & Signal=False -> -10 (structural weakness even without signal)
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from typing import Dict, List
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def calculate_bearish_signal(rows: List[Dict]) -> Dict:
|
|
31
|
+
"""
|
|
32
|
+
Calculate bearish signal status score.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
rows: All detail rows sorted by date ascending.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Dict with score, signal, and component details.
|
|
39
|
+
"""
|
|
40
|
+
if not rows:
|
|
41
|
+
return {
|
|
42
|
+
"score": 50,
|
|
43
|
+
"signal": "NO DATA: No data available for bearish signal analysis",
|
|
44
|
+
"data_available": False,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
latest = rows[-1]
|
|
48
|
+
signal_active = latest["Bearish_Signal"]
|
|
49
|
+
trend = latest["Breadth_200MA_Trend"]
|
|
50
|
+
ma8 = latest["Breadth_Index_8MA"]
|
|
51
|
+
ma200 = latest["Breadth_Index_200MA"]
|
|
52
|
+
|
|
53
|
+
# Pink Zone detection (from source repo: chart's pink background)
|
|
54
|
+
# Condition: 200MA trend declining AND 8MA below 200MA
|
|
55
|
+
in_pink_zone = (trend == -1) and (ma8 < ma200)
|
|
56
|
+
|
|
57
|
+
# Count consecutive pink zone days
|
|
58
|
+
pink_zone_days = 0
|
|
59
|
+
if in_pink_zone:
|
|
60
|
+
for i in range(len(rows) - 1, -1, -1):
|
|
61
|
+
r = rows[i]
|
|
62
|
+
if (r["Breadth_200MA_Trend"] == -1 and
|
|
63
|
+
r["Breadth_Index_8MA"] < r["Breadth_Index_200MA"]):
|
|
64
|
+
pink_zone_days += 1
|
|
65
|
+
else:
|
|
66
|
+
break
|
|
67
|
+
|
|
68
|
+
# Base score
|
|
69
|
+
base_score = _base_score(signal_active, trend)
|
|
70
|
+
|
|
71
|
+
# Context adjustment
|
|
72
|
+
context_adj = 0
|
|
73
|
+
if signal_active:
|
|
74
|
+
if ma8 > 0.50:
|
|
75
|
+
context_adj = +15 # Weak signal amid strong breadth
|
|
76
|
+
elif ma8 < 0.25:
|
|
77
|
+
context_adj = -5 # Severe signal amid extreme weakness
|
|
78
|
+
|
|
79
|
+
# Pink Zone adjustment: structural weakness even without bearish signal
|
|
80
|
+
pink_zone_adj = 0
|
|
81
|
+
if in_pink_zone and not signal_active:
|
|
82
|
+
pink_zone_adj = -10
|
|
83
|
+
|
|
84
|
+
score = round(base_score + context_adj + pink_zone_adj)
|
|
85
|
+
score = max(0, min(100, score))
|
|
86
|
+
|
|
87
|
+
signal_text = _generate_signal(signal_active, trend, ma8, in_pink_zone, score)
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
"score": score,
|
|
91
|
+
"signal": signal_text,
|
|
92
|
+
"data_available": True,
|
|
93
|
+
"signal_active": signal_active,
|
|
94
|
+
"trend": trend,
|
|
95
|
+
"current_8ma": ma8,
|
|
96
|
+
"current_200ma": ma200,
|
|
97
|
+
"in_pink_zone": in_pink_zone,
|
|
98
|
+
"pink_zone_days": pink_zone_days,
|
|
99
|
+
"base_score": base_score,
|
|
100
|
+
"context_adjustment": context_adj,
|
|
101
|
+
"pink_zone_adjustment": pink_zone_adj,
|
|
102
|
+
"date": latest["Date"],
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _base_score(signal_active: bool, trend: int) -> int:
|
|
107
|
+
"""Calculate base score from signal and trend combination."""
|
|
108
|
+
if not signal_active and trend == 1:
|
|
109
|
+
return 85 # All clear
|
|
110
|
+
elif not signal_active and trend == -1:
|
|
111
|
+
return 50 # No warning but downtrend
|
|
112
|
+
elif signal_active and trend == 1:
|
|
113
|
+
return 30 # Warning in uptrend
|
|
114
|
+
else: # signal_active and trend == -1
|
|
115
|
+
return 10 # Fully bearish
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _generate_signal(
|
|
119
|
+
signal_active: bool, trend: int, ma8: float,
|
|
120
|
+
in_pink_zone: bool, score: int,
|
|
121
|
+
) -> str:
|
|
122
|
+
"""Generate human-readable signal."""
|
|
123
|
+
trend_str = "uptrend" if trend == 1 else "downtrend"
|
|
124
|
+
pink_str = " [PINK ZONE]" if in_pink_zone else ""
|
|
125
|
+
|
|
126
|
+
if not signal_active:
|
|
127
|
+
if trend == 1:
|
|
128
|
+
return f"ALL CLEAR: No bearish signal, {trend_str}"
|
|
129
|
+
elif in_pink_zone:
|
|
130
|
+
return (
|
|
131
|
+
f"CAUTION: No bearish signal but in Pink Zone "
|
|
132
|
+
f"(downtrend + 8MA < 200MA){pink_str}"
|
|
133
|
+
)
|
|
134
|
+
else:
|
|
135
|
+
return f"CAUTION: No bearish signal but {trend_str}"
|
|
136
|
+
else:
|
|
137
|
+
if ma8 > 0.50:
|
|
138
|
+
return (
|
|
139
|
+
f"WARNING (muted): Bearish signal active in {trend_str}, "
|
|
140
|
+
f"but 8MA={ma8:.3f} still relatively strong{pink_str}"
|
|
141
|
+
)
|
|
142
|
+
elif ma8 < 0.25:
|
|
143
|
+
return (
|
|
144
|
+
f"CRITICAL: Bearish signal active in {trend_str}, "
|
|
145
|
+
f"8MA={ma8:.3f} extremely weak{pink_str}"
|
|
146
|
+
)
|
|
147
|
+
else:
|
|
148
|
+
return (
|
|
149
|
+
f"BEARISH: Signal active in {trend_str}, 8MA={ma8:.3f}{pink_str}"
|
|
150
|
+
)
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Component 3: Peak/Trough Cycle Position (Weight: 20%)
|
|
4
|
+
|
|
5
|
+
Determines where we are in the breadth cycle relative to recent peaks and troughs.
|
|
6
|
+
|
|
7
|
+
Input: Is_Peak, Is_Trough, Is_Trough_8MA_Below_04, Breadth_Index_8MA (last 120 days)
|
|
8
|
+
|
|
9
|
+
Scoring (100 = healthy):
|
|
10
|
+
Latest marker = TROUGH:
|
|
11
|
+
<= 20 days & 8MA rising -> 85 (early recovery)
|
|
12
|
+
<= 20 days & 8MA flat/falling -> 30 (failed reversal)
|
|
13
|
+
21-60 days & 8MA rising -> 75 (sustained recovery)
|
|
14
|
+
> 60 days & 8MA rising -> 65 (mature recovery)
|
|
15
|
+
|
|
16
|
+
Latest marker = PEAK:
|
|
17
|
+
<= 20 days & 8MA falling -> 20 (post-peak decline)
|
|
18
|
+
<= 20 days & 8MA flat/rising -> 60 (high-level consolidation)
|
|
19
|
+
21-60 days & 8MA falling -> 15 (sustained decline)
|
|
20
|
+
> 60 days & 8MA falling -> 10 (prolonged decline)
|
|
21
|
+
> 60 days & 8MA rising -> 50 (possible bottom formation)
|
|
22
|
+
|
|
23
|
+
Extreme Trough Bonus: Is_Trough_8MA_Below_04 == True -> +10
|
|
24
|
+
No marker within 120 days -> 50 (neutral)
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from typing import Dict, List, Optional, Tuple
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def calculate_cycle_position(rows: List[Dict]) -> Dict:
|
|
31
|
+
"""
|
|
32
|
+
Calculate peak/trough cycle position score.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
rows: All detail rows sorted by date ascending.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Dict with score, signal, and component details.
|
|
39
|
+
"""
|
|
40
|
+
if not rows or len(rows) < 10:
|
|
41
|
+
return {
|
|
42
|
+
"score": 50,
|
|
43
|
+
"signal": "NO DATA: Insufficient data for cycle analysis",
|
|
44
|
+
"data_available": False,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
# Look at last 120 trading days
|
|
48
|
+
lookback = min(120, len(rows))
|
|
49
|
+
recent = rows[-lookback:]
|
|
50
|
+
latest = rows[-1]
|
|
51
|
+
current_8ma = latest["Breadth_Index_8MA"]
|
|
52
|
+
|
|
53
|
+
# Find latest peak or trough marker
|
|
54
|
+
marker_type, marker_idx, days_since = _find_latest_marker(recent)
|
|
55
|
+
|
|
56
|
+
# Determine 8MA trend (rising/falling) over last 5 days
|
|
57
|
+
if len(rows) >= 6:
|
|
58
|
+
ma8_trend = "rising" if current_8ma > rows[-6]["Breadth_Index_8MA"] else "falling"
|
|
59
|
+
else:
|
|
60
|
+
ma8_trend = "unknown"
|
|
61
|
+
|
|
62
|
+
# Check for extreme trough
|
|
63
|
+
extreme_trough = False
|
|
64
|
+
if marker_type == "TROUGH":
|
|
65
|
+
extreme_trough = recent[marker_idx]["Is_Trough_8MA_Below_04"]
|
|
66
|
+
|
|
67
|
+
# Calculate score
|
|
68
|
+
score = _calculate_score(marker_type, days_since, ma8_trend, extreme_trough)
|
|
69
|
+
score = max(0, min(100, score))
|
|
70
|
+
|
|
71
|
+
signal = _generate_signal(marker_type, days_since, ma8_trend, score)
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
"score": score,
|
|
75
|
+
"signal": signal,
|
|
76
|
+
"data_available": True,
|
|
77
|
+
"latest_marker_type": marker_type,
|
|
78
|
+
"days_since_marker": days_since,
|
|
79
|
+
"ma8_trend": ma8_trend,
|
|
80
|
+
"current_8ma": current_8ma,
|
|
81
|
+
"extreme_trough": extreme_trough,
|
|
82
|
+
"date": latest["Date"],
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _find_latest_marker(recent: List[Dict]) -> Tuple[Optional[str], int, Optional[int]]:
|
|
87
|
+
"""
|
|
88
|
+
Find the most recent peak or trough marker.
|
|
89
|
+
|
|
90
|
+
Returns (marker_type, index_in_recent, days_since).
|
|
91
|
+
"""
|
|
92
|
+
for i in range(len(recent) - 1, -1, -1):
|
|
93
|
+
row = recent[i]
|
|
94
|
+
if row["Is_Peak"]:
|
|
95
|
+
days_since = len(recent) - 1 - i
|
|
96
|
+
return "PEAK", i, days_since
|
|
97
|
+
if row["Is_Trough"] or row["Is_Trough_8MA_Below_04"]:
|
|
98
|
+
days_since = len(recent) - 1 - i
|
|
99
|
+
return "TROUGH", i, days_since
|
|
100
|
+
|
|
101
|
+
return None, -1, None
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _calculate_score(
|
|
105
|
+
marker_type: Optional[str],
|
|
106
|
+
days_since: Optional[int],
|
|
107
|
+
ma8_trend: str,
|
|
108
|
+
extreme_trough: bool,
|
|
109
|
+
) -> int:
|
|
110
|
+
"""Calculate cycle position score based on marker and context."""
|
|
111
|
+
if marker_type is None or days_since is None:
|
|
112
|
+
return 50 # Neutral when no marker found
|
|
113
|
+
|
|
114
|
+
rising = ma8_trend == "rising"
|
|
115
|
+
|
|
116
|
+
if marker_type == "TROUGH":
|
|
117
|
+
if days_since <= 20:
|
|
118
|
+
base = 85 if rising else 30
|
|
119
|
+
elif days_since <= 60:
|
|
120
|
+
base = 75 if rising else 35
|
|
121
|
+
else:
|
|
122
|
+
base = 65 if rising else 40
|
|
123
|
+
|
|
124
|
+
# Extreme trough bonus (contrarian buy signal)
|
|
125
|
+
if extreme_trough:
|
|
126
|
+
base += 10
|
|
127
|
+
|
|
128
|
+
elif marker_type == "PEAK":
|
|
129
|
+
if days_since <= 20:
|
|
130
|
+
base = 60 if rising else 20
|
|
131
|
+
elif days_since <= 60:
|
|
132
|
+
base = 45 if rising else 15
|
|
133
|
+
else:
|
|
134
|
+
base = 50 if rising else 10
|
|
135
|
+
|
|
136
|
+
else:
|
|
137
|
+
base = 50
|
|
138
|
+
|
|
139
|
+
return base
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _generate_signal(
|
|
143
|
+
marker_type: Optional[str],
|
|
144
|
+
days_since: Optional[int],
|
|
145
|
+
ma8_trend: str,
|
|
146
|
+
score: int,
|
|
147
|
+
) -> str:
|
|
148
|
+
"""Generate human-readable signal."""
|
|
149
|
+
if marker_type is None:
|
|
150
|
+
return "NEUTRAL: No cycle marker in last 120 days"
|
|
151
|
+
|
|
152
|
+
if marker_type == "TROUGH":
|
|
153
|
+
if days_since <= 20:
|
|
154
|
+
phase = "early recovery" if ma8_trend == "rising" else "failed reversal attempt"
|
|
155
|
+
elif days_since <= 60:
|
|
156
|
+
phase = "sustained recovery" if ma8_trend == "rising" else "stalled recovery"
|
|
157
|
+
else:
|
|
158
|
+
phase = "mature recovery" if ma8_trend == "rising" else "weakening recovery"
|
|
159
|
+
return f"TROUGH ({days_since}d ago): {phase}, 8MA {ma8_trend}"
|
|
160
|
+
|
|
161
|
+
else: # PEAK
|
|
162
|
+
if days_since <= 20:
|
|
163
|
+
phase = "consolidation near highs" if ma8_trend == "rising" else "post-peak decline"
|
|
164
|
+
elif days_since <= 60:
|
|
165
|
+
phase = "gradual decline" if ma8_trend != "rising" else "recovery attempt"
|
|
166
|
+
else:
|
|
167
|
+
phase = "possible bottom formation" if ma8_trend == "rising" else "prolonged decline"
|
|
168
|
+
return f"PEAK ({days_since}d ago): {phase}, 8MA {ma8_trend}"
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Component 6: S&P 500 vs Breadth Divergence (Weight: 10%)
|
|
4
|
+
|
|
5
|
+
Detects divergence between price action and breadth participation.
|
|
6
|
+
|
|
7
|
+
Input: S&P500_Price, Breadth_Index_8MA (last 60 days)
|
|
8
|
+
|
|
9
|
+
Scoring (100 = healthy):
|
|
10
|
+
60-day changes:
|
|
11
|
+
sp500_pct = (latest - 60d_ago) / 60d_ago * 100
|
|
12
|
+
breadth_change = latest_8ma - 8ma_60d_ago
|
|
13
|
+
|
|
14
|
+
Both rising -> 70 (healthy rally)
|
|
15
|
+
Both falling -> 30 (consistent decline)
|
|
16
|
+
SP up(>3%) & Breadth down(<-0.05) -> 10 (dangerous divergence)
|
|
17
|
+
SP up(>1%) & Breadth down(<-0.03) -> 25
|
|
18
|
+
SP down(<-3%) & Breadth up(>+0.05) -> 80 (bullish divergence)
|
|
19
|
+
SP down(<-1%) & Breadth up(>+0.03) -> 65
|
|
20
|
+
Otherwise -> 50
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from typing import Dict, List
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def calculate_divergence(rows: List[Dict]) -> Dict:
|
|
27
|
+
"""
|
|
28
|
+
Calculate S&P 500 vs breadth divergence score.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
rows: All detail rows sorted by date ascending.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Dict with score, signal, and component details.
|
|
35
|
+
"""
|
|
36
|
+
if not rows or len(rows) < 60:
|
|
37
|
+
# Use whatever data is available, minimum 20 days
|
|
38
|
+
if not rows or len(rows) < 20:
|
|
39
|
+
return {
|
|
40
|
+
"score": 50,
|
|
41
|
+
"signal": "NO DATA: Insufficient data for divergence analysis",
|
|
42
|
+
"data_available": False,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
lookback = min(60, len(rows))
|
|
46
|
+
latest = rows[-1]
|
|
47
|
+
past = rows[-lookback]
|
|
48
|
+
|
|
49
|
+
sp_latest = latest["S&P500_Price"]
|
|
50
|
+
sp_past = past["S&P500_Price"]
|
|
51
|
+
ma8_latest = latest["Breadth_Index_8MA"]
|
|
52
|
+
ma8_past = past["Breadth_Index_8MA"]
|
|
53
|
+
|
|
54
|
+
if sp_past <= 0:
|
|
55
|
+
return {
|
|
56
|
+
"score": 50,
|
|
57
|
+
"signal": "NO DATA: Invalid S&P 500 price data",
|
|
58
|
+
"data_available": False,
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
sp500_pct = (sp_latest - sp_past) / sp_past * 100
|
|
62
|
+
breadth_change = ma8_latest - ma8_past
|
|
63
|
+
|
|
64
|
+
# Determine divergence type and score
|
|
65
|
+
score, div_type = _score_divergence(sp500_pct, breadth_change)
|
|
66
|
+
score = max(0, min(100, score))
|
|
67
|
+
|
|
68
|
+
signal = _generate_signal(sp500_pct, breadth_change, div_type, score)
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
"score": score,
|
|
72
|
+
"signal": signal,
|
|
73
|
+
"data_available": True,
|
|
74
|
+
"sp500_pct_change": round(sp500_pct, 2),
|
|
75
|
+
"breadth_change": round(breadth_change, 4),
|
|
76
|
+
"sp500_latest": sp_latest,
|
|
77
|
+
"sp500_past": sp_past,
|
|
78
|
+
"ma8_latest": ma8_latest,
|
|
79
|
+
"ma8_past": ma8_past,
|
|
80
|
+
"lookback_days": lookback,
|
|
81
|
+
"divergence_type": div_type,
|
|
82
|
+
"date": latest["Date"],
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _score_divergence(sp_pct: float, breadth_chg: float) -> tuple:
|
|
87
|
+
"""Score based on price/breadth divergence. Returns (score, type_label)."""
|
|
88
|
+
sp_up = sp_pct > 0
|
|
89
|
+
breadth_up = breadth_chg > 0
|
|
90
|
+
|
|
91
|
+
# Dangerous divergence: SP up, breadth down
|
|
92
|
+
if sp_pct > 3.0 and breadth_chg < -0.05:
|
|
93
|
+
return 10, "Dangerous bearish divergence"
|
|
94
|
+
if sp_pct > 1.0 and breadth_chg < -0.03:
|
|
95
|
+
return 25, "Moderate bearish divergence"
|
|
96
|
+
|
|
97
|
+
# Bullish divergence: SP down, breadth up
|
|
98
|
+
if sp_pct < -3.0 and breadth_chg > 0.05:
|
|
99
|
+
return 80, "Strong bullish divergence"
|
|
100
|
+
if sp_pct < -1.0 and breadth_chg > 0.03:
|
|
101
|
+
return 65, "Moderate bullish divergence"
|
|
102
|
+
|
|
103
|
+
# Aligned movements
|
|
104
|
+
if sp_up and breadth_up:
|
|
105
|
+
return 70, "Healthy alignment (both rising)"
|
|
106
|
+
if not sp_up and not breadth_up:
|
|
107
|
+
return 30, "Consistent decline (both falling)"
|
|
108
|
+
|
|
109
|
+
return 50, "Mixed signals"
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _generate_signal(
|
|
113
|
+
sp_pct: float, breadth_chg: float, div_type: str, score: int
|
|
114
|
+
) -> str:
|
|
115
|
+
"""Generate human-readable signal."""
|
|
116
|
+
return (
|
|
117
|
+
f"{div_type}: S&P {sp_pct:+.1f}%, "
|
|
118
|
+
f"Breadth 8MA {breadth_chg:+.3f} over 60d"
|
|
119
|
+
)
|
package/.claude/skills/market-breadth-analyzer/scripts/calculators/historical_context_calculator.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Component 5: Historical Percentile (Weight: 10%)
|
|
4
|
+
|
|
5
|
+
Determines where the current breadth level sits in the full historical distribution.
|
|
6
|
+
|
|
7
|
+
Input: Breadth_Index_8MA (all rows), Summary CSV (average peak/trough values)
|
|
8
|
+
|
|
9
|
+
Scoring (100 = healthy):
|
|
10
|
+
Percentile rank of current 8MA among all historical 8MA values:
|
|
11
|
+
>= 80th -> 90 >= 60th -> 70 >= 40th -> 50
|
|
12
|
+
>= 20th -> 30 < 20th -> 10
|
|
13
|
+
|
|
14
|
+
Adjustment:
|
|
15
|
+
Current >= avg_peak * 0.95 -> -10 (overheated caution)
|
|
16
|
+
Current <= avg_trough * 1.05 -> +10 (oversold zone)
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from typing import Dict, List, Optional
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def calculate_historical_percentile(
|
|
23
|
+
rows: List[Dict],
|
|
24
|
+
summary: Dict[str, str],
|
|
25
|
+
) -> Dict:
|
|
26
|
+
"""
|
|
27
|
+
Calculate historical percentile score.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
rows: All detail rows sorted by date ascending.
|
|
31
|
+
summary: Summary CSV as dict {Metric: Value}.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Dict with score, signal, and component details.
|
|
35
|
+
"""
|
|
36
|
+
if not rows:
|
|
37
|
+
return {
|
|
38
|
+
"score": 50,
|
|
39
|
+
"signal": "NO DATA: No data available for historical analysis",
|
|
40
|
+
"data_available": False,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# Extract all 8MA values
|
|
44
|
+
all_8ma = [r["Breadth_Index_8MA"] for r in rows]
|
|
45
|
+
current_8ma = all_8ma[-1]
|
|
46
|
+
|
|
47
|
+
# Calculate percentile rank
|
|
48
|
+
below_count = sum(1 for v in all_8ma if v < current_8ma)
|
|
49
|
+
percentile = (below_count / len(all_8ma)) * 100
|
|
50
|
+
|
|
51
|
+
# Base score from percentile
|
|
52
|
+
base_score = _score_percentile(percentile)
|
|
53
|
+
|
|
54
|
+
# Parse summary values
|
|
55
|
+
avg_peak = _safe_float(summary.get("Average Peaks (200MA)", ""))
|
|
56
|
+
avg_trough = _safe_float(summary.get("Average Troughs (8MA < 0.4)", ""))
|
|
57
|
+
|
|
58
|
+
# Adjustment for extremes
|
|
59
|
+
adjustment = 0
|
|
60
|
+
if avg_peak is not None and current_8ma >= avg_peak * 0.95:
|
|
61
|
+
adjustment = -10 # Overheated
|
|
62
|
+
elif avg_trough is not None and current_8ma <= avg_trough * 1.05:
|
|
63
|
+
adjustment = +10 # Oversold
|
|
64
|
+
|
|
65
|
+
score = round(base_score + adjustment)
|
|
66
|
+
score = max(0, min(100, score))
|
|
67
|
+
|
|
68
|
+
signal = _generate_signal(percentile, adjustment, score)
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
"score": score,
|
|
72
|
+
"signal": signal,
|
|
73
|
+
"data_available": True,
|
|
74
|
+
"current_8ma": current_8ma,
|
|
75
|
+
"percentile_rank": percentile,
|
|
76
|
+
"base_score": base_score,
|
|
77
|
+
"adjustment": adjustment,
|
|
78
|
+
"avg_peak": avg_peak,
|
|
79
|
+
"avg_trough": avg_trough,
|
|
80
|
+
"total_observations": len(all_8ma),
|
|
81
|
+
"date": rows[-1]["Date"],
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _score_percentile(percentile: float) -> int:
|
|
86
|
+
"""Score from percentile rank."""
|
|
87
|
+
if percentile >= 80:
|
|
88
|
+
return 90
|
|
89
|
+
elif percentile >= 60:
|
|
90
|
+
return 70
|
|
91
|
+
elif percentile >= 40:
|
|
92
|
+
return 50
|
|
93
|
+
elif percentile >= 20:
|
|
94
|
+
return 30
|
|
95
|
+
else:
|
|
96
|
+
return 10
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _safe_float(val: str) -> Optional[float]:
|
|
100
|
+
"""Safely parse float from string."""
|
|
101
|
+
try:
|
|
102
|
+
return float(val)
|
|
103
|
+
except (ValueError, TypeError):
|
|
104
|
+
return None
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _generate_signal(percentile: float, adjustment: int, score: int) -> str:
|
|
108
|
+
"""Generate human-readable signal."""
|
|
109
|
+
adj_str = ""
|
|
110
|
+
if adjustment < 0:
|
|
111
|
+
adj_str = " (near historical peak - overheated)"
|
|
112
|
+
elif adjustment > 0:
|
|
113
|
+
adj_str = " (near historical trough - oversold)"
|
|
114
|
+
|
|
115
|
+
if score >= 70:
|
|
116
|
+
return f"HIGH: {percentile:.0f}th percentile - above average breadth{adj_str}"
|
|
117
|
+
elif score >= 40:
|
|
118
|
+
return f"AVERAGE: {percentile:.0f}th percentile - normal range{adj_str}"
|
|
119
|
+
else:
|
|
120
|
+
return f"LOW: {percentile:.0f}th percentile - below average breadth{adj_str}"
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Component 2: 8MA vs 200MA Crossover Dynamics (Weight: 20%)
|
|
4
|
+
|
|
5
|
+
Detects momentum and direction changes via the gap between fast and slow MAs.
|
|
6
|
+
|
|
7
|
+
Input: Breadth_Index_8MA, Breadth_Index_200MA (latest + 5-day lookback)
|
|
8
|
+
|
|
9
|
+
Scoring (100 = healthy):
|
|
10
|
+
Gap = 8MA - 200MA
|
|
11
|
+
>= +0.15 -> 95 >= +0.10 -> 80 >= +0.05 -> 65
|
|
12
|
+
>= 0.00 -> 50 >= -0.05 -> 35 >= -0.10 -> 20
|
|
13
|
+
< -0.10 -> 5
|
|
14
|
+
|
|
15
|
+
Direction Modifier (5-day 8MA trend):
|
|
16
|
+
Gap < 0 and 8MA rising -> +10 (recovery signal)
|
|
17
|
+
Gap > 0 and 8MA falling -> -10 (deterioration signal)
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from typing import Dict, List
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def calculate_ma_crossover(rows: List[Dict]) -> Dict:
|
|
24
|
+
"""
|
|
25
|
+
Calculate 8MA vs 200MA crossover dynamics score.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
rows: All detail rows sorted by date ascending.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Dict with score, signal, and component details.
|
|
32
|
+
"""
|
|
33
|
+
if not rows or len(rows) < 6:
|
|
34
|
+
return {
|
|
35
|
+
"score": 50,
|
|
36
|
+
"signal": "NO DATA: Insufficient data for crossover analysis",
|
|
37
|
+
"data_available": False,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
latest = rows[-1]
|
|
41
|
+
ma8 = latest["Breadth_Index_8MA"]
|
|
42
|
+
ma200 = latest["Breadth_Index_200MA"]
|
|
43
|
+
gap = ma8 - ma200
|
|
44
|
+
|
|
45
|
+
# Gap score
|
|
46
|
+
gap_score = _score_gap(gap)
|
|
47
|
+
|
|
48
|
+
# 8MA direction over last 5 days
|
|
49
|
+
ma8_5d_ago = rows[-6]["Breadth_Index_8MA"]
|
|
50
|
+
ma8_rising = ma8 > ma8_5d_ago
|
|
51
|
+
ma8_direction = "rising" if ma8_rising else "falling"
|
|
52
|
+
|
|
53
|
+
# Direction modifier
|
|
54
|
+
direction_modifier = 0
|
|
55
|
+
if gap < 0 and ma8_rising:
|
|
56
|
+
direction_modifier = +10 # Recovery signal
|
|
57
|
+
elif gap > 0 and not ma8_rising:
|
|
58
|
+
direction_modifier = -10 # Deterioration signal
|
|
59
|
+
|
|
60
|
+
score = round(gap_score + direction_modifier)
|
|
61
|
+
score = max(0, min(100, score))
|
|
62
|
+
|
|
63
|
+
signal = _generate_signal(gap, ma8_direction, direction_modifier, score)
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
"score": score,
|
|
67
|
+
"signal": signal,
|
|
68
|
+
"data_available": True,
|
|
69
|
+
"gap": gap,
|
|
70
|
+
"gap_score": gap_score,
|
|
71
|
+
"current_8ma": ma8,
|
|
72
|
+
"current_200ma": ma200,
|
|
73
|
+
"ma8_5d_ago": ma8_5d_ago,
|
|
74
|
+
"ma8_direction": ma8_direction,
|
|
75
|
+
"direction_modifier": direction_modifier,
|
|
76
|
+
"date": latest["Date"],
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _score_gap(gap: float) -> int:
|
|
81
|
+
"""Score based on 8MA - 200MA gap."""
|
|
82
|
+
if gap >= 0.15:
|
|
83
|
+
return 95
|
|
84
|
+
elif gap >= 0.10:
|
|
85
|
+
return 80
|
|
86
|
+
elif gap >= 0.05:
|
|
87
|
+
return 65
|
|
88
|
+
elif gap >= 0.00:
|
|
89
|
+
return 50
|
|
90
|
+
elif gap >= -0.05:
|
|
91
|
+
return 35
|
|
92
|
+
elif gap >= -0.10:
|
|
93
|
+
return 20
|
|
94
|
+
else:
|
|
95
|
+
return 5
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _generate_signal(gap: float, direction: str, modifier: int, score: int) -> str:
|
|
99
|
+
"""Generate human-readable signal."""
|
|
100
|
+
mod_str = ""
|
|
101
|
+
if modifier > 0:
|
|
102
|
+
mod_str = " (recovery signal)"
|
|
103
|
+
elif modifier < 0:
|
|
104
|
+
mod_str = " (deterioration signal)"
|
|
105
|
+
|
|
106
|
+
if score >= 80:
|
|
107
|
+
return f"BULLISH: 8MA well above 200MA (gap={gap:+.3f}, 8MA {direction}){mod_str}"
|
|
108
|
+
elif score >= 60:
|
|
109
|
+
return f"POSITIVE: 8MA above 200MA (gap={gap:+.3f}, 8MA {direction}){mod_str}"
|
|
110
|
+
elif score >= 40:
|
|
111
|
+
return f"NEUTRAL: Near crossover (gap={gap:+.3f}, 8MA {direction}){mod_str}"
|
|
112
|
+
elif score >= 20:
|
|
113
|
+
return f"NEGATIVE: 8MA below 200MA (gap={gap:+.3f}, 8MA {direction}){mod_str}"
|
|
114
|
+
else:
|
|
115
|
+
return f"BEARISH: 8MA far below 200MA (gap={gap:+.3f}, 8MA {direction}){mod_str}"
|