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 |
Summary
Functions
Build aggressive DCA preset from user configuration or defaults.
Build defensive DCA preset from user configuration or defaults.
Calculates DCA ladder results if reserve is available and DCA is enabled.
Convert a long DCA ladder preset to a short preset.
Calculate DCA ladder steps using reserve allocation.
Enhance DCA steps with additional risk and portfolio metrics.
Types
@type dca_step() :: map()
@type mmr_schedule() :: [{DeltaCalc.Decimal.input(), DeltaCalc.Decimal.input()}]
Functions
@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 configurationentry_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")}]
@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 configurationentry_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")}]
@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 configurationposition_with_tokens: Position map with tokens calculationdca_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/8result
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 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)
# 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"
}
}
@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_bufferadded to the applicable MMR and:mmr_schedulelist 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)
# 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"
}
}
@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 fromdca_ladder/8aum: 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, ...}]