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) } */