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

DCA ladder planning and strategy management.

Builds defensive and aggressive DCA presets, calculates reserve-funded ladder steps,
and enhances those steps with portfolio metrics.

## API Functions
| Function | Arity | Description | Param Kinds |
| --- | --- | --- | --- |
| `enhance_dca_steps` | 5 | Enhance DCA steps with leverage-to-AUM and black swan safety metrics. | `steps: value`, `aum: value`, `black_swan_pct: value`, `entry_price: value`, `side: value` |
| `build_aggressive_preset` | 3 | Build aggressive DCA preset from user configuration or defaults. | `params: value`, `entry_price: value`, `side: value` |
| `build_defensive_preset` | 3 | Build defensive DCA preset from user configuration or defaults. | `params: value`, `entry_price: value`, `side: value` |
| `calculate_dca_ladder` | 1 | Calculate defensive and aggressive DCA ladder results when reserve is available. | `dca_params: value` |
| `convert_ladder_for_short` | 1 | Convert a long DCA ladder preset to a short preset. | `long_preset: value` |
| `dca_ladder` | 8 | Calculate DCA ladder steps using reserve allocation. | `position: value`, `reserve: value`, `entry_price: value`, `ui_lev: value`, `ladder_preset: value`, `side: value`, `mmr_rate: value`, `opts: value` |

# `dca_params`

```elixir
@type dca_params() :: %{
  params: map(),
  position_with_tokens: map(),
  dca_reserve: Decimal.t(),
  entry_price: Decimal.t(),
  ui_leverage: Decimal.t(),
  side: :long | :short,
  mmr_rate: Decimal.t(),
  mark_buffer: Decimal.t(),
  aum: Decimal.t(),
  black_swan_pct: Decimal.t()
}
```

# `dca_preset`

```elixir
@type dca_preset() :: [{Decimal.t(), Decimal.t()}]
```

# `dca_result`

```elixir
@type dca_result() :: %{optional(:defensive) =&gt; map(), optional(:aggressive) =&gt; map()}
```

# `dca_step`

```elixir
@type dca_step() :: map()
```

# `mmr_schedule`

```elixir
@type mmr_schedule() :: [{DeltaCalc.Decimal.input(), DeltaCalc.Decimal.input()}]
```

# `build_aggressive_preset`

```elixir
@spec build_aggressive_preset(map(), Decimal.t(), :long | :short) :: dca_preset()
```

Build aggressive DCA preset from user configuration or defaults.

Aggressive DCA goes with the current position direction:
- For longs: buy at higher prices (momentum trading)
- For shorts: buy at lower prices (momentum trading)

## Parameters
- `params`: Parameters map containing DCA price and allocation configuration
- `entry_price`: Entry price for calculating price multipliers (Decimal)
- `side`: Position side (:long or :short)

## Returns
List of {price_multiplier, allocation_decimal} tuples.

## Examples

    params = %{
      aggressive_prices: [Decimal.new("3150"), Decimal.new("3300")],
      dca_allocations: [Decimal.new("40"), Decimal.new("30")]
    }

    build_aggressive_preset(params, Decimal.new("3000"), :long)
    #=> [{Decimal.new("1.05"), Decimal.new("0.40")}, {Decimal.new("1.10"), Decimal.new("0.30")}]

# `build_defensive_preset`

```elixir
@spec build_defensive_preset(map(), Decimal.t(), :long | :short) :: dca_preset()
```

Build defensive DCA preset from user configuration or defaults.

Defensive DCA goes against the current position direction:
- For longs: buy at lower prices (averaging down)
- For shorts: buy at higher prices (averaging up)

## Parameters
- `params`: Parameters map containing DCA price and allocation configuration
- `entry_price`: Entry price for calculating price multipliers (Decimal)
- `side`: Position side (:long or :short)

## Returns
List of {price_multiplier, allocation_decimal} tuples.

## Examples

    params = %{
      defensive_prices: [Decimal.new("2850"), Decimal.new("2700")],
      dca_allocations: [Decimal.new("40"), Decimal.new("30")]
    }

    build_defensive_preset(params, Decimal.new("3000"), :long)
    #=> [{Decimal.new("0.95"), Decimal.new("0.40")}, {Decimal.new("0.90"), Decimal.new("0.30")}]

