A comprehensive wealth management platform for MTWI Financial Services, a Nigerian pre-licensing investment firm. The system provides dual interfaces — an admin panel for portfolio managers and a secure client portal for investors — with real-time market data, multi-currency reporting, compliance (KYC/AML), automated PDF report generation, and dividend tracking.

Wealth managers in emerging markets face a fragmented toolkit. Client assets are scattered across custodians like Bamboo, Trove, Chaka, and InvestNow, plus local bank accounts and alternative investments like real estate and private equity. Portfolios span USD and NGN, making exchange rate volatility a reporting challenge. PDF reports were compiled by hand in spreadsheets — error-prone and unscalable. KYC documentation was managed via email with no audit trail, and clients had no self-service window into their holdings.
The goal was a single platform that solves all of these — with the polish of a modern fintech product — while keeping operational complexity low for a small team.
Full-stack development — architecture, design, implementation, and deployment. I designed the dual-database architecture (Supabase + Appwrite), built the admin panel with full CRUD for all entities, engineered a token-based client authentication system with optional email 2FA, implemented real-time price streaming via SSE proxying TradingView WebSocket data, created a live exchange rate feed, developed automated PDF report generation with Modified Dietz return calculation, built a multi-step KYC submission flow, and designed the client-facing portfolio dashboard.
Operational data (clients, portfolios, trades, prices, exchange rates) lives in Supabase (PostgreSQL) for relational integrity and analytics queries. Compliance data (KYC submissions, uploaded ID documents, dividend records) lives in Appwrite for document storage with built-in file handling. This separation means the compliance database can be locked down independently without bloating the relational schema.
Instead of creating a Supabase Auth user per client (which incurs per-seat costs), the system uses a portfolio_tokens table. Each client gets a unique URL with a token, optional username/password, and optional email 2FA. The auth layer provides password validation, 6-digit OTP via email with 10-minute expiry, 5-attempt lockout with brute-force protection, and replay-proof verification.
Server-Sent Events (SSE) via ReadableStream provides real-time updates without WebSocket servers — no sticky sessions needed on Vercel serverless. Two SSE streams run: one proxies TradingView's WebSocket adapter pushing price updates every 200ms with direction indicators, and another polls open.er-api.com every 30 seconds for the USD/NGN rate.
The client-facing portal provides a read-only, privacy-aware dashboard with six tabs:






The platform includes optional email-based two-factor authentication, a multi-step KYC submission form with ID document upload and admin review workflow, sliding window rate limiting (100 req/min per IP) on API routes, security headers (HSTS, CSP, X-Frame-Options), and a custom logger that prevents PII from appearing in production logs. A privacy mode toggle hides monetary values on screen.
Automated PDF report generation with Modified Dietz time-weighted return calculation, benchmark comparison (S&P 500, NGX All-Share), P&L attribution by category and sector, currency allocation breakdown, and professional PDF layout using @react-pdf/renderer.
The platform replaced a manual spreadsheet-based workflow with a unified digital experience. Clients get real-time visibility into their portfolio value, holdings, and performance — previously available only through periodic email updates. Portfolio managers manage all client data from a single admin panel instead of juggling multiple custodial platforms. Compliance moved from ad-hoc email submissions to a structured KYC workflow with document storage and audit trail. Reporting went from manual spreadsheet compilation to one-click PDF generation with professional branding.
I'm currently available for freelance and contract work. Send me a message and I'll respond within 24 hours.