第一行的计算方式与所有其他行和所有行不同,但第一行以前一行为条件

问题描述

我有一个 df 列,其中包含每日收益行的行情,并且索引是日期时间索引

            SPY      IWM       TLT                                                                          
2016-01-04  0.914939  0.998960  1.014094  
2016-01-05  1.014062  1.002650  1.002819  
2016-01-06  0.991911  0.999906  1.014441  
2016-01-07  0.937087  0.995280  1.014140  
2016-01-08  1.005388  0.999147  0.995572
I have initial weights for each ticker on day one
SPY  50
IWM  25
TLT  25

Each weight has a band
SPY = 40,60
IWM = 20,30
TLT = 20,30

数据帧每天持续 5 年。第一天,我想计算原始重量乘以当天的回报。对于那天之后的每一天,我想计算当天的回报(即前一天的价值乘以该天的回报)并每天检查三者中任何一个的权重是否在带外。每天的权重是该股票代码的值/该天值的总和。如果其中一个代码权重连续 5 天违反区间,我想重新平衡所有三个权重,第二天的行应该是原始权重除以前几天的投资组合价值。

 Example       SPY      IWM         TLT      PortValue    SPYW    IWMW TLTW
    XX Date      51.45     27.25      21.54       100.24     51.3    27.18  21.4 No Rebal,nextday*prevday
    XX Date       59        29         15          103       57       28    14.5  Rebal,next day below
    NEXT DAY  50/103*ret 25/103*ret  25/103*ret

我什么都试过了。 lambda 函数、np.where、for 循环、if 语句、上述所有内容的嵌套变体。我无法在第一天绕过索引的 bool 测试,并使其在剩余的日子里工作,其中下一行取决于前一行的计算,而不是日期时间索引位置

解决方法

有趣的问题。以下内容应该可以工作 - 显然,进行了许多修改以反映您的实际数据。请注意,这完全忽略了日期时间索引,因为它提供信息并且不会影响结果:

portfolio = [50,25,25] #starting amount of each investment in the portfolio
allocations =[.50,.25,.25] #base allocations among the portfolio investments
tickers = list(df3.columns.values)
bands = [[.4,.6],[.20,.30],.30]] #permissible bands for each investment/ticker
violations = {ticker:0 for ticker in tickers} #initialize a violations counter for each ticker

#start iterating through each day in the dataframe:
for i,row in df.iterrows():
    yields = row.to_list() #extract the daily yields
    portfolio = [investment*yld for investment,yld in zip(portfolio,yields) ] #recalculate the new value of each investment 
    weights = [investment/sum(portfolio)for investment in portfolio] #recalculate the new relative weight of each investment
    for weight,band in zip(weights,bands):
        ticker = tickers[weights.index(weight)] #for each ticker - 
        #check if it's outside its permitted band
        if weight<band[0] or weight>band[1]:
            violations[ticker] +=1 #if it is,increment its specific violations counter            
        else:
            violations[ticker]=0 #if not,reset the counter to zero,to account for non-consecutive violations  
        if violations[ticker] == 5: #5 consecutive violations detected....
            portfolio = [sum(portfolio)*allocation for allocation in allocations] #rebalance the portofolio