Skip to content

Commit

Permalink
Merge pull request #112 from bancorprotocol/fix/marginal-update
Browse files Browse the repository at this point in the history
  • Loading branch information
zavelevsky authored Dec 6, 2023
2 parents e5805f3 + f2a86a5 commit 37bae46
Show file tree
Hide file tree
Showing 5 changed files with 336 additions and 26 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@bancor/carbon-sdk",
"type": "module",
"source": "src/index.ts",
"version": "0.0.88-DEV",
"version": "0.0.89-DEV",
"description": "The SDK is a READ-ONLY tool, intended to facilitate working with Carbon contracts. It's a convenient wrapper around our matching algorithm, allowing programs and users get a ready to use transaction data that will allow them to manage strategies and fulfill trades",
"main": "dist/index.js",
"module": "dist/index.js",
Expand Down
35 changes: 25 additions & 10 deletions src/strategy-management/Toolkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,8 @@ export class Toolkit {
new Decimal(buyPriceLow),
new Decimal(sellPriceHigh),
new Decimal(marketPrice),
new Decimal(spreadPercentage)
new Decimal(spreadPercentage),
quoteDecimals
);

const result = {
Expand Down Expand Up @@ -757,6 +758,7 @@ export class Toolkit {
*/
public async calculateOverlappingStrategySellBudget(
baseToken: string,
quoteToken: string,
buyPriceLow: string,
sellPriceHigh: string,
marketPrice: string,
Expand All @@ -766,12 +768,14 @@ export class Toolkit {
logger.debug('calculateOverlappingStrategySellBudget called', arguments);
const decimals = this._decimals;
const baseDecimals = await decimals.fetchDecimals(baseToken);
const quoteDecimals = await decimals.fetchDecimals(quoteToken);
const budget = calculateOverlappingSellBudget(
new Decimal(buyPriceLow),
new Decimal(sellPriceHigh),
new Decimal(marketPrice),
new Decimal(spreadPercentage),
new Decimal(buyBudget)
new Decimal(buyBudget),
quoteDecimals
);

const result = trimDecimal(budget.toString(), baseDecimals);
Expand Down Expand Up @@ -811,7 +815,8 @@ export class Toolkit {
new Decimal(sellPriceHigh),
new Decimal(marketPrice),
new Decimal(spreadPercentage),
new Decimal(sellBudget)
new Decimal(sellBudget),
quoteDecimals
);

const result = trimDecimal(budget.toString(), quoteDecimals);
Expand Down Expand Up @@ -962,19 +967,19 @@ export class Toolkit {
baseDecimals,
quoteDecimals,
buyPriceLow ?? originalStrategy.buyPriceLow,
buyPriceMarginal &&
buyPriceMarginal !== undefined && // if we got marginal price use it - otherwise act as reset and use buy high
buyPriceMarginal !== MarginalPriceOptions.reset &&
buyPriceMarginal !== MarginalPriceOptions.maintain
? buyPriceMarginal
: originalStrategy.buyPriceMarginal,
: buyPriceHigh ?? originalStrategy.buyPriceHigh,
buyPriceHigh ?? originalStrategy.buyPriceHigh,
buyBudget ?? originalStrategy.buyBudget,
sellPriceLow ?? originalStrategy.sellPriceLow,
sellPriceMarginal &&
sellPriceMarginal !== undefined && // if we got marginal price use it - otherwise act as reset and use sell low
sellPriceMarginal !== MarginalPriceOptions.reset &&
sellPriceMarginal !== MarginalPriceOptions.maintain
? sellPriceMarginal
: originalStrategy.sellPriceMarginal,
: sellPriceLow ?? originalStrategy.sellPriceLow,
sellPriceHigh ?? originalStrategy.sellPriceHigh,
sellBudget ?? originalStrategy.sellBudget
);
Expand All @@ -983,8 +988,8 @@ export class Toolkit {
// step 3: to avoid changes due to rounding errors, we will override the new encoded strategy with selected values from the old encoded strategy:
// - if budget wasn't defined - we will use the old y
// - if no price was defined - we will use the old A and B
// - if any price was defined - we will reset z to y
// - if any budget was defined - will set z according to MarginalPriceOptions
// - if any price was defined - we will reset z to y
// - if marginalPrice is a number - we will use it to calculate z
const encodedBN = encodedStrategyStrToBN(encoded);
if (buyBudget === undefined) {
Expand Down Expand Up @@ -1021,6 +1026,7 @@ export class Toolkit {
}
}

// if we have budget to set we handle reset (z <- y) and maintain (maintain y:z ratio). We don't handle marginal price value because it's expressed in z
if (sellBudget !== undefined) {
if (
sellPriceMarginal === undefined ||
Expand All @@ -1038,10 +1044,19 @@ export class Toolkit {
}
}

if (buyPriceLow !== undefined || buyPriceHigh !== undefined) {
if (
(buyPriceLow !== undefined || buyPriceHigh !== undefined) &&
(buyPriceMarginal === MarginalPriceOptions.reset ||
buyPriceMarginal === undefined)
) {
newEncodedStrategy.order1.z = newEncodedStrategy.order1.y;
}
if (sellPriceLow !== undefined || sellPriceHigh !== undefined) {

if (
(sellPriceLow !== undefined || sellPriceHigh !== undefined) &&
(sellPriceMarginal === MarginalPriceOptions.reset ||
sellPriceMarginal === undefined)
) {
newEncodedStrategy.order0.z = newEncodedStrategy.order0.y;
}

Expand Down
53 changes: 42 additions & 11 deletions src/strategy-management/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,11 +313,30 @@ export function subtractFee(
.floor();
}

export function enforcePriceRange(
minPrice: Decimal,
maxPrice: Decimal,
marginalPrice: Decimal,
tokenDecimals: number,
canEqualMin: boolean,
canEqualMax: boolean
) {
const oneWei = new Decimal(1).div(new Decimal(10).pow(tokenDecimals));
if (marginalPrice.lte(minPrice))
return canEqualMin ? minPrice : minPrice.plus(oneWei);

if (marginalPrice.gte(maxPrice))
return canEqualMax ? maxPrice : maxPrice.minus(oneWei);

return marginalPrice;
}

export function calculateOverlappingPriceRanges(
buyPriceLow: Decimal, // in quote tkn per 1 base tkn
sellPriceHigh: Decimal, // in quote tkn per 1 base tkn
marketPrice: Decimal, // in quote tkn per 1 base tkn
spreadPercentage: Decimal // e.g. for 0.1% pass '0.1'
spreadPercentage: Decimal, // e.g. for 0.1% pass '0.1'
tokenDecimals: number
): {
buyPriceHigh: Decimal;
buyPriceMarginal: Decimal;
Expand All @@ -328,16 +347,24 @@ export function calculateOverlappingPriceRanges(
const buyPriceHigh = sellPriceHigh.div(spreadFactor);
const sellPriceLow = buyPriceLow.mul(spreadFactor);

// buy marginal price is the market price minus 0.5 spread. But it can never be lower than buyPriceLow
const buyPriceMarginal = Decimal.max(
// buy marginal price is derived from the market price. But must be LTE buyPriceHigh and GT buyPriceLow
const buyPriceMarginal = enforcePriceRange(
buyPriceLow,
marketPrice.div(spreadFactor.sqrt())
buyPriceHigh,
marketPrice.div(spreadFactor.sqrt()),
tokenDecimals,
false,
true
);

// sell marginal price is the market price plus 0.5 spread. But ir can never be higher than sellPriceHigh
const sellPriceMarginal = Decimal.min(
// sell marginal price is derived from the market price. But must be GTE sellPriceLow and LT sellPriceHigh
const sellPriceMarginal = enforcePriceRange(
sellPriceLow,
sellPriceHigh,
marketPrice.mul(spreadFactor.sqrt())
marketPrice.mul(spreadFactor.sqrt()),
tokenDecimals,
true,
false
);

return {
Expand All @@ -353,7 +380,8 @@ export function calculateOverlappingSellBudget(
sellPriceHigh: Decimal, // in quote tkn per 1 base tkn
marketPrice: Decimal, // in quote tkn per 1 base tkn
spreadPercentage: Decimal, // e.g. for 0.1% pass '0.1'
buyBudget: Decimal // in quote tkn
buyBudget: Decimal, // in quote tkn
quoteTokenDecimals: number
): Decimal {
// zero buy budget means zero sell budget
if (buyBudget.isZero()) return new Decimal(0);
Expand All @@ -363,7 +391,8 @@ export function calculateOverlappingSellBudget(
buyPriceLow,
sellPriceHigh,
marketPrice,
spreadPercentage
spreadPercentage,
quoteTokenDecimals
);

// if buy range takes the entire range then there's zero sell budget
Expand Down Expand Up @@ -393,7 +422,8 @@ export function calculateOverlappingBuyBudget(
sellPriceHigh: Decimal, // in quote tkn per 1 base tkn
marketPrice: Decimal, // in quote tkn per 1 base tkn
spreadPercentage: Decimal, // e.g. for 0.1% pass '0.1'
sellBudget: Decimal // in quote tkn
sellBudget: Decimal, // in quote tkn
quoteTokenDecimals: number
): Decimal {
// zero sell budget means zero buy budget
if (sellBudget.isZero()) return new Decimal(0);
Expand All @@ -403,7 +433,8 @@ export function calculateOverlappingBuyBudget(
buyPriceLow,
sellPriceHigh,
marketPrice,
spreadPercentage
spreadPercentage,
quoteTokenDecimals
);

// if sell range takes the entire range then there's zero buy budget
Expand Down
Loading

0 comments on commit 37bae46

Please sign in to comment.