summaryrefslogtreecommitdiffstats
path: root/scaddins/source/pricing
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /scaddins/source/pricing
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'scaddins/source/pricing')
-rw-r--r--scaddins/source/pricing/black_scholes.cxx939
-rw-r--r--scaddins/source/pricing/black_scholes.hxx146
-rw-r--r--scaddins/source/pricing/pricing.component27
-rw-r--r--scaddins/source/pricing/pricing.cxx510
-rw-r--r--scaddins/source/pricing/pricing.hxx198
5 files changed, 1820 insertions, 0 deletions
diff --git a/scaddins/source/pricing/black_scholes.cxx b/scaddins/source/pricing/black_scholes.cxx
new file mode 100644
index 000000000..88bdfc999
--- /dev/null
+++ b/scaddins/source/pricing/black_scholes.cxx
@@ -0,0 +1,939 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * Copyright (C) 2012 Tino Kluge <tino.kluge@hrz.tu-chemnitz.de>
+ *
+ */
+
+#include <cstdio>
+#include <cstdlib>
+#include <cmath>
+#include <cassert>
+#include <algorithm>
+#include <rtl/math.hxx>
+#include "black_scholes.hxx"
+
+// options prices and greeks in the Black-Scholes model
+// also known as TV (theoretical value)
+
+// the code is structured as follows:
+
+// (1) basic assets
+// - cash-or-nothing option: bincash()
+// - asset-or-nothing option: binasset()
+
+// (2) derived basic assets, can all be priced based on (1)
+// - vanilla put/call: putcall() = +/- ( binasset() - K*bincash() )
+// - truncated put/call (barriers active at maturity only)
+
+// (3) write a wrapper function to include all vanilla prices
+// - this is so we don't duplicate code when pricing barriers
+// as this is derived from vanillas
+
+// (4) single barrier options (knock-out), priced based on truncated vanillas
+// - it follows from the reflection principle that the price W(S) of a
+// single barrier option is given by
+// W(S) = V(S) - (B/S)^a V(B^2/S), a = 2(rd-rf)/vol^2 - 1
+// where V(S) is the price of the corresponding truncated vanilla
+// option
+// - to reduce code duplication and in anticipation of double barrier
+// options we write the following function
+// barrier_term(S,c) = V(c*S) - (B/S)^a V(c*B^2/S)
+
+// (5) double barrier options (knock-out)
+// - value is an infinite sum over option prices of the corresponding
+// truncated vanillas (truncated at both barriers):
+
+// W(S)=sum (B2/B1)^(i*a) (V(S(B2/B1)^(2i)) - (B1/S)^a V(B1^2/S (B2/B1)^(2i))
+
+// (6) write routines for put/call barriers and touch options which
+// mainly call the general double barrier pricer
+// the main routines are touch() and barrier()
+// both can price in/out barriers, double/single barriers as well as
+// vanillas
+
+
+// the framework allows any barriers to be priced as long as we define
+// the value/greek functions for the corresponding truncated vanilla
+// and wrap them into internal::vanilla() and internal::vanilla_trunc()
+
+// disadvantage of that approach is that due to the rules of
+// differentiations the formulas for greeks become long and possible
+// simplifications in the formulas won't be made
+
+// other code inefficiency due to multiplication with pm (+/- 1)
+// cvtsi2sd: int-->double, 6/3 cycles
+// mulsd: double-double multiplication, 5/1 cycles
+// with -O3, however, it compiles 2 versions with pm=1, and pm=-1
+// which are efficient
+// note this is tiny anyway as compared to exp/log (100 cycles),
+// pow (200 cycles), erf (70 cycles)
+
+// this code is not tested for numerical instability, ie overruns,
+// underruns, accuracy, etc
+
+
+namespace sca::pricing::bs {
+
+
+// helper functions
+
+static double sqr(double x) {
+ return x*x;
+}
+// normal density (see also ScInterpreter::phi)
+static double dnorm(double x) {
+ //return (1.0/sqrt(2.0*M_PI))*exp(-0.5*x*x); // windows may not have M_PI
+ return 0.39894228040143268*exp(-0.5*x*x);
+}
+// cumulative normal distribution (see also ScInterpreter::integralPhi)
+static double pnorm(double x) {
+ return 0.5 * std::erfc(-x * 0.7071067811865475);
+}
+
+// binary option cash (domestic)
+// call - pays 1 if S_T is above strike K
+// put - pays 1 if S_T is below strike K
+double bincash(double S, double vol, double rd, double rf,
+ double tau, double K,
+ types::PutCall pc, types::Greeks greeks) {
+ assert(tau>=0.0);
+ assert(S>0.0);
+ assert(vol>0.0);
+ assert(K>=0.0);
+
+ double val=0.0;
+
+ if(tau<=0.0) {
+ // special case tau=0 (expiry)
+ switch(greeks) {
+ case types::Value:
+ if( (pc==types::Call && S>=K) || (pc==types::Put && S<=K) ) {
+ val = 1.0;
+ } else {
+ val = 0.0;
+ }
+ break;
+ default:
+ val = 0.0;
+ }
+ } else if(K==0.0) {
+ // special case with zero strike
+ if(pc==types::Put) {
+ // up-and-out (put) with K=0
+ val=0.0;
+ } else {
+ // down-and-out (call) with K=0 (zero coupon bond)
+ switch(greeks) {
+ case types::Value:
+ val = 1.0;
+ break;
+ case types::Theta:
+ val = rd;
+ break;
+ case types::Rho_d:
+ val = -tau;
+ break;
+ default:
+ val = 0.0;
+ }
+ }
+ } else {
+ // standard case with K>0, tau>0
+ double d1 = ( log(S/K)+(rd-rf+0.5*vol*vol)*tau ) / (vol*sqrt(tau));
+ double d2 = d1 - vol*sqrt(tau);
+ int pm = (pc==types::Call) ? 1 : -1;
+
+ switch(greeks) {
+ case types::Value:
+ val = pnorm(pm*d2);
+ break;
+ case types::Delta:
+ val = pm*dnorm(d2)/(S*vol*sqrt(tau));
+ break;
+ case types::Gamma:
+ val = -pm*dnorm(d2)*d1/(sqr(S*vol)*tau);
+ break;
+ case types::Theta:
+ val = rd*pnorm(pm*d2)
+ + pm*dnorm(d2)*(log(S/K)/(vol*sqrt(tau))-0.5*d2)/tau;
+ break;
+ case types::Vega:
+ val = -pm*dnorm(d2)*d1/vol;
+ break;
+ case types::Volga:
+ val = pm*dnorm(d2)/(vol*vol)*(-d1*d1*d2+d1+d2);
+ break;
+ case types::Vanna:
+ val = pm*dnorm(d2)/(S*vol*vol*sqrt(tau))*(d1*d2-1.0);
+ break;
+ case types::Rho_d:
+ val = -tau*pnorm(pm*d2) + pm*dnorm(d2)*sqrt(tau)/vol;
+ break;
+ case types::Rho_f:
+ val = -pm*dnorm(d2)*sqrt(tau)/vol;
+ break;
+ default:
+ printf("bincash: greek %d not implemented\n", greeks );
+ abort();
+ }
+ }
+ return exp(-rd*tau)*val;
+}
+
+// binary option asset (foreign)
+// call - pays S_T if S_T is above strike K
+// put - pays S_T if S_T is below strike K
+double binasset(double S, double vol, double rd, double rf,
+ double tau, double K,
+ types::PutCall pc, types::Greeks greeks) {
+ assert(tau>=0.0);
+ assert(S>0.0);
+ assert(vol>0.0);
+ assert(K>=0.0);
+
+ double val=0.0;
+ if(tau<=0.0) {
+ // special case tau=0 (expiry)
+ switch(greeks) {
+ case types::Value:
+ if( (pc==types::Call && S>=K) || (pc==types::Put && S<=K) ) {
+ val = S;
+ } else {
+ val = 0.0;
+ }
+ break;
+ case types::Delta:
+ if( (pc==types::Call && S>=K) || (pc==types::Put && S<=K) ) {
+ val = 1.0;
+ } else {
+ val = 0.0;
+ }
+ break;
+ default:
+ val = 0.0;
+ }
+ } else if(K==0.0) {
+ // special case with zero strike (forward with zero strike)
+ if(pc==types::Put) {
+ // up-and-out (put) with K=0
+ val = 0.0;
+ } else {
+ // down-and-out (call) with K=0 (type of forward)
+ switch(greeks) {
+ case types::Value:
+ val = S;
+ break;
+ case types::Delta:
+ val = 1.0;
+ break;
+ case types::Theta:
+ val = rf*S;
+ break;
+ case types::Rho_f:
+ val = -tau*S;
+ break;
+ default:
+ val = 0.0;
+ }
+ }
+ } else {
+ // normal case
+ double d1 = ( log(S/K)+(rd-rf+0.5*vol*vol)*tau ) / (vol*sqrt(tau));
+ double d2 = d1 - vol*sqrt(tau);
+ int pm = (pc==types::Call) ? 1 : -1;
+
+ switch(greeks) {
+ case types::Value:
+ val = S*pnorm(pm*d1);
+ break;
+ case types::Delta:
+ val = pnorm(pm*d1) + pm*dnorm(d1)/(vol*sqrt(tau));
+ break;
+ case types::Gamma:
+ val = -pm*dnorm(d1)*d2/(S*sqr(vol)*tau);
+ break;
+ case types::Theta:
+ val = rf*S*pnorm(pm*d1)
+ + pm*S*dnorm(d1)*(log(S/K)/(vol*sqrt(tau))-0.5*d1)/tau;
+ break;
+ case types::Vega:
+ val = -pm*S*dnorm(d1)*d2/vol;
+ break;
+ case types::Volga:
+ val = pm*S*dnorm(d1)/(vol*vol)*(-d1*d2*d2+d1+d2);
+ break;
+ case types::Vanna:
+ val = pm*dnorm(d1)/(vol*vol*sqrt(tau))*(d2*d2-1.0);
+ break;
+ case types::Rho_d:
+ val = pm*S*dnorm(d1)*sqrt(tau)/vol;
+ break;
+ case types::Rho_f:
+ val = -tau*S*pnorm(pm*d1) - pm*S*dnorm(d1)*sqrt(tau)/vol;
+ break;
+ default:
+ printf("binasset: greek %d not implemented\n", greeks );
+ abort();
+ }
+ }
+ return exp(-rf*tau)*val;
+}
+
+// just for convenience we can combine bincash and binasset into
+// one function binary
+// using bincash() if fd==types::Domestic
+// using binasset() if fd==types::Foreign
+static double binary(double S, double vol, double rd, double rf,
+ double tau, double K,
+ types::PutCall pc, types::ForDom fd,
+ types::Greeks greek) {
+ double val=0.0;
+ switch(fd) {
+ case types::Domestic:
+ val = bincash(S,vol,rd,rf,tau,K,pc,greek);
+ break;
+ case types::Foreign:
+ val = binasset(S,vol,rd,rf,tau,K,pc,greek);
+ break;
+ default:
+ // never get here
+ assert(false);
+ }
+ return val;
+}
+
+// further wrapper to combine single/double barrier binary options
+// into one function
+// B1<=0 - it is assumed lower barrier not set
+// B2<=0 - it is assumed upper barrier not set
+static double binary(double S, double vol, double rd, double rf,
+ double tau, double B1, double B2,
+ types::ForDom fd, types::Greeks greek) {
+ assert(tau>=0.0);
+ assert(S>0.0);
+ assert(vol>0.0);
+
+ double val=0.0;
+
+ if(B1<=0.0 && B2<=0.0) {
+ // no barriers set, payoff 1.0 (domestic) or S_T (foreign)
+ val = binary(S,vol,rd,rf,tau,0.0,types::Call,fd,greek);
+ } else if(B1<=0.0 && B2>0.0) {
+ // upper barrier (put)
+ val = binary(S,vol,rd,rf,tau,B2,types::Put,fd,greek);
+ } else if(B1>0.0 && B2<=0.0) {
+ // lower barrier (call)
+ val = binary(S,vol,rd,rf,tau,B1,types::Call,fd,greek);
+ } else if(B1>0.0 && B2>0.0) {
+ // double barrier
+ if(B2<=B1) {
+ val = 0.0;
+ } else {
+ val = binary(S,vol,rd,rf,tau,B2,types::Put,fd,greek)
+ - binary(S,vol,rd,rf,tau,B1,types::Put,fd,greek);
+ }
+ } else {
+ // never get here
+ assert(false);
+ }
+
+ return val;
+}
+
+// vanilla put/call option
+// call pays (S_T-K)^+
+// put pays (K-S_T)^+
+// this is the same as: +/- (binasset - K*bincash)
+double putcall(double S, double vol, double rd, double rf,
+ double tau, double K,
+ types::PutCall putcall, types::Greeks greeks) {
+
+ assert(tau>=0.0);
+ assert(S>0.0);
+ assert(vol>0.0);
+ assert(K>=0.0);
+
+ double val = 0.0;
+ int pm = (putcall==types::Call) ? 1 : -1;
+
+ if(K==0 || tau==0.0) {
+ // special cases, simply refer to binasset() and bincash()
+ val = pm * ( binasset(S,vol,rd,rf,tau,K,putcall,greeks)
+ - K*bincash(S,vol,rd,rf,tau,K,putcall,greeks) );
+ } else {
+ // general case
+ // we could just use pm*(binasset-K*bincash), however
+ // since the formula for delta and gamma simplify we write them
+ // down here
+ double d1 = ( log(S/K)+(rd-rf+0.5*vol*vol)*tau ) / (vol*sqrt(tau));
+ double d2 = d1 - vol*sqrt(tau);
+
+ switch(greeks) {
+ case types::Value:
+ val = pm * ( exp(-rf*tau)*S*pnorm(pm*d1)-exp(-rd*tau)*K*pnorm(pm*d2) );
+ break;
+ case types::Delta:
+ val = pm*exp(-rf*tau)*pnorm(pm*d1);
+ break;
+ case types::Gamma:
+ val = exp(-rf*tau)*dnorm(d1)/(S*vol*sqrt(tau));
+ break;
+ default:
+ // too lazy for the other greeks, so simply refer to binasset/bincash
+ val = pm * ( binasset(S,vol,rd,rf,tau,K,putcall,greeks)
+ - K*bincash(S,vol,rd,rf,tau,K,putcall,greeks) );
+ }
+ }
+ return val;
+}
+
+// truncated put/call option, single barrier
+// need to specify whether it's down-and-out or up-and-out
+// regular (keeps monotonicity): down-and-out for call, up-and-out for put
+// reverse (destroys monoton): up-and-out for call, down-and-out for put
+// call pays (S_T-K)^+
+// put pays (K-S_T)^+
+double putcalltrunc(double S, double vol, double rd, double rf,
+ double tau, double K, double B,
+ types::PutCall pc, types::KOType kotype,
+ types::Greeks greeks) {
+
+ assert(tau>=0.0);
+ assert(S>0.0);
+ assert(vol>0.0);
+ assert(K>=0.0);
+ assert(B>=0.0);
+
+ int pm = (pc==types::Call) ? 1 : -1;
+ double val = 0.0;
+
+ switch(kotype) {
+ case types::Regular:
+ if( (pc==types::Call && B<=K) || (pc==types::Put && B>=K) ) {
+ // option degenerates to standard plain vanilla call/put
+ val = putcall(S,vol,rd,rf,tau,K,pc,greeks);
+ } else {
+ // normal case with truncation
+ val = pm * ( binasset(S,vol,rd,rf,tau,B,pc,greeks)
+ - K*bincash(S,vol,rd,rf,tau,B,pc,greeks) );
+ }
+ break;
+ case types::Reverse:
+ if( (pc==types::Call && B<=K) || (pc==types::Put && B>=K) ) {
+ // option degenerates to zero payoff
+ val = 0.0;
+ } else {
+ // normal case with truncation
+ val = binasset(S,vol,rd,rf,tau,K,types::Call,greeks)
+ - binasset(S,vol,rd,rf,tau,B,types::Call,greeks)
+ - K * ( bincash(S,vol,rd,rf,tau,K,types::Call,greeks)
+ - bincash(S,vol,rd,rf,tau,B,types::Call,greeks) );
+ }
+ break;
+ default:
+ assert(false);
+ }
+ return val;
+}
+
+// wrapper function for put/call option which combines
+// double/single/no truncation barrier
+// B1<=0 - assume no lower barrier
+// B2<=0 - assume no upper barrier
+double putcalltrunc(double S, double vol, double rd, double rf,
+ double tau, double K, double B1, double B2,
+ types::PutCall pc, types::Greeks greek) {
+
+ assert(tau>=0.0);
+ assert(S>0.0);
+ assert(vol>0.0);
+ assert(K>=0.0);
+
+ double val=0.0;
+
+ if(B1<=0.0 && B2<=0.0) {
+ // no barriers set, plain vanilla
+ val = putcall(S,vol,rd,rf,tau,K,pc,greek);
+ } else if(B1<=0.0 && B2>0.0) {
+ // upper barrier: reverse barrier for call, regular barrier for put
+ if(pc==types::Call) {
+ val = putcalltrunc(S,vol,rd,rf,tau,K,B2,pc,types::Reverse,greek);
+ } else {
+ val = putcalltrunc(S,vol,rd,rf,tau,K,B2,pc,types::Regular,greek);
+ }
+ } else if(B1>0.0 && B2<=0.0) {
+ // lower barrier: regular barrier for call, reverse barrier for put
+ if(pc==types::Call) {
+ val = putcalltrunc(S,vol,rd,rf,tau,K,B1,pc,types::Regular,greek);
+ } else {
+ val = putcalltrunc(S,vol,rd,rf,tau,K,B1,pc,types::Reverse,greek);
+ }
+ } else if(B1>0.0 && B2>0.0) {
+ // double barrier
+ if(B2<=B1) {
+ val = 0.0;
+ } else {
+ int pm = (pc==types::Call) ? 1 : -1;
+ val = pm * (
+ putcalltrunc(S,vol,rd,rf,tau,K,B1,pc,types::Regular,greek)
+ - putcalltrunc(S,vol,rd,rf,tau,K,B2,pc,types::Regular,greek)
+ );
+ }
+ } else {
+ // never get here
+ assert(false);
+ }
+ return val;
+}
+
+namespace internal {
+
+// wrapper function for all non-path dependent options
+// this is only an internal function, used to avoid code duplication when
+// going to path-dependent barrier options,
+// K<0 - assume binary option
+// K>=0 - assume put/call option
+static double vanilla(double S, double vol, double rd, double rf,
+ double tau, double K, double B1, double B2,
+ types::PutCall pc, types::ForDom fd,
+ types::Greeks greek) {
+ double val = 0.0;
+ if(K<0.0) {
+ // binary option if K<0
+ val = binary(S,vol,rd,rf,tau,B1,B2,fd,greek);
+ } else {
+ val = putcall(S,vol,rd,rf,tau,K,pc,greek);
+ }
+ return val;
+}
+static double vanilla_trunc(double S, double vol, double rd, double rf,
+ double tau, double K, double B1, double B2,
+ types::PutCall pc, types::ForDom fd,
+ types::Greeks greek) {
+ double val = 0.0;
+ if(K<0.0) {
+ // binary option if K<0
+ // truncated is actually the same as the vanilla binary
+ val = binary(S,vol,rd,rf,tau,B1,B2,fd,greek);
+ } else {
+ val = putcalltrunc(S,vol,rd,rf,tau,K,B1,B2,pc,greek);
+ }
+ return val;
+}
+
+} // namespace internal
+
+// path dependent options
+
+
+namespace internal {
+
+// helper term for any type of options with continuously monitored barriers,
+// internal, should not be called from outside
+// calculates value and greeks based on
+// V(S) = V1(sc*S) - (B/S)^a V1(sc*B^2/S)
+// (a=2 mu/vol^2, mu drift in logspace, ie. mu=(rd-rf-1/2vol^2))
+// with sc=1 and V1() being the price of the respective truncated
+// vanilla option, V() would be the price of the respective barrier
+// option if only one barrier is present
+static double barrier_term(double S, double vol, double rd, double rf,
+ double tau, double K, double B1, double B2, double sc,
+ types::PutCall pc, types::ForDom fd,
+ types::Greeks greek) {
+
+ assert(tau>=0.0);
+ assert(S>0.0);
+ assert(vol>0.0);
+
+ // V(S) = V1(sc*S) - (B/S)^a V1(sc*B^2/S)
+ double val = 0.0;
+ double B = (B1>0.0) ? B1 : B2;
+ double a = 2.0*(rd-rf)/(vol*vol)-1.0; // helper variable
+ double b = 4.0*(rd-rf)/(vol*vol*vol); // helper variable -da/dvol
+ double c = 12.0*(rd-rf)/(vol*vol*vol*vol); // helper -db/dvol
+ switch(greek) {
+ case types::Value:
+ val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
+ - pow(B/S,a)*
+ vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
+ break;
+ case types::Delta:
+ val = sc*vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
+ + pow(B/S,a) * (
+ a/S*
+ vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value)
+ + sqr(B/S)*sc*
+ vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
+ );
+ break;
+ case types::Gamma:
+ val = sc*sc*vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
+ - pow(B/S,a) * (
+ a*(a+1.0)/(S*S)*
+ vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value)
+ + (2.0*a+2.0)*B*B/(S*S*S)*sc*
+ vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Delta)
+ + sqr(sqr(B/S))*sc*sc*
+ vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Gamma)
+ );
+ break;
+ case types::Theta:
+ val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
+ - pow(B/S,a)*
+ vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
+ break;
+ case types::Vega:
+ val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
+ - pow(B/S,a) * (
+ - b*log(B/S)*
+ vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value)
+ + 1.0*
+ vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
+ );
+ break;
+ case types::Volga:
+ val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
+ - pow(B/S,a) * (
+ log(B/S)*(b*b*log(B/S)+c)*
+ vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value)
+ - 2.0*b*log(B/S)*
+ vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Vega)
+ + 1.0*
+ vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Volga)
+ );
+ break;
+ case types::Vanna:
+ val = sc*vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
+ - pow(B/S,a) * (
+ b/S*(log(B/S)*a+1.0)*
+ vanilla_trunc(B*B/S*sc,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value)
+ + b*log(B/S)*sqr(B/S)*sc*
+ vanilla_trunc(B*B/S*sc,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Delta)
+ - a/S*
+ vanilla_trunc(B*B/S*sc,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Vega)
+ - sqr(B/S)*sc*
+ vanilla_trunc(B*B/S*sc,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Vanna)
+ );
+ break;
+ case types::Rho_d:
+ val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
+ - pow(B/S,a) * (
+ 2.0*log(B/S)/(vol*vol)*
+ vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value)
+ + 1.0*
+ vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
+ );
+ break;
+ case types::Rho_f:
+ val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
+ - pow(B/S,a) * (
+ - 2.0*log(B/S)/(vol*vol)*
+ vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value)
+ + 1.0*
+ vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
+ );
+ break;
+ default:
+ printf("barrier_term: greek %d not implemented\n", greek );
+ abort();
+ }
+ return val;
+}
+
+// one term of the infinite sum for the valuation of double barriers
+static double barrier_double_term( double S, double vol, double rd, double rf,
+ double tau, double K, double B1, double B2,
+ double fac, double sc, int i,
+ types::PutCall pc, types::ForDom fd, types::Greeks greek) {
+
+ double val = 0.0;
+ double b = 4.0*i*(rd-rf)/(vol*vol*vol); // helper variable -da/dvol
+ double c = 12.0*i*(rd-rf)/(vol*vol*vol*vol); // helper -db/dvol
+ switch(greek) {
+ case types::Value:
+ val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek);
+ break;
+ case types::Delta:
+ val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek);
+ break;
+ case types::Gamma:
+ val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek);
+ break;
+ case types::Theta:
+ val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek);
+ break;
+ case types::Vega:
+ val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek)
+ - b*log(B2/B1)*fac *
+ barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Value);
+ break;
+ case types::Volga:
+ val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek)
+ - 2.0*b*log(B2/B1)*fac *
+ barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Vega)
+ + log(B2/B1)*fac*(c+b*b*log(B2/B1)) *
+ barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Value);
+ break;
+ case types::Vanna:
+ val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek)
+ - b*log(B2/B1)*fac *
+ barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Delta);
+ break;
+ case types::Rho_d:
+ val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek)
+ + 2.0*i/(vol*vol)*log(B2/B1)*fac *
+ barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Value);
+ break;
+ case types::Rho_f:
+ val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek)
+ - 2.0*i/(vol*vol)*log(B2/B1)*fac *
+ barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Value);
+ break;
+ default:
+ printf("barrier_double_term: greek %d not implemented\n", greek );
+ abort();
+ }
+ return val;
+}
+
+// general pricer for any type of options with continuously monitored barriers
+// allows two, one or zero barriers, only knock-out style
+// payoff profiles allowed based on vanilla_trunc()
+static double barrier_ko(double S, double vol, double rd, double rf,
+ double tau, double K, double B1, double B2,
+ types::PutCall pc, types::ForDom fd,
+ types::Greeks greek) {
+
+ assert(tau>=0.0);
+ assert(S>0.0);
+ assert(vol>0.0);
+
+ double val = 0.0;
+
+ if(B1<=0.0 && B2<=0.0) {
+ // no barriers --> vanilla case
+ val = vanilla(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
+ } else if(B1>0.0 && B2<=0.0) {
+ // lower barrier
+ if(S<=B1) {
+ val = 0.0; // knocked out
+ } else {
+ val = barrier_term(S,vol,rd,rf,tau,K,B1,B2,1.0,pc,fd,greek);
+ }
+ } else if(B1<=0.0 && B2>0.0) {
+ // upper barrier
+ if(S>=B2) {
+ val = 0.0; // knocked out
+ } else {
+ val = barrier_term(S,vol,rd,rf,tau,K,B1,B2,1.0,pc,fd,greek);
+ }
+ } else if(B1>0.0 && B2>0.0) {
+ // double barrier
+ if(S<=B1 || S>=B2) {
+ val = 0.0; // knocked out (always true if wrong input B1>B2)
+ } else {
+ // more complex calculation as we have to evaluate an infinite
+ // sum
+ // to reduce very costly pow() calls we define some variables
+ double a = 2.0*(rd-rf)/(vol*vol)-1.0; // 2 (mu-1/2vol^2)/sigma^2
+ double BB2=sqr(B2/B1);
+ double BBa=pow(B2/B1,a);
+ double BB2inv=1.0/BB2;
+ double BBainv=1.0/BBa;
+ double fac=1.0;
+ double facinv=1.0;
+ double sc=1.0;
+ double scinv=1.0;
+
+ // initial term i=0
+ val=barrier_double_term(S,vol,rd,rf,tau,K,B1,B2,fac,sc,0,pc,fd,greek);
+ // infinite loop, 10 should be plenty, normal would be 2
+ for(int i=1; i<10; i++) {
+ fac*=BBa;
+ facinv*=BBainv;
+ sc*=BB2;
+ scinv*=BB2inv;
+ double add =
+ barrier_double_term(S,vol,rd,rf,tau,K,B1,B2,fac,sc,i,pc,fd,greek) +
+ barrier_double_term(S,vol,rd,rf,tau,K,B1,B2,facinv,scinv,-i,pc,fd,greek);
+ val += add;
+ //printf("%i: val=%e (add=%e)\n",i,val,add);
+ if(fabs(add) <= 1e-12*fabs(val)) {
+ break;
+ }
+ }
+ // not knocked-out double barrier end
+ }
+ // double barrier end
+ } else {
+ // no such barrier combination exists
+ assert(false);
+ }
+
+ return val;
+}
+
+// knock-in style barrier
+static double barrier_ki(double S, double vol, double rd, double rf,
+ double tau, double K, double B1, double B2,
+ types::PutCall pc, types::ForDom fd,
+ types::Greeks greek) {
+ return vanilla(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
+ -barrier_ko(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
+}
+
+// general barrier
+static double barrier(double S, double vol, double rd, double rf,
+ double tau, double K, double B1, double B2,
+ types::PutCall pc, types::ForDom fd,
+ types::BarrierKIO kio, types::BarrierActive bcont,
+ types::Greeks greek) {
+
+ double val = 0.0;
+ if( kio==types::KnockOut && bcont==types::Maturity ) {
+ // truncated vanilla option
+ val = vanilla_trunc(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
+ } else if ( kio==types::KnockOut && bcont==types::Continuous ) {
+ // standard knock-out barrier
+ val = barrier_ko(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
+ } else if ( kio==types::KnockIn && bcont==types::Maturity ) {
+ // inverse truncated vanilla
+ val = vanilla(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
+ - vanilla_trunc(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
+ } else if ( kio==types::KnockIn && bcont==types::Continuous ) {
+ // standard knock-in barrier
+ val = barrier_ki(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
+ } else {
+ // never get here
+ assert(false);
+ }
+ return val;
+}
+
+} // namespace internal
+
+
+// touch/no-touch options (cash/asset or nothing payoff profile)
+double touch(double S, double vol, double rd, double rf,
+ double tau, double B1, double B2, types::ForDom fd,
+ types::BarrierKIO kio, types::BarrierActive bcont,
+ types::Greeks greek) {
+
+ double K=-1.0; // dummy
+ types::PutCall pc = types::Call; // dummy
+ double val = 0.0;
+ if( kio==types::KnockOut && bcont==types::Maturity ) {
+ // truncated vanilla option
+ val = internal::vanilla_trunc(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
+ } else if ( kio==types::KnockOut && bcont==types::Continuous ) {
+ // standard knock-out barrier
+ val = internal::barrier_ko(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
+ } else if ( kio==types::KnockIn && bcont==types::Maturity ) {
+ // inverse truncated vanilla
+ val = internal::vanilla(S,vol,rd,rf,tau,K,-1.0,-1.0,pc,fd,greek)
+ - internal::vanilla_trunc(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
+ } else if ( kio==types::KnockIn && bcont==types::Continuous ) {
+ // standard knock-in barrier
+ val = internal::vanilla(S,vol,rd,rf,tau,K,-1.0,-1.0,pc,fd,greek)
+ - internal::barrier_ko(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
+ } else {
+ // never get here
+ assert(false);
+ }
+ return val;
+}
+
+// barrier option (put/call payoff profile)
+double barrier(double S, double vol, double rd, double rf,
+ double tau, double K, double B1, double B2,
+ double rebate,
+ types::PutCall pc, types::BarrierKIO kio,
+ types::BarrierActive bcont,
+ types::Greeks greek) {
+ assert(tau>=0.0);
+ assert(S>0.0);
+ assert(vol>0.0);
+ assert(K>=0.0);
+ types::ForDom fd = types::Domestic;
+ double val=internal::barrier(S,vol,rd,rf,tau,K,B1,B2,pc,fd,kio,bcont,greek);
+ if(rebate!=0.0) {
+ // opposite of barrier knock-in/out type
+ types::BarrierKIO kio2 = (kio==types::KnockIn) ? types::KnockOut
+ : types::KnockIn;
+ val += rebate*touch(S,vol,rd,rf,tau,B1,B2,fd,kio2,bcont,greek);
+ }
+ return val;
+}
+
+// probability of hitting a barrier
+// this is almost the same as the price of a touch option (domestic)
+// as it pays one if a barrier is hit; we only have to offset the
+// discounting and we get the probability
+double prob_hit(double S, double vol, double mu,
+ double tau, double B1, double B2) {
+ double const rd=0.0;
+ double rf=-mu;
+ return 1.0 - touch(S,vol,rd,rf,tau,B1,B2,types::Domestic,types::KnockOut,
+ types::Continuous, types::Value);
+}
+
+// probability of being in-the-money, ie payoff is greater zero,
+// assuming payoff(S_T) > 0 iff S_T in [B1, B2]
+// this the same as the price of a cash or nothing option
+// with no discounting
+double prob_in_money(double S, double vol, double mu,
+ double tau, double B1, double B2) {
+ assert(S>0.0);
+ assert(vol>0.0);
+ assert(tau>=0.0);
+ double val = 0.0;
+ if( B1<B2 || B1<=0.0 || B2<=0.0 ) {
+ val = binary(S,vol,0.0,-mu,tau,B1,B2,types::Domestic,types::Value);
+ }
+ return val;
+}
+double prob_in_money(double S, double vol, double mu,
+ double tau, double K, double B1, double B2,
+ types::PutCall pc) {
+ assert(S>0.0);
+ assert(vol>0.0);
+ assert(tau>=0.0);
+
+ // if K<0 we assume a binary option is given
+ if(K<0.0) {
+ return prob_in_money(S,vol,mu,tau,B1,B2);
+ }
+
+ double val = 0.0;
+ double BM1, BM2; // range of in the money [BM1, BM2]
+ // non-sense parameters with no positive payoff
+ if( (B1>B2 && B1>0.0 && B2>0.0) ||
+ (K>=B2 && B2>0.0 && pc==types::Call) ||
+ (K<=B1 && pc==types::Put) ) {
+ val = 0.0;
+ // need to figure out between what barriers payoff is greater 0
+ } else if(pc==types::Call) {
+ BM1=std::max(B1, K);
+ BM2=B2;
+ val = prob_in_money(S,vol,mu,tau,BM1,BM2);
+ } else if (pc==types::Put) {
+ BM1=B1;
+ BM2= (B2>0.0) ? std::min(B2,K) : K;
+ val = prob_in_money(S,vol,mu,tau,BM1,BM2);
+ } else {
+ // don't get here
+ assert(false);
+ }
+ return val;
+}
+
+} // namespace sca
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scaddins/source/pricing/black_scholes.hxx b/scaddins/source/pricing/black_scholes.hxx
new file mode 100644
index 000000000..9e82857e0
--- /dev/null
+++ b/scaddins/source/pricing/black_scholes.hxx
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * Copyright (C) 2012 Tino Kluge <tino.kluge@hrz.tu-chemnitz.de>
+ *
+ */
+
+
+#pragma once
+
+// options prices and greeks in the Black-Scholes model
+// also known as TV (theoretical value)
+
+namespace sca {
+namespace pricing {
+
+namespace bs {
+
+namespace types {
+enum Greeks {
+ Value = 0,
+ Delta = 1, // d/dS
+ Gamma = 2, // d^2/dS^2
+ Theta = 3, // d/dt
+ Vega = 4, // d/dsigma
+ Volga = 5, // d^2/dsigma^2
+ Vanna = 6, // d^2/dsigma dS
+ Rho_d = 7, // d/dr_d
+ Rho_f = 8 // d/dr_f
+};
+
+enum PutCall {
+ Call = 1,
+ Put = -1
+};
+
+enum KOType {
+ Regular = 0,
+ Reverse = 1
+};
+
+enum BarrierKIO {
+ KnockIn = -1,
+ KnockOut = 1
+};
+
+// barrier observed continuously or just at maturity (truncated payoff)
+enum BarrierActive {
+ Continuous = 0,
+ Maturity = 1
+};
+
+enum ForDom {
+ Domestic = 0,
+ Foreign = 1
+};
+
+} // namespace types
+
+
+// binary option cash (domestic)
+// call - pays 1 if S_T is above strike K
+// put - pays 1 if S_T is below strike K
+double bincash(double S, double vol, double rd, double rf,
+ double tau, double K,
+ types::PutCall pc, types::Greeks greeks);
+
+// binary option asset (foreign)
+// call - pays S_T if S_T is above strike K
+// put - pays S_T if S_T is below strike K
+double binasset(double S, double vol, double rd, double rf,
+ double tau, double K,
+ types::PutCall pc, types::Greeks greeks);
+
+// vanilla put/call option
+// call pays (S_T-K)^+
+// put pays (K-S_T)^+
+// this is the same as: +/- (binasset - K*bincash)
+double putcall(double S, double vol, double rd, double rf,
+ double tau, double K,
+ types::PutCall putcall, types::Greeks greeks);
+
+
+// truncated put/call option, single barrier
+// need to specify whether it's down-and-out or up-and-out
+// regular (keeps monotonicity): down-and-out for call, up-and-out for put
+// reverse (destroys monoton): up-and-out for call, down-and-out for put
+// call pays (S_T-K)^+
+// put pays (K-S_T)^+
+double putcalltrunc(double S, double vol, double rd, double rf,
+ double tau, double K, double B,
+ types::PutCall pc, types::KOType kotype,
+ types::Greeks greeks);
+
+
+// wrapper function for put/call option which combines
+// double/single/no truncation barrier
+// B1<=0 - assume no lower barrier
+// B2<=0 - assume no upper barrier
+double putcalltrunc(double S, double vol, double rd, double rf,
+ double tau, double K, double B1, double B2,
+ types::PutCall pc, types::Greeks greek);
+
+// barrier
+// touch/no-touch options (cash/asset or nothing payoff profile)
+double touch(double S, double vol, double rd, double rf,
+ double tau, double B1, double B2, types::ForDom fd,
+ types::BarrierKIO kio, types::BarrierActive bcont,
+ types::Greeks greek);
+
+// barrier
+// barrier option (put/call payoff profile)
+double barrier(double S, double vol, double rd, double rf,
+ double tau, double K, double B1, double B2,
+ double rebate,
+ types::PutCall pc, types::BarrierKIO kio,
+ types::BarrierActive bcont,
+ types::Greeks greek);
+
+
+// probability of hitting a barrier
+double prob_hit(double S, double vol, double mu,
+ double tau, double B1, double B2);
+
+
+// probability of being in-the-money, ie payoff is greater zero,
+// assuming payoff(S_T) > 0 iff S_T in [B1, B2]
+double prob_in_money(double S, double vol, double mu,
+ double tau, double B1, double B2);
+double prob_in_money(double S, double vol, double mu,
+ double tau, double K, double B1, double B2,
+ types::PutCall pc);
+
+
+} // namespace bs
+
+} // namespace pricing
+} // namespace sca
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scaddins/source/pricing/pricing.component b/scaddins/source/pricing/pricing.component
new file mode 100644
index 000000000..fbabf1fb6
--- /dev/null
+++ b/scaddins/source/pricing/pricing.component
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.sheet.addin.PricingFunctionsImpl"
+ constructor="scaddins_ScaPricingAddIn_get_implementation" single-instance="true">
+ <service name="com.sun.star.sheet.AddIn"/>
+ <service name="com.sun.star.sheet.addin.PricingFunctions"/>
+ </implementation>
+</component>
diff --git a/scaddins/source/pricing/pricing.cxx b/scaddins/source/pricing/pricing.cxx
new file mode 100644
index 000000000..f4e9e53f4
--- /dev/null
+++ b/scaddins/source/pricing/pricing.cxx
@@ -0,0 +1,510 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+// pricing functions add in
+
+// all of the UNO add-in technical details have been copied from
+// ../datefunc/datefunc.cxx
+
+#include "pricing.hxx"
+#include "black_scholes.hxx"
+#include <pricing.hrc>
+#include <strings.hrc>
+
+#include <cppuhelper/factory.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weak.hxx>
+#include <algorithm>
+#include <cmath>
+#include <string_view>
+#include <unotools/resmgr.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace ::com::sun::star;
+using namespace sca::pricing;
+
+
+constexpr OUStringLiteral ADDIN_SERVICE = u"com.sun.star.sheet.AddIn";
+constexpr OUStringLiteral MY_SERVICE = u"com.sun.star.sheet.addin.PricingFunctions";
+constexpr OUStringLiteral MY_IMPLNAME = u"com.sun.star.sheet.addin.PricingFunctionsImpl";
+
+#define UNIQUE false // function name does not exist in Calc
+
+#define STDPAR false // all parameters are described
+
+#define FUNCDATA( FuncName, CompName, ParamCount, Category, Double, IntPar ) \
+ { "get" #FuncName, PRICING_FUNCNAME_##FuncName, PRICING_FUNCDESC_##FuncName, CompName, ParamCount, Category, Double, IntPar }
+
+const ScaFuncDataBase pFuncDataArr[] =
+{
+ FUNCDATA(OptBarrier, "OPT_BARRIER", 13, ScaCategory::Finance, UNIQUE, STDPAR),
+ FUNCDATA(OptTouch, "OPT_TOUCH", 11, ScaCategory::Finance, UNIQUE, STDPAR),
+ FUNCDATA(OptProbHit, "OPT_PROB_HIT", 6, ScaCategory::Finance, UNIQUE, STDPAR),
+ FUNCDATA(OptProbInMoney, "OPT_PROB_INMONEY", 8, ScaCategory::Finance, UNIQUE, STDPAR)
+};
+
+#undef FUNCDATA
+
+ScaFuncData::ScaFuncData( const ScaFuncDataBase& rBaseData ) :
+ aIntName( OUString::createFromAscii( rBaseData.pIntName ) ),
+ pUINameID( rBaseData.pUINameID ),
+ pDescrID( rBaseData.pDescrID ),
+ nParamCount( rBaseData.nParamCount ),
+ eCat( rBaseData.eCat ),
+ bDouble( rBaseData.bDouble ),
+ bWithOpt( rBaseData.bWithOpt )
+{
+ aCompList.push_back(OUString::createFromAscii(rBaseData.pCompName));
+}
+
+sal_uInt16 ScaFuncData::GetStrIndex( sal_uInt16 nParam ) const
+{
+ if( !bWithOpt )
+ nParam++;
+ return (nParam > nParamCount) ? (nParamCount * 2) : (nParam * 2);
+}
+
+void sca::pricing::InitScaFuncDataList(ScaFuncDataList& rList)
+{
+ for (const auto & nIndex : pFuncDataArr)
+ rList.push_back(ScaFuncData(nIndex));
+}
+
+// entry points for service registration / instantiation
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+scaddins_ScaPricingAddIn_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new ScaPricingAddIn());
+}
+
+// "normal" service implementation
+ScaPricingAddIn::ScaPricingAddIn()
+{
+}
+
+ScaPricingAddIn::~ScaPricingAddIn()
+{
+}
+
+static const char* pLang[] = { "en" };
+static const char* pCoun[] = { "US" };
+const sal_uInt32 nNumOfLoc = SAL_N_ELEMENTS( pLang );
+
+void ScaPricingAddIn::InitDefLocales()
+{
+ pDefLocales.reset( new lang::Locale[ nNumOfLoc ] );
+
+ for( sal_uInt32 nIndex = 0; nIndex < nNumOfLoc; nIndex++ )
+ {
+ pDefLocales[ nIndex ].Language = OUString::createFromAscii( pLang[ nIndex ] );
+ pDefLocales[ nIndex ].Country = OUString::createFromAscii( pCoun[ nIndex ] );
+ }
+}
+
+const lang::Locale& ScaPricingAddIn::GetLocale( sal_uInt32 nIndex )
+{
+ if( !pDefLocales )
+ InitDefLocales();
+
+ return (nIndex < nNumOfLoc) ? pDefLocales[ nIndex ] : aFuncLoc;
+}
+
+void ScaPricingAddIn::InitData()
+{
+ aResLocale = Translate::Create("sca", LanguageTag(aFuncLoc));
+ pFuncDataList.reset(new ScaFuncDataList);
+ InitScaFuncDataList(*pFuncDataList);
+ pDefLocales.reset();
+}
+
+OUString ScaPricingAddIn::GetFuncDescrStr(const TranslateId* pResId, sal_uInt16 nStrIndex)
+{
+ return ScaResId(pResId[nStrIndex - 1]);
+}
+
+// XServiceName
+OUString SAL_CALL ScaPricingAddIn::getServiceName()
+{
+ // name of specific AddIn service
+ return MY_SERVICE;
+}
+
+// XServiceInfo
+OUString SAL_CALL ScaPricingAddIn::getImplementationName()
+{
+ return MY_IMPLNAME;
+}
+
+sal_Bool SAL_CALL ScaPricingAddIn::supportsService( const OUString& aServiceName )
+{
+ return cppu::supportsService(this, aServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL ScaPricingAddIn::getSupportedServiceNames()
+{
+ return { ADDIN_SERVICE, MY_SERVICE };
+}
+
+// XLocalizable
+void SAL_CALL ScaPricingAddIn::setLocale( const lang::Locale& eLocale )
+{
+ aFuncLoc = eLocale;
+ InitData(); // change of locale invalidates resources!
+}
+
+lang::Locale SAL_CALL ScaPricingAddIn::getLocale()
+{
+ return aFuncLoc;
+}
+
+// function descriptions start here
+// XAddIn
+OUString SAL_CALL ScaPricingAddIn::getProgrammaticFuntionName( const OUString& )
+{
+ // not used by calc
+ // (but should be implemented for other uses of the AddIn service)
+ return OUString();
+}
+
+OUString SAL_CALL ScaPricingAddIn::getDisplayFunctionName( const OUString& aProgrammaticName )
+{
+ OUString aRet;
+
+ auto fDataIt = std::find_if(pFuncDataList->begin(), pFuncDataList->end(),
+ FindScaFuncData( aProgrammaticName ) );
+ if (fDataIt != pFuncDataList->end() )
+ {
+ aRet = ScaResId(fDataIt->GetUINameID());
+ if( fDataIt->IsDouble() )
+ aRet += "_ADD";
+ }
+ else
+ {
+ aRet = "UNKNOWNFUNC_" + aProgrammaticName;
+ }
+
+ return aRet;
+}
+
+OUString SAL_CALL ScaPricingAddIn::getFunctionDescription( const OUString& aProgrammaticName )
+{
+ OUString aRet;
+
+ auto fDataIt = std::find_if( pFuncDataList->begin(), pFuncDataList->end(),
+ FindScaFuncData( aProgrammaticName ) );
+ if( fDataIt != pFuncDataList->end() )
+ aRet = GetFuncDescrStr( fDataIt->GetDescrID(), 1 );
+
+ return aRet;
+}
+
+OUString SAL_CALL ScaPricingAddIn::getDisplayArgumentName(
+ const OUString& aProgrammaticName, sal_Int32 nArgument )
+{
+ OUString aRet;
+
+ auto fDataIt = std::find_if( pFuncDataList->begin(), pFuncDataList->end(),
+ FindScaFuncData( aProgrammaticName ) );
+ if( fDataIt != pFuncDataList->end() && (nArgument <= 0xFFFF) )
+ {
+ sal_uInt16 nStr = fDataIt->GetStrIndex( static_cast< sal_uInt16 >( nArgument ) );
+ if( nStr )
+ aRet = GetFuncDescrStr( fDataIt->GetDescrID(), nStr );
+ else
+ aRet = "internal";
+ }
+
+ return aRet;
+}
+
+OUString SAL_CALL ScaPricingAddIn::getArgumentDescription(
+ const OUString& aProgrammaticName, sal_Int32 nArgument )
+{
+ OUString aRet;
+
+ auto fDataIt = std::find_if( pFuncDataList->begin(), pFuncDataList->end(),
+ FindScaFuncData( aProgrammaticName ) );
+ if( fDataIt != pFuncDataList->end() && (nArgument <= 0xFFFF) )
+ {
+ sal_uInt16 nStr = fDataIt->GetStrIndex( static_cast< sal_uInt16 >( nArgument ) );
+ if( nStr )
+ aRet = GetFuncDescrStr( fDataIt->GetDescrID(), nStr + 1 );
+ else
+ aRet = "for internal use only";
+ }
+
+ return aRet;
+}
+
+OUString SAL_CALL ScaPricingAddIn::getProgrammaticCategoryName(
+ const OUString& aProgrammaticName )
+{
+ OUString aRet;
+
+ auto fDataIt = std::find_if( pFuncDataList->begin(), pFuncDataList->end(),
+ FindScaFuncData( aProgrammaticName ) );
+ if( fDataIt != pFuncDataList->end() )
+ {
+ switch( fDataIt->GetCategory() )
+ {
+ case ScaCategory::DateTime: aRet = "Date&Time"; break;
+ case ScaCategory::Text: aRet = "Text"; break;
+ case ScaCategory::Finance: aRet = "Financial"; break;
+ case ScaCategory::Inf: aRet = "Information"; break;
+ case ScaCategory::Math: aRet = "Mathematical"; break;
+ case ScaCategory::Tech: aRet = "Technical"; break;
+ }
+ }
+
+ if( aRet.isEmpty() )
+ aRet = "Add-In";
+ return aRet;
+}
+
+OUString SAL_CALL ScaPricingAddIn::getDisplayCategoryName(
+ const OUString& aProgrammaticName )
+{
+ return getProgrammaticCategoryName( aProgrammaticName );
+}
+
+// XCompatibilityNames
+uno::Sequence< sheet::LocalizedName > SAL_CALL ScaPricingAddIn::getCompatibilityNames(
+ const OUString& aProgrammaticName )
+{
+ auto fDataIt = std::find_if( pFuncDataList->begin(), pFuncDataList->end(),
+ FindScaFuncData( aProgrammaticName ) );
+ if( fDataIt == pFuncDataList->end() )
+ return uno::Sequence< sheet::LocalizedName >( 0 );
+
+ const std::vector<OUString>& rStrList = fDataIt->GetCompNameList();
+ sal_uInt32 nCount = rStrList.size();
+
+ uno::Sequence< sheet::LocalizedName > aRet( nCount );
+ sheet::LocalizedName* pArray = aRet.getArray();
+
+ for( sal_uInt32 nIndex = 0; nIndex < nCount; nIndex++ )
+ pArray[ nIndex ] = sheet::LocalizedName( GetLocale( nIndex ), rStrList[nIndex] );
+
+ return aRet;
+}
+
+// actual function implementation starts here
+// auxiliary input handling functions
+namespace {
+
+bool getinput_putcall(bs::types::PutCall& pc, std::u16string_view str) {
+ if(o3tl::starts_with(str, u"c")) {
+ pc=bs::types::Call;
+ } else if(o3tl::starts_with(str, u"p")) {
+ pc=bs::types::Put;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool getinput_putcall(bs::types::PutCall& pc, const uno::Any& anyval) {
+ OUString str;
+ if(anyval.getValueTypeClass() == uno::TypeClass_STRING) {
+ anyval >>= str;
+ } else if(anyval.getValueTypeClass() == uno::TypeClass_VOID) {
+ str="c"; // call as default
+ } else {
+ return false;
+ }
+ return getinput_putcall(pc, str);
+}
+
+bool getinput_strike(double& strike, const uno::Any& anyval) {
+ if(anyval.getValueTypeClass() == uno::TypeClass_DOUBLE) {
+ anyval >>= strike;
+ } else if(anyval.getValueTypeClass() == uno::TypeClass_VOID) {
+ strike=-1.0; // -1 as default (means not set)
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool getinput_inout(bs::types::BarrierKIO& kio, std::u16string_view str) {
+ if(o3tl::starts_with(str, u"i")) {
+ kio=bs::types::KnockIn;
+ } else if(o3tl::starts_with(str, u"o")) {
+ kio=bs::types::KnockOut;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool getinput_barrier(bs::types::BarrierActive& cont, std::u16string_view str) {
+ if(o3tl::starts_with(str, u"c")) {
+ cont=bs::types::Continuous;
+ } else if(o3tl::starts_with(str, u"e")) {
+ cont=bs::types::Maturity;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool getinput_fordom(bs::types::ForDom& fd, std::u16string_view str) {
+ if(o3tl::starts_with(str, u"f")) {
+ fd=bs::types::Foreign;
+ } else if(o3tl::starts_with(str, u"d")) {
+ fd=bs::types::Domestic;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool getinput_greek(bs::types::Greeks& greek, const uno::Any& anyval) {
+ OUString str;
+ if(anyval.getValueTypeClass() == uno::TypeClass_STRING) {
+ anyval >>= str;
+ } else if(anyval.getValueTypeClass() == uno::TypeClass_VOID) {
+ str="value";
+ } else {
+ return false;
+ }
+
+ if(str == "value" || str == "price" || str == "v" || str == "p") {
+ greek=bs::types::Value;
+ } else if(str == "delta" || str == "d") {
+ greek=bs::types::Delta;
+ } else if(str == "gamma" || str == "g") {
+ greek=bs::types::Gamma;
+ } else if(str == "theta" || str == "t") {
+ greek=bs::types::Theta;
+ } else if(str == "vega" || str == "e") {
+ greek=bs::types::Vega;
+ } else if(str == "volga" || str == "o") {
+ greek=bs::types::Volga;
+ } else if(str == "vanna" || str == "a") {
+ greek=bs::types::Vanna;
+ } else if(str == "rho" || str == "r") {
+ greek=bs::types::Rho_d;
+ } else if(str == "rhof" || str == "f") {
+ greek=bs::types::Rho_f;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+} // namespace for auxiliary functions
+
+// OPT_BARRIER(...)
+double SAL_CALL ScaPricingAddIn::getOptBarrier( double spot, double vol,
+ double r, double rf, double T, double strike,
+ double barrier_low, double barrier_up, double rebate,
+ const OUString& put_call, const OUString& in_out,
+ const OUString& barriercont, const uno::Any& greekstr )
+{
+ bs::types::PutCall pc;
+ bs::types::BarrierKIO kio;
+ bs::types::BarrierActive bcont;
+ bs::types::Greeks greek;
+ // read and check input values
+ if( spot<=0.0 || vol<=0.0 || T<0.0 || strike<0.0 ||
+ !getinput_putcall(pc,put_call) ||
+ !getinput_inout(kio,in_out) ||
+ !getinput_barrier(bcont,barriercont) ||
+ !getinput_greek(greek,greekstr) ){
+ throw lang::IllegalArgumentException();
+ }
+
+ double fRet=bs::barrier(spot,vol,r,rf,T,strike, barrier_low,barrier_up,
+ rebate,pc,kio,bcont,greek);
+
+ RETURN_FINITE( fRet );
+}
+
+// OPT_TOUCH(...)
+double SAL_CALL ScaPricingAddIn::getOptTouch( double spot, double vol,
+ double r, double rf, double T,
+ double barrier_low, double barrier_up,
+ const OUString& for_dom, const OUString& in_out,
+ const OUString& barriercont, const uno::Any& greekstr )
+{
+ bs::types::ForDom fd;
+ bs::types::BarrierKIO kio;
+ bs::types::BarrierActive bcont;
+ bs::types::Greeks greek;
+ // read and check input values
+ if( spot<=0.0 || vol<=0.0 || T<0.0 ||
+ !getinput_fordom(fd,for_dom) ||
+ !getinput_inout(kio,in_out) ||
+ !getinput_barrier(bcont,barriercont) ||
+ !getinput_greek(greek,greekstr) ){
+ throw lang::IllegalArgumentException();
+ }
+
+ double fRet=bs::touch(spot,vol,r,rf,T,barrier_low,barrier_up,
+ fd,kio,bcont,greek);
+
+ RETURN_FINITE( fRet );
+}
+
+// OPT_PRB_HIT(...)
+double SAL_CALL ScaPricingAddIn::getOptProbHit( double spot, double vol,
+ double mu, double T,
+ double barrier_low, double barrier_up )
+{
+ // read and check input values
+ if( spot<=0.0 || vol<=0.0 || T<0.0 ) {
+ throw lang::IllegalArgumentException();
+ }
+
+ double fRet=bs::prob_hit(spot,vol,mu,T,barrier_low,barrier_up);
+
+ RETURN_FINITE( fRet );
+}
+
+// OPT_PROB_INMONEY(...)
+double SAL_CALL ScaPricingAddIn::getOptProbInMoney( double spot, double vol,
+ double mu, double T,
+ double barrier_low, double barrier_up,
+ const uno::Any& strikeval, const uno::Any& put_call )
+{
+ bs::types::PutCall pc=bs::types::Call;
+ double K = 0;
+
+ // read and check input values
+ if( spot<=0.0 || vol<=0.0 || T<0.0 ||
+ !getinput_putcall(pc,put_call) ||
+ !getinput_strike(K,strikeval) ) {
+ throw lang::IllegalArgumentException();
+ }
+
+ double fRet=bs::prob_in_money(spot,vol,mu,T,K,barrier_low,barrier_up,pc);
+
+ RETURN_FINITE( fRet );
+}
+
+OUString ScaPricingAddIn::ScaResId(TranslateId aResId)
+{
+ return Translate::get(aResId, aResLocale);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scaddins/source/pricing/pricing.hxx b/scaddins/source/pricing/pricing.hxx
new file mode 100644
index 000000000..6c6ad7d1d
--- /dev/null
+++ b/scaddins/source/pricing/pricing.hxx
@@ -0,0 +1,198 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+// option pricing functions add in
+
+// most parts of this files are technical UNO details which are
+// all copied from ../datefunc/datefunc.hxx
+// to avoid having to rename all classes to do with UNO
+// technicalities we use our own namespace
+
+#pragma once
+
+
+#include <vector>
+#include <memory>
+#include <com/sun/star/lang/XServiceName.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/sheet/XAddIn.hpp>
+#include <com/sun/star/sheet/XCompatibilityNames.hpp>
+#include <com/sun/star/sheet/addin/XPricingFunctions.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <unotools/resmgr.hxx>
+
+namespace com::sun::star::lang { class XMultiServiceFactory; }
+
+#define RETURN_FINITE(d) if( !std::isfinite( d ) ) throw css::lang::IllegalArgumentException(); return d;
+
+
+namespace sca::pricing {
+
+enum class ScaCategory
+{
+ DateTime,
+ Text,
+ Finance,
+ Inf,
+ Math,
+ Tech
+};
+
+struct ScaFuncDataBase
+{
+ const char* pIntName; // internal name (get***)
+ TranslateId pUINameID; // resource ID to UI name
+ const TranslateId* pDescrID; // resource ID to description, parameter names and ~ description
+ // pCompName was originally meant to be able to load Excel documents that for
+ // some time were stored with localized function names.
+ // This is not relevant to this add-in, so we only supply the
+ // English function name.
+ // see also: GetExcelName() or GetCompNames() or getCompatibilityNames()
+ const char* pCompName;
+ sal_uInt16 nParamCount; // number of named / described parameters
+ ScaCategory eCat; // function category
+ bool bDouble; // name already exist in Calc
+ bool bWithOpt; // first parameter is internal
+};
+
+class ScaFuncData final
+{
+private:
+ OUString aIntName; // internal name (get***)
+ TranslateId pUINameID; // resource ID to UI name
+ const TranslateId* pDescrID; // leads also to parameter descriptions!
+ sal_uInt16 nParamCount; // num of parameters
+ std::vector<OUString> aCompList; // list of all valid names
+ ScaCategory eCat; // function category
+ bool bDouble; // name already exist in Calc
+ bool bWithOpt; // first parameter is internal
+
+public:
+ ScaFuncData(const ScaFuncDataBase& rBaseData);
+
+ const TranslateId& GetUINameID() const { return pUINameID; }
+ const TranslateId* GetDescrID() const { return pDescrID; }
+ ScaCategory GetCategory() const { return eCat; }
+ bool IsDouble() const { return bDouble; }
+
+ sal_uInt16 GetStrIndex( sal_uInt16 nParam ) const;
+ bool Is( std::u16string_view rCompare ) const
+ { return aIntName == rCompare; }
+
+ const std::vector<OUString>& GetCompNameList() const { return aCompList; }
+};
+
+
+typedef std::vector<ScaFuncData> ScaFuncDataList;
+
+void InitScaFuncDataList(ScaFuncDataList& rMap);
+
+// Predicate for use with std::find_if
+struct FindScaFuncData
+{
+ const OUString& m_rId;
+ explicit FindScaFuncData( const OUString& rId ) : m_rId(rId) {}
+ bool operator() ( ScaFuncData const & rCandidate ) const { return rCandidate.Is(m_rId); }
+};
+
+} // namespace sca::pricing
+
+
+// AddIn class for pricing functions
+
+class ScaPricingAddIn : public ::cppu::WeakImplHelper<
+ css::sheet::XAddIn,
+ css::sheet::XCompatibilityNames,
+ css::sheet::addin::XPricingFunctions,
+ css::lang::XServiceName,
+ css::lang::XServiceInfo >
+{
+private:
+ css::lang::Locale aFuncLoc;
+ std::unique_ptr<css::lang::Locale[]> pDefLocales;
+ std::locale aResLocale;
+ std::unique_ptr<sca::pricing::ScaFuncDataList> pFuncDataList;
+
+
+ void InitDefLocales();
+ const css::lang::Locale& GetLocale( sal_uInt32 nIndex );
+ void InitData();
+
+ /// @throws css::uno::RuntimeException
+ OUString GetFuncDescrStr(const TranslateId* pResId, sal_uInt16 nStrIndex);
+
+public:
+ ScaPricingAddIn();
+ virtual ~ScaPricingAddIn() override;
+
+ OUString ScaResId(TranslateId aResId);
+
+ // XAddIn
+ virtual OUString SAL_CALL getProgrammaticFuntionName( const OUString& aDisplayName ) override;
+ virtual OUString SAL_CALL getDisplayFunctionName( const OUString& aProgrammaticName ) override;
+ virtual OUString SAL_CALL getFunctionDescription( const OUString& aProgrammaticName ) override;
+ virtual OUString SAL_CALL getDisplayArgumentName( const OUString& aProgrammaticName, sal_Int32 nArgument ) override;
+ virtual OUString SAL_CALL getArgumentDescription( const OUString& aProgrammaticName, sal_Int32 nArgument ) override;
+ virtual OUString SAL_CALL getProgrammaticCategoryName( const OUString& aProgrammaticName ) override;
+ virtual OUString SAL_CALL getDisplayCategoryName( const OUString& aProgrammaticName ) override;
+
+ // XCompatibilityNames
+ virtual css::uno::Sequence< css::sheet::LocalizedName > SAL_CALL getCompatibilityNames( const OUString& aProgrammaticName ) override;
+
+ // XLocalizable
+ virtual void SAL_CALL setLocale( const css::lang::Locale& eLocale ) override;
+ virtual css::lang::Locale SAL_CALL getLocale() override;
+
+ // XServiceName
+ virtual OUString SAL_CALL getServiceName() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+
+ // methods from own interfaces start here
+
+
+ virtual double SAL_CALL getOptBarrier( double spot, double vol,
+ double r, double rf, double T, double strike,
+ double barrier_low, double barrier_up, double rebate,
+ const OUString& put_call, const OUString& in_out,
+ const OUString& continuous, const css::uno::Any& greek ) override;
+
+ virtual double SAL_CALL getOptTouch( double spot, double vol,
+ double r, double rf, double T,
+ double barrier_low, double barrier_up,
+ const OUString& for_dom, const OUString& in_out,
+ const OUString& barriercont, const css::uno::Any& greekstr ) override;
+
+ virtual double SAL_CALL getOptProbHit( double spot, double vol,
+ double mu, double T,
+ double barrier_low, double barrier_up ) override;
+
+ virtual double SAL_CALL getOptProbInMoney( double spot, double vol,
+ double mu, double T,
+ double barrier_low, double barrier_up,
+ const css::uno::Any& strikeval, const css::uno::Any& put_call ) override;
+
+};
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */