ray-finance 0.2.5 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src=".github/ray-logo.png" alt="Ray" width="120" />
2
+ <img src=".github/ray-logo.png" alt="Ray" width="108" />
3
3
  </p>
4
4
 
5
5
  <p align="center">
@@ -14,40 +14,17 @@
14
14
 
15
15
  <br />
16
16
 
17
- ```
18
- Friday, Mar 28
19
-
20
- net worth $45,230 +$120
21
-
22
- spending $2,340 this month · $340 less vs last month
23
- Dining -$114 · Shopping -$142 · Groceries -$73
24
-
25
- ▓▓▓▓▓▓▓░ Dining 92%
26
-
27
- ▓▓▓▓░░░░ Emergency fund $18,200/$40,000
28
-
29
- upcoming Netflix $16 in 3d · Comcast $142 in 6d
30
-
31
- score 72/100 · 5d no dining · 3d on pace
32
-
33
- ──────────────────────────────────────────────────
34
-
35
- ❯ if I quit my job to freelance, how long can I survive?
36
-
37
- Based on your last 3 months: you burn $4,820/mo after
38
- fixed costs. With $18,200 in savings, that's
39
- 3.8 months of runway at current spend.
40
-
41
- Cut dining and shopping to last-month levels and
42
- you stretch to 5.1 months. Land one $8k contract
43
- in that window and you never dip below $10k.
44
- ```
17
+ <p align="center">
18
+ <img src=".github/ray-demo.png" alt="Ray demo" />
19
+ </p>
45
20
 
46
21
  Open Ray and it shows your net worth, spending vs last month, budget pacing, and upcoming bills — before you type a word. Ask a question and it answers from your real data, not guesses. Local-first. Encrypted. Open source.
47
22
 
48
23
  ## Features
49
24
 
50
25
  - **It already knows** — Every conversation starts with a real-time financial briefing. Net worth, spending velocity, budget alerts, goal pace, upcoming bills, and your daily score. No "let me look that up."
26
+ - **Persistent context** — Ray maintains a financial profile that evolves with you: income, goals, strategy, key decisions, and open items. It updates this context as your situation changes, so every conversation picks up where the last one left off.
27
+ - **Long-term memory** — Important details from conversations are saved as memories. Mention you're saving for a house or switching jobs and Ray remembers — without you repeating yourself.
51
28
  - **Bank sync via Plaid** — Connect checking, savings, credit cards, investments, and loans
52
29
  - **Encrypted local database** — All data stays on your machine in an AES-256 encrypted SQLite database
53
30
  - **Daily scoring** — A 0-100 behavior score with streaks and 14 unlockable achievements. No restaurants for a week? That's Kitchen Hero. Five zero-spend days? Monk Mode.
@@ -65,6 +42,26 @@ Open Ray and it shows your net worth, spending vs last month, budget pacing, and
65
42
  npm install -g ray-finance
66
43
  ```
67
44
 
45
+ ## Try It
46
+
47
+ Explore Ray with realistic fake data — no bank accounts needed.
48
+
49
+ ```bash
50
+ ray demo # seed a demo database
51
+ ray --demo status # financial overview
52
+ ray --demo accounts # linked accounts with balances
53
+ ray --demo spending # spending breakdown by category
54
+ ray --demo budgets # budget tracking
55
+ ray --demo goals # financial goal progress
56
+ ray --demo score # daily score, streaks, achievements
57
+ ray --demo alerts # financial alerts
58
+ ray --demo transactions # recent transactions
59
+ ```
60
+
61
+ The dashboard commands work with no setup at all. To also try the AI chat with demo data, run `ray setup` first and add an [Anthropic API key](https://console.anthropic.com) or Ray API key — then `ray --demo` will start an interactive session where you can ask questions about the fake portfolio.
62
+
63
+ When you're ready to connect real accounts, run `ray link`.
64
+
68
65
  ## Quick Start
69
66
 
70
67
  ```bash
@@ -98,6 +95,8 @@ Run `ray --help` to see all available commands.
98
95
  | Command | Description |
99
96
  |---------|-------------|
100
97
  | `ray` | Interactive AI chat with your financial advisor |
98
+ | `ray demo` | Seed a demo database with realistic fake data |
99
+ | `ray --demo <cmd>` | Run any command against demo data |
101
100
  | `ray setup` | Configure API keys and preferences |
