Average Fee Estimation
Core Formula
fee = fixed_amount + rate * transaction_value / 10000
All values in EUR. The average fee is the arithmetic mean of fees computed across all matching rules in fees.json.
Data Files
fees.json: 1000 fee rules (card_scheme, account_type, merchant_category_code, aci, is_credit, capture_delay, monthly_fraud_level, monthly_volume, intracountry, fixed_amount, rate)
merchant_category_codes.csv: columns mcc (int) and description (string) — use this to look up MCC code from a description
Null / Empty = "Applies to All"
For every field in fees.json:
- Scalar fields (is_credit, capture_delay, intracountry, etc.):
null → rule applies to all values
- List fields (account_type, merchant_category_code, aci): empty list
[] → rule applies to all values
When the question specifies a filter value, include a rule if the rule's field is null/empty OR the field explicitly contains that value.
Question Type Patterns
Type 1: Credit/debit filter only
"For credit transactions, what is the average fee that card scheme X would charge for Y EUR?"
import json
with open('fees.json') as f:
fees = json.load(f)
matching = [r for r in fees
if r['card_scheme'] == 'X'
and (r['is_credit'] is None or r['is_credit'] == True)] # True for credit
fee_values = [r['fixed_amount'] + r['rate'] * Y / 10000 for r in matching]
answer = round(sum(fee_values) / len(fee_values), 6)
Type 2: Account type filter
"For account type T, what is the average fee that card scheme X would charge for Y EUR?"
matching = [r for r in fees
if r['card_scheme'] == 'X'
and (r['account_type'] == [] or 'T' in r['account_type'])]
fee_values = [r['fixed_amount'] + r['rate'] * Y / 10000 for r in matching]
answer = round(sum(fee_values) / len(fee_values), 6)
Type 3: Account type + MCC description filter
"For account type T and the MCC description: [description], what is the average fee that card scheme X would charge for Y EUR?"
First look up the MCC code:
import pandas as pd
mcc_df = pd.read_csv('merchant_category_codes.csv')
# Match by substring or full description
mcc_code = mcc_df[mcc_df['description'].str.contains('keyword', case=False)]['mcc'].values[0]
Then filter:
matching = [r for r in fees
if r['card_scheme'] == 'X'
and (r['account_type'] == [] or 'T' in r['account_type'])
and (r['merchant_category_code'] == [] or mcc_code in r['merchant_category_code'])]
fee_values = [r['fixed_amount'] + r['rate'] * Y / 10000 for r in matching]
answer = round(sum(fee_values) / len(fee_values), 6)
Type 4: Cheapest / most expensive card scheme
"In the average scenario, which card scheme would provide the cheapest/most expensive fee for Y EUR?"
Average ALL rules per card scheme (no filtering):
from collections import defaultdict
scheme_fees = defaultdict(list)
for r in fees:
fee = r['fixed_amount'] + r['rate'] * Y / 10000
scheme_fees[r['card_scheme']].append(fee)
scheme_avg = {s: sum(fl)/len(fl) for s, fl in scheme_fees.items()}
cheapest = min(scheme_avg, key=scheme_avg.get)
most_expensive = max(scheme_avg, key=scheme_avg.get)
Implementation Notes
Empty list [] vs null: In fees.json, list fields use [] (not null) to mean "all". Check with r['account_type'] == [], not r['account_type'] is None.
MCC lookup: Use str.contains() with a distinctive keyword from the description. The mcc column is an integer.
No other data files needed: For average fee questions, only fees.json and (for MCC) merchant_category_codes.csv are required. Do not filter by payments.csv data.
Output format: Round to 6 decimal places. If the question asks for a card scheme name (cheapest/most expensive), return the scheme name string.
"Not Applicable" rule: Only use if the question is genuinely unanswerable (e.g., no rules match, undefined scenario). For "average scenario" questions, average ALL rules across the card scheme without additional filtering.
Verified Examples
| Task |
Query |
Approach |
Answer |
| 1277 |
GlobalCard, credit, 50 EUR |
is_credit=True or null |
0.315937 |
| 1279 |
SwiftCharge, credit, 50 EUR |
is_credit=True or null |
0.338686 |
| 1281 |
GlobalCard, credit, 100 EUR |
is_credit=True or null |
0.560694 |
| 1538 |
NexPay, acct_type=R, 1000 EUR |
acct=R or [] |
5.625868 |
| 1570 |
NexPay, acct_type=D, 1000 EUR |
acct=D or [] |
5.504371 |
| 1574 |
NexPay, acct_type=D, 5000 EUR |
acct=D or [] |
27.250479 |
| 1577 |
GlobalCard, acct_type=D, 1234 EUR |
acct=D or [] |
6.580365 |
| 1341 |
GlobalCard, acct=H, MCC=5813, 50 EUR |
acct=H or [], MCC=5813 or [] |
0.369324 |
| 1347 |
SwiftCharge, acct=H, MCC=5813, 100 EUR |
acct=H or [], MCC=5813 or [] |
0.626512 |
| 1508 |
Most expensive for 50 EUR |
all rules per scheme |
NexPay |
| 1510 |
Most expensive for 100 EUR |
all rules per scheme |
NexPay |
| 1515 |
Cheapest for 5000 EUR |
all rules per scheme |
GlobalCard |
1---2name: average-fee-estimation-53description: Solve questions about estimating the average fee a card scheme would charge for a transaction. Use this skill for questions asking about average fees per card scheme (e.g., "what would be the average fee that GlobalCard would charge for a transaction value of 50 EUR?"), comparing which card scheme is cheapest or most expensive, or filtering by credit/debit status, account type, or merchant category code description.4---56# Average Fee Estimation78## Core Formula910```11fee = fixed_amount + rate * transaction_value / 1000012```1314All values in EUR. The **average fee** is the arithmetic mean of fees computed across all matching rules in `fees.json`.1516## Data Files1718- `fees.json`: 1000 fee rules (card_scheme, account_type, merchant_category_code, aci, is_credit, capture_delay, monthly_fraud_level, monthly_volume, intracountry, fixed_amount, rate)19- `merchant_category_codes.csv`: columns `mcc` (int) and `description` (string) — use this to look up MCC code from a description2021## Null / Empty = "Applies to All"2223For every field in `fees.json`:24- **Scalar fields** (is_credit, capture_delay, intracountry, etc.): `null` → rule applies to all values25- **List fields** (account_type, merchant_category_code, aci): empty list `[]` → rule applies to all values2627When the question specifies a filter value, **include a rule if** the rule's field is null/empty OR the field explicitly contains that value.2829## Question Type Patterns3031### Type 1: Credit/debit filter only32> "For credit transactions, what is the average fee that card scheme X would charge for Y EUR?"3334```python35import json36with open('fees.json') as f:37 fees = json.load(f)3839matching = [r for r in fees40 if r['card_scheme'] == 'X'41 and (r['is_credit'] is None or r['is_credit'] == True)] # True for credit4243fee_values = [r['fixed_amount'] + r['rate'] * Y / 10000 for r in matching]44answer = round(sum(fee_values) / len(fee_values), 6)45```4647### Type 2: Account type filter48> "For account type T, what is the average fee that card scheme X would charge for Y EUR?"4950```python51matching = [r for r in fees52 if r['card_scheme'] == 'X'53 and (r['account_type'] == [] or 'T' in r['account_type'])]5455fee_values = [r['fixed_amount'] + r['rate'] * Y / 10000 for r in matching]56answer = round(sum(fee_values) / len(fee_values), 6)57```5859### Type 3: Account type + MCC description filter60> "For account type T and the MCC description: [description], what is the average fee that card scheme X would charge for Y EUR?"6162First look up the MCC code:63```python64import pandas as pd65mcc_df = pd.read_csv('merchant_category_codes.csv')66# Match by substring or full description67mcc_code = mcc_df[mcc_df['description'].str.contains('keyword', case=False)]['mcc'].values[0]68```6970Then filter:71```python72matching = [r for r in fees73 if r['card_scheme'] == 'X'74 and (r['account_type'] == [] or 'T' in r['account_type'])75 and (r['merchant_category_code'] == [] or mcc_code in r['merchant_category_code'])]7677fee_values = [r['fixed_amount'] + r['rate'] * Y / 10000 for r in matching]78answer = round(sum(fee_values) / len(fee_values), 6)79```8081### Type 4: Cheapest / most expensive card scheme82> "In the average scenario, which card scheme would provide the cheapest/most expensive fee for Y EUR?"8384Average ALL rules per card scheme (no filtering):85```python86from collections import defaultdict87scheme_fees = defaultdict(list)88for r in fees:89 fee = r['fixed_amount'] + r['rate'] * Y / 1000090 scheme_fees[r['card_scheme']].append(fee)9192scheme_avg = {s: sum(fl)/len(fl) for s, fl in scheme_fees.items()}93cheapest = min(scheme_avg, key=scheme_avg.get)94most_expensive = max(scheme_avg, key=scheme_avg.get)95```9697## Implementation Notes98991. **Empty list `[]` vs null**: In `fees.json`, list fields use `[]` (not null) to mean "all". Check with `r['account_type'] == []`, not `r['account_type'] is None`.1001012. **MCC lookup**: Use `str.contains()` with a distinctive keyword from the description. The `mcc` column is an integer.1021033. **No other data files needed**: For average fee questions, only `fees.json` and (for MCC) `merchant_category_codes.csv` are required. Do not filter by payments.csv data.1041054. **Output format**: Round to 6 decimal places. If the question asks for a card scheme name (cheapest/most expensive), return the scheme name string.1061075. **"Not Applicable" rule**: Only use if the question is genuinely unanswerable (e.g., no rules match, undefined scenario). For "average scenario" questions, average ALL rules across the card scheme without additional filtering.108109## Verified Examples110111| Task | Query | Approach | Answer |112|------|-------|----------|--------|113| 1277 | GlobalCard, credit, 50 EUR | is_credit=True or null | 0.315937 |114| 1279 | SwiftCharge, credit, 50 EUR | is_credit=True or null | 0.338686 |115| 1281 | GlobalCard, credit, 100 EUR | is_credit=True or null | 0.560694 |116| 1538 | NexPay, acct_type=R, 1000 EUR | acct=R or [] | 5.625868 |117| 1570 | NexPay, acct_type=D, 1000 EUR | acct=D or [] | 5.504371 |118| 1574 | NexPay, acct_type=D, 5000 EUR | acct=D or [] | 27.250479 |119| 1577 | GlobalCard, acct_type=D, 1234 EUR | acct=D or [] | 6.580365 |120| 1341 | GlobalCard, acct=H, MCC=5813, 50 EUR | acct=H or [], MCC=5813 or [] | 0.369324 |121| 1347 | SwiftCharge, acct=H, MCC=5813, 100 EUR | acct=H or [], MCC=5813 or [] | 0.626512 |122| 1508 | Most expensive for 50 EUR | all rules per scheme | NexPay |123| 1510 | Most expensive for 100 EUR | all rules per scheme | NexPay |124| 1515 | Cheapest for 5000 EUR | all rules per scheme | GlobalCard |