Arbitraging the risk premium of the Euro € stablecoin

Daryl
5 min readMar 8, 2024

--

Photo by JustStartInvesting on Unsplash

What are stablecoins

The invention of the stablecoin is probably one of the more helpful inventions to hit digital assets.

As digital tokens which are 1:1 redeemable for their real equivalents, their benefits are 2 fold.

  1. They allow us to lock in gains and rest easy in the knowledge they wont be lost.
  2. They enable us to do so without the long waiting time associated with KYC exchanges that pay out fiat in return for Crypto

While numerous efforts to encourage widespread stablecoin usage is underway, there are currently 2 currencies on stablecoins in fairly wide circulation.

  • The US Dollar through stablecoins USDC and USDT
  • The Euro through stablecoin EURC

The Euro Stablecoin

If you were a crypto trader in the European Union, you probably have your savings and earnings in Euro.

The EURC provides a convenient means to exit your trades and calculate your profits without the additional inconvenience of converting via USD/EUR and dealing with the short term FX risk of the value of the Euro depreciating against the US Dollar.

What is a risk premium

In the early days of Bitcoin, a common risk premium was called the Kimchi Premium.

Bitcoin traded at significantly higher prices on exchanges in South Korea, and to a lesser extent, Japan compared to elsewhere. This was mostly attributed to regulations that made Bitcoin difficult and inconvenient to buy. To be compensated for this inconvenience and uncertainty, Crypto exchanges charged a higher fee to sell Bitcoin.

In a similar vein, the risk premium exists for EURC today. In comparison to the US Dollar, the Euro’s level of usage worldwide is significantly lower.

It would be harder for Circle, the company offering both USDC and EURC, to offload their stores of EURC tokens to obtain Euro fiat, compared to offloading their USDC tokens for USD fiat.

As a result of this, EURC often trades at a premium to the Euro.

A comparison of fiat vs stablecoin rates for Feb 2024

Good opportunity for an Arbitrage Play

In theory, the risk premium exists by default. EURC trades at a premium far more frequently than it trades at a discount.

Enabling us to arbitrage any time we want. RIGHT?

Wrong.

The premium tends to hover in the neighbourhood of 1%. Such thin margin tends to render the trade ineffective, due to transaction fees.

However, as the exchange rate chart show, the premium sometimes can reach 3x or even 4x of their usual rates.

How to arbitrage

An Arbitrage strategy here would involve holding a certain amount of EURC, USDC tokens and fiat US Dollars, Euro at any one time and simulatneously executing 2 orders:

  • The Long trade would involve buying Euro fiat in the FX Market.
  • The Short trade would involve selling EURC tokens on supported blockchains.

The following section contains the code used to harness the necessary information to inform the arbitrage trade .

Obtaining spot FX Exchange rates

Reliable FX data can be obtained via the API Polygon.io.

A multitude of tutorials exist on youtube already, so we shall skip this portion.

Obtaining stablecoin exchange rates

A fair amount of stablecoin trading occurs on decentralised exchanges located on various blockchains.

Luckily, with the help of APIs like Flipsde, we are able to extract blockchain data very quickly with a couple of quick SQL queries.

Flipside contains and SQL sandbox enabling us to test our syntaxs.

We can see transaction data on exchanges like Orca and Jupiter.

Snowflake SQL queries ran on Flipside

We can pluck this SQL query into a Python script and collate this information into a Pandas dataframe.

from flipside import Flipside
import os
import pandas as pd
from dotenv import load_dotenv

load_dotenv()
apikey = os.getenv('FLIPSIDE-API-KEY')
flipside = Flipside(apikey, "https://api-v2.flipsidecrypto.xyz")


sql = """
select
symbol_in,
amount_in,
symbol_out,
amount_out,
block_timestamp
from
crosschain.defi.ez_dex_swaps
where
BLOCK_TIMESTAMP BETWEEN '2024-02-01' AND'2024-03-01'
and SYMBOL_IN in ('usdc')
and SYMBOL_OUT in ('eurc')
"""

"""Run the query against Flipside's query engine and await the results"""
query_result_set = flipside.query(sql)

"""This function will be added to Flipside package after testing, just copy/paste as needed for now"""
def auto_paginate_result(query_result_set, page_size=10000):
"""
This function auto-paginates a query result to get all the data. It assumes 10,000 rows per page.
In case of an error, reduce the page size. Uses numpy.
"""
num_rows = query_result_set.page.totalRows
page_count = np.ceil(num_rows / page_size).astype(int)
all_rows = []
current_page = 1
while current_page <= page_count:
results = flipside.get_query_results(
query_result_set.query_id,
page_number=current_page,
page_size=page_size
)

if results.records:
all_rows.extend(results.records) # Use extend() to add list elements

current_page += 1 # Increment the current page number

return all_rows # Return all_rows in JSON format

""" Get your data as a pandas data frame"""

file_name ='usdc_eurc.csv'

sol_data = auto_paginate_result(query_result_set)
sol_data = pd.DataFrame(sol_data)
sol_data.to_csv(file_name, index=False)

Upon obtaining our 2 sources of data and placing them into dataframes, we can put them side by side on the same graph identify the frequency of arbitrage opportunities.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import plotly.graph_objects as go
from datetime import datetime
from plotly.subplots import make_subplots

data = pd.read_csv('eurc_usdc.csv')
data=data.drop("__row_index", axis=1)
data.insert(4,
"eurc/usdc rate",
data['amount_in'] / data['amount_out'])

def timestamp_clean(x: str):
x=x.replace("T", " ").rstrip(".000Z")
x=x + "0" if x[-2] == ":" else x
x=x + "00" if x[-1] == ":" else x
return datetime.strptime(x, '%Y-%m-%d %H:%M:%S')
pass

data["block_timestamp"] = data["block_timestamp"].apply(timestamp_clean)
data = data.sort_values(by="block_timestamp").drop_duplicates()


data_fiat = pd.read_csv("price_data/polygon/EURUSD/ticker.csv")
data_fiat["time"] = data_fiat["time"].apply(timestamp_clean)

plt.title("Euro/USD: Fiat vs Stablecoin on Solana", fontsize=15)
plt.xticks(rotation=45)
plt.xlabel("Date")
plt.ylabel("Exchange Rate", fontsize="15")
plt.plot(data["block_timestamp"], data["eurc/usdc rate"], label="EURC/USDC (stablecoin)")
plt.plot(data_fiat["time"], data_fiat["close"], label='EUR/USD (fiat)')
plt.grid(True)
plt.legend()
plt.show()

And out churns the plot that was seen earlier.

--

--

Daryl

Graduated with a Physics degree, I write about physics, coding and quantitative finance.