Logo

SQL Server OptionMatrix Function

Updated 2023-11-17 15:02:31.763000

Description

Use the table-valued function OptionMatrix to generate a result set of return values by varying two inputs into the calculated value. For example, you could generate a result set that shows how a change in the underlying and a change in the volatility affect the price.

Syntax

SELECT *
FROM [westclintech].[wct].[OptionMatrix] (
  <@CallPut, nvarchar(4000),>
 ,<@AssetPrice, float,>
 ,<@StrikePrice, float,>
 ,<@TimeToMaturity, float,>
 ,<@RiskFreeRate, float,>
 ,<@DividendRate, float,>
 ,<@Volatility, float,>
 ,<@ReturnValue, nvarchar(4000),>
 ,<@AmEur, nvarchar(4000),>
 ,<@Row, nvarchar(4000),>
 ,<@RowStep, float,>
 ,<@RowNumSteps, int,>
 ,<@Col, nvarchar(4000),>
 ,<@ColStep, float,>
 ,<@ColNumSteps, int,>)

Arguments

@CallPut

identifies the option as being a call ('C') or a put ('P'). @CallPut is an expression of type nvarchar or of a type that can be implicitly converted to nvarchar.

@AssetPrice

the price of the underlying asset. @AssetPrice is an expression of type float or of a type that can be implicitly converted to float.

@StrikePrice

the exercise price of the option. @StrikePrice is an expression of type float or of a type that can be implicitly converted to float.

@TimeToMaturity

the time to expiration of the option, expressed in years. @TimeToMaturity is an expression of type float or of a type that can be implicitly converted to float.

@RiskFreeRate

the annualized, continuously compounded risk-free rate of return over the life of the option. @RiskFreeRate is an expression of type float or of a type that can be implicitly converted to float.

@DividendRate

the annualized, continuously compounded dividend rate over the life of the option. For currency options, @DividendRate should be the foreign risk-free interest rate. @DividendRate is an expression of type float or of a type that can be implicitly converted to float.

@Volatility

the volatility of the relative price change of the underlying asset. @Volatility is an expression of type float or of a type that can be implicitly converted to float.

@ReturnValue

identifies the calculation to be performed. @ReturnValue is an expression of type nvarchar or of a type that can be implicitly converted to nvarchar. @ReturnValue is not case-sensitive. The following values are acceptable for @ReturnValue:

@ReturnValueReturns
'P','PRICE'Price
'D','DELTA'Delta
'G','GAMMA'Gamma
'T','THETA'Theta
'V','VEGA'Vega
'R','RHO'Rho
'L','LAMBDA'Lambda

@AmEur

identifies the option as being American ('A') or European ('E'). @AmEur is an expression of type nvarchar or of a type that can be implicitly converted to nvarchar.

@Row

Identifies the variable which is changing with each row. @Row is an expression of type nvarchar or of a type that can be implicitly converted to nvarchar. The following values may be passed into @Row:

column 1
'S' , 'U' , 'ASSETP' , 'UNDERLYING'
'X' , 'K' , 'STRIKE'
'T' , 'TIME'
'R' , 'RF' , 'RISKFREE'
'D' , 'DIV' , 'DIVIDEND'
'V' , 'VOL' , 'VOLATILITY' , 'SIGMA'

@RowStep

Identifies the value by which the intial row value is incremented and/or decremented. In the case of time ('T') the row values are only decremented and the step value is assumed to be expressed in days. @RowStep is an expression of type float or of a type that can be implicitly converted to float.

@RowNumSteps

Identifies the number of times that the initial row value is incremented and/or decremented. @RowNumSteps is an expression of type int or of a type that can be implicitly converted to int.

@Col

Identifies the variable which is changing with each column. @Col is an expression of type nvarchar or of a type that can be implicitly converted to nvarchar. The following values may be passed into @Col:

