<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Multi Asset Portfolio Analytics on Lenny Kiruthu</title>
        <link>https://lennykiruthu.xyz/multi-asset-portfolio-analytics-platform/</link>
        <description>Recent content in Multi Asset Portfolio Analytics on Lenny Kiruthu</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en-us</language><atom:link href="https://lennykiruthu.xyz/multi-asset-portfolio-analytics-platform/index.xml" rel="self" type="application/rss+xml" /><item>
            <title>Architecture</title>
            <link>https://lennykiruthu.xyz/multi-asset-portfolio-analytics-platform/architecture/</link>
            <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
            <guid>https://lennykiruthu.xyz/multi-asset-portfolio-analytics-platform/architecture/</guid>
            <description>&lt;h1 id=&#34;architecture&#34;&gt;Architecture&#xA;&lt;/h1&gt;&lt;p&gt;This page documents the current architecture of the platform. I update it as the system evolves.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Last updated:&lt;/strong&gt; April 2026&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;stack-overview&#34;&gt;Stack Overview&#xA;&lt;/h2&gt;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Layer&lt;/th&gt;&#xA;          &lt;th&gt;Technology&lt;/th&gt;&#xA;          &lt;th&gt;Notes&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Data Sources&lt;/td&gt;&#xA;          &lt;td&gt;yFinance, FRED API&lt;/td&gt;&#xA;          &lt;td&gt;Equity prices + macro indicators&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Storage&lt;/td&gt;&#xA;          &lt;td&gt;PostgreSQL&lt;/td&gt;&#xA;          &lt;td&gt;Single database, multiple schemas&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Transformation&lt;/td&gt;&#xA;          &lt;td&gt;dbt&lt;/td&gt;&#xA;          &lt;td&gt;Staging → intermediate → mart pattern&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Orchestration&lt;/td&gt;&#xA;          &lt;td&gt;Dagster&lt;/td&gt;&#xA;          &lt;td&gt;Scheduled jobs + event sensors&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Auth / API&lt;/td&gt;&#xA;          &lt;td&gt;FastAPI&lt;/td&gt;&#xA;          &lt;td&gt;JWT-based auth, user management&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Frontend&lt;/td&gt;&#xA;          &lt;td&gt;Streamlit&lt;/td&gt;&#xA;          &lt;td&gt;Transaction input + personal dashboards&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Deployment&lt;/td&gt;&#xA;          &lt;td&gt;Docker&lt;/td&gt;&#xA;          &lt;td&gt;Compose-based, self-hosted&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;data-sources&#34;&gt;Data Sources&#xA;&lt;/h2&gt;&lt;h3 id=&#34;yfinance&#34;&gt;yFinance&#xA;&lt;/h3&gt;&lt;p&gt;Yahoo Finance is the primary source for daily equity prices. When a user logs a transaction for a ticker the system hasn&amp;rsquo;t seen before, it backfills the full available price history for that ticker into &lt;code&gt;bronze.raw_prices&lt;/code&gt; before writing the transaction. This ensures the daily holdings computation has complete data.&lt;/p&gt;&#xA;&lt;h3 id=&#34;fred-federal-reserve-economic-data&#34;&gt;FRED (Federal Reserve Economic Data)&#xA;&lt;/h3&gt;&lt;p&gt;Macroeconomic indicators are pulled monthly from the FRED API into &lt;code&gt;bronze.raw_fred&lt;/code&gt;. These are used downstream to classify macro regimes (e.g. expansionary vs. contractionary) which are layered onto portfolio performance charts as context.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;dbt-transformation-layer&#34;&gt;dbt Transformation Layer&#xA;&lt;/h2&gt;&lt;p&gt;The dbt project follows a standard three-layer medallion pattern inside a &lt;code&gt;default&lt;/code&gt; schema group:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;bronze (raw)  →  staging  →  intermediate  →  mart (gold)&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;staging-models&#34;&gt;Staging models&#xA;&lt;/h3&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;stg_raw_prices&lt;/code&gt;&lt;/strong&gt; — Cleans and types the raw yFinance price data.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;stg_fred_macro&lt;/code&gt;&lt;/strong&gt; — Cleans and types the raw FRED indicator data.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;stg_transactions&lt;/code&gt;&lt;/strong&gt; — Cleans user-submitted transaction records.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;intermediate-models&#34;&gt;Intermediate models&#xA;&lt;/h3&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;int_daily_holdings&lt;/code&gt;&lt;/strong&gt; — Reconstructs each user&amp;rsquo;s holdings on every calendar day based on their transaction history. This is the core computational model.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;fct_daily_returns&lt;/code&gt;&lt;/strong&gt; — Joins daily holdings against prices to produce daily return figures per user, per asset.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;fct_macro_regimes&lt;/code&gt;&lt;/strong&gt; — Classifies each date into a macro regime using the FRED indicators.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;mart-models-gold-layer&#34;&gt;Mart models (gold layer)&#xA;&lt;/h3&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;gold_macro_context&lt;/code&gt;&lt;/strong&gt; — Macro regime data formatted for dashboard consumption.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;gold_p_timeseries&lt;/code&gt;&lt;/strong&gt; — Portfolio-level daily time series (value, returns, drawdown) per user.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;gold__performance&lt;/code&gt;&lt;/strong&gt; — Aggregate performance metrics per user.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;gold_ticker_kpis&lt;/code&gt;&lt;/strong&gt; — Per-ticker KPIs: total return, holding period, cost basis vs. current value.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;dim_assets&lt;/code&gt;&lt;/strong&gt; — Asset dimension table: ticker metadata, sector, asset class.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;The DAG flows from &lt;code&gt;raw_prices&lt;/code&gt; and &lt;code&gt;raw_fred&lt;/code&gt; (source nodes) through the staging and intermediate layers into the gold marts.&#xA;&lt;img alt=&#34;alt&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;900px&#34; data-flex-grow=&#34;375&#34; height=&#34;490&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://lennykiruthu.xyz/multi-asset-portfolio-analytics-platform/architecture/images/dbt_lineage.png&#34; srcset=&#34;https://lennykiruthu.xyz/multi-asset-portfolio-analytics-platform/architecture/images/dbt_lineage_hu_e2a54f0250286181.png 800w, https://lennykiruthu.xyz/multi-asset-portfolio-analytics-platform/architecture/images/dbt_lineage_hu_3d115b57b7dcd10d.png 1600w, https://lennykiruthu.xyz/multi-asset-portfolio-analytics-platform/architecture/images/dbt_lineage.png 1838w&#34; width=&#34;1838&#34;&gt;&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;orchestration-dagster&#34;&gt;Orchestration (Dagster)&#xA;&lt;/h2&gt;&lt;p&gt;Three jobs/sensors manage pipeline execution:&lt;/p&gt;&#xA;&lt;h3 id=&#34;daily_market_data_ingestion&#34;&gt;&lt;code&gt;daily_market_data_ingestion&lt;/code&gt;&#xA;&lt;/h3&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Schedule:&lt;/strong&gt; 06:00 PM UTC, Monday–Friday&lt;/li&gt;&#xA;&lt;li&gt;Fetches updated prices from yFinance for all tickers in the system, then triggers a full dbt run to refresh all downstream models.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;monthly_fred_macro_refresh&#34;&gt;&lt;code&gt;monthly_fred_macro_refresh&lt;/code&gt;&#xA;&lt;/h3&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Schedule:&lt;/strong&gt; 06:00 AM UTC, 1st of each month&lt;/li&gt;&#xA;&lt;li&gt;Pulls updated FRED indicators and refreshes the macro context models.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;transaction_sensor&#34;&gt;&lt;code&gt;transaction_sensor&lt;/code&gt;&#xA;&lt;/h3&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Type:&lt;/strong&gt; Standard sensor (event-driven)&lt;/li&gt;&#xA;&lt;li&gt;Monitors for new transactions written to &lt;code&gt;bronze.transactions&lt;/code&gt;. When triggered, it backfills price history for any new tickers and reruns the affected downstream models.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;storage-postgresql&#34;&gt;Storage (PostgreSQL)&#xA;&lt;/h2&gt;&lt;p&gt;A single Postgres instance with a schema-per-layer approach:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;bronze&lt;/code&gt; — Raw ingested data (managed by Dagster/ingestion scripts)&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;default&lt;/code&gt; — dbt-managed transformed models&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;public&lt;/code&gt; — Application tables managed by FastAPI (users, sessions)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;auth--api-fastapi&#34;&gt;Auth &amp;amp; API (FastAPI)&#xA;&lt;/h2&gt;&lt;p&gt;A FastAPI service handles:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;User registration and login&lt;/li&gt;&#xA;&lt;li&gt;JWT token issuance and validation&lt;/li&gt;&#xA;&lt;li&gt;Transaction write endpoints (with ticker validation before write)&lt;/li&gt;&#xA;&lt;li&gt;Serving portfolio data to the Streamlit frontend&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;frontend-streamlit&#34;&gt;Frontend (Streamlit)&#xA;&lt;/h2&gt;&lt;p&gt;The Streamlit app is the user-facing interface. It has two main sections:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;Transaction Ledger&lt;/strong&gt; — Log BUY/SELL transactions with ticker, quantity, price, and date. New tickers are validated and backfilled automatically.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Portfolio Analytics&lt;/strong&gt; — Personal dashboard showing portfolio performance, macro context overlays, and per-ticker KPIs. &lt;em&gt;(In active development as of April 2026.)&lt;/em&gt;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Authentication is handled via the FastAPI service — Streamlit calls the API on login and stores the JWT in session state.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;deployment-docker&#34;&gt;Deployment (Docker)&#xA;&lt;/h2&gt;&lt;p&gt;The full stack runs as a Docker Compose application:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;db&lt;/code&gt; — PostgreSQL&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;api&lt;/code&gt; — FastAPI service&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;dagster&lt;/code&gt; — Dagster webserver + daemon&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;app&lt;/code&gt; — Streamlit frontend&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Everything is containerised and runs on a single host. Persistent volumes are used for the database. The plan is to move this to a cloud VM (AWS EC2 or DigitalOcean Droplet) for persistent public hosting.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;whats-missing--in-progress&#34;&gt;What&amp;rsquo;s Missing / In Progress&#xA;&lt;/h2&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Streamlit analytics dashboard&lt;/strong&gt; — Currently migrating from Apache Superset. The transaction ledger is done; the analytics tab is being built.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;African market data sources&lt;/strong&gt; — The long-term data source expansion goal. No timeline yet.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Multi-user scaling&lt;/strong&gt; — The schema and models support multiple users already; the deployment doesn&amp;rsquo;t yet handle scale gracefully.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;CI/CD&lt;/strong&gt; — Currently manual deploys. Will add GitHub Actions.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
        </item><item>
            <title>Getting Started</title>
            <link>https://lennykiruthu.xyz/multi-asset-portfolio-analytics-platform/getting-started/</link>
            <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
            <guid>https://lennykiruthu.xyz/multi-asset-portfolio-analytics-platform/getting-started/</guid>
            <description>&lt;h1 id=&#34;getting-started&#34;&gt;Getting Started&#xA;&lt;/h1&gt;&lt;p&gt;The platform is self-hosted and currently in early access. Here&amp;rsquo;s how to get up and running.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;creating-an-account&#34;&gt;Creating an Account&#xA;&lt;/h2&gt;&lt;p&gt;Navigate to the platform URL and you&amp;rsquo;ll land on the login screen. Click &lt;strong&gt;Create Account&lt;/strong&gt; and fill in your email and password. There&amp;rsquo;s no email verification step right now — your account is created immediately.&lt;/p&gt;&#xA;&lt;p&gt;Once logged in, you&amp;rsquo;ll see the &lt;strong&gt;Portfolio Transaction Ledger&lt;/strong&gt; — the main interface for logging trades.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;logging-your-first-transaction&#34;&gt;Logging Your First Transaction&#xA;&lt;/h2&gt;&lt;p&gt;The sidebar on the left has everything you need:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Select a ticker&lt;/strong&gt; — Use the dropdown to pick a ticker symbol. If it&amp;rsquo;s a ticker the system hasn&amp;rsquo;t seen before, it will validate it against Yahoo Finance and backfill the full price history before saving. This can take a few seconds.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Choose transaction type&lt;/strong&gt; — BUY or SELL.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Set quantity&lt;/strong&gt; — Number of shares or units. Supports fractional shares.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Set price per unit&lt;/strong&gt; — The price you paid (or received) in USD.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Set the transaction date&lt;/strong&gt; — Defaults to today. You can backdate transactions to reflect your actual purchase history.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;The &lt;strong&gt;Total BUY value&lt;/strong&gt; at the bottom updates in real time as you adjust quantity and price.&lt;/p&gt;&#xA;&lt;p&gt;Hit &lt;strong&gt;Submit&lt;/strong&gt; and your transaction is written to the ledger. The &lt;code&gt;transaction_sensor&lt;/code&gt; in Dagster will pick it up and update your portfolio models on the next sensor tick.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;viewing-your-portfolio&#34;&gt;Viewing Your Portfolio&#xA;&lt;/h2&gt;&lt;p&gt;The &lt;strong&gt;Portfolio Analytics&lt;/strong&gt; tab (next to Transactions) will show your portfolio dashboard once you have transactions and the models have run. &lt;em&gt;(This tab is currently being built — check back soon.)&lt;/em&gt;&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;notes&#34;&gt;Notes&#xA;&lt;/h2&gt;&lt;ul&gt;&#xA;&lt;li&gt;Prices are in USD. Multi-currency support is not yet implemented.&lt;/li&gt;&#xA;&lt;li&gt;The platform pulls daily prices on weekdays at 6PM UTC. Your portfolio metrics update after each run.&lt;/li&gt;&#xA;&lt;li&gt;Macro context (FRED data) refreshes on the 1st of each month.&lt;/li&gt;&#xA;&lt;li&gt;If you log a transaction and the analytics don&amp;rsquo;t reflect it immediately, the pipeline may not have run yet. Give it until after 6PM UTC.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
        </item></channel>
</rss>
