Расчет максимальной просадки набора возвращает


Я написал простую функцию, которая вычисляет и возвращает максимальное сокращение набора возвращает. Я пытаюсь выжать как можно больше эффективности для скорости из кода, как это возможно. У меня примерно так же быстро, как я могу пойти. У кого-нибудь есть предложения о том, как более эффективно писать эту функцию, возможно, через списочные включения и т. д.?

import numpy as np

def max_drawdown(returns):

    draw_series = np.array(np.ones(np.size(returns)))
    max_return = 0; max_draw = 1; draw = 1
    returns = returns + 1

    for r in range(returns.count()-1, 0, -1):

        if returns[r] > max_return:
            max_return = returns[r]

        else:
            draw = returns[r] / max_return
            if draw < max_draw:
                max_draw = draw

        draw_series[r-1] = -(1 - max_draw)

    return draw_series


6343
6
задан 15 июля 2011 в 09:07 Источник Поделиться
Комментарии
5 ответов

Вы правильно отмечаете, что ваша реализация является ужасно неэффективной по сравнению с большинством встроенных операций и NumPy аналогичной сложности. 100Х ускорение было бы разумно для больших массивов после того, как вы устраните питон петли. Было бы тривиально, чтобы заменить на Python цикл с некоторыми включает индексацию или вещания, если бы не досадные draw_series[Р-1] = -(1 - max_draw) линии, которая действует на следующий-К-быть-вычисляемый элемент в массиве. Это аналог библиотеки numpy это накопить , но, очевидно, нет реализации его для конкретного алгоритма. У вас есть три варианта как я вижу:


  1. Исследование вашей проблемы жесткого и посмотреть, если вы разложите его в пакете numpy-только
    накапливать и регулярные операции.

  2. Смотрите, если ваш алгоритм может быть выражен в виде скомпилированного numexpr
    выражение.

  3. Скомпилировать эту функцию, используя на Cython, f2py или под

Одно незначительное улучшение заключается в замене возвращает = возвращает + 1 с возврат += 1 , которая будет работать на месте и избежать повторного выделения возвращает массив.

Надеюсь, что это помогает. удачи.

5
ответ дан 16 июля 2011 в 12:07 Источник Поделиться

Неопытный, наверное, не совсем правильно. Я думаю, что это может фактически выполнять операции в обратном направлении, но вы сможете легко сбыть.

import numpy as np

def max_drawdown(returns):
returns += 1
max_returns = np.maximum.accumulate(returns)
draw = returns / max_returns
max_draw = np.minimum.accumulate(draw)
draw_series = -(1 - max_draw)

return draw_series

Комментарии на ваш код:

import numpy as np

def max_drawdown(returns):

draw_series = np.array(np.ones(np.size(returns)))

НП.из них возвращает массив. Нет оснований для передачи его в НП.после этого массив. Если вы не собираетесь использовать те, которые вы храните в использовании и NumPy массива.пустой, который пропускает шага инициализации.

    max_return = 0; max_draw = 1; draw = 1

Вы объявляете рисовать далеко от того, где он используется. Просто назначить его в сферу своего применения. Несколько заданий на одной подкладке тоже одобряется в Python.

    returns = returns + 1

Использовать составное задание

    for r in range(returns.count()-1, 0, -1):

Я рекомендую против р, а не обычное сокращение, и я думаю, что это делает код трудно читать.

        if returns[r] > max_return:
max_return = returns[r]

else:
draw = returns[r] / max_return
if draw < max_draw:
max_draw = draw

Ваши математические кажется непостижимой, но, возможно, имеет смысл в контексте. Рассмотрим некоторые комментарии, чтобы объяснить рассуждения

        draw_series[r-1] = -(1 - max_draw)

return draw_series

2
ответ дан 18 июля 2011 в 03:07 Источник Поделиться

Не просто оптимизировать и оптимизировать, что догадки.

Узнайте, какие строки кода несут ответственность за большую часть времени,
как показано в этот ответ,
и сосредоточить свое внимание.

