Capitalize with Ocean Protocol: An SMA Algorithmic Trading Tutorial

Ocean Protocol Team
Ocean Protocol
Published in
8 min readApr 4, 2023

--

Why Trade Crypto?

While there are many reasons that people become interested in trading cryptocurrencies, the following are three main reasons that attract investors:

  1. portfolio diversification from traditional financial assets
  2. high volatility opportunities
  3. around-the-clock 🕚 24/7 trading activity.

Algorithmic trading can help you stay solvent in this non-stop financial market. Why? Because algorithms can trade at all times, even while you are sleeping! 😴 To start your algorithmic trading journey, we offer you a momentum strategy using the Simple Moving Average (SMA). We will also introduce Ocean Protocol, an open-source data sharing ecosystem of tools that can allow traders to sell the output price predictions of their algorithms!

Figure 1. Golden and Death Cross technical indicators are shown on a time series chart of BTCUSD prices.

What are Golden & Death Crosses?

What does SMA have to do with golden and death crosses in trading? What are they? A golden cross is a bullish indicator that happens when the SMA 50 crosses above the SMA 200. Why is that bullish? 🐂 Because the SMA 50 moves faster relative to recent price changes than the SMA 200, so this cross shows that the latest prices are moving upward. 📈 The opposite case, when the SMA 50 moves below the SMA 200, is a death cross and is a bearish indicator 🧸 that indicates a downward trend in the latest prices. 📉

Golden and Death crosses are shown in Fig 1.

The Simple Moving Average Explained Simply! 🧑‍🏫

So how is the SMA calculated? SMA is the average of a time series of prices in a window of a fixed length. The technique is called “moving” because the window slides in accordance with the time series. A time window of 200 days is typically used for computing the “historical trend” and a window of 50 days is used to compute the “current trend” in golden/death cross strategies. Of course, it is possible to form a golden or death cross interpretation on different SMA indicators, such as the SMA 5 versus the SMA 20 for higher frequency trading decisions for example. In this tutorial, however, we will stick with the traditional SMA 50 and 200 day indicators. We’ll show you how this works in the code below!

Let’s Trade!

Using the SMA 50 & 200 day indicators, we show you a quick algorithm to detect that glorious Golden cross buying signal 💰 and ominous Death cross selling signal 💀. Check it out below:


# Get 201 days of data
eth = get_ethereum_201_days_data()

# Use 200 days to compute SMAs from the day before
sma_day_before_200 = sum(eth[:200]) / len(eth[:200])
sma_day_before_50 = sum(eth[:50]) / len(eth[:50])

# Use 200 days to compute SMAs from the current day
sma_current_day_200 = sum(eth[1:]) / len(eth[1:])
sma_current_day_50 = sum(eth[1:]) / len(eth[1:])

# Detect Cross
day_before_diff = sma_day_before_50 - sma_day_before_200
current_day_diff = sma_current_day_50 - sma_current_day_200

# Golden Cross
if day_before_diff < 0 and current_day_diff > 0:
advice “buy”
# Death Cross
else if day_before_diff > 0 and current_day_diff < 0:
advice “sell”
# No Cross
else:
advice “do nothing”

On Maximizing Profit

Did you know that you can tune the window length for the SMA calculation to optimize your profits?

We show you how to do a grid search using different SMA time windows over the last 6 years of historical ETH data to find optimal buying and selling points. This way, we select the time window with the highest returns for the trading strategy.

The Grid Search Algorithm:

  1. Select multiple combinations of time windows to compute historical and current trends. The historical trend window should range from 10 to 210 days with a step of 10 days, while the current trend window should range from 5 to 50 days with a step of 5 days.
  2. Perform the trading strategy for each combination. Simulate transactions for a year, from January to December, at the beginning of which the trading return and investment are zero. Execute purchases and sells during the year based on the algorithm signals, and calculate the return on the last day of December for each year.
  3. Obtain a return value for each combination of time window. Analyze the results by averaging the returns per year and per window combination to determine which window size combination consistently yields the highest returns across years.

The colorful Figure 2 below shows all the different time windows we tried in our grid search for trading over 6 years. The brighter the color, the higher the percentage of total return. And guess what? We found out that the sweet spot for computing the current trend is between 10 and 20 days, while for historical trend it’s between 30 and 50 days. Time to put our findings to good use and capitalize on them!

Figure 2. Percentage return of trading with given different SMA window combinations.

Monetizing Your Asset with Ocean Protocol 🌊

