"""Independent audit of raw responses and Decimal calculations; offline, fail closed."""
import csv
import datetime as dt
from decimal import Decimal, localcontext, ROUND_HALF_UP
import gzip
import hashlib
import html
import io
import json
from pathlib import Path
import re
D=Decimal
ROOT=Path(__file__).resolve().parents[1]
DATA=ROOT/'articles/data/sp500-all-country'
def run_audit():
manifest=json.loads((DATA/'sources.json').read_text());payload=(DATA/'observations.csv').read_bytes()
assert hashlib.sha256(payload).hexdigest()==manifest['input_sha256'],'CSV hash changed'
rows=list(csv.DictReader(io.StringIO(payload.decode())));series={};dividends={}
for src in manifest['sources']:
raw=gzip.decompress((DATA/'raw'/(src['name']+'.gz')).read_bytes())
assert hashlib.sha256(raw).hexdigest()==src['sha256'],'Raw source hash changed'
if src['name']=='FED_JPY':
values={}
for tr in re.findall(r'
]*>(.*?)
',raw.decode(),re.S|re.I):
cells=[html.unescape(re.sub('<[^>]+>','',s)).strip() for s in re.findall(r']*>(.*?)',tr,re.S|re.I)]
if len(cells)!=2:continue
try:date=dt.datetime.strptime(cells[0],'%d-%b-%y').date().isoformat()
except ValueError:continue
if manifest['start']<=date<=manifest['end'] and cells[1]!='ND':
assert date not in values;values[date]=float(cells[1])
series['usd_jpy']=values
else:
response=json.loads(raw)['chart'];assert response['error'] is None and len(response['result'])==1
r=response['result'][0];assert r['meta']['currency']=='USD';assert not r.get('events',{}).get('splits')
dates=[dt.datetime.fromtimestamp(t,dt.timezone.utc).date().isoformat() for t in r['timestamp']]
assert dates==sorted(set(dates))
close=r['indicators']['quote'][0]['close'];adj=r['indicators']['adjclose'][0]['adjclose']
assert len(dates)==len(close)==len(adj)
for key,vals in [('close',close),('adjusted',adj)]:series[src['name'].lower()+'_'+key+'_usd']={d:v for d,v in zip(dates,vals) if manifest['start']<=d<=manifest['end'] and v is not None}
events={dt.datetime.fromtimestamp(e['date'],dt.timezone.utc).date().isoformat():D(str(e['amount'])) for e in r['events']['dividends'].values()}
with localcontext() as ctx:
ctx.prec=50;factor=D(1);errors=[]
for i in range(len(dates)-1,0,-1):
if dates[i] in events:factor*=1-events[dates[i]]/D(str(close[i-1]))
observed=D(str(adj[i-1]))/D(str(close[i-1]))/(D(str(adj[-1]))/D(str(close[-1])))
errors.append(abs(factor/observed-1))
assert max(errors)=w['start']]
for key in ['spy','acwi']:
for ccy in ['usd','jpy']:
values=[D(r[key+'_adjusted_usd'])*(D(r['usd_jpy']) if ccy=='jpy' else 1) for r in selected]
span=D((dt.date.fromisoformat(selected[-1]['date'])-dt.date.fromisoformat(selected[0]['date'])).days)
cagr=((values[-1]/values[0]).ln()*D('365.2425')/span).exp()-1
high=values[0];worst=D(0)
for v in values:high=max(high,v);worst=min(worst,v/high-1)
a=w['assets'][key][ccy]
assert abs(cagr-D(str(a['cagr'])))