4 Metrics for Algorithmic Traders, Week 2
As a part of our Open Crypto Data Initiative, we aim to provide traders and web3 enthusiasts with the tools to understand cryptocurrency markets and derive key insights into their mechanics. However, this data is limited by the knowledge of the traders using it, and so we’re providing a series of useful metris and indicators that algorithmic and high-frequency traders can add to their arsenal when developing a trading strategy.
5. Gini Coefficient
The Gini Coefficient is a economic indicator commonly used to measure the degree of inequality in a society. It measures how much the wealth distribution in a given context deviates from a theoretical context where wealth is equally distributed.
The value of the Gini Coefficient takes values between 0 and 1:
- A Gini Coefficient of 0 represents perfect equality, where every individual owns an equivalent amount of wealth.
- A Gini Coefficient of 1 represents perfect inequality, where one individual owns all wealth.
In the context of Web 3.0 and blockchains, each wallet is analogous to an individual who owns wealth, and the amount of cryptocurrency or coin in each wallet represents the wealth owned by the individual. By looking at the Gini Coefficient for particular cryptocurrencies and tokens such as BTC, ETH, and SOL, we can compare the wealth inequality across different trading pairs, blockchains, and decentralised environments.
As with any econometric indicator, analysts, academics, and business intelligence agents can calculate the Gini Coefficient across many different blockchains and use them to compare wealth inequality in the decentralised space. Investors and portfolio managers can also look at the Gini coefficient as a way to evaluate different assets to trade or include in their investments.
For instance, in Sai, Buckley and Le Gear (2021) the authors calculated the Gini Coefficient for Bitcoin-like currencies such as Bitcoin Cash, Dogecoin, Litecoin, and Ethereum. They found that the Gini Coefficient was high but gradually decreasing, indicating high levels of wealth inequality which they attribute to low levels of adoption. They concluded that as more and more people adopt Bitcoin and cryptocurrencies, the wealth inequality in the decentralised space will continue to decrease.
Formula and Calculation
The Gini Coefficient is calculated by taking the ratio between the area beneath the Lorenz Curve between the area below the Line of Inequality.
In a plot of percentage of owned wealth against the percentage of total population, the Line of Equality is a straight line from (0, 0) to (100, 100). This means that factoring 1% more of the population into the total wealth considered increases percentage wealth by 1% as well. On the other hand, the Lorenz Curve is the actual increase in wealth per percentage population considered.
The figure above shows the Lorenz Curve of Ireland plotted with the Line of Inequality. We can see that the final 10% of the population owns 50% of total wealth.
By normalising and rescaling the x and y axes to be values between 0 and 1, the formula to calculate the Gini Coefficient is
where A is the area under the Line of Equality minus B, the area under the Lorenz Curve. Note that A + B = 1.
To calculate the Gini Coefficient at a given point in time for a cryptocurrency, we can gather the total number of wallets which own that cryptocurrency and how much of the cryptocurrency they own. We can then sort the wallets by increasing wealth owned, then use the sorted wallets to calculate the Lorenz Curve.
We can also filter inactive wallets by applying certain criteria, such as only including wallets which own more than a certain amount of wealth and wallets which have had more than a certain number of transactions in a past period of time.
Once find the area under the lines, we can easily use the formula above to calculate the Gini Coefficient.
data = [29, 40, 55, 67, 89, 44, 21, 30, 5, 6, 0, 0, 0, 0, 0, 1]def gini_coefficient(balances, threshold=0):
Returns the Gini Coefficient for a given array of wallets and their coin balances.
balances: 1D Array where each entry is the balance of an individual wallet for a specific coin.
threshold: Minimum amount of coin held by a wallet for the function to include it in the Gini Coefficient calculation.
balances = pd.Series(balances) # Filtering array so only holders with more than .
balances = balances[balances >= threshold] balances = balances.sort_values()
total = balances.sum()
balances = balances / total # Normalising cum_wealth = balances.cumsum()
wealth_lag = cum_wealth.shift(1)
wealth_lag = 0 diff = cum_wealth - wealth_lag
x_axis_tick = 1/len(balances)
triangle_areas = 0.5 * diff * x_axis_tick
rect_areas = x_axis_tick * wealth_lag
B = triangle_areas.sum() + rect_areas.sum()
A = 0.5 - B return A/(A+B)gini_coefficient(data)>>> 0.6059431524547804
An economist wants to write an article comparing the wealth structure for two coins ABC and CAB with the goal of describing the “inequality” in the coin’s ecosystem. They calculate the Gini Coefficients for ABC and CAB, and find that they are 0.2 and 0.5 respectively. Since coin CAB has a higher Gini Coefficient than ABC, it shows that the wealth distribution among wallets which are holding ABC deviates further from the line of perfect equality than coin CAB.
The economist can then infer that a smaller number of wallets hold a greater percentage of the total supply of CAB, and that wealth is more evenly distributed among ABC holders than CAB.
Sai, Buckley, Le Gear (2021) https://doi.org/10.3389/fbloc.2021.730122
6. Fisher Transform
The Fisher Transform was originally developed to solve difficulties in estimating confidence intervals and tests of significance by transforming skewed data into an approximate normal distribution. In finance, the transformation is generally applied to the price of an asset to yield a technical indicator which shows turning points in prices clearer.
In essence, it normalises the movement of asset prices so that statistically rare prices are more clearly shown. If the Fisher Transform of a price deviates significantly from its usual value based on a lookback period, traders can use it as a leading signal which indicates a potential price reversal.
The value of the Fisher Transformation is unbounded, so traders have to empirically study common values which the transformation can take. What is considered a “high” Fisher Transformation value varies between assets. For instance, the high value for asset A can be 7, while any value above 3 can indicate a rare event for asset B.
Many traders use the Fisher Transform as a leading signal to inform their decisions. They interpret an extreme reading as predictive of a possible reversal, and if the direction of the indicator changes it could signal that the price is going to drop.
The Fisher Transform provides a large volume of trade signals — many of which are not profitable to follow. Traders often use the Fisher Transform along with other indicators and trend analysis.
Formula and Calculations
The formula for the Fisher Transformation is
where X is the transformation of price to a level between -1 and 1.
The Fisher Transform is often calculated for a given lookback period such as two weeks, then recalculated as each period ends. Price values will have to be renormalised as the highest and lowest prices change as periods come and go.
Alice is looking for a signal to help her more clearly understand when a rare event has occurred in the universe of asset A. She calculates the Fisher Transform with a nine-day lookback period, and observes that a common high value for the indicator is 7. One day, the Fisher Transform reaches a value of 9, then starts reversing direction. She determines that this is good evidence that a price reversal is coming, and exits a portion of her position in asset A.
import numpy as npdef fisher_transform(prices):
Calculates the Fisher Transform for a security based
on a sequence of its price.
prices: 1D array of prices at each point in time.
shifted = prices - np.mean(prices)
normalised = shifted / np.amax(shifted)
fisher_values = 0.5 * np.log((1+normalised)/(1-normalised))
7. Sortino Ratio
The Sortino Ratio is a modified version of the Sharpe ratio that allows a trader to specify a target or required return rate. It differs from the Sharpe ratio in the sense that it only penalises returns that fall below this target, while the Share ratio penalises volatility without caring about the direction necessarily. As such, while both the Sharpe ratio and Sortino ratio act as measures of the risk-adjusted returns of an investment, they do so in different ways and so can have different conclusions as to the actual value an investment has in generating returns.
The Sortino ratio is calculated as:
Where R is the average realised return of the asset, T is the required return rate or target (typically the risk-free rate), and DR is the target semi-deviation, which is called downside deviation. The calculation for DR returns it as a percentage, and so can be used for rankings in a similar method to how standard deviations allow for rankings. Downside deviation is essentially the standard deviation of a portfolio’s performance, but only for the returns that fall below a target threshold. Practically, it measures the volatility of undesirable returns. The target threshold can be a variety of things, but it is typically either 0 or the risk-free rate.
Calculating the downside deviation is relatively simple when looking at discrete values, i.e annual returns:
- Choose your value for T. In this example we will use 2%
- Subtract T from your returns
3. Filter out the list of subtracted returns by negative values
4. Compute the squares of this list, sum them, divide by the total number of observations, and take the square root of that result
Now that we have this, we can calculate the Sortino ratio of our portfolio relatively easily:
As mentioned above, the Sortino ratio provides an indication of the risk-accounted returns of an investment, similar to the Sharpe ratio. However, while the Sharpe ratio penalises all volatility, the Sortino ratio only penalises negative volatility via downside deviation. This is more sensical for investors as upside volatility tends to actually be a good indicator for most portfolios. Like with the Sharpe ratio, the higher the Sortino ratio, the better the portfolio. The ratio itself can be treated as the return per unit of bad risk that the portfolio takes on.
The implementation for the Sortino ratio is simple using the Pandas library, which allows for filtering and the calculation of the standard deviation to be done fairly easily
import pandas as pd
from math import sqrtdef sortino_ratio(series, rf):
Calculation for the Sortino ratio
series -- the portfolio data to be analysed
rf -- the risk free rate
mean = series.mean() - rf
# For this example we will use a target of 0
neg = series[series < 0]
squares = neg.map(lambda x: x**2)
DR = sqrt(squares.sum()/series.size)
return mean/DRseries = pd.Series([3, 32, 5, 18, -4, -6, -3, 28])
print(sortino_ratio(series, 0.02))> 3.297311870868525
8. Treynor Ratio
The Treynor Ratio, like the Sortino and Sharpe ratio, is a measure of a portfolio’s performance compared to a risk-free rate, per unit of market risk assumed. It is given by:
Where r_i is portfolio i’s return, r_f is the risk free rate, and beta_i is portfolio i’s Beta. The Beta coefficient is a measure of the portfolio in comparison to the market as a whole. Typically, one would use an index like the S&P 500 in traditional finance, but any reasonable indicator of a market’s performance will do. Understanding a portfolio’s beta warrants its own article, but in short, Beta values can be interpreted as follows:
- Beta = 1: Price activity has a strong correlation with the market. Including this asset in your portfolio won’t noticeably add any risk, but low volatility means a lower chance of solid returns
- Beta < 1: The asset tends to be less volatile than the market. Including this stock in a portfolio will make the overall portfolio less risky.
- Beta > 1: The asset tends to be more volatile than the market. In cryptocurrency, You’ll see a lot of this if you use an index like the S&P 500 to compare with your altcoin of choice
- Beta < 0: It’s possible for an asset to have a negative beta. For example, a beta of -1 indicates that the asset is inversely correlated to the market. This is common in put options, whose derivative markets are designed to be inversely correlated with the trends in the current market, and so ideally would have betas close to -1
It’s important to note that for the Treynor Ratio, an asset with a negative Beta makes the result meaningless. A negative Beta would make the ratio itself negative, which would imply that assets such as put options would have a positive ratio when r_i < r_f, and a negative ratio when r_i > r_f, which is obviously absurd. Like with other ratios that measure the success of a portfolio relative to assumed risk, the higher the end result, the better.
The Treynor ratio has the same value to an investor as the Sharpe ratio and the Sortino ratio — they’re all measures of how well a portfolio performs per unit of risk taken. They only differ in how exactly that risk is calculated. The Treynor ratio operates under the premise that investors ought to consider the inherent risk the portfolio carries by comparing it to the market as a whole. For example, if a standard market index showed a return of 8% over a given period and your portfolio showed a return of 15% over the same period, the Treynor ratio would account for the only 7% difference in returns. In other words, the Treynor ratio is used to determine whether your portfolio is noticeably outperforming the market. If the market gained 20% in the past year but your portfolio only gained 17%, a Sharpe ratio may provide the picture that your portfolio was actually a great and efficient, but a Treynor ratio would highlight that it would have actually been a better investment to just throw your money into a market index.
import numpy as npdef treynor_ratio(market_returns, port_returns, rf):
Calculates the Treynor ratio for a given portfolio performance, given market performance, and a risk-free rate Parameters
market_returns : numpy.ndarray
A 1D numpy array of market returns
port_returns : numpy.ndarray
A 1D numpy array of portfolio returns
rf : float
The risk-free rate
""" mean_port_returns = np.mean(port_returns)
covariance = np.cov(port_returns, market_returns)
variance = np.var(market_returns)
beta = covariance / variance
return (mean_port_returns - rf) / beta market_returns = np.array(sorted([5,-2,4,7,4,3,1,-1]))
comp_returns = np.array(sorted([2,6,3,4,1,0,-1,-2]))>>> print("Treynor ratio:", treynor_ratio(market_returns, comp_returns, 0.02))
Treynor ratio: 1.6557172818791945