Trading Data Model

Trading Data Model

8 data marts168 fieldsVlad FlaksRus Obolonsky

A click becomes a lead, then a registration, a verified client and a funded account at a forex and CFD brokerage, with lifetime value and withdrawals tracked alongside.

Overview

The client acquisition and funding side of a retail forex and CFD brokerage: 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 is allowed in at all, and the deposits that determine whether any of it paid for itself. The funnel is deliberately long — a click becomes a lead, a lead becomes a registration, a registration becomes a verified client, and only then a first deposit and a first real trade — and every step of it is measurable here, alongside the lifetime value, the withdrawals and the lifecycle segments that say what happened after.

Scope: this model ends where trading begins. It covers acquisition, verification, funding and client lifecycle up to the first trade. Trading activity itself — executed trades, instruments, volumes, and the spread and commission a broker earns on them — is not part of it, so questions about trading revenue or volume by symbol have no answer here. What it does answer is what a funded client costs, where they come from, and what they are worth once they arrive.

Example Questions

  • Which channels buy traders worth keeping rather than merely cheap ones — do the campaigns with the lowest cost per first deposit produce the clients who end up as champions with real lifetime value, or the ones who deposit once and go dormant?
  • Does the desk change the outcome — do the leads someone managed to reach deposit more often, and sooner, than the ones that were never answered?
  • Which acquisition regions return more deposit volume than they consume in budget, and how much of that money is withdrawn again within a few months?

Explore on canvas →

Ad Spend Data Mart

Ad Spend

Every dollar the brokerage puts into buying traffic, one row per day, source, medium, campaign and targeting country — the exact grain the media buyers work at. Each row carries the money twice, once as cost and once as cost_normalized — both already in this reporting's single currency, so either sums safely — next to the impressions and clicks it bought, so cost per click, cost per thousand impressions and click-through rate all come out of a single table. Targeting country is where the budget was spent, not where the trader who answered the ad turns out to live — the two diverge often enough that keeping them apart is the whole point of the field.

Fields

ColumnTypeAliasDescription
dateDATEDatePK. Composite key together with source, medium, campaign, targeting_country. Calendar date the spend occurred — use for daily/weekly/monthly spend trends. FK to Attribution
sourceSTRINGSourcePK. Ad traffic source — which channel ran the ad, e.g. google, facebook, tiktok, bing, native, applesearch. Answers "which channel/platform did we spend on". FK to Attribution
mediumSTRINGMediumPK. Ad format / traffic medium, e.g. cpc, cpm, paid-social. Answers "what type of ad" (search vs social vs display). FK to Attribution
campaignSTRINGCampaignPK. UTM campaign name, e.g. search_generic_fx, acq_video_q3. Use for campaign-level spend breakdowns. FK to Attribution
targeting_countrySTRINGTargeting CountryPK. Country the ad budget was targeted at — i.e. where the money was spent. This is NOT where the resulting client lives; for that use Clients.country or Attribution.client_country. Use this field to answer "spend by country". FK to Attribution
targeting_regionSTRINGTargeting RegionBusiness-region roll-up of targeting_country: SEA, ME, EU, LATAM, AFRICA, CA, UK, AU, ANZ, Other. Use for "spend by region" instead of listing every country.
ad_platformSTRINGAd PlatformName of the advertising platform that billed this spend: Google Ads, Meta Ads, TikTok Ads, Bing Ads, YouTube Ads, Apple Search Ads, Native Ads Network. Answers "which ad platform/network".
costFLOATCostAd spend in this dataset's single reporting currency — identical to cost_normalized here, so summing it directly is safe. Kept as a separate field for consistency with source systems where ad platforms genuinely bill in local currency and a normalized column is needed.
cost_normalizedFLOATCost NormalizedAd spend converted to USD. This is the field to SUM for "total spend", "ad cost", "budget spent", "how much did we spend" questions — safe to aggregate across countries and campaigns.
impressionsINTEGERImpressionsNumber of times the ad was displayed. SUM for total impressions; divide clicks by impressions for CTR.
clicksINTEGERClicksNumber of ad clicks. SUM for total clicks; divide cost_normalized by clicks for CPC, or clicks by impressions for CTR.

Relationships

