In this section, we are concerned with building the server component of the COM interface. We will first take a look at the prerequisites for building the server components using Python. Then, we will proceed to build an option pricing the COM server using some of the topics we covered in Chapter 4, Numerical Procedures.
The COM interface is an industry standard by Microsoft; therefore, the following software is required to complete this tutorial:
pythoncom
moduleThe
pythoncom
module contains Python extensions for Microsoft Windows. The files are available freely as pywin32 on SourceForge at http://sourceforge.net/projects/pywin32/files/. To download the executable file, navigate to the pywin32 folder, and select the latest available build. Download the installer executable that is compatible with your system. Note that there is one download package for each supported version of Python. Be sure to check for the version of Python installed in your environment and download the corresponding package. Some packages have a 32-bit and a 64-bit version available. You must download the one that corresponds to the Python you have installed. Even if you have a 64-bit computer, if you installed a 32-bit version of Python, you must install the 32-bit version of pywin32.
Once the executable file is downloaded to your hard drive, run the installer, and follow the onscreen instructions to add the pythoncom
module to your Python environment.
Let's build a simple COM server using the classic Black-Scholes options pricing model to calculate the theoretical value of a call or a put option. The calculator is implemented as the BlackScholes
class with a method named pricer
that accepts the current underlying price, strike price, annualized interest rate, time left to maturity in terms of years, volatility of the underlying instrument, and annualized dividend yield as its input parameters. The full COM server code in Python is given as follows:
""" Black-Scholes pricer COM server """ import numpy as np import scipy.stats as stats import pythoncom class BlackScholes: _public_methods_ = ["call_pricer", "put_pricer"] _reg_progid_ = "BlackScholes.Pricer" _reg_clsid_ = pythoncom.CreateGuid() def d1(self, S0, K, r, T, sigma, div): return (np.log(S0/K) + ((r-div) + sigma**2 / 2) * T)/ (sigma * np.sqrt(T)) def d2(self, S0, K, r, T, sigma, div): return (np.log(S0 / K) + ((r-div) - sigma**2 / 2) * T) / (sigma * np.sqrt(T)) def call_pricer(self, S0, K, r, T, sigma, div): d1 = self.d1(S0, K, r, T, sigma, div) d2 = self.d2(S0, K, r, T, sigma, div) return S0 * np.exp(-div * T) * stats.norm.cdf(d1) - K * np.exp(-r * T) * stats.norm.cdf(d2) def put_pricer(self, S0, K, r, T, sigma, div): d1 = self.d1(S0, K, r, T, sigma, div) d2 = self.d2(S0, K, r, T, sigma, div) return K * np.exp(-r * T) * stats.norm.cdf(-d2) - S0 * np.exp(-div * T) *stats.norm.cdf(-d1) if __name__ == "__main__": # Run "python binomial_tree_am.py" # to register the COM server. # Run "python binomial_tree_am.py --unregister" # to unregister it. print "Registering COM server..." import win32com.server.register win32com.server.register.UseCommandLine(BlackScholes)
Note the use of the three magic variables: _public_methods_
, _reg_progid_
, and _reg_clsid_
in the COM server object. The _public_methods_
variable defines the methods that are exposed to the COM clients. The _reg_progid_
variable defines the name of the COM server that is called from the COM client. The _reg_clsid_
variable contains the unique class identifier in the registry.
Assuming that the code is saved in the black_scholes.py
file, we can compile the COM server and register with the registry:
$ python black_scholes.py Registering COM server… Registered: BlackScholes.Pricer
The COM server is now accessible for COM communications.
To unregister the COM server, the additional --unregister
parameter is used:
$ python black_scholes.py --unregister Registering COM server… Unregistered: BlackScholes.Pricer
The COM server is now unregistered and cannot be accessed by the COM clients.
In Chapter 4, Numerical Procedures, we looked at several options pricing models. One such model is the
Cox-Ross-Rubinstein (CRR) model using a binomial tree. Before we can create a second COM server based on this model, let's copy and paste these class files created earlier, namely, BinomialCRROption.py
, BinomialTreeOption.py
, and StockOption.py
, to our working directory.
Now, let's create our COM server using the BinomialCRRCOMServer
class and save it as binomial_crr_com.py
:
""" Binomial CRR tree COM server """ from BinomialCRROption import BinomialCRROption import pythoncom class BinomialCRRCOMServer: _public_methods_ = [ 'pricer'] _reg_progid_ = "BinomialCRRCOMServer.Pricer" _reg_clsid_ = pythoncom.CreateGuid() def pricer(self, S0, K, r, T, N, sigma, is_call=True, div=0., is_eu=False): model = BinomialCRROption(S0, K, r, T, N, {"sigma": sigma, "div": div, "is_call": is_call, "is_eu": is_eu}) return model.price() if __name__ == "__main__": print "Registering COM server..." import win32com.server.register win32com.server.register.UseCommandLine(BinomialCRRCOMServer)
Similar to our Black-Scholes COM server, here the pricer
method creates an instance of the BinomialCRROption
class and returns the calculated price from the CRR binomial tree model.
In Chapter 4, Numerical Procedures, we also explored the use of a trinomial lattice in options pricing. Let's use this model as our third COM server. Let's copy and paste the related class files, namely, TrinomialLattice.py
and TrinomialTreeOption.py
, to our working directory.
Create our COM server with the TrinomialLatticeCOMServer
class and save it as trinomial_lattice_com.py
:
""" Trinomial Lattice COM server """ from TrinomialLattice import TrinomialLattice import pythoncom class TrinomialLatticeCOMServer: _public_methods_ = ['pricer'] _reg_progid_ = "TrinomialLatticeCOMServer.Pricer" _reg_clsid_ = pythoncom.CreateGuid() def pricer(self, S0, K, r, T, N, sigma, is_call=True, div=0., is_eu=False): model = TrinomialLattice(S0, K, r, T, N, {"sigma": sigma, "div": div, "is_call": is_call, "is_eu": is_eu}) return model.price() if __name__ == "__main__": print "Registering COM server..." import win32com.server.register win32com.server.register.UseCommandLine(TrinomialLatticeCOMServer)
Now, let's build and register our three COM server Python files with the registry:
$ python black_scholes.py Registering COM server… Registered: BlackScholes.Pricer $ python binomial_crr_com.py Registering COM server… Registered: BinomialCRRCOMServer.Pricer $ python trinomial_lattice_com.py Registering COM server… Registered: TrinomialLatticeCOMServer.Pricer
With our COM server components successfully registered with the registry, we can now proceed to create our COM client in Excel in the next section.