Skip to content

Commit a2920e3

Browse files
committed
Add market
1 parent 6ddf1ff commit a2920e3

File tree

5 files changed

+183
-40
lines changed

5 files changed

+183
-40
lines changed

package-lock.json

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"dependencies": {
1212
"discord.js": "^12.5.1",
1313
"node-fetch": "^2.6.1",
14+
"tinyqueue": "^2.0.3",
1415
"uuid": "^8.3.2"
1516
},
1617
"devDependencies": {

src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
setUser,
1515
startDBWorker,
1616
} from "./db";
17-
import { buy, getAmount, getYield, sell } from "./user";
17+
import { buy, sell } from "./user";
1818

1919
const client = new Discord.Client();
2020

src/market.ts

+163
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import TinyQueue from "tinyqueue";
2+
3+
export interface BuyTradeRequest {
4+
userID: string;
5+
time: number;
6+
price: number;
7+
amount: number;
8+
type: 'buy';
9+
}
10+
11+
export interface SellTradeRequest {
12+
userID: string;
13+
time: number;
14+
price: number;
15+
amount: number;
16+
stocks: Stock[];
17+
type: 'sell';
18+
}
19+
20+
export interface Stock {
21+
price: number;
22+
amount: number;
23+
}
24+
25+
export type TradeRequest = SellTradeRequest | BuyTradeRequest;
26+
27+
interface MarketState {
28+
reqs: TradeRequest[];
29+
}
30+
31+
interface Trade {
32+
buyer: string;
33+
seller: string;
34+
buyerGain: number;
35+
sellerYield: number;
36+
price: number;
37+
amount: number;
38+
}
39+
40+
var state: MarketState = {
41+
reqs: []
42+
};
43+
44+
function reduceStock(stocks: Stock[], amount: number) {
45+
var rem = amount;
46+
const out = [];
47+
while (rem > 0 || stocks.length === 0) {
48+
const take = Math.min(stocks[0].amount, rem);
49+
if (take === stocks[0].amount) {
50+
stocks.shift();
51+
out.push(Object.assign({}, stocks[0]));
52+
} else {
53+
stocks[0].amount -= take;
54+
out.push({ price: stocks[0].price, amount: take });
55+
}
56+
rem -= take;
57+
}
58+
return out;
59+
}
60+
61+
function calculateYield(stocks: Stock[], price: number) {
62+
if (!stocks || stocks.length === 0) {
63+
return 0;
64+
}
65+
const weighted = stocks
66+
.map((index) => {
67+
const neww = index.amount * price;
68+
const orii = index.amount * index.price;
69+
return (index.amount * (neww - orii)) / orii * 100.0;
70+
})
71+
.reduce((x, y) => x + y);
72+
const total = stocks
73+
.map((index) => index.amount)
74+
.reduce((x, y) => x + y);
75+
return weighted / total;
76+
}
77+
78+
export function processTradeRequests() {
79+
const buyQueue = new TinyQueue([], (a, b) => (a.price - b.price));
80+
const sellQueue = new TinyQueue([], (a, b) => (b.price - a.price));
81+
state.reqs.filter(req => req.type === 'sell').forEach(req => sellQueue.push(req));
82+
state.reqs.filter(req => req.type === 'buy').forEach(req => buyQueue.push(req));
83+
84+
const dones: Trade[] = [];
85+
for (const req of state.reqs) {
86+
if (req.amount <= 0) continue;
87+
if (req.type === 'buy') {
88+
while (req.amount > 0) {
89+
if (sellQueue.length === 0) break;
90+
const selling = sellQueue.peek();
91+
if (selling.amount === 0) {
92+
sellQueue.pop();
93+
continue;
94+
}
95+
if (req.price < selling.price) break;
96+
97+
const doneAmount = Math.min(req.amount, selling.amount);
98+
const donePrice = selling.price;
99+
var sellerYield = 0;
100+
if (req.amount >= selling.amount) {
101+
selling.amount = 0;
102+
sellerYield = calculateYield(selling.stocks, donePrice);
103+
req.amount -= selling.amount;
104+
sellQueue.pop();
105+
} else {
106+
req.amount = 0;
107+
const rr = reduceStock(selling.stocks, doneAmount);
108+
sellerYield = calculateYield(rr, donePrice);
109+
selling.amount -= req.amount;
110+
selling.stocks = [];
111+
}
112+
dones.push({
113+
buyer: req.userID,
114+
seller: selling.userID,
115+
buyerGain: (req.price - donePrice) * doneAmount,
116+
sellerYield: sellerYield,
117+
price: donePrice,
118+
amount: doneAmount
119+
});
120+
}
121+
} else {
122+
while (req.amount > 0) {
123+
if (buyQueue.length === 0) break;
124+
const buying = buyQueue.peek();
125+
if (buying.amount === 0) {
126+
buyQueue.pop();
127+
continue;
128+
}
129+
if (req.price > buying.price) break;
130+
131+
const doneAmount = Math.min(req.amount, buying.amount);
132+
const donePrice = buying.price;
133+
var sellerYield = 0;
134+
if (req.amount >= buying.amount) {
135+
buying.amount = 0;
136+
req.amount -= buying.amount;
137+
const rr = reduceStock(req.stocks, doneAmount);
138+
sellerYield = calculateYield(rr, donePrice);
139+
buyQueue.pop();
140+
} else {
141+
req.amount = 0;
142+
buying.amount -= req.amount;
143+
sellerYield = calculateYield(req.stocks, donePrice);
144+
req.stocks = [];
145+
}
146+
dones.push({
147+
buyer: buying.userID,
148+
seller: req.userID,
149+
buyerGain: 0,
150+
sellerYield: sellerYield,
151+
price: donePrice,
152+
amount: doneAmount
153+
});
154+
}
155+
}
156+
}
157+
state.reqs = state.reqs.filter(req => req.amount !== 0);
158+
return dones;
159+
}
160+
161+
function system() {
162+
163+
}

