Simple Trading Game Simulation
Tweetin data-science · Sat 27 June 2020
in data-science · Sat 27 June 2020
Here I'm trying to simulate a trade game between N people, everybody holds M unit of money. After doing P transactions we want to know what's the final wealth distribution of each people. I'm doing this to study the realworld wealth disparity using a simple trade simulation.
Rules of the trade:
Want to play with this notebook - Try out here
# !pip install seaborn numpy scipy
from collections import Counter
import seaborn as sns
import numpy as np
import scipy
start_state = np.ones(1000) * 100
def start_trade(num_trades=1000, num_people=1000, drop_zero=True, plot=True):
# Starts with 100 unit of Money equally allocated to 1000 people.
start_state = np.ones(num_people) * 100
person_indexes = range(0, num_people)
# Using PRNG to get samples from uniform distribution, it's not ideal but
# good enough for most of the times.
get_person_id = lambda: np.random.choice(person_indexes)
toss = lambda: np.random.choice(["H", "T"])
for i in range(num_trades):
# picking two people randomly for trade.
# p1 = get_person_id()
# p2 = get_person_id()
p1 = get_random_person_id(start_state, drop_zero)
p2 = get_random_person_id(start_state, drop_zero)
# Toss
t = toss()
#If it's head, first person gets the money or second person.
if t == "H":
start_state[p1] += 1
start_state[p2] -= 1
else:
start_state[p1] -= 1
start_state[p2] += 1
return start_state
def get_random_person_id(state: np.array, drop_zero=False):
"""
Randomly pick a person from list of persons.
Array index is person_id.
"""
if drop_zero:
state = np.argwhere(state > 0).squeeze()
else:
state = np.argwhere(state).squeeze()
return np.random.choice(state)
def plot_distribution(nums, num_trades, figname='trade-dist.png'):
''' plot the final state of the trade'''
plot = sns.distplot(
nums,
axlabel=f"Final distribution of wealth after {num_trades} Trades.")
fig = plot.get_figure()
fig.savefig(figname)
Here we are ploting the outcome that traders get from multiple trades between N number of people and then done a distribution plot. That means it generats a Probabilistics Distribution plot over the outcomes here.
We can call the outcome (a random variable) here as the outcome or final wealth of a person after doing N random trades with M number of people.
The distribution plot has a estimated line line plot, area under that will be always 1. Now you can interpret this in terms of percentage / probability.
Assume you have played the #4 trade mentioend below, ie; Doing 1 Million transactions, here you can clearly see a bell curve, that means after the game ~50% of time you will gain money, and ~50% of time you will lose money. But at every stage the loss / gain can be different impact to that person, because loss is more impactful if a person loss 1 rupee from 50 compared to a loss of 1 rupee from total of 150. This shows that even if the game is fair the loss isn't fair that shows why at the end of the game we are seeing a distribution, and faviouring those who has more accumulation.
Another key point is most of the plots average return (Expected value) is less than 100, that means you are most likely going to lose money.
NOTE: ~50% means approximately 50%
Multiple experiments are ran with different number of trades. That helps to see how the final wealth distribution will look like if all of the people do the trade based on coin toss.
num_trades = 1000
final_state = start_trade(num_trades, drop_zero=False)
plot_distribution(final_state, num_trades)
num_trades = 10000
final_state = start_trade(num_trades)
plot_distribution(final_state, num_trades)
num_trades = 100000
final_state = start_trade(num_trades)
plot_distribution(final_state, num_trades)
num_trades = 1000000
final_state = start_trade(num_trades)
plot_distribution(final_state, num_trades)
# Pick out the Probability Density Function and see what's the probability of getting
# 100 when you play this game ?
hist = np.histogram(final_state, bins=20)
hist_dist = scipy.stats.rv_histogram(hist)
hist_dist.pdf(100.0) * 100 # ie; not even 1% chance you get 100 afte this trade.
Starts with 100 unit of cash. If we play this N number of times this also converges to bell curve (Normal distribution).
num_trades = 20
final_state = start_trade(num_trades, num_people=2)
plot_distribution(final_state, num_trades)
num_trades = 1000
final_state = start_trade(num_trades, num_people=2)
plot_distribution(final_state, num_trades)
num_trades = 100000
final_state = start_trade(num_trades, num_people=2)
plot_distribution(final_state, num_trades)
num_trades = 1000000
final_state = start_trade(num_trades, num_people=2)
plot_distribution(final_state, num_trades)
Previous case each traders lose or gain 1 unit of cash, which is an absolute value. Let's make it a 1% of his wealth will be lost when he lose the trade. Here We are making it relative to check that the final wealth distribution uniform or not.
def start_trade_v2(num_trades=1000, num_people=1000, plot=True):
# Starts with 100 unit of Money equally allocated to 1000 people.
start_state = np.ones(num_people) * 100
person_indexes = range(0, num_people)
# Using PRNG to get samples from uniform distribution, it's not ideal but
# good enough for most of the times.
get_person_id = lambda: np.random.choice(person_indexes)
toss = lambda: np.random.choice(["H", "T"])
for i in range(num_trades):
# picking two people randomly for trade.
p1 = get_person_id()
p2 = get_person_id()
# Toss
t = toss()
#If it's head, first person gets the money or second person.
w1 = start_state[p1]
w2 = start_state[p2]
if t == "H":
# Penalty is 1% of the w2's wealth
start_state[p1] += (w2 / 100)
start_state[p2] -= (w2 / 100)
else:
start_state[p1] -= (w1 / 100)
start_state[p2] += (w1 / 100)
return start_state
num_trades = 1000
final_state = start_trade_v2(num_trades, num_people=1000)
plot_distribution(final_state, num_trades)
Let's assume the scenario that if the wealth of a person is below 100, then he don't have to give penalty. If a person has wealth equal or more than 100 then he has to give penalty of 1 if he fails. Let's see how this will come.
def start_trade_v3(num_trades=1000, num_people=1000, plot=True):
# Starts with 100 unit of Money equally allocated to 1000 people.
start_state = np.ones(num_people) * 100
person_indexes = range(0, num_people)
# Using PRNG to get samples from uniform distribution, it's not ideal but
# good enough for most of the times.
get_person_id = lambda: np.random.choice(person_indexes)
toss = lambda: np.random.choice(["H", "T"])
for i in range(num_trades):
# picking two people randomly for trade.
p1 = get_person_id()
p2 = get_person_id()
# Toss
t = toss()
#If it's head, first person gets the money or second person.
w1 = start_state[p1]
w2 = start_state[p2]
w1p = 0
w2p = 0
if w1 >= 100:
w1p = 1
if w2 >= 100:
w2p = 1
if t == "H":
# Penalty is 1% of the w2's wealth
start_state[p1] += w2p
start_state[p2] -= w2p
else:
start_state[p1] -= w1p
start_state[p2] += w1p
return start_state
num_trades = 100000
final_state = start_trade_v3(num_trades, num_people=1000)
plot_distribution(final_state, num_trades)
Here also Norml or close to power law distribution. Again more people lose money it seems.
Another attempt to get equal / uniform distribution after the N trades. In this case the rule is, who ever losing the toss has to lose whatever trader earned extra since beginning.
This setting giving the uniform distribution for everybody has 100 each at the end of the trade also.
def start_trade_v4(num_trades=1000, num_people=1000, plot=True):
# Starts with 100 unit of Money equally allocated to 1000 people.
start_state = np.ones(num_people) * 100
person_indexes = range(0, num_people)
# Using PRNG to get samples from uniform distribution, it's not ideal but
# good enough for most of the times.
get_person_id = lambda: np.random.choice(person_indexes)
toss = lambda: np.random.choice(["H", "T"])
for i in range(num_trades):
# picking two people randomly for trade.
p1 = get_person_id()
p2 = get_person_id()
# Toss
t = toss()
#If it's head, first person gets the money or second person.
w1 = start_state[p1]
w2 = start_state[p2]
w1p = 0
w2p = 0
if w1 >= 100:
w1p = w1 - 100
if w2 >= 100:
w2p = w2 - 100
if t == "H":
# Penalty is 1% of the w2's wealth
start_state[p1] += w2p
start_state[p2] -= w2p
else:
start_state[p1] -= w1p
start_state[p2] += w1p
return start_state
num_trades = 1000
final_state = start_trade_v4(num_trades, num_people=1000)
plot_distribution(final_state, num_trades)