Ocean Protocol is an ecosystem of open-source tools that share datasets and algorithms on the blockchain. 🐙🐬 In this tutorial, we’ll publish our algorithm as an NFT with metadata on the blockchain and a corresponding “datatoken” to gate access to the NFT.

Learn more about Ocean Protocol in the Ocean Academy.

Minting an algorithm “dataNFT”

It’s trivial to mint your trading strategy algorithm on the blockchain as a “dataNFT’’ using Ocean Protocol’s Python library, ocean-lib! At the same time we mint the NFT, ocean-lib will also create a datatoken that controls access to this NFT. We’ll be able to sell copies of this datatoken on the blockchain to immediately monetize our algorithm!

First, we pip install the Ocean Python Library.

$ python -m pip install ocean-lib

Type ‘python’ in your terminal to open up the Python console. Let’s code some Python!

$ python

Then, we’ll import all the libraries we need & setup our blockchain as Polygon’s Mumbai test network for this tutorial:

>>> from ocean_lib.web3_internal.utils import connect_to_network
>>> connect_to_network("polygon-test") # mumbai is "polygon-test"
>>>
>>> from brownie.network import accounts
>>>
>>> from ocean_lib.example_config import get_config_dict
>>> from ocean_lib.ocean.ocean import Ocean
>>>
>>> from datetime import datetime, timedelta
>>> from ocean_lib.models.compute_input import ComputeInput
>>> import pickle
>>> import os

Next, you can choose whether to connect your wallet to your system’s environmental variables so that you can use it to pay for the gas fees. Or, you can use Bob to act as the wallet’s owner, as shown in the below code. Note that if you use Bob, then you will have to give him fake Matic to pay for gas. You can put his address in at https://faucet.polygon.technology/

>>> config = get_config_dict("polygon-test")
>>> ocean = Ocean(config)
>>> accounts.clear()
>>> bob_private_key = os.getenv('REMOTE_TEST_PRIVATE_KEY2')
>>> bob = accounts.add(bob_private_key)

Upload your SMA cross strategy algorithm to an open repository (for a full SMA algorithm example, please see the Addendum). The public URL to this file will be encrypted by Ocean so that purchasers of the NFT only get the output of the algorithm instead of the algorithm itself.

To publish the algorithm NFT + Metadata + datatoken using the algorithm’s URL, do the following:

>>> ALGO_url = "YOUR_PUBLIC_URL_HERE"
>>> name = "golden_death_crosses_recommender"
>>> (ALGO_data_nft, ALGO_datatoken, ALGO_ddo) = ocean.assets.create_algo_asset(
name,
ALGO_url,
{"from": bob},
image="pandas/pandas",
tag="pip-all",
checksum="sha256:f85d01f3455431325b7b5b7b18ff26d88c0ca9f9f45383b9147fe596e90917ce",
wait_for_aqua=True
)

>>> print(f"ALGO_ddo did = '{ALGO_ddo.did}'")

Once your algorithm is published as an asset on the blockchain, we’ll create a compute service to run your algorithm on a dataset. At the end, we will only sell the output of your algorithm, rather than the algorithm asset itself.

For this tutorial, you can use the Data asset referenced in this link which is free to use.

Side note: This data asset provides 1000 hours of ETH price, meaning that the algorithm is not going to be able to use windows greater than 42 days.

To add a compute service to the data asset:

>>> ALGO_did = "ALGORITHM_DID_HERE"
>>> DATA_did = "DATA_DID_HERE"
>>> DATA_ddo = ocean.assets.resolve(DATA_did)
>>> ALGO_ddo = ocean.assets.resolve(ALGO_did)

>>> compute_service = DATA_ddo.services[1]
>>> compute_service.add_publisher_trusted_algorithm(ALGO_ddo)
>>> DATA_ddo = ocean.assets.update(DATA_ddo, {"from": bob})

Whichever data asset you use, make sure to have permissions for the Data asset to attach a compute service to it. At Ocean Protocol, we call this configuration feature “Compute-to-Data”.

Consumers of your asset will be able to use your algorithm to generate recommendations on whether to buy or sell ETH.

Let’s begin by using the asset to pay for the service.

>>> algo_service = ALGO_ddo.services[0]
>>> free_c2d_env = ocean.compute.get_free_c2d_environment(compute_service.service_endpoint)

>>> ALGO_compute_input = ComputeInput(ALGO_ddo, algo_service)
>>> DATA_compute_input = ComputeInput(DATA_ddo, compute_service)