src/user.ts

+13-39
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,25 @@
1-
interface Index {
2-
price: number;
3-
amount: number;
4-
}
1+
import { Stock } from "./market";
52

63
interface User {
74
money: number;
8-
indices?: Index[];
9-
}
10-
11-
export function getYield(user: User, currentPrice) {
12-
if (!user.indices || user.indices.length === 0) {
13-
return 0;
14-
}
15-
const weighted = user.indices
16-
.map((index) => {
17-
const neww = index.amount * currentPrice;
18-
const orii = index.amount * index.price;
19-
return (index.amount * (neww - orii)) / orii * 100.0;
20-
})
21-
.reduce((x, y) => x + y);
22-
const total = user.indices
23-
.map((index) => index.amount)
24-
.reduce((x, y) => x + y);
25-
return weighted / total;
5+
stocks?: Stock[];
266
}
277

28-
export function getAmount(user: User) {
29-
if (!user.indices || user.indices.length === 0) {
30-
return 0;
31-
}
32-
return user.indices.map((index) => index.amount).reduce((x, y) => x + y);
33-
}
348

359
export function sell(user: User, amount, currentPrice) {
36-
if (!user.indices) {
37-
user.indices = [];
10+
if (!user.stocks) {
11+
user.stocks = [];
3812
}
39-
if (user.indices.length === 0) {
13+
if (user.stocks.length === 0) {
4014
return;
4115
}
4216
var rem = amount;
43-
while (rem > 0) {
44-
const take = Math.min(user.indices[0].amount, rem);
45-
if (take === user.indices[0].amount) {
46-
user.indices.shift();
17+
while (rem > 0 || user.stocks.length === 0) {
18+
const take = Math.min(user.stocks[0].amount, rem);
19+
if (take === user.stocks[0].amount) {
20+
user.stocks.shift();
4721
} else {
48-
user.indices[0].amount -= take;
22+
user.stocks[0].amount -= take;
4923
}
5024
rem -= take;
5125
}
@@ -54,8 +28,8 @@ export function sell(user: User, amount, currentPrice) {
5428

5529
export function buy(user: User, amount, currentPrice) {
5630
user.money -= amount * currentPrice;
57-
if (!user.indices) {
58-
user.indices = [];
31+
if (!user.stocks) {
32+
user.stocks = [];
5933
}
60-
user.indices.push({ price: currentPrice, amount: amount });
34+
user.stocks.push({ price: currentPrice, amount: amount });
6135
}

0 commit comments

Comments
 (0)