Giter VIP home page Giter VIP logo

mcd_risk_tool's Introduction

MakerDAO MCD Risk Tool

Status GitHub Pull Requests License


Analyzing different collateral type risk with models

Code will be availible once Readme has been reviewed

🗄 Table of Contents

Goal of the Risk Tool:

Goal of the tool is to display risk and allow users to double click on visually evaluating risk.

💽 Setup:

If you would like to install and use this tool on your own machine make sure you have pipenv installed. If you need to install follow these simple instructions.

Downloading The Prerequisites

The packages needed to run this tool: pipenv, homebrew, python3, pandas, and numpy.

The packages pandas and numpy will need to be installed in order for these repositories to work. If you do not have them installed, run these commands in your terminal:

pipenv install pandas
pipenv install numpy

Cloning The Repo

Clone the MCD Risk Model Repo:

git clone https://github.com/madison1111/mcd_risk_model.git

Then cd into the directory:

cd mcd_risk_model

Loading the Models

To reproduce results run the following commands:

python3 mcd_risk_model_1.py
python3 mcd_risk_model_2.py

For Analysis

Start your Jupyter notebook:

jupyter notebook

If your browser doesn't automatically open, then go to:

http://localhost:8888

🎛 Running the Models

Notebooks can be found in the notebooks folder

MCD Risk Model 1:

mcd_risk_model_1.py

Step 1:

Before doing anything else this script imports the various dependencies that allow for the script to be run in python as well as creates a class for the initial variables that we need

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import time

And create the class:

class mcd_model_1:
    def __init__(self, start, end):
        self.start = start
        self.end = end

After, we look to call our data and will make two separate methods within our mcd_model_1 class, one for a single collateral type, and one for multiple collateral types

Data is encapsulated in Risk RESTful API

Historical Data

Collect trading history across all exchanges

Read in data

Data methodology is outlined in Risk API github repo

Historical exchange data

There is no ‘master exchange’ to pull from so we pull from several BTI verified exchanges, as price is determined by supply and demand and wash trading is a real problem among cryptocurrency exchanges

We pull data from 6 major exchanges: Coinbase, Bitfinex, Poloniex, Bitstamp, and Binance and Kraken to calculate an aggregate price index.

For example we can inspect the last 4 rows of the dataframe using the tail() method

Screen Shot 2019-08-02 at 11 00 15 AM

Close - reversion benchmark

  • Reversion metrics give us an indication of our temporary impact

Open - momentum benchmark

Previous Close - momentum benchmark

  • Momentum metrics give us an indication of the direction of the price drift

Two Separate Methods

Define single collateral type:

def get_single_collateral(self, symbol):
        #dates
        start = self.start 
        end = self.end 
        
        prices = ETH['Close']
        returns = prices.pct_change()
        
        self.returns = returns
        self.prices = prices

Define multiple collateral types:

def get_multiple_collateral(self, symbols, weights):
        start = self.start
        end = self.end
        
        #Get exchange price data
        df = ETH['Close']
       
        #percent Change
        returns = df.pct_change()
        returns += 1
        
        #define allocation of each asset 
        portfolio_value = returns * weights
        portfolio_value['Portfolio Value'] = portfolio_value.sum(axis=1)
        
        #Portfolio prices
        prices = portfolio_value['Portfolio Value']
        
        #portfolio returns 
        returns = portfolio_value['Portfolio Value'].pct_change()
        returns = returns.replace([np.inf, -np.inf], np.nan)
                
        self.returns = returns
        self.prices = prices

Checking out momentum:

#Moving averages are a great way to determine the momentum of the cryptocurrency
ma_days = [10,20,50]
for ma in ma_days:
    column_name = "MA %s days" %(str(ma))
    ETH[column_name] = ETH['Adj Close'].rolling(window=ma,center=False).mean()
ETH[['Adj Close','MA 10 days','MA 20 days','MA 50 days']].plot(legend=True)

Moving average representation:

Moving average representation expresses any time series Yt as:

Screen Shot 2019-08-02 at 1 17 27 PM

ETH output:

Screen Shot 2019-08-02 at 11 23 36 AM

Once we have historical returns, we can gauge their relative dispersion with a measure called variance

Now we will calculate volatility and historical time-to-revert for collateralization ratio

Volatility: is calculated as the standard deviation of returns

Close to Close Volatility Method:

