Skip to main content
Version: 1.2.2

Code

The code part of the simulation is the most complex to configure but is also the beating heart of the simulator. Please reference every entry below and write actions that you would like the simulator and agents to take during the simulator.

The specific variable for the code sections are always a string, this can be the code that you specify right in the configuration file as a string. But you can also define a file link using file:// as leading string and then specify the file. The file can be specified in a folder either by giving the full path by leading the path with a / or the relative path by leading it with . but also from your home directory by leading it with ~.

SDK

For ease of use an SDK has been published with the classes of each input. When you're writing the code in a file you can easily import this sdk and use it to get easier type hinting.

To install the SDK please follow these steps.

  1. Add this to ~/.pypirc
[distutils]
index-servers =
almanak-py

[almanak-py]
repository: https://europe-west4-python.pkg.dev/almanak-production/almanak-py/
  1. Make sure this is in the pip.conf file of which the location you can fine here. (If the file is in the wrong location it won't work, for me it's ~/.config/pip/pip.conf )
[global]
extra-index-url = https://europe-west4-python.pkg.dev/almanak-production/almanak-py/simple/
  1. If this is set you can do
pip install almanak-simulations-sdk

Levels of code

There are multiple levels of code. Some code blocks can be overwritten by more specific levels, this gives the engineer who wants to run a simulation very fine-grained control of what each agent performs without having to have duplicate code in the simulation source files.

The top of the following level is the most specific and will always be taken first, if none is set it will go down the list of these options until it finds one that is set and set the code for the agent to that.

  1. Agent
  2. Agent Class
  3. General Code (this document)

So if there's code set directly to an agent it will always have priority over an agent class. If no agent code has been specified it will look at the agent class that the agent uses and if it has code, if it does the agent will use that. If it does not then it will use the general code set in the code field in the configuration file.

Wrapper Classes

Wrapper classes provide code blocks with access to some internal components of the simulator itself. These include the ability to define new variables, read current and historical price data, and access the EVM client to invoke functions at that level.

There are two wrapper classes: EnvironmentWrapper and AgentWrapper. EnvironmentWrapper provides access to environment-level data, and any custom variables defined at this level will persist across all agents. AgentWrapper, on the other hand, provides agent-specific data based on the current agent taking a step, and any variables defined at this level will only be visible to that particular agent.

Environment Wrapper

This class is a wrapper for accessing particular functions and values in the GenericEnvironment class without directly surfacing all functions and inner variables for the class itself.

__init__

This function initializes an instance of the EnvironmentWrapper class. It copies specific attributes from the provided _environment object to the newly created EnvironmentWrapper instance.

Parameters

  • _environment: The GenericEnvironment instance to be wrapped.

Returns

None.

add_variable

This function allows you to add a custom variable to the environment that you can read from at a later point.

Parameters

  • *args, **kwargs: The parameters are passed directly to the underlying _environment.add_variable() method.

Returns

None.

get_variable

This function retrieves a previously set variable from the environment.

Parameters

  • *args, **kwargs: The parameters are passed directly to the underlying _environment.get_variable() method.

Returns

The value of the variable. This can be of any type, depending on what was previously set.

set_variable

This function allows you to set a custom variable that you have added before to a different value.

Parameters

  • *args, **kwargs: The parameters are passed directly to the underlying _environment.set_variable() method.

Returns

None.

get_client

This function returns the client that can be found in the environment.

Parameters

None.

Returns

ProjectClient: The client that is used to perform actions. If no client exists, returns None.

get_current_step

Retrieve the current step number in the simulation.

Parameters

None.

Returns

int: The current step number.

get_all_prices

Retrieve the full price series. This can either be historical or simulated price data based on the historical flag. If simulated data is retrieved, prices up to the current step are returned, preventing any agent lookahead.

Parameters

None.

Returns

Dict: The key of the dict is the token and the value is the price list.

get_current_prices

This function retrieves the current price for the current step in the environment.

Parameters

None.

Returns

Dict: The key of the dict is the token symbol and the value is the price.

