I am kind of stuck on how to achieve my desired output.
I just want a bit of help on how should I approach this problem, as I am a junior dev trying to build out a project and I feel that I am overcomplicating things here and still not getting what I want.
The problem:
I have a large array of object, here is a chunk of them:
const trades: Trade[] = [
{ side: 'BUY', asset: 'ETH', quantity: 50, price: 1800 },
{ side: 'BUY', asset: 'ETH', quantity: 20, price: 1850 },
{ side: 'SELL', asset: 'BTC', quantity: 25, price: 36800 },
{ side: 'SELL', asset: 'ETH', quantity: 5, price: 1900 },
{ side: 'BUY', asset: 'BTC', quantity: 10, price: 37000 },
{ side: 'SELL', asset: 'ETH', quantity: 40, price: 1700 },
{ side: 'SELL', asset: 'ETH', quantity: 25, price: 1750 },
{ side: 'BUY', asset: 'BTC', quantity: 15, price: 35000 },
{ side: 'BUY', asset: 'BTC', quantity: 5, price: 39000 },
{ side: 'SELL', asset: 'BTC', quantity: 5, price: 38500 },
{ side: 'SELL', asset: 'ETH', quantity: 80, price: 1350 },
];
interfaces:
interface OriginTrade {
side: 'BUY' | 'SELL';
asset: string;
quantity: number;
remainingQuantity: number;
linkedTrades: MatchedTrade[];
}
interface MatchedTrade {
trade: Trade;
remainingQuantity: number;
index: number;
}
interface Trade {
side: 'BUY' | 'SELL';
asset: string;
quantity: number;
price: number;
}
The code itself:
const originTrades: OriginTrade[] = trades.reduce<OriginTrade[]>(
(acc, curr, i) => {
const { side, asset, quantity } = curr;
if (!acc[asset]) {
const originQuantity = side === 'SELL' ? -quantity : quantity; // transform quantity into negative if side is SELL
acc[asset] = curr; // If there is no existing origin trade for this asset, set the current trade as the origin trade
// and push the trades into the array
acc.push({
side: side,
asset: asset,
quantity: originQuantity,
linkedTrades: [],
remainingQuantity: originQuantity,
});
} else {
// if there is an existing previous trade get it and also the side
// const existingTrade = acc[asset];
const existingTrade = acc.find((trade) => trade.asset === asset)!;
// set the math operator based on the side
const operator = curr.side === 'BUY' ? '+' : '-';
// then use that operator to calculate remaining quantity
const calculateRemainingQuantity = eval(existingTrade.remainingQuantity + operator + quantity);
// set the quantity of the current trade based on the side
curr.quantity = curr.side === 'SELL' ? -curr.quantity : curr.quantity;
// const existingTradeSide = existingTrade.side;
existingTrade.linkedTrades.push({ trade: curr, remainingQuantity: calculateRemainingQuantity, index: existingTrade.linkedTrades.length });
existingTrade.remainingQuantity = calculateRemainingQuantity;
}
return acc;
},
[]
);
console.log('actuall output', originTrades);
What I want to achieve is the following:
- if there is no ‘originTrade’, then assign the first of its kind a an origin trade
- in the originTrade for the given asset, add all the upcoming trades into the linkedTrades[], IF the quantity (or balance) is above 0 (therefore we know that the position is not fully closed yet)
- when goes to 0 or is below 0, then create another ‘originTrade’ object and assign all the upcoming trades into the linkedTrades[].
What part I am struggling with:
- I have achieved assigning the trades into the initial originTrade and linked all the individual trades into the linkedTrades for that given asset using the reduce, but I am running short on ideas how to achieve that when the remainingQuantity for that given originTrade hits 0 or below 0, then create another originTrade and start linking the trades into that new one.
I feel that I am overcomplicating the code at this point.
Any help would be really appreciated!
Here is the expected output I want to achieve:
[
{
"side": "BUY",
"asset": "ETH",
"quantity": 50,
"linkedTrades": [
{
"trade": {
"side": "BUY",
"asset": "ETH",
"quantity": 20,
"price": 1850
},
"remainingQuantity": 70,
"index": 0
},
{
"trade": {
"side": "SELL",
"asset": "ETH",
"quantity": -5,
"price": 1900
},
"remainingQuantity": 65,
"index": 1
},
{
"trade": {
"side": "SELL",
"asset": "ETH",
"quantity": -40,
"price": 1700
},
"remainingQuantity": 25,
"index": 2
},
{
"trade": {
"side": "SELL",
"asset": "ETH",
"quantity": -25,
"price": 1750
},
"remainingQuantity": 0,
"index": 3
}
],
"remainingQuantity": 0
},
{
"side": "SELL",
"asset": "BTC",
"quantity": -25,
"linkedTrades": [
{
"trade": {
"side": "BUY",
"asset": "BTC",
"quantity": 10,
"price": 37000
},
"remainingQuantity": -15,
"index": 0
},
{
"trade": {
"side": "BUY",
"asset": "BTC",
"quantity": 15,
"price": 35000
},
"remainingQuantity": 0,
"index": 1
}
],
"remainingQuantity": 0
},
{
"side": "SELL",
"asset": "ETH",
"quantity": -80,
"linkedTrades": [],
"remainingQuantity": -80
},
{
"side": "BUY",
"asset": "BTC",
"quantity": 5,
"linkedTrades": [
{
"trade": {
"side": "SELL",
"asset": "BTC",
"quantity": -5,
"price": 38500
},
"remainingQuantity": 0,
"index": 3
}
],
"remainingQuantity": 0
}
]
And here is the output I am getting now:
[
{
"side": "BUY",
"asset": "ETH",
"quantity": 50,
"linkedTrades": [
{
"trade": {
"side": "BUY",
"asset": "ETH",
"quantity": 20,
"price": 1850
},
"remainingQuantity": 70,
"index": 0
},
{
"trade": {
"side": "SELL",
"asset": "ETH",
"quantity": -5,
"price": 1900
},
"remainingQuantity": 65,
"index": 1
},
{
"trade": {
"side": "SELL",
"asset": "ETH",
"quantity": -40,
"price": 1700
},
"remainingQuantity": 25,
"index": 2
},
{
"trade": {
"side": "SELL",
"asset": "ETH",
"quantity": -25,
"price": 1750
},
"remainingQuantity": 0,
"index": 3
},
{
"trade": {
"side": "SELL",
"asset": "ETH",
"quantity": -80,
"price": 1350
},
"remainingQuantity": -80,
"index": 4
}
],
"remainingQuantity": -80
},
{
"side": "SELL",
"asset": "BTC",
"quantity": -25,
"linkedTrades": [
{
"trade": {
"side": "BUY",
"asset": "BTC",
"quantity": 10,
"price": 37000
},
"remainingQuantity": -15,
"index": 0
},
{
"trade": {
"side": "BUY",
"asset": "BTC",
"quantity": 15,
"price": 35000
},
"remainingQuantity": 0,
"index": 1
},
{
"trade": {
"side": "BUY",
"asset": "BTC",
"quantity": 5,
"price": 39000
},
"remainingQuantity": 5,
"index": 2
},
{
"trade": {
"side": "SELL",
"asset": "BTC",
"quantity": -5,
"price": 38500
},
"remainingQuantity": 0,
"index": 3
}
],
"remainingQuantity": 0
}
]
The whole code
HERE IN TS: # https://playcode.io/1450531
Here using JS editor in SO:
const trades = [
{ side: 'BUY', asset: 'ETH', quantity: 50, price: 1800 }, // ORIGIN TRADE n1
{ side: 'BUY', asset: 'ETH', quantity: 20, price: 1850 }, // LINKED TO ORIGIN TRADE n1
{ side: 'SELL', asset: 'BTC', quantity: 25, price: 36800 }, // ORIGIN TRADE n2
{ side: 'SELL', asset: 'ETH', quantity: 5, price: 1900 }, // LINKED TO ORIGIN TRADE n1
{ side: 'BUY', asset: 'BTC', quantity: 10, price: 37000 }, // LINKED TO ORIGIN TRADE n2
{ side: 'SELL', asset: 'ETH', quantity: 40, price: 1700 }, // LINKED TO ORIGIN TRADE n1
{ side: 'SELL', asset: 'ETH', quantity: 25, price: 1750 }, // LINKED TO ORIGIN TRADE n1
{ side: 'BUY', asset: 'BTC', quantity: 15, price: 35000 }, // LINKED TO ORIGIN TRADE n2
{ side: 'BUY', asset: 'BTC', quantity: 5, price: 39000 }, // ORIGIN TRADE n3
{ side: 'SELL', asset: 'BTC', quantity: 5, price: 38500 },// LINKED TO ORIGIN TRADE n3
{ side: 'SELL', asset: 'ETH', quantity: 80, price: 1350 }, // ORIGIN TRADE n4
];
const originTrades = trades.reduce(
(acc, curr, i) => {
const { side, asset, quantity } = curr;
if (!acc[asset]) {
const originQuantity = side === 'SELL' ? -quantity : quantity; // transform quantity into negative if side is SELL
acc[asset] = curr; // If there is no existing origin trade for this asset, set the current trade as the origin trade
// and push the trades into the array
acc.push({
side: side,
asset: asset,
quantity: originQuantity,
linkedTrades: [],
remainingQuantity: originQuantity,
});
} else {
// if there is an existing previous trade get it and also the side
// const existingTrade = acc[asset];
const existingTrade = acc.find((trade) => trade.asset === asset);
// set the math operator based on the side
const operator = curr.side === 'BUY' ? '+' : '-';
// then use that operator to calculate remaining quantity
const calculateRemainingQuantity = eval(existingTrade.remainingQuantity + operator + quantity);
// set the quantity of the current trade based on the side
curr.quantity = curr.side === 'SELL' ? -curr.quantity : curr.quantity;
// const existingTradeSide = existingTrade.side;
existingTrade.linkedTrades.push({ trade: curr, remainingQuantity: calculateRemainingQuantity, index: existingTrade.linkedTrades.length });
existingTrade.remainingQuantity = calculateRemainingQuantity;
}
return acc;
},
[]
);
console.log('actuall output', originTrades);
const expectedOutput = [
{
"side": "BUY",
"asset": "ETH",
"quantity": 50,
"linkedTrades": [
{
"trade": {
"side": "BUY",
"asset": "ETH",
"quantity": 20,
"price": 1850
},
"remainingQuantity": 70,
"index": 0
},
{
"trade": {
"side": "SELL",
"asset": "ETH",
"quantity": -5,
"price": 1900
},
"remainingQuantity": 65,
"index": 1
},
{
"trade": {
"side": "SELL",
"asset": "ETH",
"quantity": -40,
"price": 1700
},
"remainingQuantity": 25,
"index": 2
},
{
"trade": {
"side": "SELL",
"asset": "ETH",
"quantity": -25,
"price": 1750
},
"remainingQuantity": 0,
"index": 3
}
],
"remainingQuantity": 0
},
{
"side": "SELL",
"asset": "BTC",
"quantity": -25,
"linkedTrades": [
{
"trade": {
"side": "BUY",
"asset": "BTC",
"quantity": 10,
"price": 37000
},
"remainingQuantity": -15,
"index": 0
},
{
"trade": {
"side": "BUY",
"asset": "BTC",
"quantity": 15,
"price": 35000
},
"remainingQuantity": 0,
"index": 1
}
],
"remainingQuantity": 0
},
{
"side": "SELL",
"asset": "ETH",
"quantity": -80,
"linkedTrades": [],
"remainingQuantity": -80
},
{
"side": "BUY",
"asset": "BTC",
"quantity": 5,
"linkedTrades": [
{
"trade": {
"side": "SELL",
"asset": "BTC",
"quantity": -5,
"price": 38500
},
"remainingQuantity": 0,
"index": 3
}
],
"remainingQuantity": 0
}
]
console.log('expected output', expectedOutput);
I also added an ‘expectedOutput’ JSON there at the end and console logged it.
Thank you!