Versie 0.3.3
This commit is contained in:
1 parent
1cf6eeed29
commit
6e741a51fe
57 files changed
+1362
-2135
No files matched your search
+131
@@ -0,0 +1,131 @@
|
||||
// Generated, do not edit. sha256:c7cd149bf7a3cd10c21ce3f8a14833aa6c606b7953811447bfbf09af17e22088
|
||||
import { mkdirSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
|
||||
import { CONFIG } from '../shared/config.js';
|
||||
import { priceFromTotal } from '../shared/units.js';
|
||||
import { toSearchName } from '../shared/search-name.js';
|
||||
import { initDatabase, closeDatabase, one } from './db.js';
|
||||
import { portfolioDir, listPortfolios } from './portfolios.mjs';
|
||||
import { hasFeature } from './license.mjs';
|
||||
import { completeSetup } from './data/onboarding.js';
|
||||
import { createEvent } from './data/events.js';
|
||||
import { createValuation } from './data/valuations.js';
|
||||
|
||||
export const DEMO_MONTHS = 30;
|
||||
|
||||
const BTC_EUR = {
|
||||
'2023-01': 21000, '2023-04': 27000, '2023-07': 26500, '2023-10': 32000,
|
||||
'2024-01': 39000, '2024-02': 48000, '2024-03': 63000, '2024-04': 59000, '2024-05': 62000, '2024-06': 58000,
|
||||
'2024-07': 59000, '2024-08': 54000, '2024-09': 57000, '2024-10': 63000, '2024-11': 88000, '2024-12': 92000,
|
||||
'2025-01': 97000, '2025-02': 84000, '2025-03': 78000, '2025-04': 82000, '2025-05': 92000, '2025-06': 91000,
|
||||
'2025-07': 100000, '2025-08': 97000, '2025-09': 96000, '2025-10': 98000, '2025-11': 82000, '2025-12': 76000,
|
||||
'2026-01': 79000, '2026-02': 74000, '2026-03': 72000, '2026-04': 78000, '2026-05': 84000, '2026-06': 88000,
|
||||
'2026-07': 92000, '2026-08': 90000, '2026-09': 94000, '2026-10': 96000,
|
||||
};
|
||||
const MONTHS = Object.keys(BTC_EUR).sort();
|
||||
|
||||
const SAT = 1e8;
|
||||
const CENT = 100;
|
||||
const ACCOUNTS = Object.freeze({
|
||||
exchange: { name: 'Exchange', kind: 'exchange' },
|
||||
wallet: { name: 'Hardware wallet', kind: 'wallet' },
|
||||
bank: { name: 'Bank', kind: 'bank' },
|
||||
});
|
||||
|
||||
const iso = (y, m, day) => new Date(Date.UTC(y, m, day, 10, 0, 0)).toISOString();
|
||||
|
||||
function priceOn(at) {
|
||||
const key = at.slice(0, 7);
|
||||
const idx = MONTHS.findIndex(k => k >= key);
|
||||
const here = BTC_EUR[MONTHS[idx < 0 ? MONTHS.length - 1 : idx]];
|
||||
const next = BTC_EUR[MONTHS[Math.min((idx < 0 ? MONTHS.length - 1 : idx) + 1, MONTHS.length - 1)]];
|
||||
const frac = (Number(at.slice(8, 10)) - 1) / 30;
|
||||
return Math.round(here + (next - here) * frac);
|
||||
}
|
||||
|
||||
export function demoRows(today = new Date()) {
|
||||
const rows = [];
|
||||
const pos = { exchange: 0, wallet: 0, bank: 0 };
|
||||
const nu = today.toISOString();
|
||||
const start = new Date(Date.UTC(today.getUTCFullYear(), today.getUTCMonth() - (DEMO_MONTHS - 1), 1));
|
||||
const add = (r) => { if (r.at <= nu) rows.push(r); return r.at <= nu; };
|
||||
|
||||
for (let i = 0; i < DEMO_MONTHS; i++) {
|
||||
const y = start.getUTCFullYear(), m = start.getUTCMonth() + i;
|
||||
if (i === 0 && add({ kind: 'event', type: 'buy', asset: 'EUR', to: 'bank', at: iso(y, m, 1), amount: 4000 * CENT, fiatPrice: 1 })) pos.bank += 4000 * CENT;
|
||||
if (add({ kind: 'event', type: 'buy', asset: 'EUR', to: 'bank', at: iso(y, m, 1), amount: 900 * CENT, fiatPrice: 1 })) pos.bank += 900 * CENT;
|
||||
for (const day of [2, 9, 16, 23]) {
|
||||
const at = iso(y, m, day);
|
||||
const price = priceOn(at);
|
||||
const fee = 0.4;
|
||||
const amount = Math.round((75 - fee) / price * SAT);
|
||||
if (add({ kind: 'event', type: 'buy', asset: 'BTC', to: 'exchange', at, amount, fiatPrice: priceFromTotal(75, amount, 8, fee, 'buy'), fiatFee: fee })) {
|
||||
pos.exchange += amount;
|
||||
}
|
||||
}
|
||||
if (i % 4 === 3) {
|
||||
const amount = pos.exchange - 100_000;
|
||||
if (amount > 0 && add({ kind: 'event', type: 'transfer', asset: 'BTC', from: 'exchange', to: 'wallet', at: iso(y, m, 26), amount })) {
|
||||
pos.exchange -= amount; pos.wallet += amount;
|
||||
}
|
||||
}
|
||||
if (i === 20) {
|
||||
const amount = 2_000_000;
|
||||
if (add({ kind: 'event', type: 'transfer', asset: 'BTC', from: 'wallet', to: 'exchange', at: iso(y, m, 10), amount })) {
|
||||
pos.wallet -= amount; pos.exchange += amount;
|
||||
const at = iso(y, m, 11);
|
||||
if (add({ kind: 'event', type: 'sell', asset: 'BTC', from: 'exchange', at, amount, fiatPrice: priceOn(at), fiatFee: 2 })) pos.exchange -= amount;
|
||||
}
|
||||
}
|
||||
if (m % 3 === 2) {
|
||||
const amount = Math.round(pos.bank * 0.015 / 4);
|
||||
if (amount > 0 && add({ kind: 'event', type: 'interest', asset: 'EUR', to: 'bank', at: iso(y, m, 28), amount, fiatPrice: 1 })) pos.bank += amount;
|
||||
}
|
||||
const peil = (account, asset) => {
|
||||
const at = new Date(Date.UTC(y, m, 28, 22, 0, 0)).toISOString();
|
||||
add({ kind: 'valuation', asset, account, at, valuedOn: at.slice(0, 10), balance: pos[account],
|
||||
fiatRate: asset === 'EUR' ? 1 : priceOn(at) });
|
||||
};
|
||||
if (m % 6 === 5) { peil('bank', 'EUR'); if (pos.wallet > 0) peil('wallet', 'BTC'); }
|
||||
if (m % 12 === 11 && pos.exchange > 0) peil('exchange', 'BTC');
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
export async function seedDemo(today = new Date()) {
|
||||
const accounts = Object.values(ACCOUNTS);
|
||||
await completeSetup({ assets: ['BTC'], persona: 'broad', accounts, widgets: ['standing', 'spot'] });
|
||||
const assetId = (symbol) => one(`SELECT id FROM Asset WHERE symbol = ?`, [symbol]).id;
|
||||
const accountId = (key) => one(`SELECT id FROM Account WHERE searchName = ?`, [toSearchName(ACCOUNTS[key].name)]).id;
|
||||
const ids = { BTC: assetId('BTC'), EUR: assetId('EUR') };
|
||||
for (const r of demoRows(today)) {
|
||||
if (r.kind === 'event') {
|
||||
await createEvent({
|
||||
assetId: ids[r.asset], type: r.type, occurredAt: r.at, amount: r.amount, fiatPrice: r.fiatPrice ?? null,
|
||||
fiatFee: r.fiatFee ?? null, fromAccountId: r.from ? accountId(r.from) : null,
|
||||
toAccountId: r.to ? accountId(r.to) : null, source: 'manual',
|
||||
});
|
||||
} else {
|
||||
await createValuation({ assetId: ids[r.asset], accountId: accountId(r.account), balance: r.balance,
|
||||
fiatRate: r.fiatRate, valuedOn: r.valuedOn, rateAt: r.at, source: 'manual' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function createDemoPortfolio(today = new Date()) {
|
||||
const map = portfolioDir(CONFIG.PORTFOLIO.DEMO_NAME);
|
||||
mkdirSync(map, { recursive: true });
|
||||
initDatabase(join(map, 'db.sqlite'));
|
||||
try {
|
||||
await seedDemo(today);
|
||||
} finally {
|
||||
closeDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
export async function ensureDemo(layout) {
|
||||
if (!layout.fresh || hasFeature('portfolios') || listPortfolios().some(p => p.demo)) return false;
|
||||
await createDemoPortfolio();
|
||||
return true;
|
||||
}
|
||||
Reference in new issue
Block a user