102
101
  | `ray link` | Connect a new bank account |
103
102
  | `ray sync` | Pull latest transactions and balances |
@@ -155,6 +154,7 @@ Ray stores everything in `~/.ray/`:
155
154
  context.md # Persistent financial context for AI
156
155
  data/
157
156
  finance.db # Encrypted SQLite database
157
+ demo.db # Demo database (created by `ray demo`)
158
158
  sync.log # Daily sync output
159
159
  ```
160
160
 
@@ -174,6 +174,7 @@ RAY_API_KEY= # Ray API key (managed mode, replaces the above)
174
174
 
175
175
  ## Roadmap
176
176
 
177
+ - [ ] Bring your own model — use any LLM provider (OpenAI, Ollama, open-source models, etc.)
177
178
  - [ ] Daily digest email — morning summary of your finances
178
179
 
179
180
  Have an idea? [Open a PR](https://github.com/cdinnison/ray-finance/pulls).
@@ -1,6 +1,6 @@
1
1
  export declare function runSync(): Promise<void>;
2
2
  export declare function runLink(): Promise<void>;
3
- export declare function showAccounts(): void;
3
+ export declare function showAccounts(): Promise<void>;
4
4
  export declare function showStatus(): void;
5
5
  export declare function showTransactions(options?: {
6
6
  limit?: number;
@@ -5,7 +5,7 @@ import { getLatestScore, getAchievements, getMonthlySavings } from "../scoring/i
5
5
  import { generateAlerts } from "../alerts/index.js";
6
6
  import { runDailySync } from "../daily-sync.js";
7
7
  import { startLinkServer } from "../server.js";
8
- import { heading, progressBar, formatMoney, formatMoneyColored, dim, formatDuration, formatError } from "./format.js";
8
+ import { heading, progressBar, formatMoney, formatMoneyColored, dim, formatDuration, formatError, renderLogo, institutionName } from "./format.js";
9
9
  export async function runSync() {
10
10
  const ora = (await import("ora")).default;
11
11
  const spinner = ora("Syncing transactions...").start();
@@ -36,9 +36,9 @@ export async function runLink() {
36
36
  stop();
37
37
  spinner.succeed("Bank account linked successfully!");
38
38
  }
39
- export function showAccounts() {
39
+ export async function showAccounts() {
40
40
  const db = getDb();
41
- const institutions = db.prepare(`SELECT i.name as institution, i.item_id, i.created_at,
41
+ const institutions = db.prepare(`SELECT i.name as institution, i.item_id, i.created_at, i.logo, i.primary_color,
42
42
  a.name, a.type, a.subtype, a.mask, a.current_balance, a.currency
43
43
  FROM institutions i
44
44
  LEFT JOIN accounts a ON a.item_id = i.item_id AND a.hidden = 0
@@ -48,20 +48,40 @@ export function showAccounts() {
48
48
  return;
49
49
  }
50
50
  console.log(`\n${heading("Linked Accounts")}\n`);
51
- let currentInst = "";
51
+ // Group rows by institution
52
+ const groups = new Map();
52
53
  for (const row of institutions) {
53
- if (row.institution !== currentInst) {
54
- currentInst = row.institution;
55
- console.log(chalk.bold(currentInst));
54
+ const key = row.item_id;
55
+ if (!groups.has(key))
56
+ groups.set(key, []);
57
+ groups.get(key).push(row);
58
+ }
59
+ // Compute column widths across all accounts for alignment
60
+ const allAccounts = institutions.filter(r => r.name);
61
+ const maxName = Math.max(...allAccounts.map(r => `${r.name}${r.mask ? ` ••${r.mask}` : ""}`.length), 0);
62
+ const maxLabel = Math.max(...allAccounts.map(r => (r.subtype || r.type || "").length), 0);
63
+ for (const [, rows] of groups) {
64
+ const first = rows[0];
65
+ // Logo inline with institution name
66
+ let logoStr = "";
67
+ if (first.logo) {
68
+ const logo = await renderLogo(first.logo);
69
+ if (logo)
70
+ logoStr = logo.replace(/\n/g, "") + " ";
56
71
  }
57
- if (!row.name) {
58
- console.log(dim(" No accounts found"));
59
- continue;
72
+ console.log(`${logoStr}${institutionName(first.institution, first.primary_color)}`);
73
+ for (const row of rows) {
74
+ if (!row.name) {
75
+ console.log(dim(" No accounts found"));
76
+ continue;
77
+ }
78
+ const nameWithMask = `${row.name}${row.mask ? ` ••${row.mask}` : ""}`;
79
+ const label = row.subtype || row.type || "";
80
+ const balance = row.current_balance != null ? rawFormatMoney(row.current_balance) : "—";
81
+ const namePad = nameWithMask.padEnd(maxName + 2);
82
+ const labelPad = label.padEnd(maxLabel + 2);
83
+ console.log(` ${namePad}${dim(labelPad)}${balance}`);
60
84
  }
61
- const mask = row.mask ? ` ••${row.mask}` : "";
62
- const balance = row.current_balance != null ? rawFormatMoney(row.current_balance) : "—";
63
- const label = row.subtype || row.type || "";
64
- console.log(` ${row.name}${dim(mask)} ${dim(label)} ${balance}`);
65
85
  }
66
86
  console.log("");
67
87
  }
@@ -27,3 +27,7 @@ export declare function helpScreen(commands: {
27
27
  /** Colorize AI response text for the terminal */
28
28
  export declare function formatResponse(text: string): string;
29
29
  export declare const DISCLAIMER: string;
30
+ /** Render a base64-encoded PNG as compact ANSI art (3 rows) */
31
+ export declare function renderLogo(base64: string): Promise<string>;
32
+ /** Color an institution name using its Plaid primary_color */
33
+ export declare function institutionName(name: string, primaryColor: string | null): string;
@@ -199,3 +199,26 @@ export function formatResponse(text) {
199
199
  }
200
200
  export const DISCLAIMER = "Ray is an AI tool, not a licensed financial advisor. Output is informational, " +
201
201
  "may be inaccurate, and does not constitute financial advice.";
202
+ // ─── Institution Logo Rendering ─── //
203
+ /** Render a base64-encoded PNG as compact ANSI art (3 rows) */
204
+ export async function renderLogo(base64) {
205
+ try {
206
+ const terminalImage = (await import("terminal-image")).default;
207
+ const buffer = Buffer.from(base64, "base64");
208
+ const rendered = await terminalImage.buffer(buffer, { height: 1, preserveAspectRatio: true });
209
+ return rendered.trimEnd();
210
+ }
211
+ catch {
212
+ return "";
213
+ }
214
+ }
215
+ /** Color an institution name using its Plaid primary_color */
216
+ export function institutionName(name, primaryColor) {
217
+ if (primaryColor) {
218
+ try {
219
+ return chalk.hex(primaryColor).bold(name);
220
+ }
221
+ catch { }
222
+ }
223
+ return chalk.bold(name);
224
+ }
package/dist/cli/index.js CHANGED
@@ -1,8 +1,20 @@
1
1
  #!/usr/bin/env node
2
+ import { resolve } from "path";
3
+ import { homedir } from "os";
4
+ // --demo flag: use dedicated demo database (must run before config import)
5
+ const isDemoMode = process.argv.includes("--demo");
6
+ if (isDemoMode) {
7
+ process.argv = process.argv.filter(a => a !== "--demo");
8
+ }
2
9
  import { Command } from "commander";
3
10
  import { createRequire } from "module";
4
11
  import { config, isConfigured, useManaged, RAY_PROXY_BASE } from "../config.js";
5
12
  import { helpScreen } from "./format.js";
13
+ // Override config for demo mode (demo DB is unencrypted)
14
+ if (isDemoMode) {
15
+ config.dbPath = resolve(homedir(), ".ray", "data", "demo.db");
16
+ config.dbEncryptionKey = "";
17
+ }
6
18
  const require = createRequire(import.meta.url);
7
19
  const { version } = require("../../package.json");
8
20
  const program = new Command();
@@ -54,7 +66,7 @@ program
54
66
  .action(async () => {
55
67
  ensureConfigured();
56
68
  const { showAccounts } = await import("./commands.js");
57
- showAccounts();
69
+ await showAccounts();
58
70
  });
59
71
  program
60
72
  .command("status")
@@ -181,6 +193,14 @@ program
181
193
  const { runDoctor } = await import("./doctor.js");
182
194
  await runDoctor();
183
195
  });
196
+ program
197
+ .command("demo")
198
+ .description("Seed a demo database with realistic fake data")
199
+ .action(async () => {
200
+ const demoPath = resolve(homedir(), ".ray", "data", "demo.db");
201
+ const { seedDemoDb } = await import("../demo/seed.js");
202
+ seedDemoDb(demoPath);
203
+ });
184
204
  program
185
205
  .command("completions")
186
206
  .description("Install shell completions")
@@ -189,6 +209,8 @@ program
189
209
  installCompletions();
190
210
  });
191
211
  function ensureConfigured() {
212
+ if (isDemoMode)
213
+ return;
192
214
  if (!isConfigured()) {
193
215
  console.error("Ray is not configured. Run 'ray setup' first.");
194
216
  process.exit(1);
@@ -213,6 +235,7 @@ program.configureHelp({
213
235
  { name: "billing", desc: "Manage your Ray subscription" },
214
236
  { name: "update", desc: "Update Ray to the latest version" },
215
237
  { name: "doctor", desc: "Check system health" },
238
+ { name: "demo", desc: "Seed a demo database with fake data" },
216
239
  { name: "completions", desc: "Install shell completions" },
217
240
  ]),
218
241
  });
@@ -1,11 +1,12 @@
1
- import { syncTransactions, syncBalances, syncInvestments, syncLiabilities, syncRecurring, isProductNotSupported, } from "./plaid/sync.js";
1
+ import { syncTransactions, syncBalances, syncInvestments, syncInvestmentTransactions, syncLiabilities, syncRecurring, isProductNotSupported, refreshProducts, } from "./plaid/sync.js";
2
2
  import { calculateDailyScore, checkAchievements } from "./scoring/index.js";
3
3
  import { decryptPlaidToken } from "./db/encryption.js";
4
4
  import { config } from "./config.js";
5
+ import { institutionName } from "./cli/format.js";
5
6
  /** Run the daily sync for a single database */
6
7
  export async function runDailySync(db) {
7
8
  const institutions = db
8
- .prepare(`SELECT item_id, access_token, name, products, cursor FROM institutions`)
9
+ .prepare(`SELECT item_id, access_token, name, products, cursor, primary_color FROM institutions`)
9
10
  .all();
10
11
  if (institutions.length === 0) {
11
12
  console.log("No linked institutions.");
@@ -31,8 +32,15 @@ export async function runDailySync(db) {
31
32
  console.error(` Skipping ${inst.name}: failed to decrypt access token (wrong key or corrupt data)`);
32
33
  continue;
33
34
  }
34
- const products = JSON.parse(inst.products);
35
- console.log(`Syncing: ${inst.name} (${products.join(", ")})`);
35
+ let products = JSON.parse(inst.products);
36
+ // Refresh products list from Plaid if needed
37
+ try {
38
+ products = await refreshProducts(db, inst.item_id, accessToken);
39
+ }
40
+ catch {
41
+ // Non-fatal — use stored products
42
+ }
43
+ console.log(`Syncing: ${institutionName(inst.name, inst.primary_color)} (${products.join(", ")})`);
36
44
  try {
37
45
  instSynced++;
38
46
  // Always sync balances
@@ -45,22 +53,34 @@ export async function runDailySync(db) {
45
53
  console.log(` Transactions: +${txResult.added} ~${txResult.modified} -${txResult.removed}`);
46
54
  }
47
55
  // Sync investments
48
- try {
49
- const invResult = await syncInvestments(db, accessToken);
50
- console.log(` Investments: ${invResult.holdings} holdings, ${invResult.securities} securities`);
51
- }
52
- catch (e) {
53
- if (!isProductNotSupported(e))
54
- console.error(` Investments error: ${e.message}`);
56
+ if (products.includes("investments")) {
57
+ try {
58
+ const invResult = await syncInvestments(db, accessToken);
59
+ console.log(` Investments: ${invResult.holdings} holdings, ${invResult.securities} securities`);
60
+ }
61
+ catch (e) {
62
+ if (!isProductNotSupported(e))
63
+ console.error(` Investments error: ${e.message}`);
64
+ }
65
+ try {
66
+ const invTxResult = await syncInvestmentTransactions(db, accessToken);
67
+ console.log(` Investment transactions: ${invTxResult.transactions}`);
68
+ }
69
+ catch (e) {
70
+ if (!isProductNotSupported(e))
71
+ console.error(` Investment transactions error: ${e.message}`);
72
+ }
55
73
  }
56
74
  // Sync liabilities
57
- try {
58
- await syncLiabilities(db, accessToken);
59
- console.log(` Liabilities: synced`);
60
- }
61
- catch (e) {
62
- if (!isProductNotSupported(e))
63
- console.error(` Liabilities error: ${e.message}`);
75
+ if (products.includes("liabilities")) {
76
+ try {
77
+ await syncLiabilities(db, accessToken);
78
+ console.log(` Liabilities: synced`);
79
+ }
80
+ catch (e) {
81
+ if (!isProductNotSupported(e))
82
+ console.error(` Liabilities error: ${e.message}`);
83
+ }
64
84
  }
65
85
  // Sync recurring transaction streams
66
86
  if (products.includes("transactions")) {
package/dist/db/schema.js CHANGED
@@ -189,6 +189,22 @@ export function migrate(db) {
189
189
  created_at TEXT DEFAULT (datetime('now'))
190
190
  );
191
191
 
192
+ CREATE TABLE IF NOT EXISTS investment_transactions (
193
+ investment_transaction_id TEXT PRIMARY KEY,
194
+ account_id TEXT NOT NULL REFERENCES accounts(account_id),
195
+ security_id TEXT,
196
+ date TEXT NOT NULL,
197
+ name TEXT NOT NULL,
198
+ quantity REAL,
199
+ amount REAL NOT NULL,
200
+ price REAL,
201
+ fees REAL,
202
+ type TEXT,
203
+ subtype TEXT,
204
+ iso_currency_code TEXT,
205
+ created_at TEXT DEFAULT (datetime('now'))
206
+ );
207
+
192
208
  CREATE TABLE IF NOT EXISTS ai_audit_log (
193
209
  id INTEGER PRIMARY KEY AUTOINCREMENT,
194
210
  tool_name TEXT NOT NULL,
@@ -198,11 +214,48 @@ export function migrate(db) {
198
214
  created_at TEXT DEFAULT (datetime('now'))
199
215
  );
200
216
  `);
