Solve payment routing and cost optimization problems in the dabstep dataset. Use this skill for questions about which card scheme to steer merchant traffic to (for minimum or maximum fees), or which Authorization Characteristics Indicator (ACI) to incentivize for fraudulent transactions to minimize fees. Always invoke this skill when the question asks about steering traffic, optimal card scheme selection, or ACI optimization with fee comparison.
Solve payment routing and cost optimization problems in the dabstep dataset. Use this skill for questions about which card scheme to steer merchant traffic to (for minimum or maximum fees), or which Authorization Characteristics Indicator (ACI) to incentivize for fraudulent transactions to minimize fees. Always invoke this skill when the question asks about steering traffic, optimal card scheme selection, or ACI optimization with fee comparison.
Routing and Cost Optimization
Problem Types
Type 1: Card Scheme Routing
"Which card scheme should merchant X steer traffic to in order to pay min/max fees in [month/year]?"
Compute total fees per card scheme for ALL merchant transactions in the period, then pick the scheme with min/max total.
Type 2: ACI Optimization for Fraudulent Transactions
"If fraudulent transactions were moved to a different ACI, what would be the preferred choice for lowest fees?"
Compute total fees per candidate ACI for FRAUDULENT transactions only, then pick the ACI with lowest total that covers ALL fraudulent transactions.
Answer Format
{card_scheme_or_ACI}:{total_fee_rounded_to_2_decimals}
Examples: GlobalCard:2800.62 or C:17.82
"""Map numeric or named capture_delay to fee rule category."""
if
in
'immediate'
'manual'
return
int
if
3
return
'<3'
elif
5
return
'3-5'
else
return
'>5'
'capture_delay'
# Filter transactions for the period
32
59
# e.g., February
'merchant'
'day_of_year'
'day_of_year'
'intracountry'
'issuing_country'
'acquirer_country'
# Monthly metrics (from ALL merchant transactions in the period)
'eur_amount'
sum
'has_fraudulent_dispute'
True
'eur_amount'
sum
100
if
0
else
0
def
get_vol_cat
vol
if
100000
return
'<100k'
elif
1000000
return
'100k-1m'
elif
5000000
return
'1m-5m'
else
return
'>5m'
def
get_fraud_cat
pct
if
7.2
return
'<7.2%'
elif
7.7
return
'7.2%-7.7%'
elif
8.3
return
'7.7%-8.3%'
else
return
'>8.3%'
Fee Rule Matching
CRITICAL: Empty list [] means "match all" (same as null). Never treat [] as "match nothing".
defrule_matches(rule, card_scheme, aci, is_credit, intracountry,
acct=account_type, cap=capture_delay_norm, m=mcc,
vc=vol_cat, fc=fraud_cat):
if rule['card_scheme'] != card_scheme: returnFalseif rule['account_type'] and acct notin rule['account_type']: returnFalseif rule['capture_delay'] isnotNoneand rule['capture_delay'] != cap: returnFalseif rule['merchant_category_code'] and m notin rule['merchant_category_code']: returnFalseif rule['is_credit'] isnotNoneand rule['is_credit'] != is_credit: returnFalseif rule['aci'] and aci notin rule['aci']: returnFalseif rule['intracountry'] isnotNoneandbool(rule['intracountry']) != intracountry: returnFalseif rule['monthly_fraud_level'] isnotNoneand rule['monthly_fraud_level'] != fc: returnFalseif rule['monthly_volume'] isnotNoneand rule['monthly_volume'] != vc: returnFalsereturnTruedefget_min_fee_for_combo(card_scheme, aci, is_credit, intracountry, amount):
"""Minimum fee rule fee for a given transaction context. Returns None if no rule matches."""
applicable = [r for r in fees if rule_matches(r, card_scheme, aci, is_credit, intracountry)]
ifnot applicable: returnNonereturnmin(r['fixed_amount'] + r['rate'] * amount / 10000for r in applicable)
Type 1: Card Scheme Routing
all_schemes = ['GlobalCard', 'NexPay', 'SwiftCharge', 'TransactPlus']
n = len(merch_txns)
scheme_totals = {}
for scheme in all_schemes:
total, matched = 0, 0for _, txn in merch_txns.iterrows():
fee = get_min_fee_for_combo(scheme, txn['aci'], txn['is_credit'],
txn['intracountry'], txn['eur_amount'])
if fee isnotNone:
total += fee; matched += 1
scheme_totals[scheme] = {'total': total, 'matched': matched}
print(f"{scheme}: {round(total,2)} ({matched}/{n} matched)")
# Prefer schemes with full coverage; for min/max pick accordingly
full = {k: v for k, v in scheme_totals.items() if v['matched'] == n}
candidates = full if full else scheme_totals
# For min fees: use min(); for max fees: use max()
best = min(candidates, key=lambda k: candidates[k]['total']) # change to max() for 'maximum fees'print(f"Answer: {best}:{round(scheme_totals[best]['total'], 2)}")
Type 2: ACI Optimization
fraud_txns = merch_txns[merch_txns['has_fraudulent_dispute'] == True].copy()
print(f"Fraudulent transactions: {len(fraud_txns)}, current ACIs: {fraud_txns['aci'].unique()}")
current_acis = set(fraud_txns['aci'].unique())
candidate_acis = [a for a in ['A','B','C','D','E','F','G'] if a notin current_acis]
n_fraud = len(fraud_txns)
aci_totals = {}
for target_aci in candidate_acis:
total, matched = 0, 0for _, txn in fraud_txns.iterrows():
fee = get_min_fee_for_combo(txn['card_scheme'], target_aci, txn['is_credit'],
txn['intracountry'], txn['eur_amount'])
if fee isnotNone:
total += fee; matched += 1
aci_totals[target_aci] = {'total': total, 'matched': matched}
print(f"ACI {target_aci}: total={round(total,4)}, matched={matched}/{n_fraud}")
full = {k: v for k, v in aci_totals.items() if v['matched'] == n_fraud}
candidates = full if full else aci_totals
best_aci = min(candidates, key=lambda k: candidates[k]['total'])
print(f"Answer: {best_aci}:{round(aci_totals[best_aci]['total'], 2)}")
Yearly Questions (2023)
For full-year questions, compute monthly metrics per natural month and aggregate:
MONTHS = [(1,31),(32,59),(60,90),(91,120),(121,151),(152,181),
(182,212),(213,243),(244,273),(274,304),(305,334),(335,365)]
all_schemes = ['GlobalCard', 'NexPay', 'SwiftCharge', 'TransactPlus']
scheme_totals_yearly = {s: 0for s in all_schemes}
for d_start, d_end in MONTHS:
m_txns = payments[
(payments['merchant'] == merchant_name) &
(payments['day_of_year'] >= d_start) &
(payments['day_of_year'] <= d_end)
].copy()
iflen(m_txns) == 0:
continue
m_txns['intracountry'] = m_txns['issuing_country'] == m_txns['acquirer_country']
mv = m_txns['eur_amount'].sum()
fv = m_txns[m_txns['has_fraudulent_dispute']==True]['eur_amount'].sum()
vc = get_vol_cat(mv)
fc = get_fraud_cat((fv/mv)*100if mv > 0else0)
# Override vol_cat/fraud_cat for this month's matchingfor scheme in all_schemes:
for _, txn in m_txns.iterrows():
applicable = [r for r in fees if rule_matches(r, scheme, txn['aci'],
txn['is_credit'], txn['intracountry'], vc=vc, fc=fc)]
if applicable:
scheme_totals_yearly[scheme] += min(
r['fixed_amount'] + r['rate'] * txn['eur_amount'] / 10000for r in applicable)
best = min(scheme_totals_yearly, key=lambda k: scheme_totals_yearly[k])
print(f"Answer: {best}:{round(scheme_totals_yearly[best], 2)}")
Key Pitfalls
[] = match all: Empty list and null both mean "applies to all". This is the most common source of bugs — if you treat [] as "no match", you'll find zero matching rules for many transactions.
capture_delay normalization: merchant_data stores numeric strings like "7" or "2". Map to fee-rule categories: int < 3 → "<3", int 3-5 → "3-5", int > 5 → ">5". Named values ("immediate", "manual") stay unchanged.
MCC absent from fee rules: Some MCCs (like 7997) don't appear in any rule's specific MCC list. Only rules with empty [] MCC list apply — these are the "catch-all" rules.
intracountry in payments.csv: acquirer_country is already a 2-letter country code. Compute intracountry = issuing_country == acquirer_country directly.
Multiple matching rules → minimum fee: When multiple rules match a transaction, the merchant is charged the minimum applicable fee.
Scope of monthly metrics: Use ALL merchant transactions in the period (not just fraudulent) to compute monthly_vol and fraud_pct.