Logo

SQL Server WAC Function

Updated 2023-11-13 17:08:55.470000

Description

Use WAC to calculate running weighted average cost values in an ordered resultant table, without the need for a self-join. WAC calculates balances for each value from the first value to the last value in the ordered group or partition. WAC can return the quantity on-hand, the inventory value, the gross margin on sale, the gross margin percentage, the cost of goods sold, the average inventory price, the last inventory price, the cumulative cost of goods sold, the cumulative gross margin on sale, and the cumulative gross margin percentage.

WAC supports both long and short inventory positions. In other words, if the quantity on hand falls below the zero, WAC calculates from the last sale or withdrawal transaction, rather than from the last purchases or additions to inventory.

WAC assumes that the quantity (i.e. the number of units) of the transaction and the monetary value of the transaction have the same sign. WAC adds NULL values to the inventory at the last price.

WAC requires monotonically ascending row numbers within a partition; otherwise the function returns an error message

WAC resets all calculated values when the row number value is 1.

Syntax

SELECT [westclintech].[wct].[WAC](
  <@Qty, float,>
 ,<@Cost, float,>
 ,<@RV, nvarchar(4000),>
 ,<@Round, int,>
 ,<@RowNum, int,>
 ,<@Id, tinyint,>)

Arguments

@Qty

the number of units being added to or subtracted from inventory. @Qty is an expression of type float or of a type that can be implicitly converted to float.

@Cost

the value associated with @Qty. Additions to inventory should have a @Cost > 0; withdrawals from inventory should have a cost <= 0. If you are not interested in calculating the gross margin on sales or if the quantity on hand will never be less than zero, then just enter zero for the @Cost when the @Qty is less than zero. @Cost is an expression of type float or of a type that can be implicitly converted to float.

@RV

the calculated value returned by the function. Permissible values are:

column 1column 2
'Q' , 'QTY'Quantity on hand
'B' , 'EV' , 'EB'Inventory value
'G' , 'GM'Gross margin
'C' , 'COG' , 'COGS'Cost of goods sold
'U' , 'UP'(Average) Unit price
'L' , 'LP'Last price
'CC' , 'COGC' , 'COGSC'Cumulative cost of goods sold
'GC' , 'GMC'Cumulative gross margin
'GP' , 'GMP'Gross margin percentage
'CGP' , 'CGMP'Cumulative gross margin percentage

@Round

the number of decimals places to store the result. @Round is only used in the calculation of the gross margin. @Round is an expression of type int or of a type that can be implicitly converted to int.

@RowNum

the number of the row within the group for which the sum is being calculated. If @RowNum for the current row in a set is less than or equal to the previous @RowNum and @RowNum is not equal to 1, an error message will be generated. @RowNum is an expression of type int or of a type that can be implicitly converted to int.

@Id

a unique identifier for the WAC calculation. @Id allows you to specify multiple moving sums within a resultant table. @Id is an expression of type tinyint or of a type that can be implicitly converted to tinyint.

Return Type

float

Remarks

If @Id is NULL then @Id = 0.

To calculate LIFO values, use the LIFO function.

To calculate FIFO values, use the FIFO function.

If @RowNum is equal to 1, then cost of goods sold = 0, gross margin = 0, quantity on hand = @Qty, and inventory balance = @Cost.

@RowNum must be in ascending order.

There may be cases where the order in which the data are returned to the function and the order in which the results are returned are different, generally due to parallelism. You can use OPTION(MAXDOP 1) or OPTION(MAXDOP 1,FORCE ORDER) to help eliminate this problem.

Examples

In the following examples, we calculate FIFO inventory values for stock trades in the ABC, XYZ, and GHI companies. We will create a temporary table, #c and populate it with some data. We can be either short or long the shares at any point in time.

