alphacouncil-agent 0.9.4 → 1.0.1
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-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +2 -2
- package/.codex-plugin/plugin.json +4 -4
- package/AGENTS.md +25 -11
- package/CHANGELOG.md +146 -0
- package/CLAUDE.md +24 -7
- package/README.ja.md +15 -10
- package/README.md +21 -13
- package/README.zh-CN.md +17 -12
- package/data/authored/core-seats.mjs +808 -0
- package/data/authored/growth-seats.mjs +581 -0
- package/data/authored/index.mjs +114 -0
- package/data/authored/quant-seats.mjs +564 -0
- package/data/authored/value-seats.mjs +663 -0
- package/data/build-profile.v1.json +2 -1
- package/data/council-evaluation-protocol.v1.json +7 -4
- package/data/master-selector-method-locales.v1.mjs +10 -0
- package/data/persona-v3-ai-assisted-solo-profile.v1.json +2 -2
- package/data/persona-v3-build-specs.v1.mjs +60 -3
- package/docs/INSTALL.md +6 -10
- package/docs/personas.md +2 -1
- package/docs/plans/v1.0-roadmap.md +487 -0
- package/docs/plans/v1.1-basket-native-seats.md +267 -0
- package/docs/releases/v0.9.5.md +96 -0
- package/docs/releases/v1.0.0.md +164 -0
- package/docs/report-contract.md +30 -6
- package/knowledge/ai-assisted-solo/experiments/runs/a.json +13 -13
- package/knowledge/ai-assisted-solo/experiments/runs/b.json +2 -2
- package/knowledge/ai-assisted-solo/experiments/runs/c.json +2 -2
- package/knowledge/ai-assisted-solo/experiments/runs/d13.json +126 -126
- package/knowledge/ai-assisted-solo/experiments/runs/d26.json +274 -254
- package/knowledge/ai-assisted-solo/experiments/runs/e-d13.json +129 -129
- package/knowledge/ai-assisted-solo/experiments/runs/e-d26.json +277 -257
- package/knowledge/ai-assisted-solo/experiments/runs/h_ai_reference.json +278 -258
- package/knowledge/ai-assisted-solo/experiments/simulation-input.json +7278 -556
- package/knowledge/ai-assisted-solo/experiments/simulation-manifest.json +22 -22
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_ackman/reviews/{change_value_bridge.ai-review.json → market_capitalisation.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_ackman/reviews/{power_map.ai-review.json → owner_earnings_yield.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_aschenbrenner/reviews/{compute_power_bridge.ai-review.json → market_capitalisation.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_aschenbrenner/reviews/{timeline_reverse_valuation.ai-review.json → price_implied_cash_yield.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_asness/reviews/{crowding_cost_stress.ai-review.json → nominal_earnings_yield_gap.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_asness/reviews/{factor_decomposer.ai-review.json → real_earnings_yield_gap.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_bogle/reviews/expected_return_over_long_bond.ai-review.json +277 -0
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_bogle/reviews/fundamental_expected_return.ai-review.json +277 -0
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_buffett/reviews/{incremental_returns.ai-review.json → market_capitalisation.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_buffett/reviews/{owner_earnings_rebuilder.ai-review.json → owner_earnings_yield.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_burry/reviews/{mispricing_carry.ai-review.json → net_current_asset_surplus_per_share.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_burry/reviews/{capital_structure_reader.ai-review.json → net_current_asset_value_per_share.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_cathie_wood/reviews/{cost_adoption_curve.ai-review.json → current_cash_yield.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_cathie_wood/reviews/{capture_valuation_bridge.ai-review.json → market_capitalisation.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_dalio/reviews/{regime_classifier.ai-review.json → policy_real_rate.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/{master_munger/reviews/incentive_map.ai-review.json → master_dalio/reviews/real_curve_slope.ai-review.json} +51 -51
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_damodaran/reviews/{reverse_valuation.ai-review.json → implied_equity_risk_premium.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_damodaran/reviews/{story_dcf.ai-review.json → premium_versus_long_run_average.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_druckenmiller/reviews/{liquidity_revision_map.ai-review.json → liquidity_curve_impulse.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_druckenmiller/reviews/{inflection_payoff.ai-review.json → policy_real_rate.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_duan_yongping/reviews/{user_value_evidence.ai-review.json → five_year_opportunity_cost.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_duan_yongping/reviews/{opportunity_cost.ai-review.json → margin_retention.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_fisher/reviews/{research_productivity.ai-review.json → margin_retention.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_fisher/reviews/{scuttlebutt_graph.ai-review.json → reinvestment_spread.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/{master_dalio/reviews/debt_driver_stress.ai-review.json → master_forensic_short/reviews/cash_conversion_gap.ai-review.json} +51 -51
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_forensic_short/reviews/{accounting_rebuilder.ai-review.json → cash_cover_of_leverage.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_graham/reviews/{asset_floor.ai-review.json → defensive_hurdle_base.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_graham/reviews/{normalized_earnings.ai-review.json → half_normalised_earnings_yield.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_jhunjhunwala/reviews/{penetration_liquidity.ai-review.json → cash_quality_gap.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_jhunjhunwala/reviews/{promoter_governance.ai-review.json → real_structural_growth.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_klarman/reviews/{recovery_waterfall.ai-review.json → downside_asset_coverage.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_klarman/reviews/{cash_catalyst_compare.ai-review.json → market_capitalisation.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_li_lu/reviews/{promise_integrity_ledger.ai-review.json → cash_backed_decade_return.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_li_lu/reviews/{ten_year_return_bridge.ai-review.json → decade_return_over_long_bond.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_lynch/reviews/{story_numbers_check.ai-review.json → growth_justified_capitalisation.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_lynch/reviews/{category_classifier.ai-review.json → market_capitalisation.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_marks/reviews/{cycle_temperature.ai-review.json → credit_cycle_position.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_marks/reviews/{implied_expectations.ai-review.json → equity_over_corporate_debt.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_munger/reviews/{failure_path_graph.ai-review.json → debt_service_cushion.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_munger/reviews/reported_earnings_cash_gap.ai-review.json +277 -0
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_natenberg/reviews/{surface_builder.ai-review.json → normalised_skew.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_natenberg/reviews/{greeks_payoff.ai-review.json → round_trip_cost.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_pabrai/reviews/{downside_floor.ai-review.json → downside_floor_coverage.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_pabrai/reviews/{discrete_payoff.ai-review.json → market_capitalisation.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_simons/reviews/{leakage_audit.ai-review.json → absolute_session_move.ai-review.json} +42 -42
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_simons/reviews/{oos_cost_test.ai-review.json → session_move_over_implied.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_sinclair/reviews/{realized_vol_forecast.ai-review.json → round_trip_cost.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_sinclair/reviews/{net_edge_sizer.ai-review.json → volatility_per_unit_of_width.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_soros/reviews/{reflexivity_graph.ai-review.json → credit_cycle_position.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_soros/reviews/{reversal_monitor.ai-review.json → financing_burden.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_taleb/reviews/{tail_friction.ai-review.json → debt_service_cushion.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_taleb/reviews/{payoff_ruin.ai-review.json → normalised_skew.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_thorp/reviews/{fractional_kelly.ai-review.json → edge_per_unit_of_volatility.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_thorp/reviews/{edge_recalculator.ai-review.json → net_edge.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/review-manifest.json +288 -270
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-semantic-adjudications/index.json +2 -2
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-semantic-extractions/index.json +2 -2
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-semantic-skeptic-reviews/index.json +2 -2
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-source-prereviews/index.json +6 -2
- package/knowledge/solo-test/masters/master_ackman/decision_policy.json +122 -11
- package/knowledge/solo-test/masters/master_ackman/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_ackman/manifest.json +13 -6
- package/knowledge/solo-test/masters/master_ackman/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_ackman/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_ackman/tools.json +56 -30
- package/knowledge/solo-test/masters/master_aschenbrenner/decision_policy.json +101 -10
- package/knowledge/solo-test/masters/master_aschenbrenner/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_aschenbrenner/manifest.json +12 -4
- package/knowledge/solo-test/masters/master_aschenbrenner/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_aschenbrenner/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_aschenbrenner/tools.json +56 -30
- package/knowledge/solo-test/masters/master_asness/decision_policy.json +89 -10
- package/knowledge/solo-test/masters/master_asness/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_asness/manifest.json +11 -4
- package/knowledge/solo-test/masters/master_asness/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_asness/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_asness/tools.json +56 -30
- package/knowledge/solo-test/masters/master_bogle/counterfactuals.jsonl +0 -0
- package/knowledge/solo-test/masters/master_bogle/decision_cases.jsonl +0 -0
- package/knowledge/solo-test/masters/master_bogle/decision_policy.json +170 -0
- package/knowledge/solo-test/masters/master_bogle/doctrine.jsonl +14 -0
- package/knowledge/solo-test/masters/master_bogle/evaluation/calibration_cases.jsonl +0 -0
- package/knowledge/solo-test/masters/master_bogle/evaluation/experiments.json +5 -0
- package/knowledge/solo-test/masters/master_bogle/evaluation/golden_cases.jsonl +0 -0
- package/knowledge/solo-test/masters/master_bogle/evaluation/pairwise_cases.jsonl +0 -0
- package/knowledge/solo-test/masters/master_bogle/failures.jsonl +0 -0
- package/knowledge/solo-test/masters/master_bogle/manifest.json +141 -0
- package/knowledge/solo-test/masters/master_bogle/memory_policy.json +5 -0
- package/knowledge/solo-test/masters/master_bogle/provisional-index.json +41 -0
- package/knowledge/solo-test/masters/master_bogle/research_policy.json +11 -0
- package/knowledge/solo-test/masters/master_bogle/sources.jsonl +1 -0
- package/knowledge/solo-test/masters/master_bogle/tools.json +120 -0
- package/knowledge/solo-test/masters/master_bogle/voice.en.md +7 -0
- package/knowledge/solo-test/masters/master_bogle/voice.zh.md +7 -0
- package/knowledge/solo-test/masters/master_buffett/decision_policy.json +102 -11
- package/knowledge/solo-test/masters/master_buffett/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_buffett/manifest.json +10 -4
- package/knowledge/solo-test/masters/master_buffett/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_buffett/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_buffett/tools.json +55 -29
- package/knowledge/solo-test/masters/master_burry/decision_policy.json +102 -12
- package/knowledge/solo-test/masters/master_burry/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_burry/manifest.json +12 -6
- package/knowledge/solo-test/masters/master_burry/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_burry/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_burry/tools.json +56 -30
- package/knowledge/solo-test/masters/master_cathie_wood/decision_policy.json +74 -12
- package/knowledge/solo-test/masters/master_cathie_wood/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_cathie_wood/manifest.json +12 -6
- package/knowledge/solo-test/masters/master_cathie_wood/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_cathie_wood/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_cathie_wood/tools.json +56 -30
- package/knowledge/solo-test/masters/master_dalio/decision_policy.json +123 -16
- package/knowledge/solo-test/masters/master_dalio/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_dalio/manifest.json +10 -4
- package/knowledge/solo-test/masters/master_dalio/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_dalio/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_dalio/tools.json +56 -30
- package/knowledge/solo-test/masters/master_damodaran/decision_policy.json +58 -10
- package/knowledge/solo-test/masters/master_damodaran/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_damodaran/manifest.json +9 -4
- package/knowledge/solo-test/masters/master_damodaran/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_damodaran/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_damodaran/tools.json +54 -28
- package/knowledge/solo-test/masters/master_druckenmiller/decision_policy.json +89 -10
- package/knowledge/solo-test/masters/master_druckenmiller/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_druckenmiller/manifest.json +11 -4
- package/knowledge/solo-test/masters/master_druckenmiller/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_druckenmiller/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_druckenmiller/tools.json +55 -29
- package/knowledge/solo-test/masters/master_duan_yongping/decision_policy.json +138 -13
- package/knowledge/solo-test/masters/master_duan_yongping/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_duan_yongping/manifest.json +17 -7
- package/knowledge/solo-test/masters/master_duan_yongping/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_duan_yongping/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_duan_yongping/tools.json +82 -30
- package/knowledge/solo-test/masters/master_fisher/decision_policy.json +102 -11
- package/knowledge/solo-test/masters/master_fisher/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_fisher/manifest.json +13 -6
- package/knowledge/solo-test/masters/master_fisher/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_fisher/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_fisher/tools.json +58 -32
- package/knowledge/solo-test/masters/master_forensic_short/decision_policy.json +93 -14
- package/knowledge/solo-test/masters/master_forensic_short/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_forensic_short/manifest.json +6 -4
- package/knowledge/solo-test/masters/master_forensic_short/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_forensic_short/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_forensic_short/tools.json +53 -27
- package/knowledge/solo-test/masters/master_graham/decision_policy.json +103 -12
- package/knowledge/solo-test/masters/master_graham/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_graham/manifest.json +12 -4
- package/knowledge/solo-test/masters/master_graham/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_graham/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_graham/tools.json +67 -28
- package/knowledge/solo-test/masters/master_jhunjhunwala/decision_policy.json +77 -12
- package/knowledge/solo-test/masters/master_jhunjhunwala/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_jhunjhunwala/manifest.json +11 -6
- package/knowledge/solo-test/masters/master_jhunjhunwala/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_jhunjhunwala/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_jhunjhunwala/tools.json +54 -28
- package/knowledge/solo-test/masters/master_klarman/decision_policy.json +105 -14
- package/knowledge/solo-test/masters/master_klarman/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_klarman/manifest.json +8 -2
- package/knowledge/solo-test/masters/master_klarman/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_klarman/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_klarman/tools.json +55 -29
- package/knowledge/solo-test/masters/master_li_lu/decision_policy.json +101 -10
- package/knowledge/solo-test/masters/master_li_lu/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_li_lu/manifest.json +13 -6
- package/knowledge/solo-test/masters/master_li_lu/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_li_lu/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_li_lu/tools.json +58 -32
- package/knowledge/solo-test/masters/master_lynch/decision_policy.json +127 -16
- package/knowledge/solo-test/masters/master_lynch/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_lynch/manifest.json +14 -6
- package/knowledge/solo-test/masters/master_lynch/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_lynch/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_lynch/tools.json +67 -28
- package/knowledge/solo-test/masters/master_marks/decision_policy.json +71 -9
- package/knowledge/solo-test/masters/master_marks/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_marks/manifest.json +11 -6
- package/knowledge/solo-test/masters/master_marks/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_marks/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_marks/tools.json +54 -28
- package/knowledge/solo-test/masters/master_munger/decision_policy.json +141 -16
- package/knowledge/solo-test/masters/master_munger/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_munger/manifest.json +10 -6
- package/knowledge/solo-test/masters/master_munger/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_munger/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_munger/tools.json +52 -26
- package/knowledge/solo-test/masters/master_natenberg/decision_policy.json +59 -11
- package/knowledge/solo-test/masters/master_natenberg/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_natenberg/manifest.json +9 -6
- package/knowledge/solo-test/masters/master_natenberg/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_natenberg/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_natenberg/tools.json +50 -24
- package/knowledge/solo-test/masters/master_pabrai/decision_policy.json +125 -14
- package/knowledge/solo-test/masters/master_pabrai/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_pabrai/manifest.json +8 -2
- package/knowledge/solo-test/masters/master_pabrai/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_pabrai/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_pabrai/tools.json +55 -29
- package/knowledge/solo-test/masters/master_simons/decision_policy.json +59 -11
- package/knowledge/solo-test/masters/master_simons/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_simons/manifest.json +8 -4
- package/knowledge/solo-test/masters/master_simons/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_simons/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_simons/tools.json +43 -30
- package/knowledge/solo-test/masters/master_sinclair/decision_policy.json +59 -11
- package/knowledge/solo-test/masters/master_sinclair/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_sinclair/manifest.json +8 -6
- package/knowledge/solo-test/masters/master_sinclair/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_sinclair/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_sinclair/tools.json +54 -28
- package/knowledge/solo-test/masters/master_soros/decision_policy.json +98 -10
- package/knowledge/solo-test/masters/master_soros/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_soros/manifest.json +10 -7
- package/knowledge/solo-test/masters/master_soros/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_soros/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_soros/tools.json +54 -28
- package/knowledge/solo-test/masters/master_taleb/decision_policy.json +124 -13
- package/knowledge/solo-test/masters/master_taleb/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_taleb/manifest.json +9 -5
- package/knowledge/solo-test/masters/master_taleb/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_taleb/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_taleb/tools.json +50 -24
- package/knowledge/solo-test/masters/master_thorp/decision_policy.json +61 -13
- package/knowledge/solo-test/masters/master_thorp/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_thorp/manifest.json +11 -6
- package/knowledge/solo-test/masters/master_thorp/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_thorp/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_thorp/tools.json +56 -30
- package/knowledge/solo-test/persona-v3-solo-test-formulas/compilation-manifest.json +290 -264
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_ackman/components/tools.json +58 -32
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_ackman/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_ackman/provisional-derivations/owner_earnings_yield.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_aschenbrenner/components/tools.json +58 -32
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_aschenbrenner/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_aschenbrenner/provisional-derivations/price_implied_cash_yield.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_asness/components/tools.json +58 -32
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_asness/provisional-derivations/nominal_earnings_yield_gap.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_asness/provisional-derivations/real_earnings_yield_gap.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_bogle/components/tools.json +120 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_bogle/provisional-derivations/expected_return_over_long_bond.derived-proxy-evidence.json +77 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_bogle/provisional-derivations/fundamental_expected_return.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_buffett/components/tools.json +57 -31
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_buffett/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_buffett/provisional-derivations/owner_earnings_yield.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_burry/components/tools.json +58 -32
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_burry/provisional-derivations/net_current_asset_surplus_per_share.derived-proxy-evidence.json +77 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_burry/provisional-derivations/net_current_asset_value_per_share.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_cathie_wood/components/tools.json +58 -32
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_cathie_wood/provisional-derivations/current_cash_yield.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_cathie_wood/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_dalio/components/tools.json +58 -32
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_dalio/provisional-derivations/policy_real_rate.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_dalio/provisional-derivations/real_curve_slope.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_damodaran/components/tools.json +56 -30
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_damodaran/provisional-derivations/implied_equity_risk_premium.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_damodaran/provisional-derivations/{reverse_valuation.derived-proxy-evidence.json → premium_versus_long_run_average.derived-proxy-evidence.json} +29 -16
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_druckenmiller/components/tools.json +57 -31
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_druckenmiller/provisional-derivations/liquidity_curve_impulse.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_druckenmiller/provisional-derivations/policy_real_rate.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_duan_yongping/components/tools.json +84 -32
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_duan_yongping/provisional-derivations/five_year_opportunity_cost.derived-proxy-evidence.json +102 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_duan_yongping/provisional-derivations/margin_retention.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_fisher/components/tools.json +60 -34
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_fisher/provisional-derivations/margin_retention.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_fisher/provisional-derivations/reinvestment_spread.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_forensic_short/components/tools.json +55 -29
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_forensic_short/provisional-derivations/{accounting_rebuilder.derived-proxy-evidence.json → cash_conversion_gap.derived-proxy-evidence.json} +28 -15
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_forensic_short/provisional-derivations/cash_cover_of_leverage.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_graham/components/tools.json +69 -30
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_graham/provisional-derivations/defensive_hurdle_base.derived-proxy-evidence.json +89 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_graham/provisional-derivations/half_normalised_earnings_yield.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_jhunjhunwala/components/tools.json +56 -30
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_jhunjhunwala/provisional-derivations/cash_quality_gap.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_jhunjhunwala/provisional-derivations/real_structural_growth.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_klarman/components/tools.json +57 -31
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_klarman/provisional-derivations/downside_asset_coverage.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_klarman/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_li_lu/components/tools.json +60 -34
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_li_lu/provisional-derivations/cash_backed_decade_return.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_li_lu/provisional-derivations/decade_return_over_long_bond.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_lynch/components/tools.json +69 -30
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_lynch/provisional-derivations/growth_justified_capitalisation.derived-proxy-evidence.json +89 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_lynch/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_marks/components/tools.json +56 -30
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_marks/provisional-derivations/credit_cycle_position.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_marks/provisional-derivations/equity_over_corporate_debt.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_munger/components/tools.json +54 -28
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_munger/provisional-derivations/debt_service_cushion.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_munger/provisional-derivations/reported_earnings_cash_gap.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_natenberg/components/tools.json +52 -26
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_natenberg/provisional-derivations/{surface_builder.derived-proxy-evidence.json → normalised_skew.derived-proxy-evidence.json} +27 -14
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_natenberg/provisional-derivations/round_trip_cost.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_pabrai/components/tools.json +57 -31
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_pabrai/provisional-derivations/downside_floor_coverage.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_pabrai/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_simons/components/tools.json +45 -32
- package/knowledge/solo-test/persona-v3-solo-test-formulas/{master_natenberg/provisional-derivations/greeks_payoff.derived-proxy-evidence.json → master_simons/provisional-derivations/absolute_session_move.derived-proxy-evidence.json} +17 -17
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_simons/provisional-derivations/session_move_over_implied.derived-proxy-evidence.json +77 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_sinclair/components/tools.json +56 -30
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_sinclair/provisional-derivations/round_trip_cost.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_sinclair/provisional-derivations/volatility_per_unit_of_width.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_soros/components/tools.json +56 -30
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_soros/provisional-derivations/credit_cycle_position.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_soros/provisional-derivations/financing_burden.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_taleb/components/tools.json +52 -26
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_taleb/provisional-derivations/debt_service_cushion.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_taleb/provisional-derivations/{tail_friction.derived-proxy-evidence.json → normalised_skew.derived-proxy-evidence.json} +27 -14
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_thorp/components/tools.json +58 -32
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_thorp/provisional-derivations/edge_per_unit_of_volatility.derived-proxy-evidence.json +77 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_thorp/provisional-derivations/net_edge.derived-proxy-evidence.json +76 -0
- package/mcp/lib/breadth.mjs +126 -0
- package/mcp/lib/constants.mjs +15 -0
- package/mcp/lib/damodaran.mjs +119 -0
- package/mcp/lib/fred.mjs +246 -0
- package/mcp/lib/fund-holdings-parsers.mjs +297 -0
- package/mcp/lib/fund-observations.mjs +152 -0
- package/mcp/lib/fundamentals.mjs +806 -0
- package/mcp/lib/funds.mjs +785 -0
- package/mcp/lib/gates.mjs +71 -4
- package/mcp/lib/grounding.mjs +209 -36
- package/mcp/lib/index-aggregate.mjs +733 -0
- package/mcp/lib/insider-ownership.mjs +149 -0
- package/mcp/lib/instrument-facts.mjs +855 -0
- package/mcp/lib/instruments.mjs +216 -0
- package/mcp/lib/markdown.mjs +216 -43
- package/mcp/lib/master-catalog.mjs +4 -0
- package/mcp/lib/orchestrator.mjs +173 -29
- package/mcp/lib/packets.mjs +59 -6
- package/mcp/lib/personas/engine.mjs +23 -10
- package/mcp/lib/personas-v3/deterministic-executor.mjs +13 -2
- package/mcp/lib/personas-v3/ga-external-evidence.mjs +3 -2
- package/mcp/lib/personas-v3/ga-gate.mjs +2 -1
- package/mcp/lib/personas-v3/ga-package-evidence.mjs +5 -4
- package/mcp/lib/personas-v3/grounding-adapter.mjs +309 -2
- package/mcp/lib/personas-v3/production-root.mjs +5 -4
- package/mcp/lib/personas-v3/release-formula-evidence.mjs +14 -7
- package/mcp/lib/personas-v3/release-source-evidence.mjs +2 -1
- package/mcp/lib/personas-v3/releases.mjs +5 -4
- package/mcp/lib/personas-v3/runtime-release-integrity.mjs +6 -5
- package/mcp/lib/personas-v3/semantic-source-adjudication.mjs +2 -1
- package/mcp/lib/personas-v3/semantic-source-extraction.mjs +2 -1
- package/mcp/lib/personas-v3/semantic-source-skeptic-review.mjs +2 -1
- package/mcp/lib/personas-v3/source-review-operations.mjs +3 -2
- package/mcp/lib/personas-v3/staging.mjs +22 -3
- package/mcp/lib/prompts.mjs +87 -3
- package/mcp/lib/quotes.mjs +5 -0
- package/mcp/lib/rpc.mjs +27 -10
- package/mcp/lib/run-store.mjs +5 -3
- package/mcp/lib/sec.mjs +32 -0
- package/mcp/lib/tables.mjs +11 -0
- package/mcp/lib/voice.mjs +122 -0
- package/mcp/lib/xbrl-aggregates.mjs +153 -0
- package/mcp/lib/xbrl-series.mjs +341 -0
- package/mcp/lib/xlsx.mjs +155 -0
- package/package.json +5 -5
- package/personas/masters/masters-value/bogle.md +145 -0
- package/schemas/council-evaluation-protocol-v1.schema.json +2 -2
- package/schemas/persona-v3-ai-assisted-solo-profile-v1.schema.json +2 -2
- package/schemas/persona-v3-ai-machine-simulation-v1.schema.json +1 -1
- package/schemas/persona-v3-build-spec-v1.schema.json +3 -3
- package/schemas/persona-v3-release-formula-review-evidence-v1.schema.json +3 -3
- package/schemas/persona-v3-release-manifest-v1.schema.json +6 -6
- package/schemas/persona-v3-release-source-review-evidence-v1.schema.json +3 -3
- package/schemas/persona-v3-tool-graph-v1.schema.json +1 -1
- package/schemas/source-review-batch-v1.schema.json +9 -9
- package/scripts/assemble-persona-v3-release.mjs +3 -2
- package/scripts/build-persona-v3-solo-test-packs.mjs +6 -8
- package/scripts/check-persona-v3-ga.mjs +1 -1
- package/scripts/check-persona-v3-production-candidates.mjs +1 -1
- package/scripts/compile-persona-v3-formulas.mjs +19 -18
- package/scripts/doctor.mjs +11 -9
- package/scripts/export-persona-source-review-batch.mjs +1 -1
- package/scripts/generate-persona-v3-drafts.mjs +2 -2
- package/scripts/lib/build-profile.mjs +24 -0
- package/scripts/lib/council-evaluation-protocol.mjs +7 -6
- package/scripts/lib/host-capabilities.mjs +2 -1
- package/scripts/lib/packaged-host-parity.mjs +9 -3
- package/scripts/lib/persona-v3-ai-assisted-solo-status.mjs +6 -4
- package/scripts/lib/persona-v3-ai-formula-review.mjs +6 -5
- package/scripts/lib/persona-v3-ai-machine-simulations.mjs +32 -4
- package/scripts/lib/persona-v3-build-specs.mjs +3 -2
- package/scripts/lib/persona-v3-draft-packs.mjs +2 -2
- package/scripts/lib/persona-v3-formula-pipeline.mjs +11 -10
- package/scripts/lib/persona-v3-production-candidates.mjs +10 -9
- package/scripts/lib/persona-v3-solo-formula-pipeline.mjs +188 -16
- package/scripts/lib/persona-v3-solo-test-packs.mjs +112 -11
- package/scripts/package-smoke.mjs +2 -1
- package/scripts/report-persona-corpus-gaps.mjs +4 -1
- package/scripts/review-persona-v3-solo-formulas.mjs +12 -8
- package/scripts/scaffold-persona-v3-staging.mjs +1 -1
- package/skills/agent-skills-governance/SKILL.md +8 -2
- package/skills/alphacouncil-agent/SKILL.md +12 -4
- package/skills/public-equity-investing/SKILL.md +17 -0
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_forensic_short/reviews/borrow_catalyst.ai-review.json +0 -277
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_ackman/provisional-derivations/change_value_bridge.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_ackman/provisional-derivations/power_map.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_aschenbrenner/provisional-derivations/compute_power_bridge.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_aschenbrenner/provisional-derivations/timeline_reverse_valuation.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_asness/provisional-derivations/crowding_cost_stress.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_asness/provisional-derivations/factor_decomposer.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_buffett/provisional-derivations/incremental_returns.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_buffett/provisional-derivations/owner_earnings_rebuilder.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_burry/provisional-derivations/capital_structure_reader.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_burry/provisional-derivations/mispricing_carry.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_cathie_wood/provisional-derivations/capture_valuation_bridge.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_cathie_wood/provisional-derivations/cost_adoption_curve.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_dalio/provisional-derivations/debt_driver_stress.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_dalio/provisional-derivations/regime_classifier.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_damodaran/provisional-derivations/story_dcf.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_druckenmiller/provisional-derivations/inflection_payoff.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_druckenmiller/provisional-derivations/liquidity_revision_map.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_duan_yongping/provisional-derivations/opportunity_cost.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_duan_yongping/provisional-derivations/user_value_evidence.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_fisher/provisional-derivations/research_productivity.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_fisher/provisional-derivations/scuttlebutt_graph.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_forensic_short/provisional-derivations/borrow_catalyst.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_graham/provisional-derivations/asset_floor.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_graham/provisional-derivations/normalized_earnings.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_jhunjhunwala/provisional-derivations/penetration_liquidity.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_jhunjhunwala/provisional-derivations/promoter_governance.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_klarman/provisional-derivations/cash_catalyst_compare.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_klarman/provisional-derivations/recovery_waterfall.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_li_lu/provisional-derivations/promise_integrity_ledger.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_li_lu/provisional-derivations/ten_year_return_bridge.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_lynch/provisional-derivations/category_classifier.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_lynch/provisional-derivations/story_numbers_check.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_marks/provisional-derivations/cycle_temperature.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_marks/provisional-derivations/implied_expectations.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_munger/provisional-derivations/failure_path_graph.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_munger/provisional-derivations/incentive_map.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_pabrai/provisional-derivations/discrete_payoff.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_pabrai/provisional-derivations/downside_floor.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_simons/provisional-derivations/leakage_audit.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_simons/provisional-derivations/oos_cost_test.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_sinclair/provisional-derivations/net_edge_sizer.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_sinclair/provisional-derivations/realized_vol_forecast.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_soros/provisional-derivations/reflexivity_graph.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_soros/provisional-derivations/reversal_monitor.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_taleb/provisional-derivations/payoff_ruin.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_thorp/provisional-derivations/edge_recalculator.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_thorp/provisional-derivations/fractional_kelly.derived-proxy-evidence.json +0 -64
|
@@ -0,0 +1,806 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Operating-company fundamentals derived from SEC Company Facts XBRL.
|
|
3
|
+
*
|
|
4
|
+
* screen.mjs eliminates on seven mechanical rules. The method seats need the operating
|
|
5
|
+
* figures those rules never produce -- owner earnings, NCAV, an asset floor, revenue growth,
|
|
6
|
+
* incremental returns, leverage, the share count -- so this module derives them from the
|
|
7
|
+
* same filings, with the arithmetic and the assumptions returned alongside the number.
|
|
8
|
+
*
|
|
9
|
+
* Three properties are enforced in code, not asked for in a comment:
|
|
10
|
+
*
|
|
11
|
+
* 1. `public_at` is the FILING date of the newest input, never the clock. A fiscal year
|
|
12
|
+
* ending 2024-09-28 was not knowable until the 10-K was filed on 2024-11-01, and a
|
|
13
|
+
* metric stamped "now" silently converts a backtest into a measurement of the future.
|
|
14
|
+
* 2. A missing tag is a named gap in `unavailable`, never a zero, never a silent switch to
|
|
15
|
+
* a neighbouring concept, and never a stale period carried forward. The metric is null.
|
|
16
|
+
* 3. Units are explicit: ratios are decimals (0.15, not 15); money carries a currency and
|
|
17
|
+
* a scale; share counts carry the `shares` unit XBRL actually files them under.
|
|
18
|
+
*
|
|
19
|
+
* Tag resolution reuses `annualSeries` from sec.mjs -- the same alias merge, the same
|
|
20
|
+
* 300-400 day annual window, the same look-ahead filter on `filed` that the screen uses.
|
|
21
|
+
* Quarterly extraction is the one thing sec.mjs does not offer, so `quarterlySeries` below
|
|
22
|
+
* is written with deliberately identical semantics rather than a second set of rules.
|
|
23
|
+
*
|
|
24
|
+
* All network access goes through sec.mjs, which owns the ~10 req/s throttle and the
|
|
25
|
+
* User-Agent SEC requires. Nothing here calls fetch directly.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { fetchCompanyFacts, fetchUniverse } from "./sec.mjs";
|
|
29
|
+
import { invalidParams } from "./errors.mjs";
|
|
30
|
+
import {
|
|
31
|
+
ebitdaForYear,
|
|
32
|
+
fourQuarters,
|
|
33
|
+
investedCapital,
|
|
34
|
+
matchedProfits,
|
|
35
|
+
profitForYear,
|
|
36
|
+
tangibleBookForYear,
|
|
37
|
+
totalDebtForYear,
|
|
38
|
+
} from "./xbrl-aggregates.mjs";
|
|
39
|
+
import {
|
|
40
|
+
DEBT_BUCKETS,
|
|
41
|
+
FUNDAMENTAL_CONCEPTS,
|
|
42
|
+
TOOL_ID,
|
|
43
|
+
TOOL_VERSION,
|
|
44
|
+
alignLatest,
|
|
45
|
+
alignYear,
|
|
46
|
+
alignmentGap,
|
|
47
|
+
buildMetric,
|
|
48
|
+
combineAlignments,
|
|
49
|
+
daysBetween,
|
|
50
|
+
decimal,
|
|
51
|
+
fiscalYearOf,
|
|
52
|
+
gap,
|
|
53
|
+
loadSeries,
|
|
54
|
+
publicInstant,
|
|
55
|
+
quarterlySeries,
|
|
56
|
+
sourceIds,
|
|
57
|
+
sourceRecords,
|
|
58
|
+
tagsFor,
|
|
59
|
+
toInstant,
|
|
60
|
+
} from "./xbrl-series.mjs";
|
|
61
|
+
|
|
62
|
+
export { FUNDAMENTAL_CONCEPTS, TOOL_ID, TOOL_VERSION, quarterlySeries };
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
const DAY_MS = 86_400_000;
|
|
66
|
+
|
|
67
|
+
/** Fact ids this module can produce, in the order they are derived. */
|
|
68
|
+
export const FUNDAMENTAL_FACT_IDS = Object.freeze([
|
|
69
|
+
"financial.owner_earnings",
|
|
70
|
+
"financial.net_current_asset_value",
|
|
71
|
+
"valuation.downside_asset_value",
|
|
72
|
+
"valuation.downside_floor",
|
|
73
|
+
"valuation.revenue_growth",
|
|
74
|
+
"financial.incremental_return_on_capital",
|
|
75
|
+
"financial.leverage",
|
|
76
|
+
"capital_allocation.share_count",
|
|
77
|
+
]);
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Maintenance capital expenditure is not a reported line item anywhere in US GAAP. Buffett's
|
|
81
|
+
* owner earnings needs it, so it is proxied -- and because the maintenance/growth split is an
|
|
82
|
+
* assumption rather than a recomputation, `financial.owner_earnings` is labelled `estimated`.
|
|
83
|
+
* The proxy travels with the number so a seat can disagree with the factor instead of the
|
|
84
|
+
* figure.
|
|
85
|
+
*/
|
|
86
|
+
export const MAINTENANCE_CAPEX_PROXY = Object.freeze({
|
|
87
|
+
id: "min_of_current_and_median_capex_times_factor",
|
|
88
|
+
median_window_years: 5,
|
|
89
|
+
minimum_years: 3,
|
|
90
|
+
factor: 0.7,
|
|
91
|
+
formula: "maintenance_capex = min(capex_fy, median(capex over the last 5 fiscal years)) * 0.7",
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
/** Windows and floors that would otherwise be magic numbers inside the derivations. */
|
|
95
|
+
const WINDOWS = Object.freeze({
|
|
96
|
+
revenue_cagr_max_years: 6, // 6 annual points == a 5-year CAGR
|
|
97
|
+
revenue_cagr_min_points: 3,
|
|
98
|
+
incremental_roic_years: 5,
|
|
99
|
+
incremental_roic_min_years: 3,
|
|
100
|
+
/** |change in invested capital| must clear this share of the starting base to be a divisor. */
|
|
101
|
+
incremental_roic_min_denominator_fraction: 0.01,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
/* -------------------------------------------------------------------------- utilities -- */
|
|
106
|
+
|
|
107
|
+
const sum = (values) => values.reduce((total, value) => total + value, 0);
|
|
108
|
+
|
|
109
|
+
const median = (values) => {
|
|
110
|
+
const sorted = [...values].sort((left, right) => left - right);
|
|
111
|
+
const middle = Math.floor(sorted.length / 2);
|
|
112
|
+
return sorted.length % 2 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
const money = (value) => Math.round(value);
|
|
117
|
+
const finite = (value) => typeof value === "number" && Number.isFinite(value);
|
|
118
|
+
|
|
119
|
+
/* ---------------------------------------------------------------------- derived facts -- */
|
|
120
|
+
|
|
121
|
+
/** 1. Buffett owner earnings: net income + D&A - maintenance capex (an estimate, by design). */
|
|
122
|
+
function ownerEarnings(series, cik, gaps, proxy) {
|
|
123
|
+
const factId = "financial.owner_earnings";
|
|
124
|
+
const concepts = ["net_income", "depreciation_amortisation", "capex"];
|
|
125
|
+
const alignment = alignLatest(series, concepts);
|
|
126
|
+
if (!alignment.ok) {
|
|
127
|
+
gaps.push(alignmentGap(factId, alignment, concepts));
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
const window = series.capex
|
|
131
|
+
.filter((entry) => entry.fiscal_year <= alignment.fiscal_year)
|
|
132
|
+
.slice(-proxy.median_window_years);
|
|
133
|
+
if (window.length < proxy.minimum_years) {
|
|
134
|
+
gaps.push(gap({
|
|
135
|
+
factId,
|
|
136
|
+
code: "insufficient_history",
|
|
137
|
+
detail: `maintenance capex needs at least ${proxy.minimum_years} fiscal years of capex; found ${window.length}`,
|
|
138
|
+
missingTags: FUNDAMENTAL_CONCEPTS.capex.tags,
|
|
139
|
+
}));
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
const capexNow = alignment.entries.capex.value;
|
|
143
|
+
const capexMedian = median(window.map((entry) => entry.value));
|
|
144
|
+
const maintenance = Math.min(capexNow, capexMedian) * proxy.factor;
|
|
145
|
+
const netIncome = alignment.entries.net_income.value;
|
|
146
|
+
const depreciation = alignment.entries.depreciation_amortisation.value;
|
|
147
|
+
// The median window can contain a restatement filed after the aligned year's 10-K, so
|
|
148
|
+
// public_at takes the newest filing across everything the number touched.
|
|
149
|
+
const publicAt = [alignment.public_at, ...window.map((entry) => toInstant(entry.filed))]
|
|
150
|
+
.filter(Boolean).sort().at(-1);
|
|
151
|
+
|
|
152
|
+
return buildMetric({
|
|
153
|
+
factId,
|
|
154
|
+
valueKind: "monetary",
|
|
155
|
+
value: money(netIncome + depreciation - maintenance),
|
|
156
|
+
unit: "currency_units",
|
|
157
|
+
currency: "USD",
|
|
158
|
+
scale: 1,
|
|
159
|
+
alignment: { ...alignment, public_at: publicAt, used: [...alignment.used, ...window] },
|
|
160
|
+
cik,
|
|
161
|
+
// Not "rederived": nothing in the filings states the maintenance/growth split, so this is
|
|
162
|
+
// an estimate whose value moves with the proxy. Mislabelling it would let a seat treat an
|
|
163
|
+
// assumption as a recomputation of something the company actually reported.
|
|
164
|
+
derivation: "estimated",
|
|
165
|
+
inputs: {
|
|
166
|
+
net_income: netIncome,
|
|
167
|
+
depreciation_amortisation: depreciation,
|
|
168
|
+
depreciation_tag: alignment.entries.depreciation_amortisation.tag,
|
|
169
|
+
capex_fiscal_year: capexNow,
|
|
170
|
+
capex_median: capexMedian,
|
|
171
|
+
capex_median_years: window.length,
|
|
172
|
+
capex_median_from_fiscal_year: window[0].fiscal_year,
|
|
173
|
+
maintenance_capex: money(maintenance),
|
|
174
|
+
proxy: { ...proxy },
|
|
175
|
+
},
|
|
176
|
+
assumptions: [
|
|
177
|
+
`maintenance capital expenditure is not a reported line item; proxied as ${proxy.formula}`,
|
|
178
|
+
`for FY${alignment.fiscal_year}: min(capex ${money(capexNow)}, ${window.length}-year median ${money(capexMedian)}) * ${proxy.factor} = ${money(maintenance)} USD`,
|
|
179
|
+
"the maintenance/growth split is an assumption, not a recomputation, so this fact is derivation=estimated",
|
|
180
|
+
`depreciation and amortisation taken from ${alignment.entries.depreciation_amortisation.tag}`,
|
|
181
|
+
],
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** 2. Graham NCAV: current assets - total liabilities. */
|
|
186
|
+
function netCurrentAssetValue(series, cik, gaps) {
|
|
187
|
+
const factId = "financial.net_current_asset_value";
|
|
188
|
+
const concepts = ["current_assets", "total_liabilities"];
|
|
189
|
+
const alignment = alignLatest(series, concepts);
|
|
190
|
+
if (!alignment.ok) {
|
|
191
|
+
gaps.push(alignmentGap(factId, alignment, concepts));
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
const currentAssets = alignment.entries.current_assets.value;
|
|
195
|
+
const liabilities = alignment.entries.total_liabilities.value;
|
|
196
|
+
return buildMetric({
|
|
197
|
+
factId,
|
|
198
|
+
valueKind: "monetary",
|
|
199
|
+
value: money(currentAssets - liabilities),
|
|
200
|
+
unit: "currency_units",
|
|
201
|
+
currency: "USD",
|
|
202
|
+
scale: 1,
|
|
203
|
+
alignment,
|
|
204
|
+
cik,
|
|
205
|
+
derivation: "rederived",
|
|
206
|
+
inputs: { current_assets: currentAssets, total_liabilities: liabilities },
|
|
207
|
+
assumptions: [
|
|
208
|
+
"Graham NCAV as filed: current assets less ALL liabilities, with no haircut applied to receivables or inventory",
|
|
209
|
+
"an unclassified balance sheet (banks, insurers, many REITs) files no AssetsCurrent, which is reported as a gap rather than substituted",
|
|
210
|
+
],
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** 3. Klarman-style asset floor: tangible book + cash - total debt. */
|
|
215
|
+
function downsideAssetValue(series, cik, gaps) {
|
|
216
|
+
const factId = "valuation.downside_asset_value";
|
|
217
|
+
const concepts = ["equity", "cash"];
|
|
218
|
+
const alignment = alignLatest(series, concepts);
|
|
219
|
+
if (!alignment.ok) {
|
|
220
|
+
gaps.push(alignmentGap(factId, alignment, concepts));
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
const tangible = tangibleBookForYear(series, alignment.fiscal_year);
|
|
224
|
+
if (tangible?.misaligned) {
|
|
225
|
+
gaps.push(gap({
|
|
226
|
+
factId,
|
|
227
|
+
code: "period_misaligned",
|
|
228
|
+
detail: `${tangible.misaligned} is reported in other periods but not FY${alignment.fiscal_year}, so tangible book cannot be struck for this balance sheet`,
|
|
229
|
+
missingTags: FUNDAMENTAL_CONCEPTS[tangible.misaligned].tags,
|
|
230
|
+
}));
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
const debt = totalDebtForYear(series, alignment.fiscal_year);
|
|
234
|
+
if (!debt) {
|
|
235
|
+
gaps.push(gap({
|
|
236
|
+
factId,
|
|
237
|
+
code: "missing_tag",
|
|
238
|
+
detail: "no debt tag of any kind for the aligned fiscal year; total debt is unknown, which is not the same as zero",
|
|
239
|
+
missingTags: tagsFor(["debt_combined", ...DEBT_BUCKETS]),
|
|
240
|
+
}));
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
const cash = alignment.entries.cash.value;
|
|
244
|
+
const used = [...alignment.used, ...tangible.entries, ...debt.entries];
|
|
245
|
+
return buildMetric({
|
|
246
|
+
factId,
|
|
247
|
+
valueKind: "monetary",
|
|
248
|
+
value: money(tangible.value + cash - debt.value),
|
|
249
|
+
unit: "currency_units",
|
|
250
|
+
currency: "USD",
|
|
251
|
+
scale: 1,
|
|
252
|
+
alignment: { ...alignment, used },
|
|
253
|
+
cik,
|
|
254
|
+
derivation: "rederived",
|
|
255
|
+
inputs: {
|
|
256
|
+
tangible_book_value: money(tangible.value),
|
|
257
|
+
equity: tangible.equity,
|
|
258
|
+
tangible_deductions: tangible.deductions,
|
|
259
|
+
cash,
|
|
260
|
+
cash_tag: alignment.entries.cash.tag,
|
|
261
|
+
total_debt: debt.value,
|
|
262
|
+
debt_buckets: debt.buckets,
|
|
263
|
+
},
|
|
264
|
+
assumptions: [
|
|
265
|
+
"construction is tangible book + cash - total debt, as specified for this fact",
|
|
266
|
+
"cash is already inside tangible book, so this deliberately counts the cash balance twice: it is a downside cushion, not a balance-sheet identity",
|
|
267
|
+
...tangible.assumptions,
|
|
268
|
+
debt.missing_buckets.length
|
|
269
|
+
? `total debt summed from ${debt.buckets.join(", ")}; ${debt.missing_buckets.join(", ")} not reported for this period`
|
|
270
|
+
: `total debt summed from ${debt.buckets.join(", ")}`,
|
|
271
|
+
],
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** Read an already-built metric back as an alignment so a second fact can combine with it. */
|
|
276
|
+
function alignmentOfMetric(metric) {
|
|
277
|
+
return {
|
|
278
|
+
ok: true,
|
|
279
|
+
fiscal_year: metric.fiscal_year,
|
|
280
|
+
period_start: metric.period_start,
|
|
281
|
+
period_end: metric.period_end,
|
|
282
|
+
public_at: metric.public_at,
|
|
283
|
+
entries: {},
|
|
284
|
+
used: metric.source_records.map((record) => ({
|
|
285
|
+
concept: record.concept,
|
|
286
|
+
tag: record.tag,
|
|
287
|
+
unit: record.unit,
|
|
288
|
+
start: null,
|
|
289
|
+
end: record.period_end,
|
|
290
|
+
filed: record.filed,
|
|
291
|
+
accession: record.accession,
|
|
292
|
+
value: record.value,
|
|
293
|
+
fiscal_year: fiscalYearOf(record.period_end),
|
|
294
|
+
})),
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/** 4. Pabrai-style floor: min(NCAV, tangible book), reporting which term bound it. */
|
|
299
|
+
function downsideFloor(series, cik, gaps, ncav) {
|
|
300
|
+
const factId = "valuation.downside_floor";
|
|
301
|
+
if (!ncav) {
|
|
302
|
+
gaps.push(gap({
|
|
303
|
+
factId,
|
|
304
|
+
code: "missing_input",
|
|
305
|
+
detail: "net current asset value could not be computed, and the minimum of an unknown is unknown",
|
|
306
|
+
missingTags: tagsFor(["current_assets", "total_liabilities"]),
|
|
307
|
+
}));
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
const equityAlignment = alignLatest(series, ["equity"]);
|
|
311
|
+
if (!equityAlignment.ok) {
|
|
312
|
+
gaps.push(alignmentGap(factId, equityAlignment, ["equity"]));
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
const tangible = tangibleBookForYear(series, equityAlignment.fiscal_year);
|
|
316
|
+
if (tangible?.misaligned) {
|
|
317
|
+
gaps.push(gap({
|
|
318
|
+
factId,
|
|
319
|
+
code: "period_misaligned",
|
|
320
|
+
detail: `${tangible.misaligned} is reported in other periods but not FY${equityAlignment.fiscal_year}`,
|
|
321
|
+
missingTags: FUNDAMENTAL_CONCEPTS[tangible.misaligned].tags,
|
|
322
|
+
}));
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
const tangibleAlignment = { ...equityAlignment, used: tangible.entries };
|
|
326
|
+
const combined = combineAlignments([alignmentOfMetric(ncav), tangibleAlignment]);
|
|
327
|
+
if (!combined.ok) {
|
|
328
|
+
gaps.push(alignmentGap(factId, combined, ["current_assets", "equity"]));
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
const terms = [
|
|
332
|
+
{ term: "net_current_asset_value", value: ncav.value },
|
|
333
|
+
{ term: "tangible_book_value", value: money(tangible.value) },
|
|
334
|
+
];
|
|
335
|
+
const binding = terms.reduce((low, item) => (item.value < low.value ? item : low));
|
|
336
|
+
return buildMetric({
|
|
337
|
+
factId,
|
|
338
|
+
valueKind: "monetary",
|
|
339
|
+
value: binding.value,
|
|
340
|
+
unit: "currency_units",
|
|
341
|
+
currency: "USD",
|
|
342
|
+
scale: 1,
|
|
343
|
+
alignment: combined,
|
|
344
|
+
cik,
|
|
345
|
+
derivation: "rederived",
|
|
346
|
+
inputs: {
|
|
347
|
+
net_current_asset_value: ncav.value,
|
|
348
|
+
tangible_book_value: money(tangible.value),
|
|
349
|
+
bound_by: binding.term,
|
|
350
|
+
},
|
|
351
|
+
components: { bound_by: binding.term, terms },
|
|
352
|
+
assumptions: [
|
|
353
|
+
"floor is min(NCAV, tangible book value); the binding term is reported in components.bound_by",
|
|
354
|
+
"no liquidation haircut and no EV/EBIT term is applied, so this is an accounting floor, not a realisable price",
|
|
355
|
+
...tangible.assumptions,
|
|
356
|
+
],
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* The compound annual growth rate across the available annual revenue history.
|
|
362
|
+
*
|
|
363
|
+
* The exponent is the measured day span, not the number of periods: 52/53-week fiscal
|
|
364
|
+
* calendars and stub years make "five filings" and "five years" different numbers, and using
|
|
365
|
+
* the filing count biases every growth rate in the same direction.
|
|
366
|
+
*/
|
|
367
|
+
function revenueCagr(annual, factId, gaps) {
|
|
368
|
+
if (annual.length < WINDOWS.revenue_cagr_min_points) {
|
|
369
|
+
gaps.push(gap({
|
|
370
|
+
factId,
|
|
371
|
+
code: annual.length ? "insufficient_history" : "missing_tag",
|
|
372
|
+
detail: `a revenue CAGR needs at least ${WINDOWS.revenue_cagr_min_points} annual periods; found ${annual.length}`,
|
|
373
|
+
missingTags: FUNDAMENTAL_CONCEPTS.revenue.tags,
|
|
374
|
+
}));
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
const first = annual[0];
|
|
378
|
+
const last = annual.at(-1);
|
|
379
|
+
if (!(first.value > 0) || !(last.value > 0)) {
|
|
380
|
+
gaps.push(gap({
|
|
381
|
+
factId,
|
|
382
|
+
code: "non_positive_base",
|
|
383
|
+
detail: `a CAGR is undefined across a non-positive revenue (${first.value} -> ${last.value})`,
|
|
384
|
+
}));
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
const spanDays = daysBetween(first.end, last.end);
|
|
388
|
+
if (spanDays <= 0) {
|
|
389
|
+
gaps.push(gap({ factId, code: "period_misaligned", detail: "revenue periods do not advance in time" }));
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
const publicAt = publicInstant(annual);
|
|
393
|
+
if (!publicAt) {
|
|
394
|
+
gaps.push(gap({ factId, code: "missing_filing_date", detail: "a revenue period carries no filing date" }));
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
const years = spanDays / 365.25;
|
|
398
|
+
return { cagr: (last.value / first.value) ** (1 / years) - 1, first, last, spanDays, years, publicAt };
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/** 5. Revenue growth: 5-year CAGR as the value, trailing-4-quarter YoY as a component. */
|
|
402
|
+
function revenueGrowth(companyFacts, series, cik, gaps, asOf) {
|
|
403
|
+
const factId = "valuation.revenue_growth";
|
|
404
|
+
const annual = series.revenue.slice(-WINDOWS.revenue_cagr_max_years);
|
|
405
|
+
const compound = revenueCagr(annual, factId, gaps);
|
|
406
|
+
if (!compound) return null;
|
|
407
|
+
const { cagr, first, last, spanDays, years, publicAt } = compound;
|
|
408
|
+
const yoy = trailingYearOverYear(companyFacts, asOf);
|
|
409
|
+
if (yoy.value === null && yoy.gap) gaps.push(gap({ factId, component: "yoy_ttm", ...yoy.gap }));
|
|
410
|
+
|
|
411
|
+
const alignment = {
|
|
412
|
+
ok: true,
|
|
413
|
+
fiscal_year: last.fiscal_year,
|
|
414
|
+
period_start: first.start || first.end,
|
|
415
|
+
period_end: last.end,
|
|
416
|
+
public_at: [publicAt, yoy.public_at].filter(Boolean).sort().at(-1),
|
|
417
|
+
entries: {},
|
|
418
|
+
used: [...annual, ...(yoy.entries || [])],
|
|
419
|
+
};
|
|
420
|
+
return buildMetric({
|
|
421
|
+
factId,
|
|
422
|
+
valueKind: "ratio",
|
|
423
|
+
value: decimal(cagr),
|
|
424
|
+
unit: "decimal",
|
|
425
|
+
ratioDenominator: "compound_annual_revenue_growth",
|
|
426
|
+
alignment,
|
|
427
|
+
cik,
|
|
428
|
+
derivation: "rederived",
|
|
429
|
+
inputs: {
|
|
430
|
+
revenue_start: first.value,
|
|
431
|
+
revenue_start_period_end: first.end,
|
|
432
|
+
revenue_end: last.value,
|
|
433
|
+
revenue_end_period_end: last.end,
|
|
434
|
+
span_days: spanDays,
|
|
435
|
+
years: decimal(years),
|
|
436
|
+
},
|
|
437
|
+
components: {
|
|
438
|
+
cagr: decimal(cagr),
|
|
439
|
+
cagr_years: decimal(years),
|
|
440
|
+
yoy_ttm: yoy.value === null ? null : decimal(yoy.value),
|
|
441
|
+
yoy_ttm_latest: yoy.latest ?? null,
|
|
442
|
+
yoy_ttm_prior: yoy.prior ?? null,
|
|
443
|
+
},
|
|
444
|
+
assumptions: [
|
|
445
|
+
`value is the compound annual growth rate over ${decimal(years)} years (${first.end} -> ${last.end}), expressed as a decimal`,
|
|
446
|
+
"the exponent uses the measured day span, not the count of filings, because fiscal calendars are 52 or 53 weeks",
|
|
447
|
+
yoy.value === null
|
|
448
|
+
? "trailing-four-quarter year-over-year is unavailable and reported as a gap rather than substituted for the CAGR"
|
|
449
|
+
: "components.yoy_ttm is the sum of the latest four quarters against the four before them, as a decimal",
|
|
450
|
+
],
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/** Trailing-four-quarter revenue against the four quarters before it. */
|
|
455
|
+
function trailingYearOverYear(companyFacts, asOf) {
|
|
456
|
+
const quarters = quarterlySeries(companyFacts, FUNDAMENTAL_CONCEPTS.revenue.tags, { asOf, unit: "USD" });
|
|
457
|
+
const empty = { value: null, public_at: null, entries: [] };
|
|
458
|
+
if (!quarters || quarters.length < 8) {
|
|
459
|
+
return {
|
|
460
|
+
...empty,
|
|
461
|
+
gap: {
|
|
462
|
+
code: "insufficient_history",
|
|
463
|
+
detail: `a trailing-four-quarter comparison needs 8 tagged quarters; found ${quarters?.length || 0}`,
|
|
464
|
+
missingTags: FUNDAMENTAL_CONCEPTS.revenue.tags,
|
|
465
|
+
},
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
const latest = fourQuarters(quarters, quarters.length - 1);
|
|
469
|
+
const prior = fourQuarters(quarters, quarters.length - 5);
|
|
470
|
+
if (!latest || !prior) {
|
|
471
|
+
return { ...empty, gap: { code: "period_misaligned", detail: "the eight most recent quarters are not contiguous four-quarter blocks" } };
|
|
472
|
+
}
|
|
473
|
+
if (!(prior.total > 0)) {
|
|
474
|
+
return { ...empty, gap: { code: "non_positive_base", detail: `prior-year four-quarter revenue is ${prior.total}` } };
|
|
475
|
+
}
|
|
476
|
+
const entries = [...prior.window, ...latest.window].map((entry) => ({
|
|
477
|
+
concept: "revenue",
|
|
478
|
+
tag: entry.tag,
|
|
479
|
+
unit: "USD",
|
|
480
|
+
start: entry.start,
|
|
481
|
+
end: entry.end,
|
|
482
|
+
filed: entry.filed,
|
|
483
|
+
accession: entry.accn || null,
|
|
484
|
+
value: entry.val,
|
|
485
|
+
fiscal_year: fiscalYearOf(entry.end),
|
|
486
|
+
}));
|
|
487
|
+
return {
|
|
488
|
+
value: latest.total / prior.total - 1,
|
|
489
|
+
latest: latest.total,
|
|
490
|
+
prior: prior.total,
|
|
491
|
+
public_at: publicInstant(entries),
|
|
492
|
+
entries,
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* The two endpoints of the incremental-return window, or null with a named gap.
|
|
498
|
+
*
|
|
499
|
+
* The window is stated rather than assumed: it reaches back `incremental_roic_years` and
|
|
500
|
+
* settles for the oldest year available only while that is still at least
|
|
501
|
+
* `incremental_roic_min_years` apart, because a one-year "incremental return" is noise.
|
|
502
|
+
*/
|
|
503
|
+
function incrementalEndpoints(series, factId, gaps) {
|
|
504
|
+
const concepts = ["equity", "cash"];
|
|
505
|
+
const latest = alignLatest(series, concepts);
|
|
506
|
+
if (!latest.ok) {
|
|
507
|
+
gaps.push(alignmentGap(factId, latest, concepts));
|
|
508
|
+
return null;
|
|
509
|
+
}
|
|
510
|
+
const endYear = latest.fiscal_year;
|
|
511
|
+
const wanted = endYear - WINDOWS.incremental_roic_years;
|
|
512
|
+
const candidates = series.equity
|
|
513
|
+
.map((entry) => entry.fiscal_year)
|
|
514
|
+
.filter((year) => year <= wanted && endYear - year >= WINDOWS.incremental_roic_min_years);
|
|
515
|
+
const startYear = candidates.length
|
|
516
|
+
? Math.max(...candidates)
|
|
517
|
+
: Math.min(...series.equity.map((entry) => entry.fiscal_year));
|
|
518
|
+
if (endYear - startYear < WINDOWS.incremental_roic_min_years) {
|
|
519
|
+
gaps.push(gap({
|
|
520
|
+
factId,
|
|
521
|
+
code: "insufficient_history",
|
|
522
|
+
detail: `an incremental return needs at least ${WINDOWS.incremental_roic_min_years} years between endpoints; FY${startYear} to FY${endYear} is ${endYear - startYear}`,
|
|
523
|
+
}));
|
|
524
|
+
return null;
|
|
525
|
+
}
|
|
526
|
+
const [start, end] = [startYear, endYear].map((year) => investedCapital(series, year));
|
|
527
|
+
if (!start || !end) {
|
|
528
|
+
gaps.push(gap({
|
|
529
|
+
factId,
|
|
530
|
+
code: "missing_tag",
|
|
531
|
+
detail: `invested capital (equity + total debt - cash) is not computable for FY${startYear} and FY${endYear} together`,
|
|
532
|
+
missingTags: tagsFor(["equity", "cash", "debt_combined", ...DEBT_BUCKETS]),
|
|
533
|
+
}));
|
|
534
|
+
return null;
|
|
535
|
+
}
|
|
536
|
+
const profits = matchedProfits(series, startYear, endYear);
|
|
537
|
+
if (!profits) {
|
|
538
|
+
gaps.push(gap({
|
|
539
|
+
factId,
|
|
540
|
+
code: "missing_tag",
|
|
541
|
+
detail: `neither NOPAT nor net income is available for both FY${startYear} and FY${endYear}`,
|
|
542
|
+
missingTags: tagsFor(["operating_income", "tax_expense", "pretax_income", "net_income"]),
|
|
543
|
+
}));
|
|
544
|
+
return null;
|
|
545
|
+
}
|
|
546
|
+
return { startYear, endYear, start, end, ...profits };
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/** 6. Incremental return on capital: change in profit over change in invested capital. */
|
|
550
|
+
function incrementalReturnOnCapital(series, cik, gaps) {
|
|
551
|
+
const factId = "financial.incremental_return_on_capital";
|
|
552
|
+
const window = incrementalEndpoints(series, factId, gaps);
|
|
553
|
+
if (!window) return null;
|
|
554
|
+
const { startYear, endYear, start, end, basis, profits } = window;
|
|
555
|
+
|
|
556
|
+
const capitalChange = end.value - start.value;
|
|
557
|
+
const floor = Math.max(Math.abs(start.value) * WINDOWS.incremental_roic_min_denominator_fraction, 1);
|
|
558
|
+
if (Math.abs(capitalChange) < floor) {
|
|
559
|
+
// A near-zero denominator produces an enormous ratio that reads as a spectacular business.
|
|
560
|
+
gaps.push(gap({
|
|
561
|
+
factId,
|
|
562
|
+
code: "denominator_near_zero",
|
|
563
|
+
detail: `invested capital changed by ${money(capitalChange)} against a FY${startYear} base of ${money(start.value)}, below the ${WINDOWS.incremental_roic_min_denominator_fraction * 100}% floor; a ratio here would be an artefact of the divisor`,
|
|
564
|
+
}));
|
|
565
|
+
return null;
|
|
566
|
+
}
|
|
567
|
+
const profitChange = profits[1].value - profits[0].value;
|
|
568
|
+
const used = [...start.entries, ...end.entries, ...profits[0].entries, ...profits[1].entries];
|
|
569
|
+
const publicAt = publicInstant(used);
|
|
570
|
+
if (!publicAt) {
|
|
571
|
+
gaps.push(gap({ factId, code: "missing_filing_date", detail: "an endpoint carries no filing date" }));
|
|
572
|
+
return null;
|
|
573
|
+
}
|
|
574
|
+
return buildMetric({
|
|
575
|
+
factId,
|
|
576
|
+
valueKind: "ratio",
|
|
577
|
+
value: decimal(profitChange / capitalChange),
|
|
578
|
+
unit: "decimal",
|
|
579
|
+
ratioDenominator: `change_in_invested_capital_fy${startYear}_to_fy${endYear}`,
|
|
580
|
+
alignment: {
|
|
581
|
+
ok: true,
|
|
582
|
+
fiscal_year: endYear,
|
|
583
|
+
period_start: start.period_end,
|
|
584
|
+
period_end: end.period_end,
|
|
585
|
+
public_at: publicAt,
|
|
586
|
+
entries: {},
|
|
587
|
+
used,
|
|
588
|
+
},
|
|
589
|
+
cik,
|
|
590
|
+
derivation: "rederived",
|
|
591
|
+
inputs: {
|
|
592
|
+
profit_basis: basis,
|
|
593
|
+
profit_start: money(profits[0].value),
|
|
594
|
+
profit_end: money(profits[1].value),
|
|
595
|
+
profit_change: money(profitChange),
|
|
596
|
+
invested_capital_start: money(start.value),
|
|
597
|
+
invested_capital_end: money(end.value),
|
|
598
|
+
invested_capital_change: money(capitalChange),
|
|
599
|
+
window_years: endYear - startYear,
|
|
600
|
+
},
|
|
601
|
+
assumptions: [
|
|
602
|
+
`change in ${basis === "nopat" ? "NOPAT" : "net income"} over change in invested capital across FY${startYear} to FY${endYear} (${endYear - startYear} years)`,
|
|
603
|
+
basis === "nopat"
|
|
604
|
+
? "NOPAT is operating income times (1 - effective tax rate), the rate taken from income tax expense over pre-tax income"
|
|
605
|
+
: "NOPAT inputs (operating income, tax expense, pre-tax income) were unavailable at one or both endpoints, so net income is the profit basis",
|
|
606
|
+
"invested capital is book equity + total debt - cash and equivalents",
|
|
607
|
+
`a change in invested capital below ${WINDOWS.incremental_roic_min_denominator_fraction * 100}% of the starting base is refused rather than divided by`,
|
|
608
|
+
],
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/** 7. Leverage: total debt / equity, with net debt / EBITDA when its inputs exist. */
|
|
613
|
+
function leverage(series, cik, gaps) {
|
|
614
|
+
const factId = "financial.leverage";
|
|
615
|
+
const alignment = alignLatest(series, ["equity"]);
|
|
616
|
+
if (!alignment.ok) {
|
|
617
|
+
gaps.push(alignmentGap(factId, alignment, ["equity"]));
|
|
618
|
+
return null;
|
|
619
|
+
}
|
|
620
|
+
const debt = totalDebtForYear(series, alignment.fiscal_year);
|
|
621
|
+
if (!debt) {
|
|
622
|
+
gaps.push(gap({
|
|
623
|
+
factId,
|
|
624
|
+
code: "missing_tag",
|
|
625
|
+
detail: "no debt tag of any kind for the aligned fiscal year; unknown debt is not zero debt",
|
|
626
|
+
missingTags: tagsFor(["debt_combined", ...DEBT_BUCKETS]),
|
|
627
|
+
}));
|
|
628
|
+
return null;
|
|
629
|
+
}
|
|
630
|
+
const equity = alignment.entries.equity.value;
|
|
631
|
+
if (!(equity > 0)) {
|
|
632
|
+
gaps.push(gap({
|
|
633
|
+
factId,
|
|
634
|
+
code: "non_positive_base",
|
|
635
|
+
detail: `book equity is ${money(equity)}; debt/equity is uninterpretable at or below zero equity`,
|
|
636
|
+
}));
|
|
637
|
+
return null;
|
|
638
|
+
}
|
|
639
|
+
const multiple = netDebtToEbitda(series, alignment.fiscal_year, debt, factId, gaps);
|
|
640
|
+
const { cash, ebitda } = multiple;
|
|
641
|
+
const used = [...alignment.used, ...debt.entries, ...multiple.entries];
|
|
642
|
+
const publicAt = publicInstant(used);
|
|
643
|
+
if (!publicAt) {
|
|
644
|
+
gaps.push(gap({ factId, code: "missing_filing_date", detail: "an input carries no filing date" }));
|
|
645
|
+
return null;
|
|
646
|
+
}
|
|
647
|
+
return buildMetric({
|
|
648
|
+
factId,
|
|
649
|
+
valueKind: "ratio",
|
|
650
|
+
value: decimal(debt.value / equity),
|
|
651
|
+
unit: "decimal",
|
|
652
|
+
ratioDenominator: "book_equity",
|
|
653
|
+
alignment: { ...alignment, public_at: publicAt, used },
|
|
654
|
+
cik,
|
|
655
|
+
derivation: "rederived",
|
|
656
|
+
inputs: {
|
|
657
|
+
total_debt: debt.value,
|
|
658
|
+
debt_buckets: debt.buckets,
|
|
659
|
+
equity,
|
|
660
|
+
cash: cash ? cash.value : null,
|
|
661
|
+
ebitda: ebitda ? money(ebitda.value) : null,
|
|
662
|
+
net_debt: cash ? debt.value - cash.value : null,
|
|
663
|
+
},
|
|
664
|
+
components: { debt_to_equity: decimal(debt.value / equity), net_debt_to_ebitda: multiple.value },
|
|
665
|
+
assumptions: [
|
|
666
|
+
"value is total debt divided by book equity, as a decimal (0.5 means half a turn, not 50)",
|
|
667
|
+
debt.missing_buckets.length
|
|
668
|
+
? `total debt summed from ${debt.buckets.join(", ")}; ${debt.missing_buckets.join(", ")} not reported for this period`
|
|
669
|
+
: `total debt summed from ${debt.buckets.join(", ")}`,
|
|
670
|
+
"operating leases are included only where a filer tags them inside a capital-lease debt concept",
|
|
671
|
+
multiple.value === null
|
|
672
|
+
? "net debt / EBITDA is unavailable and reported as a gap rather than defaulted"
|
|
673
|
+
: "EBITDA is operating income plus depreciation and amortisation, not an adjusted figure",
|
|
674
|
+
],
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* Net debt / EBITDA, or null with a named component gap. Losing the multiple never takes the
|
|
680
|
+
* whole leverage fact down -- debt/equity is still a figure the filing supports.
|
|
681
|
+
*/
|
|
682
|
+
function netDebtToEbitda(series, fiscalYear, debt, factId, gaps) {
|
|
683
|
+
const cash = series.cash.find((entry) => entry.fiscal_year === fiscalYear) || null;
|
|
684
|
+
const ebitda = ebitdaForYear(series, fiscalYear);
|
|
685
|
+
const decline = (code, detail, missingTags = []) => {
|
|
686
|
+
gaps.push(gap({ factId, component: "net_debt_to_ebitda", code, detail, missingTags }));
|
|
687
|
+
return { value: null, cash, ebitda, entries: [] };
|
|
688
|
+
};
|
|
689
|
+
if (!cash) {
|
|
690
|
+
return decline("missing_tag", "cash is not reported for the aligned fiscal year, so net debt cannot be struck", FUNDAMENTAL_CONCEPTS.cash.tags);
|
|
691
|
+
}
|
|
692
|
+
if (!ebitda) {
|
|
693
|
+
return decline("missing_tag", "operating income and/or depreciation are not reported for the aligned fiscal year, so EBITDA cannot be struck", tagsFor(["operating_income", "depreciation_amortisation"]));
|
|
694
|
+
}
|
|
695
|
+
if (!(ebitda.value > 0)) {
|
|
696
|
+
return decline("non_positive_base", `EBITDA is ${money(ebitda.value)}; a multiple against non-positive EBITDA is meaningless`);
|
|
697
|
+
}
|
|
698
|
+
return {
|
|
699
|
+
value: decimal((debt.value - cash.value) / ebitda.value),
|
|
700
|
+
cash,
|
|
701
|
+
ebitda,
|
|
702
|
+
entries: [cash, ...ebitda.entries],
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/** 8. Latest diluted share count, exactly as filed. */
|
|
707
|
+
function shareCount(series, cik, gaps) {
|
|
708
|
+
const factId = "capital_allocation.share_count";
|
|
709
|
+
const alignment = alignLatest(series, ["diluted_shares"]);
|
|
710
|
+
if (!alignment.ok) {
|
|
711
|
+
gaps.push(alignmentGap(factId, alignment, ["diluted_shares"]));
|
|
712
|
+
return null;
|
|
713
|
+
}
|
|
714
|
+
const entry = alignment.entries.diluted_shares;
|
|
715
|
+
return buildMetric({
|
|
716
|
+
factId,
|
|
717
|
+
valueKind: "count",
|
|
718
|
+
value: Math.round(entry.value),
|
|
719
|
+
unit: "shares",
|
|
720
|
+
// A count is carried as a point-in-time quantity rather than as a span. It exists to be a
|
|
721
|
+
// denominator under a market capitalisation, where what matters is how many shares there
|
|
722
|
+
// are and not which window they were averaged over -- and a fund's share count, which is
|
|
723
|
+
// a genuine instant, could satisfy no duration contract at all. The averaging basis stays
|
|
724
|
+
// stated in `assumptions` rather than being implied by an interval.
|
|
725
|
+
alignment: { ...alignment, suppressInterval: true },
|
|
726
|
+
cik,
|
|
727
|
+
derivation: "reported",
|
|
728
|
+
inputs: { diluted_shares: entry.value, tag: entry.tag },
|
|
729
|
+
assumptions: [
|
|
730
|
+
`${entry.tag} is the weighted average diluted count over the fiscal period, not a point-in-time count on the period end date`,
|
|
731
|
+
"taken from the annual filing; it does not reflect issuance or buybacks after the period end",
|
|
732
|
+
],
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/* ---------------------------------------------------------------------------- exports -- */
|
|
737
|
+
|
|
738
|
+
function resolveCik(companyFacts) {
|
|
739
|
+
const digits = String(companyFacts?.cik ?? "").replace(/\D/gu, "");
|
|
740
|
+
return digits ? digits.padStart(10, "0") : "unknown";
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* Derive every fundamental this module knows how to compute from one Company Facts document.
|
|
745
|
+
*
|
|
746
|
+
* Pure: no clock, no network, no mutation of the input. Returns one object so a caller can
|
|
747
|
+
* wire `metrics` into the typed-fact adapter and surface `unavailable` as declared gaps.
|
|
748
|
+
* Every fact id is always present in `metrics`; one that could not be computed is `null` and
|
|
749
|
+
* has at least one matching entry in `unavailable`.
|
|
750
|
+
*/
|
|
751
|
+
export function deriveFundamentals({ companyFacts, asOf = null, maintenanceCapexProxy = MAINTENANCE_CAPEX_PROXY } = {}) {
|
|
752
|
+
if (!companyFacts || typeof companyFacts !== "object" || Array.isArray(companyFacts)) {
|
|
753
|
+
throw invalidParams("deriveFundamentals needs a companyFacts object from SEC Company Facts");
|
|
754
|
+
}
|
|
755
|
+
if (asOf !== null && !Number.isFinite(Date.parse(String(asOf)))) {
|
|
756
|
+
throw invalidParams(`deriveFundamentals asOf is not a date: ${JSON.stringify(asOf)}`);
|
|
757
|
+
}
|
|
758
|
+
const series = loadSeries(companyFacts, asOf);
|
|
759
|
+
const cik = resolveCik(companyFacts);
|
|
760
|
+
const gaps = [];
|
|
761
|
+
|
|
762
|
+
const ncav = netCurrentAssetValue(series, cik, gaps);
|
|
763
|
+
const metrics = {
|
|
764
|
+
"financial.owner_earnings": ownerEarnings(series, cik, gaps, maintenanceCapexProxy),
|
|
765
|
+
"financial.net_current_asset_value": ncav,
|
|
766
|
+
"valuation.downside_asset_value": downsideAssetValue(series, cik, gaps),
|
|
767
|
+
"valuation.downside_floor": downsideFloor(series, cik, gaps, ncav),
|
|
768
|
+
"valuation.revenue_growth": revenueGrowth(companyFacts, series, cik, gaps, asOf),
|
|
769
|
+
"financial.incremental_return_on_capital": incrementalReturnOnCapital(series, cik, gaps),
|
|
770
|
+
"financial.leverage": leverage(series, cik, gaps),
|
|
771
|
+
"capital_allocation.share_count": shareCount(series, cik, gaps),
|
|
772
|
+
};
|
|
773
|
+
return Object.freeze({
|
|
774
|
+
cik,
|
|
775
|
+
entity: companyFacts.entityName || null,
|
|
776
|
+
as_of: asOf,
|
|
777
|
+
tool_id: TOOL_ID,
|
|
778
|
+
tool_version: TOOL_VERSION,
|
|
779
|
+
metrics: Object.freeze(metrics),
|
|
780
|
+
unavailable: Object.freeze(gaps),
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* Thin fetching wrapper: resolve the filer, pull Company Facts through the throttled and
|
|
786
|
+
* User-Agent-bearing sec.mjs client, then hand the JSON to the pure function above.
|
|
787
|
+
*/
|
|
788
|
+
export async function fetchFundamentals({ cik, ticker, asOf = null, signal, maintenanceCapexProxy } = {}) {
|
|
789
|
+
let resolved = cik;
|
|
790
|
+
if (!resolved && ticker) {
|
|
791
|
+
const wanted = String(ticker).trim().toUpperCase();
|
|
792
|
+
const universe = await fetchUniverse({ signal });
|
|
793
|
+
const hit = universe.find((row) => String(row.ticker).toUpperCase() === wanted);
|
|
794
|
+
if (!hit) {
|
|
795
|
+
throw invalidParams(
|
|
796
|
+
`no US filer with ticker "${ticker}" in the SEC universe. `
|
|
797
|
+
+ "Non-US listings are absent from it; supply a cik instead.",
|
|
798
|
+
);
|
|
799
|
+
}
|
|
800
|
+
resolved = hit.cik;
|
|
801
|
+
}
|
|
802
|
+
if (!resolved) throw invalidParams("fetchFundamentals needs a cik or a ticker");
|
|
803
|
+
const companyFacts = await fetchCompanyFacts(resolved, { signal });
|
|
804
|
+
const derived = deriveFundamentals({ companyFacts, asOf, ...(maintenanceCapexProxy ? { maintenanceCapexProxy } : {}) });
|
|
805
|
+
return Object.freeze({ ticker: ticker || derived.entity, ...derived });
|
|
806
|
+
}
|