redeemLiveProceeds function

RedeemLiveProceeds redeemLiveProceeds(
  1. RedeemLiveInputs inputs
)

Net proceeds of closing a live position — expiry_market::redeem_live's payment decomposition, client-side. The mirror of mintCost: the same per-boundary trading fee and builder fee, the same congestion surcharge, and the inventory-impact term as a REBATE rather than a charge. There is no sponsor subsidy on a close (incentives subsidise mints only), and each deduction is clamped at the payout remaining after the ones before it, exactly as the contract clamps them, so a close can never cost more than it releases.

Use this for a local UI preview from a supplied snapshot. read.quoteRedeem simulates the actual close and remains the pre-trade check for ownership, remaining position size, live-market gates and current fees. RedeemLiveProceeds.proceeds is what min_proceeds is compared against on the real call; this preview does not guarantee execution at that amount.

Implementation

RedeemLiveProceeds redeemLiveProceeds(RedeemLiveInputs inputs) {
  _assertCostInputs(
      inputs.fees, inputs.book, inputs.penaltyRate, inputs.lotSize);
  final resolved = _resolveBoundaries(inputs.probabilities);
  final boundaries = resolved.boundaries;
  final lotSize = inputs.lotSize ?? POSITION_LOT_SIZE;
  final quantity = _rawAmount(inputs.closeQuantity);
  final ttl = _timeToExpiry(_rawMs(inputs.expiryMs),
      _rawMs(inputs.nowMs ?? DateTime.now().millisecondsSinceEpoch));
  _assertValidQuantity(quantity, lotSize);

  final positionQuantityIn = inputs.positionQuantity;
  final BigInt? positionQuantity =
      positionQuantityIn == null ? null : _rawAmount(positionQuantityIn);
  if (positionQuantity != null) {
    _assertValidQuantity(positionQuantity, lotSize);
    if (quantity > positionQuantity) {
      throw PredictInputError('closeQuantity exceeds positionQuantity');
    }
  }
  final remainingQuantity =
      positionQuantity == null ? null : positionQuantity - quantity;
  final probability = rangeProbabilityRaw(boundaries);
  final gross = _mulDown(probability, quantity);
  final fee = _min(tradingFee(inputs.fees, boundaries, quantity, ttl), gross);
  final builder =
      _min(builderFee(fee, quantity, inputs.builderCode), gross - fee);
  final penalty = _min(_mulDown(inputs.penaltyRate ?? BigInt.zero, quantity),
      gross - fee - builder);
  final book = inputs.book;
  final rebate = book != null
      ? closeInventoryImpact(inputs.fees, book, quantity)
      : BigInt.zero;
  final proceeds = gross + rebate - fee - builder - penalty;

  return RedeemLiveProceeds(
    proceeds: fromRaw(proceeds, 6),
    gross: fromRaw(gross, 6),
    fees: RedeemFees(
      trading: fromRaw(fee, 6),
      builder: fromRaw(builder, 6),
      penalty: fromRaw(penalty, 6),
      impactRebate: fromRaw(rebate, 6),
    ),
    quantityClosed: fromRaw(quantity, 6),
    remainingQuantity:
        remainingQuantity == null ? null : fromRaw(remainingQuantity, 6),
    proceedsPerContract: proceeds.toDouble() / quantity.toDouble(),
    probability: fromRaw(probability, 9),
    raw: RedeemLiveRaw(
      proceeds: proceeds,
      gross: gross,
      tradingFee: fee,
      builderFee: builder,
      penaltyFee: penalty,
      impactRebate: rebate,
      quantityClosed: quantity,
      remainingQuantity: remainingQuantity,
      probability: probability,
    ),
    exactProbabilities: resolved.exact,
  );
}