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,625 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
CANSLIM Composite Scoring Engine - Phase 1 MVP
|
|
4
|
+
|
|
5
|
+
Combines individual component scores (C, A, N, M) into weighted composite score.
|
|
6
|
+
|
|
7
|
+
Phase 1 MVP Weights:
|
|
8
|
+
- C (Current Earnings): 27%
|
|
9
|
+
- A (Annual Growth): 36%
|
|
10
|
+
- N (Newness): 27%
|
|
11
|
+
- M (Market Direction): 10%
|
|
12
|
+
Total: 100%
|
|
13
|
+
|
|
14
|
+
Interpretation Bands:
|
|
15
|
+
- 90-100: Exceptional+ (rare multi-bagger setup)
|
|
16
|
+
- 80-89: Exceptional (outstanding fundamentals)
|
|
17
|
+
- 70-79: Strong (solid across all components)
|
|
18
|
+
- 60-69: Above Average (meets thresholds)
|
|
19
|
+
- 50-59: Average (marginal)
|
|
20
|
+
- 40-49: Below Average (one or more weak)
|
|
21
|
+
- <40: Weak (fails CANSLIM criteria)
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from typing import Dict
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# Phase 1 MVP component weights (renormalized from original CANSLIM)
|
|
28
|
+
WEIGHTS_PHASE1 = {
|
|
29
|
+
"C": 0.27, # Current Earnings (original 15%, renormalized to 27%)
|
|
30
|
+
"A": 0.36, # Annual Growth (original 20%, renormalized to 36%)
|
|
31
|
+
"N": 0.27, # Newness (original 15%, renormalized to 27%)
|
|
32
|
+
"M": 0.10 # Market Direction (original 5%, renormalized to 10%)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# Phase 2 component weights (6 components, renormalized excluding L)
|
|
36
|
+
WEIGHTS_PHASE2 = {
|
|
37
|
+
"C": 0.19, # Current Earnings (15% / 0.80 = 0.1875 ≈ 0.19)
|
|
38
|
+
"A": 0.25, # Annual Growth (20% / 0.80 = 0.25)
|
|
39
|
+
"N": 0.19, # Newness (15% / 0.80 = 0.1875 ≈ 0.19)
|
|
40
|
+
"S": 0.19, # Supply/Demand (15% / 0.80 = 0.1875 ≈ 0.19)
|
|
41
|
+
"I": 0.13, # Institutional (10% / 0.80 = 0.125 ≈ 0.13)
|
|
42
|
+
"M": 0.06 # Market Direction (5% / 0.80 = 0.0625 ≈ 0.06)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# Phase 3 component weights (7 components, FULL CANSLIM - Original O'Neil weights)
|
|
46
|
+
WEIGHTS_PHASE3 = {
|
|
47
|
+
"C": 0.15, # Current Earnings - 15%
|
|
48
|
+
"A": 0.20, # Annual Growth - 20%
|
|
49
|
+
"N": 0.15, # Newness - 15%
|
|
50
|
+
"S": 0.15, # Supply/Demand - 15%
|
|
51
|
+
"L": 0.20, # Leadership/RS Rank - 20% (LARGEST component!)
|
|
52
|
+
"I": 0.10, # Institutional - 10%
|
|
53
|
+
"M": 0.05 # Market Direction - 5%
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def calculate_composite_score(c_score: float,
|
|
58
|
+
a_score: float,
|
|
59
|
+
n_score: float,
|
|
60
|
+
m_score: float) -> Dict:
|
|
61
|
+
"""
|
|
62
|
+
Calculate weighted composite CANSLIM score for Phase 1 MVP
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
c_score: Current Earnings component score (0-100)
|
|
66
|
+
a_score: Annual Growth component score (0-100)
|
|
67
|
+
n_score: Newness component score (0-100)
|
|
68
|
+
m_score: Market Direction component score (0-100)
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Dict with:
|
|
72
|
+
- composite_score: Weighted average (0-100)
|
|
73
|
+
- rating: "Exceptional+", "Exceptional", "Strong", etc.
|
|
74
|
+
- rating_description: What the rating means
|
|
75
|
+
- guidance: Recommended action
|
|
76
|
+
- weakest_component: Component with lowest score
|
|
77
|
+
- weakest_score: Score of weakest component
|
|
78
|
+
|
|
79
|
+
Example:
|
|
80
|
+
>>> result = calculate_composite_score(c_score=95, a_score=90, n_score=88, m_score=100)
|
|
81
|
+
>>> print(f"{result['composite_score']:.1f} - {result['rating']}")
|
|
82
|
+
91.2 - Exceptional+
|
|
83
|
+
"""
|
|
84
|
+
# Calculate weighted composite
|
|
85
|
+
composite = (
|
|
86
|
+
c_score * WEIGHTS_PHASE1["C"] +
|
|
87
|
+
a_score * WEIGHTS_PHASE1["A"] +
|
|
88
|
+
n_score * WEIGHTS_PHASE1["N"] +
|
|
89
|
+
m_score * WEIGHTS_PHASE1["M"]
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# Identify weakest component
|
|
93
|
+
components = {"C": c_score, "A": a_score, "N": n_score, "M": m_score}
|
|
94
|
+
weakest_component = min(components, key=components.get)
|
|
95
|
+
weakest_score = components[weakest_component]
|
|
96
|
+
|
|
97
|
+
# Get rating and interpretation
|
|
98
|
+
rating_info = interpret_composite_score(composite)
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
"composite_score": round(composite, 1),
|
|
102
|
+
"rating": rating_info["rating"],
|
|
103
|
+
"rating_description": rating_info["description"],
|
|
104
|
+
"guidance": rating_info["guidance"],
|
|
105
|
+
"weakest_component": weakest_component,
|
|
106
|
+
"weakest_score": weakest_score,
|
|
107
|
+
"component_scores": {
|
|
108
|
+
"C": c_score,
|
|
109
|
+
"A": a_score,
|
|
110
|
+
"N": n_score,
|
|
111
|
+
"M": m_score
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def interpret_composite_score(composite: float) -> Dict:
|
|
117
|
+
"""
|
|
118
|
+
Interpret composite score and provide rating/guidance
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
composite: Composite score (0-100)
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
Dict with rating, description, guidance
|
|
125
|
+
"""
|
|
126
|
+
if composite >= 90:
|
|
127
|
+
return {
|
|
128
|
+
"rating": "Exceptional+",
|
|
129
|
+
"description": "Rare multi-bagger setup - all components near-perfect",
|
|
130
|
+
"guidance": "Immediate buy, aggressive position sizing (15-20% of portfolio)"
|
|
131
|
+
}
|
|
132
|
+
elif composite >= 80:
|
|
133
|
+
return {
|
|
134
|
+
"rating": "Exceptional",
|
|
135
|
+
"description": "Outstanding fundamentals + strong momentum",
|
|
136
|
+
"guidance": "Strong buy, standard sizing (10-15% of portfolio)"
|
|
137
|
+
}
|
|
138
|
+
elif composite >= 70:
|
|
139
|
+
return {
|
|
140
|
+
"rating": "Strong",
|
|
141
|
+
"description": "Solid across all components, minor weaknesses",
|
|
142
|
+
"guidance": "Buy, standard sizing (8-12% of portfolio)"
|
|
143
|
+
}
|
|
144
|
+
elif composite >= 60:
|
|
145
|
+
return {
|
|
146
|
+
"rating": "Above Average",
|
|
147
|
+
"description": "Meets thresholds, one component weak",
|
|
148
|
+
"guidance": "Buy on pullback, conservative sizing (5-8% of portfolio)"
|
|
149
|
+
}
|
|
150
|
+
elif composite >= 50:
|
|
151
|
+
return {
|
|
152
|
+
"rating": "Average",
|
|
153
|
+
"description": "Marginal CANSLIM candidate",
|
|
154
|
+
"guidance": "Watchlist only, consider 3-5% if high conviction"
|
|
155
|
+
}
|
|
156
|
+
elif composite >= 40:
|
|
157
|
+
return {
|
|
158
|
+
"rating": "Below Average",
|
|
159
|
+
"description": "Fails one or more key thresholds",
|
|
160
|
+
"guidance": "Monitor, do not buy"
|
|
161
|
+
}
|
|
162
|
+
else:
|
|
163
|
+
return {
|
|
164
|
+
"rating": "Weak",
|
|
165
|
+
"description": "Does not meet CANSLIM criteria",
|
|
166
|
+
"guidance": "Avoid"
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def check_minimum_thresholds(c_score: float,
|
|
171
|
+
a_score: float,
|
|
172
|
+
n_score: float,
|
|
173
|
+
m_score: float) -> Dict:
|
|
174
|
+
"""
|
|
175
|
+
Check if stock meets minimum CANSLIM thresholds
|
|
176
|
+
|
|
177
|
+
Minimum thresholds for "buy" consideration:
|
|
178
|
+
- C >= 60 (18%+ quarterly EPS growth)
|
|
179
|
+
- A >= 50 (25%+ annual EPS CAGR)
|
|
180
|
+
- N >= 40 (within 25% of 52-week high)
|
|
181
|
+
- M >= 40 (market not in downtrend)
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
c_score, a_score, n_score, m_score: Component scores
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
Dict with:
|
|
188
|
+
- passes_all: Boolean - True if all thresholds met
|
|
189
|
+
- failed_components: List of components below threshold
|
|
190
|
+
- recommendation: "buy", "watchlist", or "avoid"
|
|
191
|
+
"""
|
|
192
|
+
thresholds = {"C": 60, "A": 50, "N": 40, "M": 40}
|
|
193
|
+
scores = {"C": c_score, "A": a_score, "N": n_score, "M": m_score}
|
|
194
|
+
|
|
195
|
+
failed = [comp for comp, threshold in thresholds.items()
|
|
196
|
+
if scores[comp] < threshold]
|
|
197
|
+
|
|
198
|
+
# Special case: M score = 0 (bear market) overrides everything
|
|
199
|
+
if m_score == 0:
|
|
200
|
+
return {
|
|
201
|
+
"passes_all": False,
|
|
202
|
+
"failed_components": ["M"],
|
|
203
|
+
"recommendation": "avoid",
|
|
204
|
+
"reason": "Bear market - M component = 0. Do NOT buy regardless of C, A, N scores."
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if not failed:
|
|
208
|
+
return {
|
|
209
|
+
"passes_all": True,
|
|
210
|
+
"failed_components": [],
|
|
211
|
+
"recommendation": "buy",
|
|
212
|
+
"reason": "All minimum thresholds met"
|
|
213
|
+
}
|
|
214
|
+
elif len(failed) == 1:
|
|
215
|
+
return {
|
|
216
|
+
"passes_all": False,
|
|
217
|
+
"failed_components": failed,
|
|
218
|
+
"recommendation": "watchlist",
|
|
219
|
+
"reason": f"One component below threshold: {failed[0]}"
|
|
220
|
+
}
|
|
221
|
+
else:
|
|
222
|
+
return {
|
|
223
|
+
"passes_all": False,
|
|
224
|
+
"failed_components": failed,
|
|
225
|
+
"recommendation": "avoid",
|
|
226
|
+
"reason": f"Multiple components below threshold: {', '.join(failed)}"
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def calculate_composite_score_phase2(c_score: float,
|
|
231
|
+
a_score: float,
|
|
232
|
+
n_score: float,
|
|
233
|
+
s_score: float,
|
|
234
|
+
i_score: float,
|
|
235
|
+
m_score: float) -> Dict:
|
|
236
|
+
"""
|
|
237
|
+
Calculate weighted composite CANSLIM score for Phase 2 (6 components)
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
c_score: Current Earnings component score (0-100)
|
|
241
|
+
a_score: Annual Growth component score (0-100)
|
|
242
|
+
n_score: Newness component score (0-100)
|
|
243
|
+
s_score: Supply/Demand component score (0-100)
|
|
244
|
+
i_score: Institutional component score (0-100)
|
|
245
|
+
m_score: Market Direction component score (0-100)
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
Dict with:
|
|
249
|
+
- composite_score: Weighted average (0-100)
|
|
250
|
+
- rating: "Exceptional+", "Exceptional", "Strong", etc.
|
|
251
|
+
- rating_description: What the rating means
|
|
252
|
+
- guidance: Recommended action
|
|
253
|
+
- weakest_component: Component with lowest score
|
|
254
|
+
- weakest_score: Score of weakest component
|
|
255
|
+
|
|
256
|
+
Example:
|
|
257
|
+
>>> result = calculate_composite_score_phase2(
|
|
258
|
+
... c_score=95, a_score=90, n_score=88, s_score=85, i_score=92, m_score=100
|
|
259
|
+
... )
|
|
260
|
+
>>> print(f"{result['composite_score']:.1f} - {result['rating']}")
|
|
261
|
+
91.0 - Exceptional+
|
|
262
|
+
"""
|
|
263
|
+
# Calculate weighted composite
|
|
264
|
+
composite = (
|
|
265
|
+
c_score * WEIGHTS_PHASE2["C"] +
|
|
266
|
+
a_score * WEIGHTS_PHASE2["A"] +
|
|
267
|
+
n_score * WEIGHTS_PHASE2["N"] +
|
|
268
|
+
s_score * WEIGHTS_PHASE2["S"] +
|
|
269
|
+
i_score * WEIGHTS_PHASE2["I"] +
|
|
270
|
+
m_score * WEIGHTS_PHASE2["M"]
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
# Identify weakest component
|
|
274
|
+
components = {
|
|
275
|
+
"C": c_score, "A": a_score, "N": n_score,
|
|
276
|
+
"S": s_score, "I": i_score, "M": m_score
|
|
277
|
+
}
|
|
278
|
+
weakest_component = min(components, key=components.get)
|
|
279
|
+
weakest_score = components[weakest_component]
|
|
280
|
+
|
|
281
|
+
# Get rating and interpretation
|
|
282
|
+
rating_info = interpret_composite_score(composite)
|
|
283
|
+
|
|
284
|
+
return {
|
|
285
|
+
"composite_score": round(composite, 1),
|
|
286
|
+
"rating": rating_info["rating"],
|
|
287
|
+
"rating_description": rating_info["description"],
|
|
288
|
+
"guidance": rating_info["guidance"],
|
|
289
|
+
"weakest_component": weakest_component,
|
|
290
|
+
"weakest_score": weakest_score,
|
|
291
|
+
"component_scores": {
|
|
292
|
+
"C": c_score,
|
|
293
|
+
"A": a_score,
|
|
294
|
+
"N": n_score,
|
|
295
|
+
"S": s_score,
|
|
296
|
+
"I": i_score,
|
|
297
|
+
"M": m_score
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def check_minimum_thresholds_phase2(c_score: float,
|
|
303
|
+
a_score: float,
|
|
304
|
+
n_score: float,
|
|
305
|
+
s_score: float,
|
|
306
|
+
i_score: float,
|
|
307
|
+
m_score: float) -> Dict:
|
|
308
|
+
"""
|
|
309
|
+
Check if stock meets minimum CANSLIM thresholds (Phase 2: 6 components)
|
|
310
|
+
|
|
311
|
+
Minimum thresholds for "buy" consideration:
|
|
312
|
+
- C >= 60 (18%+ quarterly EPS growth)
|
|
313
|
+
- A >= 50 (25%+ annual EPS CAGR)
|
|
314
|
+
- N >= 40 (within 25% of 52-week high)
|
|
315
|
+
- S >= 40 (accumulation pattern, ratio ≥ 1.0)
|
|
316
|
+
- I >= 40 (30+ holders or 20%+ ownership)
|
|
317
|
+
- M >= 40 (market not in downtrend)
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
c_score, a_score, n_score, s_score, i_score, m_score: Component scores
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
Dict with:
|
|
324
|
+
- passes_all: Boolean - True if all thresholds met
|
|
325
|
+
- failed_components: List of components below threshold
|
|
326
|
+
- recommendation: "buy", "watchlist", or "avoid"
|
|
327
|
+
"""
|
|
328
|
+
thresholds = {"C": 60, "A": 50, "N": 40, "S": 40, "I": 40, "M": 40}
|
|
329
|
+
scores = {
|
|
330
|
+
"C": c_score, "A": a_score, "N": n_score,
|
|
331
|
+
"S": s_score, "I": i_score, "M": m_score
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
failed = [comp for comp, threshold in thresholds.items()
|
|
335
|
+
if scores[comp] < threshold]
|
|
336
|
+
|
|
337
|
+
# Special case: M score = 0 (bear market) overrides everything
|
|
338
|
+
if m_score == 0:
|
|
339
|
+
return {
|
|
340
|
+
"passes_all": False,
|
|
341
|
+
"failed_components": ["M"],
|
|
342
|
+
"recommendation": "avoid",
|
|
343
|
+
"reason": "Bear market - M component = 0. Do NOT buy regardless of other scores."
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if not failed:
|
|
347
|
+
return {
|
|
348
|
+
"passes_all": True,
|
|
349
|
+
"failed_components": [],
|
|
350
|
+
"recommendation": "buy",
|
|
351
|
+
"reason": "All minimum thresholds met"
|
|
352
|
+
}
|
|
353
|
+
elif len(failed) == 1:
|
|
354
|
+
return {
|
|
355
|
+
"passes_all": False,
|
|
356
|
+
"failed_components": failed,
|
|
357
|
+
"recommendation": "watchlist",
|
|
358
|
+
"reason": f"One component below threshold: {failed[0]}"
|
|
359
|
+
}
|
|
360
|
+
else:
|
|
361
|
+
return {
|
|
362
|
+
"passes_all": False,
|
|
363
|
+
"failed_components": failed,
|
|
364
|
+
"recommendation": "avoid",
|
|
365
|
+
"reason": f"Multiple components below threshold: {', '.join(failed)}"
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def calculate_composite_score_phase3(c_score: float,
|
|
370
|
+
a_score: float,
|
|
371
|
+
n_score: float,
|
|
372
|
+
s_score: float,
|
|
373
|
+
l_score: float,
|
|
374
|
+
i_score: float,
|
|
375
|
+
m_score: float) -> Dict:
|
|
376
|
+
"""
|
|
377
|
+
Calculate weighted composite CANSLIM score for Phase 3 (FULL 7 components)
|
|
378
|
+
|
|
379
|
+
This is the complete CANSLIM implementation with O'Neil's original weights.
|
|
380
|
+
|
|
381
|
+
Args:
|
|
382
|
+
c_score: Current Earnings component score (0-100)
|
|
383
|
+
a_score: Annual Growth component score (0-100)
|
|
384
|
+
n_score: Newness component score (0-100)
|
|
385
|
+
s_score: Supply/Demand component score (0-100)
|
|
386
|
+
l_score: Leadership/RS Rank component score (0-100)
|
|
387
|
+
i_score: Institutional component score (0-100)
|
|
388
|
+
m_score: Market Direction component score (0-100)
|
|
389
|
+
|
|
390
|
+
Returns:
|
|
391
|
+
Dict with:
|
|
392
|
+
- composite_score: Weighted average (0-100)
|
|
393
|
+
- rating: "Exceptional+", "Exceptional", "Strong", etc.
|
|
394
|
+
- rating_description: What the rating means
|
|
395
|
+
- guidance: Recommended action
|
|
396
|
+
- weakest_component: Component with lowest score
|
|
397
|
+
- weakest_score: Score of weakest component
|
|
398
|
+
|
|
399
|
+
Example:
|
|
400
|
+
>>> result = calculate_composite_score_phase3(
|
|
401
|
+
... c_score=95, a_score=90, n_score=88, s_score=85,
|
|
402
|
+
... l_score=92, i_score=80, m_score=100
|
|
403
|
+
... )
|
|
404
|
+
>>> print(f"{result['composite_score']:.1f} - {result['rating']}")
|
|
405
|
+
89.7 - Exceptional
|
|
406
|
+
"""
|
|
407
|
+
# Calculate weighted composite using FULL CANSLIM weights
|
|
408
|
+
composite = (
|
|
409
|
+
c_score * WEIGHTS_PHASE3["C"] +
|
|
410
|
+
a_score * WEIGHTS_PHASE3["A"] +
|
|
411
|
+
n_score * WEIGHTS_PHASE3["N"] +
|
|
412
|
+
s_score * WEIGHTS_PHASE3["S"] +
|
|
413
|
+
l_score * WEIGHTS_PHASE3["L"] +
|
|
414
|
+
i_score * WEIGHTS_PHASE3["I"] +
|
|
415
|
+
m_score * WEIGHTS_PHASE3["M"]
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
# Identify weakest component
|
|
419
|
+
components = {
|
|
420
|
+
"C": c_score, "A": a_score, "N": n_score,
|
|
421
|
+
"S": s_score, "L": l_score, "I": i_score, "M": m_score
|
|
422
|
+
}
|
|
423
|
+
weakest_component = min(components, key=components.get)
|
|
424
|
+
weakest_score = components[weakest_component]
|
|
425
|
+
|
|
426
|
+
# Get rating and interpretation
|
|
427
|
+
rating_info = interpret_composite_score(composite)
|
|
428
|
+
|
|
429
|
+
return {
|
|
430
|
+
"composite_score": round(composite, 1),
|
|
431
|
+
"rating": rating_info["rating"],
|
|
432
|
+
"rating_description": rating_info["description"],
|
|
433
|
+
"guidance": rating_info["guidance"],
|
|
434
|
+
"weakest_component": weakest_component,
|
|
435
|
+
"weakest_score": weakest_score,
|
|
436
|
+
"component_scores": {
|
|
437
|
+
"C": c_score,
|
|
438
|
+
"A": a_score,
|
|
439
|
+
"N": n_score,
|
|
440
|
+
"S": s_score,
|
|
441
|
+
"L": l_score,
|
|
442
|
+
"I": i_score,
|
|
443
|
+
"M": m_score
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def check_minimum_thresholds_phase3(c_score: float,
|
|
449
|
+
a_score: float,
|
|
450
|
+
n_score: float,
|
|
451
|
+
s_score: float,
|
|
452
|
+
l_score: float,
|
|
453
|
+
i_score: float,
|
|
454
|
+
m_score: float) -> Dict:
|
|
455
|
+
"""
|
|
456
|
+
Check if stock meets minimum CANSLIM thresholds (Phase 3: FULL 7 components)
|
|
457
|
+
|
|
458
|
+
Minimum thresholds for "buy" consideration:
|
|
459
|
+
- C >= 60 (18%+ quarterly EPS growth)
|
|
460
|
+
- A >= 50 (25%+ annual EPS CAGR)
|
|
461
|
+
- N >= 40 (within 25% of 52-week high)
|
|
462
|
+
- S >= 40 (accumulation pattern, ratio >= 1.0)
|
|
463
|
+
- L >= 50 (RS Rank 60+, outperforming market)
|
|
464
|
+
- I >= 40 (30+ holders or 20%+ ownership)
|
|
465
|
+
- M >= 40 (market not in downtrend)
|
|
466
|
+
|
|
467
|
+
Args:
|
|
468
|
+
c_score, a_score, n_score, s_score, l_score, i_score, m_score: Component scores
|
|
469
|
+
|
|
470
|
+
Returns:
|
|
471
|
+
Dict with:
|
|
472
|
+
- passes_all: Boolean - True if all thresholds met
|
|
473
|
+
- failed_components: List of components below threshold
|
|
474
|
+
- recommendation: "buy", "watchlist", or "avoid"
|
|
475
|
+
"""
|
|
476
|
+
thresholds = {
|
|
477
|
+
"C": 60, "A": 50, "N": 40,
|
|
478
|
+
"S": 40, "L": 50, "I": 40, "M": 40
|
|
479
|
+
}
|
|
480
|
+
scores = {
|
|
481
|
+
"C": c_score, "A": a_score, "N": n_score,
|
|
482
|
+
"S": s_score, "L": l_score, "I": i_score, "M": m_score
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
failed = [comp for comp, threshold in thresholds.items()
|
|
486
|
+
if scores[comp] < threshold]
|
|
487
|
+
|
|
488
|
+
# Special case: M score = 0 (bear market) overrides everything
|
|
489
|
+
if m_score == 0:
|
|
490
|
+
return {
|
|
491
|
+
"passes_all": False,
|
|
492
|
+
"failed_components": ["M"],
|
|
493
|
+
"recommendation": "avoid",
|
|
494
|
+
"reason": "Bear market - M component = 0. Do NOT buy regardless of other scores."
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
# Special case: L score < 40 (major laggard) - strong warning
|
|
498
|
+
if l_score < 40:
|
|
499
|
+
if "L" not in failed:
|
|
500
|
+
failed.append("L")
|
|
501
|
+
return {
|
|
502
|
+
"passes_all": False,
|
|
503
|
+
"failed_components": failed,
|
|
504
|
+
"recommendation": "avoid",
|
|
505
|
+
"reason": f"Stock significantly underperforming market (L={l_score}). CANSLIM requires market leaders."
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
if not failed:
|
|
509
|
+
return {
|
|
510
|
+
"passes_all": True,
|
|
511
|
+
"failed_components": [],
|
|
512
|
+
"recommendation": "buy",
|
|
513
|
+
"reason": "All 7 CANSLIM thresholds met - Full methodology validation"
|
|
514
|
+
}
|
|
515
|
+
elif len(failed) == 1:
|
|
516
|
+
return {
|
|
517
|
+
"passes_all": False,
|
|
518
|
+
"failed_components": failed,
|
|
519
|
+
"recommendation": "watchlist",
|
|
520
|
+
"reason": f"One component below threshold: {failed[0]}"
|
|
521
|
+
}
|
|
522
|
+
else:
|
|
523
|
+
return {
|
|
524
|
+
"passes_all": False,
|
|
525
|
+
"failed_components": failed,
|
|
526
|
+
"recommendation": "avoid",
|
|
527
|
+
"reason": f"Multiple components below threshold: {', '.join(failed)}"
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
def compare_to_full_canslim(phase1_score: float) -> Dict:
|
|
532
|
+
"""
|
|
533
|
+
Estimate what Phase 1 MVP score would be with full 7-component CANSLIM
|
|
534
|
+
|
|
535
|
+
Phase 1 (4 components) represents 55% of full CANSLIM methodology.
|
|
536
|
+
This function estimates the equivalent full CANSLIM score.
|
|
537
|
+
|
|
538
|
+
Args:
|
|
539
|
+
phase1_score: Phase 1 composite score (0-100)
|
|
540
|
+
|
|
541
|
+
Returns:
|
|
542
|
+
Dict with:
|
|
543
|
+
- estimated_full_score: Estimated score with all 7 components (0-200 scale)
|
|
544
|
+
- equivalent_rating: Rating on full CANSLIM scale
|
|
545
|
+
- note: Explanation of estimation
|
|
546
|
+
"""
|
|
547
|
+
# Phase 1 score 80+ typically indicates exceptional fundamentals
|
|
548
|
+
# In full CANSLIM, this would likely score 140-160+ (top tier)
|
|
549
|
+
if phase1_score >= 90:
|
|
550
|
+
estimated_range = "160-200"
|
|
551
|
+
equivalent_rating = "Exceptional"
|
|
552
|
+
elif phase1_score >= 80:
|
|
553
|
+
estimated_range = "140-159"
|
|
554
|
+
equivalent_rating = "Strong"
|
|
555
|
+
elif phase1_score >= 70:
|
|
556
|
+
estimated_range = "120-139"
|
|
557
|
+
equivalent_rating = "Above Average"
|
|
558
|
+
elif phase1_score >= 60:
|
|
559
|
+
estimated_range = "105-119"
|
|
560
|
+
equivalent_rating = "Average"
|
|
561
|
+
else:
|
|
562
|
+
estimated_range = "<105"
|
|
563
|
+
equivalent_rating = "Below Average"
|
|
564
|
+
|
|
565
|
+
return {
|
|
566
|
+
"phase1_score": phase1_score,
|
|
567
|
+
"estimated_full_range": estimated_range,
|
|
568
|
+
"equivalent_rating": equivalent_rating,
|
|
569
|
+
"note": ("Phase 1 implements 4 of 7 components (55% of methodology). "
|
|
570
|
+
"Full CANSLIM (Phases 2-3) will add S, L, I components.")
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
# Example usage and testing
|
|
575
|
+
if __name__ == "__main__":
|
|
576
|
+
print("Testing CANSLIM Scorer (Phase 1 MVP)...\n")
|
|
577
|
+
|
|
578
|
+
# Test 1: Exceptional stock (NVDA-like)
|
|
579
|
+
print("Test 1: Exceptional Stock (All Components Strong)")
|
|
580
|
+
result1 = calculate_composite_score(c_score=100, a_score=95, n_score=98, m_score=100)
|
|
581
|
+
print(f" Composite Score: {result1['composite_score']}/100")
|
|
582
|
+
print(f" Rating: {result1['rating']}")
|
|
583
|
+
print(f" Description: {result1['rating_description']}")
|
|
584
|
+
print(f" Guidance: {result1['guidance']}")
|
|
585
|
+
print(f" Weakest Component: {result1['weakest_component']} ({result1['weakest_score']})\n")
|
|
586
|
+
|
|
587
|
+
full1 = compare_to_full_canslim(result1['composite_score'])
|
|
588
|
+
print(f" Estimated Full CANSLIM Range: {full1['estimated_full_range']}\n")
|
|
589
|
+
|
|
590
|
+
# Test 2: Strong stock (META-like)
|
|
591
|
+
print("Test 2: Strong Stock (Most Components Good)")
|
|
592
|
+
result2 = calculate_composite_score(c_score=85, a_score=78, n_score=88, m_score=80)
|
|
593
|
+
print(f" Composite Score: {result2['composite_score']}/100")
|
|
594
|
+
print(f" Rating: {result2['rating']}")
|
|
595
|
+
print(f" Weakest Component: {result2['weakest_component']} ({result2['weakest_score']})\n")
|
|
596
|
+
|
|
597
|
+
# Test 3: Average stock (marginal)
|
|
598
|
+
print("Test 3: Average Stock (Meets Minimums)")
|
|
599
|
+
result3 = calculate_composite_score(c_score=60, a_score=55, n_score=65, m_score=60)
|
|
600
|
+
print(f" Composite Score: {result3['composite_score']}/100")
|
|
601
|
+
print(f" Rating: {result3['rating']}")
|
|
602
|
+
print(f" Guidance: {result3['guidance']}\n")
|
|
603
|
+
|
|
604
|
+
# Test 4: Bear market scenario (M=0)
|
|
605
|
+
print("Test 4: Bear Market Scenario (M=0 Override)")
|
|
606
|
+
result4 = calculate_composite_score(c_score=95, a_score=90, n_score=92, m_score=0)
|
|
607
|
+
print(f" Composite Score: {result4['composite_score']}/100")
|
|
608
|
+
print(f" Rating: {result4['rating']}")
|
|
609
|
+
|
|
610
|
+
threshold_check = check_minimum_thresholds(c_score=95, a_score=90, n_score=92, m_score=0)
|
|
611
|
+
print(f" Threshold Check: {threshold_check['recommendation']}")
|
|
612
|
+
print(f" Reason: {threshold_check['reason']}\n")
|
|
613
|
+
|
|
614
|
+
# Test 5: Minimum threshold checking
|
|
615
|
+
print("Test 5: Threshold Validation")
|
|
616
|
+
threshold_pass = check_minimum_thresholds(c_score=70, a_score=60, n_score=65, m_score=70)
|
|
617
|
+
print(f" All thresholds met: {threshold_pass['passes_all']}")
|
|
618
|
+
print(f" Recommendation: {threshold_pass['recommendation']}\n")
|
|
619
|
+
|
|
620
|
+
threshold_fail = check_minimum_thresholds(c_score=50, a_score=45, n_score=35, m_score=60)
|
|
621
|
+
print(f" All thresholds met: {threshold_fail['passes_all']}")
|
|
622
|
+
print(f" Failed components: {threshold_fail['failed_components']}")
|
|
623
|
+
print(f" Recommendation: {threshold_fail['recommendation']}")
|
|
624
|
+
|
|
625
|
+
print("\n✓ All tests completed")
|