"""R634:主动收益的样本跟踪误差,缺日阻断;相关性不是主动风险。""" from math import isfinite,isclose,sqrt from statistics import stdev,mean,correlation def active_risk(portfolio,benchmark,frequency=252): if set(portfolio)!=set(benchmark):raise ValueError('unaligned sessions') if type(frequency) is not int or frequency<=0:raise ValueError('invalid frequency') keys=sorted(portfolio) if any(not isfinite(v) for v in list(portfolio.values())+list(benchmark.values())):raise ValueError('nonfinite return') if len(keys)<2:return {'status':'insufficient','tracking_error':None} active={t:portfolio[t]-benchmark[t] for t in keys} return {'status':'ok','active':active,'mean':mean(active.values()),'tracking_error':sqrt(frequency)*stdev(active.values())} returns=[(.02,0),(.01,0),(-.02,.015),(.02,0)] wa=[.4,.6,.7,.45] portfolio={i+1:w*a+(1-w)*b for i,((a,b),w) in enumerate(zip(returns,wa))} benchmark={i+1:(a+b)/2 for i,(a,b) in enumerate(returns)} result=active_risk(portfolio,benchmark) assert isclose(result['mean'],-.00225,rel_tol=0,abs_tol=1e-12) assert isclose(result['tracking_error'],sqrt(.002919),rel_tol=0,abs_tol=1e-12) assert all(isclose(x,y,rel_tol=0,abs_tol=1e-12) for x,y in zip(result['active'].values(),[-.002,.001,-.007,-.001])) high=[t for t,a in result['active'].items() if abs(a)>=.005] assert high==[3] contributions={'A':(.7-.5)*(-.02),'B':(.3-.5)*.015} assert isclose(sum(contributions.values()),result['active'][3],rel_tol=0,abs_tol=1e-12) streak=longest=0 for value in result['active'].values(): streak=streak+1 if value<0 else 0;longest=max(longest,streak) assert longest==2 base={1:.01,2:.02,3:-.01,4:.03};scaled={t:.9*r for t,r in base.items()} assert isclose(correlation(base.values(),scaled.values()),1,rel_tol=0,abs_tol=1e-12) assert active_risk(scaled,base)['tracking_error']>0 lagging={t:r-.001 for t,r in base.items()} assert active_risk(lagging,base)['tracking_error']<1e-15 assert active_risk(lagging,base)['mean']<0 missing=dict(benchmark);del missing[2] try:active_risk(portfolio,missing) except ValueError:pass else:raise AssertionError('missing day filled silently') nonfinite_single=[] for side in ['portfolio','benchmark']: for value in [float('nan'),float('inf'),float('-inf')]: p={1:0.};b={1:0.} (p if side=='portfolio' else b)[1]=value try:active_risk(p,b) except ValueError:pass else:nonfinite_single.append((side,str(value))) assert not nonfinite_single,nonfinite_single assert active_risk({1:.01},{1:.02})['status']=='insufficient' assert active_risk({},{})['status']=='insufficient' print('active=[-0.002,0.001,-0.007,-0.001] mean='+format(result['mean'],'.6%')+' annual_tracking_error='+format(result['tracking_error'],'.6%')) print('high_session=3 A_contribution=-0.400000pp B_contribution=-0.300000pp longest_lag_streak=2') print('correlation_1_active_risk_positive=True constant_negative_active_can_have_zero_TE=True missing_session=rejected')