<jupyter_start><jupyter_text>The Next Potential Defi Token To The Moon  Author: <NAME>  README(1726 words, 6m54s reading time) What is Defi?[Decentralized Finance](https://en.wikipedia.org/wiki/Decentralized_finance), namely Defi, is new emerging terminology for a variety of financial applications or services based on the blockchain platform. The goal of Defi is to establish a financial system that opens to every user. To fulfill this goal, Defi leads to the bull market in recent months. Most existing Defi services can be divided into five categories: Lending, DEXes, Derivatives, Payments, and Yield farming.  * **Lending** is similar to the bank. Users can borrow money from this project or deposit the money here to earn interest. However, unlike the bank, this kind of lending project does not need to monitor the users by a human. All rules are executed by the smart contract.* **DEXes** is short for Decentralized Exchanges. It's a kind of crypto exchange that allows everyone to buy or sell all kinds of tokens.* **Derivatives** is a promising direction of Defi as it provides many derivative tools to professional investors to hedge the risk.* **Payments** here may refer to the Bridge across different blockchains. For example, users from Ethereum could transfer their assets to Binance Smart Chain (BSC) or Huobi Ecosystem (Heco), or vice versa.* **Yield** is short for yield farming, which refers to a kind of project that provides high yield products to investors. For example, the project admin could design a smart contract to help investors auto-compound the stacked tokens in DEXes and then earn the performance fee. How do we find out the next potential Defi token to the moon?Many popular Defi projects offer tokens. Investors can buy the token for investment purposes. Indeed, some famous Defi tokens have a great increase in this bull market. Many investors may want to find out the next potential Defi token that will launch to the moon to maximize their profit. However, it is a challenging problem that nobody can guarantee this kind of token.  One conventional approach is to forecast the price based on time-series data. In this article, BTC is selected as an example because it is the most representative crypto-currency. I use the [Bitcoin data](https://www.kaggle.com/mczielinski/bitcoin-historical-data?select=bitstampUSD_1-min_data_2012-01-01_to_2021-03-31.csv) at 1-min intervals from Jan. 2012 to Mar. 2021. In the experiment, I first select the weighted price from Jan. 2020 to Mar. 2021 and then split the train/test data at Jan. 2021. In other words, the time-series data after Jan. 2021 is selected as the test set whereas the rest data is used for training. After data normalization, I employ the LSTM model to forecast the BTC price. The detailed experimental setting can be found on the [script](https://github.com/junzhuang-code/potential_defi_tokens/blob/main/defi_analysis.ipynb). The predicted price shows apparently lower than the actual price. One potential reason is that the data distribution is changed on the test data (the BTC price increases rapidly in the bull market) and thus the model fails to forecast the price. That is to say, the forecasting may get worse when the price changes abruptly. Besides this shortcoming, the performance of time series forecasting highly depends on the size of the dataset.  A simple solution is possible if the goal is changed to find out the potential tokens instead of the price prediction. In this article, I propose a novel scheme to find out such tokens by using timestamp data. One benefit of using timestamp data is that the model only requires a screenshot of time-series data as input. In the upcoming section, I will introduce my proposed scheme in detail.  **Data Collection**  At first, I collect a demo dataset from several famous cryptocurrency websites, such as [Defipulse](https://defipulse.com/), [Coinmarketcap](https://coinmarketcap.com/), and [Defibox](https://www.defibox.com/index). After that, I select features and label the data based on the return. More specifically, I split the return into three earning levels (1,2,3). I label the data as the high earning level when the token got the return no less than 1,000% in 2021 (to Apr. 26), whereas I label the data as the low earning level when the token got the return less than 400% in the same period. The rest data is labeled as the medium earning level. The ratio of these three levels is 8:7:5 (from low to high). The intuition is that we want to find out which token has a higher return. To do so, I convert this problem into a classification task.Besides, I make a brief introduction to the selected features as follows.* Name (Str): The name of the Defi project.* Symbol (str): The symbol of the token.* Chain (str): The blockchain that the project first deploys.* Category (str): The Category that the project belongs to.* TVL (float): Total value locked refers to the total money (USD in billion) that investors deposit in the project.* Mkt_Cap (float): The market capital of the token (USD in billion).* MC/TVL (float): The ratio of Mkt_Cap to TVL.* Cir_Supply (int): The number of tokens that are circulating in the market and are in public hands.* Total_Supply (int): The number of tokens that have been already created, minus any coins that have been burned.* Max_Supply (int): The maximum number of tokens that will ever exist in the lifetime of the cryptocurrency.* Cir_Ratio (float): The ratio of the circulating supply to the total supply.* Price0 (float): The price (USD) on the first day or in 2021-01-01 (Applied to the earlier one).* Price1 (float): The price (USD) on the collection day (2021-04-26).* Return (float): The ratio of Price1 to Price0, which indicates the return.**Preprocessing**  After building the dataset, I implement one-hot embedding on *'Chain'* and *'Category'* as these two features are categorical strings. After that, I drop some unnecessary features such as *'Name'* and *'Symbol'* since these two features are unique to each token and have no help to our task. Lastly, all values are normalized within [0, 1] for better training.**Methods**  Until now, we can employ machine learning models to classify the data. In this demo, I select two classic models, random forest classifier (RFC) and support vector machine classifier (SVMC). Before training the models, I split the dataset as train data (75%) and test data (25%). Also, I evaluate the performance by accuracy on the test data. Duo to demo purpose, I didn't tune the models. The results show that both models achieve **80%** accuracy, which means that this design can successfully find out the tokens from each earning level.| Model | Accuracy || :-----: | :----: || RFC | 0.80 || SVMC | 0.80 | ObservationsAfter achieving the goal, I want to share some observations on this demo dataset.1. About the next potential tokens:  All Defi projects on BSC have higher returns. This result may have a bias as the period of the data collection is within this year. These BSC projects start late so that most of the increase is within this year.  TVL indicates the quality of the projects. The higher quality of the project, the more people are willing to deposit the money in this project. Mkt_Cap represents the market capitalization of the token. The figure below displays the ratio of Mkt_Cap to TVL. The token could be underestimated if this ratio is smaller. Based on this observation, I notice that CRV and MDX are two tokens that are highly underestimated by the market. They could be the next potential tokens to the moon (not financial advice). Note that underestimation doesn't mean that the price will rapidly increase in a short period. Other reasons may affect the price as well. For example, both the above-mentioned tokens have low Cir_Ratio, which means that they have large pre-minted tokens. A larger supply may suppress the price longer.  2. About the top projects:  | Name | TVL | Mkt_Cap || :-----: | :----: | :----: || Uniswap | 8.83 | 19.88 || PancakeSwap | 8.35 | 5.26 || Aave | 7.41 | 4.96 || Maker | 9.75 | 4.16 || Compound | 8.89 | 3.32 |The table above presents the top five projects w.r.t. TVL and Mkt_Cap. The market has a high agreement on the top five projects. Within these projects, Compound has the lowest Return. One potential reason is that it has the lowest MC/TVL.  3. About the categories:  | Category | TVL | Mkt_Cap | Earning_Level || :-----: | :----: | :----: | :----: || Lending | 7.71 | 3.28 | 2.25 || Payments | 1.52 | 1.90 | 2.00 || Yield | 1.99 | 0.23 | 2.00 || Dexes | 5.09 | 4.10 | 1.85 || Derivatives | 0.77 | 0.84 | 1.00 |The table above describes the average values of TVL, Mkt_Cap, and Earning_Level for each category. I notice that Lending projects have the highest earning level (2.25) and TVL (7.71) on average, whereas Dexes projects have the highest market capital (4.10) on average. Those two categories are very popular in this bull market.  4. About the correlation:  The heatmap presents the correlation among features. I'm interested in some features that are helpful to the investment as follows. Cir_Ratio and Price have a higher positive correlation as shorter supply may be easier to cause the increase of the price. A similar positive correlation is also applied to TVL and Mkt_Cap as higher quality projects can attract more investors. It's expected that Cir_Ratio has a higher positive correlation to the Return but actually, it is only 0.103821.   ConclusionIn this demo, I design a scheme to find out the next potential Defi token to the moon by machine learning approach. I collect some demo data from several famous cryptocurrency websites and build machine learning models to classify which token has higher potential. Besides, I also share some observations based on the demo data. In brief, MC/TVL is a helpful feature to find out which token is underestimated. Lending and Dexes projects have higher TVL and Mkt_Cap.  In the future, this scheme could be extended to an online data analysis framework. More specially, we could monitor the data from the cryptocurrency websites' API and then analyze the data on-the-fly. To achieve the same goal, we could also convert the problem into a prediction task by using the Return as the label (we don't know the price in the future). Those attempts may be left for future work.  Conventional forecasting on time-series data<jupyter_code># Import dataset
import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
import datetime, pytz
# ref1: https://www.kaggle.com/someadityamandal/bitcoin-time-series-forecasting
# ref2: https://www.kaggle.com/mczielinski/bitcoin-historical-data?select=bitstampUSD_1-min_data_2012-01-01_to_2021-03-31.csv

def dateparse (time_in_secs):
    # Define a conversion function for the native timestamps in the csv file
    return pytz.utc.localize(datetime.datetime.fromtimestamp(float(time_in_secs)))

data_btc = pd.read_csv('./data/bitstamp_data.csv', parse_dates=[0], date_parser=dateparse)
data_btc.info()

# Preprocessing
from sklearn.preprocessing import MinMaxScaler
sc = MinMaxScaler()

# Select features for time-series prediction
data = data_btc.copy()
data['Timestamp'] = data['Timestamp'].dt.tz_localize(None)
data = data.groupby([pd.Grouper(key='Timestamp', freq='H')]).first().reset_index()
data = data.set_index('Timestamp')
data = data[['Weighted_Price']]
data['Weighted_Price'].fillna(method='ffill', inplace=True)
#data.head()

# Select the range of data and split them into train/test data
start_date = '2020-1-1'
end_date = '2021-3-31'
split_date = '2020-12-31'
data_train = data.loc[(data.index >= start_date) & (data.index <= split_date)].copy()
data_test = data.loc[(data.index >= split_date) & (data.index <= end_date)].copy()

# Data reshape
data_train_ar = data_train.values
data_test_ar = data_test.values
data_train_ar = np.reshape(data_train_ar, (len(data_train_ar), 1))
data_test_ar = np.reshape(data_test_ar, (len(data_test_ar), 1))

# Data normalization
data_train_ar = sc.fit_transform(data_train_ar)
data_test_ar = sc.transform(data_test_ar)
X_train = data_train_ar[0:len(data_train_ar)-1]
Y_train = data_train_ar[1:len(data_train_ar)]
X_train = np.reshape(X_train, (len(X_train), 1, 1))
X_test = np.reshape(data_test_ar, (len(data_test_ar), 1, 1))

# Present the historical price of BTC (2012-2021)
color_pal = ["#F8766D", "#D39200", "#93AA00", "#00BA38", "#00C19F", "#00B9E3", "#619CFF", "#DB72FB"]
_ = data.plot(style='', figsize=(15,5), color=color_pal[0], title='BTC Weighted_Price Price (USD) by Hours')

# Present the train/test data
_ = data_test \
    .rename(columns={'Weighted_Price': 'Test Data'}) \
    .join(data_train.rename(columns={'Weighted_Price': 'Train Data'}), how='outer') \
    .plot(figsize=(15,5), title='BTC Weighted_Price Price (USD) by Hours', style='')

# Train LSTM model for price prediction
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, LSTM, Dropout, Activation

model = Sequential()
model.add(LSTM(128,activation="sigmoid",input_shape=(1,1)))
model.add(Dropout(0.2))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
model.fit(X_train, Y_train, epochs=100, batch_size=50, verbose=2)

# Make the predictions
predicted_BTC_price = model.predict(X_test)
predicted_BTC_price = sc.inverse_transform(predicted_BTC_price)
data_test['Weighted_Price_Prediction'] = predicted_BTC_price
data_all = pd.concat([data_test, data_train], sort=False)
# Display the predicted results
_ = data_all[['Weighted_Price','Weighted_Price_Prediction']].plot(figsize=(15, 5))

# Evaluation by MSE
from sklearn.metrics import mean_squared_error

mean_squared_error(y_true=data_test['Weighted_Price'],
                   y_pred=data_test['Weighted_Price_Prediction'])
# Conclusion: The prediction may get worse if the distribution in test data is distinctly changed.
# Besides, prediction based on time-series data requires lots of data and thus consumes large saving space.<jupyter_output><empty_output><jupyter_text>Classification on time-stamp data<jupyter_code>import pandas as pd
import numpy as np

# Import dataset
data_df = pd.read_csv('./data/defi_data.csv')
#print("train.csv: \n")
print(data_df.info()) # 查看特征统计
print(data_df.describe()) # 查看数据的描述性统计
data_df.corr() # 展示变量之间的相关性
#data_df1.head() # 查看数据前5行

# Plotting the heatmap for the correlation matrix
import matplotlib.pyplot as plt
cor_df = data_df.corr()
plt.pcolor(cor_df)
plt.yticks(np.arange(0.5, len(cor_df.index), 1), cor_df.index, rotation=45)
plt.xticks(np.arange(0.5, len(cor_df.columns), 1), cor_df.columns, rotation=45)
plt.show()
# Lighter color indicates higher positive correlation<jupyter_output><empty_output><jupyter_text>Preprocessing<jupyter_code># Extract the label and id
label = data_df["Earning_Level"]
print("Classes distribution: \n", label.value_counts())
print("Exist null labels? Ans:", label.isnull().any())

# Utils functions for preprocessing

def ohe_cols(df, list_ohe):
    # Implement one-hot embedding on selected columns
    df = df.copy()
    for column in list_ohe:
        tmp_col = pd.get_dummies(df[column], prefix=column) # generate one-hot embedding
        df = pd.concat([df, tmp_col], axis=1) # concate the new ohe to original df
        df.drop([column], axis=1, inplace=True)
    return df

def drop_cols(df, list_drop):
    # Drop selected colums (list_drop) and return the new daraframe (df)
    df = df.copy()
    for column in df.columns:
        if column in list_drop:
            df.drop([column], axis=1, inplace=True)
    return df

def normalize(df):
    # Normalize the dataframe to [0,1].
    for column in df.columns:
        max_value = df[column].max()
        min_value = df[column].min()
        df[column] = (df[column] - min_value) / (max_value - min_value)
    return df

## Preprocessing for "defi_data.csv"

# 1. One-hot embedding
list_ohe = ["Chain", "Category"]
df_ohe = ohe_cols(data_df, list_ohe)
# 2. Drop selected columns
list_drop = ["Name", "Symbol", "Earning_Level"]
df_drop = drop_cols(df_ohe, list_drop)
# 3. Normalize the df to [0, 1]
df_nm = normalize(df_drop)
#print(df_nm.info())
df_nm<jupyter_output><empty_output><jupyter_text>Examine classic classification models<jupyter_code># Models
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC

# Random forest classifier (rfc)
# https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html
def RFC(X_train, Y_train):
    """
    @topic: Fitting the random forest classifier
    @input: X_train/Y_train: train data/label (array)
    @return: model: trained rfr
    """
    model = RandomForestClassifier(max_depth=2, random_state=0) # max_depth: the maximum depth of the tree
    model.fit(X_train, Y_train)
    return model

# SVM classifier (svmc)
# https://scikit-learn.org/stable/modules/svm.html
def SVMC(X_train, Y_train):
    """
    @topic: Fitting the SVM classifier
    @input: X_train/Y_train: train data/label (array)
    @return: model: trained SVM classifier
    """
    model = SVC(kernel='rbf') # linear, rbf, poly
    model.fit(X_train, Y_train)
    return model

# Implement 4-Folds for cross-validation
# https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.KFold.html
from sklearn.model_selection import KFold

def KFolds(data, label, K=4):
    # Split dataset (np.array) and label (np.array) into K folds (int)
    assert len(data) == len(label)
    kf = KFold(n_splits=K, random_state=0, shuffle=True)
    for train_idx, test_idx in kf.split(data):
        #print("TRAIN:", train_idx, "TEST:", test_idx)
        X_train, X_test = data[train_idx], data[test_idx]
        Y_train, Y_test = label[train_idx], label[test_idx]
    return X_train, Y_train, X_test, Y_test

K = 4 # K-Folds
data_ar = df_nm.to_numpy()
label_ar = label.to_numpy()
X_train, Y_train, X_test, Y_test = KFolds(data_ar, label_ar, K)
print("The shape of X_train & X_test are {0}, {1}.".format(X_train.shape, X_test.shape))

# Fitting given models and evaluation
from sklearn.metrics import accuracy_score
#from sklearn.model_selection import cross_val_score

def fit_evaluate(Model, X_train, Y_train, X_test, Y_test, K=4):
    # Fitting the given model and evaluate with Accuracy
    # Fit the model
    model = Model(X_train, Y_train)
    # Generate the predicted label on X_test
    Y_pred = model.predict(X_test)
    print("The classification results: ", Y_pred)
    print("The groundtruth of test samples: ", Y_test)
    # Evaluation by Accuracy
    print("Accuracy: {:.4f}.".format(accuracy_score(Y_test, Y_pred)))
    # Present the K-Folds cross validation scores (rmse)
    #print("{0}-Folds CV Score (Acc): {1}.".\
    #      format(K, cross_val_score(model, X_train, Y_train, cv=K, 
    #                                              scoring='accuracy')))

model_dict = {"RFC":RFC, "SVMC":SVMC}
for model_name, model in model_dict.items():
    print("Model {0}: ".format(model_name))
    fit_evaluate(model, X_train, Y_train, X_test, Y_test, K)
    print("-"*20)<jupyter_output>Model RFC: 
The classification results:  [2 2 1 1 1]
The groundtruth of test samples:  [2 3 1 1 1]
Accuracy: 0.8000.
--------------------
Model SVMC: 
The classification results:  [2 3 1 1 2]
The groundtruth of test samples:  [2 3 1 1 1]
Accuracy: 0.8000.
--------------------<jupyter_text>Observations<jupyter_code># About the next potential tokens
import matplotlib.pyplot as plt

mc_tvl = data_df["MC/TVL"].to_list()
x=np.arange(0,20)
plt.plot(x, mc_tvl,'bo-')
plt.title('MC/TVL of Defi Projects')
plt.xlabel('The Defi Projects')
plt.ylabel('MC/TVL')
plt.xticks(x, data_df["Name"].tolist(), rotation=75)
plt.show()

# About the top projects:
tvl_df = pd.DataFrame(data_df, columns=["Name", "TVL", "Mkt_Cap"]) # "TVL", "Mkt_Cap"
tvl_df.set_index('Name')
tvl_df.sort_values(by=['Mkt_Cap'], ascending=False)
# TVL top 5: Maker, Compound, Uniswap, PancakeSwap, Aave
# Mkt_Cap top 5: Uniswap, PancakeSwap, Aave, Maker, Compound
# The market has high agreement on top 5 projects. 
# Within these projects, Compound has the lowest Return.
# One potential reason is that it has lowest MC/TVL.

# About the categories:
groupby_df = data_df.groupby("Category").mean()
groupby_df.sort_values(by=['Earning_Level'], ascending=False)
# Lending projects have highest earning level (2.25) and TVL (7.71) in average.
# Dexes projects have highest market capital (4.10) in average.

# About the correlation
def corr_coef(A, B):
    # Compute the correlation between dataframe column A (str) and column B (str)
    return data_df[A].corr(data_df[B])

corr_name = [("Cir_Ratio", "Price1"), ("TVL", "Mkt_Cap"), ("Cir_Ratio", "Return")]
for pair in corr_name:
    print("The correlation between {0} and {1}: {2}.".format(pair[0], pair[1], corr_coef(pair[0], pair[1])))<jupyter_output>The correlation between Cir_Ratio and Price1: 0.38575768460844334.
The correlation between TVL and Mkt_Cap: 0.5725069031897233.
The correlation between Cir_Ratio and Return: 0.10382092591700631.
{'fraction_non_alphanumeric': 0.09783845278725825, 'fraction_numerical': 0.02161547212741752, 'mean_word_length': 4.59009009009009, 'pattern_counts': {'":': 2, '<': 22, '<?xml version=': 0, '>': 22, 'https://': 11, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '561210', 'license': 'MIT', 'n_tokens_mistral': 6703, 'n_tokens_neox': 6292, 'n_words': 2557, 'path': 'defi_analysis.ipynb', 'repo_name': 'junzhuang-code/potential_defi_tokens', 'size': '21100'}