--Create the temporary table
CREATE TABLE #c
(
    trn int,
    sym char(3),
    tDate date,
    qty money,
    price_unit money,
    price_extended money,
    PRIMARY KEY (trn)
);
--Populate the table with some data
INSERT INTO #c
VALUES
(01131019, 'XYZ', '2013-10-19', 424, 25.13, 10655.12);
INSERT INTO #c
VALUES
(02130617, 'ABC', '2013-06-17', 313, 12.93, 4047.09);
INSERT INTO #c
VALUES
(03130308, 'ABC', '2013-03-08', -157, 13.17, -2067.69);
INSERT INTO #c
VALUES
(04130516, 'GHI', '2013-05-16', 160, 34.48, 5516.8);
INSERT INTO #c
VALUES
(05130706, 'XYZ', '2013-07-06', -170, 23.46, -3988.2);
INSERT INTO #c
VALUES
(06130924, 'GHI', '2013-09-24', 328, 34.95, 11463.6);
INSERT INTO #c
VALUES
(07130722, 'ABC', '2013-07-22', 599, 13.65, 8176.35);
INSERT INTO #c
VALUES
(08131231, 'ABC', '2013-12-31', -145, 13.19, -1912.55);
INSERT INTO #c
VALUES
(09131025, 'XYZ', '2013-10-25', -153, 24.31, -3719.43);
INSERT INTO #c
VALUES
(10130908, 'ABC', '2013-09-08', -386, 13.65, -5268.9);
INSERT INTO #c
VALUES
(11130906, 'XYZ', '2013-09-06', -13, 23.97, -311.61);
INSERT INTO #c
VALUES
(12130621, 'ABC', '2013-06-21', -326, 12.73, -4149.98);
INSERT INTO #c
VALUES
(13131221, 'GHI', '2013-12-21', 72, 34.38, 2475.36);
INSERT INTO #c
VALUES
(14130705, 'XYZ', '2013-07-05', -277, 25.01, -6927.77);
INSERT INTO #c
VALUES
(15130307, 'GHI', '2013-03-07', 559, 35.21, 19682.39);
INSERT INTO #c
VALUES
(16131107, 'ABC', '2013-11-07', 27, 12.68, 342.36);
INSERT INTO #c
VALUES
(17130924, 'GHI', '2013-09-24', -291, 35.69, -10385.79);
INSERT INTO #c
VALUES
(18140125, 'GHI', '2014-01-25', -78, 35.46, -2765.88);
INSERT INTO #c
VALUES
(19130516, 'XYZ', '2013-05-16', 315, 23.57, 7424.55);
INSERT INTO #c
VALUES
(20130518, 'ABC', '2013-05-18', 298, 13.23, 3942.54);
INSERT INTO #c
VALUES
(21131103, 'XYZ', '2013-11-03', -326, 23.24, -7576.24);
INSERT INTO #c
VALUES
(22131012, 'XYZ', '2013-10-12', 596, 23.16, 13803.36);
INSERT INTO #c
VALUES
(23130619, 'XYZ', '2013-06-19', 296, 23.46, 6944.16);
INSERT INTO #c
VALUES
(24130418, 'XYZ', '2013-04-18', 275, 24.83, 6828.25);
INSERT INTO #c
VALUES
(25130408, 'ABC', '2013-04-08', 298, 12.98, 3868.04);
INSERT INTO #c
VALUES
(26130320, 'ABC', '2013-03-20', -92, 13.64, -1254.88);
INSERT INTO #c
VALUES
(27130906, 'XYZ', '2013-09-06', -147, 23.81, -3500.07);
INSERT INTO #c
VALUES
(28131209, 'XYZ', '2013-12-09', 315, 24.46, 7704.9);
INSERT INTO #c
VALUES
(29130602, 'XYZ', '2013-06-02', 114, 24.29, 2769.06);
INSERT INTO #c
VALUES
(30130519, 'XYZ', '2013-05-19', 467, 23.15, 10811.05);
INSERT INTO #c
VALUES
(31140205, 'XYZ', '2014-02-05', 42, 24.39, 1024.38);
INSERT INTO #c
VALUES
(32130310, 'ABC', '2013-03-10', -63, 12.61, -794.43);
INSERT INTO #c
VALUES
(33140102, 'XYZ', '2014-01-02', -196, 22.98, -4504.08);
INSERT INTO #c
VALUES
(34130507, 'XYZ', '2013-05-07', 55, 23.43, 1288.65);
INSERT INTO #c
VALUES
(35130321, 'XYZ', '2013-03-21', 275, 24.83, 6828.25);
INSERT INTO #c
VALUES
(36130917, 'XYZ', '2013-09-17', 92, 24.6, 2263.2);
INSERT INTO #c
VALUES
(37130220, 'XYZ', '2013-02-20', 528, 23.54, 12429.12);
INSERT INTO #c
VALUES
(38130311, 'XYZ', '2013-03-11', -193, 24.9, -4805.7);
INSERT INTO #c
VALUES
(39130908, 'ABC', '2013-09-08', 490, 12.69, 6218.1);
INSERT INTO #c
VALUES
(40131013, 'XYZ', '2013-10-13', 359, 23.91, 8583.69);
INSERT INTO #c
VALUES
(41130310, 'ABC', '2013-03-10', 463, 13.38, 6194.94);
INSERT INTO #c
VALUES
(42131011, 'XYZ', '2013-10-11', -250, 23.12, -5780);
INSERT INTO #c
VALUES
(43130521, 'GHI', '2013-05-21', -174, 34.2, -5950.8);
INSERT INTO #c
VALUES
(44130227, 'XYZ', '2013-02-27', 357, 22.86, 8161.02);
INSERT INTO #c
VALUES
(45131030, 'XYZ', '2013-10-30', -350, 23.36, -8176);
INSERT INTO #c
VALUES
(46130301, 'ABC', '2013-03-01', 157, 13.01, 2042.57);
INSERT INTO #c
VALUES
(47130619, 'XYZ', '2013-06-19', 413, 25.18, 10399.34);
INSERT INTO #c
VALUES
(48130430, 'ABC', '2013-04-30', 229, 13.32, 3050.28);
INSERT INTO #c
VALUES
(49130508, 'ABC', '2013-05-08', 238, 12.79, 3044.02);
INSERT INTO #c
VALUES
(50131103, 'GHI', '2013-11-03', -246, 35.61, -8760.06);
INSERT INTO #c
VALUES
(51131206, 'GHI', '2013-12-06', 85, 33.64, 2859.4);
INSERT INTO #c
VALUES
(52131014, 'GHI', '2013-10-14', -91, 33.12, -3013.92);
INSERT INTO #c
VALUES
(53140102, 'GHI', '2014-01-02', 396, 35.52, 14065.92);
INSERT INTO #c
VALUES
(54130831, 'XYZ', '2013-08-31', -61, 23.23, -1417.03);
INSERT INTO #c
VALUES
(55130630, 'XYZ', '2013-06-30', -272, 23.45, -6378.4);
INSERT INTO #c
VALUES
(56130419, 'GHI', '2013-04-19', 416, 34.46, 14335.36);
INSERT INTO #c
VALUES
(57130813, 'XYZ', '2013-08-13', -163, 23.65, -3854.95);
INSERT INTO #c
VALUES
(58130722, 'XYZ', '2013-07-22', -88, 24.64, -2168.32);
INSERT INTO #c
VALUES
(59130320, 'XYZ', '2013-03-20', -20, 23.59, -471.8);
INSERT INTO #c
VALUES
(60130419, 'XYZ', '2013-04-19', 277, 22.83, 6323.91);
INSERT INTO #c
VALUES
(61130916, 'ABC', '2013-09-16', -202, 12.94, -2613.88);
INSERT INTO #c
VALUES
(62131027, 'XYZ', '2013-10-27', -248, 24.71, -6128.08);
INSERT INTO #c
VALUES
(63130806, 'GHI', '2013-08-06', 445, 33.5, 14907.5);
INSERT INTO #c
VALUES
(64140109, 'XYZ', '2014-01-09', -253, 24.46, -6188.38);
INSERT INTO #c
VALUES
(65131227, 'GHI', '2013-12-27', 376, 33.49, 12592.24);
INSERT INTO #c
VALUES
(66140203, 'XYZ', '2014-02-03', 459, 23.7, 10878.3);
INSERT INTO #c
VALUES
(67130302, 'XYZ', '2013-03-02', 9, 24.04, 216.36);
INSERT INTO #c
VALUES
(68130223, 'ABC', '2013-02-23', 238, 12.78, 3041.64);
INSERT INTO #c
VALUES
(69130403, 'GHI', '2013-04-03', -151, 33.16, -5007.16);
INSERT INTO #c
VALUES
(70130702, 'GHI', '2013-07-02', -162, 35.48, -5747.76);
INSERT INTO #c
VALUES
(71130731, 'ABC', '2013-07-31', 79, 13.55, 1070.45);
INSERT INTO #c
VALUES
(72140204, 'XYZ', '2014-02-04', -208, 24.36, -5066.88);
INSERT INTO #c
VALUES
(73131028, 'GHI', '2013-10-28', -46, 34.65, -1593.9);
INSERT INTO #c
VALUES
(74130619, 'XYZ', '2013-06-19', 202, 22.95, 4635.9);
INSERT INTO #c
VALUES
(75131216, 'GHI', '2013-12-16', 500, 33.55, 16775);
INSERT INTO #c
VALUES
(76131009, 'ABC', '2013-10-09', 249, 13.47, 3354.03);
INSERT INTO #c
VALUES
(77130825, 'GHI', '2013-08-25', 272, 33.86, 9209.92);
INSERT INTO #c
VALUES
(78140112, 'XYZ', '2014-01-12', 332, 24.28, 8060.96);
INSERT INTO #c
VALUES
(79131223, 'XYZ', '2013-12-23', -376, 25.12, -9445.12);
INSERT INTO #c
VALUES
(80140126, 'XYZ', '2014-01-26', 404, 24.23, 9788.92);
INSERT INTO #c
VALUES
(81131123, 'GHI', '2013-11-23', 187, 33.86, 6331.82);
INSERT INTO #c
VALUES
(82140131, 'XYZ', '2014-01-31', 548, 23.65, 12960.2);
INSERT INTO #c
VALUES
(83130428, 'XYZ', '2013-04-28', 142, 23.13, 3284.46);
INSERT INTO #c
VALUES
(84140104, 'XYZ', '2014-01-04', 261, 24.46, 6384.06);
INSERT INTO #c
VALUES
(85131002, 'GHI', '2013-10-02', -295, 32.51, -9590.45);
INSERT INTO #c
VALUES
(86131114, 'ABC', '2013-11-14', -386, 12.4, -4786.4);
INSERT INTO #c
VALUES
(87131116, 'GHI', '2013-11-16', -320, 34.16, -10931.2);
INSERT INTO #c
VALUES
(88131009, 'ABC', '2013-10-09', 187, 13.36, 2498.32);
INSERT INTO #c
VALUES
(89130611, 'ABC', '2013-06-11', 130, 13.02, 1692.6);
INSERT INTO #c
VALUES
(90130430, 'ABC', '2013-04-30', -191, 13.49, -2576.59);
INSERT INTO #c
VALUES
(91130615, 'XYZ', '2013-06-15', 545, 23.88, 13014.6);
INSERT INTO #c
VALUES
(92130924, 'XYZ', '2013-09-24', -248, 24.58, -6095.84);
INSERT INTO #c
VALUES
(93130622, 'ABC', '2013-06-22', -227, 13.47, -3057.69);
INSERT INTO #c
VALUES
(94131117, 'GHI', '2013-11-17', -92, 32.65, -3003.8);
INSERT INTO #c
VALUES
(95130908, 'GHI', '2013-09-08', 103, 35.42, 3648.26);
INSERT INTO #c
VALUES
(96130716, 'GHI', '2013-07-16', -347, 35.29, -12245.63);
INSERT INTO #c
VALUES
(97131125, 'XYZ', '2013-11-25', -278, 23.18, -6444.04);
INSERT INTO #c
VALUES
(98130413, 'XYZ', '2013-04-13', 243, 24.25, 5892.75);
INSERT INTO #c
VALUES
(99130515, 'XYZ', '2013-05-15', 97, 25.13, 2437.61);
--Calculate the WAC values
SELECT *,
       wct.WAC(   qty,                                                           
                 --@Qty
                  price_extended,                                                
                            --@Cost
                  'QTY',                                                         
                            --@RV
                  2,                                                             
                            --@Round
                  ROW_NUMBER() OVER (PARTITION BY sym ORDER BY sym, tDate, trn), 
                            --@RowNum
                  0                                                              
                            --@Id
              ) as [Quantity on Hand],
       wct.WAC(   qty,                                                           
                 --@Qty
                  price_extended,                                                
                            --@Cost
                  'EV',                                                          
                            --@RV
                  2,                                                             
                            --@Round
                  ROW_NUMBER() OVER (PARTITION BY sym ORDER BY sym, tDate, trn), 
                            --@RowNum
                  1                                                              
                            --@Id
              ) as [Inventory Value]
