D46-F02-A03 / Complete engineering topic

Options and Warrants: US-GAAP Treasury Stock vs IAS 33 Assumed Proceeds

Build an exact diluted-EPS denominator bridge without blending US-GAAP and IAS 33 proceeds rules.

D46 · EARNINGS AND PER-SHARE ANALYT…
D46-F02-A03Canonical / Tested / Open
D46 / D46-F02

Ten million warrants do not automatically add ten million shares to diluted EPS. US GAAP calls the calculation the treasury stock method. IAS 33 instead describes assumed exercise and an assumed issue at average market price. The arithmetic may look alike, but the proceeds inputs and period controls are framework-specific. Only the positive net shares left after permitted assumed proceeds enter the denominator.

This article builds that bridge exactly, explains why average market price and assumed proceeds are accounting inputs rather than casual guesses, and shows why an in-the-money option can still be excluded during a loss period.

The economic picture

Imagine 10 million warrants with a USD 15 exercise price while the ordinary share averages USD 25.

Assumed exercise creates:

10m×USD15=USD150m10m\times USD15=USD150m

At USD 25 per share, the hypothetical proceeds repurchase:

USD150m/USD25=6m sharesUSD150m/USD25=6m\text{ shares}

The remaining 4 million shares were effectively issued for no consideration. Those are the incremental shares that matter for diluted EPS.

Treasury-share bridge from gross warrants to incremental shares

This synthetic bridge illustrates IAS 33 paragraphs 45–47 and the US-GAAP mechanics reproduced in FASB ASU 2020-06.

The exact formula

Let QQ be fixed gross shares on exercise and tt the fraction of the reporting period for which the instrument was outstanding. Let XX be cash exercise price, SS framework-specific service proceeds, TT permitted tax proceeds, OO declared-other proceeds, and MM representative average market price.

Qw=Qt,E=X+S+T+OQ_w=Qt,\qquad E=X+S+T+O

When M>EM>E, the award is in the money:

Qr=QwEMQ_r=Q_w\frac{E}{M} ΔD=QwQr=QwMEM\Delta D=Q_w-Q_r=Q_w\frac{M-E}{M}

If MEM\le E, the method adds zero incremental shares. It does not subtract shares from the denominator.

For a Basic continuing-operations numerator NBN_B and denominator DBD_B:

EPSB=NBDBEPS_B=\frac{N_B}{D_B} EPSC=NBDB+ΔDEPS_C=\frac{N_B}{D_B+\Delta D}

The numerator stays unchanged in this canonical case. Include the series only if it is in the money and the exact candidate is numerically below Basic EPS.

Rendering system map…

Complete assumed proceeds matter

For a plain warrant, assumed proceeds may be only the cash exercise price. For a share-based payment award, use the framework control below.

InputIAS 33 / IFRSUS GAAP
Cash exercise priceIncludeInclude
Service componentIFRS 2 fair value of future goods/servicesAverage unrecognized compensation attributable to future service
Excess tax benefitNot a component in this scoped modelExclude after ASU 2016-09
Multi-quarter ruleDetermine the presented period independentlyBuild year-to-date incremental shares from quarterly results

The implementation therefore accepts separate, auditable fields:

  • cash_exercise_price_per_share;
  • service_proceeds_per_share and service_proceeds_basis;
  • tax_proceeds_per_share;
  • other_assumed_proceeds_per_share for a declared non-IFRS/non-US policy;
  • a nonempty proceeds policy;
  • a completeness flag.

It does not invent unrecognized compensation or service value. Those depend on the governing framework, award history, modifications and reporting date. US-GAAP and IFRS inputs fail validation if tax or declared-other proceeds leak into this scoped model, or if the service basis belongs to the other framework.

For example, adding USD 5 of correctly sourced IFRS 2 future-service fair value—or, in a separate US-GAAP case, USD 5 of average unrecognized compensation—to the USD 15 cash price raises the effective price to USD 20. The assumed repurchase becomes 8 million shares, leaving 2 million incremental shares:

10m10m(20/25)=2m10m-10m(20/25)=2m

The average price is a policy, not one convenient quote

The method uses a representative average market price for the applicable period, not simply the closing price on the reporting date. IAS 33 application guidance and FASB Statement 128 both describe consistent representative averages; weekly or monthly observations can be adequate when they represent the period, while volatile conditions may require a different consistent approach.

That is why the data contract requires:

  • the average price itself;
  • the price methodology;
  • the source and point-in-time availability;
  • a completeness assertion.

For this one-series calculator, the recorded price-period start and end must match the inclusive dates on which the potential shares were exposed. A grant issued or cancelled mid-period therefore cannot reuse an unsupported full-period average.

