Skip to content

Commit 5566863

Browse files
authored
Merge pull request #66 from compolabs/feat/1119-group-order
[1119] Group orders by decimal
2 parents 026c3b4 + 9a134e9 commit 5566863

File tree

4 files changed

+111
-30
lines changed

4 files changed

+111
-30
lines changed

src/entity/SpotMarketOrder.ts

+16-4
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@ export class SpotMarketOrder {
2424
readonly baseToken: Token;
2525
readonly quoteToken = TOKENS_BY_SYMBOL.USDC; // TODO: Переписать пробрасывать через аргументы;
2626
readonly trader: string;
27-
readonly baseSize: BN;
28-
readonly baseSizeUnits: BN;
29-
readonly quoteSize: BN;
30-
readonly quoteSizeUnits: BN;
3127
readonly price: BN;
3228
readonly priceUnits: BN;
3329
readonly priceScale = 1e9;
3430
readonly priceDecimals = DEFAULT_DECIMALS;
3531
readonly type: "BUY" | "SELL";
32+
baseSize: BN;
33+
baseSizeUnits: BN;
34+
quoteSize: BN;
35+
quoteSizeUnits: BN;
3636

3737
constructor(order: SpotMarketOrderParams) {
3838
this.id = order.id;
@@ -65,4 +65,16 @@ export class SpotMarketOrder {
6565
get marketSymbol() {
6666
return `${this.baseToken.symbol}-${this.quoteToken.symbol}`;
6767
}
68+
69+
addBaseSize = (amount: BN) => {
70+
this.baseSize = this.baseSize.plus(amount);
71+
72+
this.baseSizeUnits = BN.formatUnits(this.baseSize, this.baseToken.decimals);
73+
this.quoteSize = this.baseSize
74+
.abs()
75+
.times(this.price)
76+
.times(Math.pow(10, this.quoteToken.decimals))
77+
.div(Math.pow(10, this.baseToken.decimals) * this.priceScale);
78+
this.quoteSizeUnits = BN.formatUnits(this.quoteSize, this.quoteToken.decimals);
79+
};
6880
}

src/screens/TradeScreen/OrderbookAndTradesInterface/SpotOrderBook/SpotOrderBookImpl.tsx

+15-10
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ export enum SPOT_ORDER_FILTER {
3434
BUY = 2,
3535
}
3636

37-
export const SPOT_DECIMAL_OPTIONS = [2, 3, 4, 5];
37+
const SPOT_DECIMAL_OPTIONS = [0, 1, 2, 3];
3838

39-
export const SPOT_SETTINGS_ICONS = {
39+
const SPOT_SETTINGS_ICONS = {
4040
[SPOT_ORDER_FILTER.SELL_AND_BUY]: sellAndBuyIcon,
4141
[SPOT_ORDER_FILTER.SELL]: sellIcon,
4242
[SPOT_ORDER_FILTER.BUY]: buyIcon,
@@ -113,7 +113,12 @@ const SpotOrderBookImpl: React.FC<IProps> = observer(() => {
113113
);
114114
};
115115

116-
const decimals = SPOT_DECIMAL_OPTIONS[+vm.decimalKey];
116+
const indexOfDecimal = SPOT_DECIMAL_OPTIONS.indexOf(vm.decimalGroup);
117+
118+
const handleDecimalSelect = (index: string) => {
119+
const value = SPOT_DECIMAL_OPTIONS[Number(index)];
120+
vm.setDecimalGroup(value);
121+
};
117122

118123
const renderOrders = (orders: SpotMarketOrder[], type: "sell" | "buy") => {
119124
const orderMode = type === "sell" ? ORDER_MODE.BUY : ORDER_MODE.SELL;
@@ -124,9 +129,9 @@ const SpotOrderBookImpl: React.FC<IProps> = observer(() => {
124129
return orders.map((o, index) => (
125130
<OrderRow key={index + "order"} type={type} onClick={() => orderSpotVm.selectOrderbookOrder(o, orderMode)}>
126131
<VolumeBar type={type} volumePercent={volumePercent(o).times(100).toNumber()} />
127-
<Text primary>{o.baseSizeUnits.toSignificant(decimals)}</Text>
128-
<TextOverflow color={color}>{BN.formatUnits(o.price, 9).toFormat(decimals)}</TextOverflow>
129-
<Text primary>{numeral(o.quoteSizeUnits).format(`0.${"0".repeat(decimals)}a`)}</Text>
132+
<Text primary>{o.baseSizeUnits.toFormat(3)}</Text>
133+
<TextOverflow color={color}>{BN.formatUnits(o.price, 9).toFormat(vm.decimalGroup)}</TextOverflow>
134+
<Text primary>{numeral(o.quoteSizeUnits).format(`0.${"0".repeat(vm.decimalGroup)}a`)}</Text>
130135
</OrderRow>
131136
));
132137
};
@@ -139,8 +144,8 @@ const SpotOrderBookImpl: React.FC<IProps> = observer(() => {
139144
title: new BN(10).pow(-v).toString(),
140145
key: index.toString(),
141146
}))}
142-
selected={vm.decimalKey}
143-
onSelect={({ key }) => vm.setDecimalKey(key)}
147+
selected={String(indexOfDecimal)}
148+
onSelect={({ key }) => handleDecimalSelect(key)}
144149
/>
145150
{renderSettingsIcons()}
146151
</SettingsContainer>
@@ -185,10 +190,10 @@ const SpotOrderBookImpl: React.FC<IProps> = observer(() => {
185190
decimals={SPOT_DECIMAL_OPTIONS}
186191
filterIcons={Object.entries(SPOT_SETTINGS_ICONS).map(([key, value]) => value)}
187192
isOpen={isSettingsOpen}
188-
selectedDecimal={vm.decimalKey}
193+
selectedDecimal={String(indexOfDecimal)}
189194
selectedFilter={vm.orderFilter}
190195
onClose={closeSettings}
191-
onDecimalSelect={vm.setDecimalKey}
196+
onDecimalSelect={handleDecimalSelect}
192197
onFilterSelect={vm.setOrderFilter}
193198
/>
194199
</OrderbookContainer>

src/screens/TradeScreen/OrderbookAndTradesInterface/SpotOrderBook/SpotOrderbookVM.tsx

+48-16
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { DEFAULT_DECIMALS } from "@src/constants";
66
import { SpotMarketOrder } from "@src/entity";
77
import useVM from "@src/hooks/useVM";
88
import BN from "@src/utils/BN";
9+
import { groupOrders } from "@src/utils/groupOrders";
910
import { IntervalUpdater } from "@src/utils/IntervalUpdater";
1011
import { RootStore, useStores } from "@stores";
1112

@@ -26,8 +27,8 @@ export const SpotOrderbookVMProvider: React.FC<IProps> = ({ children }) => {
2627
export const useSpotOrderbookVM = () => useVM(ctx);
2728

2829
type TOrderbookData = {
29-
buy: Array<SpotMarketOrder>;
30-
sell: Array<SpotMarketOrder>;
30+
buy: SpotMarketOrder[];
31+
sell: SpotMarketOrder[];
3132
spreadPercent: string;
3233
spreadPrice: string;
3334
};
@@ -37,13 +38,16 @@ const UPDATE_INTERVAL = 2 * 1000;
3738
class SpotOrderbookVM {
3839
rootStore: RootStore;
3940

41+
allBuyOrders: SpotMarketOrder[] = [];
42+
allSellOrders: SpotMarketOrder[] = [];
43+
4044
orderbook: TOrderbookData = {
4145
buy: [],
4246
sell: [],
4347
spreadPercent: "0.00",
4448
spreadPrice: "",
4549
};
46-
decimalKey: string = "0";
50+
decimalGroup = 2;
4751
orderFilter: SPOT_ORDER_FILTER = SPOT_ORDER_FILTER.SELL_AND_BUY;
4852
amountOfOrders: number = 0;
4953

@@ -115,7 +119,17 @@ class SpotOrderbookVM {
115119

116120
setAmountOfOrders = (value: number) => (this.amountOfOrders = value);
117121

118-
setDecimalKey = (value: string) => (this.decimalKey = value);
122+
setDecimalGroup = (value: number) => {
123+
this.decimalGroup = value;
124+
125+
const buyOrdersCombinedByDecimal = groupOrders(this.allBuyOrders, value);
126+
const sellOrdersCombinedByDecimal = groupOrders(this.allSellOrders, value);
127+
128+
this.setOrderbook({
129+
buy: buyOrdersCombinedByDecimal,
130+
sell: sellOrdersCombinedByDecimal,
131+
});
132+
};
119133

120134
setOrderFilter = (value: SPOT_ORDER_FILTER) => (this.orderFilter = value);
121135

@@ -127,7 +141,7 @@ class SpotOrderbookVM {
127141
if (!this.rootStore.initialized || !market) return;
128142

129143
const bcNetwork = FuelNetwork.getInstance();
130-
const limit = 100;
144+
const limit = 200;
131145

132146
this.isOrderBookLoading = true;
133147

@@ -136,28 +150,46 @@ class SpotOrderbookVM {
136150
bcNetwork!.fetchSpotOrders({ baseToken: market.baseToken.assetId, type: "SELL", limit }),
137151
]);
138152

139-
//bid = max of buy
140-
const buyPrices = buy.map((order) => order.price);
141-
const maxBuyPrice = buyPrices.reduce((max, current) => (current.gt(max) ? current : max), buyPrices[0]);
153+
this.allBuyOrders = buy;
154+
this.allSellOrders = sell;
142155

143-
//ask = min of sell
144-
const sellPrices = sell.map((order) => order.price);
145-
const minSellPrice = sellPrices.reduce((min, current) => (current.lt(min) ? current : min), sellPrices[0]);
156+
const buyOrdersCombinedByDecimal = groupOrders(this.allBuyOrders, this.decimalGroup);
157+
const sellOrdersCombinedByDecimal = groupOrders(this.allSellOrders, this.decimalGroup);
158+
159+
const getPrice = (orders: SpotMarketOrder[], priceType: "max" | "min"): BN => {
160+
const compareType = priceType === "max" ? "gt" : "lt";
161+
return orders.reduce((value, order) => (order.price[compareType](value) ? order.price : value), orders[0].price);
162+
};
163+
164+
const maxBuyPrice = getPrice(this.allBuyOrders, "max");
165+
const minSellPrice = getPrice(this.allSellOrders, "min");
146166

147167
if (maxBuyPrice && minSellPrice) {
148168
// spread = ask - bid
149169
const spread = minSellPrice.minus(maxBuyPrice);
150170
const formattedSpread = BN.formatUnits(spread, DEFAULT_DECIMALS).toSignificant(2);
151-
152-
const spreadPercent = minSellPrice.minus(maxBuyPrice).div(maxBuyPrice).times(100);
153-
this.setOrderbook({ buy, sell, spreadPercent: spreadPercent.toFormat(2), spreadPrice: formattedSpread });
171+
const spreadPercent = spread.div(maxBuyPrice).times(100);
172+
173+
this.setOrderbook({
174+
buy: buyOrdersCombinedByDecimal,
175+
sell: sellOrdersCombinedByDecimal,
176+
spreadPercent: spreadPercent.toFormat(2),
177+
spreadPrice: formattedSpread,
178+
});
154179
this.isOrderBookLoading = false;
155180
return;
156181
}
157182

158-
this.setOrderbook({ buy, sell, spreadPercent: "0.00", spreadPrice: "0.00" });
183+
this.setOrderbook({
184+
buy: buyOrdersCombinedByDecimal,
185+
sell: sellOrdersCombinedByDecimal,
186+
spreadPercent: "0.00",
187+
spreadPrice: "0.00",
188+
});
159189
this.isOrderBookLoading = false;
160190
};
161191

162-
private setOrderbook = (orderbook: TOrderbookData) => (this.orderbook = orderbook);
192+
private setOrderbook = (orderbook: Partial<TOrderbookData>) => {
193+
this.orderbook = { ...this.orderbook, ...orderbook };
194+
};
163195
}

src/utils/groupOrders.ts

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { DEFAULT_DECIMALS } from "@src/constants";
2+
import { SpotMarketOrder } from "@src/entity";
3+
4+
import BN from "./BN";
5+
6+
const roundPrice = (price: BN, decimals: number): BN => {
7+
const factor = new BN(10).pow(decimals);
8+
return new BN(price.dividedBy(factor).integerValue(BN.ROUND_HALF_UP).multipliedBy(factor));
9+
};
10+
11+
export const groupOrders = (orders: SpotMarketOrder[], decimals: number): SpotMarketOrder[] => {
12+
const groupedOrders: { [key: string]: SpotMarketOrder } = {};
13+
14+
orders.forEach((order) => {
15+
const roundedPrice = roundPrice(order.price, DEFAULT_DECIMALS - decimals);
16+
const price = roundedPrice.toString();
17+
18+
if (!groupedOrders[price]) {
19+
groupedOrders[price] = new SpotMarketOrder({
20+
id: order.id,
21+
baseToken: order.baseToken.assetId,
22+
trader: order.trader,
23+
baseSize: BN.ZERO,
24+
orderPrice: roundedPrice,
25+
blockTimestamp: order.timestamp.unix(),
26+
});
27+
}
28+
groupedOrders[price].addBaseSize(order.baseSize);
29+
});
30+
31+
return Object.values(groupedOrders);
32+
};

0 commit comments

Comments
 (0)