Dear Gabriel
Admittedly, utilising copy-paste to the hilt, I combined your solution with the technique used by Jeff Moden in his article "Solving the Running Total and Ordinal Rank Problems" - and came up with the following solution, which seems to work correctly. If you ask me what parts of the code are actually doing, embarrassingly I probably wouldn't be able to say!
Anyway, here goes:
DROP TABLE zpData_mod
---=====Setup working table for EMAs
SELECT ROW_NUMBER() OVER (PARTITION BY InstrumentID ORDER BY tdate)n
,InstrumentID
,tDate
,tClose
,CAST (NULL AS numeric(12,5)) [tCloseRunSum]
,CAST (NULL AS INT) [InstrumentRunCount]
,CAST (NULL AS numeric(12,5)) [EMA12]
INTO zpData_mod
FROM zpData
CREATE CLUSTERED INDEX ix_n ON zpData_mod (InstrumentID, tdate)
---declare variables needed
declare @ema_intervals int, @k1 decimal(4,3)
declare @prev_ema numeric(12,5), @initial_sma numeric(12,5)
declare @anchor int
DECLARE @PrevInstrumentID INT
DECLARE @tCloseRunSum numeric(12,5)
DECLARE @InstrumentRunCountINT
---Setup the default intervals
set @ema_intervals =12
set @k1 = 2/(1+@ema_intervals+.000)
--====Update the running total and running count using for this row
--using the "Quirky Update" and a "Pseudo-Cursor".
--The order of the UPDATE is controlled by the clustered index
UPDATE zpData_mod
SET @tCloseRunSum = tCloseRunSum = CASE
WHEN InstrumentID = @PrevInstrumentID
THEN @tCloseRunSum + tClose
ELSE tClose
END,
@InstrumentRunCount = InstrumentRunCount = CASE
WHEN InstrumentID = @PrevInstrumentID
THEN @InstrumentRunCount + 1
ELSE 1
END,
@initial_sma = ema12 = case when
N=@ema_intervals
then @tCloseRunSum/@InstrumentRunCount
else
null
end,
@PrevInstrumentID = InstrumentID
FROM zpData_mod WITH (TABLOCKX)
OPTION (MAXDOP 1)
update t1
---case statement to handle @moving_sum variable
---depending on the value of n
set
@prev_ema=case when n<@ema_intervals then null
when n=@ema_intervals then tCloseRunSum/InstrumentRunCount
when n>@ema_intervals then t1.tClose*@k1 +@prev_ema*(1-@k1)
end,
ema12=@prev_ema,
@anchor =N ---anchor so that the carryover works
FROM zpData_mod t1 with (tablockx)
option (maxdop 1)
select *
FROM zpData_mod
It is truly a privelege to be able to communicate with you!
Thank you
Stephen