217
+ // Migrate: add logo and primary_color to institutions
218
+ const instCols = db.prepare(`PRAGMA table_info(institutions)`).all();
219
+ if (!instCols.some(c => c.name === "logo")) {
220
+ db.exec(`ALTER TABLE institutions ADD COLUMN logo TEXT`);
221
+ db.exec(`ALTER TABLE institutions ADD COLUMN primary_color TEXT`);
222
+ }
201
223
  // Migrate: rename goals.deadline -> target_date for existing databases
202
224
  const goalCols = db.prepare(`PRAGMA table_info(goals)`).all();
203
225
  if (goalCols.some(c => c.name === "deadline") && !goalCols.some(c => c.name === "target_date")) {
204
226
  db.exec(`ALTER TABLE goals RENAME COLUMN deadline TO target_date`);
205
227
  }
228
+ // Migrate: add balance_limit to accounts
229
+ const acctCols = db.prepare(`PRAGMA table_info(accounts)`).all();
230
+ if (!acctCols.some(c => c.name === "balance_limit")) {
231
+ db.exec(`ALTER TABLE accounts ADD COLUMN balance_limit REAL`);
232
+ }
233
+ // Migrate: add vesting columns to holdings
234
+ const holdCols = db.prepare(`PRAGMA table_info(holdings)`).all();
235
+ if (!holdCols.some(c => c.name === "vested_value")) {
236
+ db.exec(`ALTER TABLE holdings ADD COLUMN vested_value REAL`);
237
+ db.exec(`ALTER TABLE holdings ADD COLUMN vested_quantity REAL`);
238
+ }
239
+ // Migrate: expand liabilities with type-specific columns
240
+ const liabCols = db.prepare(`PRAGMA table_info(liabilities)`).all();
241
+ if (!liabCols.some(c => c.name === "last_payment_amount")) {
242
+ db.exec(`ALTER TABLE liabilities ADD COLUMN last_payment_amount REAL`);
243
+ db.exec(`ALTER TABLE liabilities ADD COLUMN last_payment_date TEXT`);
244
+ db.exec(`ALTER TABLE liabilities ADD COLUMN credit_limit REAL`);
245
+ db.exec(`ALTER TABLE liabilities ADD COLUMN last_statement_issue_date TEXT`);
246
+ db.exec(`ALTER TABLE liabilities ADD COLUMN is_overdue INTEGER`);
247
+ db.exec(`ALTER TABLE liabilities ADD COLUMN apr_type TEXT`);
248
+ db.exec(`ALTER TABLE liabilities ADD COLUMN maturity_date TEXT`);
249
+ db.exec(`ALTER TABLE liabilities ADD COLUMN loan_type TEXT`);
250
+ db.exec(`ALTER TABLE liabilities ADD COLUMN property_address TEXT`);
251
+ db.exec(`ALTER TABLE liabilities ADD COLUMN escrow_balance REAL`);
252
+ db.exec(`ALTER TABLE liabilities ADD COLUMN loan_status TEXT`);
253
+ db.exec(`ALTER TABLE liabilities ADD COLUMN loan_name TEXT`);
254
+ db.exec(`ALTER TABLE liabilities ADD COLUMN repayment_plan TEXT`);
255
+ db.exec(`ALTER TABLE liabilities ADD COLUMN expected_payoff_date TEXT`);
256
+ db.exec(`ALTER TABLE liabilities ADD COLUMN ytd_interest_paid REAL`);
257
+ db.exec(`ALTER TABLE liabilities ADD COLUMN ytd_principal_paid REAL`);
258
+ }
206
259
  // Migrate: rebuild recurring table to use Plaid stream schema
