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,161 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Relative Strength Calculator - Minervini Weighted RS
|
|
4
|
+
|
|
5
|
+
Calculates relative price performance vs S&P 500 using Minervini's weighting:
|
|
6
|
+
- 40% weight: Last 3 months (63 trading days)
|
|
7
|
+
- 20% weight: Last 6 months (126 trading days)
|
|
8
|
+
- 20% weight: Last 9 months (189 trading days)
|
|
9
|
+
- 20% weight: Last 12 months (252 trading days)
|
|
10
|
+
|
|
11
|
+
This emphasizes recent momentum more than a simple 52-week calculation.
|
|
12
|
+
|
|
13
|
+
Scoring:
|
|
14
|
+
- 100: Weighted RS outperformance >= +50% (top 1%)
|
|
15
|
+
- 95: >= +30% (top 5%)
|
|
16
|
+
- 90: >= +20% (top 10%)
|
|
17
|
+
- 80: >= +10% (top 20%)
|
|
18
|
+
- 70: >= +5% (top 30%)
|
|
19
|
+
- 60: >= 0% (top 40%)
|
|
20
|
+
- 50: >= -5% (average)
|
|
21
|
+
- 40: >= -10% (below average)
|
|
22
|
+
- 20: >= -20% (weak)
|
|
23
|
+
- 0: < -20% (laggard)
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from typing import Dict, List, Optional
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# Minervini weighting periods (trading days) and weights
|
|
30
|
+
RS_PERIODS = [
|
|
31
|
+
(63, 0.40), # 3 months - 40%
|
|
32
|
+
(126, 0.20), # 6 months - 20%
|
|
33
|
+
(189, 0.20), # 9 months - 20%
|
|
34
|
+
(252, 0.20), # 12 months - 20%
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def calculate_relative_strength(
|
|
39
|
+
stock_prices: List[Dict],
|
|
40
|
+
sp500_prices: List[Dict],
|
|
41
|
+
) -> Dict:
|
|
42
|
+
"""
|
|
43
|
+
Calculate Minervini-weighted relative strength vs S&P 500.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
stock_prices: Daily OHLCV for stock (most recent first), need 252+ days
|
|
47
|
+
sp500_prices: Daily OHLCV for SPY (most recent first), need 252+ days
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
Dict with score (0-100), rs_rank_estimate, weighted_rs, period details
|
|
51
|
+
"""
|
|
52
|
+
if not stock_prices or len(stock_prices) < 63:
|
|
53
|
+
return {
|
|
54
|
+
"score": 0,
|
|
55
|
+
"rs_rank_estimate": 0,
|
|
56
|
+
"weighted_rs": None,
|
|
57
|
+
"error": "Insufficient stock price data (need 63+ days)",
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if not sp500_prices or len(sp500_prices) < 63:
|
|
61
|
+
return {
|
|
62
|
+
"score": 0,
|
|
63
|
+
"rs_rank_estimate": 0,
|
|
64
|
+
"weighted_rs": None,
|
|
65
|
+
"error": "Insufficient S&P 500 price data (need 63+ days)",
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
stock_closes = [d.get("close", d.get("adjClose", 0)) for d in stock_prices]
|
|
69
|
+
sp500_closes = [d.get("close", d.get("adjClose", 0)) for d in sp500_prices]
|
|
70
|
+
|
|
71
|
+
weighted_rs = 0.0
|
|
72
|
+
total_weight = 0.0
|
|
73
|
+
period_details = []
|
|
74
|
+
|
|
75
|
+
for period_days, weight in RS_PERIODS:
|
|
76
|
+
if len(stock_closes) > period_days and len(sp500_closes) > period_days:
|
|
77
|
+
stock_return = _period_return(stock_closes, period_days)
|
|
78
|
+
sp500_return = _period_return(sp500_closes, period_days)
|
|
79
|
+
relative = stock_return - sp500_return
|
|
80
|
+
|
|
81
|
+
weighted_rs += relative * weight
|
|
82
|
+
total_weight += weight
|
|
83
|
+
|
|
84
|
+
period_details.append({
|
|
85
|
+
"period_days": period_days,
|
|
86
|
+
"weight": weight,
|
|
87
|
+
"stock_return_pct": round(stock_return, 2),
|
|
88
|
+
"sp500_return_pct": round(sp500_return, 2),
|
|
89
|
+
"relative_pct": round(relative, 2),
|
|
90
|
+
})
|
|
91
|
+
elif len(stock_closes) > period_days // 2 and len(sp500_closes) > period_days // 2:
|
|
92
|
+
# Partial data: use available days with reduced weight
|
|
93
|
+
available = min(len(stock_closes) - 1, len(sp500_closes) - 1)
|
|
94
|
+
stock_return = _period_return(stock_closes, available)
|
|
95
|
+
sp500_return = _period_return(sp500_closes, available)
|
|
96
|
+
relative = stock_return - sp500_return
|
|
97
|
+
reduced_weight = weight * 0.5
|
|
98
|
+
|
|
99
|
+
weighted_rs += relative * reduced_weight
|
|
100
|
+
total_weight += reduced_weight
|
|
101
|
+
|
|
102
|
+
period_details.append({
|
|
103
|
+
"period_days": period_days,
|
|
104
|
+
"weight": reduced_weight,
|
|
105
|
+
"stock_return_pct": round(stock_return, 2),
|
|
106
|
+
"sp500_return_pct": round(sp500_return, 2),
|
|
107
|
+
"relative_pct": round(relative, 2),
|
|
108
|
+
"note": f"Partial data ({available} days available)",
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
if total_weight > 0:
|
|
112
|
+
weighted_rs = weighted_rs / total_weight
|
|
113
|
+
else:
|
|
114
|
+
return {
|
|
115
|
+
"score": 0,
|
|
116
|
+
"rs_rank_estimate": 0,
|
|
117
|
+
"weighted_rs": None,
|
|
118
|
+
"error": "Unable to calculate weighted RS (insufficient overlapping data)",
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# Score based on weighted relative performance
|
|
122
|
+
score, rs_rank = _score_rs(weighted_rs)
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
"score": score,
|
|
126
|
+
"rs_rank_estimate": rs_rank,
|
|
127
|
+
"weighted_rs": round(weighted_rs, 2),
|
|
128
|
+
"period_details": period_details,
|
|
129
|
+
"error": None,
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _period_return(closes: List[float], period: int) -> float:
|
|
134
|
+
"""Calculate return over period. Closes are most-recent-first."""
|
|
135
|
+
if len(closes) <= period or closes[period] <= 0:
|
|
136
|
+
return 0.0
|
|
137
|
+
return ((closes[0] - closes[period]) / closes[period]) * 100
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _score_rs(weighted_rs: float) -> tuple:
|
|
141
|
+
"""Score based on weighted relative strength."""
|
|
142
|
+
if weighted_rs >= 50:
|
|
143
|
+
return 100, 99
|
|
144
|
+
elif weighted_rs >= 30:
|
|
145
|
+
return 95, 95
|
|
146
|
+
elif weighted_rs >= 20:
|
|
147
|
+
return 90, 90
|
|
148
|
+
elif weighted_rs >= 10:
|
|
149
|
+
return 80, 80
|
|
150
|
+
elif weighted_rs >= 5:
|
|
151
|
+
return 70, 70
|
|
152
|
+
elif weighted_rs >= 0:
|
|
153
|
+
return 60, 60
|
|
154
|
+
elif weighted_rs >= -5:
|
|
155
|
+
return 50, 50
|
|
156
|
+
elif weighted_rs >= -10:
|
|
157
|
+
return 40, 40
|
|
158
|
+
elif weighted_rs >= -20:
|
|
159
|
+
return 20, 25
|
|
160
|
+
else:
|
|
161
|
+
return 0, 10
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Trend Template Calculator - Minervini's 7-Point Stage 2 Filter
|
|
4
|
+
|
|
5
|
+
Evaluates whether a stock meets Minervini's Stage 2 uptrend criteria.
|
|
6
|
+
This is the primary gate filter - stocks must pass this to be evaluated for VCP.
|
|
7
|
+
|
|
8
|
+
The 7-Point Trend Template:
|
|
9
|
+
1. Price > 150-day SMA AND Price > 200-day SMA
|
|
10
|
+
2. 150-day SMA > 200-day SMA
|
|
11
|
+
3. 200-day SMA trending up for at least 22 trading days (1 month)
|
|
12
|
+
4. Price > 50-day SMA
|
|
13
|
+
5. Price at least 25% above 52-week low
|
|
14
|
+
6. Price within 25% of 52-week high
|
|
15
|
+
7. Relative Strength rating > 70 (estimated)
|
|
16
|
+
|
|
17
|
+
Scoring: Each criterion = 14.3 points (7 x 14.3 = ~100)
|
|
18
|
+
Pass threshold: >= 85 (must meet at least 6 of 7 criteria)
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from typing import Dict, List, Optional
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def calculate_trend_template(
|
|
25
|
+
historical_prices: List[Dict],
|
|
26
|
+
quote_data: Dict,
|
|
27
|
+
rs_rank: Optional[int] = None,
|
|
28
|
+
ext_threshold: float = 8.0,
|
|
29
|
+
) -> Dict:
|
|
30
|
+
"""
|
|
31
|
+
Evaluate stock against Minervini's 7-point Trend Template.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
historical_prices: Daily OHLCV data (most recent first), need 200+ days
|
|
35
|
+
quote_data: Current quote with price, yearHigh, yearLow
|
|
36
|
+
rs_rank: Pre-calculated RS rank estimate (0-99). If None, criterion 7 is skipped.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
Dict with score (0-100), criteria details, pass/fail status
|
|
40
|
+
"""
|
|
41
|
+
if not historical_prices or len(historical_prices) < 50:
|
|
42
|
+
return {
|
|
43
|
+
"score": 0,
|
|
44
|
+
"passed": False,
|
|
45
|
+
"criteria": {},
|
|
46
|
+
"error": "Insufficient historical data (need 50+ days)",
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
closes = [d.get("close", d.get("adjClose", 0)) for d in historical_prices]
|
|
50
|
+
price = quote_data.get("price", closes[0] if closes else 0)
|
|
51
|
+
year_high = quote_data.get("yearHigh", 0)
|
|
52
|
+
year_low = quote_data.get("yearLow", 0)
|
|
53
|
+
|
|
54
|
+
criteria = {}
|
|
55
|
+
points_per_criterion = 14.3
|
|
56
|
+
|
|
57
|
+
# Criterion 1: Price > SMA150 AND Price > SMA200
|
|
58
|
+
sma150 = _sma(closes, 150)
|
|
59
|
+
sma200 = _sma(closes, 200)
|
|
60
|
+
c1_pass = False
|
|
61
|
+
if sma150 is not None and sma200 is not None:
|
|
62
|
+
c1_pass = price > sma150 and price > sma200
|
|
63
|
+
elif sma150 is not None:
|
|
64
|
+
c1_pass = price > sma150
|
|
65
|
+
criteria["c1_price_above_sma150_200"] = {
|
|
66
|
+
"passed": c1_pass,
|
|
67
|
+
"detail": f"Price ${price:.2f} vs SMA150 ${sma150:.2f}" + (f" / SMA200 ${sma200:.2f}" if sma200 else ""),
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# Criterion 2: SMA150 > SMA200
|
|
71
|
+
c2_pass = False
|
|
72
|
+
if sma150 is not None and sma200 is not None:
|
|
73
|
+
c2_pass = sma150 > sma200
|
|
74
|
+
criteria["c2_sma150_above_sma200"] = {
|
|
75
|
+
"passed": c2_pass,
|
|
76
|
+
"detail": f"SMA150 ${sma150:.2f} vs SMA200 ${sma200:.2f}" if sma150 and sma200 else "Insufficient data",
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# Criterion 3: SMA200 trending up for 22+ trading days
|
|
80
|
+
c3_pass = False
|
|
81
|
+
if len(closes) >= 222 and sma200 is not None:
|
|
82
|
+
sma200_22d_ago = _sma(closes[22:], 200)
|
|
83
|
+
if sma200_22d_ago is not None:
|
|
84
|
+
c3_pass = sma200 > sma200_22d_ago
|
|
85
|
+
criteria["c3_sma200_trending_up"] = {
|
|
86
|
+
"passed": c3_pass,
|
|
87
|
+
"detail": f"SMA200 today ${sma200:.2f} vs 22d ago ${sma200_22d_ago:.2f}",
|
|
88
|
+
}
|
|
89
|
+
else:
|
|
90
|
+
criteria["c3_sma200_trending_up"] = {
|
|
91
|
+
"passed": False,
|
|
92
|
+
"detail": "Insufficient data for 22d SMA200 comparison",
|
|
93
|
+
}
|
|
94
|
+
elif sma200 is not None and len(closes) >= 200:
|
|
95
|
+
# Not enough data to compute SMA200 from 22 days ago - fail conservatively
|
|
96
|
+
c3_pass = False
|
|
97
|
+
criteria["c3_sma200_trending_up"] = {
|
|
98
|
+
"passed": c3_pass,
|
|
99
|
+
"detail": f"Cannot verify 22d SMA200 trend (only {len(closes)} days available, need 222+)",
|
|
100
|
+
}
|
|
101
|
+
else:
|
|
102
|
+
criteria["c3_sma200_trending_up"] = {
|
|
103
|
+
"passed": False,
|
|
104
|
+
"detail": "Insufficient data",
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
# Criterion 4: Price > SMA50
|
|
108
|
+
sma50 = _sma(closes, 50)
|
|
109
|
+
c4_pass = False
|
|
110
|
+
if sma50 is not None:
|
|
111
|
+
c4_pass = price > sma50
|
|
112
|
+
criteria["c4_price_above_sma50"] = {
|
|
113
|
+
"passed": c4_pass,
|
|
114
|
+
"detail": f"Price ${price:.2f} vs SMA50 ${sma50:.2f}" if sma50 else "Insufficient data",
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# Criterion 5: Price at least 25% above 52-week low
|
|
118
|
+
c5_pass = False
|
|
119
|
+
if year_low > 0:
|
|
120
|
+
pct_above_low = (price - year_low) / year_low * 100
|
|
121
|
+
c5_pass = pct_above_low >= 25
|
|
122
|
+
criteria["c5_25pct_above_52w_low"] = {
|
|
123
|
+
"passed": c5_pass,
|
|
124
|
+
"detail": f"{pct_above_low:.1f}% above 52w low ${year_low:.2f} (need >= 25%)",
|
|
125
|
+
}
|
|
126
|
+
else:
|
|
127
|
+
criteria["c5_25pct_above_52w_low"] = {
|
|
128
|
+
"passed": False,
|
|
129
|
+
"detail": "52-week low data unavailable",
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
# Criterion 6: Price within 25% of 52-week high
|
|
133
|
+
c6_pass = False
|
|
134
|
+
if year_high > 0:
|
|
135
|
+
pct_below_high = (year_high - price) / year_high * 100
|
|
136
|
+
c6_pass = pct_below_high <= 25
|
|
137
|
+
criteria["c6_within_25pct_52w_high"] = {
|
|
138
|
+
"passed": c6_pass,
|
|
139
|
+
"detail": f"{pct_below_high:.1f}% below 52w high ${year_high:.2f} (need <= 25%)",
|
|
140
|
+
}
|
|
141
|
+
else:
|
|
142
|
+
criteria["c6_within_25pct_52w_high"] = {
|
|
143
|
+
"passed": False,
|
|
144
|
+
"detail": "52-week high data unavailable",
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# Criterion 7: RS Rating > 70
|
|
148
|
+
c7_pass = False
|
|
149
|
+
if rs_rank is not None:
|
|
150
|
+
c7_pass = rs_rank > 70
|
|
151
|
+
criteria["c7_rs_rank_above_70"] = {
|
|
152
|
+
"passed": c7_pass,
|
|
153
|
+
"detail": f"RS Rank: {rs_rank} (need > 70)",
|
|
154
|
+
}
|
|
155
|
+
else:
|
|
156
|
+
criteria["c7_rs_rank_above_70"] = {
|
|
157
|
+
"passed": False,
|
|
158
|
+
"detail": "RS Rank not yet calculated",
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
# Raw score from 7 criteria (用于门槛判定)
|
|
162
|
+
passed_count = sum(1 for c in criteria.values() if c["passed"])
|
|
163
|
+
raw_score = round(passed_count * points_per_criterion, 1)
|
|
164
|
+
raw_score = min(100, raw_score)
|
|
165
|
+
|
|
166
|
+
# Pass threshold: 85+ (6/7 criteria) - uses RAW score only
|
|
167
|
+
passed = raw_score >= 85
|
|
168
|
+
|
|
169
|
+
# Extended penalty: deduct for price too far above SMA50 (用于排名)
|
|
170
|
+
extended_penalty, sma50_distance_pct = _calculate_extended_penalty(
|
|
171
|
+
price, sma50, base_threshold=ext_threshold
|
|
172
|
+
)
|
|
173
|
+
score = max(0, raw_score + extended_penalty)
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
"score": score,
|
|
177
|
+
"raw_score": raw_score,
|
|
178
|
+
"passed": passed,
|
|
179
|
+
"extended_penalty": extended_penalty,
|
|
180
|
+
"sma50_distance_pct": round(sma50_distance_pct, 2) if sma50_distance_pct is not None else None,
|
|
181
|
+
"criteria_passed": passed_count,
|
|
182
|
+
"criteria_total": 7,
|
|
183
|
+
"criteria": criteria,
|
|
184
|
+
"sma50": round(sma50, 2) if sma50 else None,
|
|
185
|
+
"sma150": round(sma150, 2) if sma150 else None,
|
|
186
|
+
"sma200": round(sma200, 2) if sma200 else None,
|
|
187
|
+
"error": None,
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _calculate_extended_penalty(
|
|
192
|
+
price: float, sma50: Optional[float], base_threshold: float = 8.0
|
|
193
|
+
) -> tuple:
|
|
194
|
+
"""Calculate penalty for price extended too far above SMA 50.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
price: Current stock price
|
|
198
|
+
sma50: 50-day simple moving average
|
|
199
|
+
base_threshold: Distance % where penalty starts (default 8.0)
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
(penalty: int, distance_pct: float or None)
|
|
203
|
+
penalty is 0 or negative.
|
|
204
|
+
"""
|
|
205
|
+
if sma50 is None or sma50 <= 0:
|
|
206
|
+
return 0, None
|
|
207
|
+
|
|
208
|
+
distance_pct = (price - sma50) / sma50 * 100
|
|
209
|
+
|
|
210
|
+
if distance_pct < base_threshold:
|
|
211
|
+
return 0, distance_pct
|
|
212
|
+
|
|
213
|
+
excess = distance_pct - base_threshold
|
|
214
|
+
if excess >= 17: # base+17% (default: 25%+)
|
|
215
|
+
return -20, distance_pct
|
|
216
|
+
elif excess >= 10: # base+10% (default: 18%+)
|
|
217
|
+
return -15, distance_pct
|
|
218
|
+
elif excess >= 4: # base+4% (default: 12%+)
|
|
219
|
+
return -10, distance_pct
|
|
220
|
+
else: # base+0% (default: 8%+)
|
|
221
|
+
return -5, distance_pct
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def _sma(prices: List[float], period: int) -> Optional[float]:
|
|
225
|
+
"""Calculate Simple Moving Average. Prices are most-recent-first."""
|
|
226
|
+
if len(prices) < period:
|
|
227
|
+
return None
|
|
228
|
+
return sum(prices[:period]) / period
|