我试图在f#中实现一些我已经在c#中使用的东西,看看语法是多么简洁。我使用期权定价公式(黑色76)作为测试,因为这对我来说似乎是一个功能问题。一切似乎都很好,但我无法计算隐含的卷,因为我需要从内部调用同一个类的方法。以下是我到目前为止的情况:
module Module1
open System
type Black76 (CallPutFlag, Fwd, Strike, time, rf, vol, ?BlackPrice:float) =
let d1 = (Math.Log(Fwd / Strike) + (vol * vol * 0.5) * time) / (vol * Math.Sqrt(time))
let d2 = d1 - vol * Math.Sqrt(time)
let n = new MathNet.Numerics.Distributions.Normal()
member x.valuation =
match CallPutFlag with
| "c" | "C" | "Call" | "call" -> Math.Exp(-rf * time) * (Fwd * n.InverseCumulativeDistribution(d1) - Strike * n.InverseCumulativeDistribution(d2))
| "p" | "P" | "Put" | "put" -> Math.Exp(-rf * time) * (Strike * n.InverseCumulativeDistribution(-d2)- Fwd * n.InverseCumulativeDistribution(-d1))
| _ -> failwith "Unrecognized option type"
member x.delta =
match CallPutFlag with
| "c" | "C" | "Call" | "call" -> Math.Exp(-rf * time) * n.InverseCumulativeDistribution(d1)
| "p" | "P" | "Put" | "put" -> Math.Exp(-rf * time) * n.InverseCumulativeDistribution(-d1)
| _ -> failwith "Unrecognized option type"
member x.gamma =
Math.Exp(-rf * time) * (n.Density(d1) / (Fwd * vol * Math.Sqrt(time)))
member x.vega =
Math.Exp(-rf * time) * n.Density(d1) * Fwd * Math.Sqrt(time)
member x.rho =
match CallPutFlag with
| "c" | "C" | "Call" | "call" -> time * Strike * Math.Sqrt(-rf * time) * n.InverseCumulativeDistribution(d2)
| "p" | "P" | "Put" | "put" -> -time * Strike * Math.Sqrt(-rf * time) * n.InverseCumulativeDistribution(-d2)
| _ -> failwith "Unrecognized option type"
member x.theta =
match CallPutFlag with
| "c" | "C" | "Call" | "call" -> -(Fwd * vol * n.Density(d1)) / (2.0 * Math.Sqrt(time)) - rf * Strike * Math.Sqrt(-rf * time) * n.InverseCumulativeDistribution(d2)
| "p" | "P" | "Put" | "put" -> -(Fwd * vol * n.Density(d1)) / (2.0 * Math.Sqrt(time)) + rf * Strike * Math.Sqrt(-rf * time) * n.InverseCumulativeDistribution(-d2)
| _ -> failwith "Unrecognized option type"
member x.impliedvol =
let vst = Math.Sqrt(2.0*Math.Abs((Math.Log(Fwd/Strike)+rf*time)/time))
let tol = 0.0001
let mutable v = vst
let mutable sigmadiff = 1.0
let mutable k = 1
let kmax = 100
while (sigmadiff >= tol && k < kmax) do
let option = Black76.valuation(CallPutFlag, Fwd, Strike, time, rf, v)
let cvega = Black76.vega(CallPutFlag, Fwd, Strike, time, rf, v)
let increment = (option - BlackPrice) / cvega
v <- v - increment
k < - k + 1
sigmadiff = Math.Abs(increment)
v
除了隐含的vol函数之外,这一切都有效。此外,它似乎不比c#版本简洁得多。你能不能让我知道如何从内部调用隐含的功能?你也知道如何摆脱let mutable(毕竟你不应该在fsharp中使用它(我想)。 感谢
答案 0 :(得分:5)
如果你想让代码更简洁,更实用,那么我可能会尝试重新构建它。我认为下面这些内容应该有用。
首先,你肯定不想重复字符串上的匹配,所以我定义了一个区分联合来捕获各种计算(然后你可以只解析一次字符串):
type CallPutFlag = Call | Put
接下来,我们可以定义记录以保留等式的结果(我只添加了你正在使用的东西,但你可能想在这里添加更多):
type Black76Results = { Vega : float; Valuation : float }
现在,我认为将black76
函数与隐含波动率分开是很有意义的。 black76
函数可以运行给定输入的计算,并将结果作为值Black76Results
记录返回:
let black76 flag fwd strike time rf vol =
let d1 = (Math.Log(fwd / strike) + (vol * vol * 0.5) * time) / (vol * Math.Sqrt(time))
let d2 = d1 - vol * Math.Sqrt(time)
let n = new MathNet.Numerics.Distributions.Normal()
match flag with
| Call ->
let valuation = Math.Exp(-rf * time) * (fwd * n.InverseCumulativeDistribution(d1) - strike * n.InverseCumulativeDistribution(d2))
let delta = Math.Exp(-rf * time) * n.InverseCumulativeDistribution(d1)
let gamma = Math.Exp(-rf * time) * (n.Density(d1) / (fwd * vol * Math.Sqrt(time)))
let vega = Math.Exp(-rf * time) * n.Density(d1) * fwd * Math.Sqrt(time)
let rho = time * strike * Math.Sqrt(-rf * time) * n.InverseCumulativeDistribution(d2)
let theta = -(fwd * vol * n.Density(d1)) / (2.0 * Math.Sqrt(time)) - rf * strike * Math.Sqrt(-rf * time) * n.InverseCumulativeDistribution(d2)
{ Vega = vega; Valuation = valuation }
| Put ->
failwith "TODO: Similar for Put"
虽然Call
和Put
中有一些共享代码,但我认为当您将两者分成不同的分支时,它看起来更具可读性(您仍然可以将常见的代码片段提取到单独的代码中)功能)。
现在,impliedVol
只是一个反复调用black76
的函数:
let impliedVol flag fwd strike time rf blackPrice =
let vst = Math.Sqrt(2.0*Math.Abs((Math.Log(fwd/strike)+rf*time)/time))
let tol = 0.0001
let mutable v = vst
let mutable sigmadiff = 1.0
let mutable k = 1
let kmax = 100
while (sigmadiff >= tol && k < kmax) do
let b = black76 flag fwd strike time rf v
let option = b.Valuation
let cvega = b.Vega
let increment = (option - blackPrice) / cvega
v <- v - increment
k <- k + 1
sigmadiff <- Math.Abs(increment)
v
答案 1 :(得分:1)
所以问题就在于:
let option = Black76.valuation(CallPutFlag, Fwd, Strike, time, rf, v)
let cvega = Black76.vega(CallPutFlag, Fwd, Strike, time, rf, v)
当他们是Black76类型的对象的实例成员时,您尝试在Black76类型上调用它们。您应该使用x.valuation(...)
代替(x
,因为这就是您所谓的this
变量)。
对于C#中的this
,F#没有固定的关键字。相反,当你声明一个成员时,你会在点之前给出你想要的任何名字。
member this.method_name =
member x.another_method =