diff --git a/experiments/simulation_configuration.py b/experiments/simulation_configuration.py index 5373fbf..09a77b2 100644 --- a/experiments/simulation_configuration.py +++ b/experiments/simulation_configuration.py @@ -4,10 +4,10 @@ from model.constants import blocks_per_day, blocks_per_year BLOCKS_PER_TIMESTEP = 1 # number of blocks per timestep (=1 if sim on per-block-basis) -SIMULATION_TIME_DAYS = 2 # number of days +SIMULATION_TIME_DAYS = 1 # number of days # number of simulation_configuration timesteps TIMESTEPS = SIMULATION_TIME_DAYS * blocks_per_day // BLOCKS_PER_TIMESTEP TIMESTEPS_PER_YEAR = blocks_per_year // BLOCKS_PER_TIMESTEP -MONTE_CARLO_RUNS = 2 # number of runs -DATA_SOURCE = 'historical' # 'mock' or 'historical' +MONTE_CARLO_RUNS = 1 # number of runs +DATA_SOURCE = 'mock' # 'mock' or 'historical' TOTAL_BLOCKS = (BLOCKS_PER_TIMESTEP * TIMESTEPS) + 1 diff --git a/model/entities/account.py b/model/entities/account.py index 64be74e..ec1419a 100644 --- a/model/entities/account.py +++ b/model/entities/account.py @@ -5,10 +5,11 @@ # pylint: disable=too-few-public-methods from typing import TYPE_CHECKING from uuid import UUID -from model.entities.balance import Balance if TYPE_CHECKING: + from model.entities.balance import Balance from model.generators.accounts import AccountGenerator + class Account(): """ Agent / Account Holder @@ -16,14 +17,14 @@ class Account(): parent: "AccountGenerator" account_id: UUID account_name: str - balance: Balance + balance: "Balance" def __init__( self, parent: "AccountGenerator", account_id: UUID, account_name: str, - balance: Balance + balance: "Balance" ): self.parent = parent self.account_id = account_id diff --git a/model/entities/strategies/strategy_arbitrage_trader.py b/model/entities/strategies/strategy_arbitrage_trader.py index 3e49181..c8562f7 100644 --- a/model/entities/strategies/strategy_arbitrage_trader.py +++ b/model/entities/strategies/strategy_arbitrage_trader.py @@ -1,17 +1,11 @@ """ Strategy: Arbitrage Trader """ -from enum import Enum import numpy as np from model.types.base import MentoBuckets from .trader_strategy import TraderStrategy - - -class TradingRegime(Enum): - SELL_STABLE = "SELL_STABLE" - SELL_RESERVE_ASSET = "SELL_RESERVE_ASSET" - PASS = "PASS" +from .trader_strategy import TradingRegime # pylint: disable=using-constant-test @@ -28,9 +22,9 @@ def __init__(self, parent, acting_frequency=1): def sell_reserve_asset(self, _params, prev_state): # Arb trade will sell CELO if CELO/USD > CELO/cUSD - return self.trading_regime(prev_state) == TradingRegime.SELL_RESERVE_ASSET + return self.determine_trading_regime(prev_state) == TradingRegime.SELL_RESERVE_ASSET - def trading_regime(self, prev_state) -> TradingRegime: + def determine_trading_regime(self, prev_state) -> TradingRegime: """ Indicates how the trader will act depending on the relation of mento price and market price @@ -63,7 +57,7 @@ def define_expressions(self, _params, prev_state): mento_buckets = self.mento_buckets(prev_state) spread = self.exchange_config.spread - if self.trading_regime(prev_state) == TradingRegime.SELL_STABLE: + if self.trading_regime == TradingRegime.SELL_STABLE: self.expressions["profit"] = ( -1 * self.variables["sell_amount"] * mento_buckets['reserve_asset'] @@ -72,7 +66,7 @@ def define_expressions(self, _params, prev_state): + (1 - spread) * self.variables["sell_amount"] + market_price * self.variables["sell_amount"] ) - elif self.trading_regime(prev_state) == TradingRegime.SELL_RESERVE_ASSET: + elif self.trading_regime == TradingRegime.SELL_RESERVE_ASSET: self.expressions["profit"] = ( -self.variables["sell_amount"] * mento_buckets['stable'] @@ -83,7 +77,7 @@ def define_expressions(self, _params, prev_state): ) def trader_passes_step(self, _params, prev_state): - return (self.trading_regime(prev_state) == "PASS") or \ + return (self.determine_trading_regime(prev_state) == "PASS") or \ (prev_state["timestep"] % self.acting_frequency != 0) # # pylint: disable=attribute-defined-outside-init @@ -94,23 +88,21 @@ def calculate(self, _params, prev_state): market_price = self.market_price(prev_state) mento_buckets = self.mento_buckets(prev_state) spread = self.exchange_config.spread - mento_price = mento_buckets['stable'] / mento_buckets['reserve_asset'] - if market_price * (1 - spread) > mento_price: + if self.trading_regime == TradingRegime.SELL_STABLE: self.sell_order_stable( mento_buckets, market_price, spread ) - elif market_price / (1 - spread) < mento_price: + elif self.trading_regime == TradingRegime.SELL_RESERVE_ASSET: self.sell_order_reserve_asset( mento_buckets, market_price, spread ) - else: - self.order = {"sell_amount": None} - self.sell_amount = self.order["sell_amount"] + + # self.sell_amount = self.order["sell_amount"] def sell_order_stable(self, buckets: MentoBuckets, market_price: float, spread: float): """ @@ -127,13 +119,9 @@ def sell_order_stable(self, buckets: MentoBuckets, market_price: float, spread: spread ) - self.order = { - "sell_amount": min( - sell_amount, - max_budget_stable, - ), - "sell_reserve_asset": False, - } + self.order.sell_amount = min(sell_amount, + max_budget_stable,) + self.order.sell_reserve_asset = False def sell_order_reserve_asset( self, @@ -154,15 +142,12 @@ def sell_order_reserve_asset( spread ) - self.order = { - "sell_amount": min( - sell_amount, - max_budget_celo, - ), - "sell_reserve_asset": True, - } + self.order.sell_amount = min(sell_amount, + max_budget_celo,) + self.order.sell_reserve_asset = True # pylint: disable=no-self-use + def optimal_sell_amount(self, bucket_sell, bucket_buy, price_buy_sell, spread): """ bucket_sell: bucket of asset that is sold diff --git a/model/entities/strategies/strategy_random_trader.py b/model/entities/strategies/strategy_random_trader.py index 1fc7a39..c8cbd44 100644 --- a/model/entities/strategies/strategy_random_trader.py +++ b/model/entities/strategies/strategy_random_trader.py @@ -1,27 +1,30 @@ """ Strategy: Random Trader """ +from typing import List from cvxpy import Variable import numpy as np from experiments import simulation_configuration -from .trader_strategy import TraderStrategy +from model.entities.strategies.trader_strategy import TraderStrategy, TradingRegime +from model.types.order import Order + class RandomTrading(TraderStrategy): """ Random Trading """ + order_list: List[Order] def __init__(self, parent, acting_frequency=1): # The following is used to define the strategy and needs to be provided in subclass super().__init__(parent, acting_frequency) - self.generate_sell_amounts() - self.sell_amount = None self.rng = parent.rngp.get_rng("RandomTrader", self.parent.account_id) + self.order_list = self.generate_sell_amounts() def sell_reserve_asset(self, _params, prev_state): - return self.orders[prev_state["timestep"]]["sell_reserve_asset"] + return self.order_list["sell_reserve_asset"][prev_state["timestep"]] def define_variables(self): self.variables["sell_amount"] = Variable(pos=True) @@ -52,7 +55,7 @@ def define_constraints(self, params, prev_state): self.variables["sell_amount"] <= min( max_budget_reserve_asset, - self.orders[prev_state["timestep"]]["sell_amount"] + self.order_list["sell_amount"][prev_state["timestep"]] ) ) else: @@ -60,31 +63,44 @@ def define_constraints(self, params, prev_state): self.variables["sell_amount"] <= min( max_budget_stable, - self.orders[prev_state["timestep"]]["sell_amount"] + self.order_list["sell_amount"][prev_state["timestep"]] ) ) + def determine_trading_regime(self, prev_state) -> TradingRegime: + """ + Indicates how the trader will act depending on the relation of mento price + and market price + """ + sell_reserve_asset = self.order_list[ + 'sell_reserve_asset'][prev_state['timestep']] + regime = TradingRegime.PASS + if sell_reserve_asset: + regime = TradingRegime.SELL_RESERVE_ASSET + elif not sell_reserve_asset: + regime = TradingRegime.SELL_STABLE + return regime + def generate_sell_amounts( self, - blocks_per_timestep=simulation_configuration.BLOCKS_PER_TIMESTEP, - timesteps=simulation_configuration.TIMESTEPS, ): """ This function generates lognormal returns """ - # timesteps_per_year = constants.blocks_per_year // blocks_per_timestep - sample_size = timesteps * blocks_per_timestep + 1 - # TODO parametrise random params incl. seed - sell_gold = self.rng.binomial(1, 0.5, sample_size) - orders = np.vstack( - [sell_gold, np.abs(self.rng.normal(100, 5, size=sample_size))] - ) - self.orders = np.core.records.fromarrays( - orders, names=["sell_reserve_asset", "sell_amount"] - ) + sample_size = simulation_configuration.TOTAL_BLOCKS + 1 + + sell_reserve_asset = (self.rng.binomial(1, 0.5, sample_size) == 1) + sell_amount = np.abs(self.rng.normal(1000, 0, size=sample_size)) + + orders = {"sell_reserve_asset": sell_reserve_asset, + "sell_amount": sell_amount} + return orders def calculate(self, _params, prev_state): """ Calculates optimal trade if analytical solution is available """ - self.sell_amount = self.orders[prev_state["timestep"]]["sell_amount"] + sell_amounts = self.order_list["sell_amount"] + order_directions = self.order_list["sell_reserve_asset"] + self.order.sell_amount = sell_amounts[prev_state["timestep"]] + self.order.sell_reserve_asset = order_directions[prev_state["timestep"]] diff --git a/model/entities/strategies/trader_strategy.py b/model/entities/strategies/trader_strategy.py index 50a1c69..fc224f2 100644 --- a/model/entities/strategies/trader_strategy.py +++ b/model/entities/strategies/trader_strategy.py @@ -8,6 +8,7 @@ inside of solve() but the objective_function and the constraints should still be specified for completeness! """ +from enum import Enum from typing import TYPE_CHECKING import logging from cvxpy import Maximize, Minimize, Problem, Variable @@ -15,11 +16,18 @@ from model.types.base import MentoBuckets from model.types.pair import Pair +from model.types.order import Order from model.types.configs import MentoExchangeConfig if TYPE_CHECKING: from model.entities.trader import Trader +class TradingRegime(Enum): + SELL_STABLE = "SELL_STABLE" + SELL_RESERVE_ASSET = "SELL_RESERVE_ASSET" + PASS = "PASS" + + class TraderStrategy: """ Base trader strategy class to solve a convex optimisation problem. @@ -28,6 +36,8 @@ class TraderStrategy: """ parent: "Trader" exchange_config: MentoExchangeConfig + trading_regime: TradingRegime + order: Order def __init__(self, parent: "Trader", acting_frequency): self.parent = parent @@ -41,9 +51,13 @@ def __init__(self, parent: "Trader", acting_frequency): self.objective_function = None self.optimization_direction = None self.constraints = [] - # TODO order vs sell_amount ??? - self.sell_amount = None - self.order = None + self.trading_regime = None + self.order = Order(account_id=self.parent.account_id, + pair=None, + sell_amount=None, + buy_amount=None, + sell_reserve_asset=None, + exchange=self.parent.config.exchange) @property def reference_fiat(self): @@ -79,6 +93,12 @@ def define_expressions(self, _params, _prev_state): """ raise NotImplementedError("Subclasses must implement define_expressions()") + def determine_trading_regime(self, _prev_state) -> TradingRegime: + """ + Indicates how the trader will act depending on the state of the market + """ + raise NotImplementedError("Subclasses must implement trading_regime()") + def define_constraints(self, params, prev_state): """ Defines and returns the constraints under which the optimization is conducted @@ -167,41 +187,36 @@ def minimise_price_impact(self, sell_amount, sell_reserve_asset, params): return sell_amount_adjusted def trader_passes_step(self, _params, prev_state): + """ + evaluates whether trader passes current step due to acting frequency + """ return prev_state["timestep"] % self.acting_frequency != 0 - def return_optimal_trade(self, params, prev_state): + def create_order(self, params, prev_state): """ Returns the optimal action to be executed by actor """ - if self.trader_passes_step(params, prev_state): - # Actor not acting this timestep - trade = None - else: + self.determine_trading_regime(prev_state) + if (not self.trader_passes_step(params, prev_state) + and self.trading_regime != TradingRegime.PASS): self.optimize(params=params, prev_state=prev_state) - sell_amount = ( - self.variables["sell_amount"].value - if self.variables - else self.sell_amount + self.order.sell_amount = ( + self.variables["sell_amount"].value if self.variables else self.order.sell_amount ) - if sell_amount is None or sell_amount == 0: - trade = None + + if self.order.sell_amount is None or self.order.sell_amount == 0: + self.order.sell_amount = None else: - sell_reserve_asset = self.sell_reserve_asset(params, prev_state) - sell_amount = self.minimise_price_impact( - sell_amount, sell_reserve_asset, params) - buy_amount = self.mento.get_buy_amount( + #self.order.pair = self.sell_reserve_asset(params, prev_state) + self.order.sell_amount = self.minimise_price_impact( + self.order.sell_amount, self.order.pair, params) + self.order.buy_amount = self.mento.get_buy_amount( exchange=self.parent.config.exchange, - sell_amount=sell_amount, - sell_reserve_asset=sell_reserve_asset, + sell_amount=self.order.sell_amount, + sell_reserve_asset=self.order.sell_reserve_asset, prev_state=prev_state, ) - - trade = { - "sell_reserve_asset": sell_reserve_asset, - "sell_amount": sell_amount, - "buy_amount": buy_amount, - } - return trade + self.parent.order = self.order def market_price(self, prev_state) -> float: # TODO: Do we need to quote in equivalent Fiat for Stable? diff --git a/model/entities/trader.py b/model/entities/trader.py index 44f1a27..143a99c 100644 --- a/model/entities/trader.py +++ b/model/entities/trader.py @@ -4,12 +4,13 @@ """ # pylint: disable=too-few-public-methods from typing import TYPE_CHECKING -from copy import deepcopy from uuid import UUID from model.generators.mento import MentoExchangeGenerator from model.entities import strategies -from model.entities.account import Account, Balance +from model.entities.account import Account +from model.entities.balance import Balance +from model.types.order import Order from model.types.pair import Pair from model.types.configs import MentoExchangeConfig, TraderConfig from model.utils.rng_provider import RNGProvider @@ -27,6 +28,7 @@ class Trader(Account): exchange_config: MentoExchangeConfig mento: MentoExchangeGenerator rngp: RNGProvider + order: Order def __init__( self, @@ -40,11 +42,10 @@ def __init__( self.rngp = rngp self.mento = self.parent.container.get(MentoExchangeGenerator) self.config = config - self.exchange_config = self.mento.configs.get(self.config.exchange) - strategy_class = getattr(strategies, config.trader_type.value) assert strategy_class is not None, f"{config.trader_type.value} is not a strategy" self.strategy = strategy_class(self) + self.order = None def execute( self, @@ -54,34 +55,18 @@ def execute( """ Execute the agent's state change """ - order = self.strategy.return_optimal_trade(params, prev_state) - if order is None: + self.strategy.create_order(params, prev_state) + if self.order.sell_amount is None: return { "mento_buckets": prev_state["mento_buckets"], "floating_supply": prev_state["floating_supply"], "reserve_balance": prev_state["reserve_balance"], } - sell_amount = order["sell_amount"] - sell_reserve_asset = order["sell_reserve_asset"] - self.rebalance_portfolio(sell_amount, sell_reserve_asset, prev_state) - - next_bucket, delta = self.mento.exchange( - self.config.exchange, - sell_amount, - sell_reserve_asset, - prev_state - ) - - self.balance += delta - reserve_delta = Balance({ - self.exchange_config.reserve_asset: - -1 * delta.get(self.exchange_config.reserve_asset), - }) - self.parent.reserve.balance += reserve_delta + self.rebalance_portfolio(self.order.sell_amount, + self.order.sell_reserve_asset, params, prev_state) - next_buckets = deepcopy(prev_state["mento_buckets"]) - next_buckets[self.config.exchange] = next_bucket + next_buckets = self.mento.process_order(self.order, prev_state) return { "mento_buckets": next_buckets, @@ -89,16 +74,16 @@ def execute( "reserve_balance": self.parent.reserve.balance, } - def rebalance_portfolio(self, target_amount, target_is_reserve_asset, prev_state): + def rebalance_portfolio(self, target_amount, target_is_reserve_asset, params, prev_state): """ Sometimes the optimal trade might require selling more of an asset than the trader has in his portfolio, but the total value of the portfolio would cover it therefore they can rebalance and execute the trade. """ - reserve_asset = self.exchange_config.reserve_asset - stable = self.exchange_config.stable - reference_fiat = self.exchange_config.reference_fiat + reserve_asset = params['mento_exchanges_config'][self.config.exchange].reserve_asset + stable = params['mento_exchanges_config'][self.config.exchange].stable + reference_fiat = params['mento_exchanges_config'][self.config.exchange].reference_fiat # TODO: Should these be quoted in the specific # fiat of the stable? diff --git a/model/generators/mento.py b/model/generators/mento.py index 8dec9b3..7f3bdf7 100644 --- a/model/generators/mento.py +++ b/model/generators/mento.py @@ -4,17 +4,25 @@ Handles one or more mento instances """ +from copy import deepcopy +from functools import cached_property from typing import Any, Dict, Set import numpy as np from model.constants import blocktime_seconds from model.entities.balance import Balance +from model.types.order import Order +from model.utils.generator_container import GeneratorContainer from model.types.base import MentoBuckets, MentoExchange, Stable from model.types.pair import Pair from model.types.configs import MentoExchangeConfig from model.utils.generator import Generator, state_update_blocks from model.utils import update_from_signal +# if TYPE_CHECKING: +# from model.entities.balance import Balance +# from model.generators.accounts import AccountGenerator + # raise numpy warnings as errors np.seterr(all='raise') @@ -31,18 +39,27 @@ class MentoExchangeGenerator(Generator): """ configs: Dict[MentoExchange, MentoExchangeConfig] active_exchanges: Set[MentoExchange] + container: GeneratorContainer - def __init__(self, configs: Dict[Stable, MentoExchangeConfig], active_exchanges: Set[Stable]): + def __init__(self, configs: Dict[Stable, MentoExchangeConfig], + active_exchanges: Set[Stable], container: GeneratorContainer): self.configs = configs self.active_exchanges = active_exchanges + self.container = container @classmethod - def from_parameters(cls, params, _initial_state, _container): + def from_parameters(cls, params, _initial_state, container): return cls( params['mento_exchanges_config'], - set(params['mento_exchanges_active']) + set(params['mento_exchanges_active']), + container ) + @cached_property + def account_generator(self): + from model.generators.accounts import AccountGenerator + return self.container.get(AccountGenerator) + @state_update_blocks('bucket_update') def bucket_update(self): return [{ @@ -146,6 +163,7 @@ def exchange(self, exchange: MentoExchange, sell_amount, sell_reserve_asset, pre """ Update the simulation state with a trade between the reserve currency and stable """ + config = self.configs.get(exchange) assert config is not None @@ -171,3 +189,27 @@ def exchange(self, exchange: MentoExchange, sell_amount, sell_reserve_asset, pre }) return (next_bucket, delta) + + def process_order(self, order: Order, prev_state): + """ + wrapper function responsible for performing exchange and balancing accounts + """ + next_bucket, delta = self.exchange( + order.exchange, + order.sell_amount, + order.sell_reserve_asset, + prev_state + ) + config = self.configs.get(order.exchange) + account = self.account_generator.accounts_by_id.get(order.account_id) + account.balance += delta + + reserve_delta = Balance({ + config.reserve_asset: + -1 * delta.get(config.reserve_asset), + }) + self.account_generator.reserve.balance += reserve_delta + + next_buckets = deepcopy(prev_state["mento_buckets"]) + next_buckets[order.exchange] = next_bucket + return next_buckets diff --git a/model/system_parameters.py b/model/system_parameters.py index 7227366..9f02f75 100644 --- a/model/system_parameters.py +++ b/model/system_parameters.py @@ -213,16 +213,18 @@ class InitParameters(TypedDict): ], impacted_assets=[[ Pair(CryptoAsset.CELO, Fiat.USD), + Pair(CryptoAsset.CELO, Fiat.EUR), + Pair(CryptoAsset.CELO, Fiat.BRL), Pair(Stable.CUSD, Fiat.USD), Pair(Stable.CEUR, Fiat.EUR), Pair(Stable.CREAL, Fiat.BRL), - Pair(CryptoAsset.BTC, Fiat.USD), - Pair(CryptoAsset.ETH, Fiat.USD), - Pair(CryptoAsset.DAI, Fiat.USD), + ]], variance_market_price=[{ Pair(CryptoAsset.CELO, Fiat.USD): 1, + Pair(CryptoAsset.CELO, Fiat.EUR): 1, + Pair(CryptoAsset.CELO, Fiat.BRL): 1, Pair(Stable.CUSD, Fiat.USD): 0.01, Pair(Stable.CEUR, Fiat.EUR): 0.01, Pair(Stable.CREAL, Fiat.BRL): 0.01, @@ -241,10 +243,16 @@ class InitParameters(TypedDict): ), TraderConfig( trader_type=TraderType.ARBITRAGE_TRADER, - count=2, + count=1, balance=Balance({CryptoAsset.CELO: 500000, Stable.CEUR: 1000000}), exchange=MentoExchange.CEUR_CELO ), + # TraderConfig( + # trader_type=TraderType.RANDOM_TRADER, + # count=1, + # balance=Balance({CryptoAsset.CELO: 500000, Stable.CEUR: 1000000}), + # exchange=MentoExchange.CEUR_CELO + # ), ] ], diff --git a/model/types/base.py b/model/types/base.py index d5c6a9f..2d75bc5 100644 --- a/model/types/base.py +++ b/model/types/base.py @@ -2,14 +2,31 @@ Various Python types used in the model """ from __future__ import annotations +from enum import Enum, EnumMeta from typing import TypedDict, Union -from enum import Enum + +from model.entities.account import Account class SerializableEnum(Enum): def __str__(self): return self.value +# pylint: disable=no-value-for-parameter + + +class MetaEnum(EnumMeta): + def __contains__(cls, item): + try: + cls(item) + except ValueError: + return False + return True + + +class ReflectiveSerializableEnum(SerializableEnum, metaclass=MetaEnum): + pass + class TraderType(Enum): """ @@ -20,7 +37,7 @@ class TraderType(Enum): MAX_TRADER = "SellMax" -class Stable(SerializableEnum): +class Stable(ReflectiveSerializableEnum): """ Celo Stable assets """ @@ -29,14 +46,14 @@ class Stable(SerializableEnum): CEUR = "ceur" -class CryptoAsset(SerializableEnum): +class CryptoAsset(ReflectiveSerializableEnum): CELO = "celo" ETH = "eth" BTC = "btc" DAI = "dai" -class Fiat(SerializableEnum): +class Fiat(ReflectiveSerializableEnum): USD = "usd" EUR = "eur" BRL = "brl" @@ -79,3 +96,8 @@ class AggregationMethod(Enum): class OracleType(Enum): SINGLE_SOURCE = 'single_source' + + +class Exchange(SerializableEnum): + MENTO = 'mento' + GENERAL_MARKET = 'general_market' diff --git a/model/types/order.py b/model/types/order.py new file mode 100644 index 0000000..dd45e59 --- /dev/null +++ b/model/types/order.py @@ -0,0 +1,24 @@ +""" +Provides a Order class +""" + + +#from typing import TYPE_CHECKING +from model.types.base import Account, Exchange +from model.types.pair import Pair + + +# if TYPE_CHECKING: +# from model.entities.account import Account + +# pylint:disable = too-few-public-methods + + +class Order(): + def __init__(self, account_id, pair, sell_amount, buy_amount, sell_reserve_asset, exchange): + self.account_id: Account = account_id + self.pair: Pair = pair + self.sell_amount: float = sell_amount + self.buy_amount: float = buy_amount + self.sell_reserve_asset: bool = sell_reserve_asset + self.exchange: Exchange = exchange diff --git a/model/types/pair.py b/model/types/pair.py index a652a17..99ffb1e 100644 --- a/model/types/pair.py +++ b/model/types/pair.py @@ -47,6 +47,22 @@ def __str__(self): def inverse(self) -> Pair: return Pair(self.quote, self.base) + @classmethod + def parse_from_string(cls, string): + """ + Creating Pair from string 'baseccy_quoteccy' + """ + base, quote = string.split('_') + assert base not in Fiat, f'Wrong quote convention used for {string}' + assert quote in Fiat, f'Wrong quote convention used for {string}' + if base in CryptoAsset: + base = CryptoAsset(base) + elif base in Stable: + base = Stable(base) + quote = Fiat(quote) + + return Pair(base, quote) + def get_rate(self, state: StateVariables) -> float: """ Get the market rate for any pair as long as there's a path diff --git a/model/utils/data_feed.py b/model/utils/data_feed.py index cd15b2f..8f8a3a9 100644 --- a/model/utils/data_feed.py +++ b/model/utils/data_feed.py @@ -11,6 +11,7 @@ # pylint: disable = unused-import # pylint: disable = redefined-outer-name import data.mock_data # this is necessary to create mock data if not existent +from model.types.pair import Pair DATA_FOLDER = Path(__file__, "../../../data/").resolve() MOCK_DATA_FILE_NAME = "mock_logreturns.prq" @@ -36,7 +37,8 @@ def __init__(self, data_folder): self.data = np.array(self.historical_data) self.length = len(self.historical_data) - self.assets = list(self.historical_data.columns) + self.assets = [Pair.parse_from_string(name) + for name in self.historical_data.columns] def load_mock_data(self, data_file_name): """ @@ -59,8 +61,10 @@ def load_historical_data(self, data_file_name): raise NotImplementedError(f"File extension {file_extension} not supported") historical_log_returns = self.calculate_log_returns(historical_prices) + print(historical_log_returns) return historical_log_returns + # pylint: disable =no-self-use def calculate_log_returns(self, data_frame): """ calculates log returns out of a data frame with price time series in its columns