Related data martOnCardinalityMeaning
Attributiondate = date, source = source, medium = medium, campaign = campaign, targeting_country = targeting_country1:NThe acquisition funnel this day of spend paid for.

Attribution Data Mart

Attribution

The whole acquisition funnel already assembled on one row: for each day, source, medium, campaign and targeting country, the money spent and the impressions and clicks it bought, then the sessions that arrived, the short forms submitted, the long forms completed, the clients who cleared KYC, the first time deposits and finally the new trading clients who placed a real trade — with the deposit volume those clients went on to generate. Cost per lead, cost per FTD and return on ad spend are ratios between two columns of the same row, and because every step is carried side by side, the stage where a channel actually loses people is visible without joining anything.

Fields

ColumnTypeAliasDescription
dateDATEDateCalendar date of the funnel activity. Part of the join grain shared with Ad Spend (plus targeting_country).
sourceSTRINGSourceTraffic source. Part of the join grain.
mediumSTRINGMediumTraffic medium. Part of the join grain.
campaignSTRINGCampaignUTM campaign name. Part of the join grain.
targeting_countrySTRINGTargeting CountryCountry the ad budget was targeted at (for organic/direct rows, the country traffic actually came from). Part of the join grain; joins to Ad Spend.targeting_country.
cost_normalizedFLOATCost NormalizedAd spend in USD for this row. 0 for organic/direct/affiliate rows (they have no matching Ad Spend row). SUM this for total spend by any slice; divide by short_forms/ftd_count/ntc_count for CPA-style metrics.
costFLOATCostAd spend in the original billing currency. 0 for organic/direct/affiliate rows. Prefer cost_normalized for any cross-currency total.
impressionsINTEGERImpressionsAd impressions for this row. 0 for unpaid rows.
clicksINTEGERClicksAd clicks for this row. 0 for unpaid rows.
ad_platformSTRINGAd PlatformAd platform, e.g. Google Ads, Meta Ads, TikTok Ads. "Organic/Direct" for unpaid rows.
sessionsINTEGERSessionsWeb/app sessions attributed to this row — funnel step 1. SUM for total traffic; divide short_forms by sessions for the session→lead conversion rate.
unique_usersINTEGERUnique UsersUnique users attributed to this row.
pages_per_sessionFLOATPages Per SessionAverage pages viewed per session for this row — an engagement/traffic-quality signal, not a funnel step.
avg_session_durationFLOATAverage Session DurationAverage session duration in seconds for this row — engagement signal.
short_formsINTEGERShort FormsShort-form submissions (leads) attributed to this row — funnel step 2, "Profile Short Form" in dashboards. Divide by sessions for session→lead conversion; divide cost_normalized by this for CPA per lead.
long_formsINTEGERLong FormsLong-form / full registrations attributed to this row — funnel step 3, "Profile Long Form" in dashboards.
registrationsINTEGERRegistrationsCompleted account registrations attributed to this row. Equal to long_forms in this model (registration = completing the long form).
kyc_verifiedINTEGERKYC VerifiedClients who passed KYC verification, attributed to this row.
ftd_countINTEGERFtd CountFirst Time Depositors attributed to this row — funnel step 4, "Profile FTD" in dashboards. Divide cost_normalized by this for cost-per-FTD (CPA), the primary acquisition-efficiency metric.
ntc_countINTEGERNtc CountNew Trading Clients (placed a first real trade) attributed to this row — funnel step 5, "Profile NTC". Always ≤ ftd_count for the same row (a client must fund before trading).
deposit_volume_normalizedFLOATDeposit Volume NormalizedTotal deposit amount in USD from clients attributed to this row (cumulative, not just their first deposit). This is the "revenue" field — divide by cost_normalized for ROAS.
client_countrySTRINGClient CountryCountry of the clients who actually converted (from Clients.country). May differ from targeting_country — e.g. ads targeted at country A but the client registered from country B.
targeting_regionSTRINGTargeting RegionBusiness-region roll-up of targeting_country.
regionSTRINGRegionBusiness region — equal to targeting_region in this mart; use either.
traffic_platformSTRINGTraffic PlatformApp or Web.
attribution_idSTRINGAttribution IDPK. Unique internal identifier for this attribution row (the row's own surrogate key, not a business dimension).
user_sourceSTRINGUser SourceFirst-touch acquisition source (mirrors source in this already-aggregated mart; the first/last-touch distinction matters more at the Sessions grain).
user_mediumSTRINGUser MediumFirst-touch acquisition medium.
user_campaignSTRINGUser CampaignFirst-touch acquisition campaign.

Clients Data Mart

Clients

The trader profile, and the centre of the model: one row per registered client, carrying the four milestones the business is run on — registration, identity verification, first deposit and first real trade — with the dates between them, so how long a client takes to fund and then to trade is a stored number rather than a calculation. Each row also holds the money: every deposit and withdrawal already totalled, net deposits, lifetime value, how recently and how often the client funded, an RFM score on all three axes, and a lifecycle segment that names what the client is today, from someone who registered and never deposited to an active trader, a dormant account or a churned one. It is the mart that answers who the customers are, what they are worth and which of them are slipping away.

Fields

ColumnTypeAliasDescription
client_idSTRINGClient IDPK. Unique client identifier.
lead_idSTRINGLead IDThe first short-form submission that started this client's journey.
session_idSTRINGSession IDThe first-touch session that originally brought this client in. NULL if none could be matched.
emailSTRINGEmailClient email — identity bridge key, matches Leads.email.
phoneSTRINGPhoneClient phone — identity bridge key.
first_nameSTRINGFirst NameClient first name.
last_nameSTRINGLast NameClient last name.
countrySTRINGCountryClient's country of residence/registration — the client's actual location, NOT the ad targeting country. Use this field (not Ad Spend.targeting_country) to answer "which country has the most clients/FTD" or "FTD by country" questions.
regionSTRINGRegionBusiness-region roll-up of the client's country: SEA, ME, EU, LATAM, AFRICA, CA, UK, AU, ANZ, Other. Use for region-level FTD/LTV roll-ups.
languageSTRINGLanguageClient's preferred language.
registration_dateDATERegistration DateDate the client completed account registration. This corresponds to the "Long Form" / "Profile Long Form" funnel step in dashboards.
kyc_statusSTRINGKYC StatusIdentity-verification status: pending, verified, rejected, expired.
kyc_verified_dateDATEKYC Verified DateDate KYC was approved. NULL if not yet verified.
account_typeSTRINGAccount TypeRegulatory client classification: retail or professional.
is_ftdBOOLEANIs FtdWhether the client made at least one completed deposit (First Time Depositor). This is the "FTD" / "Profile FTD" metric — COUNTIF(is_ftd) gives the FTD count, the primary acquisition-efficiency metric.
ftd_dateDATEFtd DateDate of the client's first completed deposit. NULL if is_ftd = false. Use for FTD trend charts.
ftd_amount_normalizedFLOATFtd Amount NormalizedAmount of the first deposit, in USD. NULL if is_ftd = false.
ftd_payment_methodSTRINGFtd Payment MethodPayment method used for the first deposit: card, wire_transfer, crypto, skrill, neteller, paypal. NULL if is_ftd = false.
days_to_ftdFLOATDays To FtdDays between registration_date and ftd_date — how fast a client deposits after registering. NULL if is_ftd = false.
is_ntcBOOLEANIs NtcWhether the client placed at least one real (non-demo) trade — New Trading Client. Always implies is_ftd = true (a client must fund before trading). This is the "NTC" / "Profile NTC" metric — COUNTIF(is_ntc) gives the NTC count.
ntc_dateDATENtc DateDate of the client's first real trade. NULL if is_ntc = false.
days_to_ntcFLOATDays To NtcDays between ftd_date and ntc_date — how fast a depositor starts trading. NULL if is_ntc = false.
has_open_positionsBOOLEANHas Open PositionsWhether the client currently has open trading positions — a live-engagement signal.
created_atTIMESTAMPCreated AtRecord creation timestamp in the warehouse.
count_clientsINTEGERCount ClientsAlways 1 on every row; SUM to count clients matching a filter.
total_deposits_normalizedFLOATTotal Deposits NormalizedSum of all this client's completed deposits, in USD. SUM across clients to answer "total deposit volume" / "revenue" questions.
total_withdrawals_normalizedFLOATTotal Withdrawals NormalizedSum of all this client's completed withdrawals, in USD.
net_depositsFLOATNet Depositstotal_deposits_normalized minus total_withdrawals_normalized — net money the client has put in.
deposit_countINTEGERDeposit CountNumber of completed deposit transactions made by this client — the RFM "frequency" input.
last_deposit_dateDATELast Deposit DateDate of the client's most recent completed deposit.
days_since_last_depositFLOATDays Since Last DepositDays elapsed since last_deposit_date — the RFM "recency" input; also drives client_segment (e.g. dormant, churned).
days_since_ftdFLOATDays Since FtdDays elapsed since the client's first deposit — client "age" in the system.
ltvFLOATLTVLifetime value in USD — equal to total_deposits_normalized. Use for "what is the LTV of clients acquired via X" questions.
recency_scoreFLOATRecency ScoreRFM recency score, 1-5 (5 = deposited most recently). Derived from days_since_last_deposit.
frequency_scoreFLOATFrequency ScoreRFM frequency score, 1-5 (5 = most deposits). Derived from deposit_count.
monetary_scoreFLOATMonetary ScoreRFM monetary score, 1-5 (5 = highest net_deposits).
client_segmentSTRINGClient SegmentBehavioural lifecycle segment, already computed: active_trader, dormant, ftd_only, churned, registered_no_ftd, new. Use this directly for "give me churned/dormant clients" questions instead of recomputing from date fields.
rfm_labelSTRINGRfm LabelRFM marketing segment, already computed: champions, loyal, at_risk, lost, new, promising. lost specifically means the client's most recent contact (see Communications) was an unanswered/unsuccessful call.
data_sourceSTRINGData SourceApp or Web — the surface this client was first acquired on.

Communications Data Mart

Communications

Every conversation between the sales desk and the people it is trying to convert: one row per contact attempt across calls, email, SMS, live chat and messengers, recording who handled it, whether the desk reached out or the client got in touch, and whether the attempt actually landed or went unanswered. Rows are flagged as the first and the most recent contact with a person and as sales-driven rather than servicing, so the shape of a relationship — how many attempts it took before someone answered, when the desk last got through, and whether the last thing that happened was silence — can be read without reconstructing the timeline. This is where the human half of the funnel lives, next to the forms and the deposits.

Fields

ColumnTypeAliasDescription
communication_idSTRINGCommunication IDPK. Unique communication-event identifier.
client_idSTRINGClient ID— despite the name, this is the client the communication was with (legacy CRM field name, equivalent to client_id elsewhere). FK to Clients
agent_idSTRINGAgent IDInternal identifier of the agent who handled this communication. Not a foreign key to another mart in this model.
lead_idSTRINGLead IDThe lead this communication relates to, if any. NULL for post-registration servicing contacts. FK to Leads
channelSTRINGChannelChannel used: call, email, sms, live_chat, whatsapp, telegram.
directionSTRINGDirectioninbound (client-initiated) or outbound (agent-initiated).
statusSTRINGStatusOutcome: successful or unsuccessful (no answer / bounced / failed). This field, combined with is_last, is what drives Clients.rfm_label = lost.
subjectSTRINGSubjectTopic/subject line. NULL for phone calls.
textSTRINGTextMessage body or call notes, if recorded.
autoreplySTRINGAutoreplyContent of any triggered automated reply. NULL if none was sent.
communication_dateDATECommunication DateCalendar date of the communication.
is_firstBOOLEANIs First"true"/"false" boolean flag — first ever communication with this client.
is_lastBOOLEANIs Last"true"/"false" boolean flag — most recent communication with this client. A "true" row with channel = 'call' and status = 'unsuccessful' is what marks a client lost in Clients.rfm_label.
is_salesBOOLEANIs Sales"true"/"false" boolean flag — flagged as a sales-focused interaction.
is_last_salesBOOLEANIs Last Sales"true"/"false" boolean flag — most recent sales-flagged communication with this client.
count_communicationsINTEGERCount CommunicationsAlways 1 on every row; SUM to count communications matching a filter.

Relationships

Related data martOnCardinalityMeaning
Clientsclient_id = client_idN:1The client the desk spoke to.
Leadslead_id = lead_idN:1The lead the desk was trying to convert.

Deposits Data Mart

Deposits

The money ledger: one row per funding transaction, money in and money out, tied to the client, the trading account it moved through and the lead the client originally came from. Each transaction carries its amount in the currency it was made in and converted to USD, the rate used at the time, the payment method behind it — card, wire, crypto or an e-wallet — and a processing status, because a meaningful share of attempted funding never completes: it fails, is reversed or is cancelled, and counting those as revenue overstates the business. One row per client is flagged as that client's first ever deposit, which is where the acquisition funnel finally turns into cash.

Fields

ColumnTypeAliasDescription
deposit_idSTRINGDeposit IDPK. Unique transaction identifier.
client_idSTRINGClient IDFK to Clients
account_idSTRINGAccount IDThe specific trading account the money moved into/out of. FK to Trading Accounts
lead_idSTRINGLead IDThis client's originating lead. NULL if not traceable. FK to Leads
deposit_datetimeTIMESTAMPDeposit DatetimeTimestamp the transaction was initiated.
transaction_typeSTRINGTransaction Typedeposit (money in) or withdrawal (money out). Filter on this before summing amounts — don't sum deposits and withdrawals together.
is_ftdBOOLEANIs FtdTrue only for the single row that is this client's very first-ever deposit. Row-level equivalent of Clients.is_ftd (which is a per-client flag, not per-transaction).
statusSTRINGStatusProcessing status: pending, completed, failed, reversed, cancelled. Attempted rows carry their own payment_method, currency, amount_local and exchange_rate, so payment friction can be measured and valued; only amount_normalized is completion-gated — always filter status = 'completed' before summing revenue.
amount_localFLOATAmount LocalTransaction amount in its original currency. Populated on attempted transactions too — a failed or pending payment has an amount — so amount_local * exchange_rate is the USD value of money that never arrived. For cross-currency totals of money that DID arrive use amount_normalized.
currencySTRINGCurrencyOriginal transaction currency, taken from the account the money moved through. Populated on attempted transactions too.
amount_normalizedFLOATAmount NormalizedTransaction amount converted to USD. NULL unless status = 'completed' — the one completion-gated money column, so unarrived money can never reach a revenue total. SUM this (filtered to transaction_type = 'deposit', status = 'completed') for "deposit volume"/"revenue" questions.
exchange_rateFLOATExchange RateConversion rate captured at transaction time; 1.0 on USD rows. Populated on attempted transactions too.
payment_methodSTRINGPayment Methodcard, wire_transfer, crypto, skrill, neteller, paypal. Populated on attempted transactions too — the rail a failed payment was attempted on is the point of asking — so failure rates are comparable across methods.
countrySTRINGCountryClient's country at the time of the transaction.
created_atTIMESTAMPCreated AtRecord creation timestamp in the warehouse.
count_depositsINTEGERCount DepositsAlways 1 on every row; SUM to count transactions matching a filter (e.g. count of completed deposits).

Relationships

Related data martOnCardinalityMeaning
Clientsclient_id = client_idN:1The client who funded.
Leadslead_id = lead_idN:1The lead this funded client came from.
Trading Accountsaccount_id = account_idN:1The account the money was deposited into.

Leads Data Mart

Leads

The moment 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 the session that produced it, the email and phone the desk will call, the system that captured it — a website form, the app, a chatbot or an affiliate — and the stage the lead has reached since, from contacted through the full registration to a first deposit and a first trade, or else no answer and rejection with a stated reason. This is the top of the sales funnel, and the only place where the leads that were never reached at all are visible next to the ones that converted.

Fields

ColumnTypeAliasDescription
lead_idSTRINGLead IDPK. Unique identifier for this short-form submission.
session_idSTRINGSession IDThe session during which this form was submitted. NULL if no session could be matched (e.g. affiliate-referred leads entered outside the web funnel). FK to Sessions
client_idSTRINGClient IDSet only once this lead converts into a registered client. NULL until then — use IS NOT NULL to filter "converted leads". FK to Clients
short_form_submitted_atTIMESTAMPShort Form Submitted AtTimestamp the form was submitted. Use for lead-volume trends over time.
landing_pageSTRINGLanding PageLanding page path where the form was filled. Matches Sessions.landing_page for the same session.
countrySTRINGCountryCountry of the lead, from IP geolocation or form input.
languageSTRINGLanguageBrowser / form language of the lead.
emailSTRINGEmailEmail entered in the form — identity bridge key that later matches Clients.email.
phoneSTRINGPhonePhone entered in the form.
statusSTRINGStatusCurrent funnel stage of this lead: contacted, long_form, ftd, ntc, no_answer, rejected. Use this to answer "how many leads reached X stage" without needing to join Clients — though ftd/ntc status here should match Clients.is_ftd/is_ntc for the linked client_id.
rejection_reasonSTRINGRejection ReasonWhy the lead was rejected. NULL unless status = 'rejected'.
form_typeSTRINGForm TypeAlways "short" in this mart — it only captures short-form submissions (the fuller registration is tracked as Clients.registration_date / the long_form status, not a separate row here).
source_systemSTRINGSource SystemSystem that captured this lead: website_form, app_form, affiliate, chatbot.
is_manual_entryBOOLEANIs Manual EntryTrue if a staff member entered this lead manually rather than it being captured automatically from a form.
created_atTIMESTAMPCreated AtRecord creation timestamp in the warehouse.
count_leadsINTEGERCount LeadsAlways 1 on every row; SUM to count leads — this is the "Short Form" / "Profile Short Form" metric seen in dashboards.

Relationships

Related data martOnCardinalityMeaning
Clientsclient_id = client_idN:1The client this lead became, where it converted.
Sessionssession_id = session_idN:1The visit the form was submitted in.

Sessions Data Mart

Sessions

Every visit to the broker's site and app, one row per session, from the anonymous first click on an ad to the return visit of a client who is already trading. A row records where the visit came from — its own last-touch source, medium and campaign alongside the first-touch channel that originally acquired the visitor — where it landed, what device and country it came from, and how engaged it was in pages viewed and seconds spent. Once a visitor registers, the session carries the client identifier, which is what turns raw traffic into something you can follow all the way to a deposit.

Fields

ColumnTypeAliasDescription
dateDATEDateCalendar date of the session. FK to Attribution
session_idSTRINGSession IDPK. Unique identifier for a single session. Don't COUNT this to get session totals — SUM the count_sessions field below instead, it is purpose-built for that.
sourceSTRINGSourceThis session's own (last-touch) traffic source. If you need the channel that FIRST acquired the visitor (not just this visit), use user_source instead. FK to Attribution
mediumSTRINGMediumThis session's own (last-touch) traffic medium. See user_medium for the first-touch equivalent. FK to Attribution
campaignSTRINGCampaignThis session's own (last-touch) UTM campaign. See user_campaign for the first-touch equivalent. FK to Attribution
ad_contentSTRINGAd ContentUTM ad content — identifies the specific ad creative shown. Only populated for paid sessions.
ad_groupSTRINGAd GroupAd group within the campaign. Only populated for paid sessions.
channel_groupingSTRINGChannel GroupingSimplified channel bucket: Paid Search, Paid Social, Organic, Direct, Referral, Affiliate, Video, Display. Use this for a high-level channel-mix chart instead of raw source/medium.
keywordSTRINGKeywordSearch keyword that triggered the session. Only populated for search (cpc/organic) sessions.
landing_pageSTRINGLanding PagePath of the first page viewed in the session (e.g. /open-account, /promo/welcome-bonus), without UTM parameters. Use to answer "which landing page converts best" — join to Leads on session_id to compute a conversion rate per landing page. Note: there is no cost breakdown by landing page (ad platforms only report cost per campaign), so CPA/CPL by landing page cannot be computed, only conversion rate/counts.
landing_host_nameSTRINGLanding Host NameLanding hostname for web sessions, or the app identifier for App sessions.
urlSTRINGURLFull first-hit URL including UTM parameters, for web sessions.
started_atTIMESTAMPStarted AtTimestamp the session began. Use MIN/MAX only — not a metric to SUM or AVG.
consent_atTIMESTAMPConsent AtTimestamp of first recorded (GDPR-style) consent. NULL if no consent was recorded in this session.
ga_client_idSTRINGGa Client IDBrowser/device analytics cookie id, used only to link anonymous sessions to the same device across visits. This is NOT the CRM client — do not confuse with client_id below.
countrySTRINGCountryVisitor's country from IP geolocation — this is where the visitor actually is, not the ad targeting country (see Ad Spend.targeting_country).
regionSTRINGRegionBusiness-region roll-up of the visitor's country: SEA, ME, EU, LATAM, AFRICA, CA, UK, AU, ANZ, Other.
citySTRINGCityVisitor's city.
device_categorySTRINGDevice CategoryDesktop, Mobile or Tablet.
is_first_visitor_sessionBOOLEANIs First Visitor Session"true"/"false" boolean flag — whether this is the visitor's very first ever session.
traffic_platformSTRINGTraffic PlatformApp or Web — which surface the session happened on. Use for "app vs web" traffic-split questions.
unique_usersINTEGERUnique UsersAlways 1 on every row; SUM to count unique users in a report, do not AVG or use as a real per-row metric.
pages_per_sessionFLOATPages Per SessionNumber of pages viewed in this specific session. Engagement/quality-of-traffic signal.
avg_session_durationFLOATAverage Session DurationDuration of this specific session, in seconds. Engagement signal.
client_idSTRINGClient IDSet only once the visitor is an identified, registered client — NULL for anonymous, not-yet-registered visitors. Use to join session behaviour to CRM/deposit data. FK to Clients
count_sessionsINTEGERCount SessionsAlways 1 on every row; SUM this field to answer "how many sessions" — this is the standard sessions-count metric.
user_sourceSTRINGUser SourceFirst-touch acquisition source for this visitor — the channel that originally brought them in, which may differ from this particular session's own source. Use when the question is about acquisition/attribution rather than this specific visit.
user_mediumSTRINGUser MediumFirst-touch acquisition medium. See user_source.
user_campaignSTRINGUser CampaignFirst-touch acquisition campaign. See user_source.

Relationships

Related data martOnCardinalityMeaning
Attributiondate = date, source = source, medium = medium, campaign = campaignN:1The acquisition funnel this visit rolls into.
Clientsclient_id = client_idN:1The client this visit is recognised as.

Trading Accounts Data Mart

Trading Accounts

The accounts clients actually trade on: one row per MT4 or MT5 account, with the terms it was opened under — the base currency it is denominated in, the maximum leverage from a cautious 1:10 up to 1:500, and how the holder is classified: an ordinary retail client, a professional one, a swap-free islamic account or a practice account. Alongside the terms sit the two numbers that describe its state: the settled balance, and the equity that includes the profit or loss running on positions still open. A client can hold several accounts, which is where multi-account behaviour becomes visible — a second, higher-leverage account opened next to a conservative first one is a meaningful change in how someone is trading.

Fields

ColumnTypeAliasDescription
account_idSTRINGAccount IDPK. Unique trading account identifier.
client_idSTRINGClient IDOwner of this account — a client may have more than one account, so COUNT(account_id) can exceed COUNT(DISTINCT client_id). FK to Clients
trading_platformSTRINGTrading PlatformTrading platform: MT4 or MT5.
account_typeSTRINGAccount Typeretail, professional, demo (no real money), islamic (swap-free).
currencySTRINGCurrencyAccount's base currency: USD, EUR, GBP, AUD. balance/equity are denominated in this currency, not USD.
leverageSTRINGLeverageMaximum leverage on this account: 1:10, 1:50, 1:100, 1:200, 1:500.
statusSTRINGStatusAccount state: active, inactive, closed.
balanceFLOATBalanceCurrent balance, in the account's own currency — excludes unrealized P&L from open positions.
equityFLOATEquityCurrent equity, in the account's own currency — balance adjusted for unrealized P&L on open positions.
countrySTRINGCountryCountry of the account holder at account-creation time.
created_atTIMESTAMPCreated AtTimestamp the account was opened.
count_depositsINTEGERCount DepositsNumber of deposit transactions made into this specific account (see Deposits for the transactions themselves).

Relationships

Related data martOnCardinalityMeaning
Clientsclient_id = client_idN:1The client who trades on this account.

Apply to your project

  1. 1

    Install the Import Model plugin

    One plugin, installed once, in your own OWOX workspace.

    Get the plugin →

  2. 2

    Import this model

    Point it at this bundle and it creates every data mart above, joins and all.

    Open the model →

  3. 3

    Plug in your data and destinations

    Connect your own sources and send the results where your team already works.

    Browse connectors →

4. Optional — customize as you wish. Rename a column, drop a mart, add your own: once it is imported it is yours, and nothing here syncs back.