FROM #c
ORDER BY sym,
         tDate,
         trn;
DROP TABLE #c;

This produces the following result.

trnsymDateqtyrice_unitrice_extendedn HandValue
68130223ABC2013-02-2323812.783041.642383041.64
46130301ABC2013-03-0115713.012042.573955084.21
3130308ABC2013-03-08-15713.17-2067.692383063.40
32130310ABC2013-03-10-6312.61-794.431752252.50
41130310ABC2013-03-1046313.386194.946388447.44
26130320ABC2013-03-20-9213.64-1254.885467229.31
25130408ABC2013-04-0829812.983868.0484411097.35
48130430ABC2013-04-3022913.323050.28107314147.63
90130430ABC2013-04-30-19113.49-2576.5988211629.27
49130508ABC2013-05-0823812.793044.02112014673.29
20130518ABC2013-05-1829813.233942.54141818615.83
89130611ABC2013-06-1113013.021692.60154820308.43
2130617ABC2013-06-1731312.934047.09186124355.52
12130621ABC2013-06-21-32612.73-4149.98153520089.05
93130622ABC2013-06-22-22713.47-3057.69130817118.23
7130722ABC2013-07-2259913.658176.35190725294.58
71130731ABC2013-07-317913.551070.45198626365.03
10130908ABC2013-09-08-38613.65-5268.90160021240.71
39130908ABC2013-09-0849012.696218.10209027458.81
61130916ABC2013-09-16-20212.94-2613.88188824804.90
76131009ABC2013-10-0924913.473354.03213728158.93
88131009ABC2013-10-0918713.362498.32232430657.25
16131107ABC2013-11-072712.68342.36235130999.61
86131114ABC2013-11-14-38612.40-4786.40196525909.92
8131231ABC2013-12-31-14513.19-1912.55182023997.99
15130307GHI2013-03-0755935.2119682.3955919682.39
69130403GHI2013-04-03-15133.16-5007.1640814365.68
56130419GHI2013-04-1941634.4614335.3682428701.04
4130516GHI2013-05-1616034.485516.8098434217.84
43130521GHI2013-05-21-17434.20-5950.8081028167.12
70130702GHI2013-07-02-16235.48-5747.7664822533.70
96130716GHI2013-07-16-34735.29-12245.6330110467.04
63130806GHI2013-08-0644533.5014907.5074625374.54
77130825GHI2013-08-2527233.869209.92101834584.46
95130908GHI2013-09-0810335.423648.26112138232.72
6130924GHI2013-09-2432834.9511463.60144949696.32
17130924GHI2013-09-24-29135.69-10385.79115839715.90
85131002GHI2013-10-02-29532.51-9590.4586329598.29
52131014GHI2013-10-14-9133.12-3013.9277226477.27
73131028GHI2013-10-28-4634.65-1593.9072624899.61
50131103GHI2013-11-03-24635.61-8760.0648016462.55
87131116GHI2013-11-16-32034.16-10931.201605487.52
94131117GHI2013-11-17-9232.65-3003.80682332.20
81131123GHI2013-11-2318733.866331.822558664.02
51131206GHI2013-12-068533.642859.4034011523.42
75131216GHI2013-12-1650033.5516775.0084028298.42
13131221GHI2013-12-217234.382475.3691230773.78
65131227GHI2013-12-2737633.4912592.24128843366.02
53140102GHI2014-01-0239635.5214065.92168457431.94
18140125GHI2014-01-25-7835.46-2765.88160654771.79
37130220XYZ2013-02-2052823.5412429.1252812429.12
44130227XYZ2013-02-2735722.868161.0288520590.14
67130302XYZ2013-03-02924.04216.3689420806.50
38130311XYZ2013-03-11-19324.90-4805.7070116314.72
59130320XYZ2013-03-20-2023.59-471.8068115849.25
35130321XYZ2013-03-2127524.836828.2595622677.50
98130413XYZ2013-04-1324324.255892.75119928570.25
24130418XYZ2013-04-1827524.836828.25147435398.50
60130419XYZ2013-04-1927722.836323.91175141722.41
83130428XYZ2013-04-2814223.133284.46189345006.87
34130507XYZ2013-05-075523.431288.65194846295.52
99130515XYZ2013-05-159725.132437.61204548733.13
19130516XYZ2013-05-1631523.577424.55236056157.68
30130519XYZ2013-05-1946723.1510811.05282766968.73
29130602XYZ2013-06-0211424.292769.06294169737.79
91130615XYZ2013-06-1554523.8813014.60348682752.39
23130619XYZ2013-06-1929623.466944.16378289696.55
47130619XYZ2013-06-1941325.1810399.344195100095.8
74130619XYZ2013-06-1920222.954635.904397104731.7
55130630XYZ2013-06-30-27223.45-6378.40412598253.04
14130705XYZ2013-07-05-27725.01-6927.77384891655.20
5130706XYZ2013-07-06-17023.46-3988.20367887605.98
58130722XYZ2013-07-22-8824.64-2168.32359085509.92
57130813XYZ2013-08-13-16323.65-3854.95342781627.44
54130831XYZ2013-08-31-6123.23-1417.03336680174.49
11130906XYZ2013-09-06-1323.97-311.61335379864.84
27130906XYZ2013-09-06-14723.81-3500.07320676363.46
36130917XYZ2013-09-179224.602263.20329878626.66
92130924XYZ2013-09-24-24824.58-6095.84305072714.16
42131011XYZ2013-10-11-25023.12-5780.00280066753.98
22131012XYZ2013-10-1259623.1613803.36339680557.34
40131013XYZ2013-10-1335923.918583.69375589141.03
1131019XYZ2013-10-1942425.1310655.12417999796.15
9131025XYZ2013-10-25-15324.31-3719.43402696142.45
62131027XYZ2013-10-27-24824.71-6128.08377890220.11
45131030XYZ2013-10-30-35023.36-8176.00342881861.97
21131103XYZ2013-11-03-32623.24-7576.24310274076.96
97131125XYZ2013-11-25-27823.18-6444.04282467438.21
28131209XYZ2013-12-0931524.467704.90313975143.11
79131223XYZ2013-12-23-37625.12-9445.12276366142.22
33140102XYZ2014-01-02-19622.98-4504.08256761450.26
84140104XYZ2014-01-0426124.466384.06282867834.32
64140109XYZ2014-01-09-25324.46-6188.38257561765.69
78140112XYZ2014-01-1233224.288060.96290769826.65
80140126XYZ2014-01-2640424.239788.92331179615.57
82140131XYZ2014-01-3154823.6512960.20385992575.77
66140203XYZ2014-02-0345923.7010878.304318103454.0
72140204XYZ2014-02-04-20824.36-5066.88411098470.64
31140205XYZ2014-02-054224.391024.38415299495.02

