如何避免使用Python / Pandas构建公平曲线?

时间:2017-10-23 20:52:59

标签: python pandas

我正在尝试使用Pandas在Python中构建一个公平曲线。对于那些不知情的人来说,权益曲线是每天投资利润/损失的累积总数。下面的代码有效,但速度非常慢。我已经尝试使用Pandas .iloc构建一个替代品,但是没有什么工作。鉴于我必须引用前一行,我不确定是否可以在循环外执行此操作。

for today in range(len(f1)): #initiate a loop that runs the length of the "f1" dataframe 

    if today == 0: #if the index value is zero (aka first row in the dataframe) then...

        f1.loc[today,'StartAUM'] = StartAUM #Set intial assets 
        f1.loc[today,'Shares'] = 0 #dummy placeholder for shares; no trading on day 1 
        f1.loc[today,'PnL'] = 0 #dummy placeholder for P&L; no trading day 1 
        f1.loc[today,'EndAUM'] = StartAUM #set ending AUM; should be beginning AUM since no trades 
        continue #and on to the second row in the dataframe 

    yesterday = today - 1 #used to reference the rows (see below)

    f1.loc[today,'StartAUM'] = f1.loc[yesterday,'EndAUM'] #todays starting aseets are yesterday's ending assets 
    f1.loc[today,'Shares'] = f1.loc[yesterday,'EndAUM']//f1.loc[yesterday,'Shareprice'] #today's shares to trade = yesterday's assets/yesterday's share price 
    f1.loc[today,'PnL'] = f1.loc[today,'Shares']*f1.loc[today,'Outcome1'] #Our P&L should be the shares traded (see prior line) multiplied by the outcome for 1 share
    #Note Outcome1 came from the dataframe before this loop >> for the purposes here it's value is irrelevant 
    f1.loc[today,'EndAUM'] = f1.loc[today,'StartAUM']+f1.loc[today,'PnL'] #ending assets are starting assets + today's P&L 

6 个答案:

答案 0 :(得分:0)

这里有一个很好的例子:http://www.pythonforfinance.net/category/basic-data-analysis/我知道在Wes McKinney的Python for Data Analysis一书中有一个例子。您可以在此处找到它:http://wesmckinney.com/blog/python-for-financial-data-analysis-with-pandas/

答案 1 :(得分:0)

你需要对操作进行矢量化(不要迭代,而是立即计算整列)

# fill the initial values
f1['StartAUM'] = StartAUM  # Set intial assets
f1['Shares'] = 0  # dummy placeholder for shares; no trading on day 1
f1['PnL'] = 0  # dummy placeholder for P&L; no trading day 1
f1['EndAUM'] = StartAUM  # s

#do the computations (vectorized)
f1['StartAUM'].iloc[1:] = f1['EndAUM'].iloc[:-1]
f1['Shares'].iloc[1:] = f1['EndAUM'].iloc[:-1] // f1['Shareprice'].iloc[:-1] 
f1['PnL'] = f1['Shares'] * f1['Outcome1']  
f1['EndAUM'] = f1['StartAUM'] + f1 ['PnL'] 

编辑:由于StartAUMEndAUMShares相互依赖,无法相互计算,因此无效。我之前没有注意到这一点。

答案 2 :(得分:0)

您是否尝试使用iterrows()构建for循环?

for index, row in f1.iterrows():
    if today == 0:
        row['StartAUM'] = StartAUM #Set intial assets 
        row['Shares'] = 0 #dummy placeholder for shares; no trading on day 1 
        row['PnL'] = 0 #dummy placeholder for P&L; no trading day 1 
        row['EndAUM'] = StartAUM #set ending AUM; should be beginning AUM since no trades 
        continue #and on to the second row in the dataframe 

yesterday = row[today] - 1 #used to reference the rows (see below)

row['StartAUM'] = row['EndAUM'] #todays starting aseets are yesterday's ending assets 
row['Shares'] = row['EndAUM']//['Shareprice'] #today's shares to trade = yesterday's assets/yesterday's share price 
row['PnL'] = row['Shares']*row['Outcome1'] #Our P&L should be the shares traded (see prior line) multiplied by the outcome for 1 share
#Note Outcome1 came from the dataframe before this loop >> for the purposes here it's value is irrelevant 
row['EndAUM'] = row['StartAUM']+row['PnL'] #ending assets are starting assets + today's P&L 

