Complete API documentation for the Trade Analytics library.
The trade_analytics package provides data models and utilities for trading analytics. All public classes and exceptions are exported from the main package.
trade_analytics/
├── __init__.py # Main exports
├── models.py # TradeSide, Trade, Position, MarketData
├── calculator.py # PortfolioCalculator
└── exceptions.py # Exception hierarchy
All public classes can be imported from the main package:
from trade_analytics import (
# Models
TradeSide,
Trade,
Position,
MarketData,
# Calculator
PortfolioCalculator,
# Exceptions
TradingError,
InvalidTradeError,
InsufficientFundsError,
MarketClosedError,
MissingMarketDataError,
)Enumeration representing the direction of a trade.
from trade_analytics import TradeSide| Value | Description |
|---|---|
TradeSide.BUY |
Buy order (going long) |
TradeSide.SELL |
Sell order (going short or closing long) |
from trade_analytics import TradeSide
side = TradeSide.BUY
print(side.value) # "BUY"Immutable dataclass representing a trade execution.
from trade_analytics import TradeTrade(
symbol: str,
side: TradeSide,
quantity: Decimal,
price: Decimal,
timestamp: datetime,
trade_id: str = ""
)| Parameter | Type | Description |
|---|---|---|
symbol |
str |
Ticker symbol (e.g., "AAPL"). Automatically uppercased. |
side |
TradeSide |
Trade direction (BUY or SELL) |
quantity |
Decimal |
Number of shares/units. Must be positive. |
price |
Decimal |
Execution price. Must be positive. |
timestamp |
datetime |
Execution timestamp (UTC recommended) |
trade_id |
str |
Optional unique identifier for the trade |
All constructor parameters are available as read-only attributes.
| Method | Returns | Description |
|---|---|---|
to_dict() |
Dict[str, Any] |
Convert trade to dictionary |
from_dict(data) |
Trade |
Class method to create Trade from dictionary |
The Trade class validates all inputs on construction and raises InvalidTradeError for:
- Empty or whitespace-only symbol (
reason="empty_symbol") - Zero or negative quantity (
reason="invalid_quantity") - Zero or negative price (
reason="invalid_price")
from datetime import datetime, timezone
from decimal import Decimal
from trade_analytics import Trade, TradeSide
trade = Trade(
symbol="AAPL",
side=TradeSide.BUY,
quantity=Decimal("100"),
price=Decimal("150.50"),
timestamp=datetime.now(timezone.utc),
trade_id="T001"
)
# Access attributes
print(trade.symbol) # "AAPL"
print(trade.quantity) # Decimal("100")
# Serialize to dictionary
data = trade.to_dict()
# Deserialize from dictionary
trade2 = Trade.from_dict(data)Dataclass representing a portfolio position.
from trade_analytics import PositionPosition(
symbol: str,
quantity: Decimal,
avg_price: Decimal,
unrealized_pnl: Decimal = Decimal("0")
)| Parameter | Type | Description |
|---|---|---|
symbol |
str |
Ticker symbol. Automatically uppercased. |
quantity |
Decimal |
Net position (positive=long, negative=short, zero=flat) |
avg_price |
Decimal |
Volume-weighted average entry price. Must be non-negative. |
unrealized_pnl |
Decimal |
Unrealized profit/loss. Defaults to 0. |
All constructor parameters are available as attributes. Unlike Trade, Position is mutable.
| Method | Returns | Description |
|---|---|---|
to_dict() |
Dict[str, Any] |
Convert position to dictionary |
from_dict(data) |
Position |
Class method to create Position from dictionary |
The Position class validates inputs and raises InvalidTradeError for:
- Empty or whitespace-only symbol (
reason="empty_symbol") - Negative average price (
reason="invalid_avg_price")
from decimal import Decimal
from trade_analytics import Position
# Long position
long_position = Position(
symbol="AAPL",
quantity=Decimal("100"),
avg_price=Decimal("150.00")
)
# Short position
short_position = Position(
symbol="TSLA",
quantity=Decimal("-50"), # Negative = short
avg_price=Decimal("200.00")
)
# Serialize/deserialize
data = long_position.to_dict()
restored = Position.from_dict(data)Immutable dataclass representing current market quote data.
from trade_analytics import MarketDataMarketData(
symbol: str,
bid: Decimal,
ask: Decimal,
last: Decimal,
volume: int,
timestamp: datetime = <current UTC time>
)| Parameter | Type | Description |
|---|---|---|
symbol |
str |
Ticker symbol. Automatically uppercased. |
bid |
Decimal |
Best bid price. Must be non-negative. |
ask |
Decimal |
Best ask price. Must be non-negative and >= bid. |
last |
Decimal |
Last traded price. Must be non-negative. |
volume |
int |
Trading volume. Must be non-negative. |
timestamp |
datetime |
Quote timestamp. Defaults to current UTC time. |
| Property | Type | Description |
|---|---|---|
spread |
Decimal |
Bid-ask spread (ask - bid) |
mid |
Decimal |
Mid price ((bid + ask) / 2) |
| Method | Returns | Description |
|---|---|---|
to_dict() |
Dict[str, Any] |
Convert market data to dictionary |
from_dict(data) |
MarketData |
Class method to create MarketData from dictionary |
The MarketData class validates inputs and raises InvalidTradeError for:
- Empty or whitespace-only symbol (
reason="empty_symbol") - Negative bid price (
reason="invalid_bid") - Negative ask price (
reason="invalid_ask") - Bid greater than ask (
reason="invalid_spread") - Negative last price (
reason="invalid_last") - Negative volume (
reason="invalid_volume")
from decimal import Decimal
from trade_analytics import MarketData
market = MarketData(
symbol="AAPL",
bid=Decimal("150.00"),
ask=Decimal("150.10"),
last=Decimal("150.05"),
volume=1000000
)
# Access computed properties
print(market.spread) # Decimal("0.10")
print(market.mid) # Decimal("150.05")Static class providing portfolio analytics calculations.
from trade_analytics import PortfolioCalculatorAll methods are static (pure functions) that do not modify input data.
Calculate the total market value of all positions.
@staticmethod
def calculate_total_value(
positions: List[Position],
market_data: Dict[str, MarketData]
) -> DecimalParameters:
| Parameter | Type | Description |
|---|---|---|
positions |
List[Position] |
List of portfolio positions |
market_data |
Dict[str, MarketData] |
Dictionary mapping symbols to market data |
Returns: Decimal - Total portfolio market value (sum of quantity × last price)
Raises: MissingMarketDataError if market data is missing for any position's symbol
Example:
from decimal import Decimal
from trade_analytics import Position, MarketData, PortfolioCalculator
positions = [
Position(symbol="AAPL", quantity=Decimal("100"), avg_price=Decimal("150.00"))
]
market_data = {
"AAPL": MarketData(
symbol="AAPL",
bid=Decimal("155.00"),
ask=Decimal("155.10"),
last=Decimal("155.05"),
volume=1000000
)
}
total = PortfolioCalculator.calculate_total_value(positions, market_data)
print(total) # Decimal("15505.00")Calculate the total unrealized P&L across all positions.
@staticmethod
def calculate_pnl(
positions: List[Position],
market_data: Dict[str, MarketData]
) -> DecimalParameters:
| Parameter | Type | Description |
|---|---|---|
positions |
List[Position] |
List of portfolio positions |
market_data |
Dict[str, MarketData] |
Dictionary mapping symbols to market data |
Returns: Decimal - Total unrealized P&L (positive = profit, negative = loss)
Calculation: Sum of quantity × (current_price - avg_price) for each position
Raises: MissingMarketDataError if market data is missing for any position's symbol
Example:
from decimal import Decimal
from trade_analytics import Position, MarketData, PortfolioCalculator
positions = [
Position(symbol="AAPL", quantity=Decimal("100"), avg_price=Decimal("150.00"))
]
market_data = {
"AAPL": MarketData(
symbol="AAPL",
bid=Decimal("155.00"),
ask=Decimal("155.10"),
last=Decimal("155.05"),
volume=1000000
)
}
pnl = PortfolioCalculator.calculate_pnl(positions, market_data)
print(pnl) # Decimal("505.00") = 100 × (155.05 - 150.00)Calculate the absolute value exposure for each symbol.
@staticmethod
def calculate_exposure_by_symbol(
positions: List[Position],
market_data: Dict[str, MarketData]
) -> Dict[str, Decimal]Parameters:
| Parameter | Type | Description |
|---|---|---|
positions |
List[Position] |
List of portfolio positions |
market_data |
Dict[str, MarketData] |
Dictionary mapping symbols to market data |
Returns: Dict[str, Decimal] - Dictionary mapping symbols to their absolute exposure
Calculation: For each symbol, calculates abs(quantity × current_price). Multiple positions in the same symbol are aggregated.
Raises: MissingMarketDataError if market data is missing for any position's symbol
Example:
from decimal import Decimal
from trade_analytics import Position, MarketData, PortfolioCalculator
positions = [
Position(symbol="AAPL", quantity=Decimal("100"), avg_price=Decimal("150.00")),
Position(symbol="TSLA", quantity=Decimal("-50"), avg_price=Decimal("200.00")), # Short
]
market_data = {
"AAPL": MarketData(symbol="AAPL", bid=Decimal("155"), ask=Decimal("156"), last=Decimal("155.05"), volume=1000),
"TSLA": MarketData(symbol="TSLA", bid=Decimal("210"), ask=Decimal("211"), last=Decimal("210.50"), volume=1000),
}
exposure = PortfolioCalculator.calculate_exposure_by_symbol(positions, market_data)
# {"AAPL": Decimal("15505.00"), "TSLA": Decimal("10525.00")}All exceptions inherit from TradingError, which inherits from Exception.
Exception
└── TradingError
├── InvalidTradeError
├── InsufficientFundsError
├── MarketClosedError
└── MissingMarketDataError
Base exception for all trading-related errors.
from trade_analytics import TradingError| Attribute | Type | Description |
|---|---|---|
message |
str |
Human-readable error description |
from trade_analytics import TradingError
try:
# ... trading operation
pass
except TradingError as e:
print(f"Trading error: {e.message}")Raised when trade parameters are invalid.
from trade_analytics import InvalidTradeError| Attribute | Type | Description |
|---|---|---|
message |
str |
Human-readable error description |
trade_details |
dict |
Dictionary containing the invalid trade details |
reason |
str |
Specific reason code (e.g., "empty_symbol", "invalid_quantity") |
| Reason | Description |
|---|---|
empty_symbol |
Symbol is empty or whitespace |
invalid_quantity |
Quantity is zero or negative |
invalid_price |
Price is zero or negative |
invalid_avg_price |
Average price is negative |
invalid_bid |
Bid price is negative |
invalid_ask |
Ask price is negative |
invalid_spread |
Bid is greater than ask |
invalid_last |
Last price is negative |
invalid_volume |
Volume is negative |
from decimal import Decimal
from trade_analytics import Trade, TradeSide, InvalidTradeError
from datetime import datetime, timezone
try:
trade = Trade(
symbol="AAPL",
side=TradeSide.BUY,
quantity=Decimal("-100"), # Invalid: negative
price=Decimal("150.00"),
timestamp=datetime.now(timezone.utc)
)
except InvalidTradeError as e:
print(f"Invalid trade: {e.message}")
print(f"Reason: {e.reason}") # "invalid_quantity"
print(f"Details: {e.trade_details}") # {"quantity": "-100"}Raised when there are insufficient funds for a trade.
from trade_analytics import InsufficientFundsError| Attribute | Type | Description |
|---|---|---|
message |
str |
Human-readable error description |
required |
Decimal |
Amount of funds required |
available |
Decimal |
Amount of funds available |
from decimal import Decimal
from trade_analytics import InsufficientFundsError
try:
# ... trading operation
raise InsufficientFundsError(
"Insufficient funds for trade",
required=Decimal("10000.00"),
available=Decimal("5000.00")
)
except InsufficientFundsError as e:
print(f"Need ${e.required}, have ${e.available}")Raised when attempting to trade in a closed market.
from trade_analytics import MarketClosedError| Attribute | Type | Description |
|---|---|---|
message |
str |
Human-readable error description |
symbol |
str |
The symbol of the closed market |
market_hours |
str |
Description of market hours |
from trade_analytics import MarketClosedError
try:
# ... trading operation
raise MarketClosedError(
"Market is closed",
symbol="NYSE:AAPL",
market_hours="9:30 AM - 4:00 PM ET"
)
except MarketClosedError as e:
print(f"Cannot trade {e.symbol}. Hours: {e.market_hours}")Raised when market data is unavailable for a required symbol.
from trade_analytics import MissingMarketDataError| Attribute | Type | Description |
|---|---|---|
message |
str |
Human-readable error description |
symbol |
str |
The symbol missing market data |
available_symbols |
List[str] |
List of symbols with available data |
from decimal import Decimal
from trade_analytics import Position, PortfolioCalculator, MissingMarketDataError
positions = [
Position(symbol="TSLA", quantity=Decimal("100"), avg_price=Decimal("200.00"))
]
market_data = {} # Empty - no data
try:
PortfolioCalculator.calculate_total_value(positions, market_data)
except MissingMarketDataError as e:
print(f"Missing data for: {e.symbol}")
print(f"Available symbols: {e.available_symbols}")| Class | Mutability | Key Fields |
|---|---|---|
TradeSide |
N/A (enum) | BUY, SELL |
Trade |
Immutable | symbol, side, quantity, price, timestamp |
Position |
Mutable | symbol, quantity, avg_price, unrealized_pnl |
MarketData |
Immutable | symbol, bid, ask, last, volume |
from decimal import Decimal
from datetime import datetime
from typing import Dict, List
from trade_analytics import Position, MarketData
# Position list (portfolio)
positions: List[Position]
# Market data dictionary
market_data: Dict[str, MarketData]
# Exposure by symbol
exposure: Dict[str, Decimal]