Средневзвешенное значение временного ряда с изменением весов во времени - PullRequest
0 голосов
/ 07 января 2019

Мне нужно сгенерировать средневзвешенную доходность некоторого количества финансовых активов, где используемые веса меняются со временем. (Приложение является финансовым, но сама проблема носит весьма общий характер).

Выдержки из моих возвратов и весов следующие:

returns_df: возврат активов с течением времени

┌──────────────────┬───────────┬───────────┬───────────┐
│     DateTime     │  Asset 1  │  Asset 2  │  Asset 3  │
├──────────────────┼───────────┼───────────┼───────────┤
│ 2015-04-09 07:00 │ -0.000959 │ -0.000207 │ -0.000233 │
│ 2015-04-09 08:00 │ -0.004003 │  0.000169 │  0.001221 │
│ 2015-04-09 09:00 │ -0.000700 │ -0.000070 │ -0.000096 │
│ 2015-04-09 10:00 │ -0.000812 │ -0.000289 │  0.000177 │
│ 2015-04-09 11:00 │ -0.000030 │ -0.000168 │ -0.000315 │
└──────────────────┴───────────┴───────────┴───────────┘

weights_df: вес активов с течением времени

┌──────────────────┬─────────┬─────────┬─────────┐
│ Start of weights │ Asset 1 │ Asset 2 │ Asset 3 │
├──────────────────┼─────────┼─────────┼─────────┤
│ 2015-03-01       │       1 │       0 │       0 │
│ 2015-04-01       │   0.023 │  0.8733 │  0.1037 │
│ 2015-05-01       │       1 │       0 │       0 │
│ 2015-06-01       │  0.0477 │  0.8278 │  0.1245 │
└──────────────────┴─────────┴─────────┴─────────┘

Например, все доходы в первой таблице будут взвешены на {0.023; 0.8733; 0.103}, поскольку все они упадут после 2015-04-01, но до 2015-05-01.

Разумеется, мой реальный набор данных содержит данные, охватывающие весь диапазон дат в моих весах.

Я совсем не уверен, как подойти к этому, я подумал об использовании groupby(), но, учитывая, что форма weights_df не совпадает с returns_df, это не похоже на работу.

import numpy as np
import pandas as pd
from io import StringIO
# alternatively try `import StringIO`

returns_datatext = StringIO("""
    DateTime     │  Asset 1  │  Asset 2  │  Asset 3
2015-04-09 07:00 │ -0.000959 │ -0.000207 │ -0.000233
2015-04-09 08:00 │ -0.004003 │  0.000169 │  0.001221
2015-04-09 09:00 │ -0.000700 │ -0.000070 │ -0.000096
2015-04-09 10:00 │ -0.000812 │ -0.000289 │  0.000177
2015-04-09 11:00 │ -0.000030 │ -0.000168 │ -0.000315
""")
returns_df = pd.read_table(returns_datatext, index_col=[0], parse_dates=True, sep='│')

weights_datatext = StringIO("""
Start of weights │ Asset 1 │ Asset 2 │ Asset 3
2015-03-01       │       1 │       0 │       0
2015-04-01       │   0.023 │  0.8733 │  0.1037
2015-05-01       │       1 │       0 │       0
2015-06-01       │  0.0477 │  0.8278 │  0.1245
""")
weights_df = pd.read_table(weights_datatext, index_col=[0], parse_dates=True, sep='│')

Ожидаемые результаты для приведенной выше таблицы будут следующими:

┌──────────────────┬──────────────────┐
│     DateTime     │ Weighted average │
├──────────────────┼──────────────────┤
│ 2015-04-09 07:00 │        -0.000227 │
│ 2015-04-09 08:00 │         0.000182 │
│ 2015-04-09 09:00 │        -0.000087 │
│ 2015-04-09 10:00 │        -0.000253 │
│ 2015-04-09 11:00 │        -0.000180 │
└──────────────────┴──────────────────┘

1 Ответ

0 голосов
/ 07 января 2019

Это pd.merge_asof для связи двух, а затем numpy.average

import pandas as pd
import numpy as np

## Fix whitespace in sample data
#returns_df.index.name = 'DateTime'
#returns_df.columns = ['Asset1', 'Asset2', 'Asset3']
#weights_df.index.name= 'Start of weights'
#weights_df.columns = ['Asset1', 'Asset2', 'Asset3']

df = pd.merge_asof(returns_df, weights_df,
                   left_index=True, right_index=True, 
                   direction='backward',
                   suffixes=['', '_weight'])

cols = ['Asset1', 'Asset2', 'Asset3']
returns_df['weighted_average'] = np.average(df[cols], weights=df[[col + '_weight' for col in cols]], axis=1)

Выход: returns_df

                       Asset1    Asset2    Asset3  weighted_average
DateTime                                                           
2015-04-09 07:00:00 -0.000959 -0.000207 -0.000233         -0.000227
2015-04-09 08:00:00 -0.004003  0.000169  0.001221          0.000182
2015-04-09 09:00:00 -0.000700 -0.000070 -0.000096         -0.000087
2015-04-09 10:00:00 -0.000812 -0.000289  0.000177         -0.000253
2015-04-09 11:00:00 -0.000030 -0.000168 -0.000315         -0.000180

Для иллюстрации, это результат DataFrame от слияния. Логика выбирает ближайшую дату в weights_df, которая предшествует дате в returns_df, в качестве условия объединения:

                       Asset1    Asset2    Asset3  Asset1_weight  Asset2_weight  Asset3_weight
DateTime                                                                                      
2015-04-09 07:00:00 -0.000959 -0.000207 -0.000233          0.023         0.8733         0.1037
2015-04-09 08:00:00 -0.004003  0.000169  0.001221          0.023         0.8733         0.1037
2015-04-09 09:00:00 -0.000700 -0.000070 -0.000096          0.023         0.8733         0.1037
2015-04-09 10:00:00 -0.000812 -0.000289  0.000177          0.023         0.8733         0.1037
2015-04-09 11:00:00 -0.000030 -0.000168 -0.000315          0.023         0.8733         0.1037
...