Skip to content

Commit

Permalink
Merge branch 'pair-selector-improvements' of https://github.com/nguvi…
Browse files Browse the repository at this point in the history
…ctor/dexter into pair-selector-improvements
  • Loading branch information
Victor committed Oct 27, 2023
2 parents 09ad290 + 311276a commit 9032266
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 33 deletions.
31 changes: 18 additions & 13 deletions __tests__/orderBookSlice.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,24 @@ import {
} from "../src/app/redux/orderBookSlice";

const MOCK_SELLS = [
new OrderbookLine(25, 1, 1, 1, 1, false),
new OrderbookLine(24, 1, 1, 1, 1, false),
new OrderbookLine(23, 1, 1, 1, 1, false),
new OrderbookLine(22, 1, 1, 1, 1, false),
new OrderbookLine(21, 1, 1, 1, 1, false),
new OrderbookLine(20, 1, 1, 1, 1, false),
new OrderbookLine(19, 1, 1, 1, 1, false),
new OrderbookLine(18, 1, 1, 1, 1, false),
new OrderbookLine(17, 1, 1, 1, 1, false),
new OrderbookLine(16, 1, 1, 1, 1, false),
new OrderbookLine(15, 1, 1, 1, 1, false),
new OrderbookLine(14, 1, 1, 1, 1, false),
new OrderbookLine(13, 1, 1, 1, 1, false),
new OrderbookLine(12, 1, 1, 1, 1, false),
new OrderbookLine(11, 1, 1, 1, 1, false),
];

