1
0

Compare commits

..

4 Commits

Author SHA1 Message Date
1c5df0dfe3 🤖 Bumped version to 0.1.31
Some checks failed
Package Python / Package (push) Successful in 24s
Test Python / Test (push) Failing after 23s
This commit was automatically generated by a script: https://gitfub.space/Jmaa/python-omni
2024-08-02 15:10:11 +02:00
7eabb00201 🤖 Repository layout updated to latest version
This commit was automatically generated by a script: https://gitfub.space/Jmaa/python-omni
2024-08-02 15:09:38 +02:00
6a995d6406
Standardized __str__ format and id format 2024-08-02 07:27:17 +02:00
a610cfe07c
Added string_id format 2024-08-02 05:42:19 +02:00
5 changed files with 134 additions and 28 deletions

View File

@ -6,6 +6,19 @@
Python library defining base types for financial processing. Python library defining base types for financial processing.
Defines a base `Asset` type, and various subtypes, for universal representation
of these assets.
Defined hierarchy:
* `Asset`
- `Currency`
+ `FiatCurrency`
+ `CryptoCurrency`
- `Stock`
- `Index`
- `Commodity`
# License # License

View File

@ -1,6 +1,19 @@
"""# Finance Definitions. """# Finance Definitions.
Python library defining base types for financial processing. Python library defining base types for financial processing.
Defines a base `Asset` type, and various subtypes, for universal representation
of these assets.
Defined hierarchy:
* `Asset`
- `Currency`
+ `FiatCurrency`
+ `CryptoCurrency`
- `Stock`
- `Index`
- `Commodity`
""" """
import dataclasses import dataclasses
@ -22,7 +35,16 @@ RE_CRYPTO_TICKER_FORMAT = r'^\S+$'
@enforce_typing.enforce_types @enforce_typing.enforce_types
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
class StockExchange: class StockExchange:
"""Unified Stock Exchange identifiers.""" """Unified Stock Exchange identifiers.
Fields:
- `name`: Human-readable name.
- `mic`: Official MIC identifier.
- `bloomberg_id`: Identifier for lookup on Bloomberg.
- `crunchbase_id`: Identifier for lookup on Crunchbase.
- `yahoo_id`: Identifier for lookup on Yahoo! Finance.
- `eod_id`: Identifier for lookup on EOD.
"""
name: str name: str
mic: str mic: str
@ -42,9 +64,15 @@ class Asset:
""" """
def names(self) -> list[str]: def names(self) -> list[str]:
"""Returns a list of human readable names for this `Asset`."""
return COMMON_NAMES.get(self, []) return COMMON_NAMES.get(self, [])
def to_polygon_id(self) -> str: def to_polygon_id(self) -> str:
"""Formats this asset to the [`polygon.io`](https://polygon.io)
identifier format.
Inverse of `from_polygon_id`.
"""
# TODO: Support currency pairs not involving USD. # TODO: Support currency pairs not involving USD.
if isinstance(self, Stock): if isinstance(self, Stock):
return f'{self.ticker}' return f'{self.ticker}'
@ -61,6 +89,11 @@ class Asset:
@staticmethod @staticmethod
def from_polygon_id(polygon_id: str) -> 'Asset': def from_polygon_id(polygon_id: str) -> 'Asset':
"""Parses an asset from the [`polygon.io`](https://polygon.io)
identifier format.
Inverse of `to_polygon_id`.
"""
if polygon_id.startswith('I:'): if polygon_id.startswith('I:'):
return Index(polygon_id.removeprefix('I:')) return Index(polygon_id.removeprefix('I:'))
if polygon_id.startswith('X:'): if polygon_id.startswith('X:'):
@ -72,6 +105,51 @@ class Asset:
return FiatCurrency(polygon_id.removeprefix('C:').removesuffix('USD')) return FiatCurrency(polygon_id.removeprefix('C:').removesuffix('USD'))
return Stock.from_polygon_id(polygon_id) return Stock.from_polygon_id(polygon_id)
def to_string_id(self) -> str:
"""Formats the asset id using the fin_defs asset format."""
if isinstance(self, Stock):
return f'stock:{self.ticker}.{self.exchange.mic}'
if isinstance(self, FiatCurrency):
return f'fiat:{self.iso_code}'
if isinstance(self, CryptoCurrency):
return f'crypto:{self.ccxt_symbol}'
if isinstance(self, Index):
return f'index:{self.ticker}'
if isinstance(self, Commodity):
return f'commodity:{self.alpha_vantage_id}'
raise NotImplementedError('Unsupported asset type: ' + str(self))
@staticmethod
def from_string_id(asset_id: str) -> 'Asset':
"""Parses an `Asset` using the fin_defs asset format."""
if ':' in asset_id:
prefix, ticker = asset_id.split(':')
else:
prefix, ticker = None, asset_id
if known_symbol := WELL_KNOWN_SYMBOLS.get(ticker, None):
return known_symbol
if prefix == 'stock':
if m := re.match(r'^(\w+)(?:\.(\w+))?$', ticker):
exchange = EXCHANGES_BY_IDS[m.group(2) or 'NYSE'] # TODO?
return Stock(m.group(1), exchange)
if prefix == 'currency':
return FiatCurrency(ticker)
if prefix == 'fiat':
return FiatCurrency(ticker)
if prefix == 'index':
return Index(ticker)
if prefix == 'crypto':
return CryptoCurrency(ticker, None) # TODO
msg = f'Unsupported asset format: {asset_id}'
raise NotImplementedError(msg)
def __str__(self):
return self.to_string_id()
@enforce_typing.enforce_types @enforce_typing.enforce_types
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
@ -89,9 +167,6 @@ class FiatCurrency(Currency):
msg = f'iso_code was not in correct format: {self.iso_code}' msg = f'iso_code was not in correct format: {self.iso_code}'
raise ValueError(msg) raise ValueError(msg)
def __str__(self):
return self.iso_code
@enforce_typing.enforce_types @enforce_typing.enforce_types
@dataclasses.dataclass(frozen=True, eq=True) @dataclasses.dataclass(frozen=True, eq=True)
@ -106,10 +181,6 @@ class CryptoCurrency(Currency):
raise ValueError(msg) raise ValueError(msg)
""" """
def __str__(self):
return self.ccxt_symbol
@enforce_typing.enforce_types @enforce_typing.enforce_types
@dataclasses.dataclass(frozen=True, eq=True) @dataclasses.dataclass(frozen=True, eq=True)
class Stock(Asset): class Stock(Asset):
@ -122,9 +193,6 @@ class Stock(Asset):
msg = f'ticker was not in correct format: {self.ticker}' msg = f'ticker was not in correct format: {self.ticker}'
raise ValueError(msg) raise ValueError(msg)
def __str__(self):
return f'{self.ticker}.{self.exchange.mic}'
@staticmethod @staticmethod
def from_polygon_id(polygon_id: str, stock_exchange: StockExchange) -> 'Stock': def from_polygon_id(polygon_id: str, stock_exchange: StockExchange) -> 'Stock':
return Stock(polygon_id, stock_exchange) return Stock(polygon_id, stock_exchange)
@ -140,9 +208,6 @@ class Index(Asset):
msg = f'ticker was not in correct format: {self.ticker}' msg = f'ticker was not in correct format: {self.ticker}'
raise ValueError(msg) raise ValueError(msg)
def __str__(self):
return self.ticker
@enforce_typing.enforce_types @enforce_typing.enforce_types
@dataclasses.dataclass(frozen=True, eq=True) @dataclasses.dataclass(frozen=True, eq=True)
@ -154,9 +219,6 @@ class Commodity(Asset):
msg = f'alpha_vantage_id was not in correct format: {self.alpha_vantage_id}' msg = f'alpha_vantage_id was not in correct format: {self.alpha_vantage_id}'
raise ValueError(msg) raise ValueError(msg)
def __str__(self):
return self.alpha_vantage_id
Commodity.CRUDE_OIL_WTI = Commodity('WTI') Commodity.CRUDE_OIL_WTI = Commodity('WTI')
Commodity.CRUDE_OIL_BRENT = Commodity('BRENT') Commodity.CRUDE_OIL_BRENT = Commodity('BRENT')
@ -177,7 +239,7 @@ BTC = CryptoCurrency('BTC', coingecko_id='bitcoin')
MPC = CryptoCurrency('MPC', coingecko_id='partisia-blockchain') MPC = CryptoCurrency('MPC', coingecko_id='partisia-blockchain')
SPX = Index('SPX') SPX = Index('SPX')
NDX = Index('NDX') NDX = Index('NDX')
USDT = CryptoCurrency('USDT', 'tether') USDT = CryptoCurrency('USDT', coingecko_id='tether')
COMMON_NAMES: dict[Asset, list[str]] = { COMMON_NAMES: dict[Asset, list[str]] = {
FiatCurrency('USD'): ['U.S. Dollar'], FiatCurrency('USD'): ['U.S. Dollar'],
@ -196,15 +258,15 @@ COMMON_NAMES: dict[Asset, list[str]] = {
NDX: ['Nasdaq 100'], NDX: ['Nasdaq 100'],
BTC: ['Bitcoin BTC'], BTC: ['Bitcoin BTC'],
MPC: ['Partisia Blockchain MPC Token'], MPC: ['Partisia Blockchain MPC Token'],
CryptoCurrency('ETH', 'ethereum'): ['Ethereum Ether'], CryptoCurrency('ETH', coingecko_id='ethereum'): ['Ethereum Ether'],
CryptoCurrency('DOGE', 'dogecoin'): ['Dogecoin'], CryptoCurrency('DOGE', coingecko_id='dogecoin'): ['Dogecoin'],
CryptoCurrency('SOL', 'solana'): ['Solana SOL'], CryptoCurrency('SOL', coingecko_id='solana'): ['Solana SOL'],
CryptoCurrency('ADA', 'cardano'): ['Cardano ADA'], CryptoCurrency('ADA', coingecko_id='cardano'): ['Cardano ADA'],
CryptoCurrency('BNB', 'bnb'): ['Binance BNB'], CryptoCurrency('BNB', coingecko_id='bnb'): ['Binance BNB'],
CryptoCurrency('MATIC', 'polygon'): ['Polygon MATIC'], CryptoCurrency('MATIC', coingecko_id='polygon'): ['Polygon MATIC'],
# Stable-coins # Stable-coins
CryptoCurrency('DAI', 'dai'): ['DAI'], CryptoCurrency('DAI', coingecko_id='dai'): ['DAI'],
CryptoCurrency('USDC', 'usdc'): ['USD Coin'], CryptoCurrency('USDC', coingecko_id='usdc'): ['USD Coin'],
USDT: ['Tether USDT'], USDT: ['Tether USDT'],
} }
@ -334,7 +396,12 @@ class AssetInformation:
@enforce_typing.enforce_types @enforce_typing.enforce_types
@dataclasses.dataclass(frozen=True, eq=True, slots=True) @dataclasses.dataclass(frozen=True, eq=True, slots=True)
class AssetAmount: class AssetAmount:
"""Decimal with associated asset unit.""" """Decimal with associated asset unit.
Fields:
- `asset`: Asset unit of amount.
- `amount`: Amount of given asset.
"""
asset: Asset asset: Asset
amount: Decimal amount: Decimal
@ -397,6 +464,7 @@ class ExchangeRateSample(Asset):
"""Single exchange rate sample with a timestamp and various stats.""" """Single exchange rate sample with a timestamp and various stats."""
timestamp: datetime.date | datetime.datetime timestamp: datetime.date | datetime.datetime
base_asset: Asset
_average: Decimal | None = None _average: Decimal | None = None
last: Decimal | None = None last: Decimal | None = None
open: Decimal | None = None open: Decimal | None = None

View File

@ -1 +1 @@
__version__ = '0.1.30' __version__ = '0.1.31'

View File

@ -15,6 +15,19 @@ PACKAGE_DESCRIPTION = """
Python library defining base types for financial processing. Python library defining base types for financial processing.
Defines a base `Asset` type, and various subtypes, for universal representation
of these assets.
Defined hierarchy:
* `Asset`
- `Currency`
+ `FiatCurrency`
+ `CryptoCurrency`
- `Stock`
- `Index`
- `Commodity`
# License # License

12
test/test_ids.py Normal file
View File

@ -0,0 +1,12 @@
import pytest
import fin_defs
@pytest.mark.parametrize('asset', fin_defs.WELL_KNOWN_SYMBOLS.values())
def test_to_from_string_id(asset: fin_defs.Asset):
assert fin_defs.Asset.from_string_id(asset.to_string_id()) == asset
import fin_defs
@pytest.mark.parametrize('asset', fin_defs.WELL_KNOWN_SYMBOLS.values())
def test_to_from_polygon_id(asset: fin_defs.Asset):
assert fin_defs.Asset.from_polygon_id(asset.to_polygon_id()) == asset