Screen Shot 2019-08-02 at 1 16 45 PM

def close_to_close_volatility(close_ret, mean_ret): 
    return np.sqrt(np.sum((close_ret-mean_ret)**2)/390)

OHLC Volatility Estimation Method:

Screen Shot 2019-08-02 at 1 20 15 PM

def crypto_volatility(open, high, low, close, close_tm_): 
    return np.log(open/close_tm1)**2 + 0.5*(np.log(high/low)**2) \
        - (2*np.log(2)-1)*(np.log(close/open)**2)

CDP States:

  • bite = debt-tranche that has been liquidated
  • wipe/shut = debt-tranche that has been voluntarily closed by owner
  • unsafe = CDP is below 150% collateralization ratio
  • safe = CDP is not below 150% collateralization ratio
  • open = CDP is safe and is still open

The time to revert for collateralization ratio looks at the CDP states and every state in the sequence has a timestamp (number of seconds the CDP stayed in that state)

  1. Calculate Transition state (infinitesimal generator matrix or intensity matrix)
  2. Look at the sequence distribution
  3. Answer: What fraction of all dai-time is spent in state ‘m’ before moving to state ‘k’?
  4. A matrix of the number of transition states are produced
  5. The next component of the code multiplies 2 matrices to get ‘draw_time’ in seconds
  6. The next component categories into safe and unsafe
  7. A final matrix is produced
sequesnces_with_next_state = utils.dataframe_to_sequences_with_end_state(df) 
time_spent_matrix = utils.time_spent_before_state_change_distribution(sequence_with_next_state)
time_spent_matrix

Geometric Brownian Motion data: Volatility modeled in GBM function

Geometric Brownian Motion assumes that a constant drift is accompanied by random shocks and the period returns are normally distributed under GBM, the consequent multi-period price levels are lognormally distributed

Brownian motion models the random behavior of our collateral type over time

logarithmic_return = np.log(ETH.close)-np.log(close.shift(1))
mean_return = np.mean(returns)
volatility = returns.std()

GBM is composed of drift and shock

Screen Shot 2019-08-02 at 1 31 36 PM

Where:

Screen Shot 2019-08-02 at 1 34 35 PM

drift Screen Shot 2019-08-02 at 1 38 48 PM a long term trend in the positive or negative direction; derived from collaterals historical performance shock Screen Shot 2019-08-02 at 1 40 32 PM added or subtracted to the drift; function of the collaterals standard deviation

For each timestep, our model assumes the price will ‘drift’ up by the expected return

The drift will be ‘shocked’ (+/-) by a random shock and the random shock will be the standard deviation multiplied by a random number; this is a way of scaling the standard deviation

Pseudocode for GBM of ETH:

Screen Shot 2019-08-02 at 1 42 29 PM

GBM without jump diffusion parameters:

Xo initial collateral type price mu returns (drift coefficient) sigma volatility (diffusion coefficient) W brownian motion T time period N number of increments

def GBM(Xo, mu, sigma, W, T, N):    
    t = np.linspace(0.,1.,N+1)
    X = []
    X.append(Xo)
    for i in xrange(1,int(N+1)):
        drift = (mu - 0.5 * sigma**2) * t[i]
        diffusion = sigma * W[i-1]
        X_temp = Xo*np.exp(drift + diffusion)
        X.append(X_temp)
    return X, t

To capture drift and diffusion:

def daily_return(adj_close):
    returns = []
    for i in xrange(0, len(adj_close)-1):
        today = adj_close[i+1]
        yesterday = adj_close[i]
        daily_return = (today - yesterday)/yesterday
        returns.append(daily_return)
    return returns

returns = daily_return(adj_close)

We use these simulations and visualize them:

while (simulation < number_simulations):
    #list for each new simulation 
    prices = [] 
    #reset the counter 
    days = 0 
    #input initial price 
    prices.append(last_price) 
    time_step = 1

    while (days < predicted_days):
        drift = (mean_return - volatility**2/2)*time_step
        shock = volatility * np.random.normal() * time_step**0.5
        price = prices[days] * np.exp(drift+shock)
        prices.append(price) 
        #increment days counter 
        days = days + 1
    
    results[str(simulation)] = pd.Series(prices).values
    #increment simulation counter 
    simulation = simulation + 1

Behavioral Data

