Moving Averages

Background

A moving averages trading strategy involves using moving averages (MA's) to identify market trends. A moving average smooths out price data by creating a constantly updated average price over a specific period. Common periods used are 10, 20, 50, or 200 days.

The two most common types are the Simple Moving Average (SMA), which calculates the average price over a given number of periods, and the Exponential Moving Average (EMA), which gives more weight to recent prices.

Moving averages help traders identify the overall direction of an asset's price movement and can act as support or resistance levels.

They are also used to generate trading signals. For example, a crossover of a short-term MA above a long-term MA (golden cross) signals a potential uptrend, while a crossover below (death cross) signals a potential downtrend.

Moving Average Strategy Screenshot of https://compasslabs.ai/dashboard?example=moving_averages

Moving Averages Strategy

Our trading strategy is based on the following principles:

  • Uptrend: If the short window crosses above the long window, this suggests a potential increase in the asset's price. Therefore, we convert asset y to asset x.
  • Downtrend: If the short window crosses below the long window, this suggests a potential downturn in the asset's price. Therefore, we convert asset x to asset y.
Moving Averages Simulation Rewards

How To Run

Installation

Follow our Getting Started guide to install the dojo library and other required tools.

Then clone the dojo_examples repository and go into the relevant directory.

Terminal
git clone https://github.com/CompassLabs/dojo_examples.git
cd dojo_examples/examples/moving_averages

Running

Download the dashboard to view the simulation results. To view example simulation data, download results.db from the repository and click 'Add A Simulation' on the dashboard.

To run the simulation yourself, use the following command.

Terminal
python run.py

This command will setup your local blockchain, contracts, accounts and agents. You can then access your results on your Dojo dashboard by connecting to a running simulation.


Step-By-Step Explanation

Initialization

The MovingAveragePolicy class inherits from the BasePolicy class. It is used to implement a moving average trading policy for a UniswapV3Environment with a single pool.

policy.py
class MovingAveragePolicy(BasePolicy):  # type: ignore
  """Moving average trading policy for a UniswapV3Env with a single pool.
 
  :param agent: The agent which is using this policy.
  :param short_window: The short window length for the moving average.
  :param long_window: The long window length for the moving average.
  """
 
  def __init__(
      self, agent: BaseAgent, pool: str, short_window: int, long_window: int
  ) -> None:
      super().__init__(agent=agent)
      self._short_window_len: int = short_window
      self._long_window_len: int = long_window
      self.long_window: deque[float] = deque(maxlen=long_window)
      self.short_window: deque[float] = deque(maxlen=short_window)
      self.pool: str = pool

Signal Calculation

Signals allow us to easily view data on our Dojo dashboard. In this example, we are adding 2 signals: LongShortDiff and Locked.

The LongShortDiff signal represents the difference between the mean of the short window and the mean of the long window. It indicates the relationship between short-term and long-term price trends.

The Locked signal indicates whether the strategy is ready to make trades. It is set to True when either the short or long window is not fully populated (meaning the strategy is not yet ready to make trading decisions). Once both windows are full, it is set to False.

We can then add bookmarks on the dashboard to view when a trade was made and at what LongShortDiff value.

Screenshot of https://compasslabs.ai/dashboard?example=moving_averages

Trade Execution

The predict method, an abstract method in BasePolicy, is implemented in MovingAveragePolicy. This method defines the conditions under which the agent should swap his assets.

When the mean of the short window is greater than the mean of the long window, this means the short window 'crosses above' the long window. This indicates a potential uptrend so we return a UniswapV3Trade object with the required parameters such as quantities=(Decimal(0), y_quantity), agent, and pool. This swaps y tokens of the agent to x tokens, essentially buying the x asset.

Similarly, when the mean of the short windows is less than the mean of the long window, this means the short window 'crosses below' the long window. This indicates a potential downtrend so we return a UniswapV3Trade object with the required parameters such as quantities=(x_quantity, Decimal(0)), agent, and pool. This swaps x tokens of the agent to y tokens, essentially buying the y asset and selling the x asset.

policy.py
if self._x_to_y_indicated(pool_tokens):
  y_quantity = self.agent.quantity(pool_tokens[1])
  self._clear_windows()
  return [
      UniswapV3Trade(
          agent=self.agent,
          pool=self.pool,
          quantities=(Decimal(0), y_quantity),
      )
  ]

To run our trading simulation, we need to create instances of agents, environments and policies so we run the run.py file.

Results

You can download the results to this example below.

We offer a dashboard desktop application for visualizing your simulation results. You can download the file for the desktop application here, or just open the results in our hosted dashboard.