How to Build and Test a Reusable Python Package: A Black-Scholes Options Calculator Example

Creating reusable Python packages can greatly enhance your productivity and code management. In this blog post, we’ll walk through building a reusable package for a Black-Scholes options calculator and show you how to test it. Whether you’re a seasoned developer or just getting started, this guide will help you structure, create, and test a Python package effectively.

Step 1: Set Up Your Project Structure

A well-structured project is key to building a reusable package. Here’s how to set up your Black-Scholes options calculator project:

BlackScholesCalculator/
├── black_scholes_calculator/
│ ├── __init__.py
│ ├── calculator.py
├── tests/
│ ├── __init__.py
│ ├── test_calculator.py
├── dist/
├── README.md
├── setup.py
└── requirements.txt

Step 2: Writing the Calculator Code

Create the black_scholes_calculator/calculator.py file. This file will contain the implementation of the Black-Scholes formula.

# black_scholes_calculator/calculator.py

import math
from scipy.stats import norm

def black_scholes_call(S, K, T, r, sigma):
"""Calculate Black-Scholes option price for a call option."""
d1 = (math.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * math.sqrt(T))
d2 = d1 - sigma * math.sqrt(T)
call_price = S * norm.cdf(d1) - K * math.exp(-r * T) * norm.cdf(d2)
return call_price

def black_scholes_put(S, K, T, r, sigma):
"""Calculate Black-Scholes option price for a put option."""
d1 = (math.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * math.sqrt(T))
d2 = d1 - sigma * math.sqrt(T)
put_price = K * math.exp(-r * T) * norm.cdf(-d2) - S * norm.cdf(-d1)
return put_price

Step 3: Creating the Setup File

The setup.py file is essential for packaging your module. Create setup.py at the root of your project:

# setup.py

from setuptools import setup, find_packages

setup(
name='black_scholes_calculator',
version='0.1',
packages=find_packages(),
install_requires=[
'scipy',
],
author='Your Name',
author_email='your.email@example.com',
description='A Black-Scholes options calculator',
long_description=open('README.md').read(),
long_description_content_type='text/markdown',
url='https://github.com/yourusername/BlackScholesCalculator',
classifiers=[
'Programming Language :: Python :: 3',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
],
python_requires='>=3.6',
)

Step 4: Writing Tests

Testing is crucial for ensuring the reliability of your package. Create tests/test_calculator.py to write unit tests for your Black-Scholes calculator.

# tests/test_calculator.py

import unittest
from black_scholes_calculator.calculator import black_scholes_call, black_scholes_put

class TestBlackScholesCalculator(unittest.TestCase):

def test_black_scholes_call(self):
S = 100 # Underlying asset price
K = 100 # Strike price
T = 1 # Time to maturity (1 year)
r = 0.05 # Risk-free rate
sigma = 0.2 # Volatility

call_price = black_scholes_call(S, K, T, r, sigma)
self.assertAlmostEqual(call_price, 10.45, places=2)

def test_black_scholes_put(self):
S = 100
K = 100
T = 1
r = 0.05
sigma = 0.2

put_price = black_scholes_put(S, K, T, r, sigma)
self.assertAlmostEqual(put_price, 5.57, places=2)

if __name__ == '__main__':
unittest.main()

Step 5: Creating a Virtual Environment and Installing Dependencies

Using a virtual environment is a good practice to manage dependencies. Create and activate a virtual environment, then install the necessary packages.

python -m venv venv
source venv/bin/activate # On Windows, use `venv\Scripts\activate`
pip install scipy

Step 6: Building and Installing the Package

  1. Build the Package:Navigate to your project directory and python setup.py sdist bdist_wheel This will create distribution files in the dist/ directory.
  2. Install the Package:pip install dist/black_scholes_calculator-0.1-py3-none-any.whl

Step 7: Running the Tests

Run your tests to ensure everything is working correctly.

python -m unittest discover tests

Step 8: Using Your Package

Now that your package is built and installed, you can use it in any project by importing the necessary functions:

from black_scholes_calculator import black_scholes_call, black_scholes_put

call_price = black_scholes_call(100, 100, 1, 0.05, 0.2)
put_price = black_scholes_put(100, 100, 1, 0.05, 0.2)

print(f"Call Price: {call_price}")
print(f"Put Price: {put_price}")

Naming Conventions: BlackScholesCalculator vs. black_scholes_calculator

When creating a Python package, it’s important to understand the different naming conventions used for various parts of your project. Here’s a breakdown of these conventions and their purposes:

  1. Package Directory Name:
    • Example: black_scholes_calculator
    • Convention: Use lowercase letters with underscores.
    • Purpose: This naming convention follows Python’s PEP 8 style guide for package and module names, making it clear and consistent with standard practices. It helps avoid conflicts with class names and improves readability.
  2. Class and Function Names:
    • Example: BlackScholesCalculator, black_scholes_call
    • Convention: Use CamelCase for class names and lowercase with underscores for function names.
    • Purpose: CamelCase is used for class names to distinguish them from functions and variables, making the code more readable and maintainable.
  3. GitHub Repository Name:
    • Example: BlackScholesCalculator
    • Convention: Use CamelCase or a readable format.
    • Purpose: While there’s more flexibility in naming your GitHub repository, using CamelCase or a readable format makes it easier for users to understand the purpose of the project at a glance. It also provides a clean and professional look on your GitHub profile.

What to Enter in setup.py and __init__.py

setup.py

The setup.py script should use the package directory name in the packages argument and in the name argument for the package name.

Example:

from setuptools import setup, find_packages

setup(
name='black_scholes_calculator', # This should match your package directory name
version='0.1',
packages=find_packages(),
install_requires=[
'scipy',
],
author='Your Name',
author_email='your.email@example.com',
description='A Black-Scholes options calculator',
long_description=open('README.md').read(),
long_description_content_type='text/markdown',
url='https://github.com/yourusername/BlackScholesCalculator',
classifiers=[
'Programming Language :: Python :: 3',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
],
python_requires='>=3.6',
)

__init__.py

The __init__.py file should import the necessary classes and functions from your package modules using the package directory name.

Example:

# black_scholes_calculator/__init__.py
from .calculator import black_scholes_call, black_scholes_put

Conclusion

Building a reusable Python package is a valuable skill that can save you time and effort in your projects. By following these steps, you can create a well-structured package, implement functionality, write tests, and ensure your package is easy to distribute and use. The Black-Scholes options calculator serves as a practical example, but the principles apply to any Python package you wish to create. Happy coding!


Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.