"""Reproduce quotation-direction sensitivity with the pinned ECB snapshot. Python 3.11+; calculations use only the standard library. No network requests. """ from pathlib import Path import csv, io, json, hashlib, math, statistics BASE = Path(__file__).resolve().parent RAW = BASE / 'ecb-eurusd-original-2016-2025.csv' EXPECTED = '12bda408fce288c0d9f68de03beffc6d89592a8fde37aff2d336b122baaddab3' def drawdown(rows, prices): peak = prices[0]; peak_i = 0; worst = 0.; pair = (0, 0) path = [] for i, price in enumerate(prices): if price > peak: peak, peak_i = price, i fall = 100 * (1 - price / peak) path.append(-fall) if fall > worst: worst, pair = fall, (peak_i, i) return {'decline_pct': worst, 'peak_date': rows[pair[0]]['TIME_PERIOD'], 'trough_date': rows[pair[1]]['TIME_PERIOD']}, path def analyze(): raw = RAW.read_bytes() assert hashlib.sha256(raw).hexdigest() == EXPECTED rows = list(csv.DictReader(io.StringIO(raw.decode('utf-8-sig')))) assert len(rows) == 2560 and len({r['TIME_PERIOD'] for r in rows}) == len(rows) assert rows == sorted(rows, key=lambda r:r['TIME_PERIOD']) assert all(r['OBS_STATUS'] == 'A' and math.isfinite(float(r['OBS_VALUE'])) and float(r['OBS_VALUE']) > 0 for r in rows) periods = [] for start, end in [(2016, 2020), (2021, 2025), (2016, 2025)]: subset = [r for r in rows if start <= int(r['TIME_PERIOD'][:4]) <= end] original = [float(r['OBS_VALUE']) for r in subset] inverse = [1/x for x in original] rec = {'period': f'{start}-{end}', 'observations':len(subset), 'changes':len(subset)-1, 'first_date':subset[0]['TIME_PERIOD'], 'last_date':subset[-1]['TIME_PERIOD']} for name, prices in [('usd_per_eur', original), ('eur_per_usd', inverse)]: simple = [100*(b/a-1) for a,b in zip(prices,prices[1:])] logs = [100*math.log(b/a) for a,b in zip(prices,prices[1:])] dd,_ = drawdown(subset,prices) rec[name] = {'endpoint_change_pct':100*(prices[-1]/prices[0]-1), 'simple_population_stdev_pct':statistics.pstdev(simple), 'log_population_stdev_pct':statistics.pstdev(logs),'max_reference_drawdown':dd} periods.append(rec) _, dd_a = drawdown(rows,[float(r['OBS_VALUE']) for r in rows]) _, dd_b = drawdown(rows,[1/float(r['OBS_VALUE']) for r in rows]) with (BASE/'quotation-path.csv').open('w',encoding='utf8',newline='') as f: w = csv.writer(f);w.writerow(['date','usd_per_eur','eur_per_usd','usd_per_eur_drawdown_pct','eur_per_usd_drawdown_pct']) for i,r in enumerate(rows): a = float(r['OBS_VALUE']);w.writerow([r['TIME_PERIOD'],format(a,'.12g'),format(1/a,'.17g'),format(dd_a[i],'.17g'),format(dd_b[i],'.17g')]) result={'series':'EXR.D.USD.EUR.SP00.A','raw_sha256':EXPECTED,'rows':len(rows),'periods':periods, 'source_retrieved_at':'2026-10-07T14:18:17.889247+00:00', 'source_url':'https://data-api.ecb.europa.eu/service/data/EXR/D.USD.EUR.SP00.A?startPeriod=2016-01-01&endPeriod=2025-12-31&format=csvdata', 'formula_simple_inverse':'s = -r / (1 + r), r and s as fractions, not percentage values', 'formula_log_inverse':'ln((1/P_t)/(1/P_previous)) = -ln(P_t/P_previous)', 'units':'USD per EUR and reciprocal EUR per USD; changes and drawdowns expressed in percent', 'policy':'Consecutive published observations only; no filling missing dates. Each subperiod resets predecessor and running peak. Full period includes the 2020/2021 bridge.', 'limits':'Reference-rate changes and declines from earlier peaks, not account equity, executable bid/ask quotes, strategy returns or leveraged losses. No spread, slippage, commission, financing or intraday path.'} (BASE/'quotation-metrics.json').write_text(json.dumps(result,indent=2)+'\n',encoding='utf8') print(json.dumps({'rows':len(rows),'periods':periods},indent=2)) if __name__=='__main__': analyze()