"""R632:证券主动权重贡献、费用与精确时间链接;不吞掉复合交互。""" from decimal import Decimal as D from math import prod def weights(values): out={k:D(str(v)) for k,v in values.items()} if any(not v.is_finite() or v<0 for v in out.values()) or abs(sum(out.values())-1)>D('1e-20'): raise ValueError('invalid weights') return out P=[weights({'A':.6,'B':.4}),weights({'A':.3,'B':.7})] B=[weights({'A':.5,'B':.5})]*2 RET=[{'A':D('.10'),'B':D('0')},{'A':D('-.05'),'B':D('.04')}] COST=[D('.001'),D('.002')] portfolio=[];benchmark=[];effects=[] for p,b,returns,cost in zip(P,B,RET,COST): if not cost.is_finite() or cost<0:raise ValueError('invalid modeled cost') if any(not r.is_finite() or r<-1 for r in returns.values()):raise ValueError('invalid return') rp=sum(p[s]*returns[s] for s in p)-cost;rb=sum(b[s]*returns[s] for s in b) if rp<=-1 or rb<=-1:raise ValueError('nonpositive growth base') row={s:(p[s]-b[s])*returns[s] for s in p};row['fees']=-cost assert sum(row.values())==rp-rb portfolio.append(rp);benchmark.append(rb);effects.append(row) rp=prod(1+r for r in portfolio)-1;rb=prod(1+r for r in benchmark)-1 multipliers=[prod(1+r for r in portfolio[:t])*prod(1+r for r in benchmark[t+1:]) for t in range(2)] linked={s:sum(effects[t][s]*multipliers[t] for t in range(2)) for s in effects[0]} raw=sum(sum(row.values()) for row in effects);interaction=(rp-rb)-raw assert rp==D('.070649') and rb==D('.04475') assert linked=={'A':D('.020540'),'B':D('.008472'),'fees':D('-.003113')} assert sum(linked.values())==rp-rb==D('.025899') assert interaction==D('.000899') def group_effects(values,mapping): grouped={} for security,value in values.items(): if not value.is_finite():raise ValueError('nonfinite effect') group=mapping.get(security,'Unknown') grouped[group]=grouped.get(group,D(0))+value return grouped def check_flows(values): if any(not D(str(x)).is_finite() or D(str(x))!=0 for x in values): raise ValueError('external flow outside attribution contract') classified=group_effects({'A':D('.02'),'U':D('.003')},{'A':'Known'}) assert sum(classified.values())==D('.023') assert classified['Unknown']==D('.003') check_flows([0,0]) try:check_flows([0,100]) except ValueError:pass else:raise AssertionError('contribution mixed into return attribution') for bad in [{'A':float('nan'),'B':1},{'A':float('inf'),'B':0}]: try:weights(bad) except ValueError:pass else:raise AssertionError('nonfinite weights accepted') print('portfolio=7.064900% benchmark=4.475000% arithmetic_excess=2.589900pp') print('period_active=0.900000pp,1.600000pp raw_sum=2.500000pp temporal_interaction=0.089900pp') print('linked_A=2.054000pp linked_B=0.847200pp linked_fees=-0.311300pp residual=0') print('unknown_classification=retained NaN_weights=rejected')