Download model.py from amanlig/cibc-fx: direct link, hf CLI and curl.
- Browser
- Download file 3.07 kB
-
https://huggingface.co/amanlig/cibc-fx/resolve/main/model.py
- Command line
-
hf download hf://amanlig/cibc-fx/model.py
-
curl -L -o model.py https://huggingface.co/amanlig/cibc-fx/resolve/main/model.py
3.07 kB
| """Small-data baseline: median proportional FX adjustment by direction.""" | |
| import math | |
| from statistics import median | |
| DIRECTIONS = ('buy_usd', 'sell_usd') | |
| def positive(value): | |
| return isinstance(value, (int, float)) and not isinstance(value, bool) and math.isfinite(value) and value > 0 | |
| def fit(rows): | |
| parameters = {} | |
| for direction in DIRECTIONS: | |
| subset = [r for r in rows if r['direction'] == direction] | |
| if len(subset) < 3: | |
| raise ValueError(f'Need at least three training samples for {direction}.') | |
| parameters[direction] = { | |
| 'adjustment': median(r['applied_rate'] / r['yahoo_close'] - 1 for r in subset), | |
| 'samples': len(subset), | |
| 'usd_min': min(r['usd_amount'] for r in subset), | |
| 'usd_max': max(r['usd_amount'] for r in subset), | |
| } | |
| return {'version': 1, 'method': 'median_proportional_adjustment_by_direction', 'parameters': parameters} | |
| def predict(model, direction, reference_rate, usd_amount): | |
| if direction not in DIRECTIONS: | |
| raise ValueError('Direction must be buy_usd or sell_usd.') | |
| if not positive(reference_rate) or not positive(usd_amount): | |
| raise ValueError('Reference rate and USD amount must be finite and positive.') | |
| parameter = model['parameters'][direction] | |
| rate = reference_rate * (1 + parameter['adjustment']) | |
| return {'direction': direction, 'reference_rate': reference_rate, | |
| 'estimated_rate': rate, 'usd_amount': usd_amount, | |
| 'estimated_cad': round(usd_amount * rate, 2), | |
| 'outside_training_amount_range': not parameter['usd_min'] <= usd_amount <= parameter['usd_max'], | |
| 'reference_limitation': 'Trained using settlement-day Yahoo daily closes; accuracy with live or trade-day rates is unvalidated.'} | |
| def split_dates(rows): | |
| dates = sorted({r['settlement_date'] for r in rows}) | |
| if len(dates) < 5: | |
| raise ValueError('Need at least five distinct settlement dates.') | |
| cutoff = dates[min(len(dates)-1, max(1, int(len(dates)*.8)))] | |
| return ([r for r in rows if r['settlement_date'] < cutoff], | |
| [r for r in rows if r['settlement_date'] >= cutoff]) | |
| def metrics(model, rows): | |
| if not rows: | |
| return {'samples': 0} | |
| predictions = [predict(model, r['direction'], r['yahoo_close'], r['usd_amount']) for r in rows] | |
| return {'samples': len(rows), | |
| 'mean_absolute_rate_error': sum(abs(p['estimated_rate']-r['applied_rate']) for p,r in zip(predictions,rows))/len(rows), | |
| 'mean_absolute_cad_error': sum(abs(p['estimated_cad']-r['cad_amount']) for p,r in zip(predictions,rows))/len(rows), | |
| 'mean_absolute_percentage_cad_error': 100*sum(abs(p['estimated_cad']-r['cad_amount'])/r['cad_amount'] for p,r in zip(predictions,rows))/len(rows), | |
| 'max_absolute_cad_error': max(abs(p['estimated_cad']-r['cad_amount']) for p,r in zip(predictions,rows)), | |
| 'reference_only_mean_absolute_cad_error': sum(abs(r['usd_amount']*r['yahoo_close']-r['cad_amount']) for r in rows)/len(rows)} | |