investfly.models.marketdata

Market data models

@dataclass(frozen=True, eq=True)
class Security:

Class representing a security instrument that is traded in the market

Security( symbol: str, securityType: SecurityType)
symbol: str

Security Symbol

securityType: SecurityType

Security Type

@staticmethod
def createStock(symbol: str):
@staticmethod
def fromDict( jsonDict: Dict[str, Any]) -> Security:
def toDict(self) -> Dict[str, Any]:
class SecurityType(builtins.str, enum.Enum):

Enum representing Security Type (STOCK, ETF, CRYPTO, FOREX, FUTURE, OPTION)

STOCK = STOCK
ETF = ETF
CRYPTO = CRYPTO
FOREX = FOREX
OPTION = OPTION
FUTURE = FUTURE
@dataclass
class Quote:

Class representing Price Quote

Quote( symbol: str, date: datetime.datetime, lastPrice: float, prevClose: float | None = None, todayChange: float | None = None, todayChangePct: float | None = None, dayOpen: float | None = None, dayHigh: float | None = None, dayLow: float | None = None, volume: float | None = None)
symbol: str
date: datetime.datetime
lastPrice: float
prevClose: float | None = None
todayChange: float | None = None
todayChangePct: float | None = None
dayOpen: float | None = None
dayHigh: float | None = None
dayLow: float | None = None
volume: float | None = None
@staticmethod
def fromDict(jsonDict: Dict[str, Any]) -> Quote:
def toDict(self) -> Dict[str, Any]:
def toIndicatorValue( self, quoteField: QuoteField) -> investfly.models.common.DatedValue:
def toEODBar(self) -> Bar:
class QuoteField(builtins.str, enum.Enum):

Enum of all valid fields on Quote object

LASTPRICE = <QuoteField.LASTPRICE: 'LASTPRICE'>
DAY_OPEN = <QuoteField.DAY_OPEN: 'DAY_OPEN'>
DAY_HIGH = <QuoteField.DAY_HIGH: 'DAY_HIGH'>
DAY_LOW = <QuoteField.DAY_LOW: 'DAY_LOW'>
DAY_VOLUME = <QuoteField.DAY_VOLUME: 'DAY_VOLUME'>
PREV_DAY_CLOSE = <QuoteField.PREV_DAY_CLOSE: 'PREV_DAY_CLOSE'>
DAY_CHANGE = <QuoteField.DAY_CHANGE: 'DAY_CHANGE'>
DAY_CHANGE_PCT = <QuoteField.DAY_CHANGE_PCT: 'DAY_CHANGE_PCT'>
DAY_CHANGE_OPEN = <QuoteField.DAY_CHANGE_OPEN: 'DAY_CHANGE_OPEN'>
class Bar(builtins.dict):
symbol: str
barinterval: BarInterval
date: datetime.datetime
open: float
close: float
high: float
low: float
volume: float
class BarInterval(builtins.str, enum.Enum):

Enum to represent BarInterval

ONE_MINUTE = <BarInterval.ONE_MINUTE: 'ONE_MINUTE'>
FIVE_MINUTE = <BarInterval.FIVE_MINUTE: 'FIVE_MINUTE'>
FIFTEEN_MINUTE = <BarInterval.FIFTEEN_MINUTE: 'FIFTEEN_MINUTE'>
THIRTY_MINUTE = <BarInterval.THIRTY_MINUTE: 'THIRTY_MINUTE'>
SIXTY_MINUTE = <BarInterval.SIXTY_MINUTE: 'SIXTY_MINUTE'>
ONE_DAY = <BarInterval.ONE_DAY: 'ONE_DAY'>
def getMinutes(self) -> int:
class BarsGroup:
BarsGroup(barInterval: BarInterval)
barInterval
bars: List[Bar]
def addBars(self, bars: List[Bar]):
def addBar(self, bar: Bar):
def size(self) -> int:
@dataclass
class StockNews:
StockNews(date: datetime.datetime, title: str, description: str)
date: datetime.datetime
title: str
description: str
class FinancialField(builtins.str, enum.Enum):