>>> # Pay for dataset and algo for 1 day
>>> datasets, algorithm = ocean.assets.pay_for_compute_service(
datasets=[DATA_compute_input],
algorithm_data=ALGO_compute_input,
consume_market_order_fee_address=bob.address,
tx_dict={"from": bob},
compute_environment=free_c2d_env["id"],
valid_until=int((datetime.utcnow() + timedelta(days=1)).timestamp()),
consumer_address=free_c2d_env["consumerAddress"],
)
>>> assert algorithm, "pay for algorithm unsuccessful"

Once the transaction has been made, it’s possible to start the compute job and get the recommendations from our profit optimized algorithm.

>>> # Start compute job
>>> job_id = ocean.compute.start(
consumer_wallet=bob,
dataset=datasets[0],
compute_environment=free_c2d_env["id"],
algorithm=algorithm,
)
>>> print(f"Started compute job with id: {job_id}")

Drumroll, please!

It’s the moment you’ve been waiting for: let’s retrieve the algorithm output to see if we should sell or buy ETH tokens.

>>> # Retrieve algorithm output and log files
>>> output = ocean.compute.compute_job_result_logs(
DATA_ddo, compute_service, job_id, bob
)[0]

>>> suggestion = pickle.loads(output)
>>> assert len(suggestion ) > 0, "unpickle result unsuccessful"
>>> print(suggestion) # “buy!”

Ta da!

You are done creating a valuable SMA trading algorithm & data feed that you can immediately monetize on the blockchain.

If you have any questions, concerns, or feedback, then we want to hear from you! Connect with us on Discord, you are only 1 message away from reaching Ocean’s core tech team and we love to solve user problems!

Join our server here: https://discord.gg/TnXjkR5

Appendix: Algorithm’s Full Code

import json
import os
import pickle
import sys

import pandas as pd

from datetime import datetime

def get_input(local=False):
if local:
print("Reading local file.")

return "file0"

dids = os.getenv("DIDS", None)

if not dids:
print("No DIDs found in environment. Aborting.")
return

dids = json.loads(dids)

for did in dids:
filename = f"/data/inputs/{did}/0" # 0 for metadata service
print(f"Reading asset file {filename}.")

return filename


def run_algorithm(local=False):
hsma = 40
lsma = 12

filename = get_input(local)
if not filename:
print("Could not retrieve filename.")
return

with open(filename) as datafile:
data = datafile.readlines(0)[0]
data = json.loads(data)

uts = [int(xi[0]) / 1000 for xi in data]

# Data is per hour so reformat to per day
ts_obj = [datetime.utcfromtimestamp(s) for s in uts]
eth = [float(xi[4]) for xi in data]
dataset = pd.DataFrame({"y": eth}, index=ts_obj)
resampled = dataset.resample("1D").last()

assert len(resampled) >= hsma, f"Not enough data for windows {len(resampled)} vs {hsma}"

eth = resampled["y"]

# Compute SMAs from the past sample
hsma_day_before = sum(eth[-hsma - 1:-1]) / len(eth[-hsma - 1:-1])
lsma_day_before = sum(eth[-lsma - 1:-1]) / len(eth[-lsma - 1:-1])

# Compute SMAs from the current sample
hsma_current_day = sum(eth[-hsma:]) / len(eth[-hsma:])
lsma_current_day = sum(eth[-lsma:]) / len(eth[-lsma:])

# Detect Cross
day_before_diff = lsma_day_before - hsma_day_before
current_day_diff = lsma_current_day - hsma_current_day

# Golden Cross
if day_before_diff < 0 and current_day_diff > 0:
advice = "buy"
# Death Cross
elif day_before_diff > 0 and current_day_diff < 0:
advice = "sell"
# No Cross
else:
advice = "do nothing"


filename = "advice.pkl" if local else "/data/outputs/result"
with open(filename, "+wb") as pickle_file:
print(f"Pickling results in {filename}")
pickle.dump(advice, pickle_file)


if __name__ == "__main__":
local = len(sys.argv) == 2 and sys.argv[1] == "local"
run_algorithm(local)

About Ocean Protocol

Ocean was founded to level the playing field for AI and data. Ocean tools enable people to privately & securely publish, exchange, and consume data.

Follow Ocean on Twitter or Telegram to keep up to date. Chat directly with the Ocean community on Discord. Or, track Ocean progress directly on GitHub.

--

--