# `calculate_dca_ladder`

```elixir
@spec calculate_dca_ladder(dca_params()) :: dca_result() | nil
```

Calculates DCA ladder results if reserve is available and DCA is enabled.

Builds both defensive and aggressive DCA strategies and calculates complete
ladder results with enhanced step information including safety metrics.

## Parameters
- `dca_params`: Map or struct containing all DCA parameters:
  - `params`: Validated parameters map containing DCA configuration
  - `position_with_tokens`: Position map with tokens calculation
  - `dca_reserve`: Available DCA reserve amount (Decimal)
  - `entry_price`: Entry price for the position (Decimal)
  - `ui_leverage`: UI leverage setting (Decimal)
  - `side`: Position side (:long or :short)
  - `mmr_rate`: Minimum margin requirement rate (Decimal)
  - `mark_buffer`: Buffer added to the MMR used for liquidation calculations (Decimal)
  - `aum`: Total Assets Under Management (Decimal)
  - `black_swan_pct`: Black swan threshold as decimal (0-1)

## Returns
Map with DCA ladder results, or `nil` if no DCA available:
- `:defensive` - Defensive DCA strategy results (if available)
- `:aggressive` - Aggressive DCA strategy results (if available)

Each strategy contains:
- `:steps` - List of enhanced DCA steps with safety metrics
- Other fields from `dca_ladder/8` result

## Examples

    dca_params = %{
      params: %{
        dca_enabled: true,
        defensive_prices: [Decimal.new("2850"), Decimal.new("2700")],
        dca_allocations: [Decimal.new("30"), Decimal.new("30")]
      },
      position_with_tokens: position,
      dca_reserve: reserve,
      entry_price: entry,
      ui_leverage: leverage,
      side: :long,
      mmr_rate: mmr,
      mark_buffer: buffer,
      aum: aum,
      black_swan_pct: swan_pct
    }

    calculate_dca_ladder(dca_params)
    #=> %{
    #     defensive: %{steps: [...], final_avg_entry: ...},
    #     aggressive: %{steps: [...], final_avg_entry: ...}
    #   }

# `convert_ladder_for_short`

```elixir
@spec convert_ladder_for_short(list()) :: list()
```

Convert a long DCA ladder preset to a short preset.

## Parameters

  * `long_preset` - List of \{price_mult, reserve_pct\} pairs whose exact values use canonical decimal strings; native Elixir callers may also pass Decimal or integer. (value)

## Returns

List of \{price_mult, reserve_pct\} tuples for shorts (`list`)

```elixir
# descripex:contract
%{
  params: %{
    long_preset: %{
      description: "List of {price_mult, reserve_pct} pairs whose exact values use canonical decimal strings; native Elixir callers may also pass Decimal or integer.",
      kind: :value
    }
  },
  returns: %{
    type: :list,
    description: "List of {price_mult, reserve_pct} tuples for shorts"
  }
}
```

# `dca_ladder`

```elixir
@spec dca_ladder(
  map(),
  Decimal.t(),
  Decimal.t(),
  Decimal.t(),
  list(),
  :long | :short,
  Decimal.t(),
  keyword() | Decimal.t()
) :: map()
```

Calculate DCA ladder steps using reserve allocation.

## Parameters

  * `position` - Initial position with :notional and :eff_lev as canonical decimal strings; native Elixir callers may also pass Decimal or integer. (value)
  * `reserve` - Reserve available for DCA as a canonical decimal string; native Elixir callers may also pass Decimal or integer. (value)
  * `entry_price` - Initial entry price as a canonical decimal string; native Elixir callers may also pass Decimal or integer. (value)
  * `ui_lev` - UI leverage for new positions as a canonical decimal string; native Elixir callers may also pass Decimal or integer. (value)
  * `ladder_preset` - List of side-specific \{price_mult, reserve_pct\} pairs whose exact values use canonical decimal strings; native Elixir callers may also pass Decimal or integer; multipliers are used as supplied (value)
  * `side` - Position side (:long or :short) used for liquidation math (value)
  * `mmr_rate` - Minimum margin requirement rate as a canonical decimal string; native Elixir callers may also pass Decimal or integer. (value)
  * `opts` - Optional `:mark_buffer` added to the applicable MMR and `:mmr_schedule` list of \{minimum_notional, mmr_rate\} tiers; exact values use canonical decimal strings and native Elixir callers may also pass Decimal or integer; the highest applicable threshold wins (default: `[]`, value)

