<jupyter_start><jupyter_text>IntroductionThe purpose of this notebook is to test a hypothesis that I formulated while reading [Non-Ergodicity and its Implications for Businesses and Investors](https://fundooprofessor.wordpress.com/2019/10/13/non-ergodicity/) by <NAME>.The author does an excellent job of introducing the concept of Non-Ergodicity and explaining its implications for investment management (particularly position sizing). I strongly recommend it. It is very accessible. In the comments on Slide 64, he begins a discussion on the need for broad diversification (e.g., no more than ~1% of portfolio at risk in any investment). My hypothesis was that, since he had described a winning game, being able to participate in several similar games across a limited portfolio (size <= 15) would result in long-term wealth creation (as the author demonstrates, it doesn't work without diversification).The notebook below has the following structure:1. Defines the logic to recreate the game and simulate it for multiple iterations2. Follows the author's story arc for a single-bet-per-round game (portfolio_size = 1)3. Tests my hypothesis by creating a portfolio of similar games (portfolio_size > 1)4. Presents the results of limited diversification  Import libraries<jupyter_code>import numpy as np
import pandas as pd

import matplotlib.pyplot as plt<jupyter_output><empty_output><jupyter_text>Define function that recreates game and runs the simulation<jupyter_code>def run_simulation(portfolio_size, num_coin_tosses, num_runs, use_seed = True):
    """
    Runs a number of iterations of the coin tossing game defined in the PowerPoint 
    presentation titled "Non-Ergodicity and its Implications for Businesses and Investors" 
    by <NAME> (see link above).
    
    The game is enhanced to support multiple investments (portfolio_size) in simultaneous games
    with the same payoff structure, so that the effects of diversification can be determined.
    
    :param portfolio_size (int): Defines the number of 'investments' made in each round; 
    allocation for each investment is 1 / portfolio_size.
    
    :param num_coin_tosses (int): Defines the number of coin tosses used in each iteration.
    
    :param num_runs (int): Defines the number of iterations used in the simulation.
    
    :param use_seed (boolean): Controls whether to use a seed for the random number generator. 
    Set to 'True' (the default value), it enables simulation outcomes to be repeated.

    :returns: Tuple comprised of a 3-D numpy array containing the results for all coin 
    tosses for all portfolios for all runs, and a pandas dataframe containing the final 
    portfolio values for each run.
    
    :raises: :class:`AssertionError`: If any of the integer parameters are less than or equal to 
    zero.
    """
    
    # validate inputs
    assert(portfolio_size >= 1)
    assert(num_coin_tosses >= 1)
    assert(num_runs >= 1)
    
    # set paramaters of "game"
    win_pct = 0.50
    loss_pct = 0.40

    initial_capital = 100
    
    # generate random numbers to simulate coin tosses
    # set seed, if so desired, to ensure simulation results are repeatable
    # randint setting of 2 will generate integers 0 or 1, use 1 for heads
    if use_seed: np.random.seed(17)
    coin = np.random.randint(2, size=(num_runs, num_coin_tosses, portfolio_size))
    
    # calculate change in value for tails, heads
    # when coin toss is simulated, all values of variable 'coin' will be either 0 (tails) 
    # or 1 (heads); use value of each coin toss as index into change_in_value array, which
    # is used to calculate payoffs for each outcome of the coin toss
    change_in_value = [1 - loss_pct, 1 + win_pct]

    # calculate percentage of total capital to allocate to each portfolio
    allocation = 1 / portfolio_size
    
    # create arrays to store individual outcomes and  
    # total portfolio value at the end of each run
    results = np.zeros(shape=(num_runs, num_coin_tosses, portfolio_size))
    portfolio_values = np.zeros(num_runs)
    
    # run simulation
    for run in range(num_runs):
        
        # cur_capital tracks changes in portfolio value after each round of coin tosses
        # reset it at the beginning of each run
        cur_capital = initial_capital
    
        for toss in range(num_coin_tosses):
            # after each round of tosses, change bet size based on current amount 
            # of capital available (this is equivalent to rebalancing the portfolio
            # after every round of coin tosses)
            bet_size = cur_capital * allocation
            
            # toss one coin for each portfolio and calculate resulting change in 
            # total portfolio value
            for portfolio in range(portfolio_size):
                results[run, toss, portfolio] = change_in_value[coin[run, toss, portfolio]] * bet_size
                
                # current capital is sum of resulting portfolio values
                cur_capital = results[run, toss].sum()
     
        # save last portfolio value calculated for each run
        portfolio_values[run] = cur_capital
        
    # return individual results and final portfolio values for each run
    return results, pd.DataFrame(portfolio_values, columns=['Final Portfolio Value'])<jupyter_output><empty_output><jupyter_text>Run simulations with settings that follow author's story arcIn the initial slides, the author walks through the effects of a limited number of tosses from a few people. He shows that, while the average return is good (as you'd expect from a winning game), the results are skewed in ways that people don't expect (because they are focused on what the author terms the 'ensemble average').The simulation shows results similar to the author's for 100 people making 50 coin tosses each.<jupyter_code>results, port_values = run_simulation(portfolio_size = 1, num_coin_tosses = 50, num_runs = 100)
