make-mp-data 2.1.6 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -0
- package/dungeons/adspend.js +2 -2
- package/dungeons/ai-chat-analytics-ed.js +3 -2
- package/dungeons/anon.js +2 -2
- package/dungeons/array-of-object-loopup.js +181 -0
- package/dungeons/benchmark-heavy.js +241 -0
- package/dungeons/benchmark-light.js +141 -0
- package/dungeons/big.js +9 -8
- package/dungeons/business.js +2 -1
- package/dungeons/clinch-agi.js +632 -0
- package/dungeons/complex.js +3 -2
- package/dungeons/copilot.js +383 -0
- package/dungeons/ecommerce-store.js +0 -0
- package/dungeons/experiments.js +5 -4
- package/dungeons/foobar.js +101 -101
- package/dungeons/funnels.js +2 -2
- package/dungeons/gaming.js +3 -2
- package/dungeons/harness/harness-education.js +988 -0
- package/dungeons/harness/harness-fintech.js +976 -0
- package/dungeons/harness/harness-food.js +985 -0
- package/dungeons/harness/harness-gaming.js +1178 -0
- package/dungeons/harness/harness-media.js +961 -0
- package/dungeons/harness/harness-sass.js +923 -0
- package/dungeons/harness/harness-social.js +928 -0
- package/dungeons/kurby.js +211 -0
- package/dungeons/media.js +5 -4
- package/dungeons/mil.js +4 -3
- package/dungeons/mirror.js +2 -2
- package/dungeons/money2020-ed.js +8 -7
- package/dungeons/sanity.js +3 -2
- package/dungeons/scd.js +3 -2
- package/dungeons/simple.js +29 -14
- package/dungeons/strict-event-test.js +30 -0
- package/dungeons/student-teacher.js +3 -2
- package/dungeons/text-generation.js +84 -85
- package/dungeons/too-big-events.js +166 -0
- package/dungeons/uday-schema.json +220 -0
- package/dungeons/userAgent.js +4 -3
- package/index.js +41 -54
- package/lib/core/config-validator.js +122 -7
- package/lib/core/context.js +7 -14
- package/lib/core/storage.js +60 -30
- package/lib/generators/adspend.js +12 -27
- package/lib/generators/events.js +6 -7
- package/lib/generators/funnels.js +16 -5
- package/lib/generators/product-lookup.js +262 -0
- package/lib/generators/product-names.js +195 -0
- package/lib/generators/profiles.js +3 -3
- package/lib/generators/scd.js +13 -3
- package/lib/generators/text.js +17 -4
- package/lib/orchestrators/mixpanel-sender.js +251 -208
- package/lib/orchestrators/user-loop.js +57 -19
- package/lib/templates/funnels-instructions.txt +272 -0
- package/lib/templates/hook-examples.json +187 -0
- package/lib/templates/hooks-instructions.txt +295 -8
- package/lib/templates/phrases.js +473 -16
- package/lib/templates/refine-instructions.txt +485 -0
- package/lib/templates/schema-instructions.txt +239 -109
- package/lib/templates/schema.d.ts +173 -0
- package/lib/templates/verbose-schema.js +140 -206
- package/lib/utils/ai.js +853 -77
- package/lib/utils/chart.js +210 -0
- package/lib/utils/function-registry.js +285 -0
- package/lib/utils/json-evaluator.js +172 -0
- package/lib/utils/logger.js +38 -0
- package/lib/utils/mixpanel.js +101 -0
- package/lib/utils/project.js +3 -2
- package/lib/utils/utils.js +41 -4
- package/package.json +13 -19
- package/types.d.ts +15 -5
- package/lib/generators/text-bak-old.js +0 -1121
- package/lib/orchestrators/worker-manager.js +0 -203
- package/lib/templates/phrases-bak.js +0 -925
- package/lib/templates/prompt (old).txt +0 -98
- package/lib/templates/scratch-dungeon-template.js +0 -116
- package/lib/templates/textQuickTest.js +0 -172
|
@@ -0,0 +1,976 @@
|
|
|
1
|
+
import dayjs from "dayjs";
|
|
2
|
+
import utc from "dayjs/plugin/utc.js";
|
|
3
|
+
import "dotenv/config";
|
|
4
|
+
import * as u from "../../lib/utils/utils.js";
|
|
5
|
+
|
|
6
|
+
const SEED = "harness-fintech";
|
|
7
|
+
dayjs.extend(utc);
|
|
8
|
+
const chance = u.initChance(SEED);
|
|
9
|
+
const num_users = 5_000;
|
|
10
|
+
const days = 100;
|
|
11
|
+
|
|
12
|
+
/** @typedef {import("../../types.js").Dungeon} Config */
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* NEEDLE IN A HAYSTACK - NEOBANK APP DESIGN
|
|
16
|
+
*
|
|
17
|
+
* NexBank - A Chime/Revolut-style neobank app where users manage accounts, make transactions,
|
|
18
|
+
* send transfers, pay bills, set budgets, invest, apply for loans, and earn rewards.
|
|
19
|
+
*
|
|
20
|
+
* CORE USER LOOP:
|
|
21
|
+
* Users open accounts (personal or business) through one of four channels (app, web, referral,
|
|
22
|
+
* branch). They begin using the app to check balances, make transactions (purchases, ATM
|
|
23
|
+
* withdrawals, direct deposits, refunds), and send transfers (internal, external, P2P, wire).
|
|
24
|
+
* The app tracks spending patterns and encourages financial wellness through budgets, savings
|
|
25
|
+
* goals, and investment tools.
|
|
26
|
+
*
|
|
27
|
+
* ACCOUNT TIERS:
|
|
28
|
+
* Three-tiered system drives monetization and user segmentation:
|
|
29
|
+
* - Basic (free): Standard banking, limited rewards
|
|
30
|
+
* - Plus ($4.99/mo): Enhanced rewards, priority support, budgeting tools
|
|
31
|
+
* - Premium ($14.99/mo): 3x rewards, 2x investment returns, premium analytics
|
|
32
|
+
* Tiers are assigned as superProps, creating a persistent segmentation dimension.
|
|
33
|
+
*
|
|
34
|
+
* TRANSACTION ECOSYSTEM:
|
|
35
|
+
* Transactions are the heartbeat of the app. Seven merchant categories (grocery, restaurant,
|
|
36
|
+
* gas, retail, online, subscription, utilities) and four payment methods (debit, credit,
|
|
37
|
+
* contactless, online) model realistic spending. Transaction amounts follow a power-law
|
|
38
|
+
* distribution centered around $50, with occasional large purchases up to $5,000.
|
|
39
|
+
*
|
|
40
|
+
* FINANCIAL WELLNESS:
|
|
41
|
+
* Budget creation and alerts model the "financial health" feature set. Users create budgets
|
|
42
|
+
* across six categories with monthly limits, then receive alerts when approaching or exceeding
|
|
43
|
+
* limits. This creates a measurable engagement loop: budget creators save more and invest more
|
|
44
|
+
* (Hook #5: Budget Users Save More).
|
|
45
|
+
*
|
|
46
|
+
* SAVINGS & INVESTMENT:
|
|
47
|
+
* Savings goals (emergency, vacation, car, home, education, retirement) with target amounts
|
|
48
|
+
* and monthly contributions model long-term financial planning. Investment events (stocks, ETF,
|
|
49
|
+
* crypto, bonds, mutual funds) with buy/sell actions model portfolio activity.
|
|
50
|
+
*
|
|
51
|
+
* LENDING:
|
|
52
|
+
* Loan applications (personal, auto, home, student, business) flow through apply-to-approved
|
|
53
|
+
* funnel. Requested vs. approved amounts and interest rates create realistic lending analytics.
|
|
54
|
+
*
|
|
55
|
+
* SUPPORT & ENGAGEMENT:
|
|
56
|
+
* Support contacts across four channels (chat, phone, email, in-app) with resolution tracking.
|
|
57
|
+
* Notifications (transaction, low_balance, bill_due, reward, security, promo) with action
|
|
58
|
+
* tracking model re-engagement effectiveness.
|
|
59
|
+
*
|
|
60
|
+
* BILL PAYMENTS:
|
|
61
|
+
* Six bill types (rent, utilities, phone, insurance, subscription, loan_payment) with auto-pay
|
|
62
|
+
* toggle. Hook #6 (Auto-Pay Loyalty) creates a pattern where auto-pay users never miss
|
|
63
|
+
* payments while manual payers miss 30%, modeling real-world payment reliability.
|
|
64
|
+
*
|
|
65
|
+
* REWARDS:
|
|
66
|
+
* Four reward types (cashback, points, discount, partner_offer) with values scaled by
|
|
67
|
+
* account tier. Premium users earn 3x rewards (Hook #7), creating clear tier-based value
|
|
68
|
+
* differentiation visible in the data.
|
|
69
|
+
*
|
|
70
|
+
* HOUSEHOLD GROUPS:
|
|
71
|
+
* 500 households group users for shared financial analytics. Household-level properties
|
|
72
|
+
* (size, combined income, financial health score, primary bank) enable group-level analysis
|
|
73
|
+
* of shared financial behaviors.
|
|
74
|
+
*
|
|
75
|
+
* WHY THESE EVENTS/PROPERTIES?
|
|
76
|
+
* - Events model a complete banking loop: onboarding -> daily use -> financial planning -> monetization
|
|
77
|
+
* - Properties enable cohort analysis: account type, tier, income bracket, credit score
|
|
78
|
+
* - Funnels reveal friction: onboarding completion, transfer flows, investment journeys
|
|
79
|
+
* - Financial wellness features (budgets, goals) create engagement depth visible in the data
|
|
80
|
+
* - Tier-based rewards and investment returns drive business metric differences
|
|
81
|
+
* - The "needle in haystack" hooks simulate real fintech product insights hidden in production data
|
|
82
|
+
*/
|
|
83
|
+
|
|
84
|
+
/** @type {Config} */
|
|
85
|
+
const config = {
|
|
86
|
+
token: "048f954dddbfbd4bff34edd8b3d95c06",
|
|
87
|
+
seed: SEED,
|
|
88
|
+
numDays: days,
|
|
89
|
+
numEvents: num_users * 120,
|
|
90
|
+
numUsers: num_users,
|
|
91
|
+
hasAnonIds: false,
|
|
92
|
+
hasSessionIds: true,
|
|
93
|
+
format: "json",
|
|
94
|
+
gzip: true,
|
|
95
|
+
alsoInferFunnels: false,
|
|
96
|
+
hasLocation: true,
|
|
97
|
+
hasAndroidDevices: true,
|
|
98
|
+
hasIOSDevices: true,
|
|
99
|
+
hasDesktopDevices: true,
|
|
100
|
+
hasBrowser: false,
|
|
101
|
+
hasCampaigns: false,
|
|
102
|
+
isAnonymous: false,
|
|
103
|
+
hasAdSpend: false,
|
|
104
|
+
percentUsersBornInDataset: 50,
|
|
105
|
+
|
|
106
|
+
hasAvatar: true,
|
|
107
|
+
makeChart: false,
|
|
108
|
+
|
|
109
|
+
batchSize: 2_500_000,
|
|
110
|
+
concurrency: 10,
|
|
111
|
+
writeToDisk: false,
|
|
112
|
+
|
|
113
|
+
scdProps: {},
|
|
114
|
+
|
|
115
|
+
funnels: [
|
|
116
|
+
{
|
|
117
|
+
sequence: ["account opened", "app session", "balance checked"],
|
|
118
|
+
isFirstFunnel: true,
|
|
119
|
+
conversionRate: 85,
|
|
120
|
+
timeToConvert: 0.25,
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
// Daily banking: check balance, view transactions - most common activity
|
|
124
|
+
sequence: ["app session", "balance checked", "transaction completed"],
|
|
125
|
+
conversionRate: 80,
|
|
126
|
+
timeToConvert: 0.5,
|
|
127
|
+
weight: 5,
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
// Transfers and notifications
|
|
131
|
+
sequence: ["app session", "transfer sent", "notification opened"],
|
|
132
|
+
conversionRate: 50,
|
|
133
|
+
timeToConvert: 1,
|
|
134
|
+
weight: 3,
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
// Bill payment flow
|
|
138
|
+
sequence: ["app session", "bill paid", "notification opened"],
|
|
139
|
+
conversionRate: 60,
|
|
140
|
+
timeToConvert: 1,
|
|
141
|
+
weight: 3,
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
// Financial planning: budgets and savings
|
|
145
|
+
sequence: ["budget created", "budget alert", "savings goal set"],
|
|
146
|
+
conversionRate: 40,
|
|
147
|
+
timeToConvert: 12,
|
|
148
|
+
weight: 2,
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
// Investment and rewards
|
|
152
|
+
sequence: ["balance checked", "investment made", "reward redeemed"],
|
|
153
|
+
conversionRate: 30,
|
|
154
|
+
timeToConvert: 5,
|
|
155
|
+
weight: 2,
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
// Support and account management
|
|
159
|
+
sequence: ["support contacted", "card locked", "dispute filed"],
|
|
160
|
+
conversionRate: 35,
|
|
161
|
+
timeToConvert: 2,
|
|
162
|
+
weight: 1,
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
// Lending flow
|
|
166
|
+
sequence: ["loan applied", "loan approved", "premium upgraded"],
|
|
167
|
+
conversionRate: 25,
|
|
168
|
+
timeToConvert: 10,
|
|
169
|
+
weight: 1,
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
|
|
173
|
+
events: [
|
|
174
|
+
{
|
|
175
|
+
event: "account opened",
|
|
176
|
+
weight: 1,
|
|
177
|
+
isFirstEvent: true,
|
|
178
|
+
properties: {
|
|
179
|
+
"account_type": u.pickAWinner(["personal", "business", "personal"]),
|
|
180
|
+
"signup_channel": u.pickAWinner(["app", "web", "referral", "branch"]),
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
event: "app session",
|
|
185
|
+
weight: 20,
|
|
186
|
+
properties: {
|
|
187
|
+
"session_duration_sec": u.weighNumRange(10, 600, 0.3, 60),
|
|
188
|
+
"pages_viewed": u.weighNumRange(1, 15, 0.5, 3),
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
event: "balance checked",
|
|
193
|
+
weight: 15,
|
|
194
|
+
properties: {
|
|
195
|
+
"account_balance": u.weighNumRange(0, 50000, 0.3, 2500),
|
|
196
|
+
"account_type": u.pickAWinner(["checking", "savings", "investment"]),
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
event: "transaction completed",
|
|
201
|
+
weight: 18,
|
|
202
|
+
properties: {
|
|
203
|
+
"transaction_type": u.pickAWinner(["purchase", "atm", "direct_deposit", "refund"]),
|
|
204
|
+
"amount": u.weighNumRange(1, 5000, 0.3, 50),
|
|
205
|
+
"merchant_category": u.pickAWinner(["grocery", "restaurant", "gas", "retail", "online", "subscription", "utilities"]),
|
|
206
|
+
"payment_method": u.pickAWinner(["debit", "credit", "contactless", "online"]),
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
event: "transfer sent",
|
|
211
|
+
weight: 8,
|
|
212
|
+
properties: {
|
|
213
|
+
"transfer_type": u.pickAWinner(["internal", "external", "p2p", "wire"]),
|
|
214
|
+
"amount": u.weighNumRange(10, 10000, 0.3, 200),
|
|
215
|
+
"recipient_type": u.pickAWinner(["friend", "family", "business", "self"]),
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
event: "bill paid",
|
|
220
|
+
weight: 6,
|
|
221
|
+
properties: {
|
|
222
|
+
"bill_type": u.pickAWinner(["rent", "utilities", "phone", "insurance", "subscription", "loan_payment"]),
|
|
223
|
+
"amount": u.weighNumRange(20, 3000, 0.5, 150),
|
|
224
|
+
"auto_pay": u.pickAWinner([true, false], 0.4),
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
event: "budget created",
|
|
229
|
+
weight: 3,
|
|
230
|
+
properties: {
|
|
231
|
+
"category": u.pickAWinner(["food", "transport", "entertainment", "shopping", "bills", "savings"]),
|
|
232
|
+
"monthly_limit": u.weighNumRange(50, 2000, 0.5, 300),
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
event: "budget alert",
|
|
237
|
+
weight: 4,
|
|
238
|
+
properties: {
|
|
239
|
+
"alert_type": u.pickAWinner(["approaching_limit", "exceeded", "on_track"]),
|
|
240
|
+
"percent_used": u.weighNumRange(50, 150, 1, 90),
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
event: "savings goal set",
|
|
245
|
+
weight: 3,
|
|
246
|
+
properties: {
|
|
247
|
+
"goal_type": u.pickAWinner(["emergency", "vacation", "car", "home", "education", "retirement"]),
|
|
248
|
+
"target_amount": u.weighNumRange(500, 50000, 0.3, 5000),
|
|
249
|
+
"monthly_contribution": u.weighNumRange(25, 2000, 0.5, 200),
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
event: "investment made",
|
|
254
|
+
weight: 4,
|
|
255
|
+
properties: {
|
|
256
|
+
"investment_type": u.pickAWinner(["stocks", "etf", "crypto", "bonds", "mutual_fund"]),
|
|
257
|
+
"amount": u.weighNumRange(10, 10000, 0.3, 250),
|
|
258
|
+
"action": u.pickAWinner(["buy", "sell", "buy"]),
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
event: "card locked",
|
|
263
|
+
weight: 1,
|
|
264
|
+
properties: {
|
|
265
|
+
"reason": u.pickAWinner(["lost", "stolen", "suspicious_activity", "travel"]),
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
event: "dispute filed",
|
|
270
|
+
weight: 1,
|
|
271
|
+
properties: {
|
|
272
|
+
"dispute_amount": u.weighNumRange(10, 2000, 0.5, 100),
|
|
273
|
+
"reason": u.pickAWinner(["unauthorized", "duplicate", "not_received", "damaged", "wrong_amount"]),
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
event: "loan applied",
|
|
278
|
+
weight: 2,
|
|
279
|
+
properties: {
|
|
280
|
+
"loan_type": u.pickAWinner(["personal", "auto", "home", "student", "business"]),
|
|
281
|
+
"requested_amount": u.weighNumRange(1000, 100000, 0.3, 10000),
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
event: "loan approved",
|
|
286
|
+
weight: 1,
|
|
287
|
+
properties: {
|
|
288
|
+
"loan_type": u.pickAWinner(["personal", "auto", "home", "student", "business"]),
|
|
289
|
+
"approved_amount": u.weighNumRange(1000, 100000, 0.3, 10000),
|
|
290
|
+
"interest_rate": u.weighNumRange(3, 25, 1, 8),
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
event: "premium upgraded",
|
|
295
|
+
weight: 2,
|
|
296
|
+
properties: {
|
|
297
|
+
"old_tier": u.pickAWinner(["basic", "plus", "premium"]),
|
|
298
|
+
"new_tier": u.pickAWinner(["plus", "premium", "premium"]),
|
|
299
|
+
"monthly_fee": u.pickAWinner([4.99, 9.99, 14.99]),
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
event: "support contacted",
|
|
304
|
+
weight: 3,
|
|
305
|
+
properties: {
|
|
306
|
+
"channel": u.pickAWinner(["chat", "phone", "email", "in_app"]),
|
|
307
|
+
"issue_type": u.pickAWinner(["transaction", "account", "card", "transfer", "technical"]),
|
|
308
|
+
"resolved": u.pickAWinner([true, false], 0.8),
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
event: "notification opened",
|
|
313
|
+
weight: 10,
|
|
314
|
+
properties: {
|
|
315
|
+
"notification_type": u.pickAWinner(["transaction", "low_balance", "bill_due", "reward", "security", "promo"]),
|
|
316
|
+
"action_taken": u.pickAWinner([true, false], 0.6),
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
event: "reward redeemed",
|
|
321
|
+
weight: 4,
|
|
322
|
+
properties: {
|
|
323
|
+
"reward_type": u.pickAWinner(["cashback", "points", "discount", "partner_offer"]),
|
|
324
|
+
"value": u.weighNumRange(1, 100, 0.5, 10),
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
],
|
|
328
|
+
|
|
329
|
+
superProps: {
|
|
330
|
+
account_tier: u.pickAWinner(["basic", "basic", "basic", "plus", "plus", "premium"]),
|
|
331
|
+
platform: u.pickAWinner(["ios", "android", "web"]),
|
|
332
|
+
},
|
|
333
|
+
|
|
334
|
+
userProps: {
|
|
335
|
+
"credit_score_range": u.pickAWinner(["300-579", "580-669", "670-739", "740-799", "800-850"]),
|
|
336
|
+
"income_bracket": u.pickAWinner(["under_30k", "30k_50k", "50k_75k", "75k_100k", "100k_150k", "over_150k"]),
|
|
337
|
+
"account_age_months": u.weighNumRange(1, 60, 0.5, 12),
|
|
338
|
+
"total_balance": u.weighNumRange(0, 100000, 0.3, 5000),
|
|
339
|
+
"has_direct_deposit": u.pickAWinner([true, false], 0.6),
|
|
340
|
+
},
|
|
341
|
+
|
|
342
|
+
groupKeys: [
|
|
343
|
+
["household_id", 500, ["transaction completed", "transfer sent", "bill paid", "savings goal set"]],
|
|
344
|
+
],
|
|
345
|
+
|
|
346
|
+
groupProps: {
|
|
347
|
+
household_id: {
|
|
348
|
+
"household_size": u.weighNumRange(1, 6),
|
|
349
|
+
"combined_income": u.weighNumRange(20000, 300000, 0.3, 75000),
|
|
350
|
+
"financial_health_score": u.weighNumRange(1, 100, 1, 65),
|
|
351
|
+
"primary_bank": u.pickAWinner(["NexBank_only", "multi_bank", "NexBank_only"]),
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
|
|
355
|
+
lookupTables: [],
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* ARCHITECTED ANALYTICS HOOKS
|
|
359
|
+
*
|
|
360
|
+
* This hook function creates 8 deliberate patterns in the data:
|
|
361
|
+
*
|
|
362
|
+
* 1. PERSONAL VS BUSINESS: Business accounts get employee_count, revenue; personal get age_range, life_stage
|
|
363
|
+
* 2. PAYDAY PATTERNS: Transactions spike on 1st/15th with bigger deposits and post-payday spending
|
|
364
|
+
* 3. FRAUD DETECTION: 3% of users experience a fraud burst (rapid high-value txns -> card lock -> dispute -> support)
|
|
365
|
+
* 4. LOW BALANCE CHURN: Users with chronic low balances (<$500) lose 50% of activity after day 30
|
|
366
|
+
* 5. BUDGET DISCIPLINE: Budget creators save 2x more and invest 1.5x more
|
|
367
|
+
* 6. AUTO-PAY LOYALTY: Auto-pay users never miss bills; manual payers miss 30%
|
|
368
|
+
* 7. PREMIUM TIER VALUE: Premium users get 3x rewards; Plus users get 1.5x; Premium investors get 2x returns
|
|
369
|
+
* 8. MONTH-END ANXIETY: Last 3 days of month see 40% longer sessions and 30% lower balances
|
|
370
|
+
*/
|
|
371
|
+
hook: function (record, type, meta) {
|
|
372
|
+
const NOW = dayjs();
|
|
373
|
+
const DATASET_START = NOW.subtract(days, "days");
|
|
374
|
+
|
|
375
|
+
// ===============================================================
|
|
376
|
+
// Hook #1: PERSONAL VS BUSINESS ACCOUNTS (user)
|
|
377
|
+
// 20% of users are randomly tagged as business accounts with
|
|
378
|
+
// employee_count, annual_revenue, and industry. The other 80% get
|
|
379
|
+
// personal attributes: age_range and life_stage.
|
|
380
|
+
// ===============================================================
|
|
381
|
+
if (type === "user") {
|
|
382
|
+
const isBusiness = chance.bool({ likelihood: 20 });
|
|
383
|
+
if (isBusiness) {
|
|
384
|
+
record.account_segment = "business";
|
|
385
|
+
record.employee_count = chance.integer({ min: 5, max: 500 });
|
|
386
|
+
record.annual_revenue = chance.integer({ min: 100000, max: 10000000 });
|
|
387
|
+
record.industry = chance.pickone(["tech", "retail", "food", "services", "healthcare"]);
|
|
388
|
+
} else {
|
|
389
|
+
record.account_segment = "personal";
|
|
390
|
+
record.age_range = `${chance.pickone([18, 25, 35, 45, 55])}-${chance.pickone([24, 34, 44, 54, 65])}`;
|
|
391
|
+
record.life_stage = chance.pickone(["student", "early_career", "established", "pre_retirement", "retired"]);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// ===============================================================
|
|
396
|
+
// Hook #2: PAYDAY PATTERNS (event)
|
|
397
|
+
// On the 1st and 15th, direct deposit transactions are 2x bigger
|
|
398
|
+
// and tagged with payday: true. On days 1-3 and 15-17, transfers
|
|
399
|
+
// have a 40% chance of 1.5x boost (post-payday spending).
|
|
400
|
+
// ===============================================================
|
|
401
|
+
if (type === "event") {
|
|
402
|
+
const EVENT_TIME = dayjs(record.time);
|
|
403
|
+
const dayOfMonth = EVENT_TIME.date();
|
|
404
|
+
|
|
405
|
+
// Payday: 1st and 15th
|
|
406
|
+
if (record.event === "transaction completed" && record.transaction_type === "direct_deposit") {
|
|
407
|
+
if (dayOfMonth === 1 || dayOfMonth === 15) {
|
|
408
|
+
record.amount = Math.floor((record.amount || 50) * 2);
|
|
409
|
+
record.payday = true;
|
|
410
|
+
} else {
|
|
411
|
+
record.payday = false;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Post-payday spending: days 1-3 and 15-17
|
|
416
|
+
if (record.event === "transfer sent") {
|
|
417
|
+
const isPaydayWindow = (dayOfMonth >= 1 && dayOfMonth <= 3) || (dayOfMonth >= 15 && dayOfMonth <= 17);
|
|
418
|
+
if (isPaydayWindow && chance.bool({ likelihood: 40 })) {
|
|
419
|
+
record.amount = Math.floor((record.amount || 200) * 1.5);
|
|
420
|
+
record.post_payday_spending = true;
|
|
421
|
+
} else {
|
|
422
|
+
record.post_payday_spending = false;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// ===============================================================
|
|
427
|
+
// Hook #6: AUTO-PAY LOYALTY (event)
|
|
428
|
+
// Users with auto_pay=false on bill paid events have a 30% chance
|
|
429
|
+
// of the event being dropped entirely (missed payment). Surviving
|
|
430
|
+
// manual payments get tagged. Auto-pay users always succeed.
|
|
431
|
+
// ===============================================================
|
|
432
|
+
if (record.event === "bill paid") {
|
|
433
|
+
if (record.auto_pay === false || record.auto_pay === undefined) {
|
|
434
|
+
if (chance.bool({ likelihood: 30 })) {
|
|
435
|
+
record.event = "bill payment missed";
|
|
436
|
+
record.missed_payment = true;
|
|
437
|
+
record.manual_payment = false;
|
|
438
|
+
} else {
|
|
439
|
+
record.missed_payment = false;
|
|
440
|
+
record.manual_payment = true;
|
|
441
|
+
}
|
|
442
|
+
} else {
|
|
443
|
+
record.missed_payment = false;
|
|
444
|
+
record.manual_payment = false;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// ===============================================================
|
|
449
|
+
// Hook #7: PREMIUM TIER VALUE (event)
|
|
450
|
+
// Premium tier users get 3x reward values and 2x investment sell
|
|
451
|
+
// returns. Plus tier users get 1.5x rewards.
|
|
452
|
+
// ===============================================================
|
|
453
|
+
if (record.event === "reward redeemed") {
|
|
454
|
+
if (record.account_tier === "premium") {
|
|
455
|
+
record.value = Math.floor((record.value || 10) * 3);
|
|
456
|
+
record.premium_reward = true;
|
|
457
|
+
} else if (record.account_tier === "plus") {
|
|
458
|
+
record.value = Math.floor((record.value || 10) * 1.5);
|
|
459
|
+
record.premium_reward = false;
|
|
460
|
+
} else {
|
|
461
|
+
record.premium_reward = false;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (record.event === "investment made" && record.action === "sell") {
|
|
466
|
+
if (record.account_tier === "premium") {
|
|
467
|
+
record.amount = Math.floor((record.amount || 250) * 2);
|
|
468
|
+
record.premium_returns = true;
|
|
469
|
+
} else {
|
|
470
|
+
record.premium_returns = false;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// ===============================================================
|
|
475
|
+
// Hook #8: MONTH-END ANXIETY (event)
|
|
476
|
+
// Last 3 days of month (day >= 28) see 40% longer app sessions
|
|
477
|
+
// and 30% lower balances when checked.
|
|
478
|
+
// ===============================================================
|
|
479
|
+
if (record.event === "app session") {
|
|
480
|
+
if (dayOfMonth >= 28) {
|
|
481
|
+
record.session_duration_sec = Math.floor((record.session_duration_sec || 60) * 1.4);
|
|
482
|
+
record.month_end_anxiety = true;
|
|
483
|
+
} else {
|
|
484
|
+
record.month_end_anxiety = false;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (record.event === "balance checked") {
|
|
489
|
+
if (dayOfMonth >= 28) {
|
|
490
|
+
record.account_balance = Math.floor((record.account_balance || 2500) * 0.7);
|
|
491
|
+
record.month_end_check = true;
|
|
492
|
+
} else {
|
|
493
|
+
record.month_end_check = false;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// ===============================================================
|
|
499
|
+
// Hook #3, #4, #5: EVERYTHING - Complex behavioral patterns
|
|
500
|
+
// These hooks operate on the full event stream per user for
|
|
501
|
+
// fraud injection, low-balance churn, and budget discipline.
|
|
502
|
+
// ===============================================================
|
|
503
|
+
if (type === "everything") {
|
|
504
|
+
const userEvents = record;
|
|
505
|
+
const firstEventTime = userEvents.length > 0 ? dayjs(userEvents[0].time) : null;
|
|
506
|
+
|
|
507
|
+
// -----------------------------------------------------------
|
|
508
|
+
// Hook #3: FRAUD DETECTION PATTERN
|
|
509
|
+
// 3% of users experience a fraud event sequence: a burst of
|
|
510
|
+
// 3-5 rapid high-value transactions within 1 hour, followed by
|
|
511
|
+
// card locked, dispute filed, and support contacted events.
|
|
512
|
+
// All injected events are tagged with fraud_sequence: true.
|
|
513
|
+
// -----------------------------------------------------------
|
|
514
|
+
if (chance.bool({ likelihood: 3 })) {
|
|
515
|
+
// Find the midpoint of the user's timeline
|
|
516
|
+
if (userEvents.length >= 2) {
|
|
517
|
+
const midIdx = Math.floor(userEvents.length / 2);
|
|
518
|
+
const midEvent = userEvents[midIdx];
|
|
519
|
+
const midTime = dayjs(midEvent.time);
|
|
520
|
+
const distinctId = midEvent.user_id;
|
|
521
|
+
|
|
522
|
+
// Inject 3-5 rapid high-value transactions
|
|
523
|
+
const burstCount = chance.integer({ min: 3, max: 5 });
|
|
524
|
+
const fraudEvents = [];
|
|
525
|
+
|
|
526
|
+
for (let i = 0; i < burstCount; i++) {
|
|
527
|
+
fraudEvents.push({
|
|
528
|
+
event: "transaction completed",
|
|
529
|
+
time: midTime.add(i * 10, "minutes").toISOString(),
|
|
530
|
+
user_id: distinctId,
|
|
531
|
+
transaction_type: "purchase",
|
|
532
|
+
amount: chance.integer({ min: 500, max: 3000 }),
|
|
533
|
+
merchant_category: chance.pickone(["online", "retail"]),
|
|
534
|
+
payment_method: "credit",
|
|
535
|
+
fraud_sequence: true,
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// Card locked after the burst
|
|
540
|
+
fraudEvents.push({
|
|
541
|
+
event: "card locked",
|
|
542
|
+
time: midTime.add(burstCount * 10 + 5, "minutes").toISOString(),
|
|
543
|
+
user_id: distinctId,
|
|
544
|
+
reason: "suspicious_activity",
|
|
545
|
+
fraud_sequence: true,
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
// Dispute filed shortly after
|
|
549
|
+
fraudEvents.push({
|
|
550
|
+
event: "dispute filed",
|
|
551
|
+
time: midTime.add(burstCount * 10 + 30, "minutes").toISOString(),
|
|
552
|
+
user_id: distinctId,
|
|
553
|
+
dispute_amount: chance.integer({ min: 500, max: 3000 }),
|
|
554
|
+
reason: "unauthorized",
|
|
555
|
+
fraud_sequence: true,
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
// Support contacted
|
|
559
|
+
fraudEvents.push({
|
|
560
|
+
event: "support contacted",
|
|
561
|
+
time: midTime.add(burstCount * 10 + 45, "minutes").toISOString(),
|
|
562
|
+
user_id: distinctId,
|
|
563
|
+
channel: "phone",
|
|
564
|
+
issue_type: "card",
|
|
565
|
+
resolved: true,
|
|
566
|
+
fraud_sequence: true,
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
// Splice all fraud events into the user's timeline
|
|
570
|
+
userEvents.splice(midIdx + 1, 0, ...fraudEvents);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// -----------------------------------------------------------
|
|
575
|
+
// Hook #4: LOW BALANCE CHURN
|
|
576
|
+
// Users who have 3+ balance checks showing < $500 are
|
|
577
|
+
// "struggling". After day 30, 50% of their events are removed
|
|
578
|
+
// and remaining events are tagged low_balance_churn: true.
|
|
579
|
+
// -----------------------------------------------------------
|
|
580
|
+
let lowBalanceChecks = 0;
|
|
581
|
+
userEvents.forEach((event) => {
|
|
582
|
+
if (event.event === "balance checked" && (event.account_balance || 0) < 500) {
|
|
583
|
+
lowBalanceChecks++;
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
if (lowBalanceChecks >= 3) {
|
|
588
|
+
const day30 = DATASET_START.add(30, "days");
|
|
589
|
+
for (let i = userEvents.length - 1; i >= 0; i--) {
|
|
590
|
+
const evt = userEvents[i];
|
|
591
|
+
if (dayjs(evt.time).isAfter(day30)) {
|
|
592
|
+
if (chance.bool({ likelihood: 50 })) {
|
|
593
|
+
userEvents.splice(i, 1);
|
|
594
|
+
} else {
|
|
595
|
+
evt.low_balance_churn = true;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// -----------------------------------------------------------
|
|
602
|
+
// Hook #5: BUDGET USERS SAVE MORE
|
|
603
|
+
// Users who created any budget have 2x savings contributions,
|
|
604
|
+
// 1.5x investment amounts, and extra savings goal events
|
|
605
|
+
// spliced into their timeline. Tagged budget_discipline: true.
|
|
606
|
+
// -----------------------------------------------------------
|
|
607
|
+
let hasBudget = false;
|
|
608
|
+
userEvents.forEach((event) => {
|
|
609
|
+
if (event.event === "budget created") {
|
|
610
|
+
hasBudget = true;
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
if (hasBudget) {
|
|
615
|
+
userEvents.forEach((event, idx) => {
|
|
616
|
+
const eventTime = dayjs(event.time);
|
|
617
|
+
|
|
618
|
+
// Double savings contributions
|
|
619
|
+
if (event.event === "savings goal set") {
|
|
620
|
+
event.monthly_contribution = Math.floor((event.monthly_contribution || 200) * 2);
|
|
621
|
+
event.budget_discipline = true;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// 1.5x investment amounts
|
|
625
|
+
if (event.event === "investment made") {
|
|
626
|
+
event.amount = Math.floor((event.amount || 250) * 1.5);
|
|
627
|
+
event.budget_discipline = true;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// Splice extra savings goal events (budget-conscious users set more goals)
|
|
631
|
+
if (event.event === "budget created" && chance.bool({ likelihood: 50 })) {
|
|
632
|
+
const extraGoal = {
|
|
633
|
+
event: "savings goal set",
|
|
634
|
+
time: eventTime.add(chance.integer({ min: 1, max: 7 }), "days").toISOString(),
|
|
635
|
+
user_id: event.user_id,
|
|
636
|
+
goal_type: chance.pickone(["emergency", "vacation", "car", "home"]),
|
|
637
|
+
target_amount: chance.integer({ min: 1000, max: 20000 }),
|
|
638
|
+
monthly_contribution: chance.integer({ min: 100, max: 800 }),
|
|
639
|
+
budget_discipline: true,
|
|
640
|
+
};
|
|
641
|
+
userEvents.splice(idx + 1, 0, extraGoal);
|
|
642
|
+
}
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
return record;
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
|
|
651
|
+
export default config;
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* ===================================================================
|
|
655
|
+
* NEEDLE IN A HAYSTACK - NEXBANK NEOBANK ANALYTICS
|
|
656
|
+
* ===================================================================
|
|
657
|
+
*
|
|
658
|
+
* A Chime/Revolut-style neobank dungeon with 8 deliberately architected
|
|
659
|
+
* analytics insights hidden in the data. This dungeon is designed to
|
|
660
|
+
* showcase advanced fintech product analytics patterns and demonstrate
|
|
661
|
+
* how to find "needles" (meaningful insights) in "haystacks" (large
|
|
662
|
+
* banking datasets).
|
|
663
|
+
*
|
|
664
|
+
* ===================================================================
|
|
665
|
+
* DATASET OVERVIEW
|
|
666
|
+
* ===================================================================
|
|
667
|
+
*
|
|
668
|
+
* - 5,000 users over 100 days
|
|
669
|
+
* - 360,000 events across 18 event types
|
|
670
|
+
* - 3 funnels (onboarding, transfer flow, investment journey)
|
|
671
|
+
* - Group analytics (500 households)
|
|
672
|
+
* - Lookup table (500 merchant/transaction entries)
|
|
673
|
+
* - Account tiers (Basic, Plus, Premium)
|
|
674
|
+
*
|
|
675
|
+
* ===================================================================
|
|
676
|
+
* THE 8 ARCHITECTED HOOKS
|
|
677
|
+
* ===================================================================
|
|
678
|
+
*
|
|
679
|
+
* Each hook creates a specific, discoverable analytics insight that
|
|
680
|
+
* simulates real-world fintech product behavior patterns.
|
|
681
|
+
*
|
|
682
|
+
* -------------------------------------------------------------------
|
|
683
|
+
* 1. PERSONAL VS BUSINESS ACCOUNTS (user)
|
|
684
|
+
* -------------------------------------------------------------------
|
|
685
|
+
*
|
|
686
|
+
* PATTERN: 20% of users are business accounts with employee_count
|
|
687
|
+
* (5-500), annual_revenue ($100K-$10M), and industry. The other 80%
|
|
688
|
+
* are personal accounts with age_range and life_stage.
|
|
689
|
+
*
|
|
690
|
+
* HOW TO FIND IT:
|
|
691
|
+
* - Segment users by: account_segment = "business" vs "personal"
|
|
692
|
+
* - Compare: Transaction volumes, average amounts, transfer patterns
|
|
693
|
+
* - Analyze: Industry distribution among business accounts
|
|
694
|
+
*
|
|
695
|
+
* EXPECTED INSIGHT: Business accounts have fundamentally different
|
|
696
|
+
* usage patterns - higher transaction amounts, more wire transfers,
|
|
697
|
+
* and different bill payment profiles.
|
|
698
|
+
*
|
|
699
|
+
* REAL-WORLD ANALOGUE: Identifying and serving different customer
|
|
700
|
+
* segments (B2C vs B2B) with tailored features and pricing.
|
|
701
|
+
*
|
|
702
|
+
* -------------------------------------------------------------------
|
|
703
|
+
* 2. PAYDAY PATTERNS (event)
|
|
704
|
+
* -------------------------------------------------------------------
|
|
705
|
+
*
|
|
706
|
+
* PATTERN: On the 1st and 15th of each month, direct deposit
|
|
707
|
+
* transactions are 2x bigger and tagged with payday: true. On days
|
|
708
|
+
* 1-3 and 15-17, transfer amounts have a 40% chance of being 1.5x
|
|
709
|
+
* larger, tagged with post_payday_spending: true.
|
|
710
|
+
*
|
|
711
|
+
* HOW TO FIND IT:
|
|
712
|
+
* - Chart: transaction completed count and amount by day of month
|
|
713
|
+
* - Filter: transaction_type = "direct_deposit"
|
|
714
|
+
* - Compare: Average transfer amount on days 1-3/15-17 vs other days
|
|
715
|
+
* - Look for: payday: true and post_payday_spending: true tags
|
|
716
|
+
*
|
|
717
|
+
* EXPECTED INSIGHT: Clear biweekly spikes in deposit amounts and
|
|
718
|
+
* subsequent spending activity. The 2-3 days after payday show
|
|
719
|
+
* elevated transfer activity as users move money around.
|
|
720
|
+
*
|
|
721
|
+
* REAL-WORLD ANALOGUE: Payroll cycle effects on banking activity.
|
|
722
|
+
* Banks use this to time marketing, credit offers, and overdraft
|
|
723
|
+
* protection promotions.
|
|
724
|
+
*
|
|
725
|
+
* -------------------------------------------------------------------
|
|
726
|
+
* 3. FRAUD DETECTION PATTERN (everything)
|
|
727
|
+
* -------------------------------------------------------------------
|
|
728
|
+
*
|
|
729
|
+
* PATTERN: 3% of users experience a fraud event sequence at their
|
|
730
|
+
* timeline midpoint: 3-5 rapid high-value transactions ($500-$3,000)
|
|
731
|
+
* within 1 hour, followed by card locked (suspicious_activity),
|
|
732
|
+
* dispute filed (unauthorized), and support contacted (phone/card).
|
|
733
|
+
* All injected events tagged with fraud_sequence: true.
|
|
734
|
+
*
|
|
735
|
+
* HOW TO FIND IT:
|
|
736
|
+
* - Filter events: fraud_sequence = true
|
|
737
|
+
* - Analyze: Time between fraud transactions (< 10 min gaps)
|
|
738
|
+
* - Funnel: transaction completed -> card locked -> dispute filed -> support contacted
|
|
739
|
+
* - Segment: Users with any fraud_sequence event
|
|
740
|
+
*
|
|
741
|
+
* EXPECTED INSIGHT: ~150 users (3% of 5,000) show a distinctive burst
|
|
742
|
+
* pattern of rapid high-value purchases followed by account lockdown.
|
|
743
|
+
* Clear temporal clustering of fraud events.
|
|
744
|
+
*
|
|
745
|
+
* REAL-WORLD ANALOGUE: Fraud detection in banking. Unusual velocity
|
|
746
|
+
* and amount patterns trigger automated alerts and account freezes.
|
|
747
|
+
*
|
|
748
|
+
* -------------------------------------------------------------------
|
|
749
|
+
* 4. LOW BALANCE CHURN (everything)
|
|
750
|
+
* -------------------------------------------------------------------
|
|
751
|
+
*
|
|
752
|
+
* PATTERN: Users with 3+ balance checks showing < $500 are
|
|
753
|
+
* "struggling" users. After day 30, 50% of their events are removed
|
|
754
|
+
* (simulating reduced app usage) and surviving events are tagged
|
|
755
|
+
* with low_balance_churn: true.
|
|
756
|
+
*
|
|
757
|
+
* HOW TO FIND IT:
|
|
758
|
+
* - Segment: Users where count of (balance checked, account_balance < 500) >= 3
|
|
759
|
+
* - Compare: Event counts before day 30 vs after day 30
|
|
760
|
+
* - Filter: low_balance_churn = true
|
|
761
|
+
* - Retention analysis: Compare D30+ retention for low vs healthy balance users
|
|
762
|
+
*
|
|
763
|
+
* EXPECTED INSIGHT: Struggling users show a dramatic drop in activity
|
|
764
|
+
* after the first month. Their engagement halves while healthy-balance
|
|
765
|
+
* users maintain consistent usage.
|
|
766
|
+
*
|
|
767
|
+
* REAL-WORLD ANALOGUE: Financial stress-driven churn. Users who
|
|
768
|
+
* can't maintain balances disengage from their banking app, which
|
|
769
|
+
* predicts account closure.
|
|
770
|
+
*
|
|
771
|
+
* -------------------------------------------------------------------
|
|
772
|
+
* 5. BUDGET USERS SAVE MORE (everything)
|
|
773
|
+
* -------------------------------------------------------------------
|
|
774
|
+
*
|
|
775
|
+
* PATTERN: Users who create any budget have 2x monthly savings
|
|
776
|
+
* contributions, 1.5x investment amounts, and extra savings goal
|
|
777
|
+
* events spliced into their timeline. All affected events tagged
|
|
778
|
+
* with budget_discipline: true.
|
|
779
|
+
*
|
|
780
|
+
* HOW TO FIND IT:
|
|
781
|
+
* - Segment: Users who did "budget created" vs those who didn't
|
|
782
|
+
* - Compare: Average monthly_contribution on savings goal events
|
|
783
|
+
* - Compare: Average investment amount
|
|
784
|
+
* - Count: savings goal set events per user (budget users have more)
|
|
785
|
+
* - Filter: budget_discipline = true
|
|
786
|
+
*
|
|
787
|
+
* EXPECTED INSIGHT: Budget creators save 2x more and invest 1.5x more
|
|
788
|
+
* than non-budget users. They also set more savings goals, showing
|
|
789
|
+
* compound financial wellness behavior.
|
|
790
|
+
*
|
|
791
|
+
* REAL-WORLD ANALOGUE: Financial planning tools drive better outcomes.
|
|
792
|
+
* Users who engage with budgeting features are more financially active
|
|
793
|
+
* and retain longer - a key product-market fit signal.
|
|
794
|
+
*
|
|
795
|
+
* -------------------------------------------------------------------
|
|
796
|
+
* 6. AUTO-PAY LOYALTY (event)
|
|
797
|
+
* -------------------------------------------------------------------
|
|
798
|
+
*
|
|
799
|
+
* PATTERN: Bill paid events with auto_pay = false have a 30% chance
|
|
800
|
+
* of being dropped entirely (simulating missed payments). Auto-pay
|
|
801
|
+
* users never miss. Surviving manual payments are tagged with
|
|
802
|
+
* manual_payment: true.
|
|
803
|
+
*
|
|
804
|
+
* HOW TO FIND IT:
|
|
805
|
+
* - Segment: bill paid events by auto_pay = true vs false
|
|
806
|
+
* - Compare: Total bill paid count per user in each segment
|
|
807
|
+
* - Calculate: Effective bill completion rate by segment
|
|
808
|
+
* - Filter: manual_payment = true for surviving manual payments
|
|
809
|
+
*
|
|
810
|
+
* EXPECTED INSIGHT: Auto-pay users have 100% bill completion while
|
|
811
|
+
* manual payers show only ~70% completion. This creates a clear
|
|
812
|
+
* reliability gap that would drive auto-pay adoption campaigns.
|
|
813
|
+
*
|
|
814
|
+
* REAL-WORLD ANALOGUE: Auto-pay enrollment is one of the strongest
|
|
815
|
+
* retention predictors in fintech. Users who set up auto-pay are
|
|
816
|
+
* less likely to miss payments and less likely to churn.
|
|
817
|
+
*
|
|
818
|
+
* -------------------------------------------------------------------
|
|
819
|
+
* 7. PREMIUM TIER VALUE (event)
|
|
820
|
+
* -------------------------------------------------------------------
|
|
821
|
+
*
|
|
822
|
+
* PATTERN: Premium tier users get 3x reward values and 2x investment
|
|
823
|
+
* sell returns. Plus tier users get 1.5x rewards. Tagged with
|
|
824
|
+
* premium_reward: true and premium_returns: true respectively.
|
|
825
|
+
*
|
|
826
|
+
* HOW TO FIND IT:
|
|
827
|
+
* - Segment: Events by account_tier (basic, plus, premium)
|
|
828
|
+
* - Compare: Average reward value on reward redeemed events
|
|
829
|
+
* - Compare: Average amount on investment made (action = sell)
|
|
830
|
+
* - Filter: premium_reward = true, premium_returns = true
|
|
831
|
+
* - Analyze: Total reward value per user by tier
|
|
832
|
+
*
|
|
833
|
+
* EXPECTED INSIGHT: Clear tier-based value curve. Premium users
|
|
834
|
+
* earn 3x the rewards and 2x the investment returns of Basic users,
|
|
835
|
+
* with Plus users in between. This validates tier pricing.
|
|
836
|
+
*
|
|
837
|
+
* REAL-WORLD ANALOGUE: Premium banking tiers that provide tangible
|
|
838
|
+
* financial benefits. The reward multiplier justifies the monthly
|
|
839
|
+
* fee and drives upgrade conversions.
|
|
840
|
+
*
|
|
841
|
+
* -------------------------------------------------------------------
|
|
842
|
+
* 8. MONTH-END ANXIETY (event)
|
|
843
|
+
* -------------------------------------------------------------------
|
|
844
|
+
*
|
|
845
|
+
* PATTERN: On the last 3 days of each month (day >= 28), app sessions
|
|
846
|
+
* are 40% longer and balance checks show 30% lower balances. Tagged
|
|
847
|
+
* with month_end_anxiety: true and month_end_check: true.
|
|
848
|
+
*
|
|
849
|
+
* HOW TO FIND IT:
|
|
850
|
+
* - Chart: Average session_duration_sec by day of month
|
|
851
|
+
* - Chart: Average account_balance on balance checked by day of month
|
|
852
|
+
* - Filter: month_end_anxiety = true, month_end_check = true
|
|
853
|
+
* - Compare: Day 1-27 vs day 28-31 engagement metrics
|
|
854
|
+
*
|
|
855
|
+
* EXPECTED INSIGHT: Users spend 40% more time in the app at month-end,
|
|
856
|
+
* checking lower balances. This reflects pre-bill-pay anxiety and
|
|
857
|
+
* end-of-month financial stress.
|
|
858
|
+
*
|
|
859
|
+
* REAL-WORLD ANALOGUE: Month-end financial anxiety drives app
|
|
860
|
+
* engagement spikes. Banks can leverage this pattern for timely
|
|
861
|
+
* overdraft protection offers, savings nudges, and bill reminders.
|
|
862
|
+
*
|
|
863
|
+
* ===================================================================
|
|
864
|
+
* ADVANCED ANALYSIS IDEAS
|
|
865
|
+
* ===================================================================
|
|
866
|
+
*
|
|
867
|
+
* CROSS-HOOK PATTERNS:
|
|
868
|
+
*
|
|
869
|
+
* 1. Budget + Low Balance: Do budget creators (Hook #5) avoid the
|
|
870
|
+
* low-balance churn pattern (Hook #4)? Budget discipline should
|
|
871
|
+
* correlate with healthier balances.
|
|
872
|
+
*
|
|
873
|
+
* 2. Premium + Auto-Pay: Do premium tier users (Hook #7) have higher
|
|
874
|
+
* auto-pay adoption than basic users? Does tier upgrading predict
|
|
875
|
+
* auto-pay enrollment?
|
|
876
|
+
*
|
|
877
|
+
* 3. Fraud + Churn: Do fraud victims (Hook #3) churn more than
|
|
878
|
+
* non-victims? Does support resolution quality affect post-fraud
|
|
879
|
+
* retention?
|
|
880
|
+
*
|
|
881
|
+
* 4. Payday + Month-End: Compare payday spending spikes (Hook #2)
|
|
882
|
+
* with month-end anxiety (Hook #8). Do payday spenders run out
|
|
883
|
+
* of money by month-end?
|
|
884
|
+
*
|
|
885
|
+
* 5. Business vs Personal Fraud: Are business accounts (Hook #1)
|
|
886
|
+
* more or less likely to be fraud targets (Hook #3)?
|
|
887
|
+
*
|
|
888
|
+
* COHORT ANALYSIS:
|
|
889
|
+
*
|
|
890
|
+
* - Cohort by account_tier: Track upgrade paths and value realization
|
|
891
|
+
* across Basic -> Plus -> Premium
|
|
892
|
+
* - Cohort by signup_channel: Do referral users have better retention
|
|
893
|
+
* and higher balances?
|
|
894
|
+
* - Cohort by income_bracket: How does income correlate with feature
|
|
895
|
+
* adoption (budgets, investments, savings goals)?
|
|
896
|
+
* - Cohort by credit_score_range: Do higher credit scores predict
|
|
897
|
+
* loan approvals and premium tier adoption?
|
|
898
|
+
*
|
|
899
|
+
* FUNNEL ANALYSIS:
|
|
900
|
+
*
|
|
901
|
+
* - Onboarding Funnel: account opened -> app session -> balance checked
|
|
902
|
+
* by account type and signup channel
|
|
903
|
+
* - Transfer Flow: app session -> transfer sent -> notification opened
|
|
904
|
+
* by tier and platform
|
|
905
|
+
* - Investment Journey: balance checked -> investment made -> reward redeemed
|
|
906
|
+
* by income bracket and budget creation status
|
|
907
|
+
*
|
|
908
|
+
* ===================================================================
|
|
909
|
+
* EXPECTED METRICS SUMMARY
|
|
910
|
+
* ===================================================================
|
|
911
|
+
*
|
|
912
|
+
* Hook | Metric | Baseline | Hook Effect | Ratio
|
|
913
|
+
* ----------------------|-----------------------|----------|-------------|------
|
|
914
|
+
* Personal vs Business | Avg transaction amt | $50 | $200+ | ~4x
|
|
915
|
+
* Payday Patterns | Deposit amount | $50 | $100 | 2x
|
|
916
|
+
* Fraud Detection | Users affected | 0% | 3% | --
|
|
917
|
+
* Low Balance Churn | D30+ event count | 100% | 50% | 0.5x
|
|
918
|
+
* Budget Discipline | Monthly contribution | $200 | $400 | 2x
|
|
919
|
+
* Auto-Pay Loyalty | Bill completion rate | 100% | 70% | 0.7x
|
|
920
|
+
* Premium Tier Value | Reward value | $10 | $30 | 3x
|
|
921
|
+
* Month-End Anxiety | Session duration | 60s | 84s | 1.4x
|
|
922
|
+
*
|
|
923
|
+
* ===================================================================
|
|
924
|
+
* HOW TO RUN THIS DUNGEON
|
|
925
|
+
* ===================================================================
|
|
926
|
+
*
|
|
927
|
+
* From the dm4 root directory:
|
|
928
|
+
*
|
|
929
|
+
* npm start
|
|
930
|
+
*
|
|
931
|
+
* Or programmatically:
|
|
932
|
+
*
|
|
933
|
+
* import generate from './index.js';
|
|
934
|
+
* import config from './dungeons/harness-fintech.js';
|
|
935
|
+
* const results = await generate(config);
|
|
936
|
+
*
|
|
937
|
+
* OUTPUT FILES (with writeToDisk: true):
|
|
938
|
+
*
|
|
939
|
+
* - needle-haystack-fintech__events.json.gz - All event data
|
|
940
|
+
* - needle-haystack-fintech__user_profiles.json.gz - User profiles
|
|
941
|
+
* - needle-haystack-fintech__group_profiles.json.gz - Household profiles
|
|
942
|
+
* - needle-haystack-fintech__transaction_id_lookup.json.gz - Merchant catalog
|
|
943
|
+
*
|
|
944
|
+
* ===================================================================
|
|
945
|
+
* TESTING YOUR ANALYTICS PLATFORM
|
|
946
|
+
* ===================================================================
|
|
947
|
+
*
|
|
948
|
+
* This dungeon is perfect for testing:
|
|
949
|
+
*
|
|
950
|
+
* 1. Segmentation: Can you separate business vs personal accounts?
|
|
951
|
+
* 2. Time Patterns: Can you detect the biweekly payday cycle?
|
|
952
|
+
* 3. Anomaly Detection: Can you find the fraud burst patterns?
|
|
953
|
+
* 4. Churn Prediction: Can you predict churn from low balance signals?
|
|
954
|
+
* 5. Feature Impact: Can you measure budget tools' effect on savings?
|
|
955
|
+
* 6. Behavioral Analysis: Can you quantify auto-pay vs manual reliability?
|
|
956
|
+
* 7. Tier Analysis: Can you calculate reward ROI by account tier?
|
|
957
|
+
* 8. Temporal Patterns: Can you identify month-end anxiety in the data?
|
|
958
|
+
*
|
|
959
|
+
* ===================================================================
|
|
960
|
+
* WHY "NEEDLE IN A HAYSTACK"?
|
|
961
|
+
* ===================================================================
|
|
962
|
+
*
|
|
963
|
+
* Each hook is a "needle" - a meaningful, actionable insight hidden in a
|
|
964
|
+
* "haystack" of 360K events. The challenge is:
|
|
965
|
+
*
|
|
966
|
+
* 1. FINDING the needles (discovery)
|
|
967
|
+
* 2. VALIDATING they're real patterns (statistical significance)
|
|
968
|
+
* 3. UNDERSTANDING why they matter (business impact)
|
|
969
|
+
* 4. ACTING on them (product decisions)
|
|
970
|
+
*
|
|
971
|
+
* This mirrors real-world fintech analytics: your transaction data contains
|
|
972
|
+
* valuable insights about user behavior, but you need the right tools and
|
|
973
|
+
* skills to find them.
|
|
974
|
+
*
|
|
975
|
+
* ===================================================================
|
|
976
|
+
*/
|