get_current_token_price

This function retrieves the current price for the current step for a specific token.

Parameters

token (str): The token symbol to retrieve the current price for.

Returns

float: The current price for the token.

get_current_ohlc_prices

This function retrieves the current OHLC (open, high, low, and close) prices for the current step in the environment.

Parameters

None.

Returns

Dict: The key of the dict is the token symbol and the value is the OHLC price.

get_ohlc_prices

This function retrieves the current OHLC prices for the given index in the environment.

Parameters

  • index (int): The index corresponds to the environment step.

Returns

Dict: The key of the dict is the token symbol and the value is the OHLC price.

get_historical_prices

This function retrieves historical prices for each asset.

Parameters

  • lookback (int): How many steps you want to look back to retrieve the historical price.

Returns

Dict: The key of the dict is the token symbol and the value is the closing price.

get_historical_price_slice

This function retrieves an array of historical prices for a single asset.

Parameters

  • token (str): The token symbol to retrieve the historical price data for.

  • lookback (int): How many steps you want to look back to retrieve the historical prices. A value of 0 corresponds to the previous price.

Returns

  • List: The array of past prices.

get_volatilities

This function retrieves the volatility for each asset as modeled by the price simulator when generating the prices.

Parameters

lookback (int): How many steps you want to look back to retrieve the volatility value.

get_price_slice_std

For a given token, take the last lookback prices from the current_step, and compute the sample standard deviation of that price slice.

Parameters

  • token (str): The token symbol to retrieve the volatility of the price data for.
  • lookback (int): How many prices backwards from the current step to collect.

Returns

float: The value of the sample standard deviation of the price slice.

get_environment_id

This function retrieves the ID of the environment.

Parameters

None.

Returns

int: The ID of the environment.

get_observer

This function retrieves the observer. Note that this is not currently used and will be replaced by the metric collector.

Parameters

None.

Returns

Observer: The observer object which can be used to set or read information.

get_parameters

This function retrieves the parameters that were set.

Parameters

None.

Returns

Dict: Key-value dict of all the parameters.

get_params

This function retrieves the value of a single parameter.

Parameters

None.

Returns

Any: The value of the parameter.

get_price_discrepancy

This function retrieves the price discrepancy for a given token.

Parameters

  • token: The token for which to retrieve the price discrepancy.

Returns

The price discrepancy of the given token.

set_price_discrepancy

This function sets the price discrepancy for a given token.

Parameters

  • token: The token for which to set the price discrepancy.
  • price_discrepancy: The price discrepancy to set.

Returns

None.

add_revenue

This function adds revenue to the environment.

Parameters

  • revenue: The revenue to be added.

Returns

None.

get_revenue

This function retrieves the current revenue of the environment.

Parameters

None.

Returns

The current revenue of the environment.

set_revenue

This function sets the revenue of the environment.

Parameters

  • revenue: The revenue to be set.

Returns

None.

get_state

This function retrieves the current state of the environment.

Parameters

None.

Returns

GenericState: The current state of the environment, which includes the block number and metadata for each token at that point in time.

set_state

This function sets the state of the environment.

Parameters

  • state: GenericState: The state to be set.

Returns

None.

get_swap_fee

This function retrieves the swap fee for a given token.

Parameters

  • token: The token for which to retrieve the swap fee.

Returns

The swap fee of the given token.

set_swap_fee

This function sets the swap fee for a given token.

Parameters

  • token: The token for which to set the swap fee.
  • swap_fee: The swap fee to set.

Returns

None.

get_dsl_metamodel

This function retrieves the DSL metamodel if it exists.

Parameters

None.

Returns

The DSL metamodel if it exists, else None.

get_folder_location

This function retrieves the folder location if it exists.

Parameters

None.

Returns

The folder location if it exists, else None.

mine

Mine a block. This updates the base fee per gas for the next block based on the defined gas strategy and then invokes the EVM mine() function.

Parameters

None.

Returns

None.

get_price_granularity

