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
- Build the Package:Navigate to your project directory and
python setup.py sdist bdist_wheel
This will create distribution files in thedist/
directory. - 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:
- 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.
- Example:
- 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.
- Example:
- 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.
- Example:
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!