請大神講一講美式期權定價的方法?(看成同一歐式期權和early exercise premium兩部分
美式期權定價有很多方法。
具體有:
1。 二叉樹。這個比較簡單直觀,但是有些greeks的計算有些問題,並不很好。
2。三叉樹。
3。BAW。是一個從歐式期權定價模型上進行調整,由PDE推出來的公式。其間做了一些簡化,適用於正利率和短期以及長期期權。
4。LSM。一個神奇的Least Square Monte Carlo模型。避免了nested MC的問題。
5。 Dual Formula。選取適當的輔助變數然後進行計算,是一個比較複雜的模型
首先你要弄明白什麼是美式期權。別以為很簡單,很多金數人到畢業也沒懂。比如我問你,你用pde來定價,為什麼每一步要加那個不等式比較?用LSMC,為什麼要compare holding和exercising value?
美式期權因為提前行權的特性,他的定價公式不再是簡單的Q下Expectation了,而是多了一個sup。這類問題從本質上講是一個dynamic programming principle。這保證了它可以被一個dynamic programming的比較來solve出來。這解答了為什麼pde解法和lsmc都要在比較value,其實都是利用了dynamic programming。
最普遍採用的numerical method是pde和lsmc,前者好處是利於得到Greek,後者好處是可以同時得到optimal boundary,而且可以處理高維和correlated問題。除此以外,還有tree,not necessarily binomial,以及stochastic mesh,其實個人感覺pde和lsmc都算是stochastic mesh的特例。而如今,還有一種artificial neutral network的定價方法,業內也有研究。
近期國內商品期貨期權模擬交易開始, 不同於股票期權, 期貨期權型別為美式。 本來想申請一個個人專欄來寫點定價方面的東西, 目的也是為了鞏固所學。 但由於某些原因被知乎暫停開設個人專欄的權利(原因下面有)。 美式常用定價模型有:
1, 最小二乘蒙特卡洛模擬(LSM);
2, BAW;
3, 二叉樹。
二叉樹從精度上來講是最好的, 理論本身也十分簡單明瞭, 而且同遞迴演算法天然匹配, 但計算速度太慢, 簡直是暴力求解了(再次表明,
暴力mo
del不可取)。 大連商品交易所採取的是BAW模型, 這裡就主要講講此模型。
BAW把美式期權價格表示為:
其中S為標的價格, T為距離到期時間, w(S, T)為early exercise premium。
由於美式和歐式期權價格都滿足Black-Scholes偏微方程
其中b為標的的cost of carry, r為無風險利率。
故w亦滿足BS方程, BAW模型把w作如下表示
這個表示很關鍵, 因子
的來源可以這樣理解: T時間後得到1元, 現值只值
元, 假設我們要求立馬兌現這1元錢, 那麼此時有
的early exercise premium。
在分離出
這個因子後, w所滿足BS偏微分方程近似成了一個常微分方程。 實際上, 當期權標的的cost of carry為0時, 我們甚至可對w作分離變數處理, 令
, 這樣做的理由是當標的沒有持有成本時, early exercise premium的時間因子部分完全可由現金流貶值來刻畫! 為了說明這點我們回過頭來看BAW模型, 其把w表示為
透過取
, BAW模型中把BS方程近似為
其中N為依賴cost of carry的常數, M則是與cost of carry無關的常數。 若我們假設cost of carry為0, 則N=0, 設
, 此時BS方程為
若我們在N等於0時直接代入方程(1), 並令方程(1)與方程(2)相同, 即
此時可以發現
恰為這個微分方程的解。
言歸正傳! 從常微分方程中得到f的形式解後, 需利用邊界條件來確定唯一解, 實際上, BAW模型中該解唯一依賴的未知量是美式期權行權時刻標的資產價格
,我們知道美式的行權時間為停時, 即應該在美式期權價值第一次等於內在價值時行權, 也就是American(S, T)與max(S - StrikePrice, 0)相切的點執行。 自然, 我們就可選擇牛頓迭代法來數值計算這個點。 至此, 整個BAW模型就講完了。
最後Talk is cheap。 Show me the code。 我用C++來實現這個演算法, 當作一個小小的練習。 建立兩個標頭檔案, 一個是關於定價和計算波動率用到的數學工具, 一個是定價模型本身。 C++知識點主要用了三個(言必稱三):
1, lambda表示式和template, 此處運用只為形式上簡潔一些;
2, namespace, 為的是防止命名衝突;
3, function object, 不使用函式而使用函式物件一是為了程式碼表現得更像C++而不是C, 更重要的是BAW模型有一些臨時變數的計算很繁瑣, 並且有共同變數, 故可用函式物件直接封裝起來。
標頭檔案norm_distribution。h
#pragma once
#ifndef LIUTING_NORM_DISTTRIBUTION_H
#define LIUTING_NORM_DISTTRIBUTION_H
#include
#include
namespace
AmurTiger
{
const
double
PI
=
3。141592653589793238462643
;
auto
NormDensity
=
[](
const
double
&
d
)
{
return
(
1。0
/
sqrt
(
2。0
*
PI
))
*
exp
(
-
0。5
*
d
*
d
);
};
double
NormDist
(
const
double
&
d
)
{
if
(
d
>
6。0
)
{
return
1。0
;
};
if
(
d
<
-
6。0
)
{
return
0。0
;
};
double
b1
=
0。31938153
;
double
b2
=
-
0。356563782
;
double
b3
=
1。781477937
;
double
b4
=
-
1。821255978
;
double
b5
=
1。330274429
;
double
p
=
0。2316419
;
double
c2
=
0。3989423
;
double
a
=
fabs
(
d
);
double
t
=
1。0
/
(
1。0
+
a
*
p
);
double
b
=
c2
*
exp
((
-
d
)
*
(
d
/
2。0
));
double
n
=
((((
b5
*
t
+
b4
)
*
t
+
b3
)
*
t
+
b2
)
*
t
+
b1
)
*
t
;
n
=
1。0
-
b
*
n
;
if
(
d
<
0。0
)
n
=
1。0
-
n
;
return
n
;
};
template
<
typename
T
>
T
CalcEXP
(
const
std
::
vector
<
T
>&
v
)
{
size_t
sz
=
v
。
size
();
if
(
sz
==
0
)
return
0
;
T
s
=
0
;
for
(
auto
i
:
v
)
s
+=
i
;
return
s
/
sz
;
}
template
<
typename
T
>
T
CalcVAR
(
const
std
::
vector
<
T
>&
v
)
{
size_t
sz
=
v
。
size
();
if
(
sz
<=
1
)
return
0
;
T
e
=
CalcEXP
(
v
);
T
s
=
0
;
for
(
auto
i
:
v
)
s
+=
(
i
-
e
)
*
(
i
-
e
);
return
s
/
(
sz
-
1
);
}
}
#endif
// !LIUTING_NORM_DISTTRIBUTION_H
標頭檔案option。h
#pragma once
#ifndef LIUTING_OPTION_H
#define LIUTING_OPTION_H
#include
#include
“norm_distribution。h”
namespace
AmurTiger
{
enum
class
CALLPUT
{
CALL
,
PUT
};
enum
class
UNDERLYING
{
COMMODITY
,
FUTURE
};
enum
class
OPTIONTYPE
{
EUROPEAN
,
AMERICAN
};
struct
OPTION
{
UNDERLYING
underlying
;
OPTIONTYPE
option_type
;
CALLPUT
call_put
;
double
underlying_price
;
//標的資產現價
double
strike_price
;
//執行價
double
sigma
;
//波動率
double
t
;
//距離到期時間
double
r
;
//無風險利率
double
b
;
//cost of carry, 若無分紅等, 持有成本一般等於無風險利率r, 期貨持有成本為0
OPTION
()
=
delete
;
OPTION
(
UNDERLYING
u
,
OPTIONTYPE
ot
,
CALLPUT
cp
,
double
up
,
double
sp
,
double
sig
,
double
x
,
double
y
,
double
z
)
:
underlying
(
u
),
option_type
(
ot
),
call_put
(
cp
),
underlying_price
(
up
),
strike_price
(
sp
),
sigma
(
sig
),
t
(
x
),
r
(
y
),
b
(
z
)
{}
OPTION
(
const
OPTION
&
op
)
:
underlying
(
op
。
underlying
),
option_type
(
op
。
option_type
),
call_put
(
op
。
call_put
),
underlying_price
(
op
。
underlying_price
),
strike_price
(
op
。
strike_price
),
sigma
(
op
。
sigma
),
t
(
op
。
t
),
r
(
op
。
r
),
b
(
op
。
b
)
{}
};
class
BLACKSCHOLES
{
//只針對歐式期權
public
:
double
operator
()(
const
OPTION
&
op
)
{
double
_S
=
1
,
_t
=
sqrt
(
op
。
t
),
d1
=
0
,
d2
=
0
;
if
(
op
。
underlying
==
UNDERLYING
::
COMMODITY
)
_S
=
op
。
underlying_price
;
else
_S
=
op
。
underlying_price
*
exp
(
-
op
。
b
*
op
。
t
);
d1
=
(
log
(
_S
/
op
。
strike_price
)
+
(
op
。
b
+
0。5
*
op
。
sigma
*
op
。
sigma
)
*
op
。
t
)
/
(
_t
*
op
。
sigma
);
d2
=
d1
-
_t
*
op
。
sigma
;
if
(
op
。
call_put
==
CALLPUT
::
CALL
)
return
_S
*
exp
((
op
。
b
-
op
。
r
)
*
op
。
t
)
*
NormDist
(
d1
)
-
op
。
strike_price
*
exp
(
-
op
。
r
*
op
。
t
)
*
NormDist
(
d2
);
else
return
-
_S
*
exp
((
op
。
b
-
op
。
r
)
*
op
。
t
)
*
NormDist
(
-
d1
)
+
op
。
strike_price
*
exp
(
-
op
。
r
*
op
。
t
)
*
NormDist
(
-
d2
);
}
};
class
BARONEADESIWHALEY
{
//歐美皆可
public
:
double
operator
()(
const
OPTION
&
op
)
{
if
(
op
。
option_type
==
OPTIONTYPE
::
EUROPEAN
)
return
bs
(
op
);
if
(
op
。
call_put
==
CALLPUT
::
CALL
&&
op
。
b
>=
op
。
r
)
return
bs
(
op
);
else
{
double
first_passage_price
=
FirstPassagePrice
(
op
);
if
(
op
。
call_put
==
CALLPUT
::
CALL
)
{
if
(
op
。
underlying_price
<
first_passage_price
)
return
bs
(
op
)
+
_A
(
op
,
first_passage_price
)
*
pow
(
op
。
underlying_price
/
first_passage_price
,
_q
(
op
));
else
return
op
。
underlying_price
-
op
。
strike_price
;
}
else
{
if
(
op
。
underlying_price
>
first_passage_price
)
return
bs
(
op
)
+
_A
(
op
,
first_passage_price
)
*
pow
(
op
。
underlying_price
/
first_passage_price
,
_q
(
op
));
else
return
op
。
strike_price
-
op
。
underlying_price
;
}
}
}
private
:
BLACKSCHOLES
bs
;
const
double
ACCURACY
=
1。0e-6
;
const
int
MAXITERNUM
=
1000
;
double
FirstPassagePrice
(
const
OPTION
&
op
)
{
double
fpp
=
_seed
(
op
);
for
(
int
i
=
0
;
i
<
MAXITERNUM
;
++
i
)
{
fpp
=
FirstPassagePriceIterator
(
op
,
fpp
);
if
(
abs
(
_lhs
(
op
,
fpp
)
-
_rhs
(
op
,
fpp
))
/
op
。
strike_price
<
ACCURACY
)
return
fpp
;
}
return
fpp
;
}
double
FirstPassagePriceIterator
(
const
OPTION
&
op
,
double
p
)
{
double
temp
=
_b
(
op
,
p
);
if
(
op
。
call_put
==
CALLPUT
::
CALL
)
return
(
op
。
strike_price
+
_rhs
(
op
,
p
)
-
temp
*
p
)
/
(
1
-
temp
);
else
return
(
op
。
strike_price
-
_rhs
(
op
,
p
)
+
temp
*
p
)
/
(
1
+
temp
);
}
double
_d
(
const
OPTION
&
op
,
double
x
)
{
return
(
log
(
x
/
op
。
strike_price
)
+
(
op
。
b
+
0。5
*
op
。
sigma
*
op
。
sigma
)
*
op
。
t
)
/
(
op
。
sigma
*
sqrt
(
op
。
t
));
}
double
_q
(
const
OPTION
&
op
)
{
double
_m
=
2
*
op
。
r
/
op
。
sigma
/
op
。
sigma
,
_n
=
2
*
op
。
b
/
op
。
sigma
/
op
。
sigma
,
_k
=
1
-
exp
(
-
op
。
r
*
op
。
t
);
if
(
op
。
call_put
==
CALLPUT
::
CALL
)
return
(
-
_n
+
1
+
sqrt
((
_n
-
1
)
*
(
_n
-
1
)
+
4
*
_m
/
_k
))
/
2
;
else
return
(
-
_n
+
1
-
sqrt
((
_n
-
1
)
*
(
_n
-
1
)
+
4
*
_m
/
_k
))
/
2
;
}
double
_q_inf
(
const
OPTION
&
op
)
{
double
_m
=
2
*
op
。
r
/
op
。
sigma
/
op
。
sigma
,
_n
=
2
*
op
。
b
/
op
。
sigma
/
op
。
sigma
,
_k
=
1
-
exp
(
-
op
。
r
*
op
。
t
);
if
(
op
。
call_put
==
CALLPUT
::
CALL
)
return
(
-
_n
+
1
+
sqrt
((
_n
-
1
)
*
(
_n
-
1
)
+
4
*
_m
))
/
2
;
else
return
(
-
_n
+
1
-
sqrt
((
_n
-
1
)
*
(
_n
-
1
)
+
4
*
_m
))
/
2
;
}
double
_lhs
(
const
OPTION
&
op
,
double
x
)
{
if
(
op
。
call_put
==
CALLPUT
::
CALL
)
return
x
-
op
。
strike_price
;
else
return
op
。
strike_price
-
x
;
}
double
_rhs
(
const
OPTION
&
op
,
double
x
)
{
OPTION
_op
(
op
);
_op
。
underlying_price
=
x
;
if
(
op
。
call_put
==
CALLPUT
::
CALL
)
return
bs
(
_op
)
+
(
1
-
exp
((
op
。
b
-
op
。
r
)
*
op
。
t
)
*
NormDist
(
_d
(
op
,
x
)))
*
x
/
_q
(
op
);
else
return
bs
(
_op
)
-
(
1
-
exp
((
op
。
b
-
op
。
r
)
*
op
。
t
)
*
NormDist
(
-
_d
(
op
,
x
)))
*
x
/
_q
(
op
);
}
double
_b
(
const
OPTION
&
op
,
double
x
)
{
double
temp
=
0
;
if
(
op
。
call_put
==
CALLPUT
::
CALL
)
temp
=
exp
((
op
。
b
-
op
。
r
)
*
op
。
t
)
*
NormDist
(
_d
(
op
,
x
))
*
(
1
-
1
/
_q
(
op
))
+
(
1
-
exp
((
op
。
b
-
op
。
r
)
*
op
。
t
)
*
NormDensity
(
_d
(
op
,
x
))
/
op
。
sigma
/
sqrt
(
op
。
t
))
/
_q
(
op
);
else
temp
=
-
exp
((
op
。
b
-
op
。
r
)
*
op
。
t
)
*
NormDist
(
_d
(
op
,
x
))
*
(
1
-
1
/
_q
(
op
))
-
(
1
+
exp
((
op
。
b
-
op
。
r
)
*
op
。
t
)
*
NormDensity
(
_d
(
op
,
x
))
/
op
。
sigma
/
sqrt
(
op
。
t
))
/
_q
(
op
);
return
temp
;
}
double
_seed
(
const
OPTION
&
op
)
{
double
s_inf
=
op
。
strike_price
/
(
1
-
1
/
_q_inf
(
op
)),
h
=
0
;
if
(
op
。
call_put
==
CALLPUT
::
CALL
)
{
h
=
-
(
op
。
b
*
op
。
t
+
2
*
op
。
sigma
*
sqrt
(
op
。
t
))
*
(
op
。
strike_price
/
(
s_inf
-
op
。
strike_price
));
return
op
。
strike_price
+
(
s_inf
-
op
。
strike_price
)
*
(
1
-
exp
(
h
));
}
else
{
h
=
(
op
。
b
*
op
。
t
-
2
*
op
。
sigma
*
sqrt
(
op
。
t
))
*
(
op
。
strike_price
/
(
op
。
strike_price
-
s_inf
));
return
s_inf
+
(
op
。
strike_price
-
s_inf
)
*
exp
(
h
);
}
}
double
_A
(
const
OPTION
&
op
,
double
x
)
{
if
(
op
。
call_put
==
CALLPUT
::
CALL
)
return
x
/
_q
(
op
)
*
(
1
-
exp
((
op
。
b
-
op
。
r
)
*
op
。
t
)
*
NormDist
(
_d
(
op
,
x
)));
else
return
-
x
/
_q
(
op
)
*
(
1
-
exp
((
op
。
b
-
op
。
r
)
*
op
。
t
)
*
NormDist
(
-
_d
(
op
,
x
)));
}
};
}
#endif
// !LIUTING_OPTION_H
去搜longstaff的論文,用mc做美式期權定價,從最後一期行權收益一步步做迴歸,每一個時間點比較。
Valuing American Options by Simulation: A Simple Least-Squares Approach
Francis A。 Longstaff
UCLA
Eduardo S。 Schwartz
UCLA
除了前文提到的LSM方法外,一個更穩定的方式是PDE方法,對BS方程進行差分轉化成差分方程後,用Implied-FDM或者Crank-Nicolson等差分方法。在每一步中,疊加一個最最佳化的判定:差分值與內部價值做比較取大的。
另外在使用MC方法時,為了降低方差,提高穩定性,有很多方差縮減技巧可以使用,比如映象法,Quasi隨機數等等