Skip to content

Commit

Permalink
fix: integrate call spread contract with Oracle API. (#1928)
Browse files Browse the repository at this point in the history
  • Loading branch information
Chris-Hibbert authored Oct 28, 2020
1 parent 3bc0b70 commit a0dd761
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 64 deletions.
21 changes: 15 additions & 6 deletions packages/zoe/src/contracts/callSpread.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ const start = zcf => {

const terms = zcf.getTerms();
const {
maths: { Collateral: collateralMath, Strike: strikeMath },
maths: { Collateral: collateralMath, Strike: strikeMath, Quote: quoteMath },
brands: { Strike: strikeBrand },
issuers: { Quote: quoteIssuer },
} = terms;
assertUsesNatMath(zcf, collateralMath.getBrand());
assertUsesNatMath(zcf, strikeMath.getBrand());
Expand Down Expand Up @@ -133,17 +135,24 @@ const start = zcf => {
return floorDivide(multiply(PERCENT_BASE, numerator), denominator);
}

function payoffOptions(price) {
// either offer might be exercised late, so we pay the two seats separately.
function payoffOptions(priceQuoteAmount) {
const { Price: price } = quoteMath.getValue(priceQuoteAmount)[0];
const longShare = calculateLongShare(price);
// either offer might be exercised late, so we pay the two seats separately.
reallocateToSeat(Position.LONG, longShare);
reallocateToSeat(Position.SHORT, inverse(longShare));
}

function schedulePayoffs() {
terms.priceAuthority
.priceAtTime(terms.expiration, terms.underlyingAmount)
.then(price => payoffOptions(price));
E(terms.priceAuthority)
.priceAtTime(
terms.timer,
terms.expiration,
terms.underlyingAmount,
strikeBrand,
)
.then(quoteIssuer.getAmountOf)
.then(priceQuoteAmount => payoffOptions(priceQuoteAmount));
}

function makeOptionInvitation(dir) {
Expand Down
143 changes: 85 additions & 58 deletions packages/zoe/test/unitTests/contracts/test-callSpread.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import test from 'ava';
import { E } from '@agoric/eventual-send';
import '../../../exported';
import { makePromiseKit } from '@agoric/promise-kit';
import { makeIssuerKit, MathKind } from '@agoric/ertp';
import buildManualTimer from '../../../tools/manualTimer';

import { setup } from '../setupBasicMints';
Expand All @@ -15,11 +16,17 @@ const callSpread = `${__dirname}/../../../src/contracts/callSpread`;
const simpleExchange = `${__dirname}/../../../src/contracts/simpleExchange`;

function makeFakePriceAuthority(
timer,
// timer,
underlyingAmountMath,
strikeAmountMath,
priceSchedule,
) {
const {
mint: quoteMint,
issuer: quoteIssuer,
amountMath: quote,
} = makeIssuerKit('quote', MathKind.SET);

function priceFromSchedule(strikeTime) {
let freshestPrice = 0;
let freshestTime = -1;
Expand All @@ -32,25 +39,40 @@ function makeFakePriceAuthority(
return freshestPrice;
}

function getRecentPrice(timer, desiredPriceBrand, underlyingAmount) {
const underlyingValue = underlyingAmountMath.getValue(underlyingAmount);
return E(timer)
.getCurrentTimestamp()
.then(now => {
const price = priceFromSchedule(now);
const strikePrice = strikeAmountMath.make(price * underlyingValue);
return quoteMint.mintPayment(
quote.make(
harden([
{
Asset: underlyingAmount,
Price: strikePrice,
timer,
timestamp: now,
},
]),
),
);
});
}

const priceAuthority = {
getCurrentPrice: underlyingAmount => {
const underlyingValue = underlyingAmountMath.getValue(underlyingAmount);
return E(timer)
.getCurrentTimestamp()
.then(now => {
const price = priceFromSchedule(now);
return strikeAmountMath.make(price * underlyingValue);
});
},
priceAtTime: (timeStamp, underlyingAmount) => {
getQuoteIssuer: () => quoteIssuer,
priceAtTime: (timer, timeStamp, underlyingAmount, strikeBrand) => {
const { promise, resolve } = makePromiseKit();

underlyingAmountMath.getValue(underlyingAmount);
E(timer).setWakeup(
timeStamp,
harden({
wake: () => {
return resolve(priceAuthority.getCurrentPrice(underlyingAmount));
return resolve(
getRecentPrice(timer, strikeBrand, underlyingAmount),
);
},
}),
);
Expand Down Expand Up @@ -89,17 +111,8 @@ test('callSpread below Strike1', async t => {
// Setup Carol
const carolBucksPurse = bucksIssuer.makeEmptyPurse();

// Alice creates a callSpread instance
const issuerKeywordRecord = harden({
Underlying: simoleanIssuer,
Collateral: bucksIssuer,
Strike: moolaIssuer,
Options: invitationIssuer,
});

const manualTimer = buildManualTimer(console.log, 1);
const priceAuthority = makeFakePriceAuthority(
manualTimer,
amountMaths.get('simoleans'),
amountMaths.get('moola'),
[
Expand All @@ -117,6 +130,16 @@ test('callSpread below Strike1', async t => {
strikePrice1: moola(60),
strikePrice2: moola(100),
settlementAmount: bucks(300),
timer: manualTimer,
});

// Alice creates a callSpread instance
const issuerKeywordRecord = harden({
Underlying: simoleanIssuer,
Collateral: bucksIssuer,
Strike: moolaIssuer,
Options: invitationIssuer,
Quote: priceAuthority.getQuoteIssuer(),
});
const { creatorInvitation } = await zoe.startInstance(
installation,
Expand Down Expand Up @@ -191,17 +214,8 @@ test('callSpread above Strike2', async t => {
// Setup Carol
const carolBucksPurse = bucksIssuer.makeEmptyPurse();

// Alice creates a callSpread instance
const issuerKeywordRecord = harden({
Underlying: simoleanIssuer,
Collateral: bucksIssuer,
Strike: moolaIssuer,
Options: invitationIssuer,
});

const manualTimer = buildManualTimer(console.log, 1);
const priceAuthority = makeFakePriceAuthority(
manualTimer,
amountMaths.get('simoleans'),
amountMaths.get('moola'),
[
Expand All @@ -217,6 +231,16 @@ test('callSpread above Strike2', async t => {
strikePrice1: moola(60),
strikePrice2: moola(100),
settlementAmount: bucks(300),
timer: manualTimer,
});

// Alice creates a callSpread instance
const issuerKeywordRecord = harden({
Underlying: simoleanIssuer,
Collateral: bucksIssuer,
Strike: moolaIssuer,
Options: invitationIssuer,
Quote: priceAuthority.getQuoteIssuer(),
});

const { creatorInvitation } = await zoe.startInstance(
Expand Down Expand Up @@ -297,17 +321,8 @@ test('callSpread, mid-strike', async t => {
// Setup Carol
const carolBucksPurse = bucksIssuer.makeEmptyPurse();

// Alice creates a callSpread instance
const issuerKeywordRecord = harden({
Underlying: simoleanIssuer,
Collateral: bucksIssuer,
Strike: moolaIssuer,
Options: invitationIssuer,
});

const manualTimer = buildManualTimer(console.log, 1);
const priceAuthority = makeFakePriceAuthority(
manualTimer,
amountMaths.get('simoleans'),
amountMaths.get('moola'),
[
Expand All @@ -323,7 +338,17 @@ test('callSpread, mid-strike', async t => {
strikePrice1: moola(60),
strikePrice2: moola(100),
settlementAmount: bucks(300),
timer: manualTimer,
});
// Alice creates a callSpread instance
const issuerKeywordRecord = harden({
Underlying: simoleanIssuer,
Collateral: bucksIssuer,
Strike: moolaIssuer,
Options: invitationIssuer,
Quote: priceAuthority.getQuoteIssuer(),
});

const { creatorInvitation } = await zoe.startInstance(
installation,
issuerKeywordRecord,
Expand Down Expand Up @@ -402,17 +427,8 @@ test('callSpread, late exercise', async t => {
// Setup Carol
const carolBucksPurse = bucksIssuer.makeEmptyPurse();

// Alice creates a callSpread instance
const issuerKeywordRecord = harden({
Underlying: simoleanIssuer,
Collateral: bucksIssuer,
Strike: moolaIssuer,
Options: invitationIssuer,
});

const manualTimer = buildManualTimer(console.log, 1);
const priceAuthority = makeFakePriceAuthority(
manualTimer,
amountMaths.get('simoleans'),
amountMaths.get('moola'),
[
Expand All @@ -428,6 +444,16 @@ test('callSpread, late exercise', async t => {
strikePrice1: moola(60),
strikePrice2: moola(100),
settlementAmount: bucks(300),
timer: manualTimer,
});

// Alice creates a callSpread instance
const issuerKeywordRecord = harden({
Underlying: simoleanIssuer,
Collateral: bucksIssuer,
Strike: moolaIssuer,
Options: invitationIssuer,
Quote: priceAuthority.getQuoteIssuer(),
});
const { creatorInvitation } = await zoe.startInstance(
installation,
Expand Down Expand Up @@ -508,17 +534,8 @@ test('callSpread, sell options', async t => {
const carolBucksPurse = bucksIssuer.makeEmptyPurse();
const carolBucksPayment = bucksMint.mintPayment(bucks(100));

// Alice creates a callSpread instance
const issuerKeywordRecord = harden({
Underlying: simoleanIssuer,
Collateral: bucksIssuer,
Strike: moolaIssuer,
Options: invitationIssuer,
});

const manualTimer = buildManualTimer(console.log, 1);
const priceAuthority = makeFakePriceAuthority(
manualTimer,
amountMaths.get('simoleans'),
amountMaths.get('moola'),
[
Expand All @@ -534,6 +551,16 @@ test('callSpread, sell options', async t => {
strikePrice1: moola(60),
strikePrice2: moola(100),
settlementAmount: bucks(300),
timer: manualTimer,
});

// Alice creates a callSpread instance
const issuerKeywordRecord = harden({
Underlying: simoleanIssuer,
Collateral: bucksIssuer,
Strike: moolaIssuer,
Options: invitationIssuer,
Quote: priceAuthority.getQuoteIssuer(),
});
const { creatorInvitation } = await zoe.startInstance(
installation,
Expand Down

0 comments on commit a0dd761

Please sign in to comment.