根据数量和价格调整股票投资组合的价格

Adjust prices on stock portfolio based on quantity and price

我正在尝试调整我的股票组合并尝试计算调整后的平均价格(这是加权平均的一种形式)。

这是示例数据:


import pandas as pd 
import numpy as np

sample_dict = {'ticker': {1: 'ABCD',
  2: 'ABCD', 3: 'ABCD', 4: 'ABCD', 5: 'ABCD', 6: 'ABCD', 8: 'EFGH',
  9: 'EFGH', 10: 'EFGH', 11: 'EFGH', 12: 'EFGH', 13: 'EFGH'},
 'Date': {1: "2018, 1, 10",   2: "2018, 1, 20",
  3: "2018, 2, 7", 4: "2018, 4, 14",
  5: "2018, 5, 25", 6: "2018, 7, 4",
  8: "2018, 1, 10", 9: "2018, 1, 20",
  10: "2018, 2, 7", 11: "2018, 4, 14",
  12: "2018, 5, 25", 13: "2018, 7, 4"},
 'Sell_buy': {1: 'buy', 2: 'buy',  3: 'sell', 4: 'buy', 5: 'sell', 6: 'buy',
  8: 'buy', 9: 'buy', 10: 'buy', 11: 'buy', 12: 'sell', 13: 'sell'},
 'Qtd': {1: 100.0, 2: 300.0, 3: 200.0, 4: 500.0, 5: 600.0, 6: 500.0,
  8: 300.0, 9: 300.0, 10: 200.0, 11: 200.0, 12: 700.0, 13: 100.0},
 'Price': {1: 8.0, 2: 10.0, 3: 12.0, 4: 9.0, 5: 13.0, 6: 14.0,
  8: 8.0, 9: 10.0, 10: 12.0, 11: 9.0, 12: 13.0, 13: 14.0},
 'Costs': {1: 10.93, 2: 12.52, 3: 11.39, 4: 14.5, 5: 14.68, 6: 14.96,
  8: 10.93, 9: 12.52, 10: 11.39, 11: 14.5, 12: 14.68, 13: 14.96}}

sample_df = pd.DataFrame(sample_dict)
sample_df['Date']=pd.to_datetime(sample_df['Date'], dayfirst=True).dt.date 

我能够毫无问题地获得更新的调整数量(基于买卖):


#to calculate adjusted quantity. this works as expected
sample_df['Adj Qtd'] = sample_df.apply(lambda x: ((x.Sell_buy == "buy") - (x.Sell_buy == "sell")) * x['Qtd'], axis = 1)
sample_df['Adj Qtd'] = sample_df.groupby('ticker')['Adj Qtd'].cumsum()

但是,我无法获得正确的调整后价格。 这里的条件是,如果我卖出一只股票,我的调整后价格不应改变,并保持与买入该股票时的最后调整价格相同。

我尝试了以下方法来实现这个目标:

#Adjust Price. Works good until I reach the row where a sell was made
sample_df['Adjusted Price'] = sample_df.apply(lambda x: ((x.Sell_buy == "buy") - (x.Sell_buy == "sell")) * (x["Price"] * x["Qtd"] + x["Costs"]), axis = 1)
sample_df['Adjusted Price'] = sample_df.groupby('ticker')['Adjusted Price'].cumsum().div(sample_df['Adj Qtd'])

我可以通过以下方式部分更正卖出行的调整价格:

# When it's a "sell", adjusted price is the same from above
sample_df.loc[sample_df['Sell_buy'] == 'sell',['Adjusted Price']] = np.NaN
sample_df.fillna(method='ffill', inplace=True)

    ticker  Date    Sell_buy        Qtd Price   Costs   Adj Qtd Adjusted Price