Financial Fields supported by Invesfly

OUTSTANDING_SHARES = <FinancialField.OUTSTANDING_SHARES: 'OUTSTANDING_SHARES'>
MARKET_CAP = <FinancialField.MARKET_CAP: 'MARKET_CAP'>
REVENUE = <FinancialField.REVENUE: 'REVENUE'>
COST_OF_REVENUE = <FinancialField.COST_OF_REVENUE: 'COST_OF_REVENUE'>
GROSS_PROFIT = <FinancialField.GROSS_PROFIT: 'GROSS_PROFIT'>
OPERATING_EXPENSE = <FinancialField.OPERATING_EXPENSE: 'OPERATING_EXPENSE'>
OPERATING_INCOME = <FinancialField.OPERATING_INCOME: 'OPERATING_INCOME'>
NET_INCOME = <FinancialField.NET_INCOME: 'NET_INCOME'>
EPS = <FinancialField.EPS: 'EPS'>
CURR_ASSETS = <FinancialField.CURR_ASSETS: 'CURR_ASSETS'>
NON_CURR_ASSETS = <FinancialField.NON_CURR_ASSETS: 'NON_CURR_ASSETS'>
ASSETS = <FinancialField.ASSETS: 'ASSETS'>
CURR_LIABILITIES = <FinancialField.CURR_LIABILITIES: 'CURR_LIABILITIES'>
NON_CURR_LIABILITIES = <FinancialField.NON_CURR_LIABILITIES: 'NON_CURR_LIABILITIES'>
LIABILITIES = <FinancialField.LIABILITIES: 'LIABILITIES'>
LONG_TERM_DEBT = <FinancialField.LONG_TERM_DEBT: 'LONG_TERM_DEBT'>
EQUITY = <FinancialField.EQUITY: 'EQUITY'>
OPEARTING_CASH_FLOW = <FinancialField.OPEARTING_CASH_FLOW: 'OPEARTING_CASH_FLOW'>
INVESTING_CASH_FLOW = <FinancialField.INVESTING_CASH_FLOW: 'INVESTING_CASH_FLOW'>
FINANCING_CASH_FLOW = <FinancialField.FINANCING_CASH_FLOW: 'FINANCING_CASH_FLOW'>
NET_CASH_FLOW = <FinancialField.NET_CASH_FLOW: 'NET_CASH_FLOW'>
RETURN_ON_EQUITY = <FinancialField.RETURN_ON_EQUITY: 'RETURN_ON_EQUITY'>
RETURN_ON_ASSETS = <FinancialField.RETURN_ON_ASSETS: 'RETURN_ON_ASSETS'>
PRICE_TO_EARNINGS_RATIO = <FinancialField.PRICE_TO_EARNINGS_RATIO: 'PRICE_TO_EARNINGS_RATIO'>
PEG_RATIO = <FinancialField.PEG_RATIO: 'PEG_RATIO'>
PRICE_TO_SALES_RATIO = <FinancialField.PRICE_TO_SALES_RATIO: 'PRICE_TO_SALES_RATIO'>
PRICE_TO_BOOK_RATIO = <FinancialField.PRICE_TO_BOOK_RATIO: 'PRICE_TO_BOOK_RATIO'>
REVENUE_GROWTH = <FinancialField.REVENUE_GROWTH: 'REVENUE_GROWTH'>
EPS_GROWTH = <FinancialField.EPS_GROWTH: 'EPS_GROWTH'>
DEBT_EQUITY_RATIO = <FinancialField.DEBT_EQUITY_RATIO: 'DEBT_EQUITY_RATIO'>
CURRENT_RATIO = <FinancialField.CURRENT_RATIO: 'CURRENT_RATIO'>
GROSS_MARGIN = <FinancialField.GROSS_MARGIN: 'GROSS_MARGIN'>
PROFIT_MARGIN = <FinancialField.PROFIT_MARGIN: 'PROFIT_MARGIN'>
DIVIDEND_YIELD = <FinancialField.DIVIDEND_YIELD: 'DIVIDEND_YIELD'>
class FutureProduct(builtins.str, enum.Enum):