There is also a material cross-framework period issue. US GAAP year-to-date calculations use a weighted average of quarterly incremental-share computations. This implementation accepts US GAAP only for a single quarter. IFRS and declared local policies use the direct-period application. A caller cannot silently label an annual US computation as a quarter.

Timing without double counting

Potential-share exposure starts at the later of period start and issue date. It ends, exclusively, at the earliest of:

  • reporting period end plus one day;
  • actual exercise date;
  • lapse or cancellation date.

A 1 July issue in 2025 contributes 184/365184/365 of the gross shares. A 1 July actual exercise contributes potential shares through 30 June, or 181/365181/365. Actual shares from the exercise date onward must already be in the Basic denominator.

The time weight applies to gross potential shares. Repurchase and incremental shares inherit the same weight through the formula.

A repricing, modification or replacement is not treated as a harmless label change. The calculator requires unchanged terms during its exposure segment. Split a changed award at the effective date using the authoritative old and new terms, or route it to the accounting method applicable to the modification. Likewise, each grant or instrument series is calculated separately; do not average unlike strike prices, service components, currencies or contingencies into one synthetic grant.

Complete worked example

Assume USD 120 million of continuing-operations earnings, 50 million Basic shares, and the 10 million warrants described above.

EPSB=120/50=2.40EPS_B=120/50=2.40 ΔD=106=4m\Delta D=10-6=4m EPSC=120/(50+4)=120/54=20/92.22EPS_C=120/(50+4)=120/54=20/9\approx2.22

Because 20/9<12/520/9<12/5, the series is dilutive.

Now make the exercise price USD 30 while average market price stays USD 25. The hypothetical proceeds would repurchase 12 million shares, more than the 10 million issued. The method does not add negative incremental shares; it excludes the series.

Finally, keep the original in-the-money prices but change the Basic numerator to a USD 50 million loss:

EPSB=50/50=1.00EPS_B=-50/50=-1.00 EPSC=50/54=25/270.93EPS_C=-50/54=-25/27\approx-0.93

The candidate is numerically greater and decreases loss per share. The instrument is antidilutive even though it is in the money.

Explore these cases in the interactive treasury-share lab.

Why the historical issuer case is deferred

A pooled diluted-EPS increment is not enough to validate this options/warrants calculator. The increment may include performance awards, restricted units, or other contingently issuable shares that belong to a different topic.

A publishable named case needs primary evidence for one fixed-share, non-contingent option or warrant series: gross shares, exercise price, framework-specific service proceeds, representative average-price period, issue/exercise/cancellation dates, unchanged terms, share units, and the reported incremental result. No reviewed filing exposed that complete chain. The article therefore defers the case rather than importing a pooled issuer number, inventing grant-level terms, or substituting a market-data-provider observation.

Implementation discipline

Binary floating point is unnecessary here. Both references parse decimal strings into integers and powers of ten, calculate reduced rational numbers, compare exact cross-products, and round only display fields.

The output preserves:

  • exact time weight;
  • gross weighted shares;
  • proceeds-represented shares when exercise is assumed;
  • incremental fraction and shares;
  • exact Basic and candidate EPS;
  • separate money-ness and EPS-control diagnostics.

See the Python implementation, TypeScript implementation, and shared fixtures.

What the tests prove

CaseExpected outcome
USD 15 strike, USD 25 market4m incremental; 20/9; include
USD 30 strike, USD 25 market0 incremental; out of money
USD 25 strike, USD 25 market0 incremental; equality excluded
Continuing-operations lossIn the money but antidilutive
1 July issueExact weight 184/365
1 July exerciseExact weight 181/365
USD 5 IFRS 2 service valueEffective price 20; 2m incremental
USD 5 US-GAAP unrecognized compensationEffective price 20; 2m incremental

Negative tests reject incomplete proceeds and prices, wrong framework/application pairs, tax-benefit leakage, price-period mismatch, multiple series, changed terms, contingencies, malformed values, zero exposure, variable terms, nonzero numerator effects, and invalid rounding. Both languages consume the same expectations.

Boundaries that should remain visible

This canonical calculator is not the right method for:

  • performance-contingent awards before the contingency is resolved;
  • penny warrants already counted in Basic EPS;
  • liability-classified warrants whose assumed exercise changes income;
  • cashless, net-share, tenderable, or variable-settlement terms;
  • options to acquire convertible instruments;
  • US GAAP multi-quarter aggregation.

Those are related algorithms or upstream accounting decisions. Rejecting them is safer than returning a precise-looking but indefensible number.

Summary

The US-GAAP treasury stock method and IAS 33 assumed-proceeds treatment count the positive shares issued for no consideration, not gross contract shares. Weight the gross shares while the instrument is outstanding, use complete assumed proceeds, repurchase at a representative average market price, cap out-of-money effects at zero, and apply the exact continuing-operations EPS control.

