0

構文がどれほど簡潔かを確認するために、c# に既にあるものを f# に実装しようとしています。これは私には機能上の問題のように思われるため、テストとしてオプション価格計算式 (Black 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# バージョンよりも簡潔ではないようです。暗黙の vol 関数に対して内部からメソッドを呼び出す方法を教えてください。また、let mutable を取り除く方法を知っていますか (結局のところ、これを fsharp で使用することは想定されていません (私はそう思います))。

4

2 に答える 2

5

コードをより簡潔で機能的なものにしたい場合は、おそらくもう少し再構成を試みます。以下のこれらの行に沿った何かがうまくいくはずだと思います。

まず第一に、文字列に対してマッチングを繰り返したくないので、判別共用体を定義して計算の種類をキャプチャします (その後、文字列を 1 回だけ解析できます)。

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"

と には共有コードがいくつかありますが、この 2 つを別のブランチに分けた方が読みやすい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 に答える