const MOCK_BUYS = [
new OrderbookLine(13, 1, 1, 1, 1, false),
new OrderbookLine(12, 1, 1, 1, 1, false),
new OrderbookLine(11, 1, 1, 1, 1, false),
new OrderbookLine(10, 1, 1, 1, 1, false),
new OrderbookLine(9, 1, 1, 1, 1, false),
new OrderbookLine(8, 1, 1, 1, 1, false),
Expand All @@ -31,24 +36,24 @@ const MOCK_BUYS = [
];

describe("toOrderBookRowProps", () => {
it("returns 8 rows if the input is empty", () => {
it("returns 11 rows if the input is empty", () => {
const input: OrderbookLine[] = [];

expect(toOrderBookRowProps(input, "sell").length).toBe(8);
expect(toOrderBookRowProps(input, "sell").length).toBe(11);
});

it("returns 8 rows if the input is smaller", () => {
it("returns 11 rows if the input is smaller", () => {
const input: OrderbookLine[] = MOCK_SELLS.slice(0, 5);

expect(toOrderBookRowProps(input, "sell").length).toBe(8);
expect(toOrderBookRowProps(input, "sell").length).toBe(11);
});

it("returns 8 rows if the input is larger", () => {
expect(toOrderBookRowProps(MOCK_SELLS, "sell").length).toBe(8);
it("returns 11 rows if the input is larger", () => {
expect(toOrderBookRowProps(MOCK_SELLS, "sell").length).toBe(11);
});

it("drops the correct farther away rows for sells", () => {
const expectedSellPrices = [18, 17, 16, 15, 14, 13, 12, 11];
const expectedSellPrices = [24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14];

const sellRowProps = toOrderBookRowProps(MOCK_SELLS, "sell");

Expand All @@ -58,7 +63,7 @@ describe("toOrderBookRowProps", () => {
});

it("drops the correct farther away rows for buys", () => {
const expectedBuyPrices = [10, 9, 8, 7, 6, 5, 4, 3];
const expectedBuyPrices = [13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3];

const buyRowProps = toOrderBookRowProps(MOCK_BUYS, "buy");

Expand All @@ -68,8 +73,8 @@ describe("toOrderBookRowProps", () => {
});

it("calculates correct totals", () => {
const expectedSellTotals = [8, 7, 6, 5, 4, 3, 2, 1];
const expectedBuyTotals = [1, 2, 3, 4, 5, 6, 7, 8];
const expectedSellTotals = [11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1];
const expectedBuyTotals = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];

const sellRowProps = toOrderBookRowProps(MOCK_SELLS, "sell");
const buyRowProps = toOrderBookRowProps(MOCK_BUYS, "buy");
Expand Down
24 changes: 20 additions & 4 deletions src/app/components/OrderBook.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { CSSProperties } from "react";

import { useAppSelector } from "../hooks";
import { OrderBookRowProps } from "../redux/orderBookSlice";
import "../styles/orderbook.css";
import * as utils from "../utils";
import { OrderBookRowProps, orderBookSlice } from "../redux/orderBookSlice";
import { useAppDispatch, useAppSelector } from "../hooks";

function OrderBookRow(props: OrderBookRowProps) {
const { barColor, orderCount, price, size, total, maxTotal } = props;
Expand Down Expand Up @@ -104,6 +104,7 @@ function CurrentPriceRow() {
}

export function OrderBook() {
const dispatch = useAppDispatch();
const token1Symbol = useAppSelector(
(state) => state.pairSelector.token1.symbol
);
Expand All @@ -112,11 +113,25 @@ export function OrderBook() {
);
const sells = useAppSelector((state) => state.orderBook.sells);
const buys = useAppSelector((state) => state.orderBook.buys);
//const grouping = useAppSelector((state) => state.orderBook.grouping);

return (
<div className="p-2 text-sx text-primary-content">
<div className="grid grid-cols-2 m-1 text-secondary-content font-bold text-sm uppercase">
<div className="justify-self-start">Order book</div>
<div className="flex justify-end join">
<span className="join-item mr-2">Grouping </span>
<input
className="input-xs w-16 join-item"
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
const grouping = Number(event.target.value);
dispatch(orderBookSlice.actions.setGrouping(grouping));
}}
></input>
</div>
</div>
<div className="sized-columns">
<div className="sized-columns mx-2 col-span-4 text-sm text-secondary-content">
<div className="sized-columns mx-2 col-span-4 text-sm font-bold text-secondary-content">
<div className="text-start">
Order
<br />
Expand All @@ -135,7 +150,8 @@ export function OrderBook() {
<br />({token1Symbol})
</div>
</div>

</div>
<div className="sized-columns mx-2 col-span-4 text-sm">
{sells.map((props, index) => (
<OrderBookRow key={"sell-" + index} {...props} />
))}
Expand Down
8 changes: 4 additions & 4 deletions src/app/components/order_input/LimitOrderInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import { AiOutlineInfoCircle } from "react-icons/ai";
import { BottomRightErrorLabel } from "components/BottomRightErrorLabel";

const POST_ONLY_TOOLTIP =
"If your price is very close to the current market price, your limit order might fill immediately, " +
"making you pay taker fees. This option prevents your order from being executed immediately, " +
"guarantees that your order will make it to the order book and you will earn the liquidity provider fees.";
"Select 'POST ONLY' to ensure your order is added to the order book without matching existing orders. " +
"If your order can be partially or completely executed immediately, it will not be created. " +
"This option removes trading fees completely and ensures you receive the maker rebate.";

function NonTargetToken() {
const { token2, validationToken2, side } = useAppSelector(
Expand Down Expand Up @@ -246,7 +246,7 @@ export function LimitOrderInput() {
POST ONLY
</span>
<div
className="my-auto ml-2 tooltip z-10"
className="my-auto ml-2 tooltip before:bg-base-300 z-10"
data-tip={POST_ONLY_TOOLTIP}
>
<AiOutlineInfoCircle className="text-secondary-content text-sm" />
Expand Down
94 changes: 83 additions & 11 deletions src/app/redux/orderBookSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface OrderBookState {
bestBuy: number | null;
spread: number | null;
spreadPercent: number | null;
grouping: number | null;
}

const initialState: OrderBookState = {
Expand All @@ -30,15 +31,14 @@ const initialState: OrderBookState = {
bestBuy: null,
spread: null,
spreadPercent: null,
grouping: 0,
};

export function toOrderBookRowProps(
adexOrderbookLines: adex.OrderbookLine[],
side: "sell" | "buy"
side: "sell" | "buy",
grouping: number
): OrderBookRowProps[] {
// this will drop the rows that do not fit into 8 buys/sells
// TODO: implement pagination or scrolling

const props: OrderBookRowProps[] = [];
let adexRows = [...adexOrderbookLines]; // copy the array so we can mutate it

Expand All @@ -47,8 +47,39 @@ export function toOrderBookRowProps(
adexRows.reverse();
barColor = "hsl(var(--erc))";
}
adexRows = adexRows.slice(0, 8); // Limit to 8 rows

let groupedArray;
if (grouping > 0) {
const roundToGroup = (num: number) => Math.round(num / grouping) * grouping;

if (adexRows.length > 0) {
groupedArray = adexRows.reduce((result: adex.OrderbookLine[], item) => {
const key = roundToGroup(item.price);
const foundItem = result.find(
(x: adex.OrderbookLine) => x.price === key
); // note that we don't specify the type here
let existingItem: adex.OrderbookLine; // declare the variable without assigning it
if (foundItem !== undefined) {
// if the foundItem is not undefined, we can assign it safely
existingItem = foundItem;
existingItem.quantityRemaining += item.quantityRemaining;
existingItem.valueRemaining += item.valueRemaining;
existingItem.noOrders += item.noOrders;
existingItem.orders = existingItem.orders.concat(item.orders);
existingItem.total += item.total;
} else {
// If it's a new price, add it to the result
item.price = key;
result.push({ ...item });
}
return result;
}, []);
}
}
if (groupedArray != null) {
adexRows = groupedArray.slice(0, 11); //adexRows.slice(0, 11); // Limit to 11 rows
} else {
adexRows = adexRows.slice(0, 11);
}
let total = 0;
let maxTotal = 0;
for (let adexRow of adexRows) {
Expand All @@ -69,8 +100,8 @@ export function toOrderBookRowProps(
props[i].maxTotal = maxTotal;
}

// If there are fewer than 8 orders, fill the remaining rows with empty values
while (props.length < 8) {
// If there are fewer than 11 orders, fill the remaining rows with empty values
while (props.length < 11) {
props.push({ absentOrders: "\u00A0" });
}

Expand All @@ -93,15 +124,18 @@ export const orderBookSlice = createSlice({
reducers: {
updateAdex(state: OrderBookState, action: PayloadAction<adex.StaticState>) {
const adexState = action.payload;
const grouping = state.grouping;

const sells = toOrderBookRowProps(
adexState.currentPairOrderbook.sells,
"sell"
"sell",
grouping || 0
);
const buys = toOrderBookRowProps(
adexState.currentPairOrderbook.buys,
"buy"
"buy",
grouping || 0
);

let bestSell = sells[sells.length - 1]?.price || null;
let bestBuy = buys[0]?.price || null;

Expand All @@ -120,6 +154,44 @@ export const orderBookSlice = createSlice({
state.bestSell = bestSell;
state.bestBuy = bestBuy;
},
setGrouping(state, action: PayloadAction<number>) {
if (action.payload === null) {
state.grouping = 0;
} else if (action.payload < 0) {
state.grouping = 0;
} else {
state.grouping = action.payload;
}

const sells = toOrderBookRowProps(
adex.clientState.currentPairOrderbook.sells,
"sell",
state.grouping || 0
);
const buys = toOrderBookRowProps(
adex.clientState.currentPairOrderbook.buys,
"buy",
state.grouping || 0
);

let bestSell = sells[sells.length - 1]?.price || null;
let bestBuy = buys[0]?.price || null;

if (bestBuy !== null && bestSell !== null) {
const spread = bestSell - bestBuy;
if (bestBuy + bestSell !== 0) {
const spreadPercent = ((2 * spread) / (bestBuy + bestSell)) * 100;
state.spreadPercent = spreadPercent;
}
state.spread = spread;
}

state.sells = sells;
state.buys = buys;
state.lastPrice = adex.clientState.currentPairInfo.lastPrice;
state.bestSell = bestSell;
state.bestBuy = bestBuy;
},
},
});

Expand Down
2 changes: 1 addition & 1 deletion src/app/redux/orderInputSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export const initialState: OrderInputState = {
token2: initialTokenInput,
validationToken2: initialValidationResult,
tab: OrderTab.MARKET,
postOnly: true,
postOnly: false,
side: adex.OrderSide.BUY,
price: 0,
slippage: 0.01,
Expand Down

0 comments on commit 9032266

Please sign in to comment.