paft
Provider Agnostic Financial Types for Rust
Standardized Rust types for financial data that work with any provider—Yahoo Finance, Bloomberg, Alpha Vantage, and more.
🌟 Ecosystem Overview: For the bigger picture, vision, and contributor guidance, see the workspace README.
Quick Install
[]
# Basic installation with default feature set (domain + market + fundamentals)
= "0.8.0"
# Or, add optional analysis helpers (Polars DataFrame support)
= { = "0.8.0", = ["dataframe"] }
# Or, opt into the aggregates snapshot model as well
= { = "0.8.0", = ["aggregates"] }
# Or, enable the full bundle of features
= { = "0.8.0", = ["full"] }
# Or, customize your installation
= { = "0.8.0", = false, = ["fundamentals", "dataframe"] }
# Switch the money backend to BigDecimal (default is rust_decimal)
= { = "0.8.0", = ["bigdecimal"] }
Feature Flags
All features are optional—disable the defaults (default-features = false) and opt back into what you need.
domain(default): exposes instrument, exchange, period, and other domain models.market(default, enablesdomain): markets and history types such asQuote,Candle, andHistoryRequest.fundamentals(default, enablesdomain): fundamentals, ESG, and ownership data structures.aggregates: exposes theSnapshotaggregated instrument-snapshot model.bigdecimal: swaps the money backend toBigDecimalwhen you require arbitrary precision.dataframe: forwards DataFrame support frompaft-utils, providingToDataFrame/ToDataFrameVec.prediction: prediction market data models (Market,Token).full: convenience bundle fordomain,market,fundamentals,aggregates,prediction, anddataframe.panicking-money-ops: re-enablesMoneyarithmetic operators that panic on mismatched currencies (see below).money-formatting: forwards topaft-money/money-formattingfor locale-aware formatting and parsing APIs.tracing: enables lightweight instrumentation spans in selected constructors and validators forpaft-domain,paft-money,paft-market, andpaft-fundamentals; zero-cost when disabled.
Migration Notes
Instrumentis a flat struct (symbol,exchange,figi,isin,kind);IdentifierScheme,SecurityId, andPredictionIDare gone. Construct with thefrom_*helpers or a struct literal; access identifier fields directly (e.g.inst.figi.as_ref()). Prediction-market outcomes now live inpaft-predictionasPredictionInstrument.Instrument::figiandInstrument::isinare typedOption<Figi>/Option<Isin>. Construct withFigi::new("...")andIsin::new("..."). When you need&str, use helpers likeinst.figi.as_ref().map(AsRef::as_ref).CompanyProfile::isinandFundProfile::isinnow storeOption<Isin>; update struct literals to passIsin::new(..)?and adjust deserialization expectations accordingly.Isin::newandFigi::newnow always enforce checksum validation. If you previously relied on lenient mode, strip placeholders or keep them inSymbolfields instead.- The new identifier newtypes are
#[serde(transparent)], so existing JSON payloads continue to operate with plain strings while now enforcing checksum validation at the boundary. paft-aggregatesno longer shipsFastInfo/Info. UseSnapshotfor strictly instant-in-time market data — fundamentals/analyst/ESG fields that lived onInfobelong in thepaft-fundamentalstypes.
What's Included
Core Types
- Instruments:
Instrument(flat struct:symbol,exchange,figi,isin,kind),AssetKind - Market Data:
Quote,Candle,HistoryResponse,MarketState - Fundamentals: Financial statements, earnings, analyst ratings, and trend/revision helper rows
- Options:
OptionContractKey,OptionSide,OptionContract,OptionGreeks,OptionChain,OptionUpdate,OptionExpirationsResponse - News & Search:
NewsArticle,NewsRequest,NewsTab,SearchRequest,SearchResult - ESG & Holders: ESG scores, institutional holdings
- Aggregates (feature
aggregates):Snapshotinstrument snapshots - Prediction Markets (feature
prediction):Market,Token
Key Features
- Hierarchical Identifiers: FIGI → ISIN → Symbol@Exchange → Symbol priority
- Extensible Enums: Graceful handling of unknown provider values
- DataFrame Integration: Optional Polars support with
ToDataFrametrait - Full Serialization: serde support for JSON, CSV, and other formats
Quick Start
Basic Usage
use *;
use IsoCurrency;
// Create instruments with different levels of identification
let apple = ;
let bitcoin = from_symbol
.expect;
// Create market data. `Quote` carries the full `Instrument`, plus today's
// volume, optional snapshot time, and a provider-metadata escape hatch
// (use `()` for "no metadata").
let quote = Quote ;
Hierarchical Identifiers
// Automatic prioritization: FIGI > ISIN > Symbol@Exchange > Symbol
println!; // "BBG000B9XRY4" (uses FIGI)
println!; // "BTC-USD" (uses symbol)
// Check identification levels — fields are public on the flat struct.
if apple.figi.is_some || apple.isin.is_some
// Access specific identifiers
if let Some = apple.figi.as_ref
Historical Data
use *;
// Request 6 months of daily data (validated in constructor)
let request = try_from_range.unwrap;
DataFrame Integration
Enable DataFrame support for analysis:
[]
= { = "0.8.0", = ["dataframe"] }
use *;
let quotes = vec!;
let df = quotes.to_dataframe?;
if let Some = df.column?.as_materialized_series.mean
// Money-like fields are flattened into paired amount/currency columns such as
// `price.amount` and `price.currency`.
Locale-aware money formatting and parsing
Enable the money-formatting feature to opt into locale-aware Display and strict parsing:
[]
= { = "0.8.0", = ["money-formatting"] }
use ;
let m = from_canonical_str?;
let us = m.format_with_locale?;
let de = m.format_with_locale?;
assert_eq!;
assert_eq!;
// Strict parsing
let parsed = from_str_locale?;
Money operators and safety
By default, Money arithmetic operators (+, -, /, *) that would
panic on invalid input are disabled. Use the safe methods instead:
let sum = a.try_add?;
let diff = a.try_sub?;
let half = a.try_div?;
If you explicitly want the ergonomic panicking operators, enable the
panicking-money-ops feature via the paft facade (it forwards to paft-money):
[]
= { = "0.8.0", = ["panicking-money-ops"] }
Note: This feature is opt-in and enables the +, -, *, and / operators to
panic on currency mismatch, division by zero, or conversion/metadata failures.
Prefer try_* methods in most apps.
For ergonomics in math-heavy code, you may enable this only when you control
the data end to end (e.g., internal pipelines with strict invariants) and are
absolutely sure all arithmetic uses matching currencies. For external or
untrusted data, keep this feature disabled and use the try_* APIs.
Handling Unknown Values
paft uses extensible enums with Other(Canonical) variants to gracefully handle unknown provider values:
use IsoCurrency;
use *;
// Handle unknown currencies from providers
match currency
// Same pattern for exchanges, asset types, market states, etc.
let exchange: Exchange = "DARK_POOL_X".parse.unwrap; // Unknown exchange handled via Other
let market_state: MarketState = "DELAYED".parse.unwrap;
assert_eq!;
assert!;
This pattern ensures your code never breaks when providers return new or unexpected values.
Canonical Codes vs Human Labels
Enums ship with three complementary string representations:
- Wire:
code()returns the canonical token used in APIs and serialization. - Display:
to_string()mirrorscode()so logging and dataframes stay consistent. - Human: Opt-in helpers such as
Currency::full_name(),AssetKind::full_name(), andMarketState::full_name()provide sentence-case labels for UI surfaces.
Keep the rule of thumb: wire = code = Display; human prose = explicit helper.
More Details
- Extensible Enums Guide: Complete documentation and examples
- Best Practices: Guidelines for library authors and consumers
- Working Examples: See extensible enums in action
License
MIT License. See LICENSE for details.