"""Recalculate the accompanying examples with Python 3 (standard library only). Download this file, fm*-real-history.json and its CSV files into one folder. Run: python fm58-reproduce.py [folder] (or the fm59/fm510 equivalent). No connection to MetaTrader, account, broker or network is made; no files change. All bar labels retain the original MT5 time; they are not asserted to be UTC. """ from pathlib import Path from decimal import Decimal as D, localcontext from datetime import datetime, timedelta import csv, hashlib, json, sys def digest(path): return hashlib.sha256(path.read_bytes()).hexdigest() def verify(folder): folder=Path(folder) metas=sorted(folder.glob('fm*-real-history.json')) assert len(metas)==1, 'Expected exactly one article provenance JSON beside the CSV files' meta=json.loads(metas[0].read_text(encoding='utf-8')) result={'pass':False,'provenance_sha256':digest(metas[0]),'checks':[], 'time_basis':meta['bar_time_basis'],'source_archive_sha256':meta['source_csv_sha256']} def load(record): file=folder/record['file'] assert digest(file)==record['sha256'], 'Changed CSV: '+file.name data=list(csv.DictReader(file.read_text(encoding='utf-8-sig').splitlines())) assert len(data)==record['rows'] for row in data: row['source_index']=int(row['source_index']) for k in ('open','high','low','close'): row[k]=D(row[k]) assert row['low']<=min(row['open'],row['close'])<=max(row['open'],row['close'])<=row['high'] assert all(b['source_index']==a['source_index']+1 for a,b in zip(data,data[1:])) result['checks'].append({'csv':file.name,'sha256':digest(file),'rows':len(data),'ohlc_valid':True}) return data def equal(actual,expected): assert abs(D(actual)-expected)by_index[j][key] if key=='high' else v14: up+=(max(changes[i-1],D(0))-up)/14 down+=(max(-changes[i-1],D(0))-down)/14 rsi[i]=None if up+down==0 else D(100)-100*down/(up+down) def exponential(values,period,offset=0): out=[None]*n;seed=offset+period-1 out[seed]=sum(values[offset:seed+1])/period for i in range(seed+1,n):out[i]=out[i-1]+D(2)/(period+1)*(values[i]-out[i-1]) return out fast=exponential(c,12);slow=exponential(c,26) macd=[None if i<25 else fast[i]-slow[i] for i in range(n)] sig_ema=exponential(macd,9,25);sig_sma=[None]*n rolling=sum(macd[25:34]);sig_sma[33]=rolling/9 for i in range(34,n):rolling+=macd[i]-macd[i-9];sig_sma[i]=rolling/9 for key,expected in [('rsi14',rsi),('ema12',fast),('ema26',slow),('macd',macd),('signal_sma9',sig_sma),('signal_ema9',sig_ema)]: for row,value in zip(data,expected): if value is None:assert row[key]=='' else:equal(row[key],value) a,b=meta['rsi14']['six_above70_indices'];assert b-a+1==6 and all(rsi[i]>70 for i in range(a,b+1)) result['checks'].append({'indicator_readings_recomputed':sum(v is not None for values in [rsi,fast,slow,macd,sig_sma,sig_ema] for v in values),'rsi_full_prefix':n,'rsi_above70_consecutive':6,'last_rsi':str(rsi[-1]),'macd_SMA_and_EMA_signals_separate':True}) elif metas[0].name.startswith('fm59'): for direction,case in meta['cases'].items(): data=load(case['csv']);by={r['source_index']:r for r in data} pivot(case['a'],by);pivot(case['b'],by) A=D(case['a']['price']);B=D(case['b']['price']);lo=min(A,B);hi=max(A,B);span=hi-lo assert span/D('.0001')==D(case['range_pips']) for r,value in case['levels'].items(): assert D(value)==(hi-D(r)*span if direction=='up' else lo+D(r)*span) assert case['grid_not_before']==case['b']['nominal_confirmation_close'] result['checks'].append({'case':direction,'strict_2_plus_2_anchors':True,'levels_exact':True,'range_pips':case['range_pips'],'grid_not_before':case['grid_not_before']}) else: for kind,case in meta['cases'].items(): data=load(case['csv']);by={r['source_index']:r for r in data} for point in case['pivots']:pivot(point,by) p=[D(q['price']) for q in case['pivots']] legs=[p[1]-p[0],p[3]-p[2],p[5]-p[4]] assert [v/D('.0001') for v in legs]==[D(v) for v in case['motive_lengths_pips']] actual={'wave2_above_start':p[2]>p[0],'wave3_exceeds_wave1':p[3]>p[1], 'wave3_not_shortest':legs[1]>=min(legs[0],legs[2]),'wave4_no_overlap':p[4]>p[1]} assert actual==case['price_constraints'] assert not case['full_subdivision_certified'] and not case['larger_degree_count_certified'] assert (p[1]-p[4])/D('.0001')==D(case['overlap_pips']) assert case['snapshot_after']==case['pivots'][-1]['nominal_confirmation_close'] result['checks'].append({'case':kind,'strict_2_plus_2_pivots':True,'necessary_price_rules':actual,'overlap_pips':case['overlap_pips'],'full_count_not_certified':True}) result['pass']=True return result if __name__=='__main__': folder=Path(sys.argv[1]).resolve() if len(sys.argv)>1 else Path(__file__).resolve().parent print(json.dumps(verify(folder),indent=2,ensure_ascii=False))