The next tutorial is Contingently Issuable Share Inclusion, where the central question changes from price economics to whether specified issuance conditions are satisfied.

Treasury-share method decision flow

Rendering system map…

The flow keeps method eligibility, money-ness, and continuing-operations control as three separate decisions. A positive incremental share count does not override a loss-period antidilution result.

References3 primary sources and evidence notes

Expand the source trail, evidence role, and limitations behind the engineering choices.

R01 - IAS 33 Earnings per Share

  • Organization: IFRS Foundation / International Accounting Standards Board
  • Source type: issued accounting standard
  • Version: 2024 issued standards HTML
  • URL: https://www.ifrs.org/content/dam/ifrs/publications/html-standards/english/2024/issued/ias33.html
  • Accessed: 2026-07-23
  • Jurisdiction: IFRS
  • Supports: paragraphs 36-39 for control, ordering and timing; paragraphs 45-48 for the options-and-warrants assumed-proceeds treatment; A4-A5 for representative average prices.
  • Limitations: local endorsement and effective-date requirements remain entity-specific.

R02 - FASB ASU 2020-06, Debt with Conversion and Other Options

  • Organization: Financial Accounting Standards Board
  • Source type: official Accounting Standards Update showing Topic 260 text
  • Publication date: August 2020
  • URL: https://storage.fasb.org/ASU%202020-06.pdf
  • Accessed: 2026-07-23
  • Jurisdiction: United States
  • Supports: ASC 260-10-45-22 through 45-23 treasury stock method; ASC 260-10-55-3 quarterly/year-to-date aggregation; ASC 260-10-55-4 through 55-5 meaningful average market price.
  • Limitations: the FASB Codification is the authoritative source; the ASU is used here as an official, publicly accessible record of the amended text.

R03 - FASB ASU 2016-09, Improvements to Employee Share-Based Payment Accounting

  • Organization: Financial Accounting Standards Board
  • Source type: official Accounting Standards Update
  • Publication date: March 2016
  • URL: https://storage.fasb.org/ASU%202016-09.pdf
  • Accessed: 2026-07-23
  • Jurisdiction: United States
  • Supports: removal of excess tax benefits from treasury-stock-method assumed proceeds when computing EPS.
  • Limitations: does not replace the rest of Topic 260 or Topic 718.

Research reconciliation

US GAAP names the treasury stock method. IAS 33 describes economically similar assumed issue / assumed-proceeds mechanics without using that US label. Both require a representative period-average market price and time weighting while instruments are outstanding. The proceeds components and multi-period application are framework controls: IAS 33 paragraph 47A uses IFRS 2 future goods/services fair value; US GAAP uses unrecognized future-service compensation but, after ASU 2016-09, not excess tax benefits; US-GAAP year-to-date shares are aggregated from quarterly incremental-share computations.

Historical evidence status

A named issuer case is deferred. A pooled diluted-EPS share increment cannot be used because it may combine contingently issuable awards with options or warrants. The evidence gate requires one identifiable fixed-share, non-contingent option or warrant series with the exercise terms, framework-specific proceeds, average-price period, timing events, share units, unchanged terms, and resulting incremental shares traceable to primary filings. Missing inputs will not be inferred from a provider display or aggregate EPS reconciliation.

treasuryShare.ts
/** Exact US-GAAP treasury-stock / IAS 33 assumed-proceeds calculation. */

export type AccountingFramework = "IFRS" | "US_GAAP" | "OTHER_DECLARED";
export type RoundingMode = "half_even" | "half_up";