1   ABCD    2018-10-01  buy       100.0 8.0     10.93   100.0   8.109300
2   ABCD    2018-01-20  buy       300.0 10.0    12.52   400.0   9.558625
3   ABCD    2018-07-02  sell      200.0 12.0    11.39   200.0   9.558625
4   ABCD    2018-04-14  buy       500.0 9.0     14.50   700.0   8.466514
5   ABCD    2018-05-25  sell      600.0 13.0    14.68   100.0   8.466514
6   ABCD    2018-04-07  buy       500.0 14.0    14.96   600.0   8.544733
8   EFGH    2018-10-01  buy       300.0 8.0     10.93   300.0   8.036433
9   EFGH    2018-01-20  buy       300.0 10.0    12.52   600.0   9.039083
10  EFGH    2018-07-02  buy       200.0 12.0    11.39   800.0   9.793550
11  EFGH    2018-04-14  buy       200.0 9.0     14.50   1000.0  9.649340
12  EFGH    2018-05-25  sell      700.0 13.0    14.68   300.0   9.649340
13  EFGH    2018-04-07  sell      100.0 14.0    14.96   200.0   9.649340

如果“买入”之间没有“卖出”(就像本例中股票 EFGH 所做的那样),这将非常有效。 需要明确的是,当交易是“卖出”时,我们必须忽略调整价格,并使用该特定股票最后一次买入类型交易中的最后调整价格。

我在 excel 中做了所有这些微积分,输出应该如下: 为了进一步说明,所选单元格的 excel 公式为: =IF(C3="buy";(I2*G2+D3*E3+F3)/G3;IF(G3<>0;I2;0))

我也尝试过 .groupby("ticker").apply() 使用 shift() 的函数,以便使用上面行中的先前值,但我失败了。

我想不出一个好的、简单的解决方案。问题是计算调整后价格取决于调整后价格的先前值,这阻止了向量化或 shift() 的使用。

所以,这是丑陋的解决方案。 :)

第一步是使用 groupby,通过股票代码将其分开。然后,它遍历该组中的所有行,并计算价格的加权平均值以获得当前股票和之前的价格。然后,它将该列表添加为数据框中的一列。

def weighted_average(a, b, a_weight):
    """Take an average of a and b, with a weighted by a_weight"""
    assert 0 <= a_weight <= 1
    return a * a_weight + b * (1 - a_weight)

def get_adjusted_price_for_ticker(single_ticker_df):
    adjusted_price = 0
    current_shares = 0
    prices = []
    for _, row in single_ticker_df.iterrows():
        is_buy = row["Sell_buy"] == "buy"
        qtd = row["Qtd"]
        if is_buy:
            current_shares += qtd
            cost_per_share = (qtd * row["Price"] + row["Costs"]) / qtd
            proportion_of_new_shares = qtd / current_shares
            adjusted_price = weighted_average(cost_per_share, adjusted_price, proportion_of_new_shares)
        else:
            current_shares -= qtd
        prices.append(adjusted_price)

    single_ticker_df["Adjusted Price"] = prices
    return single_ticker_df

def get_adjusted_price(df):
    return df.groupby("ticker").apply(get_adjusted_price_for_ticker)

get_adjusted_price(sample_df)

输出:

   ticker        Date Sell_buy    Qtd  Price  Costs  Adjusted Price
1    ABCD  2018-10-01      buy  100.0    8.0  10.93        8.109300
2    ABCD  2018-01-20      buy  300.0   10.0  12.52        9.558625
3    ABCD  2018-07-02     sell  200.0   12.0  11.39        9.558625
4    ABCD  2018-04-14      buy  500.0    9.0  14.50        9.180321
5    ABCD  2018-05-25     sell  600.0   13.0  14.68        9.180321
6    ABCD  2018-04-07      buy  500.0   14.0  14.96       13.221654
8    EFGH  2018-10-01      buy  300.0    8.0  10.93        8.036433
9    EFGH  2018-01-20      buy  300.0   10.0  12.52        9.039083
10   EFGH  2018-07-02      buy  200.0   12.0  11.39        9.793550
11   EFGH  2018-04-14      buy  200.0    9.0  14.50        9.649340
12   EFGH  2018-05-25     sell  700.0   13.0  14.68        9.649340
13   EFGH  2018-04-07     sell  100.0   14.0  14.96        9.649340