Build your own trading index with Python

Custom indices offer flexibility and control over the composition and weighting of assets, providing your own unique benchmark that can better reflect your investment strategies compared to traditional indices. This article will guide you through building custom indices using Python.

Why Custom Indices?

  • Flexibility: Tailor indices to match specific investment or trading strategies.
  • Control: Customize asset inclusion and weighting criteria.
  • Innovation: Develop unique benchmarks that may offer a competitive edge in both investing and trading.

Why we will calculate US Dollar Index

While you can practically create a custom index for everything that you can find historical prices for, this article will create an index that is already out there. The US Dollar Index.

I chose USD since this way we can compare our custom result to the actual index to test it.

Second and most important I chose a currency because, when trading forex pairs you see how one currency is compared to the other. So practically when a pair goes long, for example, EURUSD, you cannot be sure if EUR isolated is getting stronger or USD is getting weaker. This is an answer that can be given only with a currency index.

Step-by-Step Guide to Creating a Custom Index

Begin by importing necessary libraries such as pandasnumpy, and yfinance for data handling and financial data fetching.

import pandas as pd
import json
import yfinance as yf
import mplfinance as mpf




Then load a dictionary of the pairs, and one with the currencies that I am interested in (link at the end of the article).

with open('currencies_weights.json') as f:
    currencies = json.load(f)

with open('currencies_pairs.json') as f:
    pairs = json.load(f)




The currencies should have most important the symbol (e.g. EUR) and the GDP of the country in billion dollars. I have selected the GDP since it seemed the most realistic measure (proven correct in the end…). You just have to be careful to have it expressed in one currency. For example, the Eurozone GDP should be expressed in USD and not EUR. I found that this is the easiest way, instead of having the GDP in local currency and then having to convert it.

Using the below for loop you will download the 1-hour prices for all the pairs and add them to the dictionary with the help of our beloved finance.

for pair in pairs:
    pairs[pair]['df'] = yf.download(pairs[pair]['name'] + '=X', start='2023-01-01',end='2023-12-31', interval='1h')




You have to select the smallest timeframe you can find data, in order to be able to create the candles in the largest timeframes like 4h or daily. Then we align the dataframes to have the same period in case Yahoo Finance does not offer some days. You can skip this part if you have an API that you feel confident will give you full proper data.

# find min and max
for pair in pairs:
    mindate = min(pairs[pair]['df'].index)
    maxdate = max(pairs[pair]['df'].index)
# exclude not common dates
for pair in pairs:
    pairs[pair]['df'] = pairs[pair]['df'].loc[mindate:maxdate]
print(mindate, maxdate)




Now you should configure for which currency you are interested in calculating the index and create a dataframe with the closing prices for all the pairs containing this currency. Invert, of course, the rate in case the currency is on the sell side, so you bring the currency for all on the buy side.

currency = 'USD'

mydict = {}
# filter the pairs that contain the currency
for pair in pairs:
    if currency in pairs[pair]['buycur']:
        print("buy", pair)
        mydict[pairs[pair]['sellcur']] = pairs[pair]['df']['Close']
    elif currency in pairs[pair]['sellcur']:
        print("sell", pair)
        mydict[pairs[pair]['buycur']] = 1 / pairs[pair]['df']['Close']
    
df_currency = pd.DataFrame(mydict)




For each column (currency) of the above dataframe, we create a list of the weights using the GDP.

weights = []
for col in df_currency.columns:
    weights.append(currencies[col]['GDP'])

weights = [weight / sum(weights) for weight in weights]




Now the magic happens by creating the weighted closing price and resampling it to the dataframe of your choice (should be higher than the original one)

# prepare the dataframe
df_index = df_currency.copy()
df_index = df_index.pct_change(fill_method=None)
df_index.dropna(inplace=True)

# calculate the wighted closing price
weights_df = pd.DataFrame(weights, index=df_index.columns, columns=['Weight'])
df_index[currency] = df_index.dot(weights_df['Weight']).cumsum(axis=0)*1000
df_index[currency] = df_index[currency].round(6)

# resample the index to daily 
df_index = df_index[currency].dropna()
ohlc = df_index.resample('D').ohlc()
ohlc.dropna(inplace=True)




That’s it. The ohlc dataframe contains the candles for the daily US Dollar index.

In order to check the process, I will download and compare the actual US Dollar index to see how close we are.

usd_dollar_index = yf.download('DX-Y.NYB', start='2023-01-01',end='2023-12-31', interval='1d')




The first check will be visual. So I will plot both our custom and the official one on top of each other. Looks good, doesn’t it?

mpf.plot(ohlc,type='candle',figratio=(30,10))
mpf.plot(usd_dollar_index,type='candle',figratio=(30,10))




Custom USD index and the official one

One more way is to check the correlation of the closing prices.

# first we should make both datetime indices timezone-naive before calculating the correlation
ohlc.index = ohlc.index.tz_localize(None)
usd_dollar_index.index = usd_dollar_index.index.tz_localize(None)

combined_df = pd.DataFrame({
    'ohlc': ohlc['close'],
    'usd_dollar_index': usd_dollar_index['Close']
})

correlation = combined_df.corr().iloc[0, 1]




This will give us an impressive 0.976 correlation which makes it clear that those 2 indexes are aligned…

There is though this slight difference, because the US Dollar index is calculated on fixed weights on 6 currencies, while in our case we had 14!

You can find the code and the json files for the pairs and the currencies at Git Hub by pressing here.