alphacouncil-agent 0.9.2 → 1.0.0

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.
Files changed (493) hide show
  1. package/.claude/commands/alpha.md +22 -3
  2. package/.claude-plugin/marketplace.json +3 -3
  3. package/.claude-plugin/plugin.json +2 -2
  4. package/.codex-plugin/plugin.json +4 -4
  5. package/.grok/commands/alpha.md +22 -3
  6. package/.opencode/command/alpha.md +22 -3
  7. package/AGENTS.md +42 -4
  8. package/CHANGELOG.md +164 -0
  9. package/CLAUDE.md +59 -7
  10. package/README.ja.md +25 -11
  11. package/README.md +44 -12
  12. package/README.zh-CN.md +37 -12
  13. package/commands/alpha.md +22 -3
  14. package/data/authored/core-seats.mjs +808 -0
  15. package/data/authored/growth-seats.mjs +581 -0
  16. package/data/authored/index.mjs +114 -0
  17. package/data/authored/quant-seats.mjs +564 -0
  18. package/data/authored/value-seats.mjs +663 -0
  19. package/data/build-profile.v1.json +2 -1
  20. package/data/council-evaluation-protocol.v1.json +7 -4
  21. package/data/master-selector-method-locales.v1.mjs +296 -0
  22. package/data/persona-v3-ai-assisted-solo-profile.v1.json +2 -2
  23. package/data/persona-v3-build-specs.v1.mjs +60 -3
  24. package/docs/INSTALL.md +43 -6
  25. package/docs/personas.md +2 -1
  26. package/docs/plans/v1.0-roadmap.md +487 -0
  27. package/docs/releases/v0.9.3.md +86 -0
  28. package/docs/releases/v0.9.4.md +79 -0
  29. package/docs/releases/v0.9.5.md +96 -0
  30. package/docs/releases/v1.0.0.md +164 -0
  31. package/docs/report-contract.md +75 -2
  32. package/knowledge/ai-assisted-solo/experiments/runs/a.json +13 -13
  33. package/knowledge/ai-assisted-solo/experiments/runs/b.json +2 -2
  34. package/knowledge/ai-assisted-solo/experiments/runs/c.json +2 -2
  35. package/knowledge/ai-assisted-solo/experiments/runs/d13.json +126 -126
  36. package/knowledge/ai-assisted-solo/experiments/runs/d26.json +274 -254
  37. package/knowledge/ai-assisted-solo/experiments/runs/e-d13.json +129 -129
  38. package/knowledge/ai-assisted-solo/experiments/runs/e-d26.json +277 -257
  39. package/knowledge/ai-assisted-solo/experiments/runs/h_ai_reference.json +278 -258
  40. package/knowledge/ai-assisted-solo/experiments/simulation-input.json +7278 -556
  41. package/knowledge/ai-assisted-solo/experiments/simulation-manifest.json +22 -22
  42. 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
  43. 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
  44. 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
  45. 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
  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
  47. 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
  48. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_bogle/reviews/expected_return_over_long_bond.ai-review.json +277 -0
  49. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_bogle/reviews/fundamental_expected_return.ai-review.json +277 -0
  50. 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
  51. 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
  52. 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
  53. 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
  54. 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
  55. 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
  56. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_dalio/reviews/{debt_driver_stress.ai-review.json → policy_real_rate.ai-review.json} +49 -49
  57. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_dalio/reviews/{regime_classifier.ai-review.json → real_curve_slope.ai-review.json} +49 -49
  58. 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
  59. 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
  60. 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
  61. 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
  62. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_duan_yongping/reviews/{opportunity_cost.ai-review.json → five_year_opportunity_cost.ai-review.json} +46 -46
  63. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_duan_yongping/reviews/{user_value_evidence.ai-review.json → margin_retention.ai-review.json} +46 -46
  64. 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
  65. 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
  66. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_forensic_short/reviews/{borrow_catalyst.ai-review.json → cash_conversion_gap.ai-review.json} +49 -49
  67. 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
  68. 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
  69. 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
  70. 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
  71. 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
  72. 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
  73. 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
  74. 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
  75. 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
  76. 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
  77. 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
  78. 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
  79. 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
  80. 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
  81. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_munger/reviews/{incentive_map.ai-review.json → reported_earnings_cash_gap.ai-review.json} +49 -49
  82. 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
  83. 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
  84. 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
  85. 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
  86. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_simons/reviews/{oos_cost_test.ai-review.json → absolute_session_move.ai-review.json} +42 -42
  87. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_simons/reviews/{leakage_audit.ai-review.json → session_move_over_implied.ai-review.json} +46 -46
  88. 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
  89. 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
  90. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_soros/reviews/{reversal_monitor.ai-review.json → credit_cycle_position.ai-review.json} +49 -49
  91. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_soros/reviews/{reflexivity_graph.ai-review.json → financing_burden.ai-review.json} +46 -46
  92. 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
  93. 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
  94. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_thorp/reviews/{edge_recalculator.ai-review.json → edge_per_unit_of_volatility.ai-review.json} +46 -46
  95. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_thorp/reviews/{fractional_kelly.ai-review.json → net_edge.ai-review.json} +49 -49
  96. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/review-manifest.json +288 -270
  97. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-semantic-adjudications/index.json +2 -2
  98. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-semantic-extractions/index.json +2 -2
  99. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-semantic-skeptic-reviews/index.json +2 -2
  100. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-source-prereviews/index.json +6 -2
  101. package/knowledge/solo-test/masters/master_ackman/decision_policy.json +122 -11
  102. package/knowledge/solo-test/masters/master_ackman/doctrine.jsonl +2 -2
  103. package/knowledge/solo-test/masters/master_ackman/manifest.json +27 -10
  104. package/knowledge/solo-test/masters/master_ackman/provisional-index.json +3 -3
  105. package/knowledge/solo-test/masters/master_ackman/sources.jsonl +1 -1
  106. package/knowledge/solo-test/masters/master_ackman/tools.json +56 -30
  107. package/knowledge/solo-test/masters/master_aschenbrenner/decision_policy.json +101 -10
  108. package/knowledge/solo-test/masters/master_aschenbrenner/doctrine.jsonl +2 -2
  109. package/knowledge/solo-test/masters/master_aschenbrenner/manifest.json +26 -8
  110. package/knowledge/solo-test/masters/master_aschenbrenner/provisional-index.json +3 -3
  111. package/knowledge/solo-test/masters/master_aschenbrenner/sources.jsonl +1 -1
  112. package/knowledge/solo-test/masters/master_aschenbrenner/tools.json +56 -30
  113. package/knowledge/solo-test/masters/master_asness/decision_policy.json +89 -10
  114. package/knowledge/solo-test/masters/master_asness/doctrine.jsonl +2 -2
  115. package/knowledge/solo-test/masters/master_asness/manifest.json +25 -8
  116. package/knowledge/solo-test/masters/master_asness/provisional-index.json +3 -3
  117. package/knowledge/solo-test/masters/master_asness/sources.jsonl +1 -1
  118. package/knowledge/solo-test/masters/master_asness/tools.json +56 -30
  119. package/knowledge/solo-test/masters/master_bogle/counterfactuals.jsonl +0 -0
  120. package/knowledge/solo-test/masters/master_bogle/decision_cases.jsonl +0 -0
  121. package/knowledge/solo-test/masters/master_bogle/decision_policy.json +170 -0
  122. package/knowledge/solo-test/masters/master_bogle/doctrine.jsonl +14 -0
  123. package/knowledge/solo-test/masters/master_bogle/evaluation/calibration_cases.jsonl +0 -0
  124. package/knowledge/solo-test/masters/master_bogle/evaluation/experiments.json +5 -0
  125. package/knowledge/solo-test/masters/master_bogle/evaluation/golden_cases.jsonl +0 -0
  126. package/knowledge/solo-test/masters/master_bogle/evaluation/pairwise_cases.jsonl +0 -0
  127. package/knowledge/solo-test/masters/master_bogle/failures.jsonl +0 -0
  128. package/knowledge/solo-test/masters/master_bogle/manifest.json +141 -0
  129. package/knowledge/solo-test/masters/master_bogle/memory_policy.json +5 -0
  130. package/knowledge/solo-test/masters/master_bogle/provisional-index.json +41 -0
  131. package/knowledge/solo-test/masters/master_bogle/research_policy.json +11 -0
  132. package/knowledge/solo-test/masters/master_bogle/sources.jsonl +1 -0
  133. package/knowledge/solo-test/masters/master_bogle/tools.json +120 -0
  134. package/knowledge/solo-test/masters/master_bogle/voice.en.md +7 -0
  135. package/knowledge/solo-test/masters/master_bogle/voice.zh.md +7 -0
  136. package/knowledge/solo-test/masters/master_buffett/decision_policy.json +102 -11
  137. package/knowledge/solo-test/masters/master_buffett/doctrine.jsonl +2 -2
  138. package/knowledge/solo-test/masters/master_buffett/manifest.json +24 -8
  139. package/knowledge/solo-test/masters/master_buffett/provisional-index.json +3 -3
  140. package/knowledge/solo-test/masters/master_buffett/sources.jsonl +1 -1
  141. package/knowledge/solo-test/masters/master_buffett/tools.json +55 -29
  142. package/knowledge/solo-test/masters/master_burry/decision_policy.json +102 -12
  143. package/knowledge/solo-test/masters/master_burry/doctrine.jsonl +2 -2
  144. package/knowledge/solo-test/masters/master_burry/manifest.json +26 -10
  145. package/knowledge/solo-test/masters/master_burry/provisional-index.json +3 -3
  146. package/knowledge/solo-test/masters/master_burry/sources.jsonl +1 -1
  147. package/knowledge/solo-test/masters/master_burry/tools.json +56 -30
  148. package/knowledge/solo-test/masters/master_cathie_wood/decision_policy.json +74 -12
  149. package/knowledge/solo-test/masters/master_cathie_wood/doctrine.jsonl +2 -2
  150. package/knowledge/solo-test/masters/master_cathie_wood/manifest.json +26 -10
  151. package/knowledge/solo-test/masters/master_cathie_wood/provisional-index.json +3 -3
  152. package/knowledge/solo-test/masters/master_cathie_wood/sources.jsonl +1 -1
  153. package/knowledge/solo-test/masters/master_cathie_wood/tools.json +56 -30
  154. package/knowledge/solo-test/masters/master_dalio/decision_policy.json +123 -16
  155. package/knowledge/solo-test/masters/master_dalio/doctrine.jsonl +2 -2
  156. package/knowledge/solo-test/masters/master_dalio/manifest.json +24 -8
  157. package/knowledge/solo-test/masters/master_dalio/provisional-index.json +3 -3
  158. package/knowledge/solo-test/masters/master_dalio/sources.jsonl +1 -1
  159. package/knowledge/solo-test/masters/master_dalio/tools.json +56 -30
  160. package/knowledge/solo-test/masters/master_damodaran/decision_policy.json +58 -10
  161. package/knowledge/solo-test/masters/master_damodaran/doctrine.jsonl +2 -2
  162. package/knowledge/solo-test/masters/master_damodaran/manifest.json +23 -8
  163. package/knowledge/solo-test/masters/master_damodaran/provisional-index.json +3 -3
  164. package/knowledge/solo-test/masters/master_damodaran/sources.jsonl +1 -1
  165. package/knowledge/solo-test/masters/master_damodaran/tools.json +54 -28
  166. package/knowledge/solo-test/masters/master_druckenmiller/decision_policy.json +89 -10
  167. package/knowledge/solo-test/masters/master_druckenmiller/doctrine.jsonl +2 -2
  168. package/knowledge/solo-test/masters/master_druckenmiller/manifest.json +25 -8
  169. package/knowledge/solo-test/masters/master_druckenmiller/provisional-index.json +3 -3
  170. package/knowledge/solo-test/masters/master_druckenmiller/sources.jsonl +1 -1
  171. package/knowledge/solo-test/masters/master_druckenmiller/tools.json +55 -29
  172. package/knowledge/solo-test/masters/master_duan_yongping/decision_policy.json +138 -13
  173. package/knowledge/solo-test/masters/master_duan_yongping/doctrine.jsonl +2 -2
  174. package/knowledge/solo-test/masters/master_duan_yongping/manifest.json +31 -11
  175. package/knowledge/solo-test/masters/master_duan_yongping/provisional-index.json +3 -3
  176. package/knowledge/solo-test/masters/master_duan_yongping/sources.jsonl +1 -1
  177. package/knowledge/solo-test/masters/master_duan_yongping/tools.json +82 -30
  178. package/knowledge/solo-test/masters/master_fisher/decision_policy.json +102 -11
  179. package/knowledge/solo-test/masters/master_fisher/doctrine.jsonl +2 -2
  180. package/knowledge/solo-test/masters/master_fisher/manifest.json +27 -10
  181. package/knowledge/solo-test/masters/master_fisher/provisional-index.json +3 -3
  182. package/knowledge/solo-test/masters/master_fisher/sources.jsonl +1 -1
  183. package/knowledge/solo-test/masters/master_fisher/tools.json +58 -32
  184. package/knowledge/solo-test/masters/master_forensic_short/decision_policy.json +93 -14
  185. package/knowledge/solo-test/masters/master_forensic_short/doctrine.jsonl +2 -2
  186. package/knowledge/solo-test/masters/master_forensic_short/manifest.json +20 -8
  187. package/knowledge/solo-test/masters/master_forensic_short/provisional-index.json +3 -3
  188. package/knowledge/solo-test/masters/master_forensic_short/sources.jsonl +1 -1
  189. package/knowledge/solo-test/masters/master_forensic_short/tools.json +53 -27
  190. package/knowledge/solo-test/masters/master_graham/decision_policy.json +103 -12
  191. package/knowledge/solo-test/masters/master_graham/doctrine.jsonl +2 -2
  192. package/knowledge/solo-test/masters/master_graham/manifest.json +26 -8
  193. package/knowledge/solo-test/masters/master_graham/provisional-index.json +3 -3
  194. package/knowledge/solo-test/masters/master_graham/sources.jsonl +1 -1
  195. package/knowledge/solo-test/masters/master_graham/tools.json +67 -28
  196. package/knowledge/solo-test/masters/master_jhunjhunwala/decision_policy.json +77 -12
  197. package/knowledge/solo-test/masters/master_jhunjhunwala/doctrine.jsonl +2 -2
  198. package/knowledge/solo-test/masters/master_jhunjhunwala/manifest.json +25 -10
  199. package/knowledge/solo-test/masters/master_jhunjhunwala/provisional-index.json +3 -3
  200. package/knowledge/solo-test/masters/master_jhunjhunwala/sources.jsonl +1 -1
  201. package/knowledge/solo-test/masters/master_jhunjhunwala/tools.json +54 -28
  202. package/knowledge/solo-test/masters/master_klarman/decision_policy.json +105 -14
  203. package/knowledge/solo-test/masters/master_klarman/doctrine.jsonl +2 -2
  204. package/knowledge/solo-test/masters/master_klarman/manifest.json +22 -6
  205. package/knowledge/solo-test/masters/master_klarman/provisional-index.json +3 -3
  206. package/knowledge/solo-test/masters/master_klarman/sources.jsonl +1 -1
  207. package/knowledge/solo-test/masters/master_klarman/tools.json +55 -29
  208. package/knowledge/solo-test/masters/master_li_lu/decision_policy.json +101 -10
  209. package/knowledge/solo-test/masters/master_li_lu/doctrine.jsonl +2 -2
  210. package/knowledge/solo-test/masters/master_li_lu/manifest.json +27 -10
  211. package/knowledge/solo-test/masters/master_li_lu/provisional-index.json +3 -3
  212. package/knowledge/solo-test/masters/master_li_lu/sources.jsonl +1 -1
  213. package/knowledge/solo-test/masters/master_li_lu/tools.json +58 -32
  214. package/knowledge/solo-test/masters/master_lynch/decision_policy.json +127 -16
  215. package/knowledge/solo-test/masters/master_lynch/doctrine.jsonl +2 -2
  216. package/knowledge/solo-test/masters/master_lynch/manifest.json +28 -10
  217. package/knowledge/solo-test/masters/master_lynch/provisional-index.json +3 -3
  218. package/knowledge/solo-test/masters/master_lynch/sources.jsonl +1 -1
  219. package/knowledge/solo-test/masters/master_lynch/tools.json +67 -28
  220. package/knowledge/solo-test/masters/master_marks/decision_policy.json +71 -9
  221. package/knowledge/solo-test/masters/master_marks/doctrine.jsonl +2 -2
  222. package/knowledge/solo-test/masters/master_marks/manifest.json +25 -10
  223. package/knowledge/solo-test/masters/master_marks/provisional-index.json +3 -3
  224. package/knowledge/solo-test/masters/master_marks/sources.jsonl +1 -1
  225. package/knowledge/solo-test/masters/master_marks/tools.json +54 -28
  226. package/knowledge/solo-test/masters/master_munger/decision_policy.json +141 -16
  227. package/knowledge/solo-test/masters/master_munger/doctrine.jsonl +2 -2
  228. package/knowledge/solo-test/masters/master_munger/manifest.json +24 -10
  229. package/knowledge/solo-test/masters/master_munger/provisional-index.json +3 -3
  230. package/knowledge/solo-test/masters/master_munger/sources.jsonl +1 -1
  231. package/knowledge/solo-test/masters/master_munger/tools.json +52 -26
  232. package/knowledge/solo-test/masters/master_natenberg/decision_policy.json +59 -11
  233. package/knowledge/solo-test/masters/master_natenberg/doctrine.jsonl +2 -2
  234. package/knowledge/solo-test/masters/master_natenberg/manifest.json +23 -10
  235. package/knowledge/solo-test/masters/master_natenberg/provisional-index.json +3 -3
  236. package/knowledge/solo-test/masters/master_natenberg/sources.jsonl +1 -1
  237. package/knowledge/solo-test/masters/master_natenberg/tools.json +50 -24
  238. package/knowledge/solo-test/masters/master_pabrai/decision_policy.json +125 -14
  239. package/knowledge/solo-test/masters/master_pabrai/doctrine.jsonl +2 -2
  240. package/knowledge/solo-test/masters/master_pabrai/manifest.json +22 -6
  241. package/knowledge/solo-test/masters/master_pabrai/provisional-index.json +3 -3
  242. package/knowledge/solo-test/masters/master_pabrai/sources.jsonl +1 -1
  243. package/knowledge/solo-test/masters/master_pabrai/tools.json +55 -29
  244. package/knowledge/solo-test/masters/master_simons/decision_policy.json +59 -11
  245. package/knowledge/solo-test/masters/master_simons/doctrine.jsonl +2 -2
  246. package/knowledge/solo-test/masters/master_simons/manifest.json +22 -8
  247. package/knowledge/solo-test/masters/master_simons/provisional-index.json +3 -3
  248. package/knowledge/solo-test/masters/master_simons/sources.jsonl +1 -1
  249. package/knowledge/solo-test/masters/master_simons/tools.json +43 -30
  250. package/knowledge/solo-test/masters/master_sinclair/decision_policy.json +59 -11
  251. package/knowledge/solo-test/masters/master_sinclair/doctrine.jsonl +2 -2
  252. package/knowledge/solo-test/masters/master_sinclair/manifest.json +22 -10
  253. package/knowledge/solo-test/masters/master_sinclair/provisional-index.json +3 -3
  254. package/knowledge/solo-test/masters/master_sinclair/sources.jsonl +1 -1
  255. package/knowledge/solo-test/masters/master_sinclair/tools.json +54 -28
  256. package/knowledge/solo-test/masters/master_soros/decision_policy.json +98 -10
  257. package/knowledge/solo-test/masters/master_soros/doctrine.jsonl +2 -2
  258. package/knowledge/solo-test/masters/master_soros/manifest.json +24 -11
  259. package/knowledge/solo-test/masters/master_soros/provisional-index.json +3 -3
  260. package/knowledge/solo-test/masters/master_soros/sources.jsonl +1 -1
  261. package/knowledge/solo-test/masters/master_soros/tools.json +54 -28
  262. package/knowledge/solo-test/masters/master_taleb/decision_policy.json +124 -13
  263. package/knowledge/solo-test/masters/master_taleb/doctrine.jsonl +2 -2
  264. package/knowledge/solo-test/masters/master_taleb/manifest.json +23 -9
  265. package/knowledge/solo-test/masters/master_taleb/provisional-index.json +3 -3
  266. package/knowledge/solo-test/masters/master_taleb/sources.jsonl +1 -1
  267. package/knowledge/solo-test/masters/master_taleb/tools.json +50 -24
  268. package/knowledge/solo-test/masters/master_thorp/decision_policy.json +61 -13
  269. package/knowledge/solo-test/masters/master_thorp/doctrine.jsonl +2 -2
  270. package/knowledge/solo-test/masters/master_thorp/manifest.json +25 -10
  271. package/knowledge/solo-test/masters/master_thorp/provisional-index.json +3 -3
  272. package/knowledge/solo-test/masters/master_thorp/sources.jsonl +1 -1
  273. package/knowledge/solo-test/masters/master_thorp/tools.json +56 -30
  274. package/knowledge/solo-test/persona-v3-solo-test-formulas/compilation-manifest.json +290 -264
  275. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_ackman/components/tools.json +58 -32
  276. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_ackman/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
  277. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_ackman/provisional-derivations/owner_earnings_yield.derived-proxy-evidence.json +76 -0
  278. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_aschenbrenner/components/tools.json +58 -32
  279. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_aschenbrenner/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
  280. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_aschenbrenner/provisional-derivations/price_implied_cash_yield.derived-proxy-evidence.json +76 -0
  281. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_asness/components/tools.json +58 -32
  282. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_asness/provisional-derivations/nominal_earnings_yield_gap.derived-proxy-evidence.json +76 -0
  283. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_asness/provisional-derivations/real_earnings_yield_gap.derived-proxy-evidence.json +76 -0
  284. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_bogle/components/tools.json +120 -0
  285. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_bogle/provisional-derivations/expected_return_over_long_bond.derived-proxy-evidence.json +77 -0
  286. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_bogle/provisional-derivations/fundamental_expected_return.derived-proxy-evidence.json +76 -0
  287. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_buffett/components/tools.json +57 -31
  288. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_buffett/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
  289. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_buffett/provisional-derivations/owner_earnings_yield.derived-proxy-evidence.json +76 -0
  290. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_burry/components/tools.json +58 -32
  291. 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
  292. 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
  293. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_cathie_wood/components/tools.json +58 -32
  294. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_cathie_wood/provisional-derivations/current_cash_yield.derived-proxy-evidence.json +76 -0
  295. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_cathie_wood/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
  296. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_dalio/components/tools.json +58 -32
  297. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_dalio/provisional-derivations/policy_real_rate.derived-proxy-evidence.json +76 -0
  298. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_dalio/provisional-derivations/real_curve_slope.derived-proxy-evidence.json +76 -0
  299. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_damodaran/components/tools.json +56 -30
  300. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_damodaran/provisional-derivations/implied_equity_risk_premium.derived-proxy-evidence.json +76 -0
  301. 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
  302. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_druckenmiller/components/tools.json +57 -31
  303. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_druckenmiller/provisional-derivations/liquidity_curve_impulse.derived-proxy-evidence.json +76 -0
  304. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_druckenmiller/provisional-derivations/policy_real_rate.derived-proxy-evidence.json +76 -0
  305. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_duan_yongping/components/tools.json +84 -32
  306. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_duan_yongping/provisional-derivations/five_year_opportunity_cost.derived-proxy-evidence.json +102 -0
  307. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_duan_yongping/provisional-derivations/margin_retention.derived-proxy-evidence.json +76 -0
  308. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_fisher/components/tools.json +60 -34
  309. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_fisher/provisional-derivations/margin_retention.derived-proxy-evidence.json +76 -0
  310. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_fisher/provisional-derivations/reinvestment_spread.derived-proxy-evidence.json +76 -0
  311. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_forensic_short/components/tools.json +55 -29
  312. 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
  313. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_forensic_short/provisional-derivations/cash_cover_of_leverage.derived-proxy-evidence.json +76 -0
  314. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_graham/components/tools.json +69 -30
  315. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_graham/provisional-derivations/defensive_hurdle_base.derived-proxy-evidence.json +89 -0
  316. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_graham/provisional-derivations/half_normalised_earnings_yield.derived-proxy-evidence.json +76 -0
  317. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_jhunjhunwala/components/tools.json +56 -30
  318. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_jhunjhunwala/provisional-derivations/cash_quality_gap.derived-proxy-evidence.json +76 -0
  319. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_jhunjhunwala/provisional-derivations/real_structural_growth.derived-proxy-evidence.json +76 -0
  320. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_klarman/components/tools.json +57 -31
  321. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_klarman/provisional-derivations/downside_asset_coverage.derived-proxy-evidence.json +76 -0
  322. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_klarman/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
  323. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_li_lu/components/tools.json +60 -34
  324. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_li_lu/provisional-derivations/cash_backed_decade_return.derived-proxy-evidence.json +76 -0
  325. 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
  326. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_lynch/components/tools.json +69 -30
  327. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_lynch/provisional-derivations/growth_justified_capitalisation.derived-proxy-evidence.json +89 -0
  328. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_lynch/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
  329. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_marks/components/tools.json +56 -30
  330. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_marks/provisional-derivations/credit_cycle_position.derived-proxy-evidence.json +76 -0
  331. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_marks/provisional-derivations/equity_over_corporate_debt.derived-proxy-evidence.json +76 -0
  332. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_munger/components/tools.json +54 -28
  333. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_munger/provisional-derivations/debt_service_cushion.derived-proxy-evidence.json +76 -0
  334. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_munger/provisional-derivations/reported_earnings_cash_gap.derived-proxy-evidence.json +76 -0
  335. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_natenberg/components/tools.json +52 -26
  336. 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
  337. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_natenberg/provisional-derivations/round_trip_cost.derived-proxy-evidence.json +76 -0
  338. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_pabrai/components/tools.json +57 -31
  339. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_pabrai/provisional-derivations/downside_floor_coverage.derived-proxy-evidence.json +76 -0
  340. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_pabrai/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
  341. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_simons/components/tools.json +45 -32
  342. 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
  343. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_simons/provisional-derivations/session_move_over_implied.derived-proxy-evidence.json +77 -0
  344. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_sinclair/components/tools.json +56 -30
  345. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_sinclair/provisional-derivations/round_trip_cost.derived-proxy-evidence.json +76 -0
  346. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_sinclair/provisional-derivations/volatility_per_unit_of_width.derived-proxy-evidence.json +76 -0
  347. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_soros/components/tools.json +56 -30
  348. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_soros/provisional-derivations/credit_cycle_position.derived-proxy-evidence.json +76 -0
  349. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_soros/provisional-derivations/financing_burden.derived-proxy-evidence.json +76 -0
  350. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_taleb/components/tools.json +52 -26
  351. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_taleb/provisional-derivations/debt_service_cushion.derived-proxy-evidence.json +76 -0
  352. 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
  353. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_thorp/components/tools.json +58 -32
  354. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_thorp/provisional-derivations/edge_per_unit_of_volatility.derived-proxy-evidence.json +77 -0
  355. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_thorp/provisional-derivations/net_edge.derived-proxy-evidence.json +76 -0
  356. package/mcp/lib/breadth.mjs +122 -0
  357. package/mcp/lib/codex.mjs +3 -3
  358. package/mcp/lib/constants.mjs +76 -25
  359. package/mcp/lib/council-options.mjs +95 -40
  360. package/mcp/lib/council-selection.mjs +95 -9
  361. package/mcp/lib/damodaran.mjs +119 -0
  362. package/mcp/lib/fred.mjs +246 -0
  363. package/mcp/lib/fund-holdings-parsers.mjs +297 -0
  364. package/mcp/lib/fundamentals.mjs +801 -0
  365. package/mcp/lib/funds.mjs +750 -0
  366. package/mcp/lib/gates.mjs +149 -15
  367. package/mcp/lib/grounding.mjs +188 -36
  368. package/mcp/lib/index-aggregate.mjs +718 -0
  369. package/mcp/lib/insider-ownership.mjs +149 -0
  370. package/mcp/lib/instrument-facts.mjs +557 -0
  371. package/mcp/lib/instruments.mjs +191 -0
  372. package/mcp/lib/lang.mjs +109 -2
  373. package/mcp/lib/markdown.mjs +620 -138
  374. package/mcp/lib/master-catalog.mjs +32 -3
  375. package/mcp/lib/orchestrator.mjs +1121 -290
  376. package/mcp/lib/packets.mjs +227 -21
  377. package/mcp/lib/personas/engine.mjs +44 -31
  378. package/mcp/lib/personas-v3/compiler.mjs +2 -0
  379. package/mcp/lib/personas-v3/deterministic-executor.mjs +13 -2
  380. package/mcp/lib/personas-v3/ga-external-evidence.mjs +3 -2
  381. package/mcp/lib/personas-v3/ga-gate.mjs +2 -1
  382. package/mcp/lib/personas-v3/ga-package-evidence.mjs +5 -4
  383. package/mcp/lib/personas-v3/grounding-adapter.mjs +309 -2
  384. package/mcp/lib/personas-v3/loader.mjs +10 -7
  385. package/mcp/lib/personas-v3/production-root.mjs +5 -4
  386. package/mcp/lib/personas-v3/release-formula-evidence.mjs +14 -7
  387. package/mcp/lib/personas-v3/release-source-evidence.mjs +2 -1
  388. package/mcp/lib/personas-v3/releases.mjs +5 -4
  389. package/mcp/lib/personas-v3/runtime-release-integrity.mjs +6 -5
  390. package/mcp/lib/personas-v3/semantic-source-adjudication.mjs +2 -1
  391. package/mcp/lib/personas-v3/semantic-source-extraction.mjs +2 -1
  392. package/mcp/lib/personas-v3/semantic-source-skeptic-review.mjs +2 -1
  393. package/mcp/lib/personas-v3/source-acquisition.mjs +10 -3
  394. package/mcp/lib/personas-v3/source-review-operations.mjs +3 -2
  395. package/mcp/lib/personas-v3/staging.mjs +22 -3
  396. package/mcp/lib/prompts.mjs +119 -2
  397. package/mcp/lib/quotes.mjs +5 -0
  398. package/mcp/lib/rpc.mjs +101 -42
  399. package/mcp/lib/run-store.mjs +28 -3
  400. package/mcp/lib/sec.mjs +32 -0
  401. package/mcp/lib/tables.mjs +11 -0
  402. package/mcp/lib/voice.mjs +122 -0
  403. package/mcp/lib/xbrl-aggregates.mjs +153 -0
  404. package/mcp/lib/xbrl-series.mjs +339 -0
  405. package/mcp/lib/xlsx.mjs +155 -0
  406. package/package.json +5 -5
  407. package/personas/masters/masters-value/bogle.md +145 -0
  408. package/schemas/council-evaluation-protocol-v1.schema.json +2 -2
  409. package/schemas/persona-v3-ai-assisted-solo-profile-v1.schema.json +2 -2
  410. package/schemas/persona-v3-ai-machine-simulation-v1.schema.json +1 -1
  411. package/schemas/persona-v3-build-spec-v1.schema.json +3 -3
  412. package/schemas/persona-v3-release-formula-review-evidence-v1.schema.json +3 -3
  413. package/schemas/persona-v3-release-manifest-v1.schema.json +6 -6
  414. package/schemas/persona-v3-release-source-review-evidence-v1.schema.json +3 -3
  415. package/schemas/persona-v3-tool-graph-v1.schema.json +1 -1
  416. package/schemas/persona-v3.schema.json +37 -2
  417. package/schemas/source-review-batch-v1.schema.json +9 -9
  418. package/scripts/assemble-persona-v3-release.mjs +3 -2
  419. package/scripts/build-persona-v3-solo-test-packs.mjs +6 -8
  420. package/scripts/check-persona-v3-ga.mjs +1 -1
  421. package/scripts/check-persona-v3-production-candidates.mjs +1 -1
  422. package/scripts/compile-persona-v3-formulas.mjs +19 -18
  423. package/scripts/doctor.mjs +11 -9
  424. package/scripts/export-persona-source-review-batch.mjs +1 -1
  425. package/scripts/generate-persona-v3-drafts.mjs +2 -2
  426. package/scripts/lib/build-profile.mjs +24 -0
  427. package/scripts/lib/council-evaluation-protocol.mjs +7 -6
  428. package/scripts/lib/host-capabilities.mjs +2 -1
  429. package/scripts/lib/packaged-host-parity.mjs +63 -8
  430. package/scripts/lib/persona-v3-ai-assisted-solo-status.mjs +6 -4
  431. package/scripts/lib/persona-v3-ai-formula-review.mjs +6 -5
  432. package/scripts/lib/persona-v3-ai-machine-simulations.mjs +32 -4
  433. package/scripts/lib/persona-v3-build-specs.mjs +3 -2
  434. package/scripts/lib/persona-v3-draft-packs.mjs +2 -2
  435. package/scripts/lib/persona-v3-formula-pipeline.mjs +11 -10
  436. package/scripts/lib/persona-v3-production-candidates.mjs +10 -9
  437. package/scripts/lib/persona-v3-solo-formula-pipeline.mjs +182 -16
  438. package/scripts/lib/persona-v3-solo-test-packs.mjs +143 -16
  439. package/scripts/package-smoke.mjs +32 -12
  440. package/scripts/report-persona-corpus-gaps.mjs +4 -1
  441. package/scripts/review-persona-v3-solo-formulas.mjs +12 -8
  442. package/scripts/run-tests.mjs +23 -2
  443. package/scripts/scaffold-persona-v3-staging.mjs +1 -1
  444. package/skills/agent-skills-governance/SKILL.md +8 -2
  445. package/skills/alphacouncil-agent/SKILL.md +60 -9
  446. package/skills/public-equity-investing/SKILL.md +17 -0
  447. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_ackman/provisional-derivations/change_value_bridge.derived-proxy-evidence.json +0 -64
  448. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_ackman/provisional-derivations/power_map.derived-proxy-evidence.json +0 -64
  449. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_aschenbrenner/provisional-derivations/compute_power_bridge.derived-proxy-evidence.json +0 -64
  450. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_aschenbrenner/provisional-derivations/timeline_reverse_valuation.derived-proxy-evidence.json +0 -64
  451. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_asness/provisional-derivations/crowding_cost_stress.derived-proxy-evidence.json +0 -64
  452. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_asness/provisional-derivations/factor_decomposer.derived-proxy-evidence.json +0 -64
  453. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_buffett/provisional-derivations/incremental_returns.derived-proxy-evidence.json +0 -64
  454. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_buffett/provisional-derivations/owner_earnings_rebuilder.derived-proxy-evidence.json +0 -64
  455. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_burry/provisional-derivations/capital_structure_reader.derived-proxy-evidence.json +0 -64
  456. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_burry/provisional-derivations/mispricing_carry.derived-proxy-evidence.json +0 -64
  457. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_cathie_wood/provisional-derivations/capture_valuation_bridge.derived-proxy-evidence.json +0 -64
  458. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_cathie_wood/provisional-derivations/cost_adoption_curve.derived-proxy-evidence.json +0 -64
  459. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_dalio/provisional-derivations/debt_driver_stress.derived-proxy-evidence.json +0 -64
  460. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_dalio/provisional-derivations/regime_classifier.derived-proxy-evidence.json +0 -64
  461. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_damodaran/provisional-derivations/story_dcf.derived-proxy-evidence.json +0 -64
  462. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_druckenmiller/provisional-derivations/inflection_payoff.derived-proxy-evidence.json +0 -64
  463. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_druckenmiller/provisional-derivations/liquidity_revision_map.derived-proxy-evidence.json +0 -64
  464. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_duan_yongping/provisional-derivations/opportunity_cost.derived-proxy-evidence.json +0 -64
  465. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_duan_yongping/provisional-derivations/user_value_evidence.derived-proxy-evidence.json +0 -64
  466. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_fisher/provisional-derivations/research_productivity.derived-proxy-evidence.json +0 -64
  467. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_fisher/provisional-derivations/scuttlebutt_graph.derived-proxy-evidence.json +0 -64
  468. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_forensic_short/provisional-derivations/borrow_catalyst.derived-proxy-evidence.json +0 -64
  469. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_graham/provisional-derivations/asset_floor.derived-proxy-evidence.json +0 -64
  470. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_graham/provisional-derivations/normalized_earnings.derived-proxy-evidence.json +0 -64
  471. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_jhunjhunwala/provisional-derivations/penetration_liquidity.derived-proxy-evidence.json +0 -64
  472. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_jhunjhunwala/provisional-derivations/promoter_governance.derived-proxy-evidence.json +0 -64
  473. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_klarman/provisional-derivations/cash_catalyst_compare.derived-proxy-evidence.json +0 -64
  474. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_klarman/provisional-derivations/recovery_waterfall.derived-proxy-evidence.json +0 -64
  475. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_li_lu/provisional-derivations/promise_integrity_ledger.derived-proxy-evidence.json +0 -64
  476. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_li_lu/provisional-derivations/ten_year_return_bridge.derived-proxy-evidence.json +0 -64
  477. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_lynch/provisional-derivations/category_classifier.derived-proxy-evidence.json +0 -64
  478. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_lynch/provisional-derivations/story_numbers_check.derived-proxy-evidence.json +0 -64
  479. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_marks/provisional-derivations/cycle_temperature.derived-proxy-evidence.json +0 -64
  480. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_marks/provisional-derivations/implied_expectations.derived-proxy-evidence.json +0 -64
  481. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_munger/provisional-derivations/failure_path_graph.derived-proxy-evidence.json +0 -64
  482. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_munger/provisional-derivations/incentive_map.derived-proxy-evidence.json +0 -64
  483. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_pabrai/provisional-derivations/discrete_payoff.derived-proxy-evidence.json +0 -64
  484. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_pabrai/provisional-derivations/downside_floor.derived-proxy-evidence.json +0 -64
  485. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_simons/provisional-derivations/leakage_audit.derived-proxy-evidence.json +0 -64
  486. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_simons/provisional-derivations/oos_cost_test.derived-proxy-evidence.json +0 -64
  487. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_sinclair/provisional-derivations/net_edge_sizer.derived-proxy-evidence.json +0 -64
  488. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_sinclair/provisional-derivations/realized_vol_forecast.derived-proxy-evidence.json +0 -64
  489. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_soros/provisional-derivations/reflexivity_graph.derived-proxy-evidence.json +0 -64
  490. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_soros/provisional-derivations/reversal_monitor.derived-proxy-evidence.json +0 -64
  491. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_taleb/provisional-derivations/payoff_ruin.derived-proxy-evidence.json +0 -64
  492. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_thorp/provisional-derivations/edge_recalculator.derived-proxy-evidence.json +0 -64
  493. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_thorp/provisional-derivations/fractional_kelly.derived-proxy-evidence.json +0 -64
