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,329 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Uptrend Analyzer - Report Generator
|
|
4
|
+
|
|
5
|
+
Generates JSON and Markdown reports for uptrend breadth analysis.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from typing import Dict
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def generate_json_report(analysis: Dict, output_file: str):
|
|
13
|
+
"""Save full analysis as JSON"""
|
|
14
|
+
with open(output_file, 'w') as f:
|
|
15
|
+
json.dump(analysis, f, indent=2, default=str)
|
|
16
|
+
print(f"JSON report saved to: {output_file}")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def generate_markdown_report(analysis: Dict, output_file: str):
|
|
20
|
+
"""Generate comprehensive Markdown report"""
|
|
21
|
+
lines = []
|
|
22
|
+
composite = analysis.get("composite", {})
|
|
23
|
+
components = analysis.get("components", {})
|
|
24
|
+
metadata = analysis.get("metadata", {})
|
|
25
|
+
|
|
26
|
+
score = composite.get("composite_score", 0)
|
|
27
|
+
zone = composite.get("zone", "Unknown")
|
|
28
|
+
exposure = composite.get("exposure_guidance", "N/A")
|
|
29
|
+
|
|
30
|
+
# Header
|
|
31
|
+
lines.append("# Uptrend Analyzer Report")
|
|
32
|
+
lines.append("")
|
|
33
|
+
lines.append(f"**Generated:** {metadata.get('generated_at', 'N/A')}")
|
|
34
|
+
lines.append(f"**Data Source:** Monty's Uptrend Ratio Dashboard (GitHub CSV)")
|
|
35
|
+
lines.append(f"**API Key Required:** No")
|
|
36
|
+
lines.append("")
|
|
37
|
+
|
|
38
|
+
# Overall Assessment
|
|
39
|
+
lines.append("---")
|
|
40
|
+
lines.append("")
|
|
41
|
+
lines.append("## Overall Assessment")
|
|
42
|
+
lines.append("")
|
|
43
|
+
zone_emoji = _zone_emoji(composite.get("zone_color", ""))
|
|
44
|
+
lines.append("| Metric | Value |")
|
|
45
|
+
lines.append("|--------|-------|")
|
|
46
|
+
lines.append(f"| **Composite Score** | **{score}/100** |")
|
|
47
|
+
lines.append(f"| **Zone** | {zone_emoji} {zone} |")
|
|
48
|
+
lines.append(f"| **Exposure Guidance** | {exposure} |")
|
|
49
|
+
lines.append(f"| **Strongest Component** | {composite.get('strongest_component', {}).get('label', 'N/A')} "
|
|
50
|
+
f"({composite.get('strongest_component', {}).get('score', 0)}/100) |")
|
|
51
|
+
lines.append(f"| **Weakest Component** | {composite.get('weakest_component', {}).get('label', 'N/A')} "
|
|
52
|
+
f"({composite.get('weakest_component', {}).get('score', 0)}/100) |")
|
|
53
|
+
dq = composite.get("data_quality", {})
|
|
54
|
+
if dq:
|
|
55
|
+
lines.append(f"| **Data Quality** | {dq.get('label', 'N/A')} |")
|
|
56
|
+
lines.append("")
|
|
57
|
+
|
|
58
|
+
# Guidance
|
|
59
|
+
lines.append(f"> **Guidance:** {composite.get('guidance', '')}")
|
|
60
|
+
lines.append("")
|
|
61
|
+
|
|
62
|
+
# Current Market Snapshot
|
|
63
|
+
breadth = components.get("market_breadth", {})
|
|
64
|
+
if breadth.get("data_available"):
|
|
65
|
+
lines.append("---")
|
|
66
|
+
lines.append("")
|
|
67
|
+
lines.append("## Current Market Snapshot")
|
|
68
|
+
lines.append("")
|
|
69
|
+
lines.append("| Metric | Value |")
|
|
70
|
+
lines.append("|--------|-------|")
|
|
71
|
+
lines.append(f"| Uptrend Ratio | {breadth.get('ratio_pct', 'N/A')}% |")
|
|
72
|
+
lines.append(f"| 10-Day MA | {breadth.get('ma_10_pct', 'N/A')}% |")
|
|
73
|
+
lines.append(f"| Trend | {breadth.get('trend', 'N/A')} |")
|
|
74
|
+
lines.append(f"| Slope | {_format_slope(breadth.get('slope'))} |")
|
|
75
|
+
lines.append(f"| Distance from 37% (Overbought) | {_format_distance(breadth.get('distance_from_upper'))} |")
|
|
76
|
+
lines.append(f"| Distance from 9.7% (Oversold) | {_format_distance(breadth.get('distance_from_lower'))} |")
|
|
77
|
+
lines.append(f"| Date | {breadth.get('date', 'N/A')} |")
|
|
78
|
+
lines.append("")
|
|
79
|
+
|
|
80
|
+
# Component Scores Table
|
|
81
|
+
lines.append("---")
|
|
82
|
+
lines.append("")
|
|
83
|
+
lines.append("## Component Scores")
|
|
84
|
+
lines.append("")
|
|
85
|
+
lines.append("| # | Component | Weight | Score | Contribution | Signal |")
|
|
86
|
+
lines.append("|---|-----------|--------|-------|--------------|--------|")
|
|
87
|
+
|
|
88
|
+
component_order = [
|
|
89
|
+
"market_breadth", "sector_participation", "sector_rotation",
|
|
90
|
+
"momentum", "historical_context",
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
for i, key in enumerate(component_order, 1):
|
|
94
|
+
comp = composite.get("component_scores", {}).get(key, {})
|
|
95
|
+
detail = components.get(key, {})
|
|
96
|
+
signal = detail.get("signal", "N/A")
|
|
97
|
+
score_val = comp.get("score", 0)
|
|
98
|
+
weight_pct = f"{comp.get('weight', 0)*100:.0f}%"
|
|
99
|
+
contribution = comp.get("weighted_contribution", 0)
|
|
100
|
+
bar = _score_bar(score_val)
|
|
101
|
+
|
|
102
|
+
lines.append(f"| {i} | **{comp.get('label', key)}** | {weight_pct} | "
|
|
103
|
+
f"{bar} {score_val} | {contribution:.1f} | {signal} |")
|
|
104
|
+
|
|
105
|
+
lines.append("")
|
|
106
|
+
|
|
107
|
+
# Component Details
|
|
108
|
+
lines.append("---")
|
|
109
|
+
lines.append("")
|
|
110
|
+
lines.append("## Component Details")
|
|
111
|
+
lines.append("")
|
|
112
|
+
|
|
113
|
+
# 1. Market Breadth
|
|
114
|
+
lines.append("### 1. Market Breadth (Overall)")
|
|
115
|
+
lines.append("")
|
|
116
|
+
if breadth.get("data_available"):
|
|
117
|
+
lines.append(f"- **Uptrend Ratio:** {breadth.get('ratio_pct', 'N/A')}%")
|
|
118
|
+
lines.append(f"- **10-Day MA:** {breadth.get('ma_10_pct', 'N/A')}%")
|
|
119
|
+
lines.append(f"- **Trend:** {breadth.get('trend', 'N/A')}")
|
|
120
|
+
lines.append(f"- **Slope:** {_format_slope(breadth.get('slope'))}")
|
|
121
|
+
lines.append(f"- **Trend Adjustment:** {breadth.get('trend_adjustment', 0):+d}")
|
|
122
|
+
else:
|
|
123
|
+
lines.append("- Data unavailable")
|
|
124
|
+
lines.append("")
|
|
125
|
+
|
|
126
|
+
# 2. Sector Participation
|
|
127
|
+
participation = components.get("sector_participation", {})
|
|
128
|
+
lines.append("### 2. Sector Participation")
|
|
129
|
+
lines.append("")
|
|
130
|
+
if participation.get("data_available"):
|
|
131
|
+
lines.append(f"- **Uptrending Sectors:** {participation.get('uptrend_count', 0)}"
|
|
132
|
+
f"/{participation.get('total_sectors', 0)}")
|
|
133
|
+
lines.append(f"- **Count Score:** {participation.get('count_score', 0)}/100")
|
|
134
|
+
lines.append(f"- **Spread:** {participation.get('spread_pct', 'N/A')}% "
|
|
135
|
+
f"(score: {participation.get('spread_score', 0)}/100)")
|
|
136
|
+
lines.append(f"- **Overbought ({'>'}37%):** {participation.get('overbought_count', 0)} "
|
|
137
|
+
f"sectors ({', '.join(participation.get('overbought_sectors', []))})")
|
|
138
|
+
lines.append(f"- **Oversold ({'<'}9.7%):** {participation.get('oversold_count', 0)} "
|
|
139
|
+
f"sectors ({', '.join(participation.get('oversold_sectors', []))})")
|
|
140
|
+
else:
|
|
141
|
+
lines.append("- Data unavailable")
|
|
142
|
+
lines.append("")
|
|
143
|
+
|
|
144
|
+
# 3. Sector Rotation
|
|
145
|
+
rotation = components.get("sector_rotation", {})
|
|
146
|
+
lines.append("### 3. Sector Rotation")
|
|
147
|
+
lines.append("")
|
|
148
|
+
if rotation.get("data_available"):
|
|
149
|
+
lines.append(f"- **Cyclical Avg:** {rotation.get('cyclical_avg_pct', 'N/A')}%")
|
|
150
|
+
lines.append(f"- **Defensive Avg:** {rotation.get('defensive_avg_pct', 'N/A')}%")
|
|
151
|
+
lines.append(f"- **Commodity Avg:** {rotation.get('commodity_avg_pct', 'N/A')}%")
|
|
152
|
+
lines.append(f"- **Cyclical-Defensive Gap:** {rotation.get('difference_pct', 'N/A')}pp")
|
|
153
|
+
if rotation.get("late_cycle_flag"):
|
|
154
|
+
lines.append(f"- **Late Cycle Warning:** YES (commodity penalty: {rotation.get('commodity_penalty', 0)})")
|
|
155
|
+
|
|
156
|
+
# Group detail tables
|
|
157
|
+
for group_name, group_key in [("Cyclical", "cyclical_details"),
|
|
158
|
+
("Defensive", "defensive_details"),
|
|
159
|
+
("Commodity", "commodity_details")]:
|
|
160
|
+
details = rotation.get(group_key, [])
|
|
161
|
+
if details:
|
|
162
|
+
lines.append(f"\n**{group_name} Sectors:**")
|
|
163
|
+
lines.append("")
|
|
164
|
+
lines.append("| Sector | Ratio | Trend | Slope |")
|
|
165
|
+
lines.append("|--------|-------|-------|-------|")
|
|
166
|
+
for d in details:
|
|
167
|
+
lines.append(f"| {d.get('sector', '')} | "
|
|
168
|
+
f"{d.get('ratio_pct', 'N/A')}% | "
|
|
169
|
+
f"{d.get('trend', 'N/A')} | "
|
|
170
|
+
f"{_format_slope(d.get('slope'))} |")
|
|
171
|
+
lines.append("")
|
|
172
|
+
else:
|
|
173
|
+
lines.append("- Data unavailable")
|
|
174
|
+
lines.append("")
|
|
175
|
+
|
|
176
|
+
# 4. Momentum
|
|
177
|
+
momentum = components.get("momentum", {})
|
|
178
|
+
lines.append("### 4. Momentum")
|
|
179
|
+
lines.append("")
|
|
180
|
+
if momentum.get("data_available"):
|
|
181
|
+
lines.append(f"- **Current Slope:** {_format_slope(momentum.get('slope'))} "
|
|
182
|
+
f"(score: {momentum.get('slope_score', 0)}/100)")
|
|
183
|
+
lines.append(f"- **Acceleration:** {momentum.get('acceleration', 'N/A')} "
|
|
184
|
+
f"({momentum.get('acceleration_label', 'N/A')}, "
|
|
185
|
+
f"score: {momentum.get('acceleration_score', 0)}/100)")
|
|
186
|
+
lines.append(f"- **Sector Slope Breadth:** "
|
|
187
|
+
f"{momentum.get('sector_positive_slope_count', 0)}"
|
|
188
|
+
f"/{momentum.get('sector_total', 0)} positive "
|
|
189
|
+
f"(score: {momentum.get('sector_slope_breadth_score', 0)}/100)")
|
|
190
|
+
else:
|
|
191
|
+
lines.append("- Data unavailable")
|
|
192
|
+
lines.append("")
|
|
193
|
+
|
|
194
|
+
# 5. Historical Context
|
|
195
|
+
historical = components.get("historical_context", {})
|
|
196
|
+
lines.append("### 5. Historical Context")
|
|
197
|
+
lines.append("")
|
|
198
|
+
if historical.get("data_available"):
|
|
199
|
+
lines.append(f"- **Current Ratio:** {historical.get('current_ratio_pct', 'N/A')}%")
|
|
200
|
+
lines.append(f"- **Percentile Rank:** {historical.get('percentile', 'N/A')}th")
|
|
201
|
+
lines.append(f"- **Historical Range:** "
|
|
202
|
+
f"{historical.get('historical_min_pct', 'N/A')}% - "
|
|
203
|
+
f"{historical.get('historical_max_pct', 'N/A')}%")
|
|
204
|
+
lines.append(f"- **Historical Median:** {historical.get('historical_median_pct', 'N/A')}%")
|
|
205
|
+
lines.append(f"- **30-Day Avg:** {historical.get('avg_30d_pct', 'N/A')}%")
|
|
206
|
+
lines.append(f"- **90-Day Avg:** {historical.get('avg_90d_pct', 'N/A')}%")
|
|
207
|
+
lines.append(f"- **Data Points:** {historical.get('data_points', 0)} "
|
|
208
|
+
f"({historical.get('date_range', 'N/A')})")
|
|
209
|
+
else:
|
|
210
|
+
lines.append("- Data unavailable")
|
|
211
|
+
lines.append("")
|
|
212
|
+
|
|
213
|
+
# Sector Heatmap Table
|
|
214
|
+
sector_details = components.get("sector_participation", {}).get("sector_details", [])
|
|
215
|
+
if sector_details:
|
|
216
|
+
lines.append("---")
|
|
217
|
+
lines.append("")
|
|
218
|
+
lines.append("## Sector Heatmap")
|
|
219
|
+
lines.append("")
|
|
220
|
+
lines.append("| Rank | Sector | Ratio | 10MA | Trend | Slope | Status |")
|
|
221
|
+
lines.append("|------|--------|-------|------|-------|-------|--------|")
|
|
222
|
+
for i, s in enumerate(sector_details, 1):
|
|
223
|
+
lines.append(f"| {i} | {s.get('sector', '')} | "
|
|
224
|
+
f"{s.get('ratio_pct', 'N/A')}% | "
|
|
225
|
+
f"{round(s['ma_10'] * 100, 1) if s.get('ma_10') is not None else 'N/A'}% | "
|
|
226
|
+
f"{s.get('trend', '')} | "
|
|
227
|
+
f"{_format_slope(s.get('slope'))} | "
|
|
228
|
+
f"{s.get('status', '')} |")
|
|
229
|
+
lines.append("")
|
|
230
|
+
|
|
231
|
+
# Recommended Actions
|
|
232
|
+
lines.append("---")
|
|
233
|
+
lines.append("")
|
|
234
|
+
lines.append("## Recommended Actions")
|
|
235
|
+
lines.append("")
|
|
236
|
+
lines.append(f"**Zone:** {zone}")
|
|
237
|
+
lines.append(f"**Exposure Guidance:** {exposure}")
|
|
238
|
+
lines.append("")
|
|
239
|
+
for action in composite.get("actions", []):
|
|
240
|
+
lines.append(f"- {action}")
|
|
241
|
+
lines.append("")
|
|
242
|
+
|
|
243
|
+
# Active Warnings (overlay adjustments)
|
|
244
|
+
active_warnings = composite.get("active_warnings", [])
|
|
245
|
+
if active_warnings:
|
|
246
|
+
lines.append("### Active Warnings")
|
|
247
|
+
lines.append("")
|
|
248
|
+
for warning in active_warnings:
|
|
249
|
+
lines.append(f"**{warning.get('label', 'WARNING')}**")
|
|
250
|
+
lines.append(f"> {warning.get('description', '')}")
|
|
251
|
+
lines.append("")
|
|
252
|
+
for action in warning.get("actions", []):
|
|
253
|
+
lines.append(f"- {action}")
|
|
254
|
+
lines.append("")
|
|
255
|
+
|
|
256
|
+
# Methodology
|
|
257
|
+
lines.append("---")
|
|
258
|
+
lines.append("")
|
|
259
|
+
lines.append("## Methodology")
|
|
260
|
+
lines.append("")
|
|
261
|
+
lines.append("This analysis uses Monty's Uptrend Ratio Dashboard data to assess market breadth health.")
|
|
262
|
+
lines.append("The dashboard tracks ~2,800 US stocks across 11 sectors, measuring the percentage in uptrends.")
|
|
263
|
+
lines.append("")
|
|
264
|
+
lines.append("**5-Component Scoring System (0-100, higher = healthier):**")
|
|
265
|
+
lines.append("")
|
|
266
|
+
lines.append("1. **Market Breadth (30%):** Overall uptrend ratio level and trend direction")
|
|
267
|
+
lines.append("2. **Sector Participation (25%):** Number of uptrending sectors and spread uniformity")
|
|
268
|
+
lines.append("3. **Sector Rotation (15%):** Cyclical vs Defensive vs Commodity balance")
|
|
269
|
+
lines.append("4. **Momentum (20%):** Slope direction, acceleration, and sector slope breadth")
|
|
270
|
+
lines.append("5. **Historical Context (10%):** Percentile rank in historical distribution")
|
|
271
|
+
lines.append("")
|
|
272
|
+
lines.append("**Key Thresholds (Monty's Dashboard):** Overbought = 37%, Oversold = 9.7%")
|
|
273
|
+
lines.append("")
|
|
274
|
+
lines.append("For detailed methodology, see `references/uptrend_methodology.md`.")
|
|
275
|
+
lines.append("")
|
|
276
|
+
|
|
277
|
+
# Disclaimer
|
|
278
|
+
lines.append("---")
|
|
279
|
+
lines.append("")
|
|
280
|
+
lines.append("**Disclaimer:** This analysis is for educational and informational purposes only. "
|
|
281
|
+
"Not investment advice. Past patterns may not predict future outcomes. "
|
|
282
|
+
"Conduct your own research and consult a financial advisor before making "
|
|
283
|
+
"investment decisions.")
|
|
284
|
+
lines.append("")
|
|
285
|
+
|
|
286
|
+
with open(output_file, 'w') as f:
|
|
287
|
+
f.write('\n'.join(lines))
|
|
288
|
+
|
|
289
|
+
print(f"Markdown report saved to: {output_file}")
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def _zone_emoji(color: str) -> str:
|
|
293
|
+
mapping = {
|
|
294
|
+
"green": "🟢",
|
|
295
|
+
"light_green": "🟢",
|
|
296
|
+
"yellow": "🟡",
|
|
297
|
+
"orange": "🟠",
|
|
298
|
+
"red": "🔴",
|
|
299
|
+
}
|
|
300
|
+
return mapping.get(color, "⚪")
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def _score_bar(score: int) -> str:
|
|
304
|
+
"""Simple text bar for score visualization"""
|
|
305
|
+
if score >= 80:
|
|
306
|
+
return "████"
|
|
307
|
+
elif score >= 60:
|
|
308
|
+
return "███░"
|
|
309
|
+
elif score >= 40:
|
|
310
|
+
return "██░░"
|
|
311
|
+
elif score >= 20:
|
|
312
|
+
return "█░░░"
|
|
313
|
+
else:
|
|
314
|
+
return "░░░░"
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def _format_slope(value) -> str:
|
|
318
|
+
"""Format slope value with 4 decimal places."""
|
|
319
|
+
if value is None:
|
|
320
|
+
return "N/A"
|
|
321
|
+
return f"{value:+.4f}"
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def _format_distance(value) -> str:
|
|
325
|
+
"""Format distance value as percentage points."""
|
|
326
|
+
if value is None:
|
|
327
|
+
return "N/A"
|
|
328
|
+
pct = round(value * 100, 1)
|
|
329
|
+
return f"{pct:+.1f}pp"
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Uptrend Analyzer - Composite Scoring Engine
|
|
4
|
+
|
|
5
|
+
Combines 5 component scores into a weighted composite (0-100).
|
|
6
|
+
Higher score = healthier market (opposite of Market Top Detector).
|
|
7
|
+
|
|
8
|
+
Component Weights:
|
|
9
|
+
1. Market Breadth (Overall): 30%
|
|
10
|
+
2. Sector Participation: 25%
|
|
11
|
+
3. Sector Rotation: 15%
|
|
12
|
+
4. Momentum: 20%
|
|
13
|
+
5. Historical Context: 10%
|
|
14
|
+
Total: 100%
|
|
15
|
+
|
|
16
|
+
Scoring Zones (higher = better):
|
|
17
|
+
80-100: Strong Bull - Full Exposure (100%)
|
|
18
|
+
60-79: Bull - Normal Exposure (80-100%)
|
|
19
|
+
40-59: Neutral - Reduced Exposure (60-80%)
|
|
20
|
+
20-39: Cautious - Defensive (30-60%)
|
|
21
|
+
0-19: Bear - Capital Preservation (0-30%)
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from typing import Dict, List, Optional
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
COMPONENT_WEIGHTS = {
|
|
28
|
+
"market_breadth": 0.30,
|
|
29
|
+
"sector_participation": 0.25,
|
|
30
|
+
"sector_rotation": 0.15,
|
|
31
|
+
"momentum": 0.20,
|
|
32
|
+
"historical_context": 0.10,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
COMPONENT_LABELS = {
|
|
36
|
+
"market_breadth": "Market Breadth (Overall)",
|
|
37
|
+
"sector_participation": "Sector Participation",
|
|
38
|
+
"sector_rotation": "Sector Rotation",
|
|
39
|
+
"momentum": "Momentum",
|
|
40
|
+
"historical_context": "Historical Context",
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def calculate_composite_score(component_scores: Dict[str, float],
|
|
45
|
+
data_availability: Optional[Dict[str, bool]] = None,
|
|
46
|
+
warning_flags: Optional[Dict[str, bool]] = None) -> Dict:
|
|
47
|
+
"""
|
|
48
|
+
Calculate weighted composite market health score.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
component_scores: Dict with keys matching COMPONENT_WEIGHTS,
|
|
52
|
+
each value 0-100
|
|
53
|
+
data_availability: Optional dict mapping component key -> bool indicating
|
|
54
|
+
if data was actually available (vs neutral default)
|
|
55
|
+
warning_flags: Optional dict of component warning flags, e.g.
|
|
56
|
+
{"late_cycle": True, "high_spread": True}
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
Dict with composite_score, zone, exposure_guidance, guidance,
|
|
60
|
+
strongest/weakest components, component breakdown, data_quality,
|
|
61
|
+
and active_warnings
|
|
62
|
+
"""
|
|
63
|
+
if data_availability is None:
|
|
64
|
+
data_availability = {}
|
|
65
|
+
if warning_flags is None:
|
|
66
|
+
warning_flags = {}
|
|
67
|
+
|
|
68
|
+
# Calculate weighted composite
|
|
69
|
+
composite = 0.0
|
|
70
|
+
for key, weight in COMPONENT_WEIGHTS.items():
|
|
71
|
+
score = component_scores.get(key, 0)
|
|
72
|
+
composite += score * weight
|
|
73
|
+
|
|
74
|
+
composite = round(composite, 1)
|
|
75
|
+
|
|
76
|
+
# Identify strongest and weakest components
|
|
77
|
+
valid_scores = {k: v for k, v in component_scores.items()
|
|
78
|
+
if k in COMPONENT_WEIGHTS}
|
|
79
|
+
|
|
80
|
+
if valid_scores:
|
|
81
|
+
strongest = max(valid_scores, key=valid_scores.get)
|
|
82
|
+
weakest = min(valid_scores, key=valid_scores.get)
|
|
83
|
+
else:
|
|
84
|
+
strongest = "N/A"
|
|
85
|
+
weakest = "N/A"
|
|
86
|
+
|
|
87
|
+
# Get zone interpretation
|
|
88
|
+
zone_info = _interpret_zone(composite)
|
|
89
|
+
|
|
90
|
+
# Overlay warning-specific adjustments
|
|
91
|
+
active_warnings = _apply_warning_overlays(zone_info, warning_flags)
|
|
92
|
+
|
|
93
|
+
# Calculate data quality
|
|
94
|
+
available_count = sum(
|
|
95
|
+
1 for k in COMPONENT_WEIGHTS
|
|
96
|
+
if data_availability.get(k, True)
|
|
97
|
+
)
|
|
98
|
+
total_components = len(COMPONENT_WEIGHTS)
|
|
99
|
+
missing_components = [
|
|
100
|
+
COMPONENT_LABELS[k] for k in COMPONENT_WEIGHTS
|
|
101
|
+
if not data_availability.get(k, True)
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
if available_count == total_components:
|
|
105
|
+
quality_label = f"Complete ({available_count}/{total_components} components)"
|
|
106
|
+
elif available_count >= total_components - 1:
|
|
107
|
+
quality_label = (f"Partial ({available_count}/{total_components} components)"
|
|
108
|
+
" - interpret with caution")
|
|
109
|
+
else:
|
|
110
|
+
quality_label = (f"Limited ({available_count}/{total_components} components)"
|
|
111
|
+
" - low confidence")
|
|
112
|
+
|
|
113
|
+
data_quality = {
|
|
114
|
+
"available_count": available_count,
|
|
115
|
+
"total_components": total_components,
|
|
116
|
+
"label": quality_label,
|
|
117
|
+
"missing_components": missing_components,
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
"composite_score": composite,
|
|
122
|
+
"zone": zone_info["zone"],
|
|
123
|
+
"zone_color": zone_info["color"],
|
|
124
|
+
"exposure_guidance": zone_info["exposure_guidance"],
|
|
125
|
+
"guidance": zone_info["guidance"],
|
|
126
|
+
"actions": zone_info["actions"],
|
|
127
|
+
"active_warnings": active_warnings,
|
|
128
|
+
"strongest_component": {
|
|
129
|
+
"component": strongest,
|
|
130
|
+
"label": COMPONENT_LABELS.get(strongest, strongest),
|
|
131
|
+
"score": valid_scores.get(strongest, 0),
|
|
132
|
+
},
|
|
133
|
+
"weakest_component": {
|
|
134
|
+
"component": weakest,
|
|
135
|
+
"label": COMPONENT_LABELS.get(weakest, weakest),
|
|
136
|
+
"score": valid_scores.get(weakest, 0),
|
|
137
|
+
},
|
|
138
|
+
"data_quality": data_quality,
|
|
139
|
+
"component_scores": {
|
|
140
|
+
k: {
|
|
141
|
+
"score": component_scores.get(k, 0),
|
|
142
|
+
"weight": w,
|
|
143
|
+
"weighted_contribution": round(component_scores.get(k, 0) * w, 1),
|
|
144
|
+
"label": COMPONENT_LABELS[k],
|
|
145
|
+
}
|
|
146
|
+
for k, w in COMPONENT_WEIGHTS.items()
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _apply_warning_overlays(zone_info: Dict,
|
|
152
|
+
warning_flags: Dict[str, bool]) -> List[Dict]:
|
|
153
|
+
"""Apply warning-driven adjustments to zone guidance and actions.
|
|
154
|
+
|
|
155
|
+
When component-level warnings (e.g. late_cycle, high_spread) are active,
|
|
156
|
+
tighten the exposure guidance and prepend cautionary actions even if the
|
|
157
|
+
composite score places the market in a bullish zone.
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
List of active warning dicts with label, description, and extra actions
|
|
161
|
+
"""
|
|
162
|
+
active = []
|
|
163
|
+
|
|
164
|
+
if warning_flags.get("late_cycle"):
|
|
165
|
+
active.append({
|
|
166
|
+
"flag": "late_cycle",
|
|
167
|
+
"label": "LATE CYCLE WARNING",
|
|
168
|
+
"description": ("Commodity sectors leading both cyclical and defensive groups. "
|
|
169
|
+
"Historically associated with late-cycle inflation or sector rotation "
|
|
170
|
+
"preceding broader market weakness."),
|
|
171
|
+
"actions": [
|
|
172
|
+
"Favor lower end of exposure range (e.g. 80% if guidance is 80-100%)",
|
|
173
|
+
"New entries limited to A-grade setups only",
|
|
174
|
+
"Tighten stops on commodity/cyclical positions",
|
|
175
|
+
"Monitor for commodity rollover as potential broad market lead indicator",
|
|
176
|
+
],
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
if warning_flags.get("high_spread"):
|
|
180
|
+
active.append({
|
|
181
|
+
"flag": "high_spread",
|
|
182
|
+
"label": "HIGH SELECTIVITY WARNING",
|
|
183
|
+
"description": ("Wide spread between strongest and weakest sectors indicates "
|
|
184
|
+
"highly selective market. Breadth may be masking narrowing leadership."),
|
|
185
|
+
"actions": [
|
|
186
|
+
"Concentrate on sectors with ratio above 10MA",
|
|
187
|
+
"Avoid lagging sectors even if trend is nominally 'up'",
|
|
188
|
+
"Reduce position count to highest-conviction ideas",
|
|
189
|
+
],
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
# If any warning is active, tighten exposure guidance for bullish zones
|
|
193
|
+
if active and zone_info["zone"] in ("Strong Bull", "Bull"):
|
|
194
|
+
original = zone_info["exposure_guidance"]
|
|
195
|
+
if zone_info["zone"] == "Strong Bull":
|
|
196
|
+
zone_info["exposure_guidance"] = "Full Exposure with Caution (90-100%)"
|
|
197
|
+
elif zone_info["zone"] == "Bull":
|
|
198
|
+
zone_info["exposure_guidance"] = "Normal Exposure, Lower End (80-90%)"
|
|
199
|
+
zone_info["guidance"] += (
|
|
200
|
+
f" However, active warnings suggest operating at the conservative "
|
|
201
|
+
f"end of the range."
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
return active
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _interpret_zone(composite: float) -> Dict:
|
|
208
|
+
"""Map composite score to health zone (higher = healthier)"""
|
|
209
|
+
if composite >= 80:
|
|
210
|
+
return {
|
|
211
|
+
"zone": "Strong Bull",
|
|
212
|
+
"color": "green",
|
|
213
|
+
"exposure_guidance": "Full Exposure (100%)",
|
|
214
|
+
"guidance": "Broad market participation with strong momentum. Ideal environment for new positions.",
|
|
215
|
+
"actions": [
|
|
216
|
+
"Full equity exposure allowed",
|
|
217
|
+
"Aggressive position sizing for breakout entries",
|
|
218
|
+
"Add to winning positions on pullbacks",
|
|
219
|
+
"Minimal hedging needed",
|
|
220
|
+
],
|
|
221
|
+
}
|
|
222
|
+
elif composite >= 60:
|
|
223
|
+
return {
|
|
224
|
+
"zone": "Bull",
|
|
225
|
+
"color": "light_green",
|
|
226
|
+
"exposure_guidance": "Normal Exposure (80-100%)",
|
|
227
|
+
"guidance": "Healthy market breadth supporting equity allocation. Standard position management.",
|
|
228
|
+
"actions": [
|
|
229
|
+
"Normal position sizing",
|
|
230
|
+
"New entries on quality setups",
|
|
231
|
+
"Standard stop-loss levels",
|
|
232
|
+
"Monitor sector rotation for early warnings",
|
|
233
|
+
],
|
|
234
|
+
}
|
|
235
|
+
elif composite >= 40:
|
|
236
|
+
return {
|
|
237
|
+
"zone": "Neutral",
|
|
238
|
+
"color": "yellow",
|
|
239
|
+
"exposure_guidance": "Reduced Exposure (60-80%)",
|
|
240
|
+
"guidance": "Mixed signals. Participate selectively with tighter risk controls.",
|
|
241
|
+
"actions": [
|
|
242
|
+
"Reduce position sizes by 20-30%",
|
|
243
|
+
"Focus on strongest sectors only",
|
|
244
|
+
"Tighten stop-losses",
|
|
245
|
+
"Avoid low-quality setups",
|
|
246
|
+
"Increase cash allocation gradually",
|
|
247
|
+
],
|
|
248
|
+
}
|
|
249
|
+
elif composite >= 20:
|
|
250
|
+
return {
|
|
251
|
+
"zone": "Cautious",
|
|
252
|
+
"color": "orange",
|
|
253
|
+
"exposure_guidance": "Defensive (30-60%)",
|
|
254
|
+
"guidance": "Weak breadth environment. Prioritize capital preservation over gains.",
|
|
255
|
+
"actions": [
|
|
256
|
+
"Significant cash allocation (40-70%)",
|
|
257
|
+
"Only hold strongest leaders in uptrending sectors",
|
|
258
|
+
"Tight stops on all positions",
|
|
259
|
+
"Consider defensive sector allocation",
|
|
260
|
+
"No new aggressive entries",
|
|
261
|
+
],
|
|
262
|
+
}
|
|
263
|
+
else:
|
|
264
|
+
return {
|
|
265
|
+
"zone": "Bear",
|
|
266
|
+
"color": "red",
|
|
267
|
+
"exposure_guidance": "Capital Preservation (0-30%)",
|
|
268
|
+
"guidance": "Severe breadth deterioration. Maximum defensive posture.",
|
|
269
|
+
"actions": [
|
|
270
|
+
"Maximum cash (70-100%)",
|
|
271
|
+
"Exit most equity positions",
|
|
272
|
+
"Only ultra-high-conviction holdings",
|
|
273
|
+
"Consider hedges (inverse ETFs, puts)",
|
|
274
|
+
"Wait for breadth recovery before re-entry",
|
|
275
|
+
],
|
|
276
|
+
}
|