Initial commit
This commit is contained in:
14
.gitignore
vendored
Normal file
14
.gitignore
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
||||
.glide/
|
||||
20
LICENSE
Normal file
20
LICENSE
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2017 Kevin Keogh - kevin.d.keogh@gmail.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
155
models.go
Normal file
155
models.go
Normal file
@@ -0,0 +1,155 @@
|
||||
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)
|
||||
|
||||
}
|
||||
*/
|
||||
34
serve.go
Normal file
34
serve.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func index(w http.ResponseWriter, r *http.Request) {
|
||||
formatRequest(r)
|
||||
fmt.Fprintf(w, "Hello go!\n")
|
||||
}
|
||||
|
||||
func formatRequest(r *http.Request) {
|
||||
|
||||
var opt Option
|
||||
err := json.NewDecoder(r.Body).Decode(&opt)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %s\n", err)
|
||||
}
|
||||
|
||||
fmt.Println("Option type:", opt.optType)
|
||||
fmt.Println("Strike:", opt.strike)
|
||||
fmt.Println("Expiry date:", opt.expiryDate.Format("2006-01-02"))
|
||||
|
||||
for k, v := range r.Header {
|
||||
fmt.Printf("%v: %v\n", k, v)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/", index)
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}
|
||||
Reference in New Issue
Block a user