## Returns

Map with steps, final_avg_entry, final_notional, final_liq, final_eff_lev (`map`)

```elixir
# descripex:contract
%{
  params: %{
    position: %{
      description: "Initial position with :notional and :eff_lev as canonical decimal strings; native Elixir callers may also pass Decimal or integer.",
      kind: :value,
      schema: %{
        "additionalProperties" => false,
        "properties" => %{
          "eff_lev" => %{"type" => "string"},
          "notional" => %{"type" => "string"}
        },
        "required" => ["notional", "eff_lev"],
        "type" => "object"
      }
    },
    opts: %{
      default: [],
      description: "Optional `:mark_buffer` added to the applicable MMR and `:mmr_schedule` list of {minimum_notional, mmr_rate} tiers; exact values use canonical decimal strings and native Elixir callers may also pass Decimal or integer; the highest applicable threshold wins",
      kind: :value
    },
    side: %{
      description: "Position side (:long or :short) used for liquidation math",
      kind: :value,
      schema: %{"enum" => ["long", "short"], "type" => "string"}
    },
    reserve: %{
      description: "Reserve available for DCA as a canonical decimal string; native Elixir callers may also pass Decimal or integer.",
      kind: :value,
      schema: %{"type" => "string"}
    },
    ui_lev: %{
      description: "UI leverage for new positions as a canonical decimal string; native Elixir callers may also pass Decimal or integer.",
      kind: :value,
      schema: %{"type" => "string"}
    },
    entry_price: %{
      description: "Initial entry price as a canonical decimal string; native Elixir callers may also pass Decimal or integer.",
      kind: :value,
      schema: %{"type" => "string"}
    },
    ladder_preset: %{
      description: "List of side-specific {price_mult, reserve_pct} pairs whose exact values use canonical decimal strings; native Elixir callers may also pass Decimal or integer; multipliers are used as supplied",
      kind: :value
    },
    mmr_rate: %{
      description: "Minimum margin requirement rate as a canonical decimal string; native Elixir callers may also pass Decimal or integer.",
      kind: :value,
      schema: %{"type" => "string"}
    }
  },
  returns: %{
    type: :map,
    description: "Map with steps, final_avg_entry, final_notional, final_liq, final_eff_lev"
  }
}
```

# `enhance_dca_steps`

```elixir
@spec enhance_dca_steps(
  [dca_step()],
  Decimal.t(),
  Decimal.t(),
  Decimal.t(),
  :long | :short
) ::
  [dca_step()] | {:error, atom()}
```

Enhance DCA steps with additional risk and portfolio metrics.

Adds leverage-to-AUM ratios and black swan safety checks to each DCA step
for comprehensive risk assessment at each ladder level.

## Parameters
- `steps`: List of DCA steps from `dca_ladder/8`
- `aum`: Total Assets Under Management (Decimal)
- `black_swan_pct`: Black swan threshold as decimal (0-1)
- `entry_price`: Entry price (Decimal)
- `side`: Position side (:long or :short)

## Returns
Enhanced list of DCA steps with additional fields:
- `:leverage_to_aum` - Cumulative position size as percentage of total AUM
- `:passes_black_swan` - Whether this step's liquidation passes black swan test
- `:black_swan_price` - Black swan price level for reference

## Examples

    steps = [%{cumulative_notional: Decimal.new("1000"), new_liq: Decimal.new("2800"), ...}]

    enhance_dca_steps(steps, Decimal.new("50000"), Decimal.new("0.15"), Decimal.new("3000"), :long)
    #=> [%{..., leverage_to_aum: Decimal.new("0.02"), passes_black_swan: true, ...}]

---

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