Эффективно рассчитать точку контроля с pandas - PullRequest
0 голосов
/ 07 марта 2020

Мой алгоритм увеличил время выполнения с 35 секунд до 15 минут при реализации этой функции в течение дневного периода. Al go извлекает ежедневную историю в большом количестве и выполняет итерацию по подмножеству кадра данных (от t0 до tX, где tX - текущая строка итерации). Это делает это, чтобы эмулировать то, что произошло бы во время операций в реальном времени al go. Я знаю, что есть способы улучшить его, используя память между вычислениями кадров, но мне было интересно, есть ли более реализация pandas -i sh, которая принесет немедленную выгоду.

Предположим, что self.Step что-то вроде 0.00001 и self.Precision есть 5; они используются для разбивки информации о барах ohl c на отдельные шаги для нахождения po c. _frame является подмножеством строк всего информационного кадра, и _low / _high соответствуют этому. Следующий блок кода выполняется для всего _frame, который может превышать ~ 250 строк каждый раз, когда новая строка добавляется с помощью al go (при расчете годового периода на ежедневных данных). Я полагаю, что именно iterrows вызывает основное замедление. Кадр данных имеет столбцы, такие как high, low, open, close, volume. Я рассчитываю время, цену, возможность и объем контроля.

# Set the complete index of prices +/- 1 step due to weird floating point precision issues
volume_prices = pd.Series(0, index=np.around(np.arange(_low - self.Step, _high + self.Step, self.Step), decimals=self.Precision))
time_prices = volume_prices.copy()
for index, state in _frame.iterrows():
    _prices = np.around(np.arange(state.low, state.high, self.Step), decimals=self.Precision)
    # Evenly distribute the bar's volume over its range
    volume_prices[_prices] += state.volume / _prices.size
    # Increment time at price
    time_prices[_prices] += 1
# Pandas only returns the 1st row of the max value,
# so we need to reverse the series to find the other side
# and then find the average price between those two extremes
volume_poc = (volume_prices.idxmax() + volume_prices.iloc[::-1].idxmax()) / 2)
time_poc = (time_prices.idxmax() + time_prices.iloc[::-1].idxmax()) / 2)

Ответы [ 3 ]

0 голосов
/ 09 марта 2020

Мне удалось снизить его до 2 минут вместо 15 - во всяком случае, на ежедневных таймфреймах. Это все еще медленно на более низких таймфреймах (10 минут в час в течение 2-летнего периода с точностью до 2 для акций). Работа с DataFrames, в отличие от Series, была намного медленнее. Я надеюсь на большее, но я не знаю, что я могу сделать, кроме следующего решения:

# Upon class instantiation, I've created attributes for each timeframe
# related to `volume_at_price` and `time_at_price`. They serve as memory
# in between frame calculations
def _prices_at(self, frame, bars=0):
    # Include 1 step above high as np.arange does not
    # include the upper limit by default
    state = frame.iloc[-min(bars + 1, frame.index.size)]
    bins = np.around(np.arange(state.low, state.high + self.Step, self.Step), decimals=self.Precision)
    return pd.Series(state.volume / bins.size, index=bins)


# SetFeature/Feature implement timeframed attributes (i.e., 'volume_at_price_D')
_v = 'volume_at_price'
_t = 'time_at_price'

# Add to x_at_price histogram
_p = self._prices_at(frame)
self.SetFeature(_v, self.Feature(_v).add(_p, fill_value=0))
self.SetFeature(_t, self.Feature(_t).add(_p * 0 + 1, fill_value=0))

# Remove old data from histogram
_p = self._prices_at(frame, self.Bars)
v = self.SetFeature(_v, self.Feature(_v).subtract(_p, fill_value=0))
t = self.SetFeature(_t, self.Feature(_t).subtract(_p * 0 + 1, fill_value=0))

self.SetFeature('volume_poc', (v.idxmax() + v.iloc[::-1].idxmax()) / 2)
self.SetFeature('time_poc', (t.idxmax() + t.iloc[::-1].idxmax()) / 2)
0 голосов
/ 09 марта 2020

