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,194 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
N Component - Newness / New Highs Calculator
|
|
4
|
+
|
|
5
|
+
Calculates CANSLIM 'N' component score based on price position relative to
|
|
6
|
+
52-week high and momentum indicators.
|
|
7
|
+
|
|
8
|
+
O'Neil's Rule: "Stocks making new price highs have no overhead supply.
|
|
9
|
+
New products, services, or management catalyze major moves."
|
|
10
|
+
|
|
11
|
+
Scoring:
|
|
12
|
+
- 100: Within 5% of 52-week high + breakout + new product catalyst
|
|
13
|
+
- 80: Within 10% of 52-week high + breakout
|
|
14
|
+
- 60: Within 15% of 52-week high OR breakout
|
|
15
|
+
- 40: Within 25% of 52-week high
|
|
16
|
+
- 20: >25% from 52-week high (lacks momentum)
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from typing import Dict, List, Optional
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def calculate_newness(quote: Dict, historical_prices: Optional[Dict] = None) -> Dict:
|
|
23
|
+
"""
|
|
24
|
+
Calculate N component score based on price position and momentum
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
quote: Stock quote data from FMP API (contains yearHigh, price, volume)
|
|
28
|
+
historical_prices: Optional historical price data for detailed analysis
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Dict with:
|
|
32
|
+
- score: 0-100 points
|
|
33
|
+
- distance_from_high_pct: Distance from 52-week high (%)
|
|
34
|
+
- current_price: Current stock price
|
|
35
|
+
- week_52_high: 52-week high price
|
|
36
|
+
- week_52_low: 52-week low price
|
|
37
|
+
- breakout_detected: Boolean
|
|
38
|
+
- interpretation: Human-readable interpretation
|
|
39
|
+
"""
|
|
40
|
+
# Validate input
|
|
41
|
+
if not quote:
|
|
42
|
+
return {
|
|
43
|
+
"score": 0,
|
|
44
|
+
"error": "Quote data missing",
|
|
45
|
+
"distance_from_high_pct": None,
|
|
46
|
+
"interpretation": "Data unavailable"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
# Extract price data
|
|
50
|
+
current_price = quote.get("price")
|
|
51
|
+
week_52_high = quote.get("yearHigh")
|
|
52
|
+
week_52_low = quote.get("yearLow")
|
|
53
|
+
current_volume = quote.get("volume")
|
|
54
|
+
avg_volume = quote.get("avgVolume")
|
|
55
|
+
|
|
56
|
+
if not current_price or not week_52_high:
|
|
57
|
+
return {
|
|
58
|
+
"score": 0,
|
|
59
|
+
"error": "Price or 52-week high data missing",
|
|
60
|
+
"distance_from_high_pct": None,
|
|
61
|
+
"interpretation": "Data unavailable"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# Calculate distance from 52-week high
|
|
65
|
+
distance_from_high_pct = ((current_price / week_52_high) - 1) * 100
|
|
66
|
+
|
|
67
|
+
# Detect breakout (new high on elevated volume)
|
|
68
|
+
breakout_detected = False
|
|
69
|
+
if current_volume and avg_volume:
|
|
70
|
+
# Within 0.5% of high AND volume 40%+ above average
|
|
71
|
+
if current_price >= week_52_high * 0.995 and current_volume > avg_volume * 1.4:
|
|
72
|
+
breakout_detected = True
|
|
73
|
+
|
|
74
|
+
# Calculate score
|
|
75
|
+
score = score_newness(distance_from_high_pct, breakout_detected)
|
|
76
|
+
|
|
77
|
+
# Generate interpretation
|
|
78
|
+
interpretation = interpret_newness_score(score, distance_from_high_pct, breakout_detected)
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
"score": score,
|
|
82
|
+
"distance_from_high_pct": round(distance_from_high_pct, 1),
|
|
83
|
+
"current_price": current_price,
|
|
84
|
+
"week_52_high": week_52_high,
|
|
85
|
+
"week_52_low": week_52_low,
|
|
86
|
+
"breakout_detected": breakout_detected,
|
|
87
|
+
"current_volume": current_volume,
|
|
88
|
+
"avg_volume": avg_volume,
|
|
89
|
+
"volume_ratio": round(current_volume / avg_volume, 2) if avg_volume else None,
|
|
90
|
+
"interpretation": interpretation,
|
|
91
|
+
"error": None
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def score_newness(distance_from_high_pct: float, breakout_detected: bool) -> int:
|
|
96
|
+
"""
|
|
97
|
+
Score N component based on price position and breakout
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
distance_from_high_pct: Distance from 52-week high (negative = below high)
|
|
101
|
+
breakout_detected: Boolean indicating volume-confirmed breakout
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Score (0-100)
|
|
105
|
+
|
|
106
|
+
Scoring Logic:
|
|
107
|
+
- Within 5% of high + breakout: 100
|
|
108
|
+
- Within 10% of high + breakout: 80
|
|
109
|
+
- Within 15% of high OR breakout: 60
|
|
110
|
+
- Within 25% of high: 40
|
|
111
|
+
- >25% from high: 20
|
|
112
|
+
"""
|
|
113
|
+
if distance_from_high_pct >= -5 and breakout_detected:
|
|
114
|
+
return 100 # Perfect setup - at new highs with volume
|
|
115
|
+
elif distance_from_high_pct >= -10 and breakout_detected:
|
|
116
|
+
return 80 # Strong momentum
|
|
117
|
+
elif distance_from_high_pct >= -15 or breakout_detected:
|
|
118
|
+
return 60 # Acceptable
|
|
119
|
+
elif distance_from_high_pct >= -25:
|
|
120
|
+
return 40 # Weak momentum
|
|
121
|
+
else:
|
|
122
|
+
return 20 # Too far from highs, lacks sponsorship
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def interpret_newness_score(score: int, distance: float, breakout: bool) -> str:
|
|
126
|
+
"""
|
|
127
|
+
Generate human-readable interpretation
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
score: Component score
|
|
131
|
+
distance: Distance from 52-week high (%)
|
|
132
|
+
breakout: Breakout detected flag
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
Interpretation string
|
|
136
|
+
"""
|
|
137
|
+
breakout_text = " with volume breakout" if breakout else ""
|
|
138
|
+
|
|
139
|
+
if score >= 90:
|
|
140
|
+
return f"Exceptional - At new highs{breakout_text} ({distance:+.1f}% from high)"
|
|
141
|
+
elif score >= 70:
|
|
142
|
+
return f"Strong - Near 52-week high{breakout_text} ({distance:+.1f}% from high)"
|
|
143
|
+
elif score >= 50:
|
|
144
|
+
return f"Acceptable - Within 15% of high ({distance:+.1f}% from high)"
|
|
145
|
+
elif score >= 30:
|
|
146
|
+
return f"Weak - Lacks momentum ({distance:+.1f}% from high)"
|
|
147
|
+
else:
|
|
148
|
+
return f"Poor - Too far from highs, overhead resistance ({distance:+.1f}% from high)"
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# Example usage
|
|
152
|
+
if __name__ == "__main__":
|
|
153
|
+
print("Testing New Highs Calculator (N Component)...\n")
|
|
154
|
+
|
|
155
|
+
# Test 1: At new high with breakout (score 100)
|
|
156
|
+
test_quote_1 = {
|
|
157
|
+
"symbol": "NVDA",
|
|
158
|
+
"price": 495.50,
|
|
159
|
+
"yearHigh": 496.00,
|
|
160
|
+
"yearLow": 280.00,
|
|
161
|
+
"volume": 60000000,
|
|
162
|
+
"avgVolume": 40000000
|
|
163
|
+
}
|
|
164
|
+
result1 = calculate_newness(test_quote_1)
|
|
165
|
+
print(f"Test 1: New High + Breakout - Score: {result1['score']}")
|
|
166
|
+
print(f" {result1['interpretation']}\n")
|
|
167
|
+
|
|
168
|
+
# Test 2: Within 10% of high, no breakout (score 60)
|
|
169
|
+
test_quote_2 = {
|
|
170
|
+
"symbol": "META",
|
|
171
|
+
"price": 450.00,
|
|
172
|
+
"yearHigh": 490.00,
|
|
173
|
+
"yearLow": 320.00,
|
|
174
|
+
"volume": 15000000,
|
|
175
|
+
"avgVolume": 16000000
|
|
176
|
+
}
|
|
177
|
+
result2 = calculate_newness(test_quote_2)
|
|
178
|
+
print(f"Test 2: Near High, No Breakout - Score: {result2['score']}")
|
|
179
|
+
print(f" {result2['interpretation']}\n")
|
|
180
|
+
|
|
181
|
+
# Test 3: Far from high (score 20)
|
|
182
|
+
test_quote_3 = {
|
|
183
|
+
"symbol": "XYZ",
|
|
184
|
+
"price": 50.00,
|
|
185
|
+
"yearHigh": 80.00,
|
|
186
|
+
"yearLow": 45.00,
|
|
187
|
+
"volume": 1000000,
|
|
188
|
+
"avgVolume": 1200000
|
|
189
|
+
}
|
|
190
|
+
result3 = calculate_newness(test_quote_3)
|
|
191
|
+
print(f"Test 3: Far from High - Score: {result3['score']}")
|
|
192
|
+
print(f" {result3['interpretation']}\n")
|
|
193
|
+
|
|
194
|
+
print("✓ All tests completed")
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
S Component - Supply and Demand Calculator
|
|
4
|
+
|
|
5
|
+
Calculates CANSLIM 'S' component score based on volume accumulation/distribution patterns.
|
|
6
|
+
|
|
7
|
+
O'Neil's Rule: "Volume is the gas in the tank of a stock. Without gas, the car doesn't
|
|
8
|
+
go anywhere. Look for stocks where volume expands on up days and contracts on down days."
|
|
9
|
+
|
|
10
|
+
Key Principle:
|
|
11
|
+
- UP-DAY VOLUME > DOWN-DAY VOLUME = Accumulation (institutions buying)
|
|
12
|
+
- DOWN-DAY VOLUME > UP-DAY VOLUME = Distribution (institutions selling)
|
|
13
|
+
|
|
14
|
+
Scoring:
|
|
15
|
+
- 100 points: Up/down ratio ≥ 2.0 (Strong Accumulation)
|
|
16
|
+
- 80 points: Ratio 1.5-2.0 (Accumulation)
|
|
17
|
+
- 60 points: Ratio 1.0-1.5 (Neutral/Weak Accumulation)
|
|
18
|
+
- 40 points: Ratio 0.7-1.0 (Neutral/Weak Distribution)
|
|
19
|
+
- 20 points: Ratio 0.5-0.7 (Distribution)
|
|
20
|
+
- 0 points: Ratio < 0.5 (Strong Distribution)
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from typing import Dict, List
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def calculate_supply_demand(historical_prices: Dict) -> Dict:
|
|
27
|
+
"""
|
|
28
|
+
Calculate supply/demand dynamics based on volume patterns
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
historical_prices: Historical price data from FMP API
|
|
32
|
+
Dict with keys: 'historical' (list of daily price/volume data)
|
|
33
|
+
Each entry should have: date, close, volume
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Dict with:
|
|
37
|
+
- score: 0-100 points
|
|
38
|
+
- up_down_ratio: Average up-day volume / average down-day volume
|
|
39
|
+
- accumulation_detected: True if ratio ≥ 1.5
|
|
40
|
+
- avg_up_volume: Average volume on up days
|
|
41
|
+
- avg_down_volume: Average volume on down days
|
|
42
|
+
- up_days_count: Number of up days in analysis period
|
|
43
|
+
- down_days_count: Number of down days
|
|
44
|
+
- interpretation: Human-readable interpretation
|
|
45
|
+
- quality_warning: Warning if data quality issues
|
|
46
|
+
- error: Error message if calculation failed
|
|
47
|
+
|
|
48
|
+
Example:
|
|
49
|
+
>>> prices = client.get_historical_prices("NVDA", days=90)
|
|
50
|
+
>>> result = calculate_supply_demand(prices)
|
|
51
|
+
>>> print(f"S Score: {result['score']}, Ratio: {result['up_down_ratio']:.2f}")
|
|
52
|
+
"""
|
|
53
|
+
# Validate input
|
|
54
|
+
if not historical_prices or "historical" not in historical_prices:
|
|
55
|
+
return {
|
|
56
|
+
"score": 0,
|
|
57
|
+
"error": "No historical price data provided",
|
|
58
|
+
"up_down_ratio": None,
|
|
59
|
+
"accumulation_detected": False,
|
|
60
|
+
"avg_up_volume": None,
|
|
61
|
+
"avg_down_volume": None,
|
|
62
|
+
"up_days_count": 0,
|
|
63
|
+
"down_days_count": 0,
|
|
64
|
+
"interpretation": "Data unavailable"
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
prices = historical_prices["historical"]
|
|
68
|
+
|
|
69
|
+
if not prices or len(prices) < 60:
|
|
70
|
+
return {
|
|
71
|
+
"score": 0,
|
|
72
|
+
"error": f"Insufficient data (need 60+ days, got {len(prices)})",
|
|
73
|
+
"up_down_ratio": None,
|
|
74
|
+
"accumulation_detected": False,
|
|
75
|
+
"avg_up_volume": None,
|
|
76
|
+
"avg_down_volume": None,
|
|
77
|
+
"up_days_count": 0,
|
|
78
|
+
"down_days_count": 0,
|
|
79
|
+
"interpretation": "Data unavailable",
|
|
80
|
+
"quality_warning": "Less than 60 days of historical data available"
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
# Analyze last 60 days (prices are typically newest first, so take first 60)
|
|
84
|
+
analysis_period = prices[:60]
|
|
85
|
+
|
|
86
|
+
# Classify days as up or down
|
|
87
|
+
up_days = []
|
|
88
|
+
down_days = []
|
|
89
|
+
previous_close = None
|
|
90
|
+
|
|
91
|
+
for day in reversed(analysis_period): # Process chronologically
|
|
92
|
+
current_close = day.get("close")
|
|
93
|
+
current_volume = day.get("volume")
|
|
94
|
+
|
|
95
|
+
if current_close is None or current_volume is None:
|
|
96
|
+
continue # Skip incomplete data
|
|
97
|
+
|
|
98
|
+
if previous_close is not None:
|
|
99
|
+
if current_close > previous_close:
|
|
100
|
+
up_days.append(current_volume)
|
|
101
|
+
elif current_close < previous_close:
|
|
102
|
+
down_days.append(current_volume)
|
|
103
|
+
# Unchanged days are ignored
|
|
104
|
+
|
|
105
|
+
previous_close = current_close
|
|
106
|
+
|
|
107
|
+
# Validate we have enough classified days
|
|
108
|
+
if len(up_days) < 10 or len(down_days) < 10:
|
|
109
|
+
return {
|
|
110
|
+
"score": 0,
|
|
111
|
+
"error": "Insufficient up/down days for analysis",
|
|
112
|
+
"up_down_ratio": None,
|
|
113
|
+
"accumulation_detected": False,
|
|
114
|
+
"avg_up_volume": None,
|
|
115
|
+
"avg_down_volume": None,
|
|
116
|
+
"up_days_count": len(up_days),
|
|
117
|
+
"down_days_count": len(down_days),
|
|
118
|
+
"interpretation": "Data quality issues",
|
|
119
|
+
"quality_warning": f"Too few classified days (up: {len(up_days)}, down: {len(down_days)})"
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
# Calculate average volumes
|
|
123
|
+
avg_up_volume = sum(up_days) / len(up_days)
|
|
124
|
+
avg_down_volume = sum(down_days) / len(down_days)
|
|
125
|
+
|
|
126
|
+
# Calculate accumulation/distribution ratio
|
|
127
|
+
if avg_down_volume == 0:
|
|
128
|
+
# Edge case: no volume on down days (extremely bullish, but rare)
|
|
129
|
+
ratio = 5.0 # Cap at 5.0 to avoid infinity
|
|
130
|
+
else:
|
|
131
|
+
ratio = avg_up_volume / avg_down_volume
|
|
132
|
+
|
|
133
|
+
# Determine accumulation status
|
|
134
|
+
accumulation_detected = ratio >= 1.5
|
|
135
|
+
|
|
136
|
+
# Score based on ratio
|
|
137
|
+
score = score_supply_demand(ratio)
|
|
138
|
+
|
|
139
|
+
# Generate interpretation
|
|
140
|
+
interpretation = interpret_supply_demand(ratio, accumulation_detected)
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
"score": score,
|
|
144
|
+
"up_down_ratio": ratio,
|
|
145
|
+
"accumulation_detected": accumulation_detected,
|
|
146
|
+
"avg_up_volume": int(avg_up_volume),
|
|
147
|
+
"avg_down_volume": int(avg_down_volume),
|
|
148
|
+
"up_days_count": len(up_days),
|
|
149
|
+
"down_days_count": len(down_days),
|
|
150
|
+
"interpretation": interpretation
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def score_supply_demand(ratio: float) -> int:
|
|
155
|
+
"""
|
|
156
|
+
Score accumulation/distribution ratio
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
ratio: Up-day volume / down-day volume
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
int: Score from 0-100
|
|
163
|
+
"""
|
|
164
|
+
if ratio >= 2.0:
|
|
165
|
+
return 100 # Strong Accumulation
|
|
166
|
+
elif ratio >= 1.5:
|
|
167
|
+
return 80 # Accumulation
|
|
168
|
+
elif ratio >= 1.0:
|
|
169
|
+
return 60 # Neutral/Weak Accumulation
|
|
170
|
+
elif ratio >= 0.7:
|
|
171
|
+
return 40 # Neutral/Weak Distribution
|
|
172
|
+
elif ratio >= 0.5:
|
|
173
|
+
return 20 # Distribution
|
|
174
|
+
else:
|
|
175
|
+
return 0 # Strong Distribution
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def interpret_supply_demand(ratio: float, accumulation: bool) -> str:
|
|
179
|
+
"""
|
|
180
|
+
Generate human-readable interpretation of supply/demand dynamics
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
ratio: Up-day volume / down-day volume
|
|
184
|
+
accumulation: True if accumulation detected
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
str: Interpretation string
|
|
188
|
+
"""
|
|
189
|
+
if ratio >= 2.0:
|
|
190
|
+
return f"Strong Accumulation (ratio: {ratio:.2f}x) - Institutions aggressively buying. Volume precedes price."
|
|
191
|
+
elif ratio >= 1.5:
|
|
192
|
+
return f"Accumulation (ratio: {ratio:.2f}x) - Bullish volume pattern. Institutional interest building."
|
|
193
|
+
elif ratio >= 1.0:
|
|
194
|
+
return f"Neutral/Weak Accumulation (ratio: {ratio:.2f}x) - Slightly positive volume trend."
|
|
195
|
+
elif ratio >= 0.7:
|
|
196
|
+
return f"Neutral/Weak Distribution (ratio: {ratio:.2f}x) - Slightly negative volume trend. Monitor closely."
|
|
197
|
+
elif ratio >= 0.5:
|
|
198
|
+
return f"Distribution (ratio: {ratio:.2f}x) - Bearish volume pattern. Institutions may be selling."
|
|
199
|
+
else:
|
|
200
|
+
return f"Strong Distribution (ratio: {ratio:.2f}x) - Heavy selling pressure. Volume leads price lower."
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
# Example usage
|
|
204
|
+
if __name__ == "__main__":
|
|
205
|
+
# Test with sample data
|
|
206
|
+
sample_data = {
|
|
207
|
+
"historical": [
|
|
208
|
+
{"date": "2024-12-10", "close": 150.0, "volume": 80000000}, # up day
|
|
209
|
+
{"date": "2024-12-09", "close": 148.0, "volume": 50000000}, # down day
|
|
210
|
+
{"date": "2024-12-06", "close": 149.0, "volume": 75000000}, # up day
|
|
211
|
+
{"date": "2024-12-05", "close": 147.0, "volume": 45000000}, # down day
|
|
212
|
+
] * 15 # Repeat to get 60 days
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
result = calculate_supply_demand(sample_data)
|
|
216
|
+
|
|
217
|
+
print("S Component Test Results:")
|
|
218
|
+
print(f" Score: {result['score']}/100")
|
|
219
|
+
print(f" Up/Down Ratio: {result['up_down_ratio']:.2f}")
|
|
220
|
+
print(f" Accumulation: {result['accumulation_detected']}")
|
|
221
|
+
print(f" Interpretation: {result['interpretation']}")
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Finviz Stock Data Client
|
|
4
|
+
|
|
5
|
+
Fetches individual stock data from Finviz including institutional ownership.
|
|
6
|
+
Uses simple web scraping with BeautifulSoup (no API key required).
|
|
7
|
+
|
|
8
|
+
Install: pip install beautifulsoup4 requests lxml
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
from finviz_stock_client import FinvizStockClient
|
|
12
|
+
|
|
13
|
+
client = FinvizStockClient()
|
|
14
|
+
data = client.get_institutional_ownership("AAPL")
|
|
15
|
+
print(f"Inst Own: {data['inst_own_pct']}%")
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import sys
|
|
19
|
+
import time
|
|
20
|
+
import re
|
|
21
|
+
from typing import Dict, Optional
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
import requests
|
|
25
|
+
from bs4 import BeautifulSoup
|
|
26
|
+
except ImportError:
|
|
27
|
+
print("ERROR: required libraries not found. Install with: pip install beautifulsoup4 requests lxml", file=sys.stderr)
|
|
28
|
+
sys.exit(1)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class FinvizStockClient:
|
|
32
|
+
"""Client for fetching stock data from Finviz"""
|
|
33
|
+
|
|
34
|
+
BASE_URL = "https://finviz.com/quote.ashx"
|
|
35
|
+
|
|
36
|
+
def __init__(self, rate_limit_seconds: float = 2.0):
|
|
37
|
+
"""
|
|
38
|
+
Initialize Finviz client
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
rate_limit_seconds: Delay between requests to avoid rate limiting (default: 2.0s)
|
|
42
|
+
"""
|
|
43
|
+
self.rate_limit_seconds = rate_limit_seconds
|
|
44
|
+
self.last_request_time = 0.0
|
|
45
|
+
self.cache = {}
|
|
46
|
+
self.session = requests.Session()
|
|
47
|
+
# Use a realistic user agent to avoid blocking
|
|
48
|
+
self.session.headers.update({
|
|
49
|
+
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
def _rate_limited_fetch(self, symbol: str) -> Optional[Dict]:
|
|
53
|
+
"""
|
|
54
|
+
Fetch stock data with rate limiting
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
symbol: Stock ticker symbol
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
Dict with stock data, or None if fetch failed
|
|
61
|
+
"""
|
|
62
|
+
# Respect rate limit
|
|
63
|
+
elapsed = time.time() - self.last_request_time
|
|
64
|
+
if elapsed < self.rate_limit_seconds:
|
|
65
|
+
time.sleep(self.rate_limit_seconds - elapsed)
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
url = f"{self.BASE_URL}?t={symbol}"
|
|
69
|
+
response = self.session.get(url, timeout=15)
|
|
70
|
+
self.last_request_time = time.time()
|
|
71
|
+
|
|
72
|
+
if response.status_code == 200:
|
|
73
|
+
return self._parse_finviz_page(response.text)
|
|
74
|
+
else:
|
|
75
|
+
print(f"WARNING: Finviz request failed with status {response.status_code} for {symbol}", file=sys.stderr)
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
except Exception as e:
|
|
79
|
+
print(f"WARNING: Failed to fetch Finviz data for {symbol}: {e}", file=sys.stderr)
|
|
80
|
+
self.last_request_time = time.time()
|
|
81
|
+
return None
|
|
82
|
+
|
|
83
|
+
def _parse_finviz_page(self, html: str) -> Dict:
|
|
84
|
+
"""
|
|
85
|
+
Parse Finviz stock page HTML
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
html: HTML content
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Dict with extracted data fields
|
|
92
|
+
"""
|
|
93
|
+
soup = BeautifulSoup(html, 'lxml')
|
|
94
|
+
|
|
95
|
+
# Finviz uses a table structure with label-value pairs
|
|
96
|
+
data = {}
|
|
97
|
+
|
|
98
|
+
# Find all table rows containing data
|
|
99
|
+
tables = soup.find_all('table', {'class': 'snapshot-table2'})
|
|
100
|
+
|
|
101
|
+
for table in tables:
|
|
102
|
+
rows = table.find_all('tr')
|
|
103
|
+
for row in rows:
|
|
104
|
+
cells = row.find_all('td')
|
|
105
|
+
# Each row has pairs of (label, value, label, value, ...)
|
|
106
|
+
for i in range(0, len(cells), 2):
|
|
107
|
+
if i + 1 < len(cells):
|
|
108
|
+
label = cells[i].get_text(strip=True)
|
|
109
|
+
value = cells[i + 1].get_text(strip=True)
|
|
110
|
+
data[label] = value
|
|
111
|
+
|
|
112
|
+
return data
|
|
113
|
+
|
|
114
|
+
def get_institutional_ownership(self, symbol: str) -> Dict:
|
|
115
|
+
"""
|
|
116
|
+
Get institutional ownership data from Finviz
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
symbol: Stock ticker symbol
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Dict with:
|
|
123
|
+
- inst_own_pct: Institutional ownership percentage (float, or None)
|
|
124
|
+
- inst_trans_pct: Institutional transactions percentage (float, or None)
|
|
125
|
+
- error: Error message if data unavailable
|
|
126
|
+
|
|
127
|
+
Example:
|
|
128
|
+
>>> client = FinvizStockClient()
|
|
129
|
+
>>> data = client.get_institutional_ownership("AAPL")
|
|
130
|
+
>>> print(f"Inst Own: {data['inst_own_pct']}%")
|
|
131
|
+
Inst Own: 60.5%
|
|
132
|
+
"""
|
|
133
|
+
# Check cache first
|
|
134
|
+
cache_key = f"finviz_inst_{symbol}"
|
|
135
|
+
if cache_key in self.cache:
|
|
136
|
+
return self.cache[cache_key]
|
|
137
|
+
|
|
138
|
+
# Fetch from Finviz
|
|
139
|
+
stock_data = self._rate_limited_fetch(symbol)
|
|
140
|
+
|
|
141
|
+
if not stock_data:
|
|
142
|
+
result = {
|
|
143
|
+
"inst_own_pct": None,
|
|
144
|
+
"inst_trans_pct": None,
|
|
145
|
+
"error": f"Failed to fetch data from Finviz for {symbol}"
|
|
146
|
+
}
|
|
147
|
+
self.cache[cache_key] = result
|
|
148
|
+
return result
|
|
149
|
+
|
|
150
|
+
# Extract institutional data
|
|
151
|
+
# Finviz returns strings like "60.50%" or "-" if unavailable
|
|
152
|
+
inst_own_str = stock_data.get("Inst Own", "-")
|
|
153
|
+
inst_trans_str = stock_data.get("Inst Trans", "-")
|
|
154
|
+
|
|
155
|
+
# Parse percentage strings
|
|
156
|
+
inst_own_pct = self._parse_percentage(inst_own_str)
|
|
157
|
+
inst_trans_pct = self._parse_percentage(inst_trans_str)
|
|
158
|
+
|
|
159
|
+
result = {
|
|
160
|
+
"inst_own_pct": inst_own_pct,
|
|
161
|
+
"inst_trans_pct": inst_trans_pct,
|
|
162
|
+
"error": None
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
# Cache result
|
|
166
|
+
self.cache[cache_key] = result
|
|
167
|
+
return result
|
|
168
|
+
|
|
169
|
+
@staticmethod
|
|
170
|
+
def _parse_percentage(pct_str: str) -> Optional[float]:
|
|
171
|
+
"""
|
|
172
|
+
Parse percentage string from Finviz
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
pct_str: Percentage string like "60.50%" or "-"
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
Float percentage value (60.50), or None if unavailable
|
|
179
|
+
"""
|
|
180
|
+
if not pct_str or pct_str == "-":
|
|
181
|
+
return None
|
|
182
|
+
|
|
183
|
+
try:
|
|
184
|
+
# Remove '%' and convert to float
|
|
185
|
+
return float(pct_str.rstrip('%'))
|
|
186
|
+
except (ValueError, AttributeError):
|
|
187
|
+
return None
|
|
188
|
+
|
|
189
|
+
def get_stock_data(self, symbol: str) -> Optional[Dict]:
|
|
190
|
+
"""
|
|
191
|
+
Get full stock data dict from Finviz
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
symbol: Stock ticker symbol
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
Dict with all Finviz data fields, or None if unavailable
|
|
198
|
+
"""
|
|
199
|
+
cache_key = f"finviz_full_{symbol}"
|
|
200
|
+
if cache_key in self.cache:
|
|
201
|
+
return self.cache[cache_key]
|
|
202
|
+
|
|
203
|
+
stock_data = self._rate_limited_fetch(symbol)
|
|
204
|
+
if stock_data:
|
|
205
|
+
self.cache[cache_key] = stock_data
|
|
206
|
+
|
|
207
|
+
return stock_data
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
# Example usage
|
|
211
|
+
if __name__ == "__main__":
|
|
212
|
+
import argparse
|
|
213
|
+
|
|
214
|
+
parser = argparse.ArgumentParser(description="Test Finviz institutional ownership fetcher")
|
|
215
|
+
parser.add_argument("symbol", help="Stock ticker symbol")
|
|
216
|
+
args = parser.parse_args()
|
|
217
|
+
|
|
218
|
+
client = FinvizStockClient()
|
|
219
|
+
data = client.get_institutional_ownership(args.symbol)
|
|
220
|
+
|
|
221
|
+
print("\nFinviz Institutional Ownership Data:")
|
|
222
|
+
print(f" Symbol: {args.symbol}")
|
|
223
|
+
print(f" Inst Own: {data['inst_own_pct']}%" if data['inst_own_pct'] is not None else " Inst Own: N/A")
|
|
224
|
+
print(f" Inst Trans: {data['inst_trans_pct']}%" if data['inst_trans_pct'] is not None else " Inst Trans: N/A")
|
|
225
|
+
|
|
226
|
+
if data.get('error'):
|
|
227
|
+
print(f" Error: {data['error']}")
|