Затем, когда вы оптимизировали это, делать все это снова, пока вы не можете улучшить его больше.

1
ответ дан 15 июля 2011 в 09:07 Источник Поделиться

Если вам нужна высокая производительность кода на Python, вероятно, не правильный язык. Вы, кажется, не делать ничего, что гораздо более интенсивным, чем необходимо для достижения нужных вычислений, так что вряд ли можно увеличить производительность гораздо больше.

Если вы должны использовать Python для какой-либо причине (например, ближайшие ваши структуры данных из среды Python), вы всегда можете использовать глоток или подобный инструмент, чтобы написать программу на C, которая делает то, что вам нужно, и ссылку, что программы на Python. Это не будет стоить это, если вы работаете на очень большой набор данных. Другая возможность состоит в том, чтобы просто дамп данных в файл, процесс программу с него и свалить выходной файл, который затем может быть прочитан программой. Конечно, вы рискуете потратить больше времени на операции ввода/вывода, которые могут перевесить любые выгоды эффективности такого подхода.

Менее радикальное предложение: вы ожидаете, что если заявление здесь:

    if returns[r] > max_return:
max_return = returns[r]

будет верно только в редких случаях? Если это так, то попробуйте следующее. Я сомневаюсь, что это позволит существенно улучшить производительность, но это просто чтобы дать ему попробовать. Он также может сделать производительность хуже (Все зависит от вашего общего типа набора данных):

Изменить если-то:

    if returns[r] > max_return:
max_return_reciprocal = 1.0/returns[r]
else:
draw = returns[r] * max_return_reciprocal
#...then the if draw <

Это может избавить вас от многих плавающей точкой разделения, которые довольно медленно по сравнению с размножается. Это наверное не существенно выиграл, хотя повышения производительности, поскольку я ожидаю, что большинство медлительность происходит от накладных расходов, связанных с Python (интерпретации кода). Вы могли бы также хотеть смотреть на то, что именно эта линия делает:

draw_series = np.array(np.ones(np.size(returns)))

Вы можете время и посмотреть, если это вызывает проблемы с производительностью?

1
ответ дан 15 июля 2011 в 11:07 Источник Поделиться

Самый быстрый я могу сделать это с помощью Python, только немного меньше, чем в два раза превышает скорость перед. Моя лучшая попытка была

 def max_drawdown(returns):

draw_series = np.empty(returns.size)

max_return = 0; min_draw = 1; draw = 1
returns += 1.0

for r in range(returns.size,0,-1):
ret = returns[r-1]
if ret > max_return:
max_return = ret
else:
draw = ret / max_return
if draw < min_draw:
min_draw = draw

draw_series[r-1] = min_draw
returns -= 1.0 # otherwise side effects
data_series -= 1.0

return draw_series

Впрочем, следует отметить, что это попытки исправить некоторые вещи, которые выглядели странно в вашей функции, такие как draw_series / Макс-возвращает индекс смещения. Но они могут быть решены относительно легко.

НП.пустой: инициализация массива, но не удосужились установить внутри, так что вы сохранить цикл через массив, так как вам придется с НП.те.

возвращает +(-)= 1 изменяет значение возвращается на место, поэтому она не должна рассматриваться потокобезопасная функция с этим дополнением. Я обошел изменить так, что нет никаких побочных эффектов после выполнения завершена, но он по-прежнему представляет проблему, если вы планируете нить это.

draw_series - 1.0 выполняет ту же как min_draw - 1 установка в серии ничья, но некоторые, как кажется, заставить Python счастливее (или как он у вас -(1 - max_draw))

Я пытался как новый массив для хранения max_returns и выполнять их поэлементного в конце и хранение 1.0 / max_return значение и умножения, но каждый, казалось, замедляли выполнение по какой-то причине.

Не похоже, что итератор перечисляет(сторнируются(возвращает)) помогли с петли, хотя это упрощенная логика.

0
ответ дан 17 июля 2011 в 10:07 Источник Поделиться