Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: replace order #75

Merged
4 commits merged into from
Aug 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions src/opendex/complete.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { BigNumber } from 'bignumber.js';
import { Exchange } from 'ccxt';
import { Observable } from 'rxjs';
import { exhaustMap } from 'rxjs/operators';
import { getCentralizedExchangeAssets$ } from '../centralized/assets';
import { Config } from '../config';
import { Loggers } from '../logger';
import {
Expand All @@ -12,13 +14,10 @@ import { getOpenDEXassets$ } from './assets';
import { logAssetBalance, parseOpenDEXassets } from './assets-utils';
import { CreateOpenDEXordersParams } from './create-orders';
import { tradeInfoToOpenDEXorders } from './orders';
import { removeOpenDEXorders$ } from './remove-orders';
import { getXudBalance$ } from './xud/balance';
import { getXudClient$ } from './xud/client';
import { createXudOrder$ } from './xud/create-order';
import { getXudTradingLimits$ } from './xud/trading-limits';
import { getCentralizedExchangeAssets$ } from '../centralized/assets';
import { Exchange } from 'ccxt';

type GetOpenDEXcompleteParams = {
config: Config;
Expand Down Expand Up @@ -82,7 +81,6 @@ const getOpenDEXcomplete$ = ({
getTradeInfo,
getXudClient$,
createXudOrder$,
removeOpenDEXorders$,
tradeInfoToOpenDEXorders,
});
})
Expand Down
62 changes: 51 additions & 11 deletions src/opendex/create-orders.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { status } from '@grpc/grpc-js';
import { Observable } from 'rxjs';
import { TestScheduler } from 'rxjs/testing';
import { XudClient } from '../proto/xudrpc_grpc_pb';
import { PlaceOrderResponse } from '../proto/xudrpc_pb';
import { getLoggers, testConfig } from '../test-utils';
import { TradeInfo } from '../trade/info';
import { createOpenDEXorders$, OpenDEXorders } from './create-orders';
import { TestError } from '../test-utils';

