folio / tests /test_data_model.py
dystomachina's picture
Initial commit for Folio project
ce4bc73
Raw
History Blame Contribute Delete
26.4 kB
"""Tests for the data model classes.
This module tests the core functionality of the data model classes in src/folio/data_model.py.
"""
from src.folio.data_model import (
ExposureBreakdown,
OptionPosition,
PortfolioGroup,
PortfolioSummary,
StockPosition,
create_portfolio_group,
)
class TestStockPosition:
"""Tests for the StockPosition class."""
def test_stock_position_init(self):
"""Test basic initialization of StockPosition."""
stock = StockPosition(
ticker="AAPL",
quantity=100,
beta=1.2,
market_exposure=15000.0,
beta_adjusted_exposure=18000.0,
price=150.0,
)
assert stock.ticker == "AAPL"
assert stock.quantity == 100
assert stock.beta == 1.2
assert stock.market_exposure == 15000.0
assert stock.beta_adjusted_exposure == 18000.0
assert stock.price == 150.0
assert stock.position_type == "stock"
def test_stock_position_to_dict(self):
"""Test conversion of StockPosition to dictionary."""
stock = StockPosition(
ticker="AAPL",
quantity=100,
beta=1.2,
market_exposure=15000.0,
beta_adjusted_exposure=18000.0,
price=150.0,
)
stock_dict = stock.to_dict()
assert stock_dict["ticker"] == "AAPL"
assert stock_dict["quantity"] == 100
assert stock_dict["beta"] == 1.2
assert stock_dict["market_exposure"] == 15000.0
assert stock_dict["beta_adjusted_exposure"] == 18000.0
assert stock_dict["price"] == 150.0
assert stock_dict["position_type"] == "stock"
def test_stock_position_from_dict(self):
"""Test creation of StockPosition from dictionary."""
stock_dict = {
"ticker": "AAPL",
"quantity": 100,
"beta": 1.2,
"market_exposure": 15000.0,
"beta_adjusted_exposure": 18000.0,
"price": 150.0,
"position_type": "stock",
}
stock = StockPosition.from_dict(stock_dict)
assert stock.ticker == "AAPL"
assert stock.quantity == 100
assert stock.beta == 1.2
assert stock.market_exposure == 15000.0
assert stock.beta_adjusted_exposure == 18000.0
assert stock.price == 150.0
assert stock.position_type == "stock"
class TestOptionPosition:
"""Tests for the OptionPosition class."""
def test_option_position_init(self):
"""Test basic initialization of OptionPosition."""
option = OptionPosition(
ticker="AAPL",
position_type="option",
quantity=10,
beta=1.2,
beta_adjusted_exposure=1800.0,
market_exposure=1500.0,
strike=150.0,
expiry="2023-01-01",
option_type="CALL",
delta=0.7,
delta_exposure=1050.0,
notional_value=15000.0,
underlying_beta=1.2,
price=15.0,
)
assert option.ticker == "AAPL"
assert option.position_type == "option"
assert option.quantity == 10
assert option.beta == 1.2
assert option.beta_adjusted_exposure == 1800.0
assert option.market_exposure == 1500.0
assert option.strike == 150.0
assert option.expiry == "2023-01-01"
assert option.option_type == "CALL"
assert option.delta == 0.7
assert option.delta_exposure == 1050.0
assert option.notional_value == 15000.0
assert option.underlying_beta == 1.2
assert option.price == 15.0
def test_option_position_to_dict(self):
"""Test conversion of OptionPosition to dictionary."""
option = OptionPosition(
ticker="AAPL",
position_type="option",
quantity=10,
beta=1.2,
beta_adjusted_exposure=1800.0,
market_exposure=1500.0,
strike=150.0,
expiry="2023-01-01",
option_type="CALL",
delta=0.7,
delta_exposure=1050.0,
notional_value=15000.0,
underlying_beta=1.2,
price=15.0,
)
option_dict = option.to_dict()
assert option_dict["ticker"] == "AAPL"
assert option_dict["position_type"] == "option"
assert option_dict["quantity"] == 10
assert option_dict["beta"] == 1.2
assert option_dict["beta_adjusted_exposure"] == 1800.0
assert option_dict["market_exposure"] == 1500.0
assert option_dict["strike"] == 150.0
assert option_dict["expiry"] == "2023-01-01"
assert option_dict["option_type"] == "CALL"
assert option_dict["delta"] == 0.7
assert option_dict["delta_exposure"] == 1050.0
assert option_dict["notional_value"] == 15000.0
assert option_dict["underlying_beta"] == 1.2
assert option_dict["price"] == 15.0
def test_option_position_from_dict(self):
"""Test creation of OptionPosition from dictionary."""
option_dict = {
"ticker": "AAPL",
"position_type": "option",
"quantity": 10,
"beta": 1.2,
"beta_adjusted_exposure": 1800.0,
"market_exposure": 1500.0,
"strike": 150.0,
"expiry": "2023-01-01",
"option_type": "CALL",
"delta": 0.7,
"delta_exposure": 1050.0,
"notional_value": 15000.0,
"underlying_beta": 1.2,
"price": 15.0,
}
option = OptionPosition.from_dict(option_dict)
assert option.ticker == "AAPL"
assert option.position_type == "option"
assert option.quantity == 10
assert option.beta == 1.2
assert option.beta_adjusted_exposure == 1800.0
assert option.market_exposure == 1500.0
assert option.strike == 150.0
assert option.expiry == "2023-01-01"
assert option.option_type == "CALL"
assert option.delta == 0.7
assert option.delta_exposure == 1050.0
assert option.notional_value == 15000.0
assert option.underlying_beta == 1.2
assert option.price == 15.0
class TestExposureBreakdown:
"""Tests for the ExposureBreakdown class."""
def test_exposure_breakdown_init(self):
"""Test basic initialization of ExposureBreakdown."""
exposure = ExposureBreakdown(
stock_exposure=15000.0,
stock_beta_adjusted=18000.0,
option_delta_exposure=1050.0,
option_beta_adjusted=1260.0,
total_exposure=16050.0,
total_beta_adjusted=19260.0,
description="Test Exposure",
formula="Stock + Options",
components={"stock": 15000.0, "options": 1050.0},
)
assert exposure.stock_exposure == 15000.0
assert exposure.stock_beta_adjusted == 18000.0
assert exposure.option_delta_exposure == 1050.0
assert exposure.option_beta_adjusted == 1260.0
assert exposure.total_exposure == 16050.0
assert exposure.total_beta_adjusted == 19260.0
assert exposure.description == "Test Exposure"
assert exposure.formula == "Stock + Options"
assert exposure.components == {"stock": 15000.0, "options": 1050.0}
def test_exposure_breakdown_to_dict(self):
"""Test conversion of ExposureBreakdown to dictionary."""
exposure = ExposureBreakdown(
stock_exposure=15000.0,
stock_beta_adjusted=18000.0,
option_delta_exposure=1050.0,
option_beta_adjusted=1260.0,
total_exposure=16050.0,
total_beta_adjusted=19260.0,
description="Test Exposure",
formula="Stock + Options",
components={"stock": 15000.0, "options": 1050.0},
)
exposure_dict = exposure.to_dict()
assert exposure_dict["stock_exposure"] == 15000.0
assert exposure_dict["stock_beta_adjusted"] == 18000.0
assert exposure_dict["option_delta_exposure"] == 1050.0
assert exposure_dict["option_beta_adjusted"] == 1260.0
assert exposure_dict["total_exposure"] == 16050.0
assert exposure_dict["total_beta_adjusted"] == 19260.0
assert exposure_dict["description"] == "Test Exposure"
assert exposure_dict["formula"] == "Stock + Options"
assert exposure_dict["components"] == {"stock": 15000.0, "options": 1050.0}
def test_exposure_breakdown_from_dict(self):
"""Test creation of ExposureBreakdown from dictionary."""
exposure_dict = {
"stock_exposure": 15000.0,
"stock_beta_adjusted": 18000.0,
"option_delta_exposure": 1050.0,
"option_beta_adjusted": 1260.0,
"total_exposure": 16050.0,
"total_beta_adjusted": 19260.0,
"description": "Test Exposure",
"formula": "Stock + Options",
"components": {"stock": 15000.0, "options": 1050.0},
}
exposure = ExposureBreakdown.from_dict(exposure_dict)
assert exposure.stock_exposure == 15000.0
assert exposure.stock_beta_adjusted == 18000.0
assert exposure.option_delta_exposure == 1050.0
assert exposure.option_beta_adjusted == 1260.0
assert exposure.total_exposure == 16050.0
assert exposure.total_beta_adjusted == 19260.0
assert exposure.description == "Test Exposure"
assert exposure.formula == "Stock + Options"
assert exposure.components == {"stock": 15000.0, "options": 1050.0}
class TestPortfolioGroup:
"""Tests for the PortfolioGroup class."""
def test_portfolio_group_init(self):
"""Test basic initialization of PortfolioGroup."""
stock = StockPosition(
ticker="AAPL",
quantity=100,
beta=1.2,
market_exposure=15000.0,
beta_adjusted_exposure=18000.0,
)
option = OptionPosition(
ticker="AAPL",
position_type="option",
quantity=10,
beta=1.2,
beta_adjusted_exposure=1800.0,
market_exposure=1500.0,
strike=150.0,
expiry="2023-01-01",
option_type="CALL",
delta=0.7,
delta_exposure=1050.0,
notional_value=15000.0,
underlying_beta=1.2,
)
group = PortfolioGroup(
ticker="AAPL",
stock_position=stock,
option_positions=[option],
net_exposure=16050.0,
beta=1.2,
beta_adjusted_exposure=19260.0,
total_delta_exposure=1050.0,
options_delta_exposure=1050.0,
)
assert group.ticker == "AAPL"
assert group.stock_position == stock
assert group.option_positions == [option]
assert group.net_exposure == 16050.0
assert group.beta == 1.2
assert group.beta_adjusted_exposure == 19260.0
assert group.total_delta_exposure == 1050.0
assert group.options_delta_exposure == 1050.0
assert group.call_count == 1
assert group.put_count == 0
def test_portfolio_group_to_dict(self):
"""Test conversion of PortfolioGroup to dictionary."""
stock = StockPosition(
ticker="AAPL",
quantity=100,
beta=1.2,
market_exposure=15000.0,
beta_adjusted_exposure=18000.0,
)
option = OptionPosition(
ticker="AAPL",
position_type="option",
quantity=10,
beta=1.2,
beta_adjusted_exposure=1800.0,
market_exposure=1500.0,
strike=150.0,
expiry="2023-01-01",
option_type="CALL",
delta=0.7,
delta_exposure=1050.0,
notional_value=15000.0,
underlying_beta=1.2,
)
group = PortfolioGroup(
ticker="AAPL",
stock_position=stock,
option_positions=[option],
net_exposure=16050.0,
beta=1.2,
beta_adjusted_exposure=19260.0,
total_delta_exposure=1050.0,
options_delta_exposure=1050.0,
)
group_dict = group.to_dict()
assert group_dict["ticker"] == "AAPL"
assert group_dict["stock_position"] == stock.to_dict()
assert group_dict["option_positions"] == [option.to_dict()]
assert group_dict["net_exposure"] == 16050.0
assert group_dict["beta"] == 1.2
assert group_dict["beta_adjusted_exposure"] == 19260.0
assert group_dict["total_delta_exposure"] == 1050.0
assert group_dict["options_delta_exposure"] == 1050.0
assert group_dict["call_count"] == 1
assert group_dict["put_count"] == 0
def test_portfolio_group_from_dict(self):
"""Test creation of PortfolioGroup from dictionary."""
stock_dict = {
"ticker": "AAPL",
"quantity": 100,
"beta": 1.2,
"market_exposure": 15000.0,
"beta_adjusted_exposure": 18000.0,
"position_type": "stock",
}
option_dict = {
"ticker": "AAPL",
"position_type": "option",
"quantity": 10,
"beta": 1.2,
"beta_adjusted_exposure": 1800.0,
"market_exposure": 1500.0,
"strike": 150.0,
"expiry": "2023-01-01",
"option_type": "CALL",
"delta": 0.7,
"delta_exposure": 1050.0,
"notional_value": 15000.0,
"underlying_beta": 1.2,
}
group_dict = {
"ticker": "AAPL",
"stock_position": stock_dict,
"option_positions": [option_dict],
"net_exposure": 16050.0,
"beta": 1.2,
"beta_adjusted_exposure": 19260.0,
"total_delta_exposure": 1050.0,
"options_delta_exposure": 1050.0,
"call_count": 1,
"put_count": 0,
}
group = PortfolioGroup.from_dict(group_dict)
assert group.ticker == "AAPL"
assert group.stock_position.ticker == "AAPL"
assert group.stock_position.quantity == 100
assert len(group.option_positions) == 1
assert group.option_positions[0].ticker == "AAPL"
assert group.option_positions[0].option_type == "CALL"
assert group.net_exposure == 16050.0
assert group.beta == 1.2
assert group.beta_adjusted_exposure == 19260.0
assert group.total_delta_exposure == 1050.0
assert group.options_delta_exposure == 1050.0
assert group.call_count == 1
assert group.put_count == 0
def test_create_portfolio_group(self):
"""Test the create_portfolio_group function."""
stock_data = {
"ticker": "AAPL",
"quantity": 100,
"beta": 1.2,
"market_exposure": 15000.0,
"beta_adjusted_exposure": 18000.0,
"description": "APPLE INC",
"price": 150.0,
}
option_data = [
{
"ticker": "AAPL",
"option_symbol": "-AAPL250417C220",
"description": "AAPL APR 17 2025 $220 CALL",
"quantity": 10,
"beta": 1.2,
"beta_adjusted_exposure": 1800.0,
"market_exposure": 1500.0,
"strike": 220.0,
"expiry": "2025-04-17",
"option_type": "CALL",
"delta": 0.7,
"delta_exposure": 1050.0,
"notional_value": 15000.0,
"price": 15.0,
}
]
group = create_portfolio_group(stock_data, option_data)
assert group.ticker == "AAPL"
assert group.stock_position.ticker == "AAPL"
assert group.stock_position.quantity == 100
assert group.stock_position.price == 150.0
assert len(group.option_positions) == 1
assert group.option_positions[0].ticker == "AAPL"
assert group.option_positions[0].option_type == "CALL"
assert group.option_positions[0].strike == 220.0
assert group.option_positions[0].expiry == "2025-04-17"
assert group.option_positions[0].price == 15.0
assert group.net_exposure == 16050.0 # 15000 + 1050
assert group.beta == 1.2
assert group.beta_adjusted_exposure == 19800.0 # 18000 + 1800
assert group.total_delta_exposure == 1050.0
assert group.options_delta_exposure == 1050.0
assert group.call_count == 1
assert group.put_count == 0
class TestPortfolioSummary:
"""Tests for the PortfolioSummary class."""
def test_portfolio_summary_init(self):
"""Test basic initialization of PortfolioSummary."""
long_exposure = ExposureBreakdown(
stock_exposure=15000.0,
stock_beta_adjusted=18000.0,
option_delta_exposure=1050.0,
option_beta_adjusted=1260.0,
total_exposure=16050.0,
total_beta_adjusted=19260.0,
description="Long Exposure",
formula="Long Stock + Long Call Delta + Short Put Delta",
components={"Long Stock": 15000.0, "Long Options": 1050.0},
)
short_exposure = ExposureBreakdown(
stock_exposure=5000.0,
stock_beta_adjusted=6000.0,
option_delta_exposure=500.0,
option_beta_adjusted=600.0,
total_exposure=5500.0,
total_beta_adjusted=6600.0,
description="Short Exposure",
formula="Short Stock + Short Call Delta + Long Put Delta",
components={"Short Stock": 5000.0, "Short Options": 500.0},
)
options_exposure = ExposureBreakdown(
stock_exposure=0.0,
stock_beta_adjusted=0.0,
option_delta_exposure=550.0,
option_beta_adjusted=660.0,
total_exposure=550.0,
total_beta_adjusted=660.0,
description="Options Exposure",
formula="Long Options Delta - Short Options Delta",
components={"Long Options": 1050.0, "Short Options": 500.0, "Net": 550.0},
)
cash_position = StockPosition(
ticker="SPAXX",
quantity=1,
beta=0.0,
market_exposure=5000.0,
beta_adjusted_exposure=0.0,
price=5000.0,
)
summary = PortfolioSummary(
net_market_exposure=10550.0, # 16050 - 5500
portfolio_beta=1.2,
long_exposure=long_exposure,
short_exposure=short_exposure,
options_exposure=options_exposure,
short_percentage=25.5, # (5500 / (16050 + 5500)) * 100
cash_like_positions=[cash_position],
cash_like_value=5000.0,
cash_like_count=1,
cash_percentage=32.1, # (5000 / (10550 + 5000)) * 100
portfolio_estimate_value=15550.0, # 10550 + 5000
)
assert summary.net_market_exposure == 10550.0
assert summary.portfolio_beta == 1.2
assert summary.long_exposure == long_exposure
assert summary.short_exposure == short_exposure
assert summary.options_exposure == options_exposure
assert summary.short_percentage == 25.5
assert summary.cash_like_positions == [cash_position]
assert summary.cash_like_value == 5000.0
assert summary.cash_like_count == 1
assert summary.cash_percentage == 32.1
assert summary.portfolio_estimate_value == 15550.0
assert summary.help_text is not None # Help text should be initialized
def test_portfolio_summary_to_dict(self):
"""Test conversion of PortfolioSummary to dictionary."""
long_exposure = ExposureBreakdown(
stock_exposure=15000.0,
stock_beta_adjusted=18000.0,
option_delta_exposure=1050.0,
option_beta_adjusted=1260.0,
total_exposure=16050.0,
total_beta_adjusted=19260.0,
description="Long Exposure",
formula="Long Stock + Long Call Delta + Short Put Delta",
components={"Long Stock": 15000.0, "Long Options": 1050.0},
)
short_exposure = ExposureBreakdown(
stock_exposure=5000.0,
stock_beta_adjusted=6000.0,
option_delta_exposure=500.0,
option_beta_adjusted=600.0,
total_exposure=5500.0,
total_beta_adjusted=6600.0,
description="Short Exposure",
formula="Short Stock + Short Call Delta + Long Put Delta",
components={"Short Stock": 5000.0, "Short Options": 500.0},
)
options_exposure = ExposureBreakdown(
stock_exposure=0.0,
stock_beta_adjusted=0.0,
option_delta_exposure=550.0,
option_beta_adjusted=660.0,
total_exposure=550.0,
total_beta_adjusted=660.0,
description="Options Exposure",
formula="Long Options Delta - Short Options Delta",
components={"Long Options": 1050.0, "Short Options": 500.0, "Net": 550.0},
)
cash_position = StockPosition(
ticker="SPAXX",
quantity=1,
beta=0.0,
market_exposure=5000.0,
beta_adjusted_exposure=0.0,
)
# Test timestamp
test_timestamp = "2025-04-08T12:34:56.789012"
summary = PortfolioSummary(
net_market_exposure=10550.0,
portfolio_beta=1.2,
long_exposure=long_exposure,
short_exposure=short_exposure,
options_exposure=options_exposure,
short_percentage=25.5,
cash_like_positions=[cash_position],
cash_like_value=5000.0,
cash_like_count=1,
cash_percentage=32.1,
portfolio_estimate_value=15550.0,
price_updated_at=test_timestamp,
)
summary_dict = summary.to_dict()
assert summary_dict["net_market_exposure"] == 10550.0
assert summary_dict["portfolio_beta"] == 1.2
assert summary_dict["long_exposure"] == long_exposure.to_dict()
assert summary_dict["short_exposure"] == short_exposure.to_dict()
assert summary_dict["options_exposure"] == options_exposure.to_dict()
assert summary_dict["short_percentage"] == 25.5
assert summary_dict["cash_like_positions"] == [cash_position.to_dict()]
assert summary_dict["cash_like_value"] == 5000.0
assert summary_dict["cash_like_count"] == 1
assert summary_dict["cash_percentage"] == 32.1
assert summary_dict["portfolio_estimate_value"] == 15550.0
assert "help_text" in summary_dict
assert summary_dict["price_updated_at"] == test_timestamp
def test_portfolio_summary_from_dict(self):
"""Test creation of PortfolioSummary from dictionary."""
long_exposure_dict = {
"stock_exposure": 15000.0,
"stock_beta_adjusted": 18000.0,
"option_delta_exposure": 1050.0,
"option_beta_adjusted": 1260.0,
"total_exposure": 16050.0,
"total_beta_adjusted": 19260.0,
"description": "Long Exposure",
"formula": "Long Stock + Long Call Delta + Short Put Delta",
"components": {"Long Stock": 15000.0, "Long Options": 1050.0},
}
short_exposure_dict = {
"stock_exposure": 5000.0,
"stock_beta_adjusted": 6000.0,
"option_delta_exposure": 500.0,
"option_beta_adjusted": 600.0,
"total_exposure": 5500.0,
"total_beta_adjusted": 6600.0,
"description": "Short Exposure",
"formula": "Short Stock + Short Call Delta + Long Put Delta",
"components": {"Short Stock": 5000.0, "Short Options": 500.0},
}
options_exposure_dict = {
"stock_exposure": 0.0,
"stock_beta_adjusted": 0.0,
"option_delta_exposure": 550.0,
"option_beta_adjusted": 660.0,
"total_exposure": 550.0,
"total_beta_adjusted": 660.0,
"description": "Options Exposure",
"formula": "Long Options Delta - Short Options Delta",
"components": {
"Long Options": 1050.0,
"Short Options": 500.0,
"Net": 550.0,
},
}
cash_position_dict = {
"ticker": "SPAXX",
"quantity": 1,
"beta": 0.0,
"market_exposure": 5000.0,
"beta_adjusted_exposure": 0.0,
"position_type": "stock",
}
summary_dict = {
"net_market_exposure": 10550.0,
"portfolio_beta": 1.2,
"long_exposure": long_exposure_dict,
"short_exposure": short_exposure_dict,
"options_exposure": options_exposure_dict,
"short_percentage": 25.5,
"cash_like_positions": [cash_position_dict],
"cash_like_value": 5000.0,
"cash_like_count": 1,
"cash_percentage": 32.1,
"portfolio_estimate_value": 15550.0,
"help_text": {"key": "value"}, # Simplified help text for testing
"price_updated_at": "2025-04-08T12:34:56.789012",
}
summary = PortfolioSummary.from_dict(summary_dict)
assert summary.net_market_exposure == 10550.0
assert summary.portfolio_beta == 1.2
assert summary.long_exposure.total_exposure == 16050.0
assert summary.short_exposure.total_exposure == 5500.0
assert summary.options_exposure.total_exposure == 550.0
assert summary.short_percentage == 25.5
assert len(summary.cash_like_positions) == 1
assert summary.cash_like_positions[0].ticker == "SPAXX"
assert summary.cash_like_value == 5000.0
assert summary.cash_like_count == 1
assert summary.cash_percentage == 32.1
assert summary.portfolio_estimate_value == 15550.0
assert summary.price_updated_at == "2025-04-08T12:34:56.789012"