column 1
'S' , 'U' , 'ASSETP' , 'UNDERLYING'
'X' , 'K' , 'STRIKE'
'T' , 'TIME'
'R' , 'RF' , 'RISKFREE'
'D' , 'DIV' , 'DIVIDEND'
'V' , 'VOL' , 'VOLATILITY' , 'SIGMA'

@ColStep

Identifies the value by which the intial column value is incremented and/or decremented. In the case of time ('T') the row values are only decremented and the step value is assumed to be expressed in days. @ColStep is an expression of type float or of a type that can be implicitly converted to float.

@ColNumSteps

Identifies the number of times that the initial column value is incremented and/or decremented. @ColNumSteps is an expression of type int or of a type that can be implicitly converted to int.

Return Type

table

colNamecolDatatypecolDesc
idx_rowintThe row index into a zero-based 2-dimensional array
idx_colintThe column index into a zero-based 2-dimensional array
rowfloatThe value of the row in @Row units
colfloatThe value of the column in @Col unit
valfloatThe return value, calculated using row and col

Remarks

@Volatility must be greater than zero (@Volatility > 0).

@TimeToMaturity must be greater than zero (@TimeToMaturity > 0).

@AssetPrice must be greater than zero (@AssetPrice > 0).

@StrikePrice must be greater than zero (@StrikePrice > 0).

If @ReturnValue is NULL, then @ReturnValue is set to 'P'.

If @DividendRate is NULL an error will be returned.

If @RiskFreeRate is NULL an error will be returned.

@RowNumSteps must be greater than zero.

@ColNumSteps must be greater than zero.

European options are calculated using Black-Scholes-Merton.

American options are calculated using Bjerksund & Stensland 2002.

For results automatically formatted into a ‘matrix' format, use the SP_OPTIONMATRIX stored procedure.

@Row cannot be the same as @Col.

For matrix P&L calculations use OPTIONPLMATRIX.

Examples

In this example, we are going to calculate how the changes in the underlying and volatility will affect the price of a Call option where the underlying is valued at 105, the strike price is 100, the option expires on 2013-06-21 and today's date is 2012-09-04. The continuously compounded risk free rate is 2% and the continuously compounded dividend rate is 1.25%. The volatility is 20%. We have put the initial values of the option into variables simply to make the SQL easier to read.

DECLARE @rv as char(1);
DECLARE @s as float;
DECLARE @x as float;
DECLARE @t as float;
DECLARE @r as float;
DECLARE @d as float;
DECLARE @v as float;
DECLARE @z as char(1);
SET @z = 'C'; --Call/Put;
SET @s = 105; --Underlying;
SET @x = 100; --Strike;
SET @t = datediff(d, '2012-09-04', '2013-06-21') / cast(365 as float); --Time;
SET @r = .02; --RiskFree;
SET @d = .0125; --Dividend;
SET @v = .20; --Volatility;
SET @rv = 'P'; --ReturnValue;

Now we will invoke the table-valued function, specifying that we want the rows to move the underlying 3 steps in increments of 0.5 and the columns to move the volatility in 2 steps in increments of 0.01. This means that we will calculate new price values where the underlying prices are 103.5, 104.0, 104.5, 105.0, 105.5, 106, and 106.5 and where the volatilities are .18, .19, .20, .21, and .22.

SELECT *
FROM wct.OptionMatrix(@z, @s, @x, @t, @r, @d, @v, @rv, 'E', 'UNDERLYING', 0.5, 3,
          'VOL', .01, 2);

This produces the following result.

idx_rowidx_colrowcolval
00103.50.188.65125863951278
01103.50.198.99666600913994
02103.50.29.34297950448698
03103.50.219.69004597092759
04103.50.2210.0377383101028
101040.188.96593117681508
111040.199.30955542553718
121040.29.65434254265256
131040.2110.0001040359328
141040.2210.346683598625
20104.50.189.28614653939416
21104.50.199.62771541070827
22104.50.29.97072809922398
23104.50.2110.3149578200949
24104.50.2210.6602165370413
301050.189.6118164453604
311050.199.95106673110088
321050.210.2920645067008
331050.2110.634542047474
341050.2210.9782772994258
40105.50.189.94284967021544
41105.50.1910.2795276440593
42105.50.210.6182779491688
43105.50.2110.958789595374
44105.50.2211.3008044666979
501060.1810.279152305361
511060.1910.6130141082259
521060.210.9492926355384
531060.2111.2876316384658
541060.2211.6277351476435
60106.50.1810.6206280142049
61106.50.1910.951439991888
62106.50.211.2850309709864
63106.50.2111.620997791316
64106.50.2211.9590050985052

