Skip to content

Instantly share code, notes, and snippets.

@beta-decay
Last active August 7, 2018 17:15
Show Gist options
  • Save beta-decay/a6abe40fc9f4ff6cac443395377ec31f to your computer and use it in GitHub Desktop.
Save beta-decay/a6abe40fc9f4ff6cac443395377ec31f to your computer and use it in GitHub Desktop.
Controller for Stack Exchange Stock Exchange
import subprocess, os, math
import matplotlib.pyplot as plt
def calcLog(x):
if x <= 0:
return 0
else:
return math.log(x)
class Trader:
def __init__(self, name, command):
# Initialise the values for the trader
self.name = name
self.command = command
self.balance = 100
self.shares = 5
self.balanceHistory = [100]
self.sharesHistory = [5]
def run_code(self, share_price, round_number):
global total_bought, total_sold
try:
# Run program
result = subprocess.run(self.command+[str(share_price), str(self.shares), str(self.balance), str(round_number)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, env=os.environ)
result_debug = result
result = result.stdout.decode("UTF-8").rstrip()
# Parse the result of the program
option = result[0]
try:
# Get number of shares
number = int(result[1:])
if number > 0:
if option == "b":
# Buy shares
# Check if player has enough money
cost = number * share_price
if self.balance - cost >= -1000:
# Player has enough money
# Update player variables
self.balance -= cost
self.shares += number
# Update round variable
total_bought += number
else:
# Player has insufficient funds
# Make player bankrupt
self.balance = 50
self.shares = 0
elif option == "s":
# Sell shares
# Check if player has enough shares
if self.shares >= number:
# Player has enough shares
# Update player variables
self.shares -= number
self.balance += number * share_price
# Update round variable
total_sold += number
else:
# Do nothing
pass
else:
# Invalid input
# Do nothing
pass
else:
# Do nothing
pass
except:
# Ignore request if the number of shares is not an integer
pass
except Exception as e:
print(self.command)
print(result_debug.stderr)
print(result_debug.stdout)
print(e,"\n")
# Initialise players
commands = [
["python","dollarcost/dollarcost.py"],
["python","fallacious/fallacious.py"],
["python", "naive/naive.py"],
["python", "buylow/buylow.py"],
["python", "oyaib/oyaib.py"],
["C:/Ruby25/bin/ruby.exe", "novice/novice.rb"],
["python", "passive/passive.py"],
["python", "monkeys/monkeys.py"],
["python", "chimps/chimps.py"],
["python", "lone/lone.py"],
["python", "halfmore/halfmore.py"],
["python", "percentage/percentage.py"],
["C:/PHP/php.exe", "experienced/experienced.php"],
["python", "illiterate/illiterate.py"],
["python", "greedyb/greedyb.py"],
["python", "equaliser/equaliser.py"],
["python", "buyreinvest/buyreinvest.py"],
["python", "fibonacci/fibonacci.py"],
["python", "lucky/lucky.py"]
]
names = [
"Dollar Cost Averager",
"Fallacious Gambler",
"Naive Statistician",
"Buy Low",
"OYAIB",
"Novice Broker",
"Passive Trader",
"Monkeys on a Typewriter",
"Chimps on a Typewriter",
"Lone Accountant",
"Half More or Nothing",
"Percentage Trader",
"Experienced Greedy Idiot",
"Illiterate Dividend Investor",
"Greedy B*****d",
"Equalizer",
"Buy/Reinvest",
"Fibonacci",
"Lucky Number 6"
]
# Fibonacci
names_and_commands = zip(names, commands)
players = []
for name, command in names_and_commands:
players.append(Trader(name, command))
# Set initial share price
share_price = 10
# Save old share prices
previous_share_prices = []
# Start main loop
for i in range(1000):
# Add share price to list
previous_share_prices.append(share_price)
# Initialise round variable
total_bought = 0
total_sold = 0
# Run all players
for player in players:
player.run_code(share_price, i+1)
if len(previous_share_prices) >= 5:
player.balance += int(sum(previous_share_prices[-5:])/5*0.05)
player.balanceHistory.append(player.balance)
player.sharesHistory.append(player.shares)
# Calculate new share price
change = total_bought-total_sold
# Regulate share price
if change > 200:
# Too rapid change
change = 200
if change < -200:
change = -200
share_price += change
# Prevent share price from falling below $1
if share_price < 1:
share_price = 1
# Add final share price
previous_share_prices.append(share_price)
# Print leaderboard
sorted_players = sorted(players, key=lambda player: player.balance)
print("Name\t\tBalance")
for j in sorted_players[::-1]:
print(j.name+"\t\t$"+str(j.balance))
# Plot a graph of share price
fig = plt.figure(figsize=(10, 6*len(players)))
plt.subplot(len(sorted_players)+1, 1, 1)
ax1 = plt.gca()
ax1.plot(list(map(calcLog, previous_share_prices)))
ax1.set_xlabel('Round')
ax1.set_ylabel('Log Share Price')
ax1.set_title('Share Price throughout game')
ax1.set_yscale("log", nonposy='clip')
plt.tight_layout()
# Plot a graph of each player
for k in range(len(sorted_players)):
player = sorted_players[k]
plt.subplot(len(sorted_players)+1, 1, k+2)
ax1 = plt.gca()
color = 'tab:red'
ax1.set_xlabel('Round')
ax1.set_ylabel('Log Balance', color=color)
ax1.set_title(player.name)
ax1.plot(list(map(calcLog, player.balanceHistory)), color=color)
ax2 = ax1.twinx() # instantiate a second axes that shares the same x-axis
color = 'tab:blue'
ax2.set_ylabel('Log Shares', color=color) # we already handled the x-label with ax1
ax2.plot(list(map(calcLog, player.sharesHistory)), color=color)
plt.tight_layout()
plt.savefig("graph.png",bbox_inches='tight')
plt.show()
@Herman-L
Copy link

Herman-L commented Aug 2, 2018

A diagram showing each player's balance (including the value of their stocks) over time might be interesting to add, since it would show how well each player did during the game.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment