Skip to content

Commit

Permalink
Merge pull request #106 from bancorprotocol/feature/overlapping-strat…
Browse files Browse the repository at this point in the history
…egies
  • Loading branch information
zavelevsky authored Nov 30, 2023
2 parents 78122a6 + 2219607 commit 0a498fd
Show file tree
Hide file tree
Showing 7 changed files with 485 additions and 38 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.85-DEV",
"version": "0.0.86-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
179 changes: 150 additions & 29 deletions src/strategy-management/Toolkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
tenPow,
formatUnits,
parseUnits,
trimDecimal,
} from '../utils/numerics';

// Internal modules
Expand Down Expand Up @@ -48,6 +49,9 @@ const logger = new Logger('Toolkit.ts');
import {
addFee,
buildStrategyObject,
calculateOverlappingBuyBudget,
calculateOverlappingPriceRanges,
calculateOverlappingSellBudget,
decodeStrategy,
encodeStrategy,
normalizeRate,
Expand Down Expand Up @@ -696,6 +700,130 @@ export class Toolkit {
);
}

public async calculateOverlappingStrategyPrices(
quoteToken: string,
buyPriceLow: string,
sellPriceHigh: string,
marketPrice: string,
spreadPercentage: string
): Promise<{
buyPriceHigh: string;
buyPriceMarginal: string;
sellPriceLow: string;
sellPriceMarginal: string;
}> {
logger.debug('calculateOverlappingStrategyPrices called', arguments);

const decimals = this._decimals;
const quoteDecimals = await decimals.fetchDecimals(quoteToken);
const prices = calculateOverlappingPriceRanges(
new Decimal(buyPriceLow),
new Decimal(sellPriceHigh),
new Decimal(marketPrice),
new Decimal(spreadPercentage)
);

const result = {
buyPriceHigh: trimDecimal(prices.buyPriceHigh.toString(), quoteDecimals),
buyPriceMarginal: trimDecimal(
prices.buyPriceMarginal.toString(),
quoteDecimals
),
sellPriceLow: trimDecimal(prices.sellPriceLow.toString(), quoteDecimals),
sellPriceMarginal: trimDecimal(
prices.sellPriceMarginal.toString(),
quoteDecimals
),
};

logger.debug('calculateOverlappingStrategyPrices info:', {
quoteDecimals,
result,
});

return result;
}

/**
* Calculates the sell budget given a buy budget of an overlapping strategy.
*
* @param {string} baseToken - The address of the base token for the strategy.
* @param {string} buyPriceLow - The minimum buy price for the strategy, in in `quoteToken` per 1 `baseToken`, as a string.
* @param {string} sellPriceHigh - The maximum sell price for the strategy, in `quoteToken` per 1 `baseToken`, as a string.
* @param {string} marketPrice - The market price, in `quoteToken` per 1 `baseToken`, as a string.
* @param {string} spreadPercentage - The spread percentage, e.g. for 10%, enter `10`.
* @param {string} buyBudget - The budget for buying tokens in the strategy, in `quoteToken`, as a string.
* @return {Promise<string>} The result of the calculation - the sell budget in token res in base token.
*/
public async calculateOverlappingStrategySellBudget(
baseToken: string,
buyPriceLow: string,
sellPriceHigh: string,
marketPrice: string,
spreadPercentage: string,
buyBudget: string
): Promise<string> {
logger.debug('calculateOverlappingStrategySellBudget called', arguments);
const decimals = this._decimals;
const baseDecimals = await decimals.fetchDecimals(baseToken);
const budget = calculateOverlappingSellBudget(
new Decimal(buyPriceLow),
new Decimal(sellPriceHigh),
new Decimal(marketPrice),
new Decimal(spreadPercentage),
new Decimal(buyBudget)
);

const result = trimDecimal(budget.toString(), baseDecimals);

logger.debug('calculateOverlappingStrategySellBudget info:', {
baseDecimals,
result,
});

return result;
}

/**
* Calculates the buy budget given a sell budget of an overlapping strategy.
*
* @param {string} quoteToken - The address of the base token for the strategy.
* @param {string} buyPriceLow - The minimum buy price for the strategy, in in `quoteToken` per 1 `baseToken`, as a string.
* @param {string} sellPriceHigh - The maximum sell price for the strategy, in `quoteToken` per 1 `baseToken`, as a string.
* @param {string} marketPrice - The market price, in `quoteToken` per 1 `baseToken`, as a string.
* @param {string} spreadPercentage - The spread percentage, e.g. for 10%, enter `10`.
* @param {string} sellBudget - The budget for selling tokens in the strategy, in `baseToken`, as a string.
* @return {Promise<string>} The result of the calculation - the buy budget in token res in quote token.
*/
public async calculateOverlappingStrategyBuyBudget(
quoteToken: string,
buyPriceLow: string,
sellPriceHigh: string,
marketPrice: string,
spreadPercentage: string,
sellBudget: string
): Promise<string> {
logger.debug('calculateOverlappingStrategyBuyBudget called', arguments);
const decimals = this._decimals;
const quoteDecimals = await decimals.fetchDecimals(quoteToken);
const budget = calculateOverlappingBuyBudget(
new Decimal(buyPriceLow),
new Decimal(sellPriceHigh),
new Decimal(marketPrice),
new Decimal(spreadPercentage),
new Decimal(sellBudget)
);

const result = trimDecimal(budget.toString(), quoteDecimals);

logger.debug('calculateOverlappingStrategyBuyBudget info:', {
quoteDecimals,
result,
});

return result;
}

