From 9bc519be2db8b7d2c252a61a7b7c374dd5990744 Mon Sep 17 00:00:00 2001 From: dapplion Date: Sat, 10 Aug 2019 16:35:12 +0200 Subject: [PATCH] Implement zargham math review and vesting period floor price --- src/App.tsx | 79 +++++++++++++++++++++++++----- src/PriceSimulationChart.tsx | 36 ++++++++++---- src/SupplyVsDemandChart.tsx | 2 + src/math.ts | 95 ++++++++++++++++++++++++++++++++++-- 4 files changed, 188 insertions(+), 24 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 5964467..c315a0e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -20,10 +20,16 @@ import { getLast, getAvg, pause } from "./utils"; import { getInitialParams, getPriceR, + getMinPrice, + getS, + vest_tokens, + getMinR, getSlippage, getTxDistribution, getDeltaR_priceGrowth, - rv_U + rv_U, + getMedian, + getSum } from "./math"; import { throttle } from "lodash"; // General styles @@ -199,7 +205,7 @@ export default function App() { const { k, // Invariant power kappa (.) R0, // Initial reserve (DAI) - // S0, // initial supply of tokens (token) + S0, // initial supply of tokens (token) V0 // invariant coef } = getInitialParams({ d0, @@ -210,6 +216,7 @@ export default function App() { const [priceTimeseries, setPriceTimeseries] = useState([0]); const [withdrawFeeTimeseries, setWithdrawFeeTimeseries] = useState([0]); + const [floorpriceTimeseries, setFloorpriceTimeseries] = useState([0]); const [totalReserve, setTotalReserve] = useState(R0); const [withdrawCount, setWithdrawCount] = useState(0); const [avgSlippage, setAvgSlippage] = useState(0); @@ -248,48 +255,97 @@ export default function App() { async function simulateRandomDelta() { const R_t: number[] = [R0]; + const S_t: number[] = [S0]; const p_t: number[] = [getPriceR({ R: R0, V0, k })]; const wFee_t: number[] = [0]; const slippage_t: number[] = []; const avgTxSize_t: number[] = []; + // hatchers tokens = S0[section added by Z] + const H_t: number[] = [S0]; // total hatcher tokens not vested + const floorprice_t: number[] = []; // initially the price is the floor as all tokens are hatcher tokens + // Random walk const numSteps = 52; + const u_min = 0.97; + const u_max = 1.04; + const tx_spread = 10; + // vesting(should this be exposed in the app ?) + const cliff = 8; // weeks before vesting starts ~2 months + const halflife = 52; // 26 weeks, half life is ~6 months + // percentage of the hatch tokens which vest per week(since that is our timescale in the sim) // numSteps = 52 take 8ms to run setSimulationRunning(true); for (let t = 0; t < numSteps; t++) { - const txsWeek = Math.ceil(t < 5 ? rv_U(0, 5) : rv_U(5, 2 * t)); - const priceGrowth = rv_U(0.99, 1.03); + const txsWeek = rv_U(5, 2 * t + 5); const R = getLast(R_t); - const deltaR = getDeltaR_priceGrowth({ R, k, priceGrowth }); + const S = getLast(S_t); + const H = getLast(H_t); + // enforce the effects of the unvested tokens not being burnable + let u_lower; + if (H === S) { + u_lower = 1; + } else { + const R_ratio = getMinR({ S, H, V0, k }) / R; + u_lower = Math.max(1 - R_ratio, u_min); + } + const priceGrowth = rv_U(u_lower, u_max); + + const deltaR = getDeltaR_priceGrowth({ R, k, priceGrowth }); const R_next = R + deltaR; - const txs = getTxDistribution({ sum: deltaR, num: txsWeek }); + const txs = getTxDistribution({ + sum: deltaR, + num: txsWeek, + spread: tx_spread + }); // Compute slippage - const slippage = getAvg( - txs.map(txR => getSlippage({ R, deltaR: txR, V0, k })) + const slippage_txs = txs.map(txR => + getSlippage({ R, deltaR: txR, V0, k }) ); + const slippage = getMedian(slippage_txs); + const txsWithdraw = txs.filter(tx => tx < 0); - const wFees = -wFee * txsWithdraw.reduce((t, c) => t + c, 0); - const _avgTxSize = - txs.reduce((t, c) => t + Math.abs(c), 0) / txs.length; + const wFees = -wFee * getSum(txsWithdraw); + // txsWithdraw.reduce((t, c) => t + c, 0); + + // Vest + const delta_H = vest_tokens({ week: t, H, halflife, cliff }); + const H_next = H - delta_H; + + // find floor price + const S_next = getS({ R, V0, k }); + const floorprice_next = getMinPrice({ + S: S_next, + H: S0 - H_next, + V0, + k + }); + + const _avgTxSize = getMedian(txsWithdraw); R_t.push(R_next); p_t.push(getPriceR({ R: R_next, V0, k })); slippage_t.push(slippage); avgTxSize_t.push(_avgTxSize); wFee_t.push(getLast(wFee_t) + wFees); + H_t.push(H_next); + floorprice_t.push(floorprice_next); setWithdrawCount(c => c + txsWithdraw.length); // Stop the simulation if it's no longer active if (!simulationActive || !canContinueSimulation) break; } + // floorprice_t is missing one data point + floorprice_t[floorprice_t.length] = floorprice_t[floorprice_t.length - 1]; + setPriceTimeseries(p_t); setWithdrawFeeTimeseries(wFee_t); + setFloorpriceTimeseries(floorprice_t); setAvgSlippage(getAvg(slippage_t)); setAvgTxSize(getAvg(avgTxSize_t)); setTotalReserve(getLast(R_t)); @@ -465,6 +521,7 @@ export default function App() { diff --git a/src/PriceSimulationChart.tsx b/src/PriceSimulationChart.tsx index bcc0e40..df5ab1e 100644 --- a/src/PriceSimulationChart.tsx +++ b/src/PriceSimulationChart.tsx @@ -17,6 +17,7 @@ import { linspace } from "./utils"; const keyHorizontal = "x"; const keyVerticalLeft = "Price (DAI/token)"; const keyVerticalRight = "Total exit tributes (DAI)"; +const keyVerticalLeft2 = "Floor price (DAI/token)"; const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -32,11 +33,13 @@ const useStyles = makeStyles((theme: Theme) => function PriceSimulationChart({ priceTimeseries, withdrawFeeTimeseries, + floorpriceTimeseries, p0, p1 }: { priceTimeseries: number[]; withdrawFeeTimeseries: number[]; + floorpriceTimeseries: number[]; p0: number; p1: number; }) { @@ -51,6 +54,7 @@ function PriceSimulationChart({ data.push({ [keyHorizontal]: t, [keyVerticalLeft]: priceTimeseries[t] || 0, + [keyVerticalLeft2]: floorpriceTimeseries[t] || 0, [keyVerticalRight]: withdrawFeeTimeseries[t] || 0 }); } @@ -70,8 +74,8 @@ function PriceSimulationChart({ const { textAnchor, viewBox, text } = props; return ( @@ -83,10 +87,12 @@ function PriceSimulationChart({ function CustomTooltip({ active, payload, label }: any) { if (active) { const price = payload[0].value; - const exit = payload[1].value; + const floor = payload[1].value; + const exit = payload[2].value; const weekNum = label; const toolTipData: string[][] = [ ["Price", price.toFixed(2), "DAI/tk"], + ["Floor", floor.toFixed(2), "DAI/tk"], ["Exit t.", formatter(exit), "DAI"], ["Week", weekNum, ""] ]; @@ -148,12 +154,10 @@ function PriceSimulationChart({ {/* Capital collected from withdraw fees - AXIS */} @@ -166,7 +170,20 @@ function PriceSimulationChart({ dataKey={keyVerticalLeft} stroke={theme.palette.primary.main} fill={theme.palette.primary.main} + fillOpacity={0.3} + strokeWidth={2} /> + + {/* a - b); + return arr.length % 2 !== 0 ? nums[mid] : (nums[mid - 1] + nums[mid]) / 2; +} + +export function getSum(arr: number[]) { + return arr.reduce((a, b) => a + b, 0); +}