Future Product Enum. A few commented out since their contracts are not available.

futureType: FutureType
description: str
contractMultiplier: float
tickSize: float
listedMonths: List[str]
exchange: str
def getMonths(self) -> List[tuple[str, int]]:

Return (month_code, month_number) pairs in listed order.

MZC = <FutureProduct.MZC: 'MZC'>
LE = <FutureProduct.LE: 'LE'>
MZW = <FutureProduct.MZW: 'MZW'>
ZC = <FutureProduct.ZC: 'ZC'>
ZW = <FutureProduct.ZW: 'ZW'>
XW = <FutureProduct.XW: 'XW'>
XC = <FutureProduct.XC: 'XC'>
XK = <FutureProduct.XK: 'XK'>
ZS = <FutureProduct.ZS: 'ZS'>
MZS = <FutureProduct.MZS: 'MZS'>
MZL = <FutureProduct.MZL: 'MZL'>
MZM = <FutureProduct.MZM: 'MZM'>
HE = <FutureProduct.HE: 'HE'>
M6A = <FutureProduct.M6A: 'M6A'>
M6B = <FutureProduct.M6B: 'M6B'>
MCD = <FutureProduct.MCD: 'MCD'>
M6E = <FutureProduct.M6E: 'M6E'>
TN = <FutureProduct.TN: 'TN'>
UB = <FutureProduct.UB: 'UB'>
ZB = <FutureProduct.ZB: 'ZB'>
ZF = <FutureProduct.ZF: 'ZF'>
ZN = <FutureProduct.ZN: 'ZN'>
ZT = <FutureProduct.ZT: 'ZT'>
SR3 = <FutureProduct.SR3: 'SR3'>
RB = <FutureProduct.RB: 'RB'>
CL = <FutureProduct.CL: 'CL'>
MCL = <FutureProduct.MCL: 'MCL'>
NG = <FutureProduct.NG: 'NG'>
QG = <FutureProduct.QG: 'QG'>
HO = <FutureProduct.HO: 'HO'>
BZ = <FutureProduct.BZ: 'BZ'>
QM = <FutureProduct.QM: 'QM'>
MNG = <FutureProduct.MNG: 'MNG'>
NQ = <FutureProduct.NQ: 'NQ'>
MNQ = <FutureProduct.MNQ: 'MNQ'>
RTY = <FutureProduct.RTY: 'RTY'>
YM = <FutureProduct.YM: 'YM'>
ES = <FutureProduct.ES: 'ES'>
MES = <FutureProduct.MES: 'MES'>
M2K = <FutureProduct.M2K: 'M2K'>
VXM = <FutureProduct.VXM: 'VXM'>
VX = <FutureProduct.VX: 'VX'>
MYM = <FutureProduct.MYM: 'MYM'>
GC = <FutureProduct.GC: 'GC'>
MGC = <FutureProduct.MGC: 'MGC'>
SIL = <FutureProduct.SIL: 'SIL'>
SI = <FutureProduct.SI: 'SI'>
HG = <FutureProduct.HG: 'HG'>
PL = <FutureProduct.PL: 'PL'>
SIC = <FutureProduct.SIC: 'SIC'>
MHG = <FutureProduct.MHG: 'MHG'>
MXP = <FutureProduct.MXP: 'MXP'>
BTC = <FutureProduct.BTC: 'BTC'>
MBT = <FutureProduct.MBT: 'MBT'>
MET = <FutureProduct.MET: 'MET'>
ETH = <FutureProduct.ETH: 'ETH'>
def getMarginRate(self) -> float:
def getMarginRequirementPerContract(self) -> float:
def calculateMarginRequirement(self, price: float, quantity: float) -> float:
class FutureType(builtins.str, enum.Enum):

Enum representing high-level future product categories.

