Opula
Opula is an analyst that only looks at your money.
What it can do
- Add Txn: Insert one or more transactions in a single call. `transactions` is an array, pass one entry for a single trade, many for a CSV / brokerage export / paste import. Rows are sorted by date asce
- Edit Txn: Update fields of an existing transaction by id. Only provided fields are changed. When `price` is supplied it is converted to USD at the transaction date before storing. The native currency
- Delete Txn: Delete a single transaction by its id. The user must confirm, never call this without explicit instruction (e.g. "delete transaction #42"). Removing a transaction recalculates all subseque
What data it sees
Do you need an account
No: the server works without sign-in
Opula is an analyst that only looks at your money. You describe what you own in plain language, then ask: why your portfolio moved, what your effective exposure is after looking through your ETFs, how concentrated you actually are, or when work becomes optional.
Opula has its own chat, so you can ask there directly. This connector adds a second way in: connect it and you can ask the same questions from Claude or ChatGPT. Same holdings, same math, same numbers wherever you ask.
Every figure is computed server-side by deterministic rules rather than generated by the model, so the same question returns the same number. There is no bank linking and no API keys to set up: sign in with Google and connect in one step. Financial data is isolated per user at the database level (Postgres RLS).
Built for real multi-currency portfolios: stocks, ETFs, crypto, real estate and cash across USD, KRW, JPY and more, covering 47 asset types and 9 exchanges, including assets a bank cannot sync such as 전세 (jeonse) deposits, unlisted equity and KRX gold.
New accounts start on a 14-day Pro trial with 30 questions a day, no card required. After that, Free covers 3 questions a day and Pro is $9.99/month (or $99/year) for 100. Recording, edits and drill-downs never count against either plan, and no analysis is locked behind a paywall.
Server tool list (41)
Raw names from tools/list. Only developers need these.
| add_txn | Insert one or more transactions in a single call. `transactions` is an array, pass one entry for a single trade, many for a CSV / brokerage export / paste import. Rows are sorted by date ascending before insertion (average cost depends on insertion order). For bulk imports: first show the user your detected column mapping and total row count, wait for their confirmation, then call this once with all rows. Types: buy/sell move shares; deposit adds shares (set price=0 for grants/transfers, or actual cost basis); dividend/tax record cash events (price = total amount, shares = 1). **Reason capture (critical for thesis tracking)**: For NEW buy/sell trades, not historical bulk imports, the `reason` field is the single highest-leverage input you can capture for the user's long-term self-feedback loop (powers `show_thesis_track` later). When the user logs a NEW single buy/sell without supplying a reason, ALWAYS ask before calling: "What's your reason for this trade? Even one short sentence, 'earnings beat thesis', 'rebalancing toward defensive', 'avg-down on dip', is enough." Then include their answer in `reason`. Skip the ask only when (a) the user is explicitly migrating historical data, or (b) the trade type is deposit/dividend/tax (no investment decision). Each row carries its own `asset_type` (default 'stock'), which splits two flows: - stock: `price` is in the market's native currency (US→USD, KRX/KOSDAQ→KRW, JP→JPY, HK→HKD, LSE→GBP, XETRA→EUR, NSE→INR, TW→TWD), `market` determines it, the `currency` param is ignored. Non-US holdings must set `market` to the listing exchange; `ticker` stays the bare local code (e.g. market: "KRX", ticker: "005930"). - crypto / commodity / real_estate / other: there is no market or price provider, these are priced manually (keep the current value fresh with add_price). `market` is ignored and stored null, `ticker` is a free-form name (e.g. "Bitcoin", "Seoul apartment"), and `price` is in the `currency` param (default USD). In both cases `price` is converted to USD at the transaction `date` before storing, every stored price is USD. A single-entry call returns the inserted transaction; a multi-entry call returns a per-ticker / per-type summary. |
| edit_txn | Update fields of an existing transaction by id. Only provided fields are changed. When `price` is supplied it is converted to USD at the transaction date before storing. The native currency it is interpreted in depends on the (effective) asset type: for a stock it is the market's native currency (the new `market` if passed, else the existing one); for a non-stock asset it is the `currency` param (default USD). Changing `asset_type` mirrors add_txn's fork: switching TO a non-stock type (crypto/commodity/real_estate/other) clears `market` to null (any `market` arg is ignored); switching TO 'stock' uses the supplied `market` (or keeps the existing one, defaulting to US). |
| delete_txn | Delete a single transaction by its id. The user must confirm, never call this without explicit instruction (e.g. "delete transaction #42"). Removing a transaction recalculates all subsequent positions/avg cost since holdings are derived from the log. |
| add_price | Set the current price of ONE non-stock asset (crypto / commodity / real_estate / other). These assets have no market data provider, so their market value is entered manually — stocks are auto-priced on read and return an error here. `name` is matched case-insensitively against the user's non-stock holdings (the free-form ticker used on their transactions, e.g. "Bitcoin", "KRX gold"). `value` is the price PER SHARE/UNIT in `currency` (default USD), converted to USD at today's date before storing. The new price flows into get_market_brief / show_portfolio / simulate_scenario automatically on the next read. Call it again whenever the user reports a new valuation; each call is dated today and the latest date wins. |
| add_balance | Upsert one or more balance sheet entries (also edits, same composite key overwrites). `entries` is an array: pass one entry to record a single balance item, many to import a net-worth spreadsheet (rows = months, columns = categories) in one call. Prefer add_monthly for full month-end settlement. VALID BALANCE CATEGORIES (each entry is category (한국어, sub_type=x)), assets: cash (현금·입출금 예금, sub_type=cash), savings (적금·예금, sub_type=cash), usd_cash (달러 현금, sub_type=cash), deposit (증권사 예수금, sub_type=cash), housing_sub (주택청약저축, sub_type=cash), cash_other (기타 현금성 자산, sub_type=cash), domestic_stock (국내주식, sub_type=investment), overseas_stock (해외주식, sub_type=investment), pension (연금(퇴직·개인), sub_type=investment), real_estate (부동산(거주·투자), sub_type=other), jeonse_deposit (전월세보증금(임차), sub_type=other), vehicle (차량, sub_type=other), asset_other (기타 자산, sub_type=other). Liabilities: credit_card (카드 대금, sub_type=short_term), short_term_other (기타 단기 부채, sub_type=short_term), loan (대출, sub_type=long_term), long_term_other (기타 장기 부채, sub_type=long_term). Use ONLY these category/sub_type pairs, do NOT invent your own (show_categories returns this catalog). **STOCKS BELONG HERE.** wealth net worth is BOOK-based (the recorded month-end ledger), so at settlement record your holdings value under domestic_stock / overseas_stock like any other asset, a month with no recorded stock value simply has no stock in its net worth. This is SEPARATE from the live market view (get_market_brief values the portfolio intraday from the transaction log): the two surfaces are independent and can legitimately differ (month-end book vs live), and recording stock in the balance does NOT double-count because wealth never fuses the live value. The balance covers the whole net-worth ledger: bank cash, savings, usd_cash, deposits, stocks (domestic_stock / overseas_stock), other trading assets (asset_other), real estate, vehicle, pension, physical assets. For non-USD users: pass `currency` as the native currency (e.g. "KRW") and `amount` in that currency, converted to USD via historical FX at `date`. Use category="usd_cash" (currency="USD") for USD-denominated CASH held alongside home-currency cash (it should be sub_type="cash" so it counts as liquid). A single-entry call returns the upserted entry; a multi-entry call returns counts grouped by period. |
| add_flow | Upsert one or more cash flow entries (also edits, same composite key overwrites). `entries` is an array: pass one entry to record a single flow item, many to import an income/expense spreadsheet (rows = months, columns = categories) in one call. Prefer add_monthly for full month-end settlement. CASH BASIS: an expense is the cash that actually left the account this period (last month's card bill paid this month = this month's expense), NOT a card swipe that has not settled. An unpaid card charge is a balance-sheet liability (credit_card), never a cash deduction here. The balance sheet stays accrual (unpaid card = credit_card liability); the two axes use different bases by design. VALID FLOW CATEGORIES (each entry is category (한국어, sub_type=x)), income: salary (급여, sub_type=employment), business (사업 소득, sub_type=employment), dividends (배당, sub_type=investment), interest (이자, sub_type=investment), income_other (기타 수입, sub_type=other). Expense: personal (생활비·소비, sub_type=consumption), insurance (보험, sub_type=fixed), phone (통신, sub_type=fixed), utilities (공과금, sub_type=fixed), rent (월세·주거비, sub_type=housing), maintenance (관리비, sub_type=housing), loan_repayment (대출 상환, sub_type=debt), expense_other (기타 지출, sub_type=other). Use ONLY these category/sub_type pairs, do NOT invent your own (show_categories returns this catalog). A single-entry call returns the upserted entry; a multi-entry call returns counts grouped by period. |
| edit_balance | Partial update of a single balance entry, identified by composite key (period + type + sub_type + category). Use this, not add_balance, when changing one field on an existing entry (e.g. fix a typo in memo without touching amount). Pass only the fields you want to change. `amount` and `currency` must be supplied together (Opula re-converts via historical FX at the entry's date, or the new `date` if provided); if you omit them, the stored USD amount is preserved exactly. `memo` accepts a string to set, or null to clear. Returns the updated entry; errors if no row matches the key. |
| edit_flow | Partial update of a single flow entry, identified by composite key (period + type + sub_type + category). Use this, not add_flow, when changing one field on an existing entry (e.g. fix a typo in memo without touching amount). Pass only the fields you want to change. `amount` and `currency` must be supplied together (Opula re-converts via historical FX at the entry's date, or the new `date` if provided); if you omit them, the stored USD amount is preserved exactly. `memo` accepts a string to set, or null to clear. Returns the updated entry; errors if no row matches the key. |
| add_monthly | Batch upsert balance and flow entries for a single period in one call. **Primary tool for month-end settlement.** When the user says "month-end closing" or "record this month", do NOT just display data. Instead: (1) present the full category checklist (call show_categories) and ask for each balance category and each cash flow category; (2) confirm the numbers; (3) call this tool once with the full arrays. VALID BALANCE CATEGORIES: assets: cash (현금·입출금 예금, sub_type=cash), savings (적금·예금, sub_type=cash), usd_cash (달러 현금, sub_type=cash), deposit (증권사 예수금, sub_type=cash), housing_sub (주택청약저축, sub_type=cash), cash_other (기타 현금성 자산, sub_type=cash), domestic_stock (국내주식, sub_type=investment), overseas_stock (해외주식, sub_type=investment), pension (연금(퇴직·개인), sub_type=investment), real_estate (부동산(거주·투자), sub_type=other), jeonse_deposit (전월세보증금(임차), sub_type=other), vehicle (차량, sub_type=other), asset_other (기타 자산, sub_type=other). Liabilities: credit_card (카드 대금, sub_type=short_term), short_term_other (기타 단기 부채, sub_type=short_term), loan (대출, sub_type=long_term), long_term_other (기타 장기 부채, sub_type=long_term). VALID FLOW CATEGORIES: income: salary (급여, sub_type=employment), business (사업 소득, sub_type=employment), dividends (배당, sub_type=investment), interest (이자, sub_type=investment), income_other (기타 수입, sub_type=other). Expense: personal (생활비·소비, sub_type=consumption), insurance (보험, sub_type=fixed), phone (통신, sub_type=fixed), utilities (공과금, sub_type=fixed), rent (월세·주거비, sub_type=housing), maintenance (관리비, sub_type=housing), loan_repayment (대출 상환, sub_type=debt), expense_other (기타 지출, sub_type=other). Use ONLY these category/sub_type pairs, do NOT invent your own. **RECORD STOCKS at settlement too.** wealth net worth is BOOK-based: at month-end record your holdings value under domestic_stock / overseas_stock alongside cash, savings, usd_cash, deposits, real estate, vehicle, pension, and physical assets, the WHOLE ledger, so past months carry an accurate month-end net worth. This is the structural/defense book and is SEPARATE from the live intraday portfolio value in get_market_brief (from the transaction log); the two can legitimately differ and recording stock here does NOT double-count (wealth never fuses the live value). (usd_cash should be sub_type="cash" so it counts as liquid.) After recording, call get_wealth_brief to show the confirmed summary. |
| delete_balance | Delete balance entries for a period. If category is provided, only that single entry is removed; otherwise all entries for the period are deleted. |
| delete_flow | Delete flow entries for a period. If category is provided, only that single entry is removed; otherwise all entries for the period are deleted. |
| show_portfolio | Current holdings derived from the user's transaction log: ticker, shares, market value, weight, today's and total P&L (absolute and %), sector/country, plus portfolio totals and a risk summary (Sharpe, volatility, drawdown) from snapshot history. All monetary values come back in `display_currency` (default USD). This is a drill-down: for any check-in or 'what should I do' question call get_market_brief first, it includes this plus news, earnings, macro, and recommendations. Use show_portfolio when the holdings table itself is the only thing asked for. Numbers match get_market_brief exactly (same underlying brief). |
| show_concentration | Portfolio concentration by Herfindahl-Hirschman Index (HHI) across ticker, sector, and country, with top contributors per dimension, plus the effective-N (correlation-adjusted holdings count) and the pairwise 90-day correlation matrix. HHI >2500 is high, >5000 very high; two holdings at 0.9 correlation count as roughly one for diversification. effective_n / correlation matrix are null/empty until `sync` populates the correlation cache. Also returns `concentration.look_through` (null when no held ETF could be decomposed): effective single-name exposure after expanding held ETFs into their constituents, so a single broad ETF reads as diversified (not a false 100% ticker), and a name held both directly and inside funds (e.g. AAPL via VOO + QQQ + direct) surfaces as its true combined weight. Surface look_through.caveat verbatim (top-10 approximation). This is current exposure only, risk/forward over it is Pro. Same block get_market_brief returns; use this drill-down when only concentration is asked for. |
| show_txns | Full transaction log (buys, sells, deposits, dividends, taxes) ordered by date ascending. Pass `ticker` to filter to one symbol, useful for "show me all my AAPL trades". Works for non-stock asset names too (e.g. "Bitcoin"); the match is case-insensitive. `price` is returned in `display_currency` (default USD). |
| show_balance | Stored balance sheet entries (assets + liabilities) by period. Each entry has type (asset|liability), sub_type (cash|investment|other|short_term|long_term), category, amount in `display_currency` (default USD), and the entry date. Without `period`, returns ALL periods, use this for trends; with `period` (YYYY-MM), returns one month's snapshot. |
| show_flow | Stored monthly cash flow entries (income + expenses) by period. Each entry has type (income|expense), sub_type, category, amount in `display_currency` (default USD), date. Without `period` returns all periods; with `period` (YYYY-MM) returns one month. For trend analysis use get_wealth_brief, whose cash_flow.trend aggregates monthly income/expenses and adds savings rate. |
| get_wealth_brief | All-in-one structural read of the user's whole-wealth picture: the manual ledger (balance + flow entries) ONLY, wealth is book-only: trades and live portfolio value are never fused in. To include the stock portfolio in net worth, record its value as a monthly balance entry (the /month-end prompt walks through this); the live portfolio lives in get_market_brief. Bundles: net_worth (total assets − liabilities, + monthly trend and agency-vs-market attribution), cash_flow (income, expenses, net flow, savings_rate, savings_efficiency, monthly trend), income_concentration (income-TYPE concentration: per-category TTM share, largest, effective_n = 1/HHI; effective_n ≈ 1 flags single-income-type dependence, NOT employer diversification, opula can't see employers), liquidity (emergency runway + liquid/illiquid split), leverage (debt_to_asset PLUS debt-service ratio: monthly_debt_service = median-of-last-3-months debt expense, dsr_total_pct over all income, dsr_earned_pct over employment income only, each null when its income denominator is zero), allocation (whole-wealth asset-class breakdown from balance entries, the stock portfolio appears as whatever balance was recorded for it), structural_concentration (largest slice), goal (retirement tracking: target vs current net worth, years remaining, cagr_needed), and consistency (the wealth-tending habit signal: settlement streak, ledger tenure, thesis coverage, check-in days). Deterministic rule-derived facts only. Money fields come back in `display_currency` (default USD); a non-USD field is null when no FX rate is cached. cagr_actual is the realized net-worth CAGR from the ledger trend (savings + market combined, first → last period); it is null until the ledger spans 12 months. |
| show_streak | How consistently the user is tending their money, the wealth-management habit signal. Returns settlement_streak (consecutive months with a month-end record; status alive | at_risk = this month not yet settled but last month was | broken = a month was skipped), ledger_tenure_months (how long they've kept records, never decreases), thesis (buy/sell trades with a recorded reason, the judgment-discipline proxy), and check_in (days they showed up, last 30 + total). Currency-neutral (counts/dates only). Use it to celebrate a streak or milestone, to nudge a settlement when status is at_risk near month-end, or whenever the user asks how consistent they've been. |
| get_market_brief | Diagnostic: ALL-IN-ONE composite read of *current market & portfolio* state, today's positioning, regime, and recommendations. Call this for market, portfolio, regime, news, or 'what should I do about my positions right now' questions. For net-worth trajectory, savings, cash flow, runway, leverage, whole-wealth allocation, or retirement-goal questions, call `get_wealth_brief` instead, the structural/long-term sibling. Returns deterministic, rule-derived facts only, no forward-looking probability distributions, no caller-supplied assumptions. All monetary values come back in `display_currency` (default USD). Bundles: portfolio + holdings (with per-stock fundamentals), concentration (HHI + effective_n + 90d correlations), diversification_insight + diversification_gaps, movers, news, earnings_upcoming + ipo_calendar + dividend_calendar + economic_calendar, macro + signals (regime, stress, next_week_scenarios), global_macro + disasters, commodities, snapshot_comparison + week_performance, contribution (per-holding return-contribution attribution across 1주/1달/YTD windows: how many %p each holding added/subtracted, price-driven only), risk_summary, stance, recommendations (action/conviction/horizon/size_hint), rebalance_chain, watchlist, meta. show_* tools are drill-downs. Self-refreshing: held-stock quotes are refreshed once a day on read (the daily price cache); manual sync is not required. |
| show_dividend | Estimated annual dividend income across all holdings. Returns per-ticker yield, annual dividend-per-share, and estimated income in `display_currency` (default USD). Only includes tickers with dividend data in the price cache. |
| show_news | Recent company news from Finnhub for a single ticker, headline, summary, source, published timestamp, URL. Default lookback is 7 days, capped to `limit` items (default 10). For news across all holdings in one call, prefer get_market_brief which aggregates the last 24h. |
| show_financials | SEC-reported financials for a ticker: key income statement, cash flow, and balance sheet metrics from XBRL filings. Quarterly figures are rebuilt into discrete single-quarter values (10-Q reports are fiscal-YTD cumulative). |
| show_valuation | Valuation deep-dive for a single ticker: PEG ratio, Price/Sales, FCF yield, and (with a FRED key) equity risk premium and real FCF yield, computed from 8 quarters of SEC filings. Use for 'is X overvalued / cheap?', 'what's the PEG?'. Market cap and PE come from the price cache; revenue, EPS, operating cash flow, and capex come from XBRL filings. |
| show_earnings | Earnings calendar. Without a ticker, returns upcoming earnings for all held tickers. With a ticker, returns history + upcoming. |
| show_snapshot | Query portfolio snapshot history. Without `ticker`, returns daily total market value over time in `display_currency` (default USD). With `ticker`, returns that holding's per-day time series (shares, avg/current price). IMPORTANT: for any projection or "when will I reach X" question this alone is insufficient, pair it with get_market_brief (regime/stress/signals) and project_net_worth; linear extrapolation from past snapshots is a category error. |
| show_risk | Portfolio risk metrics from snapshot history: annualized return/volatility, daily 1σ, max drawdown (with peak/trough dates), Sharpe, Sortino, win rate, and beta vs a benchmark (benchmark candles come from keyless Yahoo, so beta needs no API key; the Fed funds risk-free rate uses a FRED key if present, else defaults to 5%). Requires at least 10 daily snapshots. Same metrics get_market_brief summarizes in risk_summary, use this drill-down for the full breakdown or a custom date range / benchmark. |
| show_benchmark | Compare portfolio return against benchmark indices (default: SPY and QQQ). Uses daily snapshot history for an accurate time-series comparison when ≥2 snapshots exist; otherwise falls back to cost-basis vs current value. Returns per-benchmark return and alpha. Benchmark candles come from keyless Yahoo data, no API key required. |
| show_technical | Chart-indicator snapshot for a ticker: RSI(14), Bollinger Bands(20,2σ), MACD(12,26,9), ATR(14), MA50/200, 52-week position, plus composite buy/sell setup_quality scores (0–100) with strong/moderate/weak/very-weak labels. EXECUTION-ONLY tool: measures short-term market positioning and volatility, NOT intrinsic value. Never use the composite scores alone to answer 'should I buy/sell X?', those belong to fundamentals (show_valuation, show_financials, get_market_brief.holdings[].fundamentals). Use show_technical AFTER a buy/sell decision is formed on fundamentals, to size entries, set stop losses, or time exits. Uses Yahoo data (no key required). |
| show_thesis_track | Review past trades that have a recorded `reason` (the thesis) and check whether it played out. Returns each thesis trade with buy price, current price, days held, price change %, whether still held, and a descriptive outcome label (validated_holding / underwater_holding / cut_loss / etc, labels, not judgments). Use for 'did my last trade work', 'track record of my AAPL theses', 'review my decisions', or when reasoning about a new buy in a name traded before. coverage_pct shows what fraction of trades have a recorded reason, if low, encourage adding `reason` when logging future trades. |
| show_anti_portfolio | Track the post-exit performance of positions the user has fully sold, the data brokerage apps deliberately don't show. For each ticker once held but no longer held, returns last sell date/price, current price, days since exit, post-sell change %, and a label: `missed_rebound` (+20%+ since sell, premature), `good_call` (-20%+, sell saved losses), `neutral_exit`, or `too_recent` (<14d). Current price comes from the cached price series; a ticker with no cached price shows `no_price_data`. Use when contemplating a sell or reviewing past decisions; pair with show_thesis_track. |
| project_net_worth | Thought experiment: forward-looking Monte Carlo projection of net worth under caller-supplied assumptions, multi-scenario. Runs monthly GBM under one or more (return, vol) pairs in parallel, adds a fixed monthly contribution, applies one-time life events, and returns P10 / P25 / P50 / P75 / P90 trajectories per scenario. `horizon_months` accepts any length from 1 (next month) to 720 (60 years), same tool answers short-horizon outlooks and long-horizon FIRE planning. `target_value` is optional: when supplied, the response includes probability of reaching it and median months to reach; when omitted, just the distribution at the horizon. All monetary inputs/outputs are in the requested `currency` (default USD). YOU MUST set `scenarios[].expected_annual_return` and `scenarios[].annual_volatility` explicitly and disclose them verbatim to the user, Opula does NOT bake in defaults. For reference, brief.risk_summary carries the user's realized 90d annualized_return_pct and annualized_vol_pct, those make a natural 'historical' scenario you can contrast with bear/bull overlays. Pass `seed` when comparing scenarios: with a seed, all scenarios share the same shock sequence so cross-scenario differences reflect (return, vol), not random noise; without one, each run draws fresh random shocks. For *current* portfolio state, use `get_market_brief`, that is the diagnostic tool; this is the thought-experiment tool. |
| simulate_scenario | **Call this for any short-horizon outlook question (1 day to 1 week).** Trigger phrases: "tomorrow", "next week", "this Tuesday", "this Wednesday", "this Thursday", "how might X day look", "what if [macro event] happens", "FOMC impact", "earnings impact", "how will my portfolio do". DO NOT answer these from the brief alone, the brief is diagnostic only. This tool turns the brief's measured data into a quant answer. Thought experiment: deterministic conditional analysis. Given hypothetical market shocks, returns per-holding and portfolio P&L impact based on beta. Unlike `project_net_worth` (stochastic forward distribution over months/years), this is a *point estimate per scenario* for the immediate future, "if S&P moves −2% and your TSLA beta is 1.6, your TSLA P&L is impact_usd". **Workflow**: (1) call `get_market_brief`, (2) read `holdings[].fundamentals.beta_5y` into `beta_overrides`, (3) read `risk_summary.daily_vol_pct` into `daily_vol_pct`, (4) construct 2-3 scenarios (e.g. bear / base / bull at equity_pct -0.02 / 0 / +0.02; or hawkish / dovish for a Fed event). Caller MUST supply the shocks (the assumption) and disclose them in the answer. Without beta_overrides each ticker defaults to 1.0; without daily_vol_pct the one-sigma range is null. Use for next-day outlook ("if FOMC is hawkish?"), idiosyncratic event sizing ("my biggest position reports tomorrow, what's the dollar range?"), specific-day outlook ("how will Tuesday trade"), and rebalance impact ("if I trim TSLA and rotate to JNJ?"). For *long-horizon* projection (months/years, FIRE planning) use `project_net_worth` instead. |
| show_fx | Inspect the FX rate cache (foreign per 1 USD). Without `currency`, returns per-currency coverage (count + first/last date). With `currency` + `date`, returns the point-in-time rate, falling back to the most recent within `lookback_days`. With `currency` + `from`/`to`, returns the cached series for that range. With `currency` alone, returns the `limit` most-recent rows. USD has no rows (it's the base, always 1.0). |
| show_macro | Curated macro snapshot in one view: 8 indicators (VIX, 10Y Treasury, yield curve, USD index, HY credit spread, breakeven inflation, Fed funds, plus FX vs the requested display currency, each with current value, 30d/90d delta, and 5y average), the Economic Stress Index (0–100 from 5 FRED series with per-component breakdown), the macro regime bias (Risk-on / Mixed / Risk-off), and next-week IF-THEN scenarios for upcoming high-impact events. This is the same macro + signals block get_market_brief returns, call this drill-down when only the macro picture is asked for. Requires a FRED API key on your account. |
| setup_status | One-shot setup diagnostic. Call this at the start of a conversation, any time you are unsure what ledger data exists, or when the user asks about their plan, billing, or how to cancel a subscription. Returns data counts (transactions / balance / flow / FX), whether the FRED key is configured, the plan + billing guidance (plan.billing says how this account got Pro and where cancellation lives), the profile, and next_steps for onboarding. |
| show_categories | The canonical ledger category catalog, every valid (type, sub_type, category) combination for add_balance / add_flow / add_monthly, with Korean labels. Call this BEFORE asking the user for balances or cash flow and present the checklist grouped in plain Korean, so they are prompted for asset classes and flow kinds they would otherwise forget (연금, 주택청약, 예수금, 달러 현금, 차량, 배당·이자, 보험·통신·주거비 등). Also the reference when unsure which category/sub_type a value belongs to, use ONLY these pairs, never invent new ones. |
| show_profile | Read the user's stored profile (birth year, retirement target year, target net worth, risk tolerance, free-form notes). Target net worth is returned in `display_currency` (default USD). Returns null if no profile is set. |
| set_profile | Update the user's profile. Partial updates, only provided fields are changed. All optional; capture only what the user volunteers. Birth year and retirement target year are 4-digit years; `target_net_worth` is in the supplied `currency` (default USD), stored internally as USD; risk tolerance is conservative/moderate/aggressive; notes is free-form. |
| sync | Backfill the historical FX rate cache from FRED (KRW/JPY/EUR/CNY/GBP/HKD/INR/TWD per USD, increment-only; requires fred_api_key). Needed so non-USD ledger entries convert to USD at their entry date. Rarely needed manually, entry tools fetch live rates for today-dated entries on their own. |
| start_trial | Start this account's one-time, free 60-day Opula Pro trial, full access to every Pro analysis tool, no payment, no card. Offer this whenever a Pro tool returns upgrade_required with trial_available:true. One trial per account; it cannot be restarted, and an account that already has Pro does not need it. |
| redeem_code | Redeem a one-time coupon code to unlock Opula Pro on this account. If the user has a coupon (e.g. from a crowdfunding reward), paste it here. Returns the outcome: redeemed (Pro granted), already used, invalid, or not signed in. |