If the granularity is specified in the price_simulator settings block of the configuration.yaml, then this function will return the value of that granularity parameter. It will return None if that parameter is not present.

Parameters

None

Returns

Union[int, None] : a positive integer or None

Agent Wrapper

Serves as a wrapper for accessing particular functions and values in the Agent class without directly surfacing all functions and inner variables for the class itself.

add_variable

This function adds a custom variable to the agent instance.

Parameters

  • args: Variable length argument list.
  • kwargs: Arbitrary keyword arguments.

Returns

None.

get_variable

This function returns a previously set variable.

Parameters

  • args: Variable length argument list.
  • kwargs: Arbitrary keyword arguments.

Returns

Any: this can be anything that has been previously set.

set_variable

This function sets a custom variable that you have added before to a different value.

Parameters

  • args: Variable length argument list.
  • kwargs: Arbitrary keyword arguments.

Returns

None.

add_affected_token

This function adds an affected token. These affected tokens will have their internal states updated at the end of the agent step.

Parameters

  • token (string): The token symbol that has been affected.

Returns

None.

get_address

This function retrieves the agent's address.

Parameters

None.

Returns

string: The agent's address.

get_client

This function returns the EVM client that can be found in the environment that is attached to the agent.

Parameters

None.

Returns

ProjectClient: The EVM client that is used to perform EVM actions. If no client exists, returns None.

get_latest_action

This function retrieves the latest action the agent performed.

Parameters

None.

Returns

Union[str, None]: A string with a possible value. If no action has been performed, returns None.

set_latest_action

This function sets the latest action the agent performed.

Parameters

  • latest_action (string): The action that the agent performed.

Returns

None.

get_step_list

Retrieve the list of steps that the agent will take for this environment step. Note that this list will be cleared after each environment step. Further note that this list only affects the current agent and is not representative of the steps that will be taken by other agents for this environment step.

Parameters

None.

Returns

List[AgentStep]: The list of steps for the agent to take.

set_step_list

Given a list of AgentStep instances, set this as the list to be stepped over for the current agent's environment step. This should be set in the agent pre-step logic.

Parameters

step_list (List[AgentStep]): The list of steps to assign to the agent.

Returns

None.

get_wallet

This function retrieves the agent's wallet.

Parameters

None.

Returns

Wallet: The wallet, which contains information about the holdings of the agent.

get_settings

This function retrieves the agent's settings.

Parameters

None.

Returns

dict: The agent's settings.

Code Blocks

You can find a description for every code block below. Each code block can be provided in the code section of the configuration but do take into account that these sections can be overwritten by Agent Class functions and Agent functions.

environmentInitialization

Goal
The goal of the environment initialization is to call any functions that you would like to setup before running the simulation. This is not a requirement but there might some initialization steps that you would like to do before your simulation runs.

Function inputs
| input name | type | | ------------------ | ------------------------ | | a | EnvironmentWrapper | | metric_collector | MetricCollectorWrapper |

agentInitialization

Goal
Similar to the environmentInitialization you can initialize an agent here by calling some functions to get it to anitial state.

Function inputs
| input name | type | | ------------------ | ------------------------ | | a | AgentWrapper | | b | EnvironmentWrapper | | metric_collector | MetricCollectorWrapper |

environmentPreStep

Goal
Before performing a step for all the agents you are given the chance to perform functions before all the agents will be able to do something.

Function inputs
| input name | type | | ------------------ | ------------------------ | | a | EnvironmentWrapper | | metric_collector | MetricCollectorWrapper |

agentPreStep

Goal
The agent pre-step will be run for every agent separately and provides the agent class that will about to perform a step.

Function inputs
| input name | type | | ------------------ | ------------------------ | | a | AgentWrapper | | b | EnvironmentWrapper | | metric_collector | MetricCollectorWrapper |

agentFeatureVector

Goal

Function inputs
| input name | type | | ------------------ | ------------------------ | | agent | AgentWrapper | | step | AgentStep | | model_alias | str | | environment | EnvironmentWrapper | | metric_collector | MetricCollectorWrapper |

agentStep