AGRICULTURE = <FutureType.AGRICULTURE: 'AGRICULTURE'>
CURRENCY = <FutureType.CURRENCY: 'CURRENCY'>
INTEREST = <FutureType.INTEREST: 'INTEREST'>
ENERGY = <FutureType.ENERGY: 'ENERGY'>
EQUITY_INDEX = <FutureType.EQUITY_INDEX: 'EQUITY_INDEX'>
METALS = <FutureType.METALS: 'METALS'>
CRYPTOCURRENCY = <FutureType.CRYPTOCURRENCY: 'CRYPTOCURRENCY'>

A specific futures contract (e.g., ESM25) for product ES expiring June 2025.

Extends Security with contract-specific metadata. Instances are created by the platform via StrategyDataService.listFutures(); strategy developers do not construct these directly.

Since Future is a Security, it can be passed anywhere a Security is accepted — getQuote(future), getBars(future), TradeOrder(security=future, ...), etc.

Future(symbol: str, productCode: str, expiryDate: datetime.date)
productCode: str
expiryDate: datetime.date
def getYear(self) -> int:

Return the last two digits of the expiry year, e.g. expiry 2026-03-20 -> 26.

def getFullYear(self) -> int:

Return the full 4-digit expiry year.

def getMonth(self) -> str:

Return the futures month code character from the symbol.

Handles both one-digit and two-digit year formats:

  • 'ESU3' -> 'U'
  • 'ESU23' -> 'U'
class OptionType(builtins.str, enum.Enum):

Right of an option contract: CALL or PUT

CALL = <OptionType.CALL: 'CALL'>
PUT = <OptionType.PUT: 'PUT'>
@staticmethod
def fromString(s: str) -> OptionType:
@dataclass(frozen=True, eq=True)
class OptionExpiry:

Calendar date when an option contract expires (1-indexed month and day)

OptionExpiry(year: int, month: int, day: int)
year: int
month: int
day: int
def toExpirySymbol(self) -> str:

OCC YYMMDD form used in option symbols.

def toDate(self) -> datetime.date:
@staticmethod
def fromExpirySymbol( expirySymbol: str) -> OptionExpiry:
@staticmethod
def fromDate( expiryDate: datetime.date) -> OptionExpiry:
@staticmethod
def fromDict( jsonDict: Dict[str, Any]) -> OptionExpiry:
def toDict(self) -> Dict[str, Any]:
@dataclass(frozen=True, eq=True)
class StockOption:

OCC-style listed equity/ETF option contract.

OCC option symbol: TICKER + YYMMDD + C/P + strike*1000 (8 digits, zero-padded). Example: AAPL260117C00200000 => AAPL call expiring 2026-01-17 with $200 strike.

StockOption( symbol: str, underlyingSymbol: str, expiry: OptionExpiry, optionType: OptionType, strikePrice: float)
symbol: str
underlyingSymbol: str
expiry: OptionExpiry
optionType: OptionType
strikePrice: float
securityType: SecurityType
contractUnits: int
def toSecurity(self) -> Security:
@staticmethod
def fromSymbol(optionSymbol: str) -> StockOption:
@staticmethod
def buildSymbol( underlying: str, expiry: OptionExpiry, optionType: OptionType, strikePrice: float) -> str:
@staticmethod
def isOptionSymbol(symbol: str | None) -> bool:
@staticmethod
def getUnderlyingFromOptionSymbol(symbol: str | None) -> str | None:
@staticmethod
def fromDict( jsonDict: Dict[str, Any]) -> StockOption:
def toDict(self) -> Dict[str, Any]:
@dataclass
class OptionQuote:

Snapshot of an option contract quote with optional Greeks.

Greeks may be vendor-provided (Tradier/TastyTrade) or computed locally via the synthetic Black-Scholes pricer. Consumers should treat None as "not available" rather than zero.

