Skip to content

Commit

Permalink
Connecting Quantlib with RNGP (#174)
Browse files Browse the repository at this point in the history
* oracle and mento fix

* Connecting Quantlib with RNGP

* Fix random number seeding (#175)

* Fix random number seeding

* Fix comments

Co-authored-by: tobikuhlmann <[email protected]>
Co-authored-by: boqdan <[email protected]>
  • Loading branch information
3 people authored Jun 14, 2022
1 parent 8650cf0 commit d780fd8
Show file tree
Hide file tree
Showing 12 changed files with 127 additions and 54 deletions.
2 changes: 1 addition & 1 deletion experiments/simulation_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
# 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 = 1 # number of runs
MONTE_CARLO_RUNS = 2 # number of runs
DATA_SOURCE = 'historical' # 'mock' or 'historical'
TOTAL_BLOCKS = (BLOCKS_PER_TIMESTEP * TIMESTEPS) + 1
2 changes: 0 additions & 2 deletions model/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,3 @@
celo_linear_total_epoch_rewards = celo_total_epoch_rewards / 2
target_epoch_rewards = celo_linear_total_epoch_rewards / 15 / 365
target_epoch_rewards_downscaled = target_epoch_rewards * 0.8

global_rng_entropy = 1000
11 changes: 9 additions & 2 deletions model/entities/oracle_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
from typing import List
from uuid import UUID
from model.types import OracleConfig, Pair
from model.utils.rng_provider import rngp
from model.constants import blocktime_seconds
from model.utils.rng_provider import RNGProvider


class OracleProvider():
Expand All @@ -17,7 +17,14 @@ class OracleProvider():
id_: UUID
config: OracleConfig

def __init__(self, name: str, oracle_id: UUID, config: OracleConfig, pairs: List[Pair]):
def __init__(
self,
name: str,
oracle_id: UUID,
config: OracleConfig,
pairs: List[Pair],
rngp: RNGProvider
):
self.name = name
self.orace_id = oracle_id
self.config = config
Expand Down
3 changes: 1 addition & 2 deletions model/entities/strategies/strategy_random_trader.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import numpy as np

from experiments import simulation_configuration
from model.utils.rng_provider import rngp

from .trader_strategy import TraderStrategy

Expand All @@ -19,7 +18,7 @@ def __init__(self, parent, acting_frequency=1):
super().__init__(parent, acting_frequency)
self.generate_sell_amounts()
self.sell_amount = None
self.rng = rngp.get_rng("RandomTrader", self.parent.account_id)
self.rng = parent.rngp.get_rng("RandomTrader", self.parent.account_id)

def sell_reserve_asset(self, _params, prev_state):
return self.orders[prev_state["timestep"]]["sell_reserve_asset"]
Expand Down
4 changes: 4 additions & 0 deletions model/entities/trader.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from model.entities import strategies
from model.entities.account import Account, Balance
from model.types import MentoExchangeConfig, Pair, TraderConfig
from model.utils.rng_provider import RNGProvider

if TYPE_CHECKING:
from model.generators.accounts import AccountGenerator
Expand All @@ -23,15 +24,18 @@ class Trader(Account):
config: TraderConfig
exchange_config: MentoExchangeConfig
mento: MentoExchangeGenerator
rngp: RNGProvider

def __init__(
self,
parent: "AccountGenerator",
account_id: UUID,
account_name: str,
config: TraderConfig,
rngp: RNGProvider
):
super().__init__(parent, account_id, account_name, config.balance)
self.rngp = rngp
self.mento = self.parent.container.get(MentoExchangeGenerator)
self.config = config
self.exchange_config = self.mento.configs.get(self.config.exchange)
Expand Down
10 changes: 8 additions & 2 deletions model/generators/accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from model.utils import update_from_signal
from model.utils.generator import Generator, state_update_blocks
from model.utils.generator_container import GeneratorContainer
from model.utils.rng_provider import RNGProvider

ACCOUNTS_NS = uuid4()

Expand All @@ -28,13 +29,16 @@ class AccountGenerator(Generator):
# generator.
untracked_floating_supply: Balance
container: GeneratorContainer
rngp: RNGProvider

def __init__(self,
reserve_inventory: Balance,
initial_floating_supply: Balance,
traders: List[TraderConfig],
container: GeneratorContainer):
container: GeneratorContainer,
rngp: RNGProvider):
self.container = container
self.rngp = rngp
self.reserve = self.create_reserve_account(
initial_balance=reserve_inventory
)
Expand All @@ -55,6 +59,7 @@ def from_parameters(cls, params, initial_state, container):
Balance(initial_state["floating_supply"]),
params["traders"],
container,
params["rngp"]
)

