Data Modeling
Forex & CFD broker data model: a free client-acquisition template

A broker data model is the set of tables – ad spend, sessions, leads, clients, deposits – and the keys that join them, arranged around the one question a retail forex or CFD brokerage lives on: what does a funded, trading client cost, and what are they worth once they arrive? The funnel is long and gated – a click becomes a lead, a lead a registration, a registration a KYC-verified client, and only then a first deposit and a first real trade – and a model that can’t follow a person across all of those steps can’t tell a cheap channel from a good one.
This page gives you a free, ready-made brokerage acquisition model you can open in your browser, edit like a diagram, and export to OKF – Google’s open, portable format. No sign-up. It’s one of the niche models in our data model template gallery; this one is built for the acquisition and funding side of a trading business. New to the vocabulary? Start with what data modeling is.
What this model covers – and where it stops
What this model covers – and where it stops
This model covers the whole client-acquisition and funding journey: budget bought across platforms and targeting countries, the visits it produces, the short lead-capture forms those visits leave behind, the sales desk that calls them, the identity checks that decide who gets in at all, and the deposits that determine whether any of it paid for itself – plus lifetime value, withdrawals, and the lifecycle segments that say what happened after.

The funnel, in the industry’s own vocabulary
If you run a brokerage, you already think in these five steps – the model is built around them so your dashboards and your data speak the same language:
1.Session – a visit from paid or organic traffic.
2.Short form (lead) – the moment a visitor stops being anonymous and leaves a phone number.
3.Long form (registration) – the full account sign-up.
4.FTD – First Time Depositor: the client’s first completed deposit. The point the whole funnel finally turns into cash.
5.NTC – New Trading Client: the first real (non-demo) trade.
Between steps 3 and 4 sits the gate that makes brokerage acquisition unlike any other funnel: KYC. A registered user who never clears identity verification never funds, and a model that skips that step will overstate how many of your sign-ups were ever going to convert.
What’s deliberately out of scope
It’s worth being precise: this model ends where trading begins. Executed trades, instruments, volumes, and the spread or commission a broker earns are out of scope, so “trading revenue by symbol” has no answer here. What it does answer is the acquisition question – what a funded client costs, where they come from, and what they’re worth – which is where most brokerages actually lose money.
The model, mart by mart
The model, mart by mart
The template is 8 data marts. Two of them – Attribution and Clients – do most of the work, and the other six supply the detail behind them.

