diff --git a/README.md b/README.md index 6d9fc46..93dcd3f 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,32 @@ Example, here a delay of **60 seconds** is used: } ``` +There are 2 modes available with a 3rd on the way. + +* `pricefeed`: Follows an external price oracle and updates indicated bids and asks based on that. +* `constant`: Sets an `initPrice` and market makes around that price. Can be combined with single-sided liquidity to simulate limit orders. +* `independent`: Under development. The price is set independent of a price feed. + +For all modes the `slippageRate`, `maxSize`, `minSize`, `minSpread`, and `active` settings are mandatory. + +For `pricefeed` mode, the `priceFeedPrimary` is mandatory. + +For `independent` and `constant` mode, the `initPrice` is mandatory. + +The `side` setting can be toggled for single-sided liquidity. By default, the side setting is set to `d`, which stands for double-sided liquidity. To toggle single-sided liquidity, the value can be set to `b` or `s` for buy-side only or sell-side only. + +The primary price feed is the price feed used to determine the bids and asks of the market maker. The secondary price feed is used to validate the first price feed and make sure the market isn't returning bad data. If the primary and secondary price feeds vary by more than 1%, the market maker will not fill orders. + +The slippage rate is the rate at which the spread increases as the base unit increases. For the example above, the spread goes up by 1e-5 for every 1 ETH in size added to an order. That's the equivalent of 0.1 bps / ETH in slippage. + +Orders coming in below the `minSpread` from the price feed will not be filled. The spread is calculated as a decimal value. 0.01 is 1%, and 0.0002 is 2 basis points (bps). + +A market can be set inactive by flipping the active switch to `false`. + +`indicateLiquidity` can be used to set custom spread levels on the UI. By default 10 splits are used to split up the liquidity but this allows you to indicate something else besides that. Liquidity will still be filled based on the other settings, so this is for visual purposes only. It is usually omitted or left blank and most users shouldn't use it. An example with `indicateLiquidity` can be found in the Pair Setting Examples. + +## Pair Options + ###### increaseSpreadAfterFill The market maker increases the spread by the set amount. After the time (**in seconds**) the spread will fall back to the old value. This can happen multiple times in case the mm fills again in the set time (e.g. 0.1 -> 0.2 -> 0.3). Example: @@ -223,6 +249,25 @@ Sell the rip: } ``` +Indicate non-standard liquidity. + +``` +"DYDX-USDC": { + "mode": "constant", + "side": "s", + "initPrice": 20, + "slippageRate": 1e-5, + "indicateLiquidity": [ + [0.0001, 10], + [0.0003, 10], + ] + "maxSize": 1000, + "minSize": 0.5, + "minSpread": 0, + "active": true +} +``` + ## Configuration Via Environment Variables If your hosting service requires you to pass in configs via environment variables you can compress `config.json`: diff --git a/marketmaker.js b/marketmaker.js index 0a50ca4..57b2796 100644 --- a/marketmaker.js +++ b/marketmaker.js @@ -553,7 +553,7 @@ async function cryptowatchWsSetup(cryptowatchMarketIds) { subscriptionMsg.subscribe.subscriptions.push({ "streamSubscription": { - "resource": `markets:${cryptowatchMarketId}:trades` + "resource": `markets:${cryptowatchMarketId}:book:spread` } }) } @@ -571,8 +571,9 @@ async function cryptowatchWsSetup(cryptowatchMarketIds) { if (!msg.marketUpdate) return; const marketId = "cryptowatch:" + msg.marketUpdate.market.marketId; - let trades = msg.marketUpdate.tradesUpdate.trades; - let price = trades[trades.length - 1].priceStr / 1; + let ask = msg.marketUpdate.orderBookSpreadUpdate.ask.priceStr; + let bid = msg.marketUpdate.orderBookSpreadUpdate.bid.priceStr; + let price = ask / 2 + bid / 2; PRICE_FEEDS[marketId] = price; } function onclose () { @@ -644,16 +645,33 @@ function indicateLiquidity (pairs = MM_CONFIG.pairs) { const maxSellSize = Math.min(baseBalance, mmConfig.maxSize); const maxBuySize = Math.min(quoteBalance / midPrice, mmConfig.maxSize); - const splits = 10; const liquidity = []; - for (let i=1; i <= splits; i++) { - const buyPrice = midPrice * (1 - mmConfig.minSpread - (mmConfig.slippageRate * maxBuySize * i/splits)); - const sellPrice = midPrice * (1 + mmConfig.minSpread + (mmConfig.slippageRate * maxSellSize * i/splits)); - if ((['b','d']).includes(side)) { - liquidity.push(["b", buyPrice, maxBuySize / splits, expires]); + if (mmConfig.indicateLiquidity) { + for (let i in mmConfig.indicateLiquidity) { + const entry = mmConfig.indicateLiquidity[i]; + const spread = entry[0]; + const amount = entry[1]; + const buyPrice = midPrice * (1 - spread); + const sellPrice = midPrice * (1 + spread); + if ((['b','d']).includes(side)) { + liquidity.push(["b", buyPrice, amount, expires]); + } + if ((['s','d']).includes(side)) { + liquidity.push(["s", sellPrice, amount, expires]); + } } - if ((['s','d']).includes(side)) { - liquidity.push(["s", sellPrice, maxSellSize / splits, expires]); + } + else { + const splits = 10; + for (let i=1; i <= splits; i++) { + const buyPrice = midPrice * (1 - mmConfig.minSpread - (mmConfig.slippageRate * maxBuySize * i/splits)); + const sellPrice = midPrice * (1 + mmConfig.minSpread + (mmConfig.slippageRate * maxSellSize * i/splits)); + if ((['b','d']).includes(side)) { + liquidity.push(["b", buyPrice, maxBuySize / splits, expires]); + } + if ((['s','d']).includes(side)) { + liquidity.push(["s", sellPrice, maxSellSize / splits, expires]); + } } } const msg = { op: "indicateliq2", args: [CHAIN_ID, marketId, liquidity, CLIENT_ID] };