"""R623:原单体与模块基线等价;每次只关一个过滤器,机会逐项匹配。""" from math import prod,isclose ROWS=[{'id':1,'volume':True,'trend':True,'return':.02}, {'id':2,'volume':False,'trend':True,'return':-.03}, {'id':3,'volume':True,'trend':False,'return':.01}, {'id':4,'volume':True,'trend':True,'return':.04}, {'id':5,'volume':False,'trend':False,'return':-.01}] BASE={'volume':True,'trend':True} def run(switches): if set(switches)!=set(BASE):raise ValueError('unknown module') trades=[] for row in ROWS: if all(not switches[name] or row[name] for name in BASE): trades.append({'id':row['id'],'entry':row['id'],'exit':row['id']+1,'return':row['return']}) return trades def changes(base,variant): left={r['id']:r for r in base};right={r['id']:r for r in variant} return {'added':sorted(right.keys()-left.keys()),'removed':sorted(left.keys()-right.keys()), 'retimed':sorted(k for k in left.keys()&right.keys() if (left[k]['entry'],left[k]['exit'])!=(right[k]['entry'],right[k]['exit']))} def ablate(switches): if sum(switches[k]!=BASE[k] for k in BASE)!=1:raise ValueError('not one module') return run(switches) base=run(BASE) assert [t['id'] for t in base]==[r['id'] for r in ROWS if r['volume'] and r['trend']] without_volume=ablate({'volume':False,'trend':True}) without_trend=ablate({'volume':True,'trend':False}) assert changes(base,without_volume)=={'added':[2],'removed':[],'retimed':[]} assert changes(base,without_trend)=={'added':[3],'removed':[],'retimed':[]} results=[prod(1+r['return'] for r in rows)-1 for rows in [base,without_volume,without_trend]] assert all(isclose(a,b,abs_tol=1e-12) for a,b in zip(results,[.0608,.028976,.071408])) retimed=[dict(t) for t in base];retimed[0]['exit']+=1 assert changes(base,retimed)['retimed']==[1] try:ablate({'volume':False,'trend':False}) except ValueError:pass else:raise AssertionError('two modules changed') print('baseline_equivalent=True returns='+str([format(x,'.6%') for x in results])) print('without_volume='+str(changes(base,without_volume))+' without_trend='+str(changes(base,without_trend))) print('retiming_detector=matched_opportunity_1 two_module_intervention=rejected')