export interface TreasuryShareInput {
  entity_id: string;
  instrument_id: string;
  instrument_type: "OPTION" | "WARRANT";
  period_start: string;
  period_end: string;
  as_of: string;
  accounting_framework: AccountingFramework;
  period_application:
    | "IFRS_DIRECT_PERIOD"
    | "US_GAAP_SINGLE_QUARTER"
    | "OTHER_DECLARED_DIRECT_PERIOD";
  currency: string;
  basic_control_numerator: string;
  basic_weighted_average_shares: string;
  earnings_scale: string;
  share_scale: string;
  ordinary_shares_under_instrument: string;
  cash_exercise_price_per_share: string;
  service_proceeds_per_share: string;
  service_proceeds_basis:
    | "NONE"
    | "IFRS2_FUTURE_GOODS_SERVICES_FAIR_VALUE"
    | "US_GAAP_AVERAGE_UNRECOGNIZED_COMPENSATION"
    | "OTHER_DECLARED";
  tax_proceeds_per_share: string;
  other_assumed_proceeds_per_share: string;
  average_market_price_per_share: string;
  average_market_price_period_start: string;
  average_market_price_period_end: string;
  average_market_price_policy: string;
  average_market_price_source: string;
  assumed_proceeds_policy: string;
  instrument_terms_source: string;
  terms_event_policy: string;
  issue_date: string;
  actual_exercise_date: string | null;
  lapse_or_cancellation_date: string | null;
  numerator_policy: string;
  denominator_policy: string;
  day_count_policy: "ACTUAL_DAYS_IN_PERIOD";
  basic_components_complete: boolean;
  share_basis_restatement_complete: boolean;
  average_market_price_complete: boolean;
  assumed_proceeds_complete: boolean;
  instrument_terms_complete: boolean;
  terms_restatement_complete: boolean;
  fixed_share_count: boolean;
  fixed_cash_exercise_price: boolean;
  holder_advantageous_terms_selected: boolean;
  numerator_effect_is_zero: boolean;
  terms_unchanged_during_exposure: boolean;
  shares_are_not_contingently_issuable: boolean;
  instrument_series_count: number;
  instrument_is_final: boolean;
  is_final: boolean;
  eps_decimal_places: number;
  share_decimal_places: number;
  rounding_mode: RoundingMode;
}

export interface TreasuryShareResult {
  metric: "treasury_share_method_options_warrants";
  entity_id: string;
  instrument_id: string;
  instrument_type: "OPTION" | "WARRANT";
  period_start: string;
  period_end: string;
  as_of: string;
  accounting_framework: AccountingFramework;
  period_application: string;
  currency: string;
  basic_control_numerator_base: string;
  basic_weighted_average_shares_base: string;
  shares_under_instrument_base: string;
  cash_exercise_price_per_share: string;
  service_proceeds_per_share: string;
  service_proceeds_basis: string;
  tax_proceeds_per_share: string;
  other_assumed_proceeds_per_share: string;
  effective_exercise_price_per_share: string;
  average_market_price_per_share: string;
  average_market_price_period_start: string;
  average_market_price_period_end: string;
  average_market_price_policy: string;
  average_market_price_source: string;
  assumed_proceeds_policy: string;
  instrument_terms_source: string;
  terms_event_policy: string;
  exposure_start: string;
  exposure_end_exclusive: string;
  exposure_days: number;
  period_days: number;
  time_weight_exact: string;
  gross_weighted_potential_shares_exact: string;
  gross_weighted_potential_shares: string;
  assumed_repurchase_shares_exact: string;
  assumed_repurchase_shares: string;
  incremental_shares_exact: string;
  incremental_shares: string;
  incremental_fraction_exact: string;
  in_the_money: boolean;
  basic_eps_exact: string;
  basic_eps: string;
  candidate_eps_exact: string;
  candidate_eps: string;
  classification: "dilutive" | "antidilutive";
  include_in_standalone_candidate: boolean;
  reason_code:
    | "candidate_below_control"
    | "candidate_not_below_control"
    | "not_in_the_money";
  eps_decimal_places: number;
  share_decimal_places: number;
  rounding_mode: RoundingMode;
  rounding_adjusted: boolean;
  status: "final" | "provisional";
  is_final: boolean;
}

interface Rational { n: bigint; d: bigint }

const DECIMAL_RE = /^-?(?:0|[1-9]\d*)(?:\.\d+)?$/;
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
const RFC3339_RE =
  /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;
const REQUIRED_FIELDS: (keyof TreasuryShareInput)[] = [
  "entity_id", "instrument_id", "instrument_type", "period_start", "period_end",
  "as_of", "accounting_framework", "period_application", "currency", "basic_control_numerator",
  "basic_weighted_average_shares", "earnings_scale", "share_scale",
  "ordinary_shares_under_instrument", "cash_exercise_price_per_share",
  "service_proceeds_per_share", "service_proceeds_basis", "tax_proceeds_per_share",
  "other_assumed_proceeds_per_share", "average_market_price_per_share",
  "average_market_price_period_start", "average_market_price_period_end",
  "issue_date", "actual_exercise_date", "lapse_or_cancellation_date",
  "numerator_policy", "denominator_policy", "average_market_price_policy",
  "average_market_price_source", "assumed_proceeds_policy",
  "instrument_terms_source", "day_count_policy", "basic_components_complete",
  "share_basis_restatement_complete", "average_market_price_complete",
  "assumed_proceeds_complete", "instrument_terms_complete",
  "terms_restatement_complete", "fixed_share_count",
  "fixed_cash_exercise_price", "holder_advantageous_terms_selected",
  "numerator_effect_is_zero", "terms_unchanged_during_exposure",
  "shares_are_not_contingently_issuable", "instrument_series_count",
  "terms_event_policy", "instrument_is_final", "is_final",
  "eps_decimal_places", "share_decimal_places", "rounding_mode",
];