Analyzing CDP behavior and jump diffusion

To tune our model we are going to want to input: volatility, probability_of_jump, and mean_jump_size.

In jump diffusion the idea is that price movements underlie sudden changes:

Screen Shot 2019-08-02 at 1 49 00 PM

Jump diffusion will consider downtrends and captures the true picture of the collateral behavior:

Screen Shot 2019-08-02 at 1 50 25 PM

We can calculate Screen Shot 2019-08-02 at 1 51 22 PM by:

sigma*np.randn()+mu

We can then calculate the jump size J by:

def jump(probability, mean_size, volatility):
    jump_size = mean_size + volatility*np.random.randn()
    return decision(probability)*jump_size

After, we are going to want to map the instance of the event under consideration of a probability using:

def decision_collateral(probability):
   return int(np.random.random() < probability)

Taking the input data:

  • date
  • collateral_type
  • last_price
  • volatility
  • simulated_time_window
  • Time_window
results = pd.DataFrame()

simulation = 0
while (simulation < number_simulations):
    prices = []
    days = 0 
    prices.append(last_price) 
    time_step = 1
    while (days < predicted_days):
        drift = (mean_return - volatility**2/2)*time_step
        shock = volatility * np.random.normal() * time_step**0.5
        c_jump = jump(probability, mu, sigma)
        price = prices[days] * np.exp(drift+shock)
        price = price + c_jump
        prices.append(price)
        days = days + 1
    if(prices[-1] > 0):
        results[str(simulation)] = pd.Series(prices).values 
    simulation = simulation + 1

How we model GBM and Collateralization Ratio

We take this GBM with jump diffusion and the inertia reversion function to fluctuating collateralization ratio come in at ETH price path based on GBM with jump diffusion

ETH price path over time and second pass reads each day movement of ETH prices, ETH price moved x% and I am going to change collateralization ratio by y% and y is a function of x and the previous collateralization ratio and time

Evaluate liquidity by analyzing:

  • % of day's volume
  • intraday volume curve
  • cumulative intraday volume curve, etc.

Traditionally, liquidity is highest as we approach the close and second highest at open.

Parameters for ‘expected amount of loss’:

crashes that are atypical;outright collateralization or significant enough sale through liquidation

The Value at Risk (VaR) for coverage is the maximum amount we could expect to lose with likelihood Screen Shot 2019-08-02 at 1 59 23 PM

  • VaR is very similar to confidence intervals
  • Conditional Value at Risk takes into account the shape of the returns distribution

Defining CVaR:

def cvar(invested, returns, weights, alpha=0.95, lookback=500):
    var = value_at_risk(invested, returns, weights, alpha, lookback=lookback)
    returns = returns.fillna(0.0)
    portfolio_returns = returns.iloc[-lookback:].dot(weights)
    
    # Get back to a return
    var_pct_loss = var / invested
    
    return invested * np.nanmean(portfolio_returns[portfolio_returns < var_pct_loss])
  • CVaR captures the moments of the distribution; if the tails have more mass CVaR will capture this
  • Following calculating CVaR check for convergence

Tail Risk:

  • CVaR will take care of tail risk or fat tailedness
  • Tail risk: autoregressive processes tend to have more extreme values than data taken from a normal distribution and this is because the value at each time point is influenced by recent values
  • Fat tail distribution: if the series randomly jumps up, it is more likely to stay up than a non-autoregressive series, the extremes on the pdf will be fatter than the normal distribution
def calc_unadjusted_interval(X):
    T = len(X)
    mu = np.mean(X)
    sigma = np.std(X)
    lower = mu - 1.96 * (sigma/np.sqrt(T))
    upper = mu + 1.96 * (sigma/np.sqrt(T))
    return lower, upper

Statistical Rigor

Checking out visualizations of your data is not enough; we need a stronger degree of statistical rigor ie confidence intervals, pacf, etc.

Terms:

  • Volatility is calculated as the standard deviation of returns
  • Slippage is when the price 'slips' before the trade is fully executed, leading to the fill price being different from the price at the time of the order. Slippage is where our backtester calculates the realistic impact that your orders have on the execution price we receive.
  • The attributes that have the most influence on slippage are:
  • Volatility
  • Liquidity
  • Relative order size
  • Bid / Ask spread

✍️ Authors

mcd_risk_tool's People

Contributors

atnickallen avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.