Using the #c table, we can generate a report showing the gross margin on sales and cost of goods sold.

SELECT sym,
       tDate,
       qty,
       price_extended,
       wct.WAC(qty, price_extended, 'GM', 2, ROW_NUMBER() OVER (PARTITION BY sym 
                 ORDER BY sym, tDate, trn), 0) as [Gross Margin],
       wct.WAC(qty, price_extended, 'COGS', 2, ROW_NUMBER() OVER (PARTITION BY sym 
                 ORDER BY sym, tDate, trn), 1) as [COGS]
FROM #c
ORDER BY sym,
         tDate,
         trn;

This produces the following result.

symtDateqtyprice_extendedGross MarginCOGS
ABC2013-02-232383041.6400
ABC2013-03-011572042.5700
ABC2013-03-08-157-2067.6946.88-2020.81
ABC2013-03-10-63-794.43-16.47-810.9
ABC2013-03-104636194.9400
ABC2013-03-20-92-1254.8836.75-1218.13
ABC2013-04-082983868.0400
ABC2013-04-302293050.2800
ABC2013-04-30-191-2576.5958.23-2518.36
ABC2013-05-082383044.0200
ABC2013-05-182983942.5400
ABC2013-06-111301692.600
ABC2013-06-173134047.0900
ABC2013-06-21-326-4149.98-116.49-4266.47
ABC2013-06-22-227-3057.6986.87-2970.82
ABC2013-07-225998176.3500
ABC2013-07-31791070.4500
ABC2013-09-08-386-5268.9144.58-5124.32
ABC2013-09-084906218.100
ABC2013-09-16-202-2613.88-40.03-2653.91
ABC2013-10-092493354.0300
ABC2013-10-091872498.3200
ABC2013-11-0727342.3600
ABC2013-11-14-386-4786.4-303.29-5089.69
ABC2013-12-31-145-1912.550.62-1911.93
GHI2013-03-0755919682.3900
GHI2013-04-03-151-5007.16-309.55-5316.71
GHI2013-04-1941614335.3600
GHI2013-05-161605516.800
GHI2013-05-21-174-5950.8-99.92-6050.72
GHI2013-07-02-162-5747.76114.34-5633.42
GHI2013-07-16-347-12245.63178.97-12066.66
GHI2013-08-0644514907.500
GHI2013-08-252729209.9200
GHI2013-09-081033648.2600
GHI2013-09-2432811463.600
GHI2013-09-24-291-10385.79405.37-9980.42
GHI2013-10-02-295-9590.45-527.16-10117.61
GHI2013-10-14-91-3013.92-107.1-3121.02
GHI2013-10-28-46-1593.916.24-1577.66
GHI2013-11-03-246-8760.06323-8437.06
GHI2013-11-16-320-10931.2-43.83-10975.03
GHI2013-11-17-92-3003.8-151.52-3155.32
GHI2013-11-231876331.8200
GHI2013-12-06852859.400
GHI2013-12-165001677500
GHI2013-12-21722475.3600
GHI2013-12-2737612592.2400
GHI2014-01-0239614065.9200
GHI2014-01-25-78-2765.88105.73-2660.15
XYZ2013-02-2052812429.1200
XYZ2013-02-273578161.0200
XYZ2013-03-029216.3600
XYZ2013-03-11-193-4805.7313.92-4491.78
XYZ2013-03-20-20-471.86.33-465.47
XYZ2013-03-212756828.2500
XYZ2013-04-132435892.7500
XYZ2013-04-182756828.2500
XYZ2013-04-192776323.9100
XYZ2013-04-281423284.4600
XYZ2013-05-07551288.6500
XYZ2013-05-15972437.6100
XYZ2013-05-163157424.5500
XYZ2013-05-1946710811.0500
XYZ2013-06-021142769.0600
XYZ2013-06-1554513014.600
XYZ2013-06-192966944.1600
XYZ2013-06-1941310399.3400
XYZ2013-06-192024635.900
XYZ2013-06-30-272-6378.4-100.35-6478.75
XYZ2013-07-05-277-6927.77329.93-6597.84
XYZ2013-07-06-170-3988.2-61.02-4049.22
XYZ2013-07-22-88-2168.3272.26-2096.06
XYZ2013-08-13-163-3854.95-27.53-3882.48
XYZ2013-08-31-61-1417.03-35.92-1452.95
XYZ2013-09-06-13-311.611.96-309.65
XYZ2013-09-06-147-3500.07-1.31-3501.38
XYZ2013-09-17922263.200
XYZ2013-09-24-248-6095.84183.34-5912.5
XYZ2013-10-11-250-5780-180.18-5960.18
XYZ2013-10-1259613803.3600
XYZ2013-10-133598583.6900
XYZ2013-10-1942410655.1200
XYZ2013-10-25-153-3719.4365.73-3653.7
XYZ2013-10-27-248-6128.08205.74-5922.34
XYZ2013-10-30-350-8176-182.14-8358.14
XYZ2013-11-03-326-7576.24-208.77-7785.01
XYZ2013-11-25-278-6444.04-194.71-6638.75
XYZ2013-12-093157704.900
XYZ2013-12-23-376-9445.12444.23-9000.89
XYZ2014-01-02-196-4504.08-187.88-4691.96
XYZ2014-01-042616384.0600
XYZ2014-01-09-253-6188.38119.75-6068.63
XYZ2014-01-123328060.9600
XYZ2014-01-264049788.9200
XYZ2014-01-3154812960.200
XYZ2014-02-0345910878.300
XYZ2014-02-04-208-5066.8883.45-4983.43
XYZ2014-02-05421024.3800