return accounts
Expand All @@ -76,7 +81,8 @@ def create_trader(self, account_name: str, config: TraderConfig):
self,
account_id=uuid5(ACCOUNTS_NS, account_name),
account_name=account_name,
config=config
config=config,
rngp=self.rngp
)
self.accounts_by_id[account.account_id] = account
return account
Expand Down
18 changes: 12 additions & 6 deletions model/generators/markets.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@
from model.utils.generator import Generator
from model.utils.price_impact_valuator import PriceImpactValuator
from model.utils.quantlib_wrapper import QuantLibWrapper
from model.utils.rng_provider import rngp
from model.utils.rng_provider import RNGProvider

# raise numpy warnings as errors
np.seterr(all='raise')


class MarketPriceGenerator(Generator):
"""
This class is providing a market environment
Expand All @@ -34,6 +35,7 @@ def __init__(
self,
model,
impacted_assets,
rngp: RNGProvider,
increments=None,
):
self.model = model
Expand All @@ -48,22 +50,26 @@ def from_parameters(cls, params: Parameters, _initial_state, _container):
if model == MarketPriceModel.QUANTLIB:
market_price_generator = cls(
model,
params['impacted_assets']
params['impacted_assets'],
params['rngp']
)
seed_sequence = params['rngp'].__seed__(["QuantLib"])
quant_lib_seed = int(seed_sequence.generate_state(1)[0])
quant_lib_wrapper = QuantLibWrapper(
params['market_price_processes'],
params['market_price_correlation_matrix'],
TOTAL_BLOCKS
TOTAL_BLOCKS,
quant_lib_seed
)
market_price_generator.increments = quant_lib_wrapper.correlated_returns()
elif model == MarketPriceModel.PRICE_IMPACT:
market_price_generator = cls(model, params['impacted_assets'])
market_price_generator = cls(model, params['impacted_assets'], params['rngp'])
elif model == MarketPriceModel.HIST_SIM:
market_price_generator = cls(model, params['impacted_assets'])
market_price_generator = cls(model, params['impacted_assets'], params['rngp'])
market_price_generator.historical_returns()
logging.info("increments updated")
elif model == MarketPriceModel.SCENARIO:
market_price_generator = cls(model, params['impacted_assets'])
market_price_generator = cls(model, params['impacted_assets'], params['rngp'])
market_price_generator.historical_returns()
logging.info("increments updated")
return market_price_generator
Expand Down
13 changes: 8 additions & 5 deletions model/generators/oracles.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from model.types import OracleConfig, Pair
from model.utils import update_from_signal
from model.utils.generator import Generator, state_update_blocks
from model.utils.rng_provider import rngp
from model.utils.rng_provider import RNGProvider

ORACLES_NS = uuid4()

Expand All @@ -27,14 +27,16 @@ class OracleRateGenerator(Generator):
oracles_by_id: Dict[UUID, OracleProvider]
oracles_by_pair: Dict[Pair, List[OracleProvider]]
oracle_pairs: List[Pair]
rngp: RNGProvider

def __init__(
self,
oracles: List[OracleConfig],
oracle_pairs: List[Pair]
oracle_pairs: List[Pair],
rngp: RNGProvider
):
self.input = None
self.rng = rngp.get_rng("OracleGenerator")
self.rngp = rngp
self.oracle_pairs = oracle_pairs
self.oracles_by_pair = {pair: [] for pair in oracle_pairs}
self.oracles_by_id = {}
Expand All @@ -44,7 +46,7 @@ def __init__(

@classmethod
def from_parameters(cls, params, _initial_state, _container):
oracle_generator = cls(params['oracles'], params['oracle_pairs'])
oracle_generator = cls(params['oracles'], params['oracle_pairs'], params['rngp'])
return oracle_generator

def create_oracle(self, index: int, oracle_config: OracleConfig, oracle_pairs: List[Pair]):
Expand All @@ -57,7 +59,8 @@ def create_oracle(self, index: int, oracle_config: OracleConfig, oracle_pairs: L
oracle_provider = OracleProvider(name=oracle_name,
oracle_id=oracle_id,
config=oracle_config,
pairs=oracle_pairs)
pairs=oracle_pairs,
rngp=self.rngp)
self.oracles_by_id[oracle_id] = oracle_provider
for pair in self.oracle_pairs:
self.oracles_by_pair[pair].append(oracle_provider)
Expand Down
5 changes: 5 additions & 0 deletions model/system_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
TraderConfig,
TraderType,
)
from model.utils.rng_provider import RNGProvider


class Parameters(TypedDict):
Expand All @@ -35,6 +36,8 @@ class Parameters(TypedDict):
after the parameter sweep.
Used as type annotation for functions that take params.
"""
rng_seed: int
rngp: RNGProvider
mento_exchanges_config: Dict[Stable, MentoExchangeConfig]
mento_exchanges_active: List[MentoExchange]
market_price_model: MarketPriceModel
Expand All @@ -54,6 +57,7 @@ class InitParameters(TypedDict):
Each System Parameter is defined as:
system parameter key: system parameter type = default system parameter value
"""
rng_seed: List[int]
mento_exchanges_config: List[Dict[Stable, MentoExchangeConfig]]
mento_exchanges_active: List[List[MentoExchange]]
market_price_model: List[MarketPriceModel]
Expand All @@ -70,6 +74,7 @@ class InitParameters(TypedDict):


parameters = InitParameters(
rng_seed=[1000],
# Configuration params for each stable's exchange
mento_exchanges_config=[{
MentoExchange.CUSD_CELO: MentoExchangeConfig(
Expand Down
82 changes: 59 additions & 23 deletions model/utils/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,22 @@
radCAD Engine extension to give us more control over how simulations happen
"""
import copy
from functools import reduce
from typing import Any, Callable, Dict, List, NamedTuple
from radcad.engine import Engine as RadCadEngine
from radcad import core, wrappers

