1. Toggleable one- or two-stage model
Every record starts from the same first stage: one simulated Mobile
Money transaction, generated identically regardless of which mode you
choose on the
generator page. What
happens after depends on stage_mode — the same
"did it happen" / "how bad was it" split every other two-stage line on this
platform uses:
- Fraud probability — whether a transaction is fraudulent
is a logistic (log-odds) model over four independent risk factors that
combine additively, closer to motor's from-scratch logistic frequency
model than to life's or health's table-rated approach — table-rating doesn't
fit here because there's no single natural ordinal axis, like age, to
band against. How much money moved is a property of the transaction itself,
not of the fraud outcome, so
transaction_amount_ghsis drawn independently ofis_fraudulentrather than conditioned on it. This logic is identical in both stage modes and is computed beforestage_modeis even consulted — single-stage's entire output, and stage one of two-stage. - Loss given fraud (two-stage only, computed only for transactions
already flagged fraudulent) — the fraction of the transaction's own amount
the provider fails to recover is a log-normal draw, susu's "multiple of
a principal/amount field" technique, capped at 1.0x
transaction_amount_ghsthe same way susu's loss is capped at 1.0x loan principal. Cash-Out status is the primary driver — physical cash disbursed at an agent till can't be reversed, the classic "point of no return" in Mobile Money fraud response, whereas a transaction that stays inside the digital rails (P2P Transfer, Merchant Payment, Bill Payment, Airtime Top-up, Cash-In, Bulk Disbursement) leaves the receiving wallet freezable and the funds clawable-back. Agent cash-out channel, scoped to Cash-Out rows only, is the second core driver — Bank Partner Cash-Out inherits the bank's own KYC/reversal machinery (best recovery); ATM Cash-Out is fully anonymous (worst recovery). Transaction velocity applies a decorative multiplier on top, regardless of transaction type — the same structuring/smurfing signal read a second time.
Single-stage produces the fraud-probability columns only —
loss_given_fraud_ghs is entirely absent from the
output in this mode, not null-filled.
Two-stage is the default — same "default the richer shape,
allow opting down" reasoning every other two-stage line on this platform
uses. Whichever mode you pick is recorded in the dataset's manifest and
citation — two downloads of this line in different modes are, citation-wise,
two different datasets, since a single-stage download simply has no loss-given-fraud
column at all.
Unlike susu (two hypotheses on each stage, an even split) or motor/life/health (every hypothesis on one stage), momo puts four of its six hypotheses on the frequency side and two on the severity side — an asymmetric split, not a parity move for its own sake. Each hypothesis sits on whichever stage its own underlying question actually belongs to: "is this transaction fraudulent" has four independently-documented risk factors behind it; "how much is actually lost once fraud is confirmed" has two.
2. The six hypotheses
Four hypotheses act on fraud probability directly and hold in both
stage modes, since stage-one logic doesn't change based on the toggle;
two more act on loss given fraud for transactions already flagged
fraudulent, and only exist — and are only verified — in two-stage
mode, scoped to records where is_fraudulent === 1. Each row below is independently documented against publicly-known
Mobile Money fraud typologies (Bank of Ghana and GSMA mobile-money
fraud advisories), not invented to hit a target fraud or loss rate.
Tracked separately below rather than in one undifferentiated table,
since which mode a factor actually applies in is the thing a reader
needs disambiguated.
| Factor | Stage | Direction | Verification n |
|---|---|---|---|
| Transaction velocity (trailing 24h) | Fraud probability both modes | Positive | 20,000 |
| SIM-swap recency | Fraud probability both modes | Positive | 20,000 |
| Wallet tenure | Fraud probability both modes | Positive (inverse — newer wallets carry more risk) | 20,000 |
| Agent cash-out channel | Fraud probability both modes | Positive (Cash-Out rows only) | 20,000 |
| Cash-Out status | Loss given fraud two-stage only | Positive | 200,000 |
| Agent cash-out channel (recoverability) | Loss given fraud two-stage only | Positive (Cash-Out AND fraudulent rows only) | 200,000 |
Transaction velocity — a rapidly rising count of transactions in the trailing 24 hours is the structuring/"smurfing" signal: splitting or cycling funds through many small transactions to move stolen money before a victim or the telco can freeze the wallet, or to stay under a reporting threshold. SIM-swap recency is by far the largest single loading in the model — a fresh SIM swap (within the last 24 hours) is widely documented as the strongest individual fraud signal in the Mobile Money literature this line is anchored against, and the loading decays as the swap ages out of that highest-risk window. Wallet tenure works inversely: a wallet opened within the last month has no established legitimate transaction history to compare against and is disproportionately the vehicle a fraud ring picks, so the loading is highest for the newest band and actually goes slightly negative for the oldest ("3+ years") band, rather than just flattening at zero. Agent cash-out channel (fraud probability) only applies to Cash-Out rows (about 8% of all transactions) — an unmanned ATM cash-out carries the largest loading of the four channels since it leaves the weakest human trail, while a Bank Partner Cash-Out sits below the reference band, since it inherits the bank's own KYC checks.
Cash-Out status (loss given fraud) — once cash is disbursed at an agent, it can't be reversed: the classic "point of no return" in Mobile Money fraud response. A transaction that stays inside the digital rails is more often freezable and clawable-back before the money moves again, so Cash-Out transactions carry a far higher loss-given-fraud fraction than any other transaction type. Agent cash-out channel (loss given fraud) reads the same field the frequency-side hypothesis above does, but asks a different question — not "is this transaction more likely to be fraudulent," but "given a Cash-Out transaction is already fraudulent, how much of it is actually recovered." Bank Partner Cash-Out inherits the bank's own KYC and reversal machinery — the best-recovery channel, the same reason it's also the lowest-fraud-probability channel. ATM Cash-Out is fully anonymous with no attendant able to intervene once cash is dispensed — the worst-recovery channel, for the same underlying reason it's also the highest-fraud-probability one.
The four frequency-side hypotheses are verified at n=20,000, not the n=5,000 the motor/life/health/property lines default to — driven specifically by the agent cash-out channel hypothesis (unchanged from before this phase: at n=5,000 the weak, Cash-Out-only channel effect flips sign for at least one regression seed). The two severity-side hypotheses need considerably more, n=200,000: each is scoped to fraudulent rows, and one of the two further scopes to Cash-Out rows on top of that — a fraction of a fraction of all records — so the qualifying subpopulation per seed is small enough that a smaller n lets sampling noise flip the sign, the same "real-but-thin-population effect, not a broken hypothesis" conclusion the frequency-side agent-channel hypothesis already reached at a smaller n.
3. Coefficients
Fraud probability is modeled as
p = clip(sigmoid(intercept + velocity_band_loading +
agent_channel_loading + sim_swap_loading + wallet_tenure_loading),
min_probability, max_probability), where each loading is a named log-odds term keyed off the
transaction's own velocity band, agent cash-out channel (only applied
on Cash-Out rows — zero everywhere else), SIM-swap recency band, and
wallet tenure band. The intercept is tuned so the realized fraud rate
across a generated dataset lands in the low single-digit percent
range, consistent with Mobile Money fraud-detection literature
treating fraud as a rare-event classification problem, not a guess to
hit an arbitrary target.
anomaly_score is derived from that same logit
plus independent normal noise (anomaly_score_noise_sd), then passed back through the sigmoid and clipped to [0, 1] — it is
correlated with, but not a deterministic function of,
is_fraudulent, by construction, and is
computed identically in both stage modes.
When stage mode is two-stage, loss given fraud, for transactions
already flagged fraudulent, is modeled as
loss_given_fraud = min(transaction_amount_ghs · exp(log_base +
cash_out_loading + log(agent_channel_multiplier) +
log(velocity_band_multiplier) + ε), transaction_amount_ghs), where cash_out_loading applies only when the
transaction is Cash-Out (else 0), agent_channel_multiplier is a named term keyed off cash-out channel and applies only on Cash-Out
rows (1.0 everywhere else), velocity_band_multiplier is a named term keyed off velocity band and applies to every row, and
ε is normal noise. The min(..., transaction_amount_ghs) term is this line's loss cap — a fraudulent transaction can't lose more
than its own amount by construction, susu's identical indemnity-cap reasoning.
These coefficients are illustrative, chosen to produce a clearly recoverable
signal, anchored against general Mobile Money fraud-recovery practice (cash-out
as the point at which reversal becomes impossible; structuring as an evasion
tactic used by organized fraud rings to move funds before detection), not
derived from any real Ghanaian telco's own recovery data. Exact magnitudes
aren't published on this page — see the current defaults, and change them,
in the
Advanced: customize the model panel on the
Mobile Money generator if your
thesis calls for weaker or stronger effects, and note in your write-up which
values you changed from the defaults.
4. Determinism
Generation runs server-side using a seeded generator (numpy's
default_rng, PCG64) rather than an unseeded
source of randomness — same mechanism as every other line on the
platform. The same seed, record count, and generator version (stamped
in every citation and manifest — see the Cite section on the generator
page) will always reproduce byte-identical output. Report the seed and
record count alongside your dataset — that pair, together with the
generator version, is sufficient for anyone to regenerate exactly what
you analyzed.
5. What's decorative vs. load-bearing
Region and transaction amount exist to make records look like real
Ghanaian Mobile Money transactions and are internally consistent with
each other, but they are not the object of the hypotheses above — transaction_amount_ghs is drawn from a per-transaction-type range independently of the fraud
outcome (section 1), not as a driver of it — though it is what loss given
fraud is drawn as a fraction of, once a transaction is
already fraudulent. Transaction velocity's severity-side multiplier is
decorative in the same sense loan purpose's severity-side multiplier
is on susu's own page — its frequency-side role is load-bearing; only
its severity-side role is decorative. Unlike every insurance line and
susu, momo records carry no subscriber name field at all: a Mobile
Money fraud log is keyed off a wallet/MSISDN identifier, not a name,
so there is no name-inclusion toggle to disable here (contrast the
other lines' name fields, gated by the name-inclusion toggle).
6. Known limitations
- This is synthetic data. It should never be presented as, or mistaken for, real telco or transaction data in a publication.
- The four frequency-side risk factors combine additively in log-odds space, with no interaction effects between transaction velocity, SIM-swap recency, wallet tenure, and agent cash-out channel modeled beyond that simple addition, unless you add them yourself downstream.
- Loss given fraud is capped at 1.0x transaction amount regardless of how Cash-Out status, agent channel, and transaction velocity stack — this line's loss cap, a harder constraint than it might look, since it follows directly from a fraudulent transaction not being able to lose more than it was worth, not from a chosen sanity limit.
- The agent cash-out channel hypotheses (both stages) are only measurable on Cash-Out rows (about 8% of all transactions), and the severity-side one further narrows to Cash-Out rows that are also fraudulent — generating fewer than a few thousand records will make the frequency-side comparison look noisy, and fewer than roughly 200,000 will make the severity-side one look noisy, even though the other hypotheses hold up at smaller n.
- If your thesis needs a different balance between the six hypotheses, adjust the relevant loadings in the Advanced: customize the model panel on the Mobile Money generator and say so in your write-up.
7. Citing this tool
Something like: "Synthetic data generated using Dataset Foundry (seed = 42, n = 20,000), a deterministic Mobile Money fraud-signal data generator built for this thesis in the absence of accessible real-world MoMo transaction data." Include the seed and record count so the exact dataset is reproducible from the tool alone — or use the auto-generated citation block on the generator page, which also includes the generator version and any advanced-config changes.