redeemLiveProceeds function
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,
);
}