Skip to main content
Version: 1.0.0

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.

All code can be passed in either as a string using the code variable or as a file using the respective file variable which has to be a relative path link to the Python file.

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 graned 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.

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 able to do something.

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

agentPreStep

Goal
The agent prestep will be run for every agent seperatly 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| |token|`| |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.

There are few variables here that are generated by the model that is assigned to the agent. These models return variables which are hardcoded for now.

  • token is a string for the token that you are supposed to perform a step on.
  • action is an action as a float, please refer to your model to what this is.
  • quantity is the quantity of the token that the action should be performed with.

Function inputs
|input name|type| |---|---| |a|AgentWrapper| |b|token: Any | None| |c|action: float| |d|quantity: Any | None| |e|EnvironmentWrapper| |metric_collector|MetricCollectorWrapper|

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:
code: |
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:

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.