Skip to content

Commit 7b2228e

Browse files
author
Karl Ranna
committed
wip: dynamic minimum quantity
1 parent 6f14f0a commit 7b2228e

File tree

6 files changed

+93
-31
lines changed

6 files changed

+93
-31
lines changed

src/arby.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ export const startArby = ({
102102
loggers.global.info('Starting. Hello, Arby.');
103103
logConfig(config, loggers.global);
104104
verifyMarkets(config, CEXmarkets);
105+
store.setMarkets(CEXmarkets);
105106
const tradeComplete$ = trade$({
106107
config,
107108
loggers,

src/centralized/minimum-order-quantity-filter.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const MINIMUM_ORDER_SIZE: MinimumCEXquantities = {
1010
ETH: new BigNumber('0.05'),
1111
DAI: new BigNumber('15'),
1212
USDT: new BigNumber('15'),
13+
USD: new BigNumber('15'),
1314
};
1415

1516
const getMinimumOrderSize = (asset: string): BigNumber => {

src/centralized/order-builder.ts

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import BigNumber from 'bignumber.js';
2-
import { merge, Observable, of } from 'rxjs';
3-
import { filter, map, mergeMap, repeat, take } from 'rxjs/operators';
2+
import { curry } from 'ramda';
3+
import { empty, merge, Observable, of } from 'rxjs';
4+
import { map, mergeMap, repeat, take } from 'rxjs/operators';
45
import { Config } from '../config';
56
import { OrderSide } from '../constants';
67
import { Logger } from '../logger';
@@ -27,9 +28,7 @@ type GetOrderBuilderParams = {
2728
accumulateOrderFillsForQuoteAssetReceived: (
2829
config: Config
2930
) => (source: Observable<SwapSuccess>) => Observable<BigNumber>;
30-
quantityAboveMinimum: (
31-
asset: string
32-
) => (filledQuantity: BigNumber) => boolean;
31+
minimumQuantity$: Observable<BigNumber>;
3332
store: ArbyStore;
3433
};
3534

@@ -38,13 +37,40 @@ type CEXorder = {
3837
side: OrderSide;
3938
};
4039

40+
const quantityAboveMinimum = curry(
41+
(
42+
store: ArbyStore,
43+
logger: Logger,
44+
assetToTradeOnCEX: string,
45+
minimumQuantity$: Observable<BigNumber>,
46+
quantity: BigNumber
47+
) => {
48+
logger.info(
49+
`Swap success. Accumulated ${assetToTradeOnCEX} quantity: ${quantity.toFixed()}`
50+
);
51+
store.resetLastOrderUpdatePrice();
52+
return minimumQuantity$.pipe(
53+
take(1),
54+
mergeMap(minimumQuantity => {
55+
if (quantity.isGreaterThanOrEqualTo(minimumQuantity)) {
56+
return of(quantity);
57+
}
58+
logger.info(
59+
`Will not execute CEX order because ${quantity.toFixed()} is below the minimum allowed CEX quantity ${minimumQuantity.toFixed()}`
60+
);
61+
return empty();
62+
})
63+
);
64+
}
65+
);
66+
4167
const getOrderBuilder$ = ({
4268
config,
4369
logger,
4470
getOpenDEXswapSuccess$,
4571
accumulateOrderFillsForBaseAssetReceived,
4672
accumulateOrderFillsForQuoteAssetReceived,
47-
quantityAboveMinimum,
73+
minimumQuantity$,
4874
store,
4975
}: GetOrderBuilderParams): Observable<CEXorder> => {
5076
const {
@@ -59,19 +85,17 @@ const getOrderBuilder$ = ({
5985
config.CEX_QUOTEASSET === 'BTC'
6086
? config.CEX_BASEASSET
6187
: config.CEX_QUOTEASSET;
88+
const filterMinimum = quantityAboveMinimum(
89+
store,
90+
logger,
91+
assetToTradeOnCEX,
92+
minimumQuantity$
93+
);
6294
const receivedQuoteAssetOrder$ = receivedQuoteAssetSwapSuccess$.pipe(
6395
// accumulate OpenDEX order fills when receiving
6496
// quote asset
6597
accumulateOrderFillsForQuoteAssetReceived(config),
66-
mergeMap((quantity: BigNumber) => {
67-
logger.info(
68-
`Swap success. Accumulated ${assetToTradeOnCEX} quantity: ${quantity.toFixed()}`
69-
);
70-
store.resetLastOrderUpdatePrice();
71-
return of(quantity);
72-
}),
73-
// filter based on minimum CEX order quantity
74-
filter(quantityAboveMinimum(assetToTradeOnCEX)),
98+
mergeMap(filterMinimum),
7599
map(quantity => {
76100
return { quantity, side: OrderSide.BUY };
77101
}),
@@ -84,15 +108,7 @@ const getOrderBuilder$ = ({
84108
// accumulate OpenDEX order fills when receiving
85109
// quote asset
86110
accumulateOrderFillsForBaseAssetReceived(config),
87-
mergeMap((quantity: BigNumber) => {
88-
logger.info(
89-
`Swap success. Accumulated ${assetToTradeOnCEX} quantity: ${quantity.toFixed()}`
90-
);
91-
store.resetLastOrderUpdatePrice();
92-
return of(quantity);
93-
}),
94-
// filter based on minimum CEX order quantity
95-
filter(quantityAboveMinimum(assetToTradeOnCEX)),
111+
mergeMap(filterMinimum),
96112
map(quantity => {
97113
return { quantity, side: OrderSide.SELL };
98114
}),

src/centralized/order.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import BigNumber from 'bignumber.js';
2-
import { Exchange } from 'ccxt';
3-
import { Observable, timer } from 'rxjs';
2+
import { Dictionary, Exchange, Market } from 'ccxt';
3+
import { combineLatest, Observable, timer } from 'rxjs';
44
import {
55
catchError,
66
mergeMap,
77
mergeMapTo,
88
withLatestFrom,
9+
map,
910
} from 'rxjs/operators';
11+
import { ArbyStore } from 'src/store';
1012
import { Config } from '../config';
1113
import { Logger } from '../logger';
1214
import { getOpenDEXswapSuccess$ } from '../opendex/swap-success';
@@ -16,9 +18,7 @@ import {
1618
} from '../trade/accumulate-fills';
1719
import { createOrder$ } from './ccxt/create-order';
1820
import { ExecuteCEXorderParams } from './execute-order';
19-
import { quantityAboveMinimum } from './minimum-order-quantity-filter';
2021
import { CEXorder, GetOrderBuilderParams } from './order-builder';
21-
import { ArbyStore } from 'src/store';
2222

2323
type GetCentralizedExchangeOrderParams = {
2424
CEX: Exchange;
@@ -34,7 +34,7 @@ type GetCentralizedExchangeOrderParams = {
3434
getOpenDEXswapSuccess$,
3535
accumulateOrderFillsForBaseAssetReceived,
3636
accumulateOrderFillsForQuoteAssetReceived,
37-
quantityAboveMinimum,
37+
minimumQuantity$,
3838
}: GetOrderBuilderParams) => Observable<CEXorder>;
3939
centralizedExchangePrice$: Observable<BigNumber>;
4040
deriveCEXorderQuantity: (
@@ -55,13 +55,28 @@ const getCentralizedExchangeOrder$ = ({
5555
deriveCEXorderQuantity,
5656
store,
5757
}: GetCentralizedExchangeOrderParams): Observable<null> => {
58+
const minimumBaseAssetQuantity$ = (store.selectState(
59+
'markets'
60+
) as Observable<Dictionary<Market>>).pipe(
61+
map(markets => {
62+
const tradingPair = `${config.CEX_BASEASSET}/${config.CEX_QUOTEASSET}`;
63+
const market = markets[tradingPair];
64+
return new BigNumber(market.limits.amount.min);
65+
})
66+
);
67+
const minimumQuantity$ = combineLatest([minimumBaseAssetQuantity$, centralizedExchangePrice$]).pipe(
68+
map(([minimumQuantity, price]) => {
69+
// TODO: only if BTC is baseAsset
70+
return minimumQuantity.multipliedBy(price);
71+
}),
72+
);
5873
return getOrderBuilder$({
5974
config,
6075
logger,
6176
getOpenDEXswapSuccess$,
6277
accumulateOrderFillsForBaseAssetReceived,
6378
accumulateOrderFillsForQuoteAssetReceived,
64-
quantityAboveMinimum,
79+
minimumQuantity$,
6580
store,
6681
}).pipe(
6782
withLatestFrom(

src/store.spec.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import BigNumber from 'bignumber.js';
2+
import { Dictionary, Market } from 'ccxt';
23
import { getArbyStore } from './store';
34

45
describe('ArbyStore', () => {
@@ -18,6 +19,24 @@ describe('ArbyStore', () => {
1819
});
1920
});
2021

22+
it('selectState returns empty initial markets', done => {
23+
const { selectState } = getArbyStore();
24+
selectState('markets').subscribe(markets => {
25+
expect(markets).toEqual({});
26+
done();
27+
});
28+
});
29+
30+
it('selectState returns updated markets', done => {
31+
const { selectState, setMarkets } = getArbyStore();
32+
const testMarkets = ({ 'BTC/USDT': true } as unknown) as Dictionary<Market>;
33+
setMarkets(testMarkets);
34+
selectState('markets').subscribe(markets => {
35+
expect(markets).toEqual(testMarkets);
36+
done();
37+
});
38+
});
39+
2140
it('selectState returns updated last sell order price', done => {
2241
const { selectState, updateLastSellOrderUpdatePrice } = getArbyStore();
2342
const updatedPrice = new BigNumber('123');

src/store.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
import BigNumber from 'bignumber.js';
22
import { BehaviorSubject, Subject, Observable } from 'rxjs';
33
import { scan, pluck, distinctUntilKeyChanged } from 'rxjs/operators';
4+
import { Dictionary, Market } from 'ccxt';
45

56
type ArbyStore = {
67
updateLastSellOrderUpdatePrice: (price: BigNumber) => void;
78
updateLastBuyOrderUpdatePrice: (price: BigNumber) => void;
89
resetLastOrderUpdatePrice: () => void;
9-
selectState: (stateKey: ArbyStoreDataKeys) => Observable<BigNumber>;
10+
setMarkets: (markets: Dictionary<Market>) => void;
11+
selectState: (stateKey: ArbyStoreDataKeys) => Observable<BigNumber | Dictionary<Market>>;
1012
stateChanges: () => Observable<ArbyStoreData>;
1113
};
1214

1315
type ArbyStoreData = {
1416
lastSellOrderUpdatePrice: BigNumber;
1517
lastBuyOrderUpdatePrice: BigNumber;
18+
markets: Dictionary<Market>;
1619
};
1720

1821
type ArbyStoreDataKeys = keyof ArbyStoreData;
@@ -21,6 +24,7 @@ const getArbyStore = (): ArbyStore => {
2124
const initialState: ArbyStoreData = {
2225
lastSellOrderUpdatePrice: new BigNumber('0'),
2326
lastBuyOrderUpdatePrice: new BigNumber('0'),
27+
markets: {},
2428
};
2529
const store = new BehaviorSubject(initialState);
2630
const stateUpdates = new Subject() as Subject<Partial<ArbyStoreData>>;
@@ -47,6 +51,11 @@ const getArbyStore = (): ArbyStore => {
4751
lastBuyOrderUpdatePrice: new BigNumber('0'),
4852
});
4953
};
54+
const setMarkets = (markets: Dictionary<Market>) => {
55+
stateUpdates.next({
56+
markets,
57+
});
58+
};
5059
const selectState = (stateKey: ArbyStoreDataKeys) => {
5160
return store.pipe(distinctUntilKeyChanged(stateKey), pluck(stateKey));
5261
};
@@ -59,6 +68,7 @@ const getArbyStore = (): ArbyStore => {
5968
selectState,
6069
resetLastOrderUpdatePrice,
6170
stateChanges,
71+
setMarkets,
6272
};
6373
};
6474

0 commit comments

Comments
 (0)