Skip to main content
Nexural — Full-Stack Fintech Platform hero image

A trading platform built like an institution. Run by one person.

Fintech
Nexural — Full-Stack Fintech Platform

A trading platform built like an institution. Run by one person.

A production-grade trading platform with real-time execution, portfolio tracking, a Discord-native AI companion, and Stripe billing.

185
DB Tables
69
API Endpoints
61
Test Suites
0
Billing Incidents
Problem

The challenge

Most trading tools fall into one of two categories: consumer apps so simplified they're useless for serious traders, or enterprise platforms so complex they require an ops team to configure. There was no middle ground for the technically sophisticated individual trader who wanted full control without the overhead.

Nexural was built to fill that gap — a production-grade fintech platform with real-time execution, portfolio tracking, a Discord-native AI companion, and a subscription billing layer that doesn't break on webhook retries.

The challenge: building a system at this scale — 185 relational database tables, 69 API endpoints, real-time market data ingestion, and a Stripe integration that needs to survive every edge case — without a dedicated backend team, a QA department, or a six-month runway for architecture review.

Approach

How we built it

PostgreSQL on Supabase with Row-Level Security for multi-tenant isolation and real-time subscriptions via Supabase channels for live portfolio updates. Backend: FastAPI (Python) — async-first, typed with Pydantic, organized into domain-bounded modules. Frontend: Next.js with server components for data-heavy pages and client components for interactive trading UI.

Real-time via WebSocket connections for live price feeds; Supabase realtime for portfolio state synchronization. Stripe with idempotent webhook handlers, subscription lifecycle management, and metered feature gating. Discord bot backed by OpenAI GPT-4 with tool-calling for portfolio queries, market commentary, and alert management.

The philosophy: don't build features until the foundation is right. The first two weeks were data modeling — no UI, no API, just entity-relationship design, understanding where the RLS policies needed to live, and mapping every Stripe event to a database state.

Architecture

System map

How the pieces talk to each other.

Nexural ArchitectureA user request flows from the Next.js frontend to a FastAPI backend, which orchestrates a Postgres database with row-level security, Stripe webhooks, Supabase realtime channels, and a Discord bot powered by GPT-4.UsertraderNext.jsApp Router + RSCFastAPIasync / PydanticPostgres + RLS185 tablesStripe WebhooksidempotentSupabase RealtimeWS channelsDiscord BotGPT-4 tool callsMarket FeedWebSocketGitHub Actions61 test suitesHTTPSRESTSQLeventsAPIquoteslive updatesdeploysLEGENDREQUESTREALTIME
Built UI

Selected screens

Real product surfaces from the engagement — not stock illustrations.

Nexural dataset and pipeline dashboard listing 47 datasets and a 1.2TB ML store
1 / 2

Datasets dashboard — 47 sources, RLS-isolated, real-time ingestion telemetry on every row.

Evidence

What it actually looks like

Architecture diagrams, CI runs, and dashboards from the engagement — not stock illustrations.

ArchitectureSystem map
The full Nexural surface area: trading platform, Discord-native AI bot, billing, real-time market data, and the seven services that talk to each other through typed contracts.
The full Nexural surface area: trading platform, Discord-native AI bot, billing, real-time market data, and the seven services that talk to each other through typed contracts.
ReportCI · Playwright
61 test suites green on every PR. End-to-end coverage across trading flows, billing webhooks, and Discord bot interactions — the regression net that lets one engineer ship into production.
61 test suites green on every PR. End-to-end coverage across trading flows, billing webhooks, and Discord bot interactions — the regression net that lets one engineer ship into production.
DashboardLighthouse CI
Performance, accessibility, and best-practices budgets enforced in CI. The dashboard fails the build if a PR regresses the user-facing surface — not after launch.
Performance, accessibility, and best-practices budgets enforced in CI. The dashboard fails the build if a PR regresses the user-facing surface — not after launch.
Build

What shipped

185 PostgreSQL tables with full RLS policy coverage. 69 REST/WebSocket API endpoints (FastAPI). Real-time portfolio dashboard (Next.js + Supabase realtime). Trade execution interface with order management. Discord AI bot: portfolio queries, alerts, natural-language market commentary.

Stripe billing: subscription tiers, metered usage, trial periods, webhook idempotency. 61 test suites: unit, integration, E2E, contract, and security tests. CI/CD pipeline with GitHub Actions — no PR merges without passing gates.

The Stripe webhook layer is built on idempotency keys and event deduplication — a pattern now templated into micro-saas-starter on GitHub. The Discord bot uses function-calling to query the live portfolio API, meaning it responds to "how is my AAPL position" with real data, not hallucinated commentary.

Outcome

Results

Platform operational: live, stable, serving active users. Zero billing incidents since launch — the idempotent webhook architecture holds. AI bot handling 200+ natural-language portfolio queries per week.

61 test suites provide regression coverage for all critical paths. Architecture patterns extracted into 3 open-source templates used in subsequent projects.

