Вычисление "Max Draw Down" в SQL

edit: стоит рассмотреть раздел комментариев первого ответа, чтобы получить более четкое представление о проблеме.

edit: Я использую SQLServer 2005

что-то похожее на это было опубликовано раньше, но я не думаю, что плакат дал достаточно информации, чтобы по-настоящему объяснить, что такое максимальное сокращение. Все мои определения максимального вытягивания происходят из (первых двух страниц) этой статьи: http://www.stat.columbia.edu/~vecer/maxdrawdown3.pdf

у вас есть несколько терминов, определенных математически:

Running Maximum, M t

M t= max u в [0, t] (S u)
где S t - цена запаса, S, в момент времени, t.

Просадка, D t

D t= M t - S t

Max Draw Down, MDD t

MDD t= max u в [0, t] (D u)

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

stockid int  
day date  
hi  int --this is in pennies  
low int --also in pennies  

поэтому для заданного диапазона дат каждый день будет отображаться тот же самый материал для этого диапазона.

EDIT:
hi и low являются высокими для дня и минимума для каждого дня.

после определения локального max и min, вы можете соединить каждый макс с каждым минусом, который приходит после него, и вычислить разницу. Из этого набора максимальной разницей будет "Макс. Ничья вниз".

Жесткая часть, однако, находит те max и min.

edit: следует отметить: максимальная просадка определяется как величина гипотетической потери, если акция куплена у нее наивысшей точкой покупки и продается на ней с понижением точки продажи. Запасы не могут быть проданы с минимальным значением, которое было до maxval. поэтому, если глобальное значение minval предшествует глобальному maxval, эти два значения не предоставляют достаточной информации для определения максимального просадки.

Ответ 1

Некоторые вещи, которые мы должны рассмотреть в проблемной области:

  • Акции имеют диапазон цен каждый день, часто просматриваемый в диаграммах подсвечников.
  • позволяет вызывать самую высокую цену за день HI
  • позволяет вызывать самую низкую цену за день LOW
  • проблема ограничена временем, даже если временные ограничения являются датой IPO и датами Delisting
  • максимальная просадка - это то, что вы могли бы потерять на складе за это время
  • предполагая стратегию LONG: логически, если мы сможем определить все локальные maxes (MAXES) и все локальные mins (MINS), мы могли бы определить набор, где мы свяжем каждый MAX с каждым последующим MIN и вычисляем разницу DIFFS
  • Иногда разница приводит к отрицательному числу, однако это не просадка
  • поэтому нам нужно выбрать append 0 в наборе diff и выбрать max

Проблема заключается в определении MAXES и MINS, с функцией кривой мы можем применить исчисление, bummer мы не можем. Очевидно,

  • максимальные значения должны поступать из HI и
  • MINS необходимо исходить от LOW

Один из способов решения этой проблемы - определить курсор и перетащить его. Функциональные языки имеют хорошие инструменты для решения этой проблемы.

Ответ 2

Жестоко неэффективная, но очень простая версия с использованием представления ниже:

WITH DDView
AS (SELECT      pd_curr.StockID,
                pd_curr.Date,
                pd_curr.Low_Price  AS CurrPrice,
                pd_prev.High_Price AS PrevPrice,
                pd_curr.Low_Price / pd_prev.High_Price - 1.0 AS DD

    FROM        PriceData pd_curr
    INNER JOIN  PriceData pd_prev
            ON  pd_curr.StockID = pd_prev.StockID
            AND pd_curr.Date >= pd_prev.Date
            AND pd_curr.Low_Price <= pd_prev.High_Price
            AND pd_prev.Date >= '2001-12-31' -- @param: min_date of analyzed period
    WHERE       pd_curr.Date <= '2010-09-31' -- @param: max_date of analyzed period
)
SELECT      dd.StockID,
            MIN(COALESCE(dd.DD, 0)) AS MaxDrawDown
FROM        DDView dd
GROUP BY    dd.StockID

Как обычно, вы проводите анализ в определенный период времени, имеет смысл обернуть запрос в хранимой процедуре параметрами @StartDate, @EndDate и, возможно, @StockID. Опять же, это довольно неэффективно по дизайну - O (N ^ 2), но если у вас хорошие индексы и не огромный объем данных, SQL Server справится с этим довольно неплохо.

Ответ 3

Для SQL Server и для одного запаса за раз, попробуйте следующее:

Create Procedure 'MDDCalc'(
    @StartDate date,
    @EndDate date,
    @Stock int)

AS

DECLARE @MinVal Int
DECLARE @MaxVal Int
DECLARE @MaxDate date

SET @MaxVal = (
    SELECT MAX(hi)
    FROM Table
    WHERE Stockid = @Stock 
    AND Day BETWEEN (@Startdate-1) AND (@EndDate+1))

SET @MaxDate=(
    SELECT Min(Date)
    FROM Table
    WHERE Stockid = @Stock
    AND hi = @MaxVal)

SET @MinVal = (
    SELECT MIN(low)
    FROM Table
    WHERE Stockid = @Stock 
    AND Day BETWEEN (@MaxDate-1) AND (@EndDate+1))

SELECT  (@[email protected]) AS 'MDD'

Ответ 4

Недавно я столкнулся с этой проблемой, Мое решение выглядит так: данные: 3,5,7,3, -1,3, -8, -3,0,10 добавьте сумму один за другим, если сумма отлична от 0, установите ее 0, иначе получите сумму, результат будет таким, как это 0,0,0,0, -1,0, -8, -11, -11, -1 Максимальное вытягивание - это самое низкое значение в данных, -11.

Ответ 5

Это то, что вам нужно?

select StockID,max(drawdown) maxdrawdown
from (
select h.StockID,h.day highdate,l.day lowdate,h.hi - l.lo drawdown
from mdd h
  inner join mdd l on h.StockID = l.StockID
    and h.day<l.day) x
group by StockID;

Это основанный на SQL подход грубой силы. Он сравнивает каждую низкую цену после сегодняшнего дня с ценой на одном и том же складе и находит наибольшую разницу между двумя ценами. Это будет максимальный Draw Down.

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

Ответ 6

Вот пользовательская функция SQL Server 2005, которая должна очень эффективно возвращать правильный ответ для одного элемента питания.

CREATE FUNCTION dbo.StockMaxDD(@StockID int, @day datetime) RETURNS int  AS
 BEGIN
    Declare @MaxVal int;    Set @MaxVal = 0;
    Declare @MaxDD int;     Set @MaxDD = 0;

    SELECT TOP(99999)
        @MaxDD  = CASE WHEN @MaxDD < (@MaxVal-low) THEN (@MaxVal-low)  ELSE @MaxDD END,
        @MaxVal = CASE WHEN hi  > @MaxVal THEN hi   ELSE @MaxVal END
    FROM    StockHiLo
    WHERE   stockid = @Stockid
      AND   [day]  <= @day
    ORDER BY [day] ASC

    RETURN @MaxDD;
 END

Это, однако, не будет очень эффективным для одновременного выполнения нескольких запасов. Если вам нужно сделать много/всех запасов сразу, то есть аналогичный, но существенно более сложный подход, который может сделать это очень эффективно.