If we had wanted to calculate the cumulative values instead we only need the change the return value (@RV) passed into the function.

SELECT sym,
       tDate,
       qty,
       price_extended,
       wct.WAC(qty, price_extended, 'GMC', 2, ROW_NUMBER() OVER (PARTITION BY sym 
                 ORDER BY sym, tDate, trn), 0) as [Gross Margin],
       wct.WAC(qty, price_extended, 'COGSC', 2, ROW_NUMBER() OVER (PARTITION BY 
                 sym ORDER BY sym, tDate, trn), 1) as [COGS]
FROM #c
ORDER BY sym,
         tDate,
         trn;

This produces the following result.

symtDateqtyprice_extendedGross MarginCOGS
ABC2013-02-232383041.6400
ABC2013-03-011572042.5700
ABC2013-03-08-157-2067.6946.88-2020.81
ABC2013-03-10-63-794.4330.41-2831.71
ABC2013-03-104636194.9430.41-2831.71
ABC2013-03-20-92-1254.8867.16-4049.84
ABC2013-04-082983868.0467.16-4049.84
ABC2013-04-302293050.2867.16-4049.84
ABC2013-04-30-191-2576.59125.39-6568.2
ABC2013-05-082383044.02125.39-6568.2
ABC2013-05-182983942.54125.39-6568.2
ABC2013-06-111301692.6125.39-6568.2
ABC2013-06-173134047.09125.39-6568.2
ABC2013-06-21-326-4149.988.9-10834.67
ABC2013-06-22-227-3057.6995.77-13805.49
ABC2013-07-225998176.3595.77-13805.49
ABC2013-07-31791070.4595.77-13805.49
ABC2013-09-08-386-5268.9240.35-18929.81
ABC2013-09-084906218.1240.35-18929.81
ABC2013-09-16-202-2613.88200.32-21583.72
ABC2013-10-092493354.03200.32-21583.72
ABC2013-10-091872498.32200.32-21583.72
ABC2013-11-0727342.36200.32-21583.72
ABC2013-11-14-386-4786.4-102.97-26673.41
ABC2013-12-31-145-1912.55-102.35-28585.34
GHI2013-03-0755919682.3900
GHI2013-04-03-151-5007.16-309.55-5316.71
GHI2013-04-1941614335.36-309.55-5316.71
GHI2013-05-161605516.8-309.55-5316.71
GHI2013-05-21-174-5950.8-409.47-11367.43
GHI2013-07-02-162-5747.76-295.13-17000.85
GHI2013-07-16-347-12245.63-116.16-29067.51
GHI2013-08-0644514907.5-116.16-29067.51
GHI2013-08-252729209.92-116.16-29067.51
GHI2013-09-081033648.26-116.16-29067.51
GHI2013-09-2432811463.6-116.16-29067.51
GHI2013-09-24-291-10385.79289.21-39047.93
GHI2013-10-02-295-9590.45-237.95-49165.54
GHI2013-10-14-91-3013.92-345.05-52286.56
GHI2013-10-28-46-1593.9-328.81-53864.22
GHI2013-11-03-246-8760.06-5.81-62301.28
GHI2013-11-16-320-10931.2-49.64-73276.31
GHI2013-11-17-92-3003.8-201.16-76431.63
GHI2013-11-231876331.82-201.16-76431.63
GHI2013-12-06852859.4-201.16-76431.63
GHI2013-12-1650016775-201.16-76431.63
GHI2013-12-21722475.36-201.16-76431.63
GHI2013-12-2737612592.24-201.16-76431.63
GHI2014-01-0239614065.92-201.16-76431.63
GHI2014-01-25-78-2765.88-95.43-79091.78
XYZ2013-02-2052812429.1200
XYZ2013-02-273578161.0200
XYZ2013-03-029216.3600
XYZ2013-03-11-193-4805.7313.92-4491.78
XYZ2013-03-20-20-471.8320.25-4957.25
XYZ2013-03-212756828.25320.25-4957.25
XYZ2013-04-132435892.75320.25-4957.25
XYZ2013-04-182756828.25320.25-4957.25
XYZ2013-04-192776323.91320.25-4957.25
XYZ2013-04-281423284.46320.25-4957.25
XYZ2013-05-07551288.65320.25-4957.25
XYZ2013-05-15972437.61320.25-4957.25
XYZ2013-05-163157424.55320.25-4957.25
XYZ2013-05-1946710811.05320.25-4957.25
XYZ2013-06-021142769.06320.25-4957.25
XYZ2013-06-1554513014.6320.25-4957.25
XYZ2013-06-192966944.16320.25-4957.25
XYZ2013-06-1941310399.34320.25-4957.25
XYZ2013-06-192024635.9320.25-4957.25
XYZ2013-06-30-272-6378.4219.9-11436
XYZ2013-07-05-277-6927.77549.83-18033.84
XYZ2013-07-06-170-3988.2488.81-22083.06
XYZ2013-07-22-88-2168.32561.07-24179.12
XYZ2013-08-13-163-3854.95533.54-28061.6
XYZ2013-08-31-61-1417.03497.62-29514.55
XYZ2013-09-06-13-311.61499.58-29824.2
XYZ2013-09-06-147-3500.07498.27-33325.58
XYZ2013-09-17922263.2498.27-33325.58
XYZ2013-09-24-248-6095.84681.61-39238.08
XYZ2013-10-11-250-5780501.43-45198.26
XYZ2013-10-1259613803.36501.43-45198.26
XYZ2013-10-133598583.69501.43-45198.26
XYZ2013-10-1942410655.12501.43-45198.26
XYZ2013-10-25-153-3719.43567.16-48851.96
XYZ2013-10-27-248-6128.08772.9-54774.3
XYZ2013-10-30-350-8176590.76-63132.44
XYZ2013-11-03-326-7576.24381.99-70917.45
XYZ2013-11-25-278-6444.04187.28-77556.2
XYZ2013-12-093157704.9187.28-77556.2
XYZ2013-12-23-376-9445.12631.51-86557.09
XYZ2014-01-02-196-4504.08443.63-91249.05
XYZ2014-01-042616384.06443.63-91249.05
XYZ2014-01-09-253-6188.38563.38-97317.68
XYZ2014-01-123328060.96563.38-97317.68
XYZ2014-01-264049788.92563.38-97317.68
XYZ2014-01-3154812960.2563.38-97317.68
XYZ2014-02-0345910878.3563.38-97317.68
XYZ2014-02-04-208-5066.88646.83-102301.11
XYZ2014-02-05421024.38646.83-102301.11