代码可能很慢,因为loc每次开始都会经过f1iterrows()使用相同的数据帧,因为它逐行循环。 详情请见iterrows() here

答案 3 :(得分:0)

您可以尝试以下方法:

#import relevant modules
import pandas as pd
import numpy as np
from pandas_datareader import data
import matplotlib.pyplot as plt


#download data into DataFrame and create moving averages columns
f1 = data.DataReader('AAPL', 'yahoo',start='1/1/2017')

StartAUM = 1000000

#populate DataFrame with starting values
f1['Shares'] = 0
f1['PnL'] = 0
f1['EndAUM'] = StartAUM

#Set shares held to be the previous day's EndAUM divided by the previous day's closing price
f1['Shares'] = f1['EndAUM'].shift(1) / f1['Adj Close'].shift(1)
#Set the day's PnL to be the number of shares held multiplied by the change in closing price from yesterday to today's close
f1['PnL'] = f1['Shares'] * (f1['Adj Close'] - f1['Adj Close'].shift(1))
#Set day's ending AUM to be previous days ending AUM plus daily PnL
f1['EndAUM'] = f1['EndAUM'].shift(1) + f1['PnL']
#Plot the equity curve
f1['EndAUM'].plot()

以上是否解决了您的问题?

答案 4 :(得分:0)

解决方案是使用Numba包。它在很短的时间内执行循环任务。

https://numba.pydata.org/

参数/ dataframe可以传递给numba模块/函数。在时间允许的情况下,我会尝试用代码写出更详细的解释。

感谢所有

答案 5 :(得分:0)

万一其他人遇到了这种情况,您绝对可以制作出没有循环的资产曲线。

虚拟一些数据

import pandas as pd
import numpy as np
plt.style.use('ggplot')
plt.rcParams['figure.figsize'] = (13, 10)

# Some data to work with
np.random.seed(1)
stock = pd.DataFrame(
    np.random.randn(100).cumsum() + 10, 
    index=pd.date_range('1/1/2020', periods=100, freq='D'),
    columns=['Close']
    )
stock['ma_5'] = stock['Close'].rolling(5).mean()
stock['ma_15'] = stock['Close'].rolling(15).mean()

持有:基于移动平均交叉信号的简单多头/空头

longs = stock['Close'].where(stock['ma_5'] > stock['ma_15'], np.nan)
shorts = stock['Close'].where(stock['ma_5'] < stock['ma_15'], np.nan)

# Quick plot
stock.plot()
longs.plot(lw=5, c='green')
shorts.plot(lw=5, c='red')

enter image description here

股权曲线:

确定哪一方(l / s)首先持有(即:在这种情况下为第一笔交易,然后做空),然后保持初始交易价格,然后累计每日变化的总和(通常该序列中会有更多的nan值) (如果您也有退出市场的退出规则),最后向前填充nan值,并用零填充所有剩余的nan。第二个相反持仓量(在这种情况下为多头)基本相同,只是不保留起始价。另一重要的事情是将短期的每日变化反转(即:负变化对PnL应该是正的)。

lidx = np.where(longs > 0)[0][0]
sidx = np.where(shorts > 0)[0][0]
startdx = min(lidx, sidx)

# For first holding side, keep first trade price, then calc daily change fwd and ffill nan's
# For second holdng side, get cumsum of daily changes, ffill and fillna(0) (make sure short changes are inverted)
if lidx == startdx:
    lcurve = longs.diff() # get daily changes
    lcurve[lidx] = longs[lidx] # put back initial starting price
    lcurve = lcurve.cumsum().ffill() # add dialy changes/ffill to build curve
    scurve = -shorts.diff().cumsum().ffill().fillna(0) # get daily changes (make declines positive changes)
else:
    scurve = -shorts.diff() # get daily changes (make declines positive changes)
    scurve[sidx] = shorts[sidx] # put back initial starting price
    scurve = scurve.cumsum().ffill() # add dialy changes/ffill to build curve
    lcurve = longs.diff().cumsum().ffill().fillna(0) # get daily changes

将2条长/短曲线加在一起,以获得最终的资产曲线

eq_curve = lcurve + scurve

# quick plot
stock.iloc[:, :3].plot()
longs.plot(lw=5, c='green', label='Long')
shorts.plot(lw=5, c='red', label='Short')
eq_curve.plot(lw=2, ls='dotted', c='orange', label='Equity Curve')
plt.legend()

enter image description here