Install
openclaw skills install fp-dcfEstimate intrinsic value with a first-principles DCF from structured JSON or provider-backed ticker input, and return auditable FCFF, WACC, and per-share value output.
openclaw skills install fp-dcfVersion: v0.6.0
Use this skill when the task is to estimate intrinsic value from structured fundamentals and assumption inputs using a disciplined, auditable DCF workflow.
Use this skill when the user asks for:
FCFF / WACC based valuationWACC / terminal-growth sensitivityDo not use this skill as the only answer when the user asks:
For investment-report tasks, FP-DCF may be used only as the valuation layer. The agent must still handle:
steady_state_single_stage when the company is mature, normalized, or the user wants a conservative base case.two_stage when there is a defensible explicit high-growth period followed by stable growth.three_stage when high growth is expected to fade gradually before terminal growth.market_implied_growth.enabled=true when the user asks what the current price implies.matplotlib or the user asks for JSON-only output.This repository is executable when installed as a skill because it includes a concrete CLI entrypoint:
{baseDir}/scripts/run_dcf.pypython3 -m fp_dcf.cli{baseDir}/examples/sample_input.jsonakshare, baostock, numpy, pandas, yfinance, matplotlibPreferred execution pattern:
{baseDir}/examples/sample_input.json.python3 {baseDir}/scripts/run_dcf.py --input /path/to/input.json --prettypython {baseDir}/scripts/run_dcf.py --input /path/to/input.json --prettyIf the runtime supports stdin piping, this also works:
python3 {baseDir}/scripts/run_dcf.py --pretty < /path/to/input.json
Provider-backed normalization uses a local provider cache by default. To force a fresh pull for the current request, run:
python3 {baseDir}/scripts/run_dcf.py --input /path/to/input.json --pretty --refresh-provider
If the runtime needs an isolated cache location, pass:
python3 {baseDir}/scripts/run_dcf.py --input /path/to/input.json --pretty --cache-dir /path/to/cache
The main runner already returns a compact sensitivity summary and auto-renders both SVG and PNG chart artifacts by default. If the user explicitly asks for a WACC x Terminal Growth chart or sensitivity table, keep using the same main runner so the valuation JSON and artifact paths come back in a single output:
python3 {baseDir}/scripts/run_dcf.py \
--input /path/to/input.json \
--output /path/to/output.json \
--pretty
That one command will default the chart artifact paths to /path/to/output.sensitivity.svg and /path/to/output.sensitivity.png.
The expected JSON object contains:
tickermarketvaluation_modelassumptionsfundamentalsMinimum required values for a useful result:
assumptions.effective_tax_rateassumptions.marginal_tax_rateassumptions.risk_free_rateassumptions.equity_risk_premiumassumptions.betaassumptions.pre_tax_cost_of_debtfundamentals.fcff_anchor or fundamentals.ebitSupported valuation_model values in v0.6.0:
steady_state_single_stagetwo_stagethree_stageFor two_stage, the engine continues to support the legacy assumptions.high_growth_rate / high_growth_years fields and also accepts assumptions.stage1_growth_rate / stage1_years as compatible aliases.
For three_stage, the valuation path requires these assumption fields:
assumptions.terminal_growth_rateassumptions.stage1_growth_rateassumptions.stage1_yearsassumptions.stage2_end_growth_rateassumptions.stage2_yearsFor three_stage, this optional field is also supported:
assumptions.stage2_decay_mode with default linearmarket_implied_growth is the only formal market-implied block. Its meaning depends on valuation_model: steady_state_single_stage solves market-implied long-term growth, while two_stage and three_stage solve market-implied stage-1 growth.
If fundamentals.fcff_anchor is not supplied, the runner computes it from:
ebitdacapexdelta_nwc or a fallback working-capital fieldIf those structured fields are mostly missing, the runner can auto-normalize them from a live provider when:
provider is set to yahoo, orprovider is set to akshare_baostock for market=CN, orticker but is missing core DCF inputsThe minimal provider-backed input shape is:
{
"ticker": "AAPL",
"market": "US",
"provider": "yahoo",
"statement_frequency": "A",
"valuation_model": "steady_state_single_stage",
"assumptions": {
"terminal_growth_rate": 0.03
}
}
For China A-shares, this explicit provider shape is also supported:
{
"ticker": "600519.SH",
"market": "CN",
"provider": "akshare_baostock",
"statement_frequency": "A",
"valuation_model": "steady_state_single_stage",
"assumptions": {
"terminal_growth_rate": 0.025
}
}
When market="CN" and Yahoo normalization fails, FP-DCF automatically falls back to akshare_baostock. In that path, AkShare provides statement data and BaoStock provides price history and the latest close.
The payload can also drive normalization behavior through an optional normalization object:
normalization.providernormalization.use_cachenormalization.refreshnormalization.cache_dirnormalization.max_cache_age_hoursThe payload can also request sensitivity analysis through an optional sensitivity object:
sensitivity.enabledsensitivity.metricsensitivity.detailsensitivity.chart_pathsensitivity.wacc_range_bpssensitivity.wacc_step_bpssensitivity.growth_range_bpssensitivity.growth_step_bpsThe payload can also request market-implied growth through an optional market_implied_growth object:
market_implied_growth.enabledmarket_implied_growth.lower_boundmarket_implied_growth.upper_boundmarket_implied_growth.solvermarket_implied_growth.tolerancemarket_implied_growth.max_iterationsmarket_implied_growth always solves one field based on valuation_model:
steady_state_single_stage -> growth_ratetwo_stage -> stage1_growth_ratethree_stage -> stage1_growth_rateSensitivity output is enabled by default. To disable it for a specific run:
--no-sensitivity, orsensitivity.enabled=falseLive provider-backed runs are inherently date-sensitive. Do not hard-code expected valuation numbers when validating this path; validate the presence and plausibility of the returned fields instead.
Provider-backed runs return data_freshness:
fresh: provider cache age is within the allowed windowstale: provider cache age exceeds the allowed window; treat the valuation as degradedunknown: cache creation time is missing or unreadable; treat the valuation as degradedmissing: provider data is structurally missing; treat the valuation as degraded or fail if a required field is unavailableuser_supplied: manual structured input; do not force a freshness judgmentDefault freshness windows are 24 hours for price-sensitive provider snapshots and 168 hours for statement-only snapshots. Use --refresh-provider, normalization.refresh=true, or a stricter normalization.max_cache_age_hours when the user asks for current market data.
FCFF separate from the marginal tax assumption used in WACC.EBIAT/NOPAT.warnings.Use this fallback order and report which path was used:
delta_nwcop_nwc_deltanwc_deltachange_in_working_capitalIf all paths fail, flag the result as degraded rather than pretending the estimate is fully reliable.
FCFF anchor for single-stage valuation.NOPAT + ROIC + reinvestment when the driver data is usable.FCFF only when the operating-driver path is incomplete.FCFF as if it were a forward forecast.FCFF under industrial-company DCF logic.Always return:
contract_versionrequested_valuation_modeleffective_valuation_modeldegradedFCFF anchor and anchor methodWACC inputs and capital weightsvaluation.present_value_stage1valuation.present_value_stage2valuation.present_value_terminalvaluation.terminal_valuevaluation.terminal_value_sharevaluation.explicit_forecast_yearsmarket_implied_growth when that block is enabled and validdata_freshness with provider, snapshot_as_of, cache_created_at, cache_age_hours, freshness_class, and requires_refreshCanonical JSON Schema contracts live in:
contracts/valuation_input.schema.jsoncontracts/valuation_output.schema.jsoncontracts/data_freshness.schema.jsoncontracts/market_implied_growth.schema.jsoncontracts/sensitivity_summary.schema.jsonTreat contracts/ as machine contracts, not as loose documentation.
contract_version exists so downstream agents and pipelines can make explicit compatibility decisions.
Schema-valid does not guarantee business-semantic equivalence. Differences in assumptions, defaults, freshness, and degraded execution paths can still materially change how the output should be interpreted.
{baseDir} instead of guessing the install path.--input over hand-building one-line shell JSON.ticker/market plus light assumptions, rely on provider-backed normalization instead of fabricating fundamentals.--refresh-provider or set normalization.refresh=true.data_freshness.freshness_class is stale, unknown, or missing, surface the warning and avoid presenting the valuation as current.data_freshness.requires_refresh=true and the user asked for current market conditions, rerun with --refresh-provider.sensitivity.detail=true instead of bloating the default output for every run.--sensitivity-chart-output or sensitivity.chart_path when the caller explicitly wants to override the default artifact location.matplotlib is installed, because PNG/SVG chart artifacts are rendered automatically.matplotlib, disable sensitivity first with --no-sensitivity or sensitivity.enabled=false before running the main CLI.per_share_value sensitivity is unavailable because shares_out is missing, try --refresh-provider first or switch the sensitivity metric to equity_value.valuation_model=three_stage is missing stage1_growth_rate, stage1_years, stage2_end_growth_rate, stage2_years, or terminal_growth_rate, fail with a clear error instead of falling back.valuation_model is unknown, fail with an error containing unsupported valuation_model; do not silently remap it to another model.three_stage valuation into two_stage or steady_state_single_stage.v0.6.0: market_implied_growth remains the only formal market-implied block.market_implied_growth as the only formal market-implied block and derive the solved field from valuation_model.payload.market_implied_growth.enabled=true and let valuation_model=steady_state_single_stage determine the solved field.Read only what you need:
contracts/valuation_input.schema.json and contracts/valuation_output.schema.json for the canonical JSON contracts