Past Inspired Random Walk

Most people are familiar with Random Walks. The basic idea is that tomorrow’s price is today’s price plus a random return value drawn from a distribution (usually a normal distribution tuned to the volatility of the specific time series we are analysing).

However drawing returns at random can be somewhat problematic as the range of possible outcomes is very large and therefore not that informative. So in this twist of the random walk idea we choose a predetermined set of returns we are going to use for our projections. What does this idea accomplish?

The most important thing is narrowing the set of possible outcomes and tune it to our specific needs.

If we want to model, for example, what the stock of Apple will do, we would construct an appropriate set o returns to run our Monte Carlo simulation on. In this case we can start with the minimum set of returns which would have to be drawn from the historical prices of Apple stock thus far. An implementation of such an idea can be found below in Python code.

But we can choose to enrich such a set of returns with other stocks we consider relevant. We can gather in our predetermined set the returns of NVDA, MSFT and AMZN. Or maybe even the top 50 stocks in the global tech sector.

Obviously the more we include, the closer we come to the point where the Central Limit Theorem comes true and we are effectively simulating drawing returns from a normal distribution in a difficult and computationally expensive manner. So some trial and error is due in order to construct a set of returns that fits one’s specific needs.

from os import walk
import pandas as pd
import random
from tqdm import tqdm
from math import sqrt
from joblib import Parallel, delayed
import matplotlib.pyplot as plt

def index_to_time(df):

    df.set_index('date', inplace = True) # set the date as the index
    df.index = pd.to_datetime(df.index)

    return df.sort_index()


def random_walk(df, steps):

    future_dates = list( pd.date_range(str(df['date'].iloc[-1][:10]), 
                    periods=steps, freq="D") ) 

    past_dates = [dt for dt in df['date']]

    total_dates = past_dates + future_dates

    df['return'] = df['close'].pct_change().fillna(method='bfill')

    future_returns = []
    for i in range(steps):
        future_returns.append(
            random.choice(df['return'])
        )
    
    past_returns = [el for el in df['return']]
    total_returns = past_returns + future_returns

    start_price = df['close'].iloc[-1]

    future_prices = []
    for i in range(steps):
        if i==0:
            future_prices.append(
            start_price*(1+future_returns[i])
            )
        else:
            future_prices.append(
                future_prices[i-1] * (1+future_returns[i])
            )
    
    past_prices = [el for el in df['close']]
    total_prices  = past_prices + future_prices
    
    walk_df = pd.DataFrame()
    walk_df['date'] = total_dates
    walk_df['return'] = total_returns
    walk_df['close'] = total_prices
    walk_df['sim'] = ['no' for el in past_prices] + ['yes' for el in future_prices]

    return walk_df


def plot_sim(df, steps):

    sim_df = index_to_time( random_walk(btc, steps)[['date', 'close']] )
    sim_df = df[ sim_df.index > '2021' ]
    fig = plt.figure()
    fig.suptitle('Random Walk')
    plt.figure(figsize=(14,7))

    plt.plot(sim_df[:-steps+1], color = 'b')
    plt.plot(sim_df[-steps:], color = 'r')

    plt.axvline( x = sim_df.index[-steps], 
                color = 'g', linestyle = '--')
    plt.xlabel('Day')
    plt.ylabel('Price')

    axes = plt.gca()

    return plt.show()

def plot_s(df, steps):

    sim_df = index_to_time( df[['date', 'close']] )
    sim_df = sim_df[ sim_df.index > '2021' ]
    fig = plt.figure()
    fig.suptitle('Random Walk')
    plt.figure(figsize=(14,7))

    plt.plot(sim_df[:-steps+1], color = 'b')
    plt.plot(sim_df[-steps:], color = 'r')

    plt.axvline( x = sim_df.index[-steps], 
                color = 'g', linestyle = '--')
    plt.xlabel('Day')
    plt.ylabel('Price')

    axes = plt.gca()

    return plt.show()