Skip to content
Snippets Groups Projects

Asset range option

Merged Michał Kudela requested to merge kudmich/add_asset_range_option into master
All threads resolved!
Files
3
@@ -4,13 +4,15 @@ from copy import deepcopy
from decimal import Decimal
from functools import total_ordering
import operator
from typing import Any, Final, NoReturn, Optional, Union
from typing import Any, Final, NoReturn, Optional, TypeVar, Union
import abstractcp as acp
from test_tools.__private.exceptions import ParseError
from test_tools.__private.utilities.decimal_converter import DecimalConverter
AssetLimitT = TypeVar("AssetLimitT", bound="AssetBase")
@total_ordering
class AssetBase(acp.Abstract):
@@ -128,7 +130,7 @@ class AssetBase(acp.Abstract):
except ParseError as exception:
raise TypeError(f"Can't {error_detail} asset: `{self}` and `{other}`.") from exception
self.__assert_same_token(other, error_detail=error_detail)
self.assert_same_token(self, other, error_detail=error_detail)
return other
def __handle_asset_conversion(self, other: Any, error_detail: str) -> AssetBase:
@@ -137,8 +139,7 @@ class AssetBase(acp.Abstract):
if isinstance(other, (str, dict)):
other = Asset.from_(other, treat_dict_as_testnet_currencies=is_testnet)
if not isinstance(other, AssetBase):
raise TypeError(f"Can't {error_detail} objects of type `{type(other)}`.")
self.assert_is_asset(other, error_detail=error_detail)
return other
@classmethod
@@ -152,9 +153,16 @@ class AssetBase(acp.Abstract):
if cls.precision != other["precision"]:
raise ParseError(f"Asset dict precision differ: `{other['precision']}`, when expected `{cls.precision}`.")
def __assert_same_token(self, other: AssetBase, *, error_detail: str) -> Optional[NoReturn]:
if self.token != other.token:
raise TypeError(f"Can't {error_detail} assets with different tokens: `{self.token}` and `{other.token}`.")
@staticmethod
def assert_same_token(first: AssetBase, other: AssetBase, *, error_detail: str) -> Optional[NoReturn]:
if first.token != other.token:
raise TypeError(f"Can't {error_detail} assets with different tokens: `{first.token}` and `{other.token}`.")
@staticmethod
def assert_is_asset(*other: Any, error_detail: str) -> Optional[NoReturn]:
for asset in other:
if not isinstance(asset, AssetBase):
raise TypeError(f"Can't {error_detail} objects of type `{type(asset)}`.")
class Asset:
@@ -184,40 +192,49 @@ class Asset:
nai: Final[str] = "@@000000037"
class Range:
"""
Represents a range of assets by providing the upper and lower bounds for a given value.
It functions similarly to the built-in python `range(100, 110)` function, providing a convenient way to
represent a range of assets within a specified value range.
Usage:
tt.Asset.Range(lower_limit=tt.Asset.Hive(100), upper_limit=tt.Asset.Hive(110))
:param lower_limit is required. When tolerance is given it acts as the value to which we refer when specifying
the percentage range.
:param tolerance: is defined as a positive number, which is a percentage of the upper and lower deviations e.g:
asset = tt.Asset.Hive(100)
tt.Asset.Range(asset, tolerance=10) -> the range of this asset is from tt.Asset.Hive(90) to inclusive (100)
Upper limit and tolerance should be used interchangeably.
"""
def __init__(
self,
lower_limit: AssetBase,
upper_limit: Optional[None, AssetBase] = None,
lower_limit: AssetLimitT,
upper_limit: Optional[AssetLimitT] = None,
*,
percentage_range: Optional[None, int] = None,
tolerance: Union[int, float, None] = None,
):
if not upper_limit and not tolerance:
raise TypeError("Range has to be specified with either `upper_limit` or `tolerance`")
if upper_limit and tolerance:
raise TypeError("Please choose only one option from `upper_limit` or `tolerance`")
if tolerance and tolerance < 0:
raise TypeError("`tolerance` should be given as an positive number")
if upper_limit is None and percentage_range is None:
raise AssertionError("Cannot set a range without specifying an upper limit or percentage limit")
if upper_limit is not None and percentage_range is not None:
raise AssertionError(
"Using upper_limit and percentage_range at the same time is not possible. Choose one option"
)
assert isinstance(lower_limit, AssetBase), "Incorrect format on lower_limit argument, give only assets type"
self.lower_limit = lower_limit
if upper_limit is not None:
assert isinstance(
upper_limit, AssetBase
), "Incorrect format on upper_limit argument, give only assets type"
self.upper_limit = upper_limit
else:
assert isinstance(percentage_range, int), "The percentage range should be given as an integer"
self.upper_limit = self.lower_limit + (self.lower_limit * percentage_range / 100)
assert type(self.lower_limit) == type(self.upper_limit), "The specified assets are not of the same type"
assert self.lower_limit < self.upper_limit, "The upper limit cannot be greater than the lower limit"
self.__lower_limit = lower_limit if upper_limit else lower_limit - (lower_limit * tolerance / 100)
self.__upper_limit = upper_limit if upper_limit else lower_limit + (lower_limit * tolerance / 100)
AssetBase.assert_is_asset(self.__lower_limit, self.__upper_limit, error_detail="create range on")
AssetBase.assert_same_token(self.__lower_limit, self.__upper_limit, error_detail="create range on")
assert self.__lower_limit < self.__upper_limit, "The upper limit cannot be greater than the lower limit"
def __contains__(self, item: AssetBase):
assert isinstance(item, AssetBase), "Requires an asset"
assert (
type(item) == type(self.lower_limit) == type(self.upper_limit)
), "Comparing assets of different types is not possible. Make sure that given assets are of the same types"
return True if self.lower_limit <= item < self.upper_limit else False
AssetBase.assert_is_asset(item, error_detail="check if asset is in range when")
AssetBase.assert_same_token(item, self.__lower_limit, error_detail="check if asset is in range when")
return self.__lower_limit <= item <= self.__upper_limit
@classmethod
def from_(cls, data: Union[str, dict], *, treat_dict_as_testnet_currencies: bool = True) -> AssetBase:
Loading