A class describes behaviour; instances hold positions
self when a bound method is called. These distinctions are described in the Python classes tutorial.Here is a fictional long equity position. Quantity is in shares, the mark is in USD per share, and value is in USD. Integer-valued marks keep the arithmetic transparent; fees, FX and corporate actions are outside this example.
class Position:
def __init__(self, quantity: int, mark_usd: float) -> None:
self.quantity = quantity
self.mark_usd = mark_usd
def value_usd(self) -> float:
return self.quantity * self.mark_usd
position = Position(2, 100.0)
other = Position(1, 100.0)
print(position.value_usd())
# 200.0
alias = position
alias.mark_usd = 110.0
print(position.value_usd(), other.value_usd())
# 220.0 100.0
print(alias is position)
# TrueBoth positions use the same method definition, but their attributes are separate. Updating the first position's mark changes its value from USD 200 to USD 220; the second remains worth USD 100. The USD 20 difference is a mark-to-market gain under these fixed-holding assumptions, not a cash receipt.
alias = position creates another name for the same object, so the update through alias is visible through position. It does not create a snapshot. This distinction between identity and changing state matters when saving backtest history. Python's data model explains object identity and mutability.Shared state is a separate choice
A mutable attribute placed on the class can accidentally connect independent books:
class BadBook:
positions = []
first = BadBook()
second = BadBook()
first.positions.append("ABC")
print(second.positions)
# ['ABC']self.positions = [] inside __init__ when each book needs its own list. Sharing a curve or reference-data object can be intentional; sharing an order list by accident can contaminate a simulation. See the official distinction between class and instance variables.An interface is a promise about meaning
value_usd(): callers expect a value in USD. A matching method name alone cannot establish currency, valuation time or whether accrued amounts are included. Those belong in the contract and its tests. With duck typing, unrelated classes can supply the same useful interface without inheriting from a common parent.mark_usd: float does not reject a missing, nonfinite or incorrectly denominated mark. Encapsulation means organising access around a supported interface; an underscore convention is not a security boundary.Use classes when persistent identity, controlled state or interchangeable behaviour helps organise the problem. A small payoff calculation may be clearer as a function, and a large simulation may keep its numerical data in arrays. OOP by itself guarantees neither faster execution nor a better financial model.
Sources
- Python tutorial: classes, class and instance variables, inheritance, and private-variable conventions.
- Python language reference: objects, values and types.
- Python typing documentation: runtime-enforcement note.
The examples were executed with Python 3.14.4 using the standard library.