from model.utils.rng_provider import RNGProvider

from .generator_container import GENERATOR_CONTAINER_PARAM_KEY, GeneratorContainer


class SimulationConfig(NamedTuple):
params: Dict[str, Any]
state: Dict[str, Any]
state_update_blocks: List[Any]
run_index: int

# pylint: disable=too-many-locals,protected-access,too-few-public-methods
class Engine(RadCadEngine):
"""
Expand Down Expand Up @@ -54,18 +65,19 @@ def _run_stream(self, configs):
params
)
self.executable._before_subset(context=context)
params_for_run = __inject_generator_container__(param_set, initial_state)
state_update_blocks_for_run = __hydrate_state_update_blocks__(
state_update_blocks,
params_for_run)
config = __prepare_simulation_config__(SimulationConfig(
param_set,
initial_state,
state_update_blocks,
run_index))
yield wrappers.RunArgs(
simulation_index,
timesteps,
run_index,
subset_index,
copy.deepcopy(initial_state),
state_update_blocks_for_run,
params_for_run,
copy.deepcopy(config.state),
config.state_update_blocks,
config.params,
self.deepcopy,
self.drop_substeps)
self.executable._after_subset(context=context)
Expand All @@ -81,19 +93,20 @@ def _run_stream(self, configs):
self.executable._before_run(context=context)
self.executable._before_subset(context=context)

params_for_run = __inject_generator_container__(param_set, initial_state)
state_update_blocks_for_run = __hydrate_state_update_blocks__(
state_update_blocks,
params_for_run)
config = __prepare_simulation_config__(SimulationConfig(
param_set,
initial_state,
state_update_blocks,
run_index))

yield wrappers.RunArgs(
simulation_index,
timesteps,
run_index,
0,
copy.deepcopy(initial_state),
state_update_blocks_for_run,
params_for_run,
copy.deepcopy(config.state),
config.state_update_blocks,
config.params,
self.deepcopy,
self.drop_substeps,
)
Expand All @@ -104,23 +117,46 @@ def _run_stream(self, configs):
simulation=simulation
)

def __inject_generator_container__(params, initial_state):
params.update({
def __inject_rng_provider__(config: SimulationConfig):
config.params.update({
'rngp': RNGProvider(config.params['rng_seed'], config.run_index)
})
return config

def __inject_generator_container__(config: SimulationConfig):
config.params.update({
GENERATOR_CONTAINER_PARAM_KEY: GeneratorContainer(
copy.deepcopy(params),
copy.deepcopy(initial_state)
copy.deepcopy(config.params),
copy.deepcopy(config.state)
)
})
return params
return config

def __hydrate_state_update_blocks__(state_update_blocks, params):
container = params.get(GENERATOR_CONTAINER_PARAM_KEY)
def __hydrate_state_update_blocks__(config: SimulationConfig):
container = config.params.get(GENERATOR_CONTAINER_PARAM_KEY)
flat_state_update_blocks = []
for state_update_block in state_update_blocks:
for state_update_block in config.state_update_blocks:
if state_update_block.get('type') == 'generator':
generator = container.get(state_update_block.get('source'))
selectors = state_update_block.get('selectors', [])
flat_state_update_blocks += generator.state_update_blocks(selectors)
else:
flat_state_update_blocks += [state_update_block]
return flat_state_update_blocks

return SimulationConfig(
config.params,
config.state,
flat_state_update_blocks,
config.run_index
)

SimulationConfigModifier = Callable[[SimulationConfig], SimulationConfig]

MODIFIERS: List[SimulationConfigModifier] = [
__inject_rng_provider__,
__inject_generator_container__,
__hydrate_state_update_blocks__,
]

def __prepare_simulation_config__(config: SimulationConfig) -> SimulationConfig:
return reduce(lambda config, modifier: modifier(config), MODIFIERS, config)
Loading

0 comments on commit d780fd8

Please sign in to comment.