1

我想知道一些新的 SQL Server 2012 函数是否有助于解决这个问题。这是我的 DDL 和示例数据

CREATE TABLE [dbo].[transactions]
(
    [transactionId] [int]            NOT NULL,
    [dt]            [datetime]       NOT NULL,
    [balance]       [decimal](22, 6) NULL
);    
GO

INSERT [dbo].[transactions] ([transactionId], [dt], [balance]) VALUES
 (174, CAST(0x0000A19600000000 AS DateTime), CAST(1000.000000 AS Decimal(22, 6))), 
 (178, CAST(0x0000A19700869520 AS DateTime), CAST(1100.000000 AS Decimal(22, 6))),
 (179, CAST(0x0000A19700933780 AS DateTime), CAST(1212.000000 AS Decimal(22, 6))),
 (180, CAST(0x0000A19700B4B9A0 AS DateTime), CAST(1342.000000 AS Decimal(22, 6))),
 (181, CAST(0x0000A19700BB0AD0 AS DateTime), CAST(1198.000000 AS Decimal(22, 6))),
 (182, CAST(0x0000A19700E67030 AS DateTime), CAST(1234.000000 AS Decimal(22, 6))),
 (183, CAST(0x0000A19700F358E0 AS DateTime), CAST(900.000000  AS Decimal(22, 6))),
 (184, CAST(0x0000A19700F58B60 AS DateTime), CAST(876.000000  AS Decimal(22, 6))),
 (185, CAST(0x0000A19700F9AA10 AS DateTime), CAST(889.000000  AS Decimal(22, 6))),
 (186, CAST(0x0000A19701034700 AS DateTime), CAST(1133.000000 AS Decimal(22, 6))),
 (187, CAST(0x0000A19A0089E0E0 AS DateTime), CAST(1400.000000 AS Decimal(22, 6))),
 (191, CAST(0x0000A19A009450C0 AS DateTime), CAST(1566.000000 AS Decimal(22, 6))),
 (192, CAST(0x0000A19A00A5E4C0 AS DateTime), CAST(1800.000000 AS Decimal(22, 6))),
 (188, CAST(0x0000A19A00AA49C0 AS DateTime), CAST(1900.000000 AS Decimal(22, 6))),
 (189, CAST(0x0000A19A00B54640 AS DateTime), CAST(1456.000000 AS Decimal(22, 6))),
 (190, CAST(0x0000A19A00CAB2A0 AS DateTime), CAST(1234.000000 AS Decimal(22, 6))),
 (193, CAST(0x0000A19A00F12660 AS DateTime), CAST(1400.000000 AS Decimal(22, 6))),
 (195, CAST(0x0000A19A010087E0 AS DateTime), CAST(1444.000000 AS Decimal(22, 6))),
 (196, CAST(0x0000A19E00C7F380 AS DateTime), CAST(1556.000000 AS Decimal(22, 6))),
 (197, CAST(0x0000A19E00FE5560 AS DateTime), CAST(1975.000000 AS Decimal(22, 6)));

我追求的是该系列余额中最大百分比的峰谷回撤,由 dt 排序。峰谷回撤是在突破前一个高点之前,余额中的最高点到最低点的最大百分比变化。在这里更好地描述http://www.investopedia.com/terms/p/peak-to-valley-drawdown.asp 在这个数据集中,我们有两个回撤。

第一个是从 1342.00 到 876.00 (-34.72%),第二个是从 1900 到 1234 (-35.05%)

因此,该组中最大的峰谷百分比回撤为 -35.05%。我需要一个可以提供此值的 SQL Server 查询。如果可能,宁愿不必使用临时表。有任何想法吗?

4

3 回答 3

2

我不知道任何 SQL Server 2012 功能会比这更简洁或更有效地获得这个值:

;WITH x AS
(
  SELECT [drop] = ((s.balance-e.balance)*100.0/s.balance)
    FROM dbo.transactions AS s
    INNER JOIN dbo.transactions AS e
    ON s.transactionId < e.transactionId
    AND s.balance > e.balance
)
SELECT [Largest Drawdown] = -MAX([drop]) FROM x;

结果:

Largest Drawdown
----------------
-35.05263157894

不过,我承认,这仅适用于您的样本数据,因为您的谷值便于您解决要解决的问题。如果将倒数第 4 行更改为 875,则此查询将其视为集合的一部分。换句话说,我在这里计算了整个范围的回撤,而不仅仅是再次突破高点之前的范围。

我怀疑有更好的方法可以使用间隙/孤岛技术解决此查询,当我可以充分关注它时,我会尝试返回它。

于 2013-04-14T20:12:02.573 回答
1

如果第一个条目是峰值,这将丢失

 ;with trnsCTE (ID,bal) AS
  (  -- get seqential ID
        SELECT ROW_NUMBER() OVER (ORDER BY DT) as ID, [balance] 
        from [transactions] 
  ),
  trnsCTE2 (ID,bal) AS
  ( -- any peaks
       select t2.ID, t2.bal 
       from  trnsCTE as T1
       join  trnsCTE as T2
         on  ( t2.ID = t1.ID+1  
        and  t2.bal > t1.bal )
       join  trnsCTE as T3
         on  t3.ID = t2.ID+1
        and  t3.bal < t1.bal
   )
   ,
   trnsCTE3 (ID,bal) AS
   (  --  get first peak and then bigger peaks only
       SELECT distinct T1.ID, T1.BAL
       from  trnsCTE2 as T1
       where T1.ID = (select min(ID) from trnsCTE2) 
         or T1.bal > (select max(bal) from trnsCTE2 where trnsCTE2.ID < t1.ID)
   )
   -- calculate 
   select t1.id, t1.bal, min(trnsCTE.bal), (t1.bal - min(trnsCTE.bal)) * 100 / t1.bal
   from trnsCTE
   join trnsCTE3 t1
     on t1.id < trnsCTE.id 
    and ( trnsCTE.id < (select min(id) from trnsCTE3 where id > t1.id)
          or
          t1.id = ( select max(id) from trnsCTE3 ) )
   group by t1.id, t1.bal 
   order by t1.id

这直接转换为#temps OP 中没有使用#temp 表示不想使用#temp

insert into #trnsCTE (ID,bal)
SELECT ROW_NUMBER() OVER (ORDER BY DT) as ID, [balance] 
from [transactions]
于 2013-04-14T21:53:48.840 回答
0
select peak_dt, peak_balance, trough_dt, trough_balance, (peak_balance - trough_balance) * 100.0 / peak_balance as drawdown
from (
    select dt as peak_dt, balance as peak_balance, nullif(last_value(dt) over (partition by peak_valley_group order by dt rows between unbounded preceding and unbounded following), dt) as trough_dt, nullif(last_value(balance) over (partition by peak_valley_group order by dt rows between unbounded preceding and unbounded following), balance) as trough_balance, isPeak
    from (
        select *, sum(isPeak) over (order by dt) as peak_valley_group
        from (
            select dt, balance, (case when forward_trend = -1 then 1 else 0 end) as isPeak, max(balance) over (partition by forward_trend order by dt) as current_max_balance
            from (
                -- Nulls for lead/lag here produce the desired result
                select *, (case when lead(balance, 1) over (order by dt) > balance then 1 else -1 end) as forward_trend, (case when lag(balance, 1) over (order by dt) > balance then 1 else -1 end) as backward_trend
                from transactions
            ) t
            where forward_trend = backward_trend
        ) t
        where (isPeak = 1 and balance = current_max_balance) 
        or isPeak = 0
    ) t
) t
where isPeak = 1
order by peak_dt
于 2013-04-15T11:48:25.120 回答