export class TreasuryShareValidationError extends Error {
  constructor(message: string) {
    super(message);
    this.name = "TreasuryShareValidationError";
  }
}

function abs(value: bigint): bigint { return value < 0n ? -value : value; }
function gcd(a: bigint, b: bigint): bigint {
  a = abs(a); b = abs(b);
  while (b !== 0n) [a, b] = [b, a % b];
  return a;
}
function rat(n: bigint, d = 1n): Rational {
  if (d === 0n) throw new TreasuryShareValidationError("division by zero");
  if (d < 0n) { n = -n; d = -d; }
  const g = gcd(n, d);
  return {n: n / g, d: d / g};
}
function add(a: Rational, b: Rational): Rational {
  return rat(a.n * b.d + b.n * a.d, a.d * b.d);
}
function sub(a: Rational, b: Rational): Rational {
  return rat(a.n * b.d - b.n * a.d, a.d * b.d);
}
function mul(a: Rational, b: Rational): Rational {
  return rat(a.n * b.n, a.d * b.d);
}
function div(a: Rational, b: Rational): Rational {
  return rat(a.n * b.d, a.d * b.n);
}
function less(a: Rational, b: Rational): boolean {
  return a.n * b.d < b.n * a.d;
}
function greater(a: Rational, b: Rational): boolean {
  return a.n * b.d > b.n * a.d;
}
function fraction(value: Rational): string { return `${value.n}/${value.d}`; }
function decimal(value: unknown, field: string): Rational {
  if (typeof value !== "string" || !DECIMAL_RE.test(value)) {
    throw new TreasuryShareValidationError(
      `${field} must be a canonical decimal string without exponent notation`,
    );
  }
  const unsigned = value.startsWith("-") ? value.slice(1) : value;
  const [whole, frac = ""] = unsigned.split(".");
  if (whole.length + frac.length > 30 || frac.length > 12) {
    throw new TreasuryShareValidationError(
      `${field} exceeds 30 digits or 12 fraction digits`,
    );
  }
  return rat(BigInt(value.replace(".", "")), 10n ** BigInt(frac.length));
}
function normalDecimal(value: Rational): string {
  let denominator = value.d;
  let twos = 0, fives = 0;
  while (denominator % 2n === 0n) { denominator /= 2n; twos++; }
  while (denominator % 5n === 0n) { denominator /= 5n; fives++; }
  if (denominator !== 1n) return fraction(value);
  const places = Math.max(twos, fives);
  let scaled = value.n * 2n ** BigInt(places - twos) * 5n ** BigInt(places - fives);
  const negative = scaled < 0n;
  if (negative) scaled = -scaled;
  let digits = scaled.toString().padStart(places + 1, "0");
  if (places) {
    const split = digits.length - places;
    digits = `${digits.slice(0, split)}.${digits.slice(split)}`
      .replace(/0+$/, "").replace(/\.$/, "");
  }
  return negative && digits !== "0" ? `-${digits}` : digits;
}
function round(value: Rational, places: number, mode: RoundingMode): [string, boolean] {
  const negative = value.n < 0n;
  const scaled = abs(value.n) * 10n ** BigInt(places);
  let quotient = scaled / value.d;
  const remainder = scaled % value.d;
  const comparison = remainder * 2n - value.d;
  if (comparison > 0n ||
      (comparison === 0n &&
       (mode === "half_up" || (mode === "half_even" && quotient % 2n === 1n)))) {
    quotient++;
  }
  let digits = quotient.toString().padStart(places + 1, "0");
  if (places) {
    const split = digits.length - places;
    digits = `${digits.slice(0, split)}.${digits.slice(split)}`;
  }
  if (negative && quotient !== 0n) digits = `-${digits}`;
  return [digits, remainder !== 0n];
}
function text(value: unknown, field: string): string {
  if (typeof value !== "string" || value.trim() === "") {
    throw new TreasuryShareValidationError(`${field} must be non-empty text`);
  }
  return value;
}
function validDate(value: unknown, field: string, nullable = false): string | null {
  if (nullable && value === null) return null;
  if (typeof value !== "string" || !DATE_RE.test(value)) {
    throw new TreasuryShareValidationError(
      `${field} must use YYYY-MM-DD${nullable ? " or null" : ""}`,
    );
  }
  const parsed = new Date(`${value}T00:00:00Z`);
  if (Number.isNaN(parsed.valueOf()) || parsed.toISOString().slice(0, 10) !== value) {
    throw new TreasuryShareValidationError(`${field} must be a real date`);
  }
  return value;
}
function dayNumber(value: string): number {
  return Math.floor(Date.parse(`${value}T00:00:00Z`) / 86400000);
}
function dateFromDay(value: number): string {
  return new Date(value * 86400000).toISOString().slice(0, 10);
}