Entity-relationship diagram of an 8-mart brokerage acquisition model: Ad Spend and Sessions feeding Leads and Clients, with Communications (the sales desk), Deposits (the money ledger) and Trading Accounts around the Clients core, and an Attribution mart carrying the whole spend-to-deposit funnel on one row. Open this model in the canvas →
Attribution – the whole funnel on one row
The single hardest thing about brokerage reporting is that the payoff – a first deposit – happens weeks and several identity checks after the click that caused it. Attribution solves that by assembling the entire funnel on one row per day / source / campaign / targeting country: the spend, then every downstream step, side by side. CPL, cost-per-FTD and ROAS become ratios between two columns of the same row, and the stage where a channel actually loses people is visible without joining anything.
| Field | What it captures — and why you’ll recognize it |
|---|---|
cost_normalized |
Ad spend in USD for the row; 0 on organic/direct/affiliate. Divide by any downstream count for a CPA-style metric. |
sessions → short_forms → long_forms → kyc_verified → ftd_count → ntc_count |
The whole funnel as columns — session to lead to registration to KYC to first deposit to first trade. The drop between any two is your leak. |
deposit_volume_normalized |
The “revenue” column — total USD deposited by clients attributed to this row. Divide by cost_normalized for ROAS. |
targeting_country vs client_country |
Where the budget was aimed vs where converting clients actually live — carried side by side so the two never merge. |
ad_platform |
Google Ads / Meta Ads / TikTok Ads / “Organic/Direct” — channel performance without a join. |
This is a pre-aggregated accumulating-snapshot-style fact table; the grain choices are covered in the three types of fact tables.
Clients – the trader profile, and the heart of the model
One row per registered client, carrying the four milestones the business is run on – registration, verification, first deposit, first trade – with the days between them stored as numbers, plus every deposit and withdrawal already totalled, lifetime value, an RFM score, and a lifecycle segment that names what the client is today. This is the mart that answers who the customers are, what they’re worth, and which of them are slipping away.
| Field | What it captures — and why you’ll recognize it |
|---|---|
kyc_status |
pending / verified / rejected / expired — the gate. “How many registrations never cleared KYC” is a filter on this column. |
is_ftd / ftd_date / ftd_amount_normalized |
Whether, when, and how much for the first deposit. COUNTIF(is_ftd) is your FTD count — the primary acquisition-efficiency metric. |
days_to_ftd |
Days from registration to first deposit — how fast (or slow) money follows sign-up, per channel. |
is_ntc / days_to_ntc |
Whether they placed a first real trade, and how long after funding. NTC always implies FTD. |
ltv / net_deposits / total_withdrawals_normalized |
Lifetime value and the money in vs money back out — the withdrawal side most acquisition dashboards ignore. |
recency_score / frequency_score / monetary_score |
A full RFM score on deposits — 1-5 on each axis, already computed. |
client_segment |
active_trader, dormant, ftd_only, churned, registered_no_ftd, new — ask for “churned clients” directly instead of recomputing from dates. |
rfm_label |
champions, loyal, at_risk, lost, promising — where lost specifically means their last contact was an unanswered call. |
country (residence) |
The client’s actual country — explicitly not the ad targeting country — so “FTD by country” is honest. |
The reason this mart matters isn’t the milestones; it’s that the milestones sit next to LTV, RFM and the acquisition source on the same row. That’s what turns “which channel is cheapest per FTD” into the far more useful “which channel buys clients worth keeping.”
Leads – where a visitor stops being anonymous
One row per short-form submission: the handful of contact details someone leaves on a landing page before anyone has spoken to them. Each row keeps the page and session that produced it, the email and phone the desk will call, the system that captured it (website_form, app_form, affiliate, chatbot), and a status tracking the stage reached – contacted, long_form, ftd, ntc, no_answer, rejected. This is the only place in the model where the leads that were never reached at all are visible next to the ones that converted – which is exactly the comparison a sales-desk budget rides on.
Communications – the sales desk, in the data
Every contact attempt between the desk and the people it’s trying to convert: one row per attempt across call, email, sms, live_chat, whatsapp, telegram, recording who handled it, whether the desk reached out (outbound) or the client did (inbound), and whether it successfully landed or went unsuccessful.
Rows are flagged is_first / is_last / is_sales, so the shape of a relationship – how many attempts before someone answered, when the desk last got through, whether the last thing that happened was silence – reads without reconstructing a timeline. A is_last = true call with status = unsuccessful is what stamps a client lost over in the Clients mart. This is the human half of the funnel, and most models don’t have it at all.
Deposits – the money ledger, including the money that never arrived
One row per funding transaction, money in and money out, tied to the client, the account it moved through, and the originating lead. The design decision that matters here: attempted funding is first-class. A meaningful share of deposits never complete – they fail, reverse or cancel – and counting those as revenue overstates the business.
| Field | What it captures — and why you’ll recognize it |
|---|---|
transaction_type |
deposit (in) or withdrawal (out) — filter before summing; never add them together. |
status |
pending / completed / failed / reversed / cancelled — payment friction, itemised. |
amount_normalized |
USD value, NULL unless status = completed — the one completion-gated money column, so unarrived money can never reach a revenue total. |
amount_local / currency / payment_method |
Populated on attempts too — so amount_local × exchange_rate is the USD value of money that failed, and failure rates are comparable across card / wire_transfer / crypto / skrill / neteller / paypal. |
is_ftd |
The single row that is this client’s first-ever deposit — the transaction-level FTD flag. |
“Which payment method fails most, and how much money is it costing us?” is a native question here – not a data project.
Sessions, Trading Accounts & Ad Spend
The supporting cast. Sessions is every site and app visit – first- vs last-touch source, device, country, engagement – and it carries the client id once the visitor registers, which is the bridge from anonymous traffic to a named client. Trading Accounts is one row per MT4 / MT5 account (base currency, leverage, type, balance, equity) – where multi-account behaviour shows up. Ad Spend is daily spend by source, campaign and targeting_country, in one reporting currency, with impressions and clicks; it shares its campaign grain with Attribution so cost lines up with outcomes.
Every table above is a reporting-ready data mart in the sense we describe in our approach to data marts.
The design decisions that make it a brokerage model
The design decisions that make it a brokerage model
Four choices separate this from a generic marketing-funnel schema.
The whole funnel lives on one row
Stitching spend to FTDs across five tables is where brokerage numbers usually break, because the deposit lands weeks after the click. Attribution carries every step side by side per channel-day, so cost-per-FTD is a division, not a project – and the leak point is obvious.
Failed funding is first-class
Only completed money (amount_normalized) can reach a revenue total, while method and local amount survive on failed attempts. Payment friction becomes measurable and valuable: you can put a dollar figure on the deposits your card processor is quietly losing you.
The sales desk is in the model
Communications lets you answer the question the whole call-centre budget rides on: do the leads someone actually reached deposit more often, and sooner, than the ones that were never answered? Reach becomes a measurable driver, not an article of faith.
Targeting country ≠ where the client lives
Ad Spend carries the targeting_country you bought; Clients carries the country of residence. Confusing the two is a classic brokerage reporting error – the model keeps them apart so “FTDs by country” and “spend by targeted region” never quietly merge. It’s a dimensional model at heart; the primer is dimensional data modeling, and the join backbone is a star schema.
Cheap clients vs. good clients
Cheap clients vs. good clients
The metric most brokerages optimise – lowest cost-per-FTD – quietly rewards the wrong channels. The cheapest first deposits are often one-and-done: a client funds once, never trades, and goes dormant. This model is built to catch that, because Clients carries LTV, RFM and a lifecycle segment next to the acquisition source.
So instead of ranking channels by cost-per-FTD alone, you can ask whether the cheap channels produce champions with real deposit volume or lost accounts that never came back – and re-allocate budget toward clients worth keeping. That single re-frame, from cost per funded account to value per funded account, is usually worth more than any bid optimisation.
What this model answers
What this model answers
Because the funnel is assembled and the client carries their own history, the hard brokerage questions become joins, not projects:
- Cost per FTD and ROAS by channel – straight off the Attribution row.
- Channel quality, not just channel cost – cheapest CPFTD campaigns cross-checked against the LTV and RFM label of the clients they produced.
- Does the desk change the outcome – reached vs never-answered leads compared on deposit rate and time-to-FTD.
- Where money leaks in funding – deposit failure rates and lost volume by payment method.
- Which regions pay back – deposit volume vs budget by acquisition region, and how much is withdrawn again within months.
None of these need a new table – they’re different paths across the same 8 data marts.
How to open and customize the template
How to open and customize the template
Opening it and shaping it to your brokerage takes about two minutes, then as long as you want to refine.
- Open it. Use the link under the diagram above – it loads the live model in your browser, no sign-up.
- Reshape it. Add fields (affiliate / IB id, bonus flags, regulatory entity), split Communications by desk, or add a demo-account dimension; redraw joins on the canvas.
- Set grain and keys. Confirm Deposits is one row per transaction and that the identity bridge (
email/phoneacross Leads and Clients) ties an anonymous lead to a funded client. - Export it. Use Export → OKF for a portable model, or grab a diagram image. Keep the OKF in git, or push it into OWOX Data Marts to make it live in your warehouse.
Comparing tools while you’re here? Our roundup of free database diagram design tools puts the canvas in context, and the marketing lead-gen template is the closest sibling if your funnel ends at a qualified lead rather than a funded account.
Export to OKF: a portable, AI-readable model
Export to OKF: a portable, AI-readable model
The reason this beats a static ER picture is what happens after the diagram. A drawing can’t be diffed, version-controlled, or fed to a warehouse – or to an AI assistant.
This template exports to OKF (Open Knowledge Format), Google’s open, markdown-based standard. Because it’s plain text, you can keep the model in git, review it in a pull request, and hand it to a text-to-SQL agent as trustworthy context so it stops guessing your joins – which matters more in a domain this acronym-dense (FTD, NTC, KYC, CPL). New to it? See our explainer on what OKF is, then open the broker model and export your own.
Ready to build? Open the broker acquisition model free in the canvas →
FAQ
Is the template free, and what can I export?
Yes, free with no sign-up. Export the model as OKF or a diagram image. An OWOX account is only needed to push the model into OWOX Data Marts and run it in your own warehouse.
Can it tell cheap clients from valuable ones?
Yes. The Clients mart carries LTV, an RFM score and a lifecycle segment (`active_trader`, `dormant`, `churned`…) alongside the acquisition source, so a channel's clients can be judged on the revenue they return, not just their cost-per-FTD.
How does it handle failed or pending deposits?
Every attempted transaction keeps its row, with `status` (`pending`, `completed`, `failed`, `reversed`, `cancelled`). Only `amount_normalized` (USD) is completion-gated, so unarrived money can't reach a revenue total, while method and local amount are kept on attempts so payment-failure rates are measurable.
Does the model include actual trading activity or revenue?
No — by design. It covers acquisition, verification, funding and client lifecycle up to the first trade. Trades, instruments, volumes and broker spread/commission are out of scope, so trading-revenue or volume-by-symbol questions aren't answered here.
How do you calculate cost per FTD (CPA) from this model?
Divide `cost_normalized` by `ftd_count` on the Attribution mart, at whatever grain you want (channel, campaign, targeting country, day). Because spend and FTDs sit on the same row, no join is required.
What's the difference between FTD and NTC?
FTD (First Time Depositor) is the client's first completed deposit — the point acquisition turns into cash. NTC (New Trading Client) is their first real, non-demo trade. NTC always implies FTD (you must fund before you trade), so NTC ≤ FTD for any slice.
What tables are in a forex/CFD broker data model?
For the acquisition side: Ad Spend, Sessions, Leads, Clients, Communications, Deposits, Trading Accounts, and an Attribution mart that assembles the whole spend-to-deposit funnel on one row — 8 marts in all. Executed-trade data is a separate model downstream.
Topics
About the author
Related articlesSee all articles →



