OUTER APPLY is one of those SQL Server operators that can seem mysterious at first, but once you understand its power, it becomes an invaluable tool in your T-SQL arsenal. In this post I will try to demystify it using some fun examples with the stock market and trading.
What is OUTER APPLY?
OUTER APPLY is a table operator introduced in SQL Server
2005 that allows us to invoke a table-valued function for each row returned by
an outer table expression. Think of it as a 'for each row, do this' operator that can reference columns from the outer query.
The key difference between OUTER APPLY and CROSS APPLY is
that OUTER APPLY returns all rows from the left table expression, even if the
right table expression returns no rows (similar to LEFT JOIN vs INNER JOIN).
-- Get the 5 most recent trades for each symbolSELECTs.Symbol,s.CompanyName,s.Sector,t.TradeID,t.TradeDate,t.Price,t.Volume,t.Side -- BUY/SELLFROM dbo.Securities sOUTER APPLY (SELECT TOP 5TradeID,TradeDate,Price,Volume,SideFROM dbo.Trades tWHERE t.Symbol = s.SymbolORDER BY TradeDate DESC) t
-- Calculate VWAP and trading statistics for each symbolSELECTs.Symbol,s.LastPrice,stats.VWAP,stats.TotalVolume,stats.BuyVolume,stats.SellVolume,stats.ImbalanceRatioFROM dbo.Securities sOUTER APPLY (SELECTSUM(Price * Volume) / NULLIF(SUM(Volume), 0) AS VWAP,SUM(Volume) TotalVolume,SUM(CASE WHEN Side = 'BUY' THEN Volume ELSE 0 END) AS BuyVolume,SUM(CASE WHEN Side = 'SELL' THEN Volume ELSE 0 END) AS SellVolume,CAST(SUM(CASE WHEN Side = 'BUY' THEN Volume ELSE 0 END) as FLOAT) /NULLIF(SUM(CASE WHEN Side = 'SELL' THEN Volume ELSE 0 END), 0) AS ImbalanceRatioFROM dbo.Trades tWHERE t.Symbol = s.SymbolAND t.TradeDate >= DATEADD(HOUR, -1, GETDATE()) -- Last hour) stats
-- Find the next fill after each order placementSELECTo.OrderID,o.Symbol,o.OrderTime,o.OrderType,o.LimitPrice,fill.ExecutionTime,fill.FillPrice,fill.FillQuantity,DATEDIFF(MILLISECOND, o.OrderTime, fill.ExecutionTime) AS LatencyMsFROM dbo.Orders oOUTER APPLY (SELECT TOP 1e.ExecutionTime,e.Price as FillPrice,e.Quantity as FillQuantityFROM dbo.Executions eWHERE e.OrderID = o.OrderIDAND e.ExecutionTime >= o.OrderTimeORDER BY e.ExecutionTime) fillWHERE o.OrderDate = CAST(GETDATE() as DATE)
-- Get current positions with realized P&LSELECTa.AccountID,a.AccountName,pos.Symbol,pos.NetPosition,pos.AvgCost,pos.RealizedPnLFROM dbo.Accounts aOUTER APPLY (SELECTSymbol,SUM(CASE WHEN Side = 'BUY' THEN Quantity ELSE -Quantity END) AS NetPosition,AVG(CASE WHEN Side = 'BUY' THEN Price END) AS AvgCost,SUM(CASE WHEN Side = 'SELL' THEN (Price - Cost) * Quantity ELSE 0 END) AS RealizedPnLFROM dbo.Trades tWHERE t.AccountID = a.AccountIDAND t.TradeDate >= DATEADD(DAY, -30, GETDATE())GROUP BY SymbolHAVING SUM(CASE WHEN Side = 'BUY' THEN Quantity ELSE -Quantity END) <> 0) pos
-- Don't use OUTER APPLY for this:SELECT s.*, t.LastTradePriceFROM dbo.Securities sOUTER APPLY (SELECT Price as LastTradePriceFROM dbo.Trades tWHERE t.Symbol = s.Symbol) t-- Use a simple LEFT JOIN instead:SELECT s.*, t.Price as LastTradePriceFROM dbo.Securities s LEFT JOIN Trades tON s.Symbol = t.Symbol
-- Instead of OUTER APPLY for trade ranking, consider using ROW_NUMBER() with a CTEWITH RankedTrades AS (SELECTSymbol,TradeID,Price,Volume,TradeDate,ROW_NUMBER() OVER (PARTITION BY Symbol ORDER BY Volume DESC) AS VolumeRankFROM dbo.TradesWHERE TradeDate = CAST(GETDATE() as DATE))SELECT * FROM RankedTrades WHERE VolumeRank <= 10
-- Get bid/ask spread and depth for each symbolSELECTs.Symbol,s.LastPrice,depth.BestBid,depth.BestAsk,depth.Spread,depth.BidDepth,depth.AskDepthFROM dbo.Securities sOUTER APPLY (SELECTMAX(CASE WHEN Side = 'BUY' THEN Price END) AS BestBid,MIN(CASE WHEN Side = 'SELL' THEN Price END) AS BestAsk,MIN(CASE WHEN Side = 'SELL' THEN Price END) -MAX(CASE WHEN Side = 'BUY' THEN Price END) AS Spread,SUM(CASE WHEN Side = 'BUY' THEN Size ELSE 0 END) AS BidDepth,SUM(CASE WHEN Side = 'SELL' THEN Size ELSE 0 END) AS AskDepthFROM dbo.MarketDepth mdWHERE md.Symbol = s.SymbolAND md.UpdateTime >= DATEADD(SECOND, -1, GETDATE())) depthWHERE s.IsActive = 1
No comments:
Post a Comment