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,380 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
L Component - Leadership / Relative Strength Calculator
|
|
4
|
+
|
|
5
|
+
Calculates CANSLIM 'L' component score based on relative price performance vs market.
|
|
6
|
+
|
|
7
|
+
O'Neil's Rule: "The top-performing stocks, prior to major advances, have Relative Strength
|
|
8
|
+
Ratings averaging 87 at pivot points. You want to see 80+ RS Rank - meaning the stock is
|
|
9
|
+
outperforming 80% of all other stocks over the past 52 weeks."
|
|
10
|
+
|
|
11
|
+
Key Principles:
|
|
12
|
+
- RS Rank 90+: Market leader, top 10% performer
|
|
13
|
+
- RS Rank 80-89: Strong performer, top 20%
|
|
14
|
+
- RS Rank 70-79: Above average, top 30%
|
|
15
|
+
- RS Rank 60-69: Average performer
|
|
16
|
+
- RS Rank <60: Laggard, underperforming majority
|
|
17
|
+
|
|
18
|
+
Implementation Note:
|
|
19
|
+
Since IBD's official RS Rank is proprietary, we estimate RS using:
|
|
20
|
+
1. 52-week price performance vs S&P 500
|
|
21
|
+
2. Sector-relative performance (optional enhancement)
|
|
22
|
+
|
|
23
|
+
Scoring:
|
|
24
|
+
- 100 points: Relative outperformance > +30% vs S&P 500 (market leader)
|
|
25
|
+
- 90 points: Relative outperformance +20% to +30%
|
|
26
|
+
- 80 points: Relative outperformance +10% to +20% (strong)
|
|
27
|
+
- 60 points: Relative outperformance 0% to +10% (above average)
|
|
28
|
+
- 40 points: Relative performance -10% to 0% (lagging)
|
|
29
|
+
- 20 points: Relative performance -20% to -10% (weak)
|
|
30
|
+
- 0 points: Relative underperformance > -20% (avoid)
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from typing import Dict, List, Optional
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def calculate_leadership(
|
|
37
|
+
historical_prices: List[Dict],
|
|
38
|
+
sp500_historical: Optional[List[Dict]] = None,
|
|
39
|
+
sp500_performance: Optional[float] = None
|
|
40
|
+
) -> Dict:
|
|
41
|
+
"""
|
|
42
|
+
Calculate Leadership/Relative Strength score (L component)
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
historical_prices: List of daily price data for the stock (oldest to newest)
|
|
46
|
+
Each entry: {"date": str, "close": float, "volume": int}
|
|
47
|
+
sp500_historical: Optional list of S&P 500 daily prices (for RS calculation)
|
|
48
|
+
sp500_performance: Optional pre-calculated S&P 500 52-week performance (%)
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Dict with:
|
|
52
|
+
- score: 0-100 points
|
|
53
|
+
- stock_52w_performance: Stock's 52-week return (%)
|
|
54
|
+
- sp500_52w_performance: S&P 500's 52-week return (%)
|
|
55
|
+
- relative_performance: Stock return - S&P 500 return (%)
|
|
56
|
+
- rs_rank_estimate: Estimated RS Rank (0-99)
|
|
57
|
+
- interpretation: Human-readable interpretation
|
|
58
|
+
- quality_warning: Warning if data quality issues
|
|
59
|
+
- error: Error message if calculation failed
|
|
60
|
+
|
|
61
|
+
Example:
|
|
62
|
+
>>> prices = client.get_historical_prices("NVDA", days=365)
|
|
63
|
+
>>> sp500 = client.get_historical_prices("SPY", days=365)
|
|
64
|
+
>>> result = calculate_leadership(prices, sp500)
|
|
65
|
+
>>> print(f"L Score: {result['score']}, RS Estimate: {result['rs_rank_estimate']}")
|
|
66
|
+
"""
|
|
67
|
+
# Validate input
|
|
68
|
+
if not historical_prices or len(historical_prices) < 50:
|
|
69
|
+
return {
|
|
70
|
+
"score": 0,
|
|
71
|
+
"error": "Insufficient historical price data (need 50+ days)",
|
|
72
|
+
"stock_52w_performance": None,
|
|
73
|
+
"sp500_52w_performance": None,
|
|
74
|
+
"relative_performance": None,
|
|
75
|
+
"rs_rank_estimate": None,
|
|
76
|
+
"interpretation": "Data unavailable"
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# Calculate stock's 52-week (or available period) performance
|
|
80
|
+
# Prices should be sorted oldest to newest
|
|
81
|
+
try:
|
|
82
|
+
# Get start and end prices (handle both orderings)
|
|
83
|
+
if historical_prices[0].get("date", "") < historical_prices[-1].get("date", ""):
|
|
84
|
+
# Oldest first (ascending)
|
|
85
|
+
start_price = historical_prices[0].get("close", 0)
|
|
86
|
+
end_price = historical_prices[-1].get("close", 0)
|
|
87
|
+
else:
|
|
88
|
+
# Newest first (descending)
|
|
89
|
+
start_price = historical_prices[-1].get("close", 0)
|
|
90
|
+
end_price = historical_prices[0].get("close", 0)
|
|
91
|
+
|
|
92
|
+
if start_price <= 0:
|
|
93
|
+
return {
|
|
94
|
+
"score": 0,
|
|
95
|
+
"error": "Invalid start price (zero or negative)",
|
|
96
|
+
"stock_52w_performance": None,
|
|
97
|
+
"sp500_52w_performance": None,
|
|
98
|
+
"relative_performance": None,
|
|
99
|
+
"rs_rank_estimate": None,
|
|
100
|
+
"interpretation": "Data quality issue"
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
stock_performance = ((end_price - start_price) / start_price) * 100
|
|
104
|
+
days_analyzed = len(historical_prices)
|
|
105
|
+
|
|
106
|
+
except (KeyError, TypeError, ZeroDivisionError) as e:
|
|
107
|
+
return {
|
|
108
|
+
"score": 0,
|
|
109
|
+
"error": f"Price calculation error: {str(e)}",
|
|
110
|
+
"stock_52w_performance": None,
|
|
111
|
+
"sp500_52w_performance": None,
|
|
112
|
+
"relative_performance": None,
|
|
113
|
+
"rs_rank_estimate": None,
|
|
114
|
+
"interpretation": "Calculation error"
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# Calculate S&P 500 performance for comparison
|
|
118
|
+
sp500_perf = None
|
|
119
|
+
quality_warning = None
|
|
120
|
+
|
|
121
|
+
if sp500_performance is not None:
|
|
122
|
+
# Use pre-calculated S&P 500 performance
|
|
123
|
+
sp500_perf = sp500_performance
|
|
124
|
+
elif sp500_historical and len(sp500_historical) >= 50:
|
|
125
|
+
# Calculate from provided S&P 500 data
|
|
126
|
+
try:
|
|
127
|
+
if sp500_historical[0].get("date", "") < sp500_historical[-1].get("date", ""):
|
|
128
|
+
sp500_start = sp500_historical[0].get("close", 0)
|
|
129
|
+
sp500_end = sp500_historical[-1].get("close", 0)
|
|
130
|
+
else:
|
|
131
|
+
sp500_start = sp500_historical[-1].get("close", 0)
|
|
132
|
+
sp500_end = sp500_historical[0].get("close", 0)
|
|
133
|
+
|
|
134
|
+
if sp500_start > 0:
|
|
135
|
+
sp500_perf = ((sp500_end - sp500_start) / sp500_start) * 100
|
|
136
|
+
except (KeyError, TypeError, ZeroDivisionError):
|
|
137
|
+
quality_warning = "S&P 500 performance calculation failed"
|
|
138
|
+
else:
|
|
139
|
+
quality_warning = "S&P 500 data unavailable - using absolute performance only"
|
|
140
|
+
|
|
141
|
+
# Calculate relative performance
|
|
142
|
+
if sp500_perf is not None:
|
|
143
|
+
relative_performance = stock_performance - sp500_perf
|
|
144
|
+
else:
|
|
145
|
+
# Fallback: use absolute performance with penalty
|
|
146
|
+
relative_performance = stock_performance
|
|
147
|
+
if quality_warning is None:
|
|
148
|
+
quality_warning = "Using absolute performance (S&P 500 comparison unavailable)"
|
|
149
|
+
|
|
150
|
+
# Score based on relative performance
|
|
151
|
+
score, rs_rank_estimate = score_leadership(relative_performance, sp500_perf is not None)
|
|
152
|
+
|
|
153
|
+
# Generate interpretation
|
|
154
|
+
interpretation = interpret_leadership(
|
|
155
|
+
score, stock_performance, sp500_perf, relative_performance, days_analyzed
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
"score": score,
|
|
160
|
+
"stock_52w_performance": round(stock_performance, 2),
|
|
161
|
+
"sp500_52w_performance": round(sp500_perf, 2) if sp500_perf is not None else None,
|
|
162
|
+
"relative_performance": round(relative_performance, 2),
|
|
163
|
+
"rs_rank_estimate": rs_rank_estimate,
|
|
164
|
+
"days_analyzed": days_analyzed,
|
|
165
|
+
"interpretation": interpretation,
|
|
166
|
+
"quality_warning": quality_warning,
|
|
167
|
+
"error": None
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def score_leadership(relative_performance: float, has_benchmark: bool) -> tuple:
|
|
172
|
+
"""
|
|
173
|
+
Score leadership based on relative performance
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
relative_performance: Stock performance minus S&P 500 performance (%)
|
|
177
|
+
has_benchmark: True if S&P 500 comparison was available
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
tuple: (score, rs_rank_estimate)
|
|
181
|
+
"""
|
|
182
|
+
# Scoring thresholds for relative performance
|
|
183
|
+
if relative_performance >= 50:
|
|
184
|
+
score = 100
|
|
185
|
+
rs_rank_estimate = 99 # Top 1%
|
|
186
|
+
elif relative_performance >= 30:
|
|
187
|
+
score = 95
|
|
188
|
+
rs_rank_estimate = 95 # Top 5%
|
|
189
|
+
elif relative_performance >= 20:
|
|
190
|
+
score = 90
|
|
191
|
+
rs_rank_estimate = 90 # Top 10%
|
|
192
|
+
elif relative_performance >= 10:
|
|
193
|
+
score = 80
|
|
194
|
+
rs_rank_estimate = 80 # Top 20%
|
|
195
|
+
elif relative_performance >= 5:
|
|
196
|
+
score = 70
|
|
197
|
+
rs_rank_estimate = 70 # Top 30%
|
|
198
|
+
elif relative_performance >= 0:
|
|
199
|
+
score = 60
|
|
200
|
+
rs_rank_estimate = 60 # Top 40%
|
|
201
|
+
elif relative_performance >= -5:
|
|
202
|
+
score = 50
|
|
203
|
+
rs_rank_estimate = 50 # Average
|
|
204
|
+
elif relative_performance >= -10:
|
|
205
|
+
score = 40
|
|
206
|
+
rs_rank_estimate = 40 # Below average
|
|
207
|
+
elif relative_performance >= -20:
|
|
208
|
+
score = 20
|
|
209
|
+
rs_rank_estimate = 25 # Weak
|
|
210
|
+
else:
|
|
211
|
+
score = 0
|
|
212
|
+
rs_rank_estimate = 10 # Laggard
|
|
213
|
+
|
|
214
|
+
# Apply penalty if no benchmark comparison available
|
|
215
|
+
if not has_benchmark:
|
|
216
|
+
score = int(score * 0.8) # 20% penalty for absolute-only comparison
|
|
217
|
+
rs_rank_estimate = int(rs_rank_estimate * 0.9)
|
|
218
|
+
|
|
219
|
+
return score, rs_rank_estimate
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def interpret_leadership(
|
|
223
|
+
score: int,
|
|
224
|
+
stock_performance: float,
|
|
225
|
+
sp500_performance: Optional[float],
|
|
226
|
+
relative_performance: float,
|
|
227
|
+
days_analyzed: int
|
|
228
|
+
) -> str:
|
|
229
|
+
"""
|
|
230
|
+
Generate human-readable interpretation
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
score: Leadership score (0-100)
|
|
234
|
+
stock_performance: Stock's period return (%)
|
|
235
|
+
sp500_performance: S&P 500's period return (%)
|
|
236
|
+
relative_performance: Stock - S&P 500 (%)
|
|
237
|
+
days_analyzed: Number of days analyzed
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
str: Interpretation string
|
|
241
|
+
"""
|
|
242
|
+
# Period description
|
|
243
|
+
if days_analyzed >= 250:
|
|
244
|
+
period = "52-week"
|
|
245
|
+
elif days_analyzed >= 180:
|
|
246
|
+
period = "9-month"
|
|
247
|
+
elif days_analyzed >= 90:
|
|
248
|
+
period = "quarterly"
|
|
249
|
+
else:
|
|
250
|
+
period = f"{days_analyzed}-day"
|
|
251
|
+
|
|
252
|
+
# Performance description
|
|
253
|
+
if stock_performance > 0:
|
|
254
|
+
stock_msg = f"+{stock_performance:.1f}%"
|
|
255
|
+
else:
|
|
256
|
+
stock_msg = f"{stock_performance:.1f}%"
|
|
257
|
+
|
|
258
|
+
# Relative performance description
|
|
259
|
+
if sp500_performance is not None:
|
|
260
|
+
if relative_performance > 0:
|
|
261
|
+
rel_msg = f"+{relative_performance:.1f}% vs S&P 500"
|
|
262
|
+
else:
|
|
263
|
+
rel_msg = f"{relative_performance:.1f}% vs S&P 500"
|
|
264
|
+
else:
|
|
265
|
+
rel_msg = "(absolute performance)"
|
|
266
|
+
|
|
267
|
+
# Rating based on score
|
|
268
|
+
if score >= 90:
|
|
269
|
+
rating = "Market Leader"
|
|
270
|
+
action = "Strong momentum, prime CANSLIM candidate"
|
|
271
|
+
elif score >= 80:
|
|
272
|
+
rating = "Strong Performer"
|
|
273
|
+
action = "Outperforming market significantly"
|
|
274
|
+
elif score >= 60:
|
|
275
|
+
rating = "Above Average"
|
|
276
|
+
action = "Slight outperformance"
|
|
277
|
+
elif score >= 40:
|
|
278
|
+
rating = "Average"
|
|
279
|
+
action = "Matching or slightly lagging market"
|
|
280
|
+
elif score >= 20:
|
|
281
|
+
rating = "Laggard"
|
|
282
|
+
action = "Underperforming market - caution"
|
|
283
|
+
else:
|
|
284
|
+
rating = "Weak"
|
|
285
|
+
action = "Significant underperformance - avoid"
|
|
286
|
+
|
|
287
|
+
return f"{rating} - {period} return {stock_msg} ({rel_msg}). {action}"
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def calculate_sector_relative_strength(
|
|
291
|
+
stock_performance: float,
|
|
292
|
+
sector_stocks_performance: List[float]
|
|
293
|
+
) -> Dict:
|
|
294
|
+
"""
|
|
295
|
+
Calculate relative strength within sector (optional enhancement)
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
stock_performance: Target stock's period return (%)
|
|
299
|
+
sector_stocks_performance: List of sector peers' period returns (%)
|
|
300
|
+
|
|
301
|
+
Returns:
|
|
302
|
+
Dict with:
|
|
303
|
+
- sector_rank: Stock's rank within sector (1 = best)
|
|
304
|
+
- sector_percentile: Percentile within sector (0-100)
|
|
305
|
+
- is_sector_leader: True if top 20% in sector
|
|
306
|
+
"""
|
|
307
|
+
if not sector_stocks_performance:
|
|
308
|
+
return {
|
|
309
|
+
"sector_rank": None,
|
|
310
|
+
"sector_percentile": None,
|
|
311
|
+
"is_sector_leader": False,
|
|
312
|
+
"error": "No sector data available"
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
# Add stock to list and sort (descending)
|
|
316
|
+
all_stocks = sector_stocks_performance + [stock_performance]
|
|
317
|
+
all_stocks_sorted = sorted(all_stocks, reverse=True)
|
|
318
|
+
|
|
319
|
+
# Find rank
|
|
320
|
+
rank = all_stocks_sorted.index(stock_performance) + 1
|
|
321
|
+
total = len(all_stocks)
|
|
322
|
+
percentile = ((total - rank) / total) * 100
|
|
323
|
+
|
|
324
|
+
return {
|
|
325
|
+
"sector_rank": rank,
|
|
326
|
+
"sector_total": total,
|
|
327
|
+
"sector_percentile": round(percentile, 1),
|
|
328
|
+
"is_sector_leader": percentile >= 80
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
# Example usage
|
|
333
|
+
if __name__ == "__main__":
|
|
334
|
+
print("Testing Leadership Calculator (L Component)...")
|
|
335
|
+
print()
|
|
336
|
+
|
|
337
|
+
# Test 1: Strong outperformer
|
|
338
|
+
sample_prices = [
|
|
339
|
+
{"date": "2024-01-01", "close": 100.0},
|
|
340
|
+
{"date": "2024-06-01", "close": 120.0},
|
|
341
|
+
{"date": "2025-01-01", "close": 180.0} # +80% YoY
|
|
342
|
+
]
|
|
343
|
+
sample_sp500 = [
|
|
344
|
+
{"date": "2024-01-01", "close": 4500.0},
|
|
345
|
+
{"date": "2024-06-01", "close": 4700.0},
|
|
346
|
+
{"date": "2025-01-01", "close": 5400.0} # +20% YoY
|
|
347
|
+
]
|
|
348
|
+
|
|
349
|
+
result = calculate_leadership(sample_prices, sample_sp500)
|
|
350
|
+
print("Test 1: Strong Outperformer (+80% stock vs +20% S&P)")
|
|
351
|
+
print(f" Stock Performance: {result['stock_52w_performance']}%")
|
|
352
|
+
print(f" S&P 500 Performance: {result['sp500_52w_performance']}%")
|
|
353
|
+
print(f" Relative Performance: {result['relative_performance']}%")
|
|
354
|
+
print(f" L Score: {result['score']}/100")
|
|
355
|
+
print(f" RS Rank Estimate: {result['rs_rank_estimate']}")
|
|
356
|
+
print(f" Interpretation: {result['interpretation']}")
|
|
357
|
+
print()
|
|
358
|
+
|
|
359
|
+
# Test 2: Underperformer
|
|
360
|
+
sample_prices_weak = [
|
|
361
|
+
{"date": "2024-01-01", "close": 100.0},
|
|
362
|
+
{"date": "2025-01-01", "close": 90.0} # -10% YoY
|
|
363
|
+
]
|
|
364
|
+
|
|
365
|
+
result2 = calculate_leadership(sample_prices_weak, sample_sp500)
|
|
366
|
+
print("Test 2: Underperformer (-10% stock vs +20% S&P)")
|
|
367
|
+
print(f" Stock Performance: {result2['stock_52w_performance']}%")
|
|
368
|
+
print(f" Relative Performance: {result2['relative_performance']}%")
|
|
369
|
+
print(f" L Score: {result2['score']}/100")
|
|
370
|
+
print(f" Interpretation: {result2['interpretation']}")
|
|
371
|
+
print()
|
|
372
|
+
|
|
373
|
+
# Test 3: Without S&P 500 data (fallback)
|
|
374
|
+
result3 = calculate_leadership(sample_prices)
|
|
375
|
+
print("Test 3: Without S&P 500 Data (Fallback)")
|
|
376
|
+
print(f" L Score: {result3['score']}/100 (20% penalty applied)")
|
|
377
|
+
print(f" Warning: {result3['quality_warning']}")
|
|
378
|
+
print()
|
|
379
|
+
|
|
380
|
+
print("✓ All tests completed")
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
M Component - Market Direction Calculator
|
|
4
|
+
|
|
5
|
+
Calculates CANSLIM 'M' component score based on overall market trend.
|
|
6
|
+
|
|
7
|
+
O'Neil's Rule: "You can be right about a stock but wrong about the market,
|
|
8
|
+
and still lose money. Three out of four stocks follow the market's trend."
|
|
9
|
+
|
|
10
|
+
Scoring:
|
|
11
|
+
- 100: Strong uptrend (S&P 500 >2% above 50-EMA) + VIX <15
|
|
12
|
+
- 80: Uptrend (S&P 500 above 50-EMA) + VIX <20
|
|
13
|
+
- 60: Early uptrend (S&P 500 just above 50-EMA)
|
|
14
|
+
- 40: Choppy/neutral (S&P 500 near 50-EMA ±2%)
|
|
15
|
+
- 20: Downtrend forming (S&P 500 below 50-EMA)
|
|
16
|
+
- 0: Bear market (S&P 500 well below 50-EMA OR VIX >30) - DO NOT BUY
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from typing import Dict, List, Optional
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def calculate_market_direction(sp500_quote: Dict,
|
|
23
|
+
sp500_prices: Optional[List[Dict]] = None,
|
|
24
|
+
vix_quote: Optional[Dict] = None) -> Dict:
|
|
25
|
+
"""
|
|
26
|
+
Calculate M component score based on S&P 500 trend and VIX
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
sp500_quote: S&P 500 quote data (symbol ^GSPC)
|
|
30
|
+
sp500_prices: Optional historical prices for EMA calculation
|
|
31
|
+
vix_quote: Optional VIX quote (symbol ^VIX)
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Dict with:
|
|
35
|
+
- score: 0-100 points
|
|
36
|
+
- trend: "strong_uptrend", "uptrend", "choppy", "downtrend", "bear_market"
|
|
37
|
+
- sp500_price: Current S&P 500 level
|
|
38
|
+
- sp500_ema_50: 50-day EMA (if calculated)
|
|
39
|
+
- distance_from_ema_pct: Distance from 50-EMA (%)
|
|
40
|
+
- vix_level: Current VIX level
|
|
41
|
+
- interpretation: Human-readable interpretation
|
|
42
|
+
- warning: Warning message if bear market (M=0)
|
|
43
|
+
"""
|
|
44
|
+
# Validate input
|
|
45
|
+
if not sp500_quote:
|
|
46
|
+
return {
|
|
47
|
+
"score": 50, # Default to neutral if data unavailable
|
|
48
|
+
"error": "S&P 500 quote data missing",
|
|
49
|
+
"trend": "unknown",
|
|
50
|
+
"interpretation": "Market data unavailable"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
sp500_price = sp500_quote.get("price")
|
|
54
|
+
if not sp500_price:
|
|
55
|
+
return {
|
|
56
|
+
"score": 50,
|
|
57
|
+
"error": "S&P 500 price missing",
|
|
58
|
+
"trend": "unknown",
|
|
59
|
+
"interpretation": "Market data unavailable"
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# Calculate or estimate 50-day EMA
|
|
63
|
+
sp500_ema_50 = None
|
|
64
|
+
if sp500_prices and len(sp500_prices) >= 50:
|
|
65
|
+
# Calculate from historical data
|
|
66
|
+
close_prices = [day.get("close") for day in sp500_prices if day.get("close")]
|
|
67
|
+
if len(close_prices) >= 50:
|
|
68
|
+
sp500_ema_50 = calculate_ema(close_prices, period=50)
|
|
69
|
+
else:
|
|
70
|
+
# Estimate: Assume EMA is ~2% below current price in uptrend, ~2% above in downtrend
|
|
71
|
+
# This is a simplified fallback when historical data unavailable
|
|
72
|
+
sp500_ema_50 = sp500_price * 0.98 # Conservative estimate
|
|
73
|
+
|
|
74
|
+
# Calculate distance from EMA
|
|
75
|
+
distance_from_ema_pct = ((sp500_price / sp500_ema_50) - 1) * 100 if sp500_ema_50 else 0
|
|
76
|
+
|
|
77
|
+
# Get VIX level
|
|
78
|
+
vix_level = None
|
|
79
|
+
if vix_quote:
|
|
80
|
+
vix_level = vix_quote.get("price")
|
|
81
|
+
|
|
82
|
+
# Determine trend
|
|
83
|
+
if distance_from_ema_pct >= 2.0:
|
|
84
|
+
trend = "strong_uptrend"
|
|
85
|
+
elif distance_from_ema_pct >= 0:
|
|
86
|
+
trend = "uptrend"
|
|
87
|
+
elif distance_from_ema_pct >= -2.0:
|
|
88
|
+
trend = "choppy"
|
|
89
|
+
elif distance_from_ema_pct >= -5.0:
|
|
90
|
+
trend = "downtrend"
|
|
91
|
+
else:
|
|
92
|
+
trend = "bear_market"
|
|
93
|
+
|
|
94
|
+
# Calculate score
|
|
95
|
+
score = score_market_direction(trend, vix_level)
|
|
96
|
+
|
|
97
|
+
# Generate interpretation
|
|
98
|
+
interpretation = interpret_market_score(score, trend, distance_from_ema_pct, vix_level)
|
|
99
|
+
|
|
100
|
+
# Warning for bear market
|
|
101
|
+
warning = None
|
|
102
|
+
if score == 0:
|
|
103
|
+
warning = ("⚠️ BEAR MARKET - Do not buy stocks regardless of C, A, N scores. "
|
|
104
|
+
"Raise 80-100% cash and wait for market recovery.")
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
"score": score,
|
|
108
|
+
"trend": trend,
|
|
109
|
+
"sp500_price": sp500_price,
|
|
110
|
+
"sp500_ema_50": sp500_ema_50,
|
|
111
|
+
"distance_from_ema_pct": round(distance_from_ema_pct, 2),
|
|
112
|
+
"vix_level": vix_level,
|
|
113
|
+
"interpretation": interpretation,
|
|
114
|
+
"warning": warning,
|
|
115
|
+
"error": None
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def calculate_ema(prices: List[float], period: int = 50) -> float:
|
|
120
|
+
"""
|
|
121
|
+
Calculate Exponential Moving Average
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
prices: List of prices (most recent first)
|
|
125
|
+
period: EMA period (default 50)
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
EMA value
|
|
129
|
+
|
|
130
|
+
Formula: EMA = Price * k + EMA_prev * (1-k), where k = 2/(period+1)
|
|
131
|
+
"""
|
|
132
|
+
if len(prices) < period:
|
|
133
|
+
return sum(prices) / len(prices) # Fallback to simple average
|
|
134
|
+
|
|
135
|
+
# Reverse to oldest-first for calculation
|
|
136
|
+
prices_reversed = prices[::-1]
|
|
137
|
+
|
|
138
|
+
# Initialize with SMA
|
|
139
|
+
sma = sum(prices_reversed[:period]) / period
|
|
140
|
+
ema = sma
|
|
141
|
+
|
|
142
|
+
# Calculate EMA
|
|
143
|
+
k = 2 / (period + 1)
|
|
144
|
+
for price in prices_reversed[period:]:
|
|
145
|
+
ema = price * k + ema * (1 - k)
|
|
146
|
+
|
|
147
|
+
return ema
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def score_market_direction(trend: str, vix_level: Optional[float]) -> int:
|
|
151
|
+
"""
|
|
152
|
+
Score M component based on trend and VIX
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
trend: Market trend classification
|
|
156
|
+
vix_level: Current VIX level
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
Score (0-100)
|
|
160
|
+
"""
|
|
161
|
+
# Base score from trend
|
|
162
|
+
if trend == "strong_uptrend":
|
|
163
|
+
base_score = 90
|
|
164
|
+
elif trend == "uptrend":
|
|
165
|
+
base_score = 70
|
|
166
|
+
elif trend == "choppy":
|
|
167
|
+
base_score = 40
|
|
168
|
+
elif trend == "downtrend":
|
|
169
|
+
base_score = 20
|
|
170
|
+
else: # bear_market
|
|
171
|
+
base_score = 0
|
|
172
|
+
|
|
173
|
+
# VIX adjustments
|
|
174
|
+
if vix_level:
|
|
175
|
+
if vix_level < 15:
|
|
176
|
+
base_score += 10 # Low fear, bullish
|
|
177
|
+
elif vix_level > 30:
|
|
178
|
+
base_score = 0 # Panic mode - override trend
|
|
179
|
+
|
|
180
|
+
return min(max(base_score, 0), 100)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def interpret_market_score(score: int,
|
|
184
|
+
trend: str,
|
|
185
|
+
distance: float,
|
|
186
|
+
vix: Optional[float]) -> str:
|
|
187
|
+
"""
|
|
188
|
+
Generate human-readable market interpretation
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
score: M component score
|
|
192
|
+
trend: Trend classification
|
|
193
|
+
distance: Distance from 50-EMA (%)
|
|
194
|
+
vix: VIX level
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
Interpretation string
|
|
198
|
+
"""
|
|
199
|
+
vix_text = f", VIX {vix:.1f}" if vix else ""
|
|
200
|
+
|
|
201
|
+
if score >= 90:
|
|
202
|
+
return f"Strong bull market - Aggressive buying recommended (S&P 500 {distance:+.1f}% from 50-EMA{vix_text})"
|
|
203
|
+
elif score >= 70:
|
|
204
|
+
return f"Bull market - Standard position sizing (S&P 500 {distance:+.1f}% from 50-EMA{vix_text})"
|
|
205
|
+
elif score >= 50:
|
|
206
|
+
return f"Early uptrend - Small initial positions (S&P 500 {distance:+.1f}% from 50-EMA{vix_text})"
|
|
207
|
+
elif score >= 30:
|
|
208
|
+
return f"Choppy/neutral - Reduce exposure, be selective (S&P 500 {distance:+.1f}% from 50-EMA{vix_text})"
|
|
209
|
+
elif score >= 10:
|
|
210
|
+
return f"Downtrend forming - Defensive posture (S&P 500 {distance:+.1f}% from 50-EMA{vix_text})"
|
|
211
|
+
else:
|
|
212
|
+
return f"Bear market - Raise 80-100% cash, DO NOT BUY (S&P 500 {distance:+.1f}% from 50-EMA{vix_text})"
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
# Example usage
|
|
216
|
+
if __name__ == "__main__":
|
|
217
|
+
print("Testing Market Calculator (M Component)...\n")
|
|
218
|
+
|
|
219
|
+
# Test 1: Strong uptrend (score 100)
|
|
220
|
+
test_sp500_1 = {"symbol": "^GSPC", "price": 4900}
|
|
221
|
+
test_vix_1 = {"symbol": "^VIX", "price": 12.5}
|
|
222
|
+
result1 = calculate_market_direction(test_sp500_1, vix_quote=test_vix_1)
|
|
223
|
+
print(f"Test 1: Strong Uptrend - Score: {result1['score']}")
|
|
224
|
+
print(f" {result1['interpretation']}\n")
|
|
225
|
+
|
|
226
|
+
# Test 2: Choppy market (score 40)
|
|
227
|
+
test_sp500_2 = {"symbol": "^GSPC", "price": 4700}
|
|
228
|
+
# EMA estimated at 4750 (price 1% below)
|
|
229
|
+
test_vix_2 = {"symbol": "^VIX", "price": 18.0}
|
|
230
|
+
result2 = calculate_market_direction(test_sp500_2, vix_quote=test_vix_2)
|
|
231
|
+
print(f"Test 2: Choppy Market - Score: {result2['score']}")
|
|
232
|
+
print(f" {result2['interpretation']}\n")
|
|
233
|
+
|
|
234
|
+
# Test 3: Bear market (score 0)
|
|
235
|
+
test_sp500_3 = {"symbol": "^GSPC", "price": 4200}
|
|
236
|
+
# EMA estimated at 4600 (price 8.7% below)
|
|
237
|
+
test_vix_3 = {"symbol": "^VIX", "price": 32.0}
|
|
238
|
+
result3 = calculate_market_direction(test_sp500_3, vix_quote=test_vix_3)
|
|
239
|
+
print(f"Test 3: Bear Market - Score: {result3['score']}")
|
|
240
|
+
print(f" {result3['interpretation']}")
|
|
241
|
+
if result3['warning']:
|
|
242
|
+
print(f" {result3['warning']}\n")
|
|
243
|
+
|
|
244
|
+
print("✓ All tests completed")
|