OptionQuote( symbol: str, underlyingSymbol: str, expiry: OptionExpiry, strikePrice: float, optionType: OptionType, date: datetime.datetime | None = None, bid: float | None = None, ask: float | None = None, last: float | None = None, mid: float | None = None, volume: float | None = None, openInterest: int | None = None, impliedVolatility: float | None = None, delta: float | None = None, gamma: float | None = None, theta: float | None = None, vega: float | None = None, rho: float | None = None, inTheMoney: bool | None = None)
symbol: str
underlyingSymbol: str
expiry: OptionExpiry
strikePrice: float
optionType: OptionType
date: datetime.datetime | None = None
bid: float | None = None
ask: float | None = None
last: float | None = None
mid: float | None = None
volume: float | None = None
openInterest: int | None = None
impliedVolatility: float | None = None
delta: float | None = None
gamma: float | None = None
theta: float | None = None
vega: float | None = None
rho: float | None = None
inTheMoney: bool | None = None
@staticmethod
def fromDict( jsonDict: Dict[str, Any]) -> OptionQuote:
def toDict(self) -> Dict[str, Any]:
def midPrice(self) -> float | None:
@dataclass
class OptionChain:

Option chain snapshot for a single underlying + expiration date.

Calls and puts are stored separately so consumers can iterate one side without filtering. The underlyingPrice/asOfTimestamp pair is preserved for IV/Greeks recomputation when those are not vendor-provided.

OptionChain( underlyingSymbol: str, expiry: OptionExpiry, underlyingPrice: float | None = None, asOfTimestamp: str | None = None, calls: List[OptionQuote] = <factory>, puts: List[OptionQuote] = <factory>)
underlyingSymbol: str
expiry: OptionExpiry
underlyingPrice: float | None = None
asOfTimestamp: str | None = None
calls: List[OptionQuote]
puts: List[OptionQuote]
def all(self) -> List[OptionQuote]:
def filterByRight( self, optionType: OptionType) -> List[OptionQuote]:
@staticmethod
def fromDict( jsonDict: Dict[str, Any]) -> OptionChain:
def toDict(self) -> Dict[str, Any]:
class OptionPricer:

Black-Scholes pricer for European-style stock/ETF options.

@staticmethod
def realizedVolatilityFromCloses(closes: Iterable[float]) -> float:

Annualized realized volatility of daily log returns.

Falls back to MIN_IMPLIED_VOL when the input is too short or has zero variance. Inputs are oldest-to-newest closing prices (matching Bar order convention).

@staticmethod
def yearsUntil(expiry: datetime.date, asOf: Optional[datetime.datetime] = None) -> float:

Time-to-expiry in calendar years, with a minimum of 1/365 to keep BS well-defined.

@staticmethod
def price( spot: float, strike: float, yearsToExpiry: float, impliedVol: float, optionType: OptionType, riskFreeRate: float = 0.04) -> PricingResult:

Black-Scholes price + Greeks for a European option.

Greeks scaling:

  • delta in [-1, 1] per share (multiply by 100 for per-contract risk).
  • theta is per-year; divide by 365 for per-day theta.
  • vega is per 1.00 of vol (multiply by 0.01 for per-1%-vol).
  • rho is per 1.00 of rate (multiply by 0.01 for per-1%-rate). These conventions match common quant texts; UI/runtime can rescale as needed.
@staticmethod
def deltaToStrike( spot: float, yearsToExpiry: float, impliedVol: float, optionType: OptionType, targetDelta: float, riskFreeRate: float = 0.04, strikeStep: float = 1.0) -> float:

Find the strike whose Black-Scholes delta is closest to the target.

Search is a coarse grid scan with a refinement step. Strike grid is anchored at the nearest multiple of strikeStep around spot and bounded at +-3 sigma * sqrt(T) from spot, which is wide enough for typical 30-60 DTE selections and avoids unstable far-OTM delta extrapolation.

@dataclass(frozen=True)
class GreeksResult:

Black-Scholes Greeks bundle returned alongside the theoretical premium.

GreeksResult(delta: float, gamma: float, theta: float, vega: float, rho: float)
delta: float
gamma: float
theta: float
vega: float
rho: float
@dataclass(frozen=True)
class PricingResult:

Theoretical price + Greeks for a single contract.

PricingResult( price: float, greeks: GreeksResult, impliedVolUsed: float)
price: float
greeks: GreeksResult
impliedVolUsed: float