# `DeltaCalc.OptionLadder`
[🔗](https://github.com/ZenHive/delta_calc/blob/v0.3.0/lib/delta_calc/option_ladder.ex#L1)

Pure rolling option ladder calculations for perp-funded option strategies.

This module keeps scheduling and execution outside DeltaCalc. Callers pass option
chain snapshots, position state, funding receipts, and strategy preferences as
plain values; the functions return deterministic decisions and Decimal amounts.

## API Functions
| Function | Arity | Description | Param Kinds |
| --- | --- | --- | --- |
| `iv_adjusted_size` | 2 | Adjust position size from IV percentile: increase below 40, reduce above 70, otherwise unchanged. | `base_size: value`, `opts: value` |
| `sync_with_funding` | 2 | Compare funding income with roll and spread costs to decide whether a roll can execute. | `roll: value`, `opts: value` |
| `select_strikes` | 2 | Build a strike ladder from spot price, IV percentile, risk profile, and option type. | `params: value`, `opts: value` |
| `check_roll_conditions` | 2 | Evaluate phase 7 rolling rules from days to expiry, PnL percent, momentum, and spread. | `position: value`, `market: value` |
| `optimal_expiries` | 2 | Select liquid expiries across front, middle, and back buckets and normalize allocations. | `expiries: value`, `opts: value` |

# `expiry`

```elixir
@type expiry() :: %{
  expiry: String.t(),
  days_to_expiry: pos_integer(),
  liquidity: DeltaCalc.Decimal.input(),
  bid_ask_spread: DeltaCalc.Decimal.input()
}
```

# `expiry_bucket`

```elixir
@type expiry_bucket() :: %{
  bucket: :front | :middle | :back,
  expiry: String.t(),
  days_to_expiry: pos_integer(),
  allocation: Decimal.t(),
  liquidity: Decimal.t(),
  bid_ask_spread: Decimal.t()
}
```

# `expiry_result`

```elixir
@type expiry_result() :: %{buckets: [expiry_bucket()], total_allocation: Decimal.t()}
```

# `funding_result`

```elixir
@type funding_result() :: %{
  funding_received: Decimal.t(),
  positions_to_roll: non_neg_integer(),
  roll_cost: Decimal.t(),
  spread_cost: Decimal.t(),
  total_cost: Decimal.t(),
  excess_funding: Decimal.t(),
  margin_used: Decimal.t(),
  status: :executed | :skipped | :deferred,
  reason: atom() | nil
}
```

# `roll_decision`

```elixir
@type roll_decision() ::
  %{action: :roll, target: :next_weekly}
  | %{action: :close_only, reason: String.t()}
  | %{action: :partial_roll, take_profit: Decimal.t(), roll_up: Decimal.t()}
  | %{action: :roll_to_atm}
  | %{action: :hold}
```

# `size_result`

```elixir
@type size_result() :: %{
  base_size: Decimal.t(),
  adjusted_size: Decimal.t(),
  multiplier: Decimal.t(),
  action: :increase_size | :normal_size | :reduce_size,
  reason: String.t()
}
```

# `strike_result`

```elixir
@type strike_result() :: %{
  risk_profile: atom(),
  option_type: :call | :put,
  spot_price: Decimal.t(),
  iv_adjustment: size_result(),
  strikes: [map()]
}
```

# `check_roll_conditions`

```elixir
@spec check_roll_conditions(map(), map()) :: roll_decision()
```

Return the roll action for a single option position.

# `iv_adjusted_size`

```elixir
@spec iv_adjusted_size(
  DeltaCalc.Decimal.input(),
  keyword()
) :: size_result()
```

Return the IV-adjusted position size and action.

# `optimal_expiries`

```elixir
@spec optimal_expiries(
  [expiry()],
  keyword()
) :: expiry_result()
```

Return selected expiry buckets with normalized allocation percentages.

# `select_strikes`

```elixir
@spec select_strikes(
  map(),
  keyword()
) :: strike_result()
```

Return an IV-aware strike ladder quantized to the caller's increment and rounding mode.

# `sync_with_funding`

```elixir
@spec sync_with_funding(
  map(),
  keyword()
) :: funding_result()
```

Return whether funding covers the roll, whether margin is used, or whether to skip/defer.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