Вот что я разработал. Я до сих пор не уверен, что ответ, который вы выводите из кода, верен, я думаю, что ваша строка volume_prices[_prices] += state.Volume / _prices.size не применяется к каждой записи в volume_prices, но здесь речь идет о бенчмаркинге. О 9x улучшении.

def vpOriginal():
    Step = 0.00001
    Precision = 5
    _frame = getData()
    _low = 85.0
    _high = 116.4
    # Set the complete index of prices +/- 1 step due to weird floating point precision issues
    volume_prices = pd.Series(0, index=np.around(np.arange(_low - Step, _high + Step, Step), decimals=Precision))
    time_prices = volume_prices.copy()
    time_prices2 = volume_prices.copy()
    for index, state in _frame.iterrows():
        _prices = np.around(np.arange(state.Low, state.High, Step), decimals=Precision)

        # Evenly distribute the bar's volume over its range
        volume_prices[_prices] += state.Volume / _prices.size
        # Increment time at price
        time_prices[_prices] += 1
    time_prices2 += 1
    # Pandas only returns the 1st row of the max value,
    # so we need to reverse the series to find the other side
    # and then find the average price between those two extremes
#    print(volume_prices.head(10))
    volume_poc = (volume_prices.idxmax() + volume_prices.iloc[::-1].idxmax() / 2)
    time_poc = (time_prices.idxmax() + time_prices.iloc[::-1].idxmax() / 2)
    return volume_poc, time_poc

def vpNoDF():
    Step = 0.00001
    Precision = 5
    _frame = getData()
    _low = 85.0
    _high = 116.4
    # Set the complete index of prices +/- 1 step due to weird floating point precision issues
    volume_prices = pd.Series(0, index=np.around(np.arange(_low - Step, _high + Step, Step), decimals=Precision))
    time_prices = volume_prices.copy()
    for index, state in _frame.iterrows():
        _prices = np.around((state.High - state.Low) / Step , 0)

        # Evenly distribute the bar's volume over its range
        volume_prices.loc[state.Low:state.High] += state.Volume / _prices
        # Increment time at price
        time_prices.loc[state.Low:state.High] += 1

    # Pandas only returns the 1st row of the max value,
    # so we need to reverse the series to find the other side
    # and then find the average price between those two extremes
    volume_poc = (volume_prices.idxmax() + volume_prices.iloc[::-1].idxmax() / 2)
    time_poc = (time_prices.idxmax() + time_prices.iloc[::-1].idxmax() / 2)
    return volume_poc, time_poc

getData()
Out[8]: 
         Date    Open    High     Low   Close    Volume  Adj Close
0  2008-10-14  116.26  116.40  103.14  104.08  70749800     104.08
1  2008-10-13  104.55  110.53  101.02  110.26  54967000     110.26
2  2008-10-10   85.70  100.00   85.00   96.80  79260700      96.80
3  2008-10-09   93.35   95.80   86.60   88.74  57763700      88.74
4  2008-10-08   85.91   96.33   85.68   89.79  78847900      89.79
5  2008-10-07  100.48  101.50   88.95   89.16  67099000      89.16
6  2008-10-06   91.96   98.78   87.54   98.14  75264900      98.14
7  2008-10-03  104.00  106.50   94.65   97.07  81942800      97.07
8  2008-10-02  108.01  108.79  100.00  100.10  57477300     100.10
9  2008-10-01  111.92  112.36  107.39  109.12  46303000     109.12

vpOriginal()
Out[9]: (142.55000000000001, 142.55000000000001)

vpNoDF()
Out[10]: (142.55000000000001, 142.55000000000001)

%timeit vpOriginal()
2.79 s ± 24.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit vpNoDF()
300 ms ± 8.04 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
0 голосов
/ 08 марта 2020

Вы можете использовать эту функцию как основу и настроить ее:

def f(x):                             #function to find the POC price and volume
    a = x['tradePrice'].value_counts().index[0]
    b = x.loc[x['tradePrice'] == a, 'tradeVolume'].sum()
    return pd.Series([a,b],['POC_Price','POC_Volume'])
...