|
|
"""Tests for pending activity handling in portfolio processing.""" |
|
|
|
|
|
import pandas as pd |
|
|
|
|
|
from src.folio.portfolio import ( |
|
|
calculate_portfolio_summary, |
|
|
process_portfolio_data, |
|
|
update_portfolio_summary_with_prices, |
|
|
) |
|
|
|
|
|
|
|
|
def test_pending_activity_extraction(): |
|
|
"""Test that pending activity value is correctly extracted from CSV data.""" |
|
|
|
|
|
df = pd.DataFrame( |
|
|
[ |
|
|
{ |
|
|
"Symbol": "AAPL", |
|
|
"Description": "APPLE INC", |
|
|
"Quantity": 100, |
|
|
"Current Value": "$10000.00", |
|
|
"Last Price": "$100.00", |
|
|
"Type": "Margin", |
|
|
"Percent Of Account": "10%", |
|
|
"Average Cost Basis": "$90.00", |
|
|
}, |
|
|
{ |
|
|
"Symbol": "Pending Activity", |
|
|
"Description": "", |
|
|
"Quantity": None, |
|
|
"Current Value": "$5000.00", |
|
|
"Last Price": None, |
|
|
"Type": None, |
|
|
"Percent Of Account": None, |
|
|
"Average Cost Basis": None, |
|
|
}, |
|
|
] |
|
|
) |
|
|
|
|
|
|
|
|
groups, summary, _ = process_portfolio_data( |
|
|
df, update_prices=False |
|
|
) |
|
|
|
|
|
|
|
|
assert summary.pending_activity_value == 5000.0 |
|
|
|
|
|
|
|
|
|
|
|
from pytest import approx |
|
|
|
|
|
assert summary.portfolio_estimate_value == approx( |
|
|
15000.0, rel=0.1 |
|
|
) |
|
|
|
|
|
|
|
|
def test_pending_activity_with_missing_value(): |
|
|
"""Test that pending activity with missing value is handled correctly.""" |
|
|
|
|
|
df = pd.DataFrame( |
|
|
[ |
|
|
{ |
|
|
"Symbol": "AAPL", |
|
|
"Description": "APPLE INC", |
|
|
"Quantity": 100, |
|
|
"Current Value": "$10000.00", |
|
|
"Last Price": "$100.00", |
|
|
"Type": "Margin", |
|
|
"Percent Of Account": "10%", |
|
|
"Average Cost Basis": "$90.00", |
|
|
}, |
|
|
{ |
|
|
"Symbol": "Pending Activity", |
|
|
"Description": "", |
|
|
"Quantity": None, |
|
|
"Current Value": None, |
|
|
"Last Price": None, |
|
|
"Type": None, |
|
|
"Percent Of Account": None, |
|
|
"Average Cost Basis": None, |
|
|
}, |
|
|
] |
|
|
) |
|
|
|
|
|
|
|
|
groups, summary, _ = process_portfolio_data( |
|
|
df, update_prices=False |
|
|
) |
|
|
|
|
|
|
|
|
assert summary.pending_activity_value == 0.0 |
|
|
|
|
|
|
|
|
|
|
|
from pytest import approx |
|
|
|
|
|
assert summary.portfolio_estimate_value == approx( |
|
|
10000.0, rel=0.1 |
|
|
) |
|
|
|
|
|
|
|
|
def test_pending_activity_from_different_columns(): |
|
|
"""Test that pending activity value is correctly extracted from different columns.""" |
|
|
|
|
|
df = pd.DataFrame( |
|
|
[ |
|
|
{ |
|
|
"Symbol": "AAPL", |
|
|
"Description": "APPLE INC", |
|
|
"Quantity": 100, |
|
|
"Current Value": "$10000.00", |
|
|
"Last Price": "$100.00", |
|
|
"Type": "Margin", |
|
|
"Percent Of Account": "10%", |
|
|
"Average Cost Basis": "$90.00", |
|
|
"Last Price Change": "$0.00", |
|
|
"Today's Gain/Loss Dollar": "$0.00", |
|
|
}, |
|
|
{ |
|
|
"Symbol": "Pending Activity", |
|
|
"Description": "", |
|
|
"Quantity": None, |
|
|
"Current Value": None, |
|
|
"Last Price": None, |
|
|
"Type": None, |
|
|
"Percent Of Account": None, |
|
|
"Average Cost Basis": None, |
|
|
"Last Price Change": "$6000.00", |
|
|
"Today's Gain/Loss Dollar": "$0.00", |
|
|
}, |
|
|
] |
|
|
) |
|
|
|
|
|
|
|
|
groups, summary, _ = process_portfolio_data( |
|
|
df, update_prices=False |
|
|
) |
|
|
|
|
|
|
|
|
assert summary.pending_activity_value == 6000.0 |
|
|
|
|
|
|
|
|
|
|
|
from pytest import approx |
|
|
|
|
|
assert summary.portfolio_estimate_value == approx( |
|
|
16000.0, rel=0.1 |
|
|
) |
|
|
|
|
|
|
|
|
df = pd.DataFrame( |
|
|
[ |
|
|
{ |
|
|
"Symbol": "AAPL", |
|
|
"Description": "APPLE INC", |
|
|
"Quantity": 100, |
|
|
"Current Value": "$10000.00", |
|
|
"Last Price": "$100.00", |
|
|
"Type": "Margin", |
|
|
"Percent Of Account": "10%", |
|
|
"Average Cost Basis": "$90.00", |
|
|
"Last Price Change": "$0.00", |
|
|
"Today's Gain/Loss Dollar": "$0.00", |
|
|
}, |
|
|
{ |
|
|
"Symbol": "Pending Activity", |
|
|
"Description": "", |
|
|
"Quantity": None, |
|
|
"Current Value": None, |
|
|
"Last Price": None, |
|
|
"Type": None, |
|
|
"Percent Of Account": None, |
|
|
"Average Cost Basis": None, |
|
|
"Last Price Change": None, |
|
|
"Today's Gain/Loss Dollar": "$7000.00", |
|
|
}, |
|
|
] |
|
|
) |
|
|
|
|
|
|
|
|
groups, summary, _ = process_portfolio_data( |
|
|
df, update_prices=False |
|
|
) |
|
|
|
|
|
|
|
|
assert summary.pending_activity_value == 7000.0 |
|
|
|
|
|
|
|
|
|
|
|
assert summary.portfolio_estimate_value == approx( |
|
|
17000.0, rel=0.1 |
|
|
) |
|
|
|
|
|
|
|
|
def test_pending_activity_preserved_when_updating_prices(): |
|
|
"""Test that pending activity value is preserved when updating prices.""" |
|
|
|
|
|
from src.folio.data_model import PortfolioGroup, StockPosition |
|
|
|
|
|
stock_position = StockPosition( |
|
|
ticker="AAPL", |
|
|
quantity=100, |
|
|
beta=1.0, |
|
|
market_exposure=10000.0, |
|
|
beta_adjusted_exposure=10000.0, |
|
|
price=100.0, |
|
|
cost_basis=90.0, |
|
|
) |
|
|
|
|
|
group = PortfolioGroup( |
|
|
ticker="AAPL", |
|
|
stock_position=stock_position, |
|
|
option_positions=[], |
|
|
net_exposure=10000.0, |
|
|
beta=1.0, |
|
|
beta_adjusted_exposure=10000.0, |
|
|
total_delta_exposure=0.0, |
|
|
options_delta_exposure=0.0, |
|
|
) |
|
|
|
|
|
|
|
|
summary = calculate_portfolio_summary([group], [], 5000.0) |
|
|
|
|
|
|
|
|
assert summary.pending_activity_value == 5000.0 |
|
|
assert summary.portfolio_estimate_value == 15000.0 |
|
|
|
|
|
|
|
|
|
|
|
updated_summary = update_portfolio_summary_with_prices([group], summary) |
|
|
|
|
|
|
|
|
assert ( |
|
|
updated_summary.pending_activity_value == 5000.0 |
|
|
) |
|
|
assert ( |
|
|
updated_summary.portfolio_estimate_value > 15000.0 |
|
|
) |
|
|
|