207
260
  const recCols = db.prepare(`PRAGMA table_info(recurring)`).all();
208
261
  if (!recCols.some(c => c.name === "stream_id")) {
@@ -0,0 +1,252 @@
1
+ export declare const institutions: {
2
+ item_id: string;
3
+ access_token: string;
4
+ name: string;
5
+ products: string;
6
+ logo: string;
7
+ primary_color: string;
8
+ }[];
9
+ export declare const accounts: ({
10
+ account_id: string;
11
+ item_id: string;
12
+ name: string;
13
+ official_name: string;
14
+ type: string;
15
+ subtype: string;
16
+ mask: string;
17
+ current_balance: number;
18
+ available_balance: number;
19
+ currency: string;
20
+ balance_limit: null;
21
+ } | {
22
+ account_id: string;
23
+ item_id: string;
24
+ name: string;
25
+ official_name: string;
26
+ type: string;
27
+ subtype: string;
28
+ mask: string;
29
+ current_balance: number;
30
+ available_balance: null;
31
+ currency: string;
32
+ balance_limit: null;
33
+ } | {
34
+ account_id: string;
35
+ item_id: string;
36
+ name: string;
37
+ official_name: string;
38
+ type: string;
39
+ subtype: string;
40
+ mask: string;
41
+ current_balance: number;
42
+ available_balance: number;
43
+ currency: string;
44
+ balance_limit: number;
45
+ } | {
46
+ account_id: string;
47
+ item_id: string;
48
+ name: string;
49
+ official_name: null;
50
+ type: string;
51
+ subtype: string;
52
+ mask: null;
53
+ current_balance: number;
54
+ available_balance: null;
55
+ currency: string;
56
+ balance_limit: null;
57
+ })[];
58
+ export declare const transactions: ({
59
+ transaction_id: string;
60
+ account_id: string;
61
+ amount: number;
62
+ date: string;
63
+ name: string;
64
+ merchant_name: string;
65
+ category: string;
66
+ subcategory: string;
67
+ pending: number;
68
+ payment_channel: string;
69
+ } | {
70
+ transaction_id: string;
71
+ account_id: string;
72
+ amount: number;
73
+ date: string;
74
+ name: string;
75
+ merchant_name: null;
76
+ category: string;
77
+ subcategory: string;
78
+ pending: number;
79
+ payment_channel: string;
80
+ })[];
81
+ export declare const securities: {
82
+ security_id: string;
83
+ name: string;
84
+ ticker: string;
85
+ type: string;
86
+ close_price: number;
87
+ close_price_as_of: string;
88
+ }[];
89
+ export declare const holdings: ({
90
+ account_id: string;
91
+ security_id: string;
92
+ quantity: number;
93
+ value: number;
94
+ cost_basis: number;
95
+ price: number;
96
+ price_as_of: string;
97
+ vested_value: null;
98
+ vested_quantity: null;
99
+ } | {
100
+ account_id: string;
101
+ security_id: string;
102
+ quantity: number;
103
+ value: number;
104
+ cost_basis: number;
105
+ price: number;
106
+ price_as_of: string;
107
+ vested_value: number;
108
+ vested_quantity: number;
109
+ })[];
110
+ export declare const liabilities: ({
111
+ account_id: string;
112
+ type: string;
113
+ interest_rate: number;
114
+ origination_date: null;
115
+ original_balance: null;
116
+ current_balance: number;
117
+ minimum_payment: number;
118
+ next_payment_due: string;
119
+ last_payment_amount: number;
120
+ last_payment_date: string;
121
+ credit_limit: number;
122
+ last_statement_issue_date: string;
123
+ is_overdue: number;
124
+ apr_type: string;
125
+ maturity_date: null;
126
+ loan_type: null;
127
+ property_address: null;
128
+ escrow_balance: null;
129
+ loan_status: null;
130
+ loan_name: null;
131
+ repayment_plan: null;
132
+ expected_payoff_date: null;
133
+ ytd_interest_paid: null;
134
+ ytd_principal_paid: null;
135
+ } | {
136
+ account_id: string;
137
+ type: string;
138
+ interest_rate: number;
139
+ origination_date: string;
140
+ original_balance: number;
141
+ current_balance: number;
142
+ minimum_payment: number;
143
+ next_payment_due: string;
144
+ last_payment_amount: number;
145
+ last_payment_date: string;
146
+ credit_limit: null;
147
+ last_statement_issue_date: null;
148
+ is_overdue: number;
149
+ apr_type: string;
150
+ maturity_date: string;
151
+ loan_type: string;
152
+ property_address: string;
153
+ escrow_balance: number;
154
+ loan_status: string;
155
+ loan_name: string;
156
+ repayment_plan: null;
157
+ expected_payoff_date: string;
158
+ ytd_interest_paid: number;
159
+ ytd_principal_paid: number;
160
+ })[];
161
+ export declare const recurring: ({
162
+ stream_id: string;
163
+ account_id: string;
164
+ merchant_name: string;
165
+ description: string;
166
+ frequency: string;
167
+ category: string;
168
+ subcategory: string;
169
+ avg_amount: number;
170
+ last_amount: number;
171
+ first_date: string;
172
+ last_date: string;
173
+ is_active: number;
174
+ status: string;
175
+ stream_type: string;
176
+ } | {
177
+ stream_id: string;
178
+ account_id: string;
179
+ merchant_name: null;
180
+ description: string;
181
+ frequency: string;
182
+ category: string;
183
+ subcategory: string;
184
+ avg_amount: number;
185
+ last_amount: number;
186
+ first_date: string;
187
+ last_date: string;
188
+ is_active: number;
189
+ status: string;
190
+ stream_type: string;
191
+ })[];
192
+ export declare const budgets: {
193
+ category: string;
194
+ monthly_limit: number;
195
+ period: string;
196
+ }[];
197
+ export declare const goals: {
198
+ name: string;
199
+ target_amount: number;
200
+ current_amount: number;
201
+ target_date: string;
202
+ status: string;
203
+ }[];
204
+ export declare const dailyScores: {
205
+ date: string;
206
+ score: number;
207
+ restaurant_count: number;
208
+ shopping_count: number;
209
+ food_spend: number;
210
+ total_spend: number;
211
+ zero_spend: number;
212
+ no_restaurant_streak: number;
213
+ no_shopping_streak: number;
214
+ on_pace_streak: number;
215
+ }[];
216
+ export declare const achievements: {
217
+ key: string;
218
+ name: string;
219
+ description: string;
220
+ unlocked_at: string;
221
+ }[];
222
+ export declare const netWorthHistory: {
223
+ date: string;
224
+ total_assets: number;
225
+ total_liabilities: number;
226
+ net_worth: number;
227
+ }[];
228
+ export declare const investmentTransactions: {
229
+ investment_transaction_id: string;
230
+ account_id: string;
231
+ security_id: string;
232
+ date: string;
233
+ name: string;
234
+ quantity: number;
235
+ amount: number;
236
+ price: number;
237
+ fees: number;
238
+ type: string;
239
+ subtype: string;
240
+ iso_currency_code: string;
241
+ }[];
242
+ export declare const recurringBills: {
243
+ name: string;
244
+ amount: number;
245
+ day_of_month: number;
246
+ type: string;
247
+ account_id: string;
248
+ }[];
249
+ export declare const memories: {
250
+ content: string;
251
+ category: string;
252
+ }[];