Cyclical and Commodity-Cycle Normalization
Install and import#
npm install fintech-algorithmsimport { cyclicalAndCommodityCycleNormalization } from "fintech-algorithms/fundamental-analysis-and-valuation/sector-specific-equity-scoring/cyclical-and-commodity-cycle-normalization";Signature#
cyclicalAndCommodityCycleNormalization(data)Normalizes a commodity producer to mid-cycle by taking the median of its price, unit-cost and volume histories, rebuilding revenue and EBITDA from those medians less fixed costs, and scoring the resulting margin, leverage, free-cash-flow consistency and where the current price sits in its own history.
Parameters#
| Name | Type | Notes |
|---|---|---|
data | RecordValue | A plain object. framework must be the string cyclical-midcycle-teaching-v1. weights must be an object holding exactly normalized_margin, normalized_leverage, cash_consistency and cycle_balance. realized_price_history, unit_cost_history, volume_history and free_cash_flow_history must each be an array of at least seven finite numbers, and all four must have the same length; the price, cost and volume series must additionally be strictly positive throughout. fixed_costs and net_debt must be nonnegative and current_realized_price strictly positive.min_observations: 7 |
Returns#
{ state: string; method: string; normalized_price: number; normalized_unit_cost: number; normalized_volume: number; normalized_revenue: number; normalized_ebitda: number; normalized_margin: number; normalized_net_leverage: number | null; positive_fcf_ratio: number; cycle_percentile: number; component_scores: Record<string, number>; weights: Record<string, number>; normalized_score: number; coverage_ratio: number; reason: string }
normalized_price, normalized_unit_cost and normalized_volume are the medians of their series. normalized_revenue is median price times median volume; normalized_ebitda is median price less median unit cost, times median volume, less fixed_costs; normalized_margin is EBITDA over revenue. normalized_net_leverage is net_debt over that EBITDA, or null when EBITDA is not positive, in which case the leverage component scores 0. positive_fcf_ratio is the share of the free-cash-flow history above zero and cycle_percentile the share of historical prices at or below current_realized_price. component_scores holds four band scores on a 0-100 scale and normalized_score is their weighted sum. state is nonpositive-midcycle-ebitda-review when EBITDA is zero or negative, otherwise the band of the score: strong-review-band at 75 or more, mixed-review-band at 50 or more, weak-review-band below that. method is cyclical-midcycle-normalization-v1 and coverage_ratio is 1.
Errors#
- When data is not a plain object — throws TypeError
- When framework is not a nonempty string, or a history entry is not a finite number — throws TypeError
- When framework is not cyclical-midcycle-teaching-v1 — throws RangeError
- When any of the four histories is not an array of at least 7 observations — throws RangeError
- When the four histories do not all have the same length — throws RangeError
- When any price, unit-cost or volume observation is zero or negative — throws RangeError
- When weights does not hold exactly the four component names, or its values do not sum to 1 within 1e-9 — throws RangeError
- When fixed_costs or net_debt is negative, or current_realized_price is not positive — throws RangeError
Complexity: time O(n log n),
space O(n).
Worked example#
executed Captured by running this function on the input its own test provides. Real output of real code — but not asserted against a published figure.
Input#
{
"framework": "cyclical-midcycle-teaching-v1",
"realized_price_history": [72, 64, 58, 83, 91, 77],
"unit_cost_history": [43, 44, 45, 47, 49, 46],
"volume_history": [9.2, 9.4, 9, 9.7, 9.8, 9.6],
"free_cash_flow_history": [52, 18, -8, 74, 96, 61],
"fixed_costs": 105,
"net_debt": 690,
"current_realized_price": 74,
"weights": {
"normalized_margin": 0.32,
"normalized_leverage": 0.28,
"cash_consistency": 0.22,
"cycle_balance": 0.18
}
}Call#
cyclicalAndCommodityCycleNormalization(data)Returns#
object with 16 fields: state, method, normalized_price, normalized_unit_cost, normalized_volume, normalized_revenue, normalized_ebitda, normalized_margin, …
{
"state": "mixed-review-band",
"method": "cyclical-midcycle-normalization-v1",
"normalized_price": 72,
"normalized_unit_cost": 45,
"normalized_volume": 9.4,
"normalized_revenue": 676.8000000000001,
"normalized_ebitda": 148.8,
"normalized_margin": 0.21985815602836878,
"normalized_net_leverage": 4.637096774193548,
"positive_fcf_ratio": 0.7777777777777778,
"cycle_percentile": 0.6666666666666666,
"component_scores": {
"normalized_margin": 73.28605200945627,
"normalized_leverage": 0,
"cash_consistency": 79.62962962962962,
"cycle_balance": 83.33333333333333
},
"weights": {
"normalized_margin": 0.32,
"normalized_leverage": 0.28,
"cash_consistency": 0.22,
"cycle_balance": 0.18
},
"normalized_score": 55.97005516154452
}Showing 14 of 16 fields.
Other exports#
This module also exports
calculate, bankFundamentalScore, insuranceFundamentalScore, reitFundamentalScore, utilityFundamentalScore, earlyStageLiquidityAndRunwayScore, holdingCompanyLookThroughScore, sectorSpecificWeightCalibration, unsupportedScopeAndCoverageDecision. Every module additionally exports run as an alias of its
primary function, and a meta object carrying its catalog id, domain, family,
shape and article URL.
Diagrams#
How it works#
This page states the contract — how to call it correctly. The article explains the concept: why it works, and where it breaks.
References#
- Oil and Gas Reporting Modernization — Small Entity Compliance Guide — U.S. Securities and Exchange Commission
- IAS 36 Impairment of Assets — International Accounting Standards Board
- Spot Prices for Crude Oil and Petroleum Products — U.S. Energy Information Administration
- Non-GAAP Financial Measures Compliance and Disclosure Interpretations — U.S. Securities and Exchange Commission
- Evidence boundary