In this example, we will look at a simple FX blotter. In FX trading, it is not at all unusual to switch from a long currency position to a short currency position during the course of a day. Additionally, if you are trading something like the US dollars against the Euro, if you are long US dollars, you are short the Euro. Thus, it's critical to pay attention to the sign. We will create another table, #fx, and populate it with some data.

CREATE TABLE #fx
(
    rn int,
    ccy char(3),
    amt_ccy money,
    rate float,
    ctr char(3),
    amt_ctr money,
    PRIMARY KEY (rn)
);
--Populate the table with some data
INSERT INTO #fx
VALUES
(1, 'GBP', 8000000, 1.619, 'USD', -12952000);
INSERT INTO #fx
VALUES
(2, 'GBP', -10000000, 1.62, 'USD', 16200000);
INSERT INTO #fx
VALUES
(3, 'GBP', -4000000, 1.613, 'USD', 6452000);
INSERT INTO #fx
VALUES
(4, 'GBP', 7000000, 1.618, 'USD', -11326000);
INSERT INTO #fx
VALUES
(5, 'GBP', 6000000, 1.623, 'USD', -9738000);
INSERT INTO #fx
VALUES
(6, 'GBP', -5000000, 1.618, 'USD', 8090000);
INSERT INTO #fx
VALUES
(7, 'GBP', -10000000, 1.602, 'USD', 16020000);
INSERT INTO #fx
VALUES
(8, 'GBP', 2000000, 1.608, 'USD', -3216000);
INSERT INTO #fx
VALUES
(9, 'GBP', -2000000, 1.602, 'USD', 3204000);
INSERT INTO #fx
VALUES
(10, 'GBP', 10000000, 1.626, 'USD', -16260000);

