-
Notifications
You must be signed in to change notification settings - Fork 5
Optino Formulae
BokkyPooBah edited this page May 16, 2020
·
10 revisions
Formulae for the calculation of the payoff and collateral
vanillaCallPayoff = max(spot - strike, 0)
cappedCallPayoff = max(min(spot, cap) - strike, 0)
= max(spot - strike, 0) - max(spot - cap, 0)
vanillaPutPayoff = max(strike - spot, 0)
flooredPutPayoff = max(strike - max(spot, floor), 0)
= max(strike - spot, 0) - max(floor - spot, 0)
-
optinoDecimals
- for Optino and Cover tokens, set to 18 -
decimals0
for token0 (or baseToken), e.g. 18 decimals for WETH in WETH/USDx -
decimals1
for token1 (or quoteToken), e.g. 6 decimals for USDx in WETH/USDx -
rateDecimals
for the rate feed. e.g. 18 for MakerDAO's feeds
-
strike
must be > 0 -
bound
must be 0 for vanilla calls or >strike
for capped calls
callPayoff = 0
if (spot > 0 && spot > strike) {
if (bound > strike && spot > bound) {
callPayoff = [(bound - strike) / spot] x [tokens / (10^optinoDecimals)] x (10^decimals0)
} else {
callPayoff = [(spot - strike) / spot] x [tokens / (10^optinoDecimals)] x (10^decimals0)
}
}
-
strike
must be > 0 -
bound
must be 0 for vanilla calls or >strike
for capped calls - Collateral is in the token0 (or baseToken)
if (bound <= strike) {
callCollateral = [tokens / (10^optinoDecimals)] x (10^decimals0)
} else {
callCollateral = [(bound - strike) / bound] x [tokens / (10^optinoDecimals)] x (10^decimals0)
}
-
strike
must be > 0 -
bound
must be 0 for vanilla puts or <strike
for floored puts
if (bound == 0 || (bound > 0 && spot >= bound)) {
putPayoff = [(strike - spot) / (10^rateDecimals)] x [tokens / (10^optinoDecimals)] x (10^decimals1)
} else {
putPayoff = [(strike - bound) / (10^rateDecimals)] x [tokens / (10^optinoDecimals)] x (10^decimals1)
}
-
strike
must be > 0 -
bound
must be 0 for vanilla puts or <strike
for floored puts - Collateral is in the token1 (or quoteToken)
putCollateral = [(strike - bound) / (10^rateDecimals)] x [tokens / (10^optinoDecimals)] x (10^decimals1)
- Using 256 bit unsigned integers
- Divisions are performed last to reduce loss of precision
-
computeCollateral(...)
calculates thecollateral
as the maximum payoff -
computePayoff(...)
calculates thepayoff
depending on the spot price, after expiry - Optino and Cover tokens can
close(...)
off against each other to release calculatedcollateral
in proportion to the tokens closed/netted - Optino token holders execute
settle()
after expiry to receive the calculatedpayoff
in proportion to the token holdings - Cover token holders execute
settle()
after expiry to receive the calculated(collateral - payoff)
in proportion to the token holdings
From https://github.com/bokkypoobah/Optino/blob/master/contracts/OptinoFactory.sol:
contract OptinoV1 {
using SafeMath for uint;
function shiftRightThenLeft(uint amount, uint8 right, uint8 left) internal pure returns (uint _result) {
if (right == left) {
return amount;
} else if (right > left) {
return amount.mul(10 ** uint(right - left));
} else {
return amount.div(10 ** uint(left - right));
}
}
function computeCollateral(uint[5] memory _seriesData, uint tokens, uint8[4] memory decimalsData) internal pure returns (uint _collateral) {
(uint callPut, uint strike, uint bound) = (_seriesData[uint(OptinoFactory.SeriesDataFields.CallPut)], _seriesData[uint(OptinoFactory.SeriesDataFields.Strike)], _seriesData[uint(OptinoFactory.SeriesDataFields.Bound)]);
(uint8 decimals, uint8 baseDecimals, uint8 quoteDecimals, uint8 rateDecimals) = (decimalsData[0], decimalsData[1], decimalsData[2], decimalsData[3]);
require(strike > 0, "strike must be > 0");
if (callPut == 0) {
require(bound == 0 || bound > strike, "Call bound must = 0 or > strike");
if (bound <= strike) {
return shiftRightThenLeft(tokens, baseDecimals, decimals);
} else {
return shiftRightThenLeft(bound.sub(strike).mul(tokens).div(bound), baseDecimals, decimals);
}
} else {
require(bound < strike, "Put bound must = 0 or < strike");
return shiftRightThenLeft(strike.sub(bound).mul(tokens), quoteDecimals, decimals).div(10 ** uint(rateDecimals));
}
}
function computePayoff(uint[5] memory _seriesData, uint spot, uint tokens, uint8[4] memory decimalsData) internal pure returns (uint _payoff) {
(uint callPut, uint strike, uint bound) = (_seriesData[uint(OptinoFactory.SeriesDataFields.CallPut)], _seriesData[uint(OptinoFactory.SeriesDataFields.Strike)], _seriesData[uint(OptinoFactory.SeriesDataFields.Bound)]);
return _computePayoff(callPut, strike, bound, spot, tokens, decimalsData);
}
function _computePayoff(uint callPut, uint strike, uint bound, uint spot, uint tokens, uint8[4] memory decimalsData) internal pure returns (uint _payoff) {
(uint8 decimals, uint8 baseDecimals, uint8 quoteDecimals, uint8 rateDecimals) = (decimalsData[0], decimalsData[1], decimalsData[2], decimalsData[3]);
if (callPut == 0) {
require(bound == 0 || bound > strike, "Call bound must = 0 or > strike");
if (spot > 0 && spot > strike) {
if (bound > strike && spot > bound) {
return shiftRightThenLeft(bound.sub(strike).mul(tokens), baseDecimals, decimals).div(spot);
} else {
return shiftRightThenLeft(spot.sub(strike).mul(tokens), baseDecimals, decimals).div(spot);
}
}
} else {
require(bound < strike, "Put bound must = 0 or < strike");
if (spot < strike) {
if (bound == 0 || (bound > 0 && spot >= bound)) {
return shiftRightThenLeft(strike.sub(spot).mul(tokens), quoteDecimals, decimals + rateDecimals);
} else {
return shiftRightThenLeft(strike.sub(bound).mul(tokens), quoteDecimals, decimals + rateDecimals);
}
}
}
}
}