port_values.hist(bins=50);
port_values.describe()<jupyter_output><empty_output><jupyter_text>As the histogram and descriptive statistics demonstrate, the average value is 568.68, a return of over 468% (starting portfolio value was 100). Unfortunately, the average is skewed by a few people who made outsized gains (max portfolio value = 27,385.63). Note that the 75%-tile value is only 61.69, which means that more than 3/4 of the people who played this "winning" game lost almost 40% of their initial capital, and half of the people lost more than 92%. The author presents a mathematical analysis to explain why this happens. The author then demostrates that, because this is a non-ergodic game, everyone who plays will lose eventually. This can be seen by increasing the number of coin tosses and reviewing the results.<jupyter_code>results, port_values = run_simulation(portfolio_size = 1, num_coin_tosses = 1000, num_runs = 100)
port_values.hist(bins=50);
port_values.describe()<jupyter_output><empty_output><jupyter_text>As the histogram and descriptive statistics demonstrate, the average value is now essentially zero (note that the value of the exponent is -9), as is the maximum value (exponent is -7), which means everyone lost all of their money, despite this being a "winning" game. Explaining the mathematical reasons for these results is what makes the author's presentation so intersting and valuable.  Test my hypothesis that limited diversification should result in positive resultsAs the author points out, if you're playing a game in which you can lose 40% of your money on one coin toss, and you play long enough, you'll most likely experience a run of tails. A run of only 6 tails would mean, no matter how much you had prior to this streak, you'd lose ~95% ((1 - 40%)6 = ~0.047) of your capital during this losing streak.This makes sense, since a streak of 6 tails occuring in 1000 tosses is probable (I'll leave the math to you). I hypothesized, however, that if you played a portfolio of similar games, you'd be exposed to the upside more often in each round, and, by allocating an equal amount to each portfolio, you should be able to increase the value of your portfolio.My initial guess, due to other research, was that a portfolio size of 15 would result in  positive returns (meaning that 1/15 or 6.67% of the total portfolio value was allocated to each of 15 coin tosses in each round). Here are the results...<jupyter_code>results, port_values = run_simulation(portfolio_size = 15, num_coin_tosses = 1000, num_runs = 100)
port_values.hist(bins=50);
port_values.describe()<jupyter_output><empty_output><jupyter_text>As the histogram and descriptive statistics demonstrate, even the minimum value is astronomically large. At first, I thought this was just a lucky outcome, but I re-ran the simulation 50 times (prior to adding a seed), and the results were similar. I wasn't completely surprised by the outcome, but I was surprised by the magnitude of the final values. So, I decided to find the minimum portfolio size that would result in a positive outcome in all cases.  Present results of limited diversificationI ran a loop that decremented the portfolio size by 1 (starting at 15) each time. I was surprised by how small the portfolio could be while ending up with positive results in all cases. As you can see, I increased the number of coin tosses per round and the number of runs, to ensure that I got a stable outcome (1000 coin tosses per run and 100 runs was fairly stable, but there was still some statistically meaningful variance). Here are the results...<jupyter_code>results, port_values = run_simulation(portfolio_size = 3, num_coin_tosses = 5000, num_runs = 1000)
port_values.hist(bins=50);
port_values.describe()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.05354395322597189, 'fraction_numerical': 0.016617088932198175, 'mean_word_length': 3.9694189602446484, 'pattern_counts': {'":': 0, '<': 28, '<?xml version=': 0, '>': 31, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '6', 'dir': 'jupyter-structured-clean-dedup', 'id': '577032', 'license': 'MIT', 'n_tokens_mistral': 2684, 'n_tokens_neox': 2463, 'n_words': 1286, 'path': 'AuthorResponses/NonErgodicityAndDiversification.ipynb', 'repo_name': 'InnovestmentLLC/public_notebooks', 'size': '9763'}