com.quantarray.anaheim.finance.VolatilitySurfaceInterpolation.scala Maven / Gradle / Ivy
The newest version!
package com.quantarray.anaheim.finance
import com.quantarray.anaheim.numerics.Interpolate
import org.apache.commons.math3.analysis.BivariateFunction
import org.apache.commons.math3.analysis.interpolation._
import java.time.{LocalDate, ZoneOffset}
import scala.collection.mutable
import scala.util.Try
trait VolatilitySurfaceInterpolation {
trait Interpolate2[T, S, F] extends Interpolate[(T, S), Option[Double], VolatilitySurface[T, S]] {
def interpolator: BivariateGridInterpolator
def tenorAxis(t: T): Double
def strikeAxis(s: S): Double
protected def function(f: VolatilitySurface[T, S]): BivariateFunction = {
val ts = f.points.map(_.tenorAxis).distinct
val ss = f.points.map(_.strikeAxis).distinct
val xs = ts.map(tenorAxis).toArray
val ys = ss.map(strikeAxis).toArray
val zs = f.points.map(_.volatility).grouped(ss.length).map(_.toArray).toArray
interpolator.interpolate(xs, ys, zs)
}
override def apply(f: VolatilitySurface[T, S], k: (T, S)): Option[Double] = {
val bf = function(f)
Try(bf.value(tenorAxis(k._1), strikeAxis(k._2))).toOption
}
}
trait CachingInterpolate2[T, S, F] extends Interpolate2[T, S, VolatilitySurface[T, S]] {
def interpolator: BivariateGridInterpolator
def tenorAxis(t: T): Double
def strikeAxis(s: S): Double
val cache = mutable.Map.empty[VolatilitySurface[T, S], BivariateFunction]
override def apply(f: VolatilitySurface[T, S], k: (T, S)): Option[Double] = {
cache.get(f) match {
case Some(bf) => Try(bf.value(tenorAxis(k._1), strikeAxis(k._2))).toOption
case _ =>
val bf = function(f)
cache.put(f, bf)
Try(bf.value(tenorAxis(k._1), strikeAxis(k._2))).toOption
}
}
}
object bicubic {
implicit object LocalDateMoneynessInterpolate extends Interpolate2[LocalDate, Moneyness, VolatilitySurface[LocalDate, Moneyness]] {
val interpolator = new BicubicInterpolator
override def tenorAxis(t: LocalDate): Double = t.atStartOfDay().toInstant(ZoneOffset.UTC).toEpochMilli.toDouble
override def strikeAxis(s: Moneyness): Double = s.value
}
object caching {
implicit object CachingLocalDateMoneynessInterpolate extends CachingInterpolate2[LocalDate, Moneyness, VolatilitySurface[LocalDate, Moneyness]] {
val interpolator = new BicubicInterpolator
override def tenorAxis(t: LocalDate): Double = t.atStartOfDay().toInstant(ZoneOffset.UTC).toEpochMilli.toDouble
override def strikeAxis(s: Moneyness): Double = s.value
}
}
}
}
object VolatilitySurfaceInterpolation extends VolatilitySurfaceInterpolation