A single engineer, with the right architecture discipline and AI-assisted development workflow, can build and maintain a system at this complexity level. The 185-table schema isn't a vanity number — it's a data architecture that needed to be right before anything was built on top of it.

Artifacts

Available

  • Database schema overview (anonymized)
  • Webhook idempotency pattern documentation
  • Discord bot architecture diagram
  • CI/CD pipeline configuration template
References

Talk to people on this work.

No fabricated quotes. Reference contacts are shared during discovery, with both parties' consent.

Reference available

Engineering lead

Fintech · 5 years

Worked alongside on production trading systems for 5+ years. Available for technical reference calls — code quality, on-call discipline, incident behavior.

Reference call shared during discovery, both consenting.
Reference available

Founder

Studio engagement

Engaged Sage Ideas for a Ship + Operate combination. Willing to talk about scope discipline, timeline accuracy, and what handoff actually looked like.

Reference call shared during discovery, both consenting.
185 tables isn't a vanity number. It's the data model that needed to be right before anything else could be built on top of it.
// build log · entry 04
Honesty

What almost happened.

Every project has near-misses. Decisions that, if we'd kept going, would have shipped a hole. The list below is the diff between the version that almost made it to prod and the version that did.

// near-miss · 01
diff
-
beforeWebhook handler retries on Stripe were going to double-charge in 6 of the 47 event types we cared about.
+
afterBuilt an idempotency table keyed on (event_id, customer_id) with a transactional INSERT-OR-NOOP and replay the side-effect from the persisted row.
$
cost~$0 in customer refunds. Zero billing incidents since launch.
// near-miss · 02
diff
-
beforeFirst architecture sketch put RLS on the API layer — the application would enforce tenant isolation in code.
+
afterMoved every multi-tenant table to Postgres RLS with role-bound policies. The DB refuses cross-tenant reads even if the app forgets to filter.
$
costTwo extra weeks. One catastrophic bug class permanently impossible.
// near-miss · 03
diff
-
beforeReal-time portfolio was about to be polled every 5s from the client — 12 RPS per active session.
+
afterSupabase realtime channels with row-level filters; client subscribes, server pushes. Same data, ~0.2 RPS per session.
$
costServer bill stayed under $80/mo at 200+ AI queries/week.
From the repo

Inline excerpts.

Trimmed, but real. These are the patterns that made the system survive Stripe retries, multi-tenant queries, and a Discord bot that won't hallucinate positions.

Stripe webhook idempotency
python
# webhook_handler.py — production excerpt
@router.post("/stripe/webhook")
async def handle_stripe(req: Request, db: Db):
    event = stripe.Webhook.construct_event(
        await req.body(),
        req.headers["stripe-signature"],
        STRIPE_WEBHOOK_SECRET,
    )

    # idempotency: insert-or-noop on (event_id) primary key
    inserted = await db.fetchval("""
        INSERT INTO stripe_events (event_id, type, payload, status)
        VALUES ($1, $2, $3, 'pending')
        ON CONFLICT (event_id) DO NOTHING
        RETURNING event_id
    """, event["id"], event["type"], event.data.object)

    if inserted is None:
        # we've seen this event. ack and exit.
        return {"ok": True, "replay": True}

    # side-effect runs exactly once, inside a transaction
    async with db.transaction():
        await process_event(event, db)
        await db.execute(
            "UPDATE stripe_events SET status='processed' WHERE event_id=$1",
            event["id"],
        )
    return {"ok": True}
// Replay-safe by construction. Same event delivered twice does nothing the second time.
Row-level security on portfolios
sql
-- migrations/2026_03_rls_portfolios.sql
ALTER TABLE portfolios ENABLE ROW LEVEL SECURITY;

CREATE POLICY portfolios_owner_select
  ON portfolios FOR SELECT
  USING (owner_id = auth.uid());

CREATE POLICY portfolios_owner_modify
  ON portfolios FOR ALL
  USING (owner_id = auth.uid())
  WITH CHECK (owner_id = auth.uid());

-- Even an authenticated client running raw SQL via PostgREST
-- cannot read another user's row. Verified in 14 contract tests.
// Tenant isolation enforced by Postgres, not by the application.
Discord AI bot — function-calling against the live API
typescript
// bots/discord/tools.ts — production excerpt
export const tools = [
  {
    type: 'function',
    function: {
      name: 'get_position',
      description: 'Fetch a live position for the calling user',
      parameters: {
        type: 'object',
        properties: { symbol: { type: 'string' } },
        required: ['symbol'],
      },
    },
  },
] as const

export async function execute(tool: ToolCall, ctx: BotCtx) {
  if (tool.function.name === 'get_position') {
    const { symbol } = JSON.parse(tool.function.arguments)
    // Real API call — same auth, same RLS as the web app.
    return await api.get('/positions/' + symbol, { token: ctx.userToken })
  }
  throw new Error('unknown tool')
}
// The bot does not hallucinate positions. It calls the same endpoint the dashboard does.
livebuild 29be8ec2026-06-11 06:38Z
// solo studio// no analytics resold// every commit human-reviewed