129 lines
4.8 KiB
Python
129 lines
4.8 KiB
Python
# # ⚠ Warning
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
|
# LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
# NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
#
|
|
# [🥭 Mango Markets](https://mango.markets/) support is available at:
|
|
# [Docs](https://docs.mango.markets/)
|
|
# [Discord](https://discord.gg/67jySBhxrg)
|
|
# [Twitter](https://twitter.com/mangomarkets)
|
|
# [Github](https://github.com/blockworks-foundation)
|
|
# [Email](mailto:hello@blockworks.foundation)
|
|
|
|
|
|
import logging
|
|
import typing
|
|
|
|
from decimal import Decimal
|
|
|
|
from .account import Account
|
|
from .cache import Cache
|
|
from .group import Group
|
|
from .instrumentvalue import InstrumentValue
|
|
from .markets import InventorySource, Market
|
|
from .openorders import OpenOrders
|
|
from .watcher import Watcher
|
|
|
|
|
|
# # 🥭 Inventory class
|
|
#
|
|
# This class details inventory of a crypto account for a market.
|
|
#
|
|
class Inventory:
|
|
def __init__(
|
|
self,
|
|
inventory_source: InventorySource,
|
|
liquidity_incentives: InstrumentValue,
|
|
available_collateral: InstrumentValue,
|
|
base: InstrumentValue,
|
|
quote: InstrumentValue,
|
|
) -> None:
|
|
self._logger: logging.Logger = logging.getLogger(self.__class__.__name__)
|
|
self.inventory_source: InventorySource = inventory_source
|
|
self.available_collateral: InstrumentValue = available_collateral
|
|
self.liquidity_incentives: InstrumentValue = liquidity_incentives
|
|
self.base: InstrumentValue = base
|
|
self.quote: InstrumentValue = quote
|
|
|
|
@property
|
|
def symbol(self) -> str:
|
|
return f"{self.base.token.symbol}/{self.quote.token.symbol}"
|
|
|
|
def __str__(self) -> str:
|
|
liquidity_incentives: str = ""
|
|
if self.liquidity_incentives.value > 0:
|
|
liquidity_incentives = f" {self.liquidity_incentives}"
|
|
return f"« Inventory {self.symbol}{liquidity_incentives} [{self.base} / {self.quote}] ({self.available_collateral} available) »"
|
|
|
|
def __repr__(self) -> str:
|
|
return f"{self}"
|
|
|
|
|
|
class InventoryAccountWatcher:
|
|
def __init__(
|
|
self,
|
|
market: Market,
|
|
account_watcher: Watcher[Account],
|
|
group_watcher: Watcher[Group],
|
|
all_open_orders_watchers: typing.Sequence[Watcher[OpenOrders]],
|
|
cache_watcher: Watcher[Cache],
|
|
):
|
|
self.account_watcher: Watcher[Account] = account_watcher
|
|
self.group_watcher: Watcher[Group] = group_watcher
|
|
self.all_open_orders_watchers: typing.Sequence[
|
|
Watcher[OpenOrders]
|
|
] = all_open_orders_watchers
|
|
self.cache_watcher: Watcher[Cache] = cache_watcher
|
|
account: Account = account_watcher.latest
|
|
self.spot_account_index: int = group_watcher.latest.slot_by_spot_market_address(
|
|
market.address
|
|
).index
|
|
base_value = InstrumentValue.find_by_symbol(
|
|
account.net_values, market.base.symbol
|
|
)
|
|
self.base_index: int = account.net_values_by_index.index(base_value)
|
|
quote_value = InstrumentValue.find_by_symbol(
|
|
account.net_values, market.quote.symbol
|
|
)
|
|
self.quote_index: int = account.net_values_by_index.index(quote_value)
|
|
|
|
@property
|
|
def latest(self) -> Inventory:
|
|
account: Account = self.account_watcher.latest
|
|
group: Group = self.group_watcher.latest
|
|
cache: Cache = self.cache_watcher.latest
|
|
|
|
# Spot markets don't accrue MNGO liquidity incentives
|
|
mngo = group.liquidity_incentive_token
|
|
mngo_accrued: InstrumentValue = InstrumentValue(mngo, Decimal(0))
|
|
|
|
all_open_orders: typing.Dict[str, OpenOrders] = {
|
|
str(oo_watcher.latest.address): oo_watcher.latest
|
|
for oo_watcher in self.all_open_orders_watchers
|
|
}
|
|
|
|
frame = account.to_dataframe(group, all_open_orders, cache)
|
|
available_collateral: InstrumentValue = account.init_health(frame)
|
|
|
|
base_value = account.net_values_by_index[self.base_index]
|
|
if base_value is None:
|
|
raise Exception(
|
|
f"Could not find net assets in account {account.address} at index {self.base_index}."
|
|
)
|
|
quote_value = account.net_values_by_index[self.quote_index]
|
|
if quote_value is None:
|
|
raise Exception(
|
|
f"Could not find net assets in account {account.address} at index {self.quote_index}."
|
|
)
|
|
|
|
return Inventory(
|
|
InventorySource.ACCOUNT,
|
|
mngo_accrued,
|
|
available_collateral,
|
|
base_value,
|
|
quote_value,
|
|
)
|