In this SQL we will keep track of GBP position, the USD position, and the P/L.

SELECT rn,
       ccy,
       amt_ccy,
       rate,
       ctr,
       amt_ctr,
       wct.WAC(amt_ccy, -amt_ctr, 'QTY', 2, ROW_NUMBER() OVER (ORDER BY rn), 0) 
                 as [GBP Position],
       wct.WAC(amt_ccy, -amt_ctr, 'EV', 2, ROW_NUMBER() OVER (ORDER BY rn), 1) as 
                 [USD Position],
       wct.WAC(amt_ccy, -amt_ctr, 'GM', 2, ROW_NUMBER() OVER (ORDER BY rn), 2) as 
                 [P/L]
FROM #fx;

This produces the following result.

rnccyamt_ccyratectramt_ctrGBP PositionUSD PositionP/L
1GBP8000000.001.619USD-12952000.008000000.0012952000.00.00
2GBP-10000000.001.620USD16200000.00-2000000.00-3240000.008000.00
3GBP-4000000.001.613USD6452000.00-6000000.00-9692000.00.00
4GBP7000000.001.618USD-11326000.001000000.001618000.00-16000.00
5GBP6000000.001.623USD-9738000.007000000.0011356000.00.00
6GBP-5000000.001.618USD8090000.002000000.003244571.43-21428.57
7GBP-10000000.001.602USD16020000.00-8000000.00-12816000.00-40571.43
8GBP2000000.001.608USD-3216000.00-6000000.00-9612000.00-12000.00
9GBP-2000000.001.602USD3204000.00-8000000.00-12816000.00.00
10GBP100000001.626USD-1626000020000003252000-192000