Goal
Perform the agent step. This function is very important, you will have to write protocol specific logic to performing a certain step using the inputs that are provided.

Before the agentStep code executes, the simulator will call each model that is specified within the agents models setting. The prediction from each model is collected into a dictionary in which the key is the model alias and the value is the output of the model prediction. The model prediction dictionary can be accessed via step.get_model_output().

  • step is the AgentStep object that the agent is stepping on, including any custom attributes that have been defined by the user in the agent pre-step code.

Function inputs

input nametype
aAgentWrapper
b`token: Any
caction: float
d`quantity: Any
eEnvironmentWrapper
metric_collectorMetricCollectorWrapper

agentPostStep

Goal
After the step has been performed is a good moment to do some recalculations or metric reporting.

Function inputs
| input name | type | | ------------------ | ------------------------ | | a | AgentWrapper | | b | EnvironmentWrapper | | metric_collector | MetricCollectorWrapper |

environmentPostStep

Goal
Similar to the agentPostStep this is run after all the agents have finished performing steps. Which could be a good moment to perform an action or calculate and report metrics.

Function inputs
| input name | type | | ------------------ | ------------------------ | | a | EnvironmentWrapper | | metric_collector | MetricCollectorWrapper |

agentTeardown

Goal
After all steps have been performed the agent will be torn down. Before all data is erased you can do a final result or metric calculation and report those here.

Function inputs
| input name | type | | ------------------ | ------------------------ | | a | AgentWrapper | | b | EnvironmentWrapper | | metric_collector | MetricCollectorWrapper |

environmentTeardown

Goal
Similar to the agentTeardown before the environment is destroyed and all data deleted you can do a final metric calculation or action here.

Function inputs
| input name | type | | ------------------ | ------------------------ | | a | EnvironmentWrapper | | metric_collector | MetricCollectorWrapper |

reward

Goal
Using the environment class please calculate what the reward value of this specific simulation is. This can be as easy as calling environment.get_reward() and reporting that, but could be very elaborate based on the protocol that you're trying to simulate.

Function inputs
| input name | type | | ------------------ | ------------------------ | | environment | EnvironmentWrapper | | metric_collector | MetricCollectorWrapper |

environmentMisc

Goal
The Misc code block enables you to write functions for the specific class that can be called from the other functions. This makes the software writing process easier. This works great for making re-usable functions that you plan on using multiple times in your code from the environment class. It is made available directly in your code. So if you created a function def add_one(num: int): this can be called by calling add_one() directly.

agentMisc

Goal
The Misc code block enables you to write functions for the specific class that can be called from the other functions. This works exactly the same as the environmentMisc.

Example

code:
environmentInitialization: |
def environment_initialization(environment, metric_collector):
determine_swapping_fee(environment)

environment_initialization(a, metric_collector)
agentInitialization: "file://code/agentInitialization.py"
environmentPreStep: "file://code/environmentPreStep.py"
agentPreStep: "file://code/agentPreStep.py"
agentFeatureVector: "file://code/agentFeatureVector.py"
agentStep: "file://code/agentStep.py"
agentPostStep: "file://code/agentPostStep.py"
environmentPostStep: "file://code/environmentPostStep.py"
agentTeardown: "file://code/agentTeardown.py"
environmentTeardown: "file://code/environmentTeardown.py"
reward: "file://code/reward.py"
environmentMisc: "file://code/environmentMisc.py"
agentMisc: "file://code/agentMisc.py"

An example of a file:

import collections
from almanak.simulations.agent import AgentWrapper
from almanak.simulations.environment import EnvironmentWrapper
from almanak.simulations.metriccollector import MetricCollectorWrapper

def agent_initialization(agent: AgentWrapper, environment: EnvironmentWrapper, metric_collector: MetricCollectorWrapper):
agent.set_variable('withdrawal_request_ids_by_token', collections.defaultdict(list))

# pyright: reportUndefinedVariable=false
agent_initialization(a, b, metric_collector)

For the full example use the CLI with the generate command, this has all code files included.