Files
go-opt-pricer/models.go
2017-09-14 23:55:47 -04:00

156 lines
4.4 KiB
Go

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