Browse Source

Updated data for seed for local testing

pull/7179/head
Jovana Jovanovic 2 weeks ago
parent
commit
8ae458c2dc
  1. 419
      prisma/seed.mts

419
prisma/seed.mts

@ -1,5 +1,11 @@
import { PrismaPg } from '@prisma/adapter-pg';
import { PrismaClient } from '@prisma/client';
import {
AssetClass,
AssetSubClass,
DataSource,
PrismaClient,
Type
} from '@prisma/client';
const adapter = new PrismaPg({
connectionString: process.env.DIRECT_URL ?? process.env.DATABASE_URL
@ -7,7 +13,61 @@ const adapter = new PrismaPg({
const prisma = new PrismaClient({ adapter });
async function main() {
const ONE_DAY_IN_MS = 24 * 60 * 60 * 1000;
const PRICE_HISTORY_DAYS = 730;
function createSeededRandom(seed: number) {
return () => {
seed = (seed * 9301 + 49297) % 233280;
return seed / 233280;
};
}
function getToday() {
const today = new Date();
today.setUTCHours(0, 0, 0, 0);
return today;
}
function getDateDaysAgo(daysAgo: number) {
return new Date(getToday().getTime() - daysAgo * ONE_DAY_IN_MS);
}
function generatePriceHistory({
drift,
seed,
startPrice,
volatility
}: {
drift: number;
seed: number;
startPrice: number;
volatility: number;
}) {
const random = createSeededRandom(seed);
let price = startPrice;
const prices: { date: Date; price: number }[] = [];
for (let daysAgo = PRICE_HISTORY_DAYS; daysAgo >= 0; daysAgo--) {
price = Math.max(price * (1 + drift + (random() - 0.5) * volatility), 0.01);
prices.push({
date: getDateDaysAgo(daysAgo),
price: Math.round(price * 100) / 100
});
}
return prices;
}
function getPriceDaysAgo(
priceHistory: { date: Date; price: number }[],
daysAgo: number
) {
const index = priceHistory.length - 1 - daysAgo;
return priceHistory[Math.max(index, 0)].price;
}
async function seedTags() {
await prisma.tag.createMany({
data: [
{
@ -23,6 +83,361 @@ async function main() {
});
}
async function seedPortfolio() {
const user = await prisma.user.findFirst({ orderBy: { createdAt: 'asc' } });
if (!user) {
console.warn(
'No user found. Sign in once to create a user, then run the seed again to add sample portfolio data.'
);
return;
}
const platformBrokerage = await prisma.platform.upsert({
create: {
id: '9fe1a2ce-b5b3-41c7-a61f-73d6c8c346f5',
name: 'Interactive Brokers',
url: 'https://www.interactivebrokers.com'
},
update: {},
where: { url: 'https://www.interactivebrokers.com' }
});
const platformCrypto = await prisma.platform.upsert({
create: {
id: '1ef9a79a-61e6-4499-89eb-74b665cc8261',
name: 'Binance',
url: 'https://www.binance.com'
},
update: {},
where: { url: 'https://www.binance.com' }
});
const accountBrokerageId = '6798f55e-b2b5-40ef-8270-7f245e4da4d1';
const accountCryptoId = '0c660823-a36f-4903-9f24-71c6211399d8';
await prisma.account.upsert({
create: {
id: accountBrokerageId,
balance: 2500,
currency: 'USD',
name: 'Brokerage Account',
platformId: platformBrokerage.id,
userId: user.id
},
update: {},
where: { id_userId: { id: accountBrokerageId, userId: user.id } }
});
await prisma.account.upsert({
create: {
id: accountCryptoId,
balance: 500,
currency: 'USD',
name: 'Crypto Exchange',
platformId: platformCrypto.id,
userId: user.id
},
update: {},
where: { id_userId: { id: accountCryptoId, userId: user.id } }
});
const symbols = {
AAPL: {
id: 'd7ae0976-9f0e-4a38-81a3-7cec78806436',
symbol: 'AAPL.SEED',
name: 'Apple Inc.',
assetClass: AssetClass.EQUITY,
assetSubClass: AssetSubClass.STOCK,
priceHistory: generatePriceHistory({
drift: 0.0006,
seed: 1,
startPrice: 150,
volatility: 0.02
})
},
MSFT: {
id: '4c84f62e-20c5-4d5f-8d51-63221dc64c8a',
symbol: 'MSFT.SEED',
name: 'Microsoft Corporation',
assetClass: AssetClass.EQUITY,
assetSubClass: AssetSubClass.STOCK,
priceHistory: generatePriceHistory({
drift: 0.0007,
seed: 2,
startPrice: 300,
volatility: 0.018
})
},
VTI: {
id: 'bc960e73-2ed5-4f46-82df-2a86595148e5',
symbol: 'VTI.SEED',
name: 'Vanguard Total Stock Market ETF',
assetClass: AssetClass.EQUITY,
assetSubClass: AssetSubClass.ETF,
priceHistory: generatePriceHistory({
drift: 0.0004,
seed: 3,
startPrice: 220,
volatility: 0.01
})
},
BTC: {
id: 'f55270af-eb6c-4328-8411-71bf08f4803a',
symbol: 'BTC.SEED',
name: 'Bitcoin',
assetClass: AssetClass.LIQUIDITY,
assetSubClass: AssetSubClass.CRYPTOCURRENCY,
priceHistory: generatePriceHistory({
drift: 0.001,
seed: 4,
startPrice: 28000,
volatility: 0.04
})
}
} as const;
const custodyFeeProfileId = 'e9cec288-3486-4f12-9dc5-9a57f3883b43';
const custodyFeeSymbol = '2160cb24-5906-499e-b854-9c7278cb35b1';
for (const {
id,
symbol,
name,
assetClass,
assetSubClass,
priceHistory
} of Object.values(symbols)) {
await prisma.symbolProfile.upsert({
create: {
id,
symbol,
assetClass,
assetSubClass,
currency: 'USD',
dataSource: DataSource.MANUAL,
name,
userId: user.id
},
update: {},
where: { dataSource_symbol: { dataSource: DataSource.MANUAL, symbol } }
});
await prisma.marketData.createMany({
data: priceHistory.map(({ date, price }) => {
return {
date,
dataSource: DataSource.MANUAL,
marketPrice: price,
symbol
};
}),
skipDuplicates: true
});
}
await prisma.symbolProfile.upsert({
create: {
id: custodyFeeProfileId,
symbol: custodyFeeSymbol,
currency: 'USD',
dataSource: DataSource.MANUAL,
name: 'Custody Fee',
userId: user.id
},
update: {},
where: {
dataSource_symbol: {
dataSource: DataSource.MANUAL,
symbol: custodyFeeSymbol
}
}
});
const orders: {
id: string;
accountId: string;
symbolProfileId: string;
type: Type;
daysAgo: number;
quantity: number;
fee: number;
unitPrice?: number;
}[] = [
{
id: 'bbe70675-1ff7-48e2-9af3-99e2c9bf01ba',
accountId: accountBrokerageId,
symbolProfileId: symbols.AAPL.id,
type: Type.BUY,
daysAgo: 700,
quantity: 20,
fee: 4.95
},
{
id: '15aa3ddc-4212-4a54-83fa-f2fad8791dfd',
accountId: accountBrokerageId,
symbolProfileId: symbols.MSFT.id,
type: Type.BUY,
daysAgo: 650,
quantity: 10,
fee: 4.95
},
{
id: '7a7c2607-20a2-4f00-a0ff-7ae2f85f2ff3',
accountId: accountBrokerageId,
symbolProfileId: symbols.VTI.id,
type: Type.BUY,
daysAgo: 600,
quantity: 15,
fee: 0
},
{
id: '46c66fe3-f12c-40d1-bb6d-8e1eee9b115f',
accountId: accountBrokerageId,
symbolProfileId: symbols.AAPL.id,
type: Type.BUY,
daysAgo: 400,
quantity: 10,
fee: 4.95
},
{
id: '5f835cc2-f7da-481c-8043-7011074f5045',
accountId: accountBrokerageId,
symbolProfileId: symbols.VTI.id,
type: Type.BUY,
daysAgo: 300,
quantity: 15,
fee: 0
},
{
id: '98d0d61c-ab30-49d1-b57c-629930a140bf',
accountId: accountBrokerageId,
symbolProfileId: symbols.MSFT.id,
type: Type.DIVIDEND,
daysAgo: 200,
quantity: 1,
fee: 0,
unitPrice: 18.5
},
{
id: 'ce733963-672a-4569-880a-7fc92de8baa5',
accountId: accountBrokerageId,
symbolProfileId: symbols.MSFT.id,
type: Type.BUY,
daysAgo: 150,
quantity: 5,
fee: 4.95
},
{
id: 'f82b04dd-ab56-4061-a67e-2fabd0303068',
accountId: accountBrokerageId,
symbolProfileId: symbols.AAPL.id,
type: Type.DIVIDEND,
daysAgo: 100,
quantity: 1,
fee: 0,
unitPrice: 9.6
},
{
id: '1b49f226-89bf-41e2-8613-3446e3722b34',
accountId: accountBrokerageId,
symbolProfileId: custodyFeeProfileId,
type: Type.FEE,
daysAgo: 90,
quantity: 1,
fee: 0,
unitPrice: 5
},
{
id: 'ca5ef988-3595-4d76-b9b1-19541ebf4fad',
accountId: accountBrokerageId,
symbolProfileId: symbols.AAPL.id,
type: Type.BUY,
daysAgo: 50,
quantity: 5,
fee: 4.95
},
{
id: '8352549c-68a0-4c9f-b4d3-910970219c0b',
accountId: accountCryptoId,
symbolProfileId: symbols.BTC.id,
type: Type.BUY,
daysAgo: 500,
quantity: 0.05,
fee: 10
},
{
id: 'c8f9bcda-215e-4a63-af43-a47de6517fcf',
accountId: accountCryptoId,
symbolProfileId: symbols.BTC.id,
type: Type.BUY,
daysAgo: 300,
quantity: 0.03,
fee: 8
},
{
id: '75dfaa3d-e73d-4a73-a2ef-d5cc98243b4f',
accountId: accountCryptoId,
symbolProfileId: symbols.BTC.id,
type: Type.BUY,
daysAgo: 100,
quantity: 0.02,
fee: 5
},
{
id: '7adf12c3-839c-4a0d-872d-13083a9b84b7',
accountId: accountCryptoId,
symbolProfileId: symbols.BTC.id,
type: Type.SELL,
daysAgo: 30,
quantity: 0.01,
fee: 5
}
];
const priceHistoryBySymbolProfileId = Object.fromEntries(
Object.values(symbols).map(({ id, priceHistory }) => [id, priceHistory])
);
for (const order of orders) {
const unitPrice =
order.unitPrice ??
getPriceDaysAgo(
priceHistoryBySymbolProfileId[order.symbolProfileId],
order.daysAgo
);
const data = {
accountId: order.accountId,
accountUserId: user.id,
currency: 'USD',
date: getDateDaysAgo(order.daysAgo),
fee: order.fee,
isDraft: false,
quantity: order.quantity,
symbolProfileId: order.symbolProfileId,
type: order.type,
unitPrice,
userId: user.id
};
await prisma.order.upsert({
create: { id: order.id, ...data },
update: data,
where: { id: order.id }
});
}
console.log(
`Seeded sample portfolio data for user ${user.id} (2 accounts, ${Object.keys(symbols).length} symbols, ${orders.length} activities).`
);
}
async function main() {
await seedTags();
await seedPortfolio();
}
main()
.catch((e) => {
console.error(e);

Loading…
Cancel
Save