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.
- Add this to
~/.pypirc
[distutils]
index-servers =
almanak-py
[almanak-py]
repository: https://europe-west4-python.pkg.dev/almanak-production/almanak-py/
- 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/
- 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.
- Agent
- Agent Class
- 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 thetoken
that theaction
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.