@@ -0,0 +1,718 @@
1
+ /**
2
+ * Aggregate index facts: valuation, breadth, positioning and volatility history.
3
+ *
4
+ * An index or index ETF cannot be valued the way an operating company is. There is no filer,
5
+ * no Company Facts screen and no single earnings statement, so the seats that need a valuation
6
+ * level, a cycle reading or a factor input have nothing to stand on unless the aggregate is
7
+ * fetched as its own dated fact. That is what this module supplies.
8
+ *
9
+ * TWO RULES ARE LOAD-BEARING HERE AND ARE ENCODED, NOT JUST DOCUMENTED.
10
+ *
11
+ * 1. BASIS. Index P/E is quoted on mutually incompatible bases. On 2026-07-24 the same S&P 500
12
+ * was 25.17x on WSJ's index basis, 28.53x on multpl.com's GAAP as-reported trailing basis,
13
+ * and 29.80x on iShares' IVV portfolio harmonic mean. None of those is wrong; they answer
14
+ * different questions. A fact therefore carries exactly one `basis` and names it, sources are
15
+ * never blended for one metric, and a percentile is refused unless its history declares the
16
+ * same basis. See `PE_BASIS` and `PE_BASIS_DIVERGENCE`.
17
+ *
18
+ * 2. LICENSING. S&P 500 and DJIA constituents and weights are licensed assets of S&P Dow Jones
19
+ * Indices. Scraping a constituent list and presenting it as the index is not a supported free
20
+ * path. The supported free path is the tracking ETF's published holdings, used as an explicit
21
+ * proxy and LABELLED as one (`^GSPC` -> IVV, `^NDX` -> QQQ, `^DJI` -> DIA). Holdings fetching
22
+ * belongs to another module; this one exposes the proxy mapping and accepts holdings passed
23
+ * in, and always stamps `is_proxy: true` on the result. See `INDEX_PROXIES`.
24
+ *
25
+ * Two of the upstreams (WSJ peyields, WSJ markets diary) are undocumented internal APIs. They
26
+ * can change shape without notice and without a version, so every field is validated for
27
+ * presence, type AND plausible magnitude before it becomes a number. A shape change must
28
+ * degrade to a named gap in `unavailable`; it must never become a silently wrong number.
29
+ */
30
+
31
+ import { LIMITS } from "./constants.mjs";
32
+ import { fetchText, resolveMarketSymbol } from "./quotes.mjs";
33
+
34
+ // ---- Sources ---------------------------------------------------------------
35
+
36
+ export const SOURCE_URLS = Object.freeze({
37
+ // Undocumented WSJ internal APIs. Both need a non-default User-Agent, which `fetchText`
38
+ // already sends. Note this is the opposite of the api.nasdaq.com rule below, so there is
39
+ // deliberately no single global UA policy in this module.
40
+ wsj_index_valuation:
41
+ "https://www.wsj.com/market-data/stocks/peyields?id=%7B%22indexType%22%3A%22OTHERS%22%7D&type=mdc_peAndYields",
42
+ wsj_market_breadth:
43
+ "https://www.wsj.com/market-data/stocks/marketsdiary?id=%7B%22application%22%3A%22WSJ%22%2C%22marketsDiaryType%22%3A%22diaries%22%7D&type=mdc_marketsdiary",
44
+ // Daily file keyed by trade date; 403 on any non-trading day AND on the current session
45
+ // before it is published, hence the walk-back in `fetchPutCallRatios`.
46
+ cboe_put_call_daily: "https://cdn.cboe.com/data/us/options/market_statistics/daily/",
47
+ cboe_vix_history: "https://cdn.cboe.com/api/global/us_indices/daily_prices/VIX_History.csv",
48
+ // Nasdaq publishes its own index membership, so this one is not S&P-licensed. It is exposed
49
+ // rather than fetched because constituent/holdings fetching is another module's job. TRAP if
50
+ // it is ever wired: a default curl/node User-Agent gets HTTP/2 INTERNAL_ERROR from this host.
51
+ nasdaq_100_constituents: "https://api.nasdaq.com/api/quote/list-type/nasdaq100",
52
+ });
53
+
54
+ /**
55
+ * Damodaran's implied ERP is the natural input for an equity-risk-premium seat, and both files
56
+ * are reachable. Neither is parsed: `.xlsx` is a zip of XML whose sheet layout is undocumented
57
+ * and moves between vintages, and `.xls` is the pre-2007 BIFF binary format. Hand-rolling a
58
+ * reader for either on Node built-ins would produce a number nobody can check, which is strictly
59
+ * worse than a named gap. The URLs are published here so a human can open them.
60
+ */
61
+ export const DAMODARAN_SOURCES = Object.freeze([
62
+ Object.freeze({
63
+ id: "implied_erp_monthly",
64
+ label: "Damodaran implied equity risk premium, monthly",
65
+ url: "https://pages.stern.nyu.edu/~adamodar/pc/implprem/ERPbymonth.xlsx",
66
+ format: "xlsx",
67
+ parser_implemented: false,
68
+ // The /pc/datasets/ sibling path 404s for this file; /pc/implprem/ is the live one.
69
+ reason: "xlsx is a zip of undocumented sheet XML; no dependency-free parser is trustworthy here",
70
+ }),
71
+ Object.freeze({
72
+ id: "implied_erp_annual",
73
+ label: "Damodaran implied equity risk premium, annual history",
74
+ url: "https://pages.stern.nyu.edu/~adamodar/pc/datasets/histimpl.xls",
75
+ format: "xls_biff",
76
+ parser_implemented: false,
77
+ reason: "legacy BIFF binary workbook; not readable with Node built-ins",
78
+ }),
79
+ ]);
80
+
81
+ // ---- Basis ------------------------------------------------------------------
82
+
83
+ export const PE_BASIS = Object.freeze({
84
+ /** WSJ's own index-level aggregation, as published on the peyields endpoint. */
85
+ WSJ_INDEX: "wsj_index_basis",
86
+ /** GAAP as-reported trailing twelve months (multpl.com and similar). */
87
+ GAAP_AS_REPORTED_TTM: "gaap_as_reported_ttm",
88
+ /** Fund-sponsor portfolio harmonic mean (iShares/Invesco fact sheets). */
89
+ ETF_PORTFOLIO_HARMONIC: "etf_portfolio_harmonic_mean",
90
+ });
91
+
92
+ /**
93
+ * One observed day where all three bases disagree by more than 18%, kept as data so the
94
+ * no-blending rule is checkable rather than folklore. A dashboard that mixes these looks broken.
95
+ */
96
+ export const PE_BASIS_DIVERGENCE = Object.freeze({
97
+ index: "^GSPC",
98
+ trade_date: "2026-07-24",
99
+ readings: Object.freeze([
100
+ Object.freeze({ basis: PE_BASIS.WSJ_INDEX, pe_trailing: 25.17, source: "WSJ peyields" }),
101
+ Object.freeze({ basis: PE_BASIS.GAAP_AS_REPORTED_TTM, pe_trailing: 28.53, source: "multpl.com" }),
102
+ Object.freeze({ basis: PE_BASIS.ETF_PORTFOLIO_HARMONIC, pe_trailing: 29.8, source: "iShares IVV" }),
103
+ ]),
104
+ });
105
+
106
+ // ---- Index proxies (licensing) ----------------------------------------------
107
+
108
+ export const PROXY_LICENSE_NOTE =
109
+ "Index membership and weights are licensed by the index provider. This is the tracking ETF's "
110
+ + "published holdings used as an explicit proxy for the index, not the index itself.";
111
+
112
+ export const INDEX_PROXIES = Object.freeze({
113
+ "^GSPC": Object.freeze({
114
+ index_name: "S&P 500",
115
+ etf: "IVV",
116
+ alternates: Object.freeze(["SPY", "VOO"]),
117
+ licensor: "S&P Dow Jones Indices",
118
+ why: "S&P 500 constituents and weights are a licensed S&P DJI asset; the free path is a tracking ETF's published holdings, labelled as a proxy",
119
+ }),
120
+ "^NDX": Object.freeze({
121
+ index_name: "Nasdaq-100",
122
+ etf: "QQQ",
123
+ alternates: Object.freeze(["QQQM"]),
124
+ licensor: "Nasdaq, Inc.",
125
+ why: "Nasdaq publishes NDX membership itself, but weights still come from the tracking ETF, so the same proxy labelling applies",
126
+ }),
127
+ "^DJI": Object.freeze({
128
+ index_name: "Dow Jones Industrial Average",
129
+ etf: "DIA",
130
+ alternates: Object.freeze([]),
131
+ licensor: "S&P Dow Jones Indices",
132
+ why: "DJIA constituents and its divisor are licensed S&P DJI assets; the free path is DIA's published holdings, labelled as a proxy",
133
+ }),
134
+ });
135
+
136
+ const INDEX_ALIASES = Object.freeze({
137
+ GSPC: "^GSPC", "^GSPC": "^GSPC", SPX: "^GSPC", "^SPX": "^GSPC", SP500: "^GSPC",
138
+ NDX: "^NDX", "^NDX": "^NDX", NASDAQ100: "^NDX",
139
+ DJI: "^DJI", "^DJI": "^DJI", DJIA: "^DJI",
140
+ RUT: "^RUT", "^RUT": "^RUT",
141
+ IXIC: "^IXIC", "^IXIC": "^IXIC",
142
+ });
143
+
144
+ /** Normalise an index identifier. Unknown input passes through unchanged rather than being guessed at. */
145
+ export function normalizeIndexSymbol(input) {
146
+ const resolved = String(resolveMarketSymbol(input) || "").trim().toUpperCase();
147
+ return Object.prototype.hasOwnProperty.call(INDEX_ALIASES, resolved) ? INDEX_ALIASES[resolved] : resolved;
148
+ }
149
+
150
+ /**
151
+ * The proxy record for an index, optionally carrying holdings fetched elsewhere.
152
+ * `is_proxy` is unconditional: the caller must never be able to read this as the index itself.
153
+ */
154
+ export function proxyConstituents(symbol, holdings = null) {
155
+ const normalized = normalizeIndexSymbol(symbol);
156
+ const proxy = INDEX_PROXIES[normalized];
157
+ if (!proxy) return null;
158
+ const rows = Array.isArray(holdings) ? Object.freeze([...holdings]) : null;
159
+ return Object.freeze({
160
+ is_proxy: true,
161
+ proxy_for: normalized,
162
+ index_name: proxy.index_name,
163
+ proxy_etf: proxy.etf,
164
+ proxy_alternates: proxy.alternates,
165
+ licensor: proxy.licensor,
166
+ proxy_reason: proxy.why,
167
+ license_note: PROXY_LICENSE_NOTE,
168
+ holdings: rows,
169
+ holdings_source: rows ? "caller_supplied_etf_holdings" : null,
170
+ holdings_count: rows ? rows.length : null,
171
+ });
172
+ }
173
+
174
+ // ---- Strict validation helpers ----------------------------------------------
175
+
176
+ /**
177
+ * Magnitude bands. Presence and type checks catch a renamed field; only a band catches the
178
+ * nastier case where a field keeps its name but changes meaning (a ratio becoming a percentage,
179
+ * a level becoming a decimal fraction).
180
+ */
181
+ const BANDS = Object.freeze({
182
+ PE: Object.freeze({ min: 1, max: 1000 }),
183
+ YIELD_PCT: Object.freeze({ min: 0, max: 25 }),
184
+ PUT_CALL: Object.freeze({ min: 0, max: 20 }),
185
+ VIX: Object.freeze({ min: 1, max: 250 }),
186
+ COUNT: Object.freeze({ min: 0, max: 1e6 }),
187
+ TRIN: Object.freeze({ min: 0, max: 50 }),
188
+ });
189
+
190
+ const MONTHS = Object.freeze({
191
+ january: 1, february: 2, march: 3, april: 4, may: 5, june: 6,
192
+ july: 7, august: 8, september: 9, october: 10, november: 11, december: 12,
193
+ });
194
+
195
+ function requireObject(value, what) {
196
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${what}: expected an object`);
197
+ return value;
198
+ }
199
+
200
+ function requireArray(value, what) {
201
+ if (!Array.isArray(value) || !value.length) throw new Error(`${what}: expected a non-empty array`);
202
+ return value;
203
+ }
204
+
205
+ function requireString(value, what) {
206
+ const text = typeof value === "string" ? value.trim() : "";
207
+ if (!text) throw new Error(`${what}: expected a non-empty string`);
208
+ return text;
209
+ }
210
+
211
+ /**
212
+ * A published number, or null when the field is absent. Absent secondary fields degrade to null;
213
+ * a field that is present but implausible throws, because that is the shape change that would
214
+ * otherwise become a confident wrong number.
215
+ */
216
+ function boundedNumber(raw, what, { min, max, allowNull = false }) {
217
+ if (raw === null || raw === undefined || raw === "") {
218
+ if (allowNull) return null;
219
+ throw new Error(`${what}: missing`);
220
+ }
221
+ const value = Number(String(raw).replace(/,/gu, ""));
222
+ if (!Number.isFinite(value)) throw new Error(`${what}: not numeric (${JSON.stringify(raw)})`);
223
+ if (value < min || value > max) throw new Error(`${what}: ${value} is outside the plausible band ${min}..${max}`);
224
+ return value;
225
+ }
226
+
227
+ /** `2026-07-24T00:00:00` -> `2026-07-24`. */
228
+ function isoDateFromWsj(raw, what) {
229
+ const match = /^(\d{4}-\d{2}-\d{2})(?:T|$)/u.exec(String(raw || "").trim());
230
+ if (!match) throw new Error(`${what}: unrecognised date ${JSON.stringify(raw)}`);
231
+ return match[1];
232
+ }
233
+
234
+ /** `Monday, July 27, 2026` -> `2026-07-27`, parsed explicitly rather than via `new Date`. */
235
+ function isoDateFromLongForm(raw, what) {
236
+ const match = /^[A-Za-z]+,\s*([A-Za-z]+)\s+(\d{1,2}),\s*(\d{4})$/u.exec(String(raw || "").trim());
237
+ const month = match ? MONTHS[match[1].toLowerCase()] : null;
238
+ if (!match || !month) throw new Error(`${what}: unrecognised date ${JSON.stringify(raw)}`);
239
+ const day = Number(match[2]);
240
+ if (day < 1 || day > 31) throw new Error(`${what}: impossible day in ${JSON.stringify(raw)}`);
241
+ return `${match[3]}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
242
+ }
243
+
244
+ /** The trade date IS the publication anchor for these feeds; the run clock is never used. */
245
+ const publicAt = (isoDay) => `${isoDay}T00:00:00.000Z`;
246
+
247
+ const isoDay = (value) => {
248
+ const text = String(value || "").slice(0, 10);
249
+ return /^\d{4}-\d{2}-\d{2}$/u.test(text) ? text : new Date().toISOString().slice(0, 10);
250
+ };
251
+
252
+ // ---- Index valuation (WSJ peyields) -----------------------------------------
253
+
254
+ /**
255
+ * WSJ's internal instrument tickers, which are not exchange symbols: the NASDAQ 100 row is
256
+ * published under `RIXF` and the S&P 500 under `INX`. This map exists precisely so nobody
257
+ * matches on the display name, which WSJ is free to reword.
258
+ */
259
+ export const WSJ_INDEX_TICKERS = Object.freeze({ INX: "^GSPC", RIXF: "^NDX", RUT: "^RUT" });
260
+
261
+ export function parseWsjPeYields(payload, { sourceUrl = SOURCE_URLS.wsj_index_valuation } = {}) {
262
+ const root = requireObject(payload, "WSJ peyields");
263
+ if (root.type !== "mdc_peAndYields") {
264
+ throw new Error(`WSJ peyields: unexpected payload type ${JSON.stringify(root.type)}`);
265
+ }
266
+ const data = requireObject(root.data, "WSJ peyields data");
267
+ const tradeDate = isoDateFromWsj(data.tradeDate, "WSJ peyields tradeDate");
268
+ const rows = requireArray(data.instruments, "WSJ peyields instruments");
269
+ const instruments = rows.map((row, index) => {
270
+ const where = `WSJ peyields instrument #${index}`;
271
+ const item = requireObject(row, where);
272
+ const ticker = requireString(item.ticker, `${where} ticker`);
273
+ return Object.freeze({
274
+ symbol: WSJ_INDEX_TICKERS[ticker] || null,
275
+ name: requireString(item.name, `${where} name`),
276
+ ticker,
277
+ pe_trailing: boundedNumber(item.priceEarningsRatio, `${where} priceEarningsRatio`, BANDS.PE),
278
+ pe_forward: boundedNumber(item.priceEarningsRatioEstimate, `${where} priceEarningsRatioEstimate`, { ...BANDS.PE, allowNull: true }),
279
+ pe_52w_ago: boundedNumber(item.priceEarningsRatio52WeekAgo, `${where} priceEarningsRatio52WeekAgo`, { ...BANDS.PE, allowNull: true }),
280
+ dividend_yield: boundedNumber(item.yield, `${where} yield`, { ...BANDS.YIELD_PCT, allowNull: true }),
281
+ // Units are spelled out because they differ inside one fact: the P/E fields are plain
282
+ // ratios, the dividend yield is a percent as published, and `earningsYield` below returns
283
+ // a decimal fraction. Reading any of the three as another is a 100x error.
284
+ pe_unit: "ratio",
285
+ dividend_yield_unit: "percent",
286
+ basis: PE_BASIS.WSJ_INDEX,
287
+ basis_note: "WSJ index-level aggregation; not comparable with GAAP as-reported TTM or an ETF portfolio harmonic mean",
288
+ trade_date: tradeDate,
289
+ public_at: publicAt(tradeDate),
290
+ source_url: sourceUrl,
291
+ });
292
+ });
293
+ return Object.freeze({
294
+ trade_date: tradeDate,
295
+ public_at: publicAt(tradeDate),
296
+ basis: PE_BASIS.WSJ_INDEX,
297
+ instruments: Object.freeze(instruments),
298
+ source_url: sourceUrl,
299
+ });
300
+ }
301
+
302
+ export async function fetchIndexValuation({ signal } = {}) {
303
+ const url = SOURCE_URLS.wsj_index_valuation;
304
+ const text = await fetchText(url, LIMITS.QUOTE_FETCH_MS, signal);
305
+ return parseWsjPeYields(JSON.parse(text), { sourceUrl: url });
306
+ }
307
+
308
+ /** 1/pe as a decimal fraction. Null for an absent, zero or negative P/E, never a signed inverse. */
309
+ export function earningsYield(pe) {
310
+ const value = Number(pe);
311
+ if (!Number.isFinite(value) || value <= 0) return null;
312
+ return Number((1 / value).toFixed(6));
313
+ }
314
+
315
+ // ---- Market breadth (WSJ markets diary) --------------------------------------
316
+
317
+ const BREADTH_FIELDS = Object.freeze({
318
+ issuestraded: Object.freeze({ key: "issues_traded", band: BANDS.COUNT, required: false }),
319
+ advances: Object.freeze({ key: "advances", band: BANDS.COUNT, required: true }),
320
+ declines: Object.freeze({ key: "declines", band: BANDS.COUNT, required: true }),
321
+ unchanged: Object.freeze({ key: "unchanged", band: BANDS.COUNT, required: false }),
322
+ newhighs: Object.freeze({ key: "new_highs", band: BANDS.COUNT, required: false }),
323
+ newlows: Object.freeze({ key: "new_lows", band: BANDS.COUNT, required: false }),
324
+ closingarmstrin: Object.freeze({ key: "trin", band: BANDS.TRIN, required: false }),
325
+ });
326
+
327
+ /**
328
+ * Only the `latestClose` column is carried. WSJ also publishes `previousClose` and `weekAgo`,
329
+ * but it does not publish the dates those columns belong to, and inferring them across holidays
330
+ * would produce an undated number wearing a date. Those columns are deliberately dropped.
331
+ */
332
+ function parseBreadthSet(set, index, tradeDate) {
333
+ const where = `WSJ markets diary set #${index}`;
334
+ const node = requireObject(set, where);
335
+ const header = requireArray(node.headerFields, `${where} headerFields`);
336
+ const first = requireObject(header[0], `${where} headerFields[0]`);
337
+ if (first.value !== "name") throw new Error(`${where}: first header column is ${JSON.stringify(first.value)}, expected "name"`);
338
+ const venue = requireString(first.label, `${where} venue label`);
339
+ const rows = requireArray(node.instruments, `${where} instruments`);
340
+
341
+ const values = {};
342
+ for (const row of rows) {
343
+ const item = requireObject(row, `${where} row`);
344
+ const spec = BREADTH_FIELDS[item.id];
345
+ if (!spec) continue;
346
+ // The NYSE table repeats volume ids for primary-market and composite rows; those are not
347
+ // extracted. A repeat of a field that IS extracted means the table changed meaning.
348
+ if (Object.prototype.hasOwnProperty.call(values, spec.key)) {
349
+ throw new Error(`${where}: duplicate row for ${item.id}`);
350
+ }
351
+ values[spec.key] = boundedNumber(item.latestClose, `${where} ${item.id}`, { ...spec.band, allowNull: true });
352
+ }
353
+ for (const spec of Object.values(BREADTH_FIELDS)) {
354
+ if (spec.required && !Number.isFinite(values[spec.key])) throw new Error(`${where}: missing ${spec.key}`);
355
+ }
356
+
357
+ const { advances, declines, new_highs: highs, new_lows: lows } = values;
358
+ return Object.freeze({
359
+ venue,
360
+ issues_traded: values.issues_traded ?? null,
361
+ advances,
362
+ declines,
363
+ unchanged: values.unchanged ?? null,
364
+ new_highs: highs ?? null,
365
+ new_lows: lows ?? null,
366
+ trin: values.trin ?? null,
367
+ net_advances: advances - declines,
368
+ advance_decline_ratio: declines > 0 ? Number((advances / declines).toFixed(4)) : null,
369
+ net_new_highs: Number.isFinite(highs) && Number.isFinite(lows) ? highs - lows : null,
370
+ unit: "issue_counts",
371
+ trade_date: tradeDate,
372
+ public_at: publicAt(tradeDate),
373
+ });
374
+ }
375
+
376
+ export function parseWsjMarketsDiary(payload, { sourceUrl = SOURCE_URLS.wsj_market_breadth } = {}) {
377
+ const root = requireObject(payload, "WSJ markets diary");
378
+ if (root.type !== "mdc_marketsdiary") {
379
+ throw new Error(`WSJ markets diary: unexpected payload type ${JSON.stringify(root.type)}`);
380
+ }
381
+ const data = requireObject(root.data, "WSJ markets diary data");
382
+ const tradeDate = isoDateFromLongForm(data.timestamp, "WSJ markets diary timestamp");
383
+ const sets = requireArray(data.instrumentSets, "WSJ markets diary instrumentSets");
384
+ const venues = sets.map((set, index) => parseBreadthSet(set, index, tradeDate));
385
+ return Object.freeze({
386
+ trade_date: tradeDate,
387
+ public_at: publicAt(tradeDate),
388
+ venues: Object.freeze(venues),
389
+ by_venue: Object.freeze(Object.fromEntries(venues.map((row) => [row.venue, row]))),
390
+ source_url: sourceUrl,
391
+ });
392
+ }
393
+
394
+ export async function fetchMarketBreadth({ signal } = {}) {
395
+ const url = SOURCE_URLS.wsj_market_breadth;
396
+ const text = await fetchText(url, LIMITS.QUOTE_FETCH_MS, signal);
397
+ return parseWsjMarketsDiary(JSON.parse(text), { sourceUrl: url });
398
+ }
399
+
400
+ // ---- Put/call ratios (CBOE daily statistics) ---------------------------------
401
+
402
+ export const CBOE_MAX_TRADING_DAY_LOOKBACK = 5;
403
+
404
+ const CBOE_RATIOS = Object.freeze({
405
+ "TOTAL PUT/CALL RATIO": "total",
406
+ "INDEX PUT/CALL RATIO": "index",
407
+ "EQUITY PUT/CALL RATIO": "equity",
408
+ "EXCHANGE TRADED PRODUCTS PUT/CALL RATIO": "exchange_traded_products",
409
+ "CBOE VOLATILITY INDEX (VIX) PUT/CALL RATIO": "vix",
410
+ "SPX + SPXW PUT/CALL RATIO": "spx_spxw",
411
+ });
412
+
413
+ /**
414
+ * Candidate trade dates, newest first, skipping weekends.
415
+ *
416
+ * The daily file 403s on any non-trading day and also on the current session until it is
417
+ * published, so the most recent trading day has to be discovered by trying. Weekends are skipped
418
+ * because CBOE never publishes one; holidays are NOT skipped, because no reliable US market
419
+ * calendar is derivable here and a wrong one would silently skip a real trading day.
420
+ */
421
+ export function tradingDayCandidates(asOf = null, maxAttempts = CBOE_MAX_TRADING_DAY_LOOKBACK) {
422
+ const limit = Number.isFinite(maxAttempts) && maxAttempts > 0 ? Math.floor(maxAttempts) : 1;
423
+ const cursor = new Date(`${isoDay(asOf)}T00:00:00Z`);
424
+ const days = [];
425
+ // Bounded so a pathological input cannot spin: at most two weekend days per five weekdays.
426
+ for (let step = 0; step < limit * 2 + 7 && days.length < limit; step += 1) {
427
+ const weekday = cursor.getUTCDay();
428
+ if (weekday !== 0 && weekday !== 6) days.push(cursor.toISOString().slice(0, 10));
429
+ cursor.setUTCDate(cursor.getUTCDate() - 1);
430
+ }
431
+ return days;
432
+ }
433
+
434
+ /** A published ratio, or null. CBOE writes `0.00` for a product with no contracts traded that
435
+ * session; that is an absence, not a put/call ratio of zero, so it must not read as extreme
436
+ * call skew. */
437
+ function cboeRatio(raw, what) {
438
+ const value = boundedNumber(raw, what, { ...BANDS.PUT_CALL, allowNull: true });
439
+ return value === null || value === 0 ? null : value;
440
+ }
441
+
442
+ function cboeVolumeBlock(payload, key) {
443
+ const rows = payload[key];
444
+ if (rows === undefined) return null;
445
+ const where = `CBOE daily options "${key}"`;
446
+ const out = {};
447
+ for (const row of requireArray(rows, where)) {
448
+ const item = requireObject(row, `${where} row`);
449
+ const slot = item.name === "VOLUME" ? "volume" : (item.name === "OPEN INTEREST" ? "open_interest" : null);
450
+ if (!slot) continue;
451
+ out[slot] = Object.freeze({
452
+ call: boundedNumber(item.call, `${where} ${item.name} call`, { min: 0, max: 1e12 }),
453
+ put: boundedNumber(item.put, `${where} ${item.name} put`, { min: 0, max: 1e12 }),
454
+ total: boundedNumber(item.total, `${where} ${item.name} total`, { min: 0, max: 1e12 }),
455
+ });
456
+ }
457
+ return Object.keys(out).length ? Object.freeze(out) : null;
458
+ }
459
+
460
+ export function parseCboeDailyOptions(payload, { tradeDate, sourceUrl, requestedAsOf = null, attempts = [] } = {}) {
461
+ const root = requireObject(payload, "CBOE daily options");
462
+ const rows = requireArray(root.ratios, "CBOE daily options ratios");
463
+ const published = new Map();
464
+ for (const row of rows) {
465
+ const item = requireObject(row, "CBOE daily options ratio row");
466
+ published.set(requireString(item.name, "CBOE daily options ratio name"), item.value);
467
+ }
468
+ if (!published.has("TOTAL PUT/CALL RATIO")) {
469
+ throw new Error("CBOE daily options: TOTAL PUT/CALL RATIO is absent");
470
+ }
471
+ const ratios = {};
472
+ for (const [name, key] of Object.entries(CBOE_RATIOS)) {
473
+ ratios[key] = published.has(name) ? cboeRatio(published.get(name), `CBOE daily options "${name}"`) : null;
474
+ }
475
+ if (ratios.total === null) throw new Error("CBOE daily options: TOTAL PUT/CALL RATIO is not a usable number");
476
+ return Object.freeze({
477
+ trade_date: tradeDate,
478
+ // The caller asked for `requestedAsOf` and got `tradeDate`; keeping both stops a Monday
479
+ // reading from being presented as today's.
480
+ requested_as_of: requestedAsOf,
481
+ is_most_recent_available: true,
482
+ public_at: publicAt(tradeDate),
483
+ unit: "ratio_of_put_to_call_volume",
484
+ ratios: Object.freeze(ratios),
485
+ volume: Object.freeze({
486
+ total: cboeVolumeBlock(root, "SUM OF ALL PRODUCTS"),
487
+ index: cboeVolumeBlock(root, "INDEX OPTIONS"),
488
+ equity: cboeVolumeBlock(root, "EQUITY OPTIONS"),
489
+ exchange_traded_products: cboeVolumeBlock(root, "EXCHANGE TRADED PRODUCTS"),
490
+ }),
491
+ skipped_dates: Object.freeze([...attempts]),
492
+ source_url: sourceUrl,
493
+ });
494
+ }
495
+
496
+ export async function fetchPutCallRatios({ signal, asOf = null, maxAttempts = CBOE_MAX_TRADING_DAY_LOOKBACK } = {}) {
497
+ const candidates = tradingDayCandidates(asOf, maxAttempts);
498
+ const attempts = [];
499
+ for (const date of candidates) {
500
+ // An upstream cancellation must not be retried four more times.
501
+ if (signal?.aborted) throw new Error("CBOE daily options: cancelled before a trading day was found");
502
+ const url = `${SOURCE_URLS.cboe_put_call_daily}${date}_daily_options`;
503
+ try {
504
+ const text = await fetchText(url, LIMITS.QUOTE_FETCH_MS, signal);
505
+ return parseCboeDailyOptions(JSON.parse(text), {
506
+ tradeDate: date, sourceUrl: url, requestedAsOf: isoDay(asOf), attempts,
507
+ });
508
+ } catch (error) {
509
+ attempts.push(`${date}: ${String(error?.message || error)}`);
510
+ }
511
+ }
512
+ throw new Error(`CBOE daily options unavailable for the last ${candidates.length} weekdays (${attempts.join("; ")})`);
513
+ }
514
+
515
+ // ---- VIX history (CBOE) ------------------------------------------------------
516
+
517
+ const VIX_HEADER = Object.freeze(["DATE", "OPEN", "HIGH", "LOW", "CLOSE"]);
518
+
519
+ /**
520
+ * A malformed or non-numeric row is dropped rather than carried forward, exactly as a missing
521
+ * FRED observation is. The band check is applied to the latest value only: history is used for
522
+ * ranking, but the latest close is the number that becomes a published fact.
523
+ */
524
+ export function parseVixHistoryCsv(csv, { sourceUrl = SOURCE_URLS.cboe_vix_history, asOf = null } = {}) {
525
+ const lines = String(csv || "").trim().split(/\r?\n/);
526
+ if (lines.length < 2) throw new Error("CBOE VIX history: empty CSV");
527
+ const header = lines[0].trim().toUpperCase().split(",").map((cell) => cell.trim());
528
+ if (VIX_HEADER.some((column, index) => header[index] !== column)) {
529
+ throw new Error(`CBOE VIX history: unexpected header ${JSON.stringify(lines[0])}`);
530
+ }
531
+ const observations = [];
532
+ for (const line of lines.slice(1)) {
533
+ const cells = line.split(",");
534
+ const match = /^(\d{2})\/(\d{2})\/(\d{4})$/u.exec(String(cells[0] || "").trim());
535
+ if (!match) continue;
536
+ const close = Number(cells[4]);
537
+ if (!Number.isFinite(close) || close <= 0) continue;
538
+ observations.push({ date: `${match[3]}-${match[1]}-${match[2]}`, value: close });
539
+ }
540
+ if (!observations.length) throw new Error("CBOE VIX history: no numeric observations");
541
+ const cutoff = asOf ? String(asOf).slice(0, 10) : null;
542
+ const visible = cutoff ? observations.filter((row) => row.date <= cutoff) : observations;
543
+ if (!visible.length) throw new Error(`CBOE VIX history: no observation at or before ${cutoff}`);
544
+ const last = visible.at(-1);
545
+ boundedNumber(last.value, "CBOE VIX history latest close", BANDS.VIX);
546
+ return Object.freeze({
547
+ id: "VIX",
548
+ unit: "index_points",
549
+ label: "CBOE Volatility Index, daily close",
550
+ observations: visible,
551
+ latest: last.value,
552
+ observation_date: last.date,
553
+ public_at: publicAt(last.date),
554
+ source_url: sourceUrl,
555
+ });
556
+ }
557
+
558
+ export async function fetchVixHistory({ signal, asOf = null } = {}) {
559
+ const url = SOURCE_URLS.cboe_vix_history;
560
+ // A ~470KB CSV needs more headroom than a JSON snapshot, matching the FRED CSV budget.
561
+ const text = await fetchText(url, LIMITS.QUOTE_FETCH_MS * 2, signal);
562
+ return parseVixHistoryCsv(text, { sourceUrl: url, asOf });
563
+ }
564
+
565
+ // ---- Percentiles -------------------------------------------------------------
566
+
567
+ /** Value `windowDays` calendar days before the latest observation, or null when unavailable. */
568
+ export function valueBefore(observations, windowDays) {
569
+ const rows = Array.isArray(observations) ? observations : observations?.observations;
570
+ if (!Array.isArray(rows) || !rows.length) return null;
571
+ const cutoff = new Date(`${rows.at(-1).date}T00:00:00Z`);
572
+ cutoff.setUTCDate(cutoff.getUTCDate() - windowDays);
573
+ const target = cutoff.toISOString().slice(0, 10);
574
+ const prior = rows.filter((row) => row.date <= target);
575
+ return prior.length ? prior.at(-1) : null;
576
+ }
577
+
578
+ /**
579
+ * Where the latest observation sits in its own history, as a 0-1 rank.
580
+ *
581
+ * Field-for-field the same contract as `fred.mjs` `percentileRank`, so a reader never has to ask
582
+ * which percentile convention a given number follows. Accepts a series object or a bare
583
+ * observation array. Returns null below `minSample`: "the 92nd percentile" means nothing without
584
+ * saying percentile of what and since when.
585
+ */
586
+ export function percentileRank(series, { sinceDays = 365 * 10, minSample = 30 } = {}) {
587
+ const rows = Array.isArray(series) ? series : series?.observations;
588
+ if (!Array.isArray(rows) || rows.length < 2) return null;
589
+ const start = valueBefore(rows, sinceDays);
590
+ const sample = start ? rows.filter((row) => row.date >= start.date) : rows;
591
+ if (sample.length < minSample) return null;
592
+ const latest = rows.at(-1).value;
593
+ const below = sample.filter((row) => row.value < latest).length;
594
+ return {
595
+ percentile: Number((below / sample.length).toFixed(4)),
596
+ sample_size: sample.length,
597
+ sample_start: sample[0].date,
598
+ sample_end: rows.at(-1).date,
599
+ };
600
+ }
601
+
602
+ export const VALUATION_PERCENTILE_GAP =
603
+ "no same-basis index P/E history is available from an implemented source; a percentile computed "
604
+ + "from one observation, or against a differently-based history, would be fabricated";
605
+
606
+ /**
607
+ * A valuation percentile that always states its metric, its basis and its sample.
608
+ *
609
+ * Refuses in three cases, all of which are gaps rather than zeroes: no history at all, a sample
610
+ * too short to rank against, or a history whose declared basis differs from the fact's. The last
611
+ * one is the whole point -- ranking a WSJ-basis 25.17x inside a GAAP-basis history would look
612
+ * like a cheap market when nothing about the market changed.
613
+ */
614
+ export function valuationPercentile(fact, { metric = "pe_trailing", history = null, sinceDays = 365 * 10 } = {}) {
615
+ const value = Number(fact?.[metric]);
616
+ if (!Number.isFinite(value)) return null;
617
+ const historyBasis = Array.isArray(history) ? null : history?.basis;
618
+ if (historyBasis && fact?.basis && historyBasis !== fact.basis) return null;
619
+ const rank = percentileRank(history, { sinceDays });
620
+ if (!rank) return null;
621
+ return Object.freeze({ metric, basis: fact?.basis ?? null, value, ...rank });
622
+ }
623
+
624
+ // ---- Orchestration -----------------------------------------------------------
625
+
626
+ async function settle(label, run, unavailable) {
627
+ try {
628
+ return await run();
629
+ } catch (error) {
630
+ unavailable.push(`${label}: ${String(error?.message || error)}`);
631
+ return null;
632
+ }
633
+ }
634
+
635
+ /** The one index the caller asked for. A feed that simply does not cover it is a named gap. */
636
+ function selectValuation(valuationSet, symbol, unavailable) {
637
+ const valuation = valuationSet?.instruments.find((row) => row.symbol === symbol) || null;
638
+ if (valuationSet && !valuation) {
639
+ const covered = valuationSet.instruments.map((row) => row.symbol || row.ticker).join(", ");
640
+ unavailable.push(`index valuation for ${symbol}: WSJ peyields publishes ${covered} only`);
641
+ }
642
+ return valuation;
643
+ }
644
+
645
+ /** Earnings yield inherits the P/E's basis and dates; it is a restatement, not a new observation. */
646
+ function earningsYieldFact(valuation) {
647
+ const value = valuation ? earningsYield(valuation.pe_trailing) : null;
648
+ if (!Number.isFinite(value)) return null;
649
+ return Object.freeze({
650
+ value,
651
+ unit: "decimal_fraction",
652
+ derived_from: "pe_trailing",
653
+ basis: valuation.basis,
654
+ trade_date: valuation.trade_date,
655
+ public_at: valuation.public_at,
656
+ source_url: valuation.source_url,
657
+ });
658
+ }
659
+
660
+ /** Gaps that exist by design rather than by upstream failure, so they are stated every run. */
661
+ function recordStructuralGaps(symbol, constituents, unavailable) {
662
+ if (!constituents) {
663
+ unavailable.push(`constituent proxy for ${symbol}: no tracking-ETF proxy is mapped for this index`);
664
+ } else if (!constituents.holdings) {
665
+ unavailable.push(
666
+ `constituents for ${symbol}: not fetched here; pass ${constituents.proxy_etf} holdings in. `
667
+ + `Membership and weights are licensed by ${constituents.licensor} and must be labelled a proxy.`,
668
+ );
669
+ }
670
+ for (const source of DAMODARAN_SOURCES) {
671
+ if (!source.parser_implemented) unavailable.push(`${source.label} (${source.url}): ${source.reason}`);
672
+ }
673
+ }
674
+
675
+ /**
676
+ * Every aggregate fact available for one index, with a named gap for everything that is not.
677
+ *
678
+ * `holdings` is the hook for the ETF-proxy path: pass the tracking ETF's published holdings in
679
+ * and they are attached, labelled as a proxy. Nothing here scrapes a constituent list.
680
+ * `peHistory` is the hook for a same-basis P/E history; without one the valuation percentile
681
+ * stays null rather than being computed from the single point this module can see.
682
+ */
683
+ export async function fetchIndexAggregate({ symbol, signal, asOf = null, holdings = null, peHistory = null } = {}) {
684
+ const normalized = normalizeIndexSymbol(symbol);
685
+ if (!normalized) throw new Error("fetchIndexAggregate requires an index symbol");
686
+ const unavailable = [];
687
+
688
+ const [valuationSet, breadth, putCall, vix] = await Promise.all([
689
+ settle("index valuation (WSJ peyields)", () => fetchIndexValuation({ signal }), unavailable),
690
+ settle("market breadth (WSJ markets diary)", () => fetchMarketBreadth({ signal }), unavailable),
691
+ settle("put/call ratios (CBOE daily)", () => fetchPutCallRatios({ signal, asOf }), unavailable),
692
+ settle("VIX history (CBOE)", () => fetchVixHistory({ signal, asOf }), unavailable),
693
+ ]);
694
+
695
+ const valuation = selectValuation(valuationSet, normalized, unavailable);
696
+ const percentile = valuation ? valuationPercentile(valuation, { history: peHistory }) : null;
697
+ if (valuation && !percentile) unavailable.push(`index P/E valuation percentile for ${normalized}: ${VALUATION_PERCENTILE_GAP}`);
698
+ const constituents = proxyConstituents(normalized, holdings);
699
+ recordStructuralGaps(normalized, constituents, unavailable);
700
+
701
+ return Object.freeze({
702
+ symbol: normalized,
703
+ as_of: asOf ? String(asOf) : new Date().toISOString(),
704
+ valuation,
705
+ earnings_yield: earningsYieldFact(valuation),
706
+ valuation_percentile: percentile,
707
+ breadth,
708
+ put_call: putCall,
709
+ vix,
710
+ vix_percentile: vix ? percentileRank(vix) : null,
711
+ constituents,
712
+ equity_risk_premium: null,
713
+ basis_warning: PE_BASIS_DIVERGENCE,
714
+ sources: SOURCE_URLS,
715
+ unimplemented_sources: DAMODARAN_SOURCES,
716
+ unavailable,
717
+ });
718
+ }