package main import ( "fmt" "math" "math/rand" "time" ) // RFC8601Time is a time.Time wrapper type RFC8601Time struct { time.Time } func (t RFC8601Time) Sub(u RFC8601Time) time.Duration { return t.Sub(u) } func (t RFC8601Time) MarshalJSON() ([]byte, error) { fmt.Println("Here!") stamp := fmt.Sprintf("\"%s\"", t.Format("2006-01-02")) return []byte(stamp), nil } // Option struct that holds all the details of an option // Option details type Option struct { OptType int64 Strike float64 ExpiryDate RFC8601Time // Market data ValueDate RFC8601Time Spot float64 Rfr float64 Vol float64 Sims int64 // Results FV float64 Delta float64 Vega float64 Rho float64 Gamma float64 Theta float64 } // Single simulation of Geometric Brownian Motion func gbmSimulation(spot float64, rfr float64, vol float64, tte float64, randNum float64) float64 { var drift, stoch float64 drift = (rfr - math.Pow(vol, 2)/2) * tte stoch = vol * math.Pow(tte, 0.5) * randNum return spot * math.Exp(drift+stoch) } // RunSimulations is the main function that runs Monte Carlo simulations // for an option. Note that it runs normal Geometric Brownian Motion // to calculate the future spot levels func RunSimulations(opt *Option) { var i int64 var level float64 levels := make([]float64, opt.Sims) tte := opt.ExpiryDate.Sub(opt.ValueDate).Hours() / (24 * 365) for i = 0; i < opt.Sims; i++ { randNum := rand.NormFloat64() // Base levels[i] = gbmSimulation(opt.Spot, opt.Rfr, opt.Vol, tte, randNum) opt.FV += math.Max((levels[i]-opt.Strike)*float64(opt.OptType), 0) // Delta level = gbmSimulation(opt.Spot+0.0001, opt.Rfr, opt.Vol, tte, randNum) opt.Delta += math.Max((level-opt.Strike)*float64(opt.OptType), 0) // Gamma -- TODO: Doesn't look right level = gbmSimulation(opt.Spot+0.0001, opt.Rfr, opt.Vol, tte, randNum) level += gbmSimulation(opt.Spot-0.0001, opt.Rfr, opt.Vol, tte, randNum) level -= 2 * gbmSimulation(opt.Spot, opt.Rfr, opt.Vol, tte, randNum) opt.Gamma += math.Max((level-opt.Strike)*float64(opt.OptType), 0) // Vega level = gbmSimulation(opt.Spot, opt.Rfr, opt.Vol+0.0001, tte, randNum) opt.Vega += math.Max((level-opt.Strike)*float64(opt.OptType), 0) // Theta level = gbmSimulation(opt.Spot, opt.Rfr, opt.Vol, tte-1./365, randNum) opt.Theta += math.Max((level-opt.Strike)*float64(opt.OptType), 0) // Rho -- TODO: Doesn't look right level = gbmSimulation(opt.Spot, opt.Rfr+0.0001, opt.Vol, tte, randNum) opt.Rho += math.Max((level-opt.Strike)*float64(opt.OptType), 0) } df := math.Exp(-opt.Rfr * tte) opt.FV = opt.FV / float64(opt.Sims) * df opt.Delta = (opt.Delta/float64(opt.Sims)*df - opt.FV) / 0.0001 opt.Gamma = (opt.Gamma/float64(opt.Sims)*df - opt.FV) / 10000 opt.Vega = (opt.Vega/float64(opt.Sims)*df - opt.FV) / 0.01 opt.Theta = (opt.Theta/float64(opt.Sims)*math.Exp(-opt.Rfr*(tte-1./365)) - opt.FV) / -(1. / 365) opt.Rho = (opt.Rho/float64(opt.Sims)*math.Exp(-(opt.Rfr+0.01)*tte) - opt.FV) } /* func main() { value := time.Date(2016, 12, 30, 0, 0, 0, 0, time.UTC) expiry := time.Date(2017, 12, 30, 0, 0, 0, 0, time.UTC) opt := Option{optType: 1, strike: 100, expiryDate: expiry, valueDate: value, spot: 100, rfr: 0.03, vol: 0.25, sims: 1000000} rand.Seed(time.Now().UTC().UnixNano()) RunSimulations(&opt) fmt.Printf( ` Valuation date: %s | BS Analytic | BS Monte Carlo | --------------------------------------------- |Type: | %10s | %13s | |Spot: | %10.2f | %13.2f | |Expiry: | %s | %s | |Strike: | %10.2f | %13.2f | |Risk-free: | %10.2f%% | %13.2f%% | |Implied Vol:| %10.2f%% | %13.2f%% | --------------------------------------------- |Fair value: | %8.4f | %11.4f | |Delta: | %8.4f | %11.4f | |Gamma: | %8.4f | %11.4f | |Vega: | %8.4f | %11.4f | |Theta: | %8.4f | %11.4f | |Rho: | %8.4f | %11.4f | |Simulations:| | %11d | --------------------------------------------- `, value.Format("2006-01-02"), "Call", "Call", opt.Spot, opt.Spot, expiry.Format("2006-01-02"), expiry.Format("2006-01-02"), opt.Strike, opt.Strike, opt.Rfr*100, opt.Rfr*100, opt.Vol*100, opt.Vol*100, opt.FV, opt.FV, opt.Delta, opt.Delta, opt.Gamma, opt.Gamma, opt.Vega, opt.Vega, opt.Theta, opt.Theta, opt.Rho, opt.Rho, opt.Sims) } */