let testScheduler: TestScheduler;
const testSchedulerSetup = () => {
Expand All @@ -16,27 +18,40 @@ const testSchedulerSetup = () => {
type CreateOpenDEXordersInputEvents = {
xudClient$: string;
xudOrder$: string;
removeOpenDEXorders$: string;
replaceXudOrder$: string;
};

const assertCreateOpenDEXorders = (
inputEvents: CreateOpenDEXordersInputEvents,
expected: string
expected: string,
inputErrors?: {
xudOrder$?: TestError;
replaceXudOrder$?: TestError;
}
) => {
testScheduler.run(helpers => {
const { cold, expectObservable } = helpers;
const getTradeInfo = (): TradeInfo => {
return ('mock trade info' as unknown) as TradeInfo;
};
const createXudOrder$ = () => {
return cold(inputEvents.xudOrder$) as Observable<PlaceOrderResponse>;
const createXudOrder$ = (createOrderParams: any) => {
if (createOrderParams.replaceOrderId) {
return cold(
inputEvents.replaceXudOrder$,
{},
inputErrors?.replaceXudOrder$
) as Observable<PlaceOrderResponse>;
} else {
return cold(
inputEvents.xudOrder$,
{},
inputErrors?.xudOrder$
) as Observable<PlaceOrderResponse>;
}
};
const getXudClient$ = () => {
return cold(inputEvents.xudClient$) as Observable<XudClient>;
};
const removeOpenDEXorders$ = () => {
return cold(inputEvents.removeOpenDEXorders$) as Observable<null>;
};
const tradeInfoToOpenDEXorders = (v: any) => {
return (v as unknown) as OpenDEXorders;
};
Expand All @@ -45,7 +60,6 @@ const assertCreateOpenDEXorders = (
getXudClient$,
createXudOrder$,
tradeInfoToOpenDEXorders,
removeOpenDEXorders$,
logger: getLoggers().global,
config: testConfig(),
});
Expand All @@ -61,10 +75,36 @@ describe('createOpenDEXorders$', () => {
it('creates buy and sell orders', () => {
const inputEvents = {
xudClient$: '1s a',
removeOpenDEXorders$: '4s a',
xudOrder$: '1s (a|)',
replaceXudOrder$: '1s (a|)',
xudOrder$: '',
};
const expected = '2s (a|)';
assertCreateOpenDEXorders(inputEvents, expected);
});

it('throws if unknown error for repace order', () => {
const inputEvents = {
xudClient$: '1s a',
replaceXudOrder$: '1s #',
xudOrder$: '',
};
const expected = '6s (a|)';
const expected = '2s #';
assertCreateOpenDEXorders(inputEvents, expected);
});

it('retries without replaceOrderId if grpc.NOT_FOUND error', () => {
const inputEvents = {
xudClient$: '1s a',
xudOrder$: '1s (a|)',
replaceXudOrder$: '1s #',
};
const inputErrors = {
replaceXudOrder$: {
code: status.NOT_FOUND,
message: 'NOT FOUND',
},
};
const expected = '3s (a|)';
assertCreateOpenDEXorders(inputEvents, expected, inputErrors);
});
});
76 changes: 42 additions & 34 deletions src/opendex/create-orders.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { forkJoin, Observable } from 'rxjs';
import { mapTo, mergeMap, take, tap } from 'rxjs/operators';
import { status } from '@grpc/grpc-js';
import { forkJoin, Observable, throwError } from 'rxjs';
import { catchError, mapTo, mergeMap, take, tap } from 'rxjs/operators';
import { Config } from '../config';
import { Logger } from '../logger';
import { XudClient } from '../proto/xudrpc_grpc_pb';
import { PlaceOrderResponse } from '../proto/xudrpc_pb';
import { TradeInfo } from '../trade/info';
import { OpenDEXorders, TradeInfoToOpenDEXordersParams } from './orders';
import { processListorders } from './process-listorders';
import { RemoveOpenDEXordersParams } from './remove-orders';
import {
OpenDEXorders,
TradeInfoToOpenDEXordersParams,
createOrderID,
} from './orders';
import { CreateXudOrderParams } from './xud/create-order';
import { listXudOrders$ } from './xud/list-orders';
import { removeXudOrder$ } from './xud/remove-order';
import { OrderSide } from '../proto/xudrpc_pb';

type CreateOpenDEXordersParams = {
config: Config;
Expand All @@ -21,13 +23,6 @@ type CreateOpenDEXordersParams = {
config,
}: TradeInfoToOpenDEXordersParams) => OpenDEXorders;
getXudClient$: (config: Config) => Observable<XudClient>;
removeOpenDEXorders$: ({
config,
getXudClient$,
listXudOrders$,
removeXudOrder$,
processListorders,
}: RemoveOpenDEXordersParams) => Observable<null>;
createXudOrder$: ({
client,
logger,
Expand All @@ -45,42 +40,55 @@ const createOpenDEXorders$ = ({
getTradeInfo,
tradeInfoToOpenDEXorders,
getXudClient$,
removeOpenDEXorders$,
createXudOrder$,
}: CreateOpenDEXordersParams): Observable<boolean> => {
return getXudClient$(config).pipe(
tap(() => logger.trace('Starting to update OpenDEX orders')),
// remove all existing orders
mergeMap(client => {
return removeOpenDEXorders$({
config,
getXudClient$,
listXudOrders$,
removeXudOrder$,
processListorders,
}).pipe(
tap(() =>
logger.trace(
`Removed all open orders for ${config.BASEASSET}/${config.QUOTEASSET}`
)
),
mapTo(client)
);
}),
// create new buy and sell orders
mergeMap(client => {
// build orders based on all the available trade info
const { buyOrder, sellOrder } = tradeInfoToOpenDEXorders({
config,
tradeInfo: getTradeInfo(),
});
// try replacing existing buy order
const buyOrder$ = createXudOrder$({
...{ client, logger },
...buyOrder,
});
...{
replaceOrderId: createOrderID(config, OrderSide.BUY),
},
}).pipe(
catchError(e => {
if (e.code === status.NOT_FOUND) {
// place order if existing one does not exist
return createXudOrder$({
...{ client, logger },
...buyOrder,
});
}
return throwError(e);
})
);
// try replacing existing sell order
const sellOrder$ = createXudOrder$({
...{ client, logger },
...sellOrder,
});
...{
replaceOrderId: createOrderID(config, OrderSide.SELL),
},
}).pipe(
catchError(e => {
if (e.code === status.NOT_FOUND) {
// place order if existing one does not exist
return createXudOrder$({
...{ client, logger },
...sellOrder,
});
}
return throwError(e);
})
);
const ordersComplete$ = forkJoin(sellOrder$, buyOrder$).pipe(mapTo(true));
return ordersComplete$;
}),
Expand Down
4 changes: 2 additions & 2 deletions src/opendex/orders.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const assertTradeInfoToOpenDEXorders = ({
orderSide: OrderSide.BUY,
pairId,
price: expected.buyPrice.toNumber(),
orderId: expect.any(String),
orderId: 'arby-ETH/BTC-buy-order',
})
);
}
Expand All @@ -46,7 +46,7 @@ const assertTradeInfoToOpenDEXorders = ({
orderSide: OrderSide.SELL,
pairId,
price: expected.sellPrice.toNumber(),
orderId: expect.any(String),
orderId: 'arby-ETH/BTC-sell-order',
})
);
}
Expand Down
14 changes: 11 additions & 3 deletions src/opendex/orders.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import BigNumber from 'bignumber.js';
import { v4 as uuidv4 } from 'uuid';
import { Config } from '../config';
import { OrderSide } from '../proto/xudrpc_pb';
import { TradeInfo } from '../trade/info';
Expand All @@ -11,6 +10,7 @@ type OpenDEXorder = {
pairId: string;
price: number;
orderId: string;
replaceOrderId?: string;
};

type OpenDEXorders = {
Expand All @@ -23,6 +23,13 @@ type TradeInfoToOpenDEXordersParams = {
config: Config;
};

const createOrderID = (config: Config, orderSide: OrderSide): string => {
const pairId = `${config.BASEASSET}/${config.QUOTEASSET}`;
return orderSide === OrderSide.BUY
? `arby-${pairId}-buy-order`
: `arby-${pairId}-sell-order`;
};

const tradeInfoToOpenDEXorders = ({
tradeInfo,
config,
Expand Down Expand Up @@ -59,14 +66,14 @@ const tradeInfoToOpenDEXorders = ({
orderSide: OrderSide.BUY,
pairId,
price: buyPrice.toNumber(),
orderId: uuidv4(),
orderId: createOrderID(config, OrderSide.BUY),
};
const sellOrder = {
quantity: coinsToSats(new BigNumber(sellQuantity.toFixed(8, 1)).toNumber()),
orderSide: OrderSide.SELL,
pairId,
price: sellPrice.toNumber(),
orderId: uuidv4(),
orderId: createOrderID(config, OrderSide.SELL),
};
return {
buyOrder,
Expand All @@ -78,5 +85,6 @@ export {
OpenDEXorders,
OpenDEXorder,
tradeInfoToOpenDEXorders,
createOrderID,
TradeInfoToOpenDEXordersParams,
};
Loading