export function calculateTreasuryShareMethod(
  input: TreasuryShareInput,
): TreasuryShareResult {
  if (input === null || typeof input !== "object" || Array.isArray(input)) {
    throw new TreasuryShareValidationError("input must be an object");
  }
  for (const field of REQUIRED_FIELDS) {
    if (!(field in input)) {
      throw new TreasuryShareValidationError(`missing required field: ${field}`);
    }
  }
  const entityId = text(input.entity_id, "entity_id");
  const instrumentId = text(input.instrument_id, "instrument_id");
  if (!["OPTION", "WARRANT"].includes(input.instrument_type)) {
    throw new TreasuryShareValidationError("instrument_type must be OPTION or WARRANT");
  }
  const start = validDate(input.period_start, "period_start")!;
  const end = validDate(input.period_end, "period_end")!;
  const issue = validDate(input.issue_date, "issue_date")!;
  const exercise = validDate(input.actual_exercise_date, "actual_exercise_date", true);
  const lapse = validDate(
    input.lapse_or_cancellation_date, "lapse_or_cancellation_date", true,
  );
  const priceStart = validDate(
    input.average_market_price_period_start, "average_market_price_period_start",
  )!;
  const priceEnd = validDate(
    input.average_market_price_period_end, "average_market_price_period_end",
  )!;
  if (start > end) {
    throw new TreasuryShareValidationError("period_start must not be after period_end");
  }
  if (issue > end) {
    throw new TreasuryShareValidationError("issue_date must not be after period_end");
  }
  for (const [event, field] of [
    [exercise, "actual_exercise_date"], [lapse, "lapse_or_cancellation_date"],
  ] as const) {
    if (event !== null && event < issue) {
      throw new TreasuryShareValidationError(`${field} must not be before issue_date`);
    }
  }
  if (!RFC3339_RE.test(input.as_of) || Number.isNaN(Date.parse(input.as_of))) {
    throw new TreasuryShareValidationError(
      "as_of must be a real RFC 3339 timestamp with an explicit offset",
    );
  }
  if (!["IFRS", "US_GAAP", "OTHER_DECLARED"].includes(input.accounting_framework)) {
    throw new TreasuryShareValidationError("accounting_framework is not supported");
  }
  const permittedApplication = {
    IFRS: "IFRS_DIRECT_PERIOD",
    US_GAAP: "US_GAAP_SINGLE_QUARTER",
    OTHER_DECLARED: "OTHER_DECLARED_DIRECT_PERIOD",
  }[input.accounting_framework];
  if (input.period_application !== permittedApplication) {
    throw new TreasuryShareValidationError(
      `period_application must be ${permittedApplication} for the selected framework`,
    );
  }
  if (!/^[A-Z]{3}$/.test(input.currency)) {
    throw new TreasuryShareValidationError(
      "currency must be a three-letter uppercase code",
    );
  }
  for (const field of [
    "numerator_policy", "denominator_policy", "average_market_price_policy",
    "average_market_price_source", "assumed_proceeds_policy",
    "instrument_terms_source", "terms_event_policy",
  ] as const) text(input[field], field);
  if (input.day_count_policy !== "ACTUAL_DAYS_IN_PERIOD") {
    throw new TreasuryShareValidationError(
      "day_count_policy must be ACTUAL_DAYS_IN_PERIOD",
    );
  }
  const trueFlags = [
    "basic_components_complete", "share_basis_restatement_complete",
    "average_market_price_complete", "assumed_proceeds_complete",
    "instrument_terms_complete", "terms_restatement_complete",
    "fixed_share_count", "fixed_cash_exercise_price",
    "holder_advantageous_terms_selected", "numerator_effect_is_zero",
    "terms_unchanged_during_exposure", "shares_are_not_contingently_issuable",
  ] as const;
  for (const field of [...trueFlags, "instrument_is_final", "is_final"] as const) {
    if (typeof input[field] !== "boolean") {
      throw new TreasuryShareValidationError(`${field} must be boolean`);
    }
  }
  for (const field of trueFlags) {
    if (!input[field]) throw new TreasuryShareValidationError(`${field} must be true`);
  }
  if (!Number.isInteger(input.instrument_series_count) || input.instrument_series_count !== 1) {
    throw new TreasuryShareValidationError(
      "instrument_series_count must be 1; calculate each series separately",
    );
  }
  if (!Number.isInteger(input.eps_decimal_places) ||
      input.eps_decimal_places < 0 || input.eps_decimal_places > 12) {
    throw new TreasuryShareValidationError(
      "eps_decimal_places must be an integer from 0 to 12",
    );
  }
  if (!Number.isInteger(input.share_decimal_places) ||
      input.share_decimal_places < 0 || input.share_decimal_places > 12) {
    throw new TreasuryShareValidationError(
      "share_decimal_places must be an integer from 0 to 12",
    );
  }
  if (!["half_even", "half_up"].includes(input.rounding_mode)) {
    throw new TreasuryShareValidationError("rounding_mode must be half_even or half_up");
  }

  const basicReported = decimal(input.basic_control_numerator, "basic_control_numerator");
  const basicSharesReported = decimal(
    input.basic_weighted_average_shares, "basic_weighted_average_shares",
  );
  const earningsScale = decimal(input.earnings_scale, "earnings_scale");
  const shareScale = decimal(input.share_scale, "share_scale");
  const sharesReported = decimal(
    input.ordinary_shares_under_instrument, "ordinary_shares_under_instrument",
  );
  const cashPrice = decimal(
    input.cash_exercise_price_per_share, "cash_exercise_price_per_share",
  );
  const serviceProceeds = decimal(
    input.service_proceeds_per_share, "service_proceeds_per_share",
  );
  const taxProceeds = decimal(input.tax_proceeds_per_share, "tax_proceeds_per_share");
  const otherProceeds = decimal(
    input.other_assumed_proceeds_per_share, "other_assumed_proceeds_per_share",
  );
  const marketPrice = decimal(
    input.average_market_price_per_share, "average_market_price_per_share",
  );
  if (basicSharesReported.n <= 0n || earningsScale.n <= 0n || shareScale.n <= 0n) {
    throw new TreasuryShareValidationError(
      "basic shares and both unit scales must be positive",
    );
  }
  if (sharesReported.n <= 0n) {
    throw new TreasuryShareValidationError(
      "ordinary_shares_under_instrument must be positive",
    );
  }
  if (cashPrice.n < 0n || serviceProceeds.n < 0n ||
      taxProceeds.n < 0n || otherProceeds.n < 0n) {
    throw new TreasuryShareValidationError(
      "all assumed-proceeds components must be non-negative",
    );
  }
  if (marketPrice.n <= 0n) {
    throw new TreasuryShareValidationError(
      "average_market_price_per_share must be positive",
    );
  }

  const startDay = dayNumber(start);
  const periodEndExclusiveDay = dayNumber(end) + 1;
  const exposureStartDay = Math.max(startDay, dayNumber(issue));
  const endCandidates = [periodEndExclusiveDay];
  if (exercise !== null) endCandidates.push(dayNumber(exercise));
  if (lapse !== null) endCandidates.push(dayNumber(lapse));
  const exposureEndDay = Math.min(...endCandidates);
  const exposureDays = exposureEndDay - exposureStartDay;
  const periodDays = periodEndExclusiveDay - startDay;
  if (exposureDays <= 0) {
    throw new TreasuryShareValidationError(
      "instrument has no potential-share exposure in the period",
    );
  }
  if (priceStart !== dateFromDay(exposureStartDay) ||
      priceEnd !== dateFromDay(exposureEndDay - 1)) {
    throw new TreasuryShareValidationError(
      "average market price period must match the inclusive potential-share exposure period",
    );
  }
  const permittedBases: Record<AccountingFramework, Set<string>> = {
    IFRS: new Set(["NONE", "IFRS2_FUTURE_GOODS_SERVICES_FAIR_VALUE"]),
    US_GAAP: new Set(["NONE", "US_GAAP_AVERAGE_UNRECOGNIZED_COMPENSATION"]),
    OTHER_DECLARED: new Set(["NONE", "OTHER_DECLARED"]),
  };
  if (!permittedBases[input.accounting_framework].has(input.service_proceeds_basis)) {
    throw new TreasuryShareValidationError(
      "service_proceeds_basis is incompatible with accounting_framework",
    );
  }
  if ((serviceProceeds.n === 0n) !== (input.service_proceeds_basis === "NONE")) {
    throw new TreasuryShareValidationError(
      "service proceeds and service_proceeds_basis must agree",
    );
  }
  if ((input.accounting_framework === "IFRS" || input.accounting_framework === "US_GAAP") &&
      (taxProceeds.n !== 0n || otherProceeds.n !== 0n)) {
    throw new TreasuryShareValidationError(
      "IFRS and current US GAAP canonical cases require zero tax and declared-other proceeds",
    );
  }

  const basicNumerator = mul(basicReported, earningsScale);
  const basicShares = mul(basicSharesReported, shareScale);
  const sharesUnderInstrument = mul(sharesReported, shareScale);
  const weight = rat(BigInt(exposureDays), BigInt(periodDays));
  const grossWeighted = mul(sharesUnderInstrument, weight);
  const effectivePrice = add(add(add(cashPrice, serviceProceeds), taxProceeds), otherProceeds);
  const inTheMoney = greater(marketPrice, effectivePrice);
  const assumedRepurchase = inTheMoney
    ? div(mul(grossWeighted, effectivePrice), marketPrice)
    : rat(0n);
  const incremental = inTheMoney ? sub(grossWeighted, assumedRepurchase) : rat(0n);
  const incrementalFraction = div(incremental, grossWeighted);
  const basicEps = div(basicNumerator, basicShares);
  const candidateEps = div(basicNumerator, add(basicShares, incremental));
  const dilutive = inTheMoney && less(candidateEps, basicEps);
  const reasonCode = !inTheMoney
    ? "not_in_the_money"
    : dilutive ? "candidate_below_control" : "candidate_not_below_control";
  const [basicDisplay, basicAdjusted] = round(
    basicEps, input.eps_decimal_places, input.rounding_mode,
  );
  const [candidateDisplay, candidateAdjusted] = round(
    candidateEps, input.eps_decimal_places, input.rounding_mode,
  );
  const [grossDisplay, grossAdjusted] = round(
    grossWeighted, input.share_decimal_places, input.rounding_mode,
  );
  const [repurchaseDisplay, repurchaseAdjusted] = round(
    assumedRepurchase, input.share_decimal_places, input.rounding_mode,
  );
  const [incrementalDisplay, incrementalAdjusted] = round(
    incremental, input.share_decimal_places, input.rounding_mode,
  );
  const isFinal = input.is_final && input.instrument_is_final;

  return {
    metric: "treasury_share_method_options_warrants",
    entity_id: entityId,
    instrument_id: instrumentId,
    instrument_type: input.instrument_type,
    period_start: start,
    period_end: end,
    as_of: input.as_of,
    accounting_framework: input.accounting_framework,
    period_application: input.period_application,
    currency: input.currency,
    basic_control_numerator_base: normalDecimal(basicNumerator),
    basic_weighted_average_shares_base: normalDecimal(basicShares),
    shares_under_instrument_base: normalDecimal(sharesUnderInstrument),
    cash_exercise_price_per_share: normalDecimal(cashPrice),
    service_proceeds_per_share: normalDecimal(serviceProceeds),
    service_proceeds_basis: input.service_proceeds_basis,
    tax_proceeds_per_share: normalDecimal(taxProceeds),
    other_assumed_proceeds_per_share: normalDecimal(otherProceeds),
    effective_exercise_price_per_share: normalDecimal(effectivePrice),
    average_market_price_per_share: normalDecimal(marketPrice),
    average_market_price_period_start: input.average_market_price_period_start,
    average_market_price_period_end: input.average_market_price_period_end,
    average_market_price_policy: input.average_market_price_policy,
    average_market_price_source: input.average_market_price_source,
    assumed_proceeds_policy: input.assumed_proceeds_policy,
    instrument_terms_source: input.instrument_terms_source,
    terms_event_policy: input.terms_event_policy,
    exposure_start: dateFromDay(exposureStartDay),
    exposure_end_exclusive: dateFromDay(exposureEndDay),
    exposure_days: exposureDays,
    period_days: periodDays,
    time_weight_exact: fraction(weight),
    gross_weighted_potential_shares_exact: fraction(grossWeighted),
    gross_weighted_potential_shares: grossDisplay,
    assumed_repurchase_shares_exact: fraction(assumedRepurchase),
    assumed_repurchase_shares: repurchaseDisplay,
    incremental_shares_exact: fraction(incremental),
    incremental_shares: incrementalDisplay,
    incremental_fraction_exact: fraction(incrementalFraction),
    in_the_money: inTheMoney,
    basic_eps_exact: fraction(basicEps),
    basic_eps: basicDisplay,
    candidate_eps_exact: fraction(candidateEps),
    candidate_eps: candidateDisplay,
    classification: dilutive ? "dilutive" : "antidilutive",
    include_in_standalone_candidate: dilutive,
    reason_code: reasonCode,
    eps_decimal_places: input.eps_decimal_places,
    share_decimal_places: input.share_decimal_places,
    rounding_mode: input.rounding_mode,
    rounding_adjusted:
      basicAdjusted || candidateAdjusted || grossAdjusted ||
      repurchaseAdjusted || incrementalAdjusted,
    status: isFinal ? "final" : "provisional",
    is_final: isFinal,
  };
}
Full-height labtreasury share labOpen full screen