0

我正在尝试在 f# 中实现一些我已经在 c# 中拥有的东西,以查看语法有多简洁。我使用期权定价公式(Black 76)作为测试,因为这对我来说似乎是一个功能问题。一切似乎都很好,但我无法计算隐含的 vol,因为我需要从内部调用同一类的方法。这是我到目前为止所拥有的:

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# 版本简洁得多。您能否让我知道如何从内部调用该方法以获取隐含的 vol 功能?你也知道如何摆脱 let mutable (毕竟你不应该在 fsharp 中使用它(我认为)。谢谢

4

2 回答 2

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"

尽管 and 中有一些共享代码CallPut但我认为当您将两者分离到不同的分支时它看起来更具可读性(您仍然可以将公共代码片段提取到单独的函数中)。

现在,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
于 2013-10-01T15:08:33.950 回答
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变量)。

F# 对于 C# 中称为this. 相反,当您声明一个成员时,您可以在点之前给出您想要的任何名称。

member this.method_name =
member x.another_method = 
于 2013-10-01T15:00:58.087 回答