If we were going to populate a 2-dimensional array with the calculated prices, then Array(0,0) would contain 8.65125863951278 and Array(6,4) would contain 11.9590050985052. If we are not interested in the idx_row and idx_col columns, we can explicitly select the columns that we want.

SELECT row,
       col,
       Cast(val as money) as val
FROM wct.OptionMatrix(@z, @s, @x, @t, @r, @d, @v, @rv, 'E', 'UNDERLYING', 0.5, 3,
          'VOL', .01, 2);

This produces the following result.

rowcolval
103.50.188.6513
103.50.198.9967
103.50.29.343
103.50.219.69
103.50.2210.0377
1040.188.9659
1040.199.3096
1040.29.6543
1040.2110.0001
1040.2210.3467
104.50.189.2861
104.50.199.6277
104.50.29.9707
104.50.2110.315
104.50.2210.6602
1050.189.6118
1050.199.9511
1050.210.2921
1050.2110.6345
1050.2210.9783
105.50.189.9428
105.50.1910.2795
105.50.210.6183
105.50.2110.9588
105.50.2211.3008
1060.1810.2792
1060.1910.613
1060.210.9493
1060.2111.2876
1060.2211.6277
106.50.1810.6206
106.50.1910.9514
106.50.211.285
106.50.2111.621
106.50.2211.959

If we wanted to PIVOT the results, we could enter the following SQL.

SELECT row,
       [0.18],
       [0.19],
       [0.20],
       [0.21],
       [0.22]
FROM
(
    SELECT row,
           col,
           Cast(val as money) as val
    FROM wct.OptionMatrix(@z, @s, @x, @t, @r, @d, @v, @rv, 'E', 'UNDERLYING', 0.5,
              3, 'VOL', .01, 2)
) d
PIVOT
(
    sum(val)
    for col in ([0.18], [0.19], [0.20], [0.21], [0.22])
) as P
ORDER BY row;

This produces the following result

row0.180.190.200.210.22
103.58.65138.99679.3439.6910.0377
1048.96599.30969.654310.000110.3467
104.59.28619.62779.970710.31510.6602
1059.61189.951110.292110.634510.9783
105.59.942810.279510.618310.958811.3008
10610.279210.61310.949311.287611.6277
106.510.620610.951411.28511.62111.959

Of course, this required that we know the column-values before running the SQL. The SP_OPTIONMATRIX stored procedure will automatically figure out the column headings for you and execute the SQL by calling the table-valued function.

Let's say you wanted to swap the rows and columns.

SELECT row,
       [103.5],
       [104],
       [104.5],
       [105],
       [105.5],
       [106],
       [106.5]
FROM
(
    SELECT row,
           col,
           Cast(val as money) as val
    FROM wct.OptionMatrix(@z, @s, @x, @t, @r, @d, @v, @rv, 'E', 'VOL', .01, 2, 
              'UNDERLYING', 0.5, 3)
) d
PIVOT
(
    sum(val)
    for col in ([103.5], [104], [104.5], [105], [105.5], [106], [106.5])
) as P
ORDER BY row;

This produces the following result.

row103.5104104.5105105.5106106.5
0.188.65138.96599.28619.61189.942810.279210.6206
0.198.99679.30969.62779.951110.279510.61310.9514
0.29.3439.65439.970710.292110.618310.949311.285
0.219.6910.000110.31510.634510.958811.287611.621
0.2210.037710.346710.660210.978311.300811.627711.959