/**
* Creates an unsigned transaction to create a strategy for buying and selling tokens of `baseToken` for price in `quoteToken` per 1 `baseToken`.
*
Expand Down Expand Up @@ -745,9 +873,11 @@ export class Toolkit {
baseToken: string,
quoteToken: string,
buyPriceLow: string,
buyPriceMarginal: string,
buyPriceHigh: string,
buyBudget: string,
sellPriceLow: string,
sellPriceMarginal: string,
sellPriceHigh: string,
sellBudget: string,
overrides?: PayableOverrides
Expand All @@ -762,9 +892,11 @@ export class Toolkit {
baseDecimals,
quoteDecimals,
buyPriceLow,
buyPriceMarginal,
buyPriceHigh,
buyBudget,
sellPriceLow,
sellPriceMarginal,
sellPriceHigh,
sellBudget
);
Expand Down Expand Up @@ -803,8 +935,8 @@ export class Toolkit {
sellPriceHigh,
sellBudget,
}: StrategyUpdate,
buyMarginalPrice?: MarginalPriceOptions | string,
sellMarginalPrice?: MarginalPriceOptions | string,
buyPriceMarginal?: MarginalPriceOptions | string,
sellPriceMarginal?: MarginalPriceOptions | string,
overrides?: PayableOverrides
): Promise<PopulatedTransaction> {
logger.debug('updateStrategy called', arguments);
Expand All @@ -830,9 +962,19 @@ export class Toolkit {
baseDecimals,
quoteDecimals,
buyPriceLow ?? originalStrategy.buyPriceLow,
buyPriceMarginal &&
buyPriceMarginal !== MarginalPriceOptions.reset &&
buyPriceMarginal !== MarginalPriceOptions.maintain
? buyPriceMarginal
: originalStrategy.buyPriceMarginal,
buyPriceHigh ?? originalStrategy.buyPriceHigh,
buyBudget ?? originalStrategy.buyBudget,
sellPriceLow ?? originalStrategy.sellPriceLow,
sellPriceMarginal &&
sellPriceMarginal !== MarginalPriceOptions.reset &&
sellPriceMarginal !== MarginalPriceOptions.maintain
? sellPriceMarginal
: originalStrategy.sellPriceMarginal,
sellPriceHigh ?? originalStrategy.sellPriceHigh,
sellBudget ?? originalStrategy.sellBudget
);
Expand Down Expand Up @@ -864,12 +1006,12 @@ export class Toolkit {

if (buyBudget !== undefined) {
if (
buyMarginalPrice === undefined ||
buyMarginalPrice === MarginalPriceOptions.reset ||
buyPriceMarginal === undefined ||
buyPriceMarginal === MarginalPriceOptions.reset ||
encodedBN.order1.y.isZero()
) {
newEncodedStrategy.order1.z = newEncodedStrategy.order1.y;
} else if (buyMarginalPrice === MarginalPriceOptions.maintain) {
} else if (buyPriceMarginal === MarginalPriceOptions.maintain) {
// maintain the current ratio of y/z
newEncodedStrategy.order1.z = mulDiv(
encodedBN.order1.z,
Expand All @@ -881,12 +1023,12 @@ export class Toolkit {

if (sellBudget !== undefined) {
if (
sellMarginalPrice === undefined ||
sellMarginalPrice === MarginalPriceOptions.reset ||
sellPriceMarginal === undefined ||
sellPriceMarginal === MarginalPriceOptions.reset ||
encodedBN.order0.y.isZero()
) {
newEncodedStrategy.order0.z = newEncodedStrategy.order0.y;
} else if (sellMarginalPrice === MarginalPriceOptions.maintain) {
} else if (sellPriceMarginal === MarginalPriceOptions.maintain) {
// maintain the current ratio of y/z
newEncodedStrategy.order0.z = mulDiv(
encodedBN.order0.z,
Expand All @@ -903,27 +1045,6 @@ export class Toolkit {
newEncodedStrategy.order0.z = newEncodedStrategy.order0.y;
}

if (
buyMarginalPrice !== undefined &&
buyMarginalPrice !== MarginalPriceOptions.reset &&
buyMarginalPrice !== MarginalPriceOptions.maintain
) {
// TODO: set newEncodedStrategy.order1.z according to the given marginal price
throw new Error(
'Support for custom marginal price is not implemented yet'
);
}
if (
sellMarginalPrice !== undefined &&
sellMarginalPrice !== MarginalPriceOptions.reset &&
sellMarginalPrice !== MarginalPriceOptions.maintain
) {
// TODO: set newEncodedStrategy.order0.z according to the given marginal price
throw new Error(
'Support for custom marginal price is not implemented yet'
);
}

logger.debug('updateStrategy info:', {
baseDecimals,
quoteDecimals,
Expand Down
Loading

0 comments on commit 0a498fd

Please sign in to comment.