35
11
Background
You are all traders for a slightly less than reputable stock firm. You all are part of a group of traders who focus only on one specific stock.
Each hour each trader has a chance to Buy X stocks or Sell X stocks. There are 50 hours per round and 3 rounds per competition. At the end of all of the rounds the trader with the highest average value wins a trip to Jamaica!
Gameplay
There are 3 rounds of 50 turns each.
Each trader begins the round with $5000 and A random number of shares between 20 and 30 shares. The price of the shares starts at a random number between 10 and 150.
Each turn each trader can Buy any number of shares they can afford or Sell any number of shares they currently hold, each for the current price per share.
The price per share increases by a random number between 1 and 5 for each share bought and decreases by a random value between 2 and 6 for each share sold. The minimum price is $1.
It is important to note that all traders process their transactions at the same time, meaning that any trader buying/selling shares will not affect the price until the next turn.
The player with the highest average value at the end of the 3 rounds wins. Value is determined by taking the amount of money left over at the end of the round and adding the number of shares owned by the trader * closing market price.
Arguments
Your program will be rerun at the start of each turn receiving the current market price, the trader's current amount of money and the number of shares owned by that trader.
Ex:
120 5000 0
Output
Your trader program must output a letter corresponding to the action it would like to take followed by the quantity.
Ex:
B10 //Buy 10 shares
or
S3 //Sell 3 shares
The trader also has the option to not do anything that turn. That can be accomplished by outputting a W or any other command that is not 'B>amnt<' or 'S>amnt<'
Submissions
Your program will be inside a 'players/>your program name<' directory:
+-- players
| +-- BotNameFolder
| +-- BotProgram
Please provide your code along with a command line argument to run it from inside the 'players' directory.
For example the Test1 trader could be run with java -cp "Test1" Test1
Additional Rules
Go ahead, shoot yourself EmoWolf, Idc.
No messing with anything outside of your BotNameFolder directory, feel free to create files in there though for persistent info throughout rounds / turns.
Do not intentionally create programs to crash the simulation.
I will accept multiple entries per user, as long as the entries act as seperate entities (No insider trading).
Leaderboard
[java&-cp&"TestPlayer"&Test1]:$10027395221
[python&daydreamer/daydreamer.py]:$5000
[java&-cp&"DayTrader"&DayTrader]:$4713199930331196453
I will try to update the leaderboard at least once a day
Controller
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Random;
public class Controller {
public static BigInteger marketValue = BigInteger.valueOf(100);
public static BigInteger newValue = BigInteger.valueOf(100);
public static final char BUY = 'B';
public static final char SELL = 'S';
public static final int MARKET_INDEX = 1;
public static final int MONEY_INDEX = 2;
public static final int SHARE_INDEX = 3;
public static int numRunning = 0;
public static final int MAX_RUNNING = 10;
public static void main(String[] args){
try {
BufferedReader br1 = new BufferedReader(new InputStreamReader(new FileInputStream("resources/config")));
int numRounds = Integer.parseInt(br1.readLine());
int turnsPerRound = Integer.parseInt(br1.readLine());
//Create the array of players
List<String> players = new LinkedList<String>();
String line1 = null;
while((line1 = br1.readLine()) != null){
players.add(line1);
}
BigInteger[] totalVals = new BigInteger[players.size()];
for(int i = 0; i < totalVals.length; i++){
totalVals[i] = BigInteger.valueOf(0);
}
br1.close();
//Begin processing
for(int round = 0; round < numRounds; round++){
//Create players' shares and currency array
Map<String,BigInteger[]> vals = new HashMap<String, BigInteger[]>();
for(int i = 0; i < players.size(); i++){
vals.put(players.get(i), new BigInteger[]{BigInteger.valueOf(5000), BigInteger.valueOf(getRandInt(20,30))});
}
marketValue = BigInteger.valueOf(getRandInt(10,150));
newValue = marketValue;
for(int turn = 0; turn < turnsPerRound; turn++){
marketValue = newValue;
Queue<Object[]> processQueue = new LinkedList<Object[]>();
for(String playerKey : vals.keySet()){
BigInteger[] valSet = vals.get(playerKey);
String[] pkParts = playerKey.split("&");
String[] parts = new String[pkParts.length + 3];
for(int i = 0; i < pkParts.length; i++){
parts[i] = pkParts[i];
}
parts[pkParts.length] = marketValue + "";
parts[pkParts.length + 1] = valSet[0] + "";
parts[pkParts.length + 2] = valSet[1] + "";
processQueue.add(new Object[]{playerKey, parts});
}
while(!processQueue.isEmpty() || numRunning > 0){
if(numRunning < MAX_RUNNING && !processQueue.isEmpty()){
numRunning++;
Object[] o = processQueue.poll();
String pKey = (String)(o[0]);
String[] p = (String[])(o[1]);
try {
Process proc = new ProcessBuilder(p).directory(new File("resources/players").getAbsoluteFile()).start();
BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String line = br.readLine();
br.close();
switch(line.charAt(0)){
case BUY :
BigInteger numShares = new BigInteger(line.substring(1).trim());
if(numShares.multiply(marketValue).compareTo(vals.get(pKey)[0]) <= 0){
BigInteger[] tempVals = vals.get(pKey);
tempVals[0] = tempVals[0].subtract(numShares.multiply(marketValue));
tempVals[1] = tempVals[1].add(numShares);
vals.put(pKey, tempVals);
newValue = newValue.add(numShares.multiply(BigInteger.valueOf(getRandInt(0,2))));
if(newValue.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) >= 1){
newValue = BigInteger.valueOf(Integer.MAX_VALUE - 1);
}
}
break;
case SELL:
BigInteger shares = new BigInteger(line.substring(1).trim());
if(shares.compareTo(vals.get(pKey)[1]) <= 0){
BigInteger[] tempVals = vals.get(pKey);
tempVals[0] = tempVals[0].add(shares.multiply(marketValue));
tempVals[1] = tempVals[1].subtract(shares);
vals.put(pKey, tempVals);
newValue = newValue.subtract(shares.multiply(BigInteger.valueOf(getRandInt(5,10))));
if(newValue.compareTo(BigInteger.valueOf(1)) <= -1){
newValue = BigInteger.valueOf(1);
}
}
break;
}
} catch (Exception e) {
System.err.println("[" + pKey + "] threw error:");
e.printStackTrace();
} finally{
numRunning--;
}
}else{
try{
Thread.sleep(50);
continue;
}catch(InterruptedException e){
continue;
}
}
}
System.out.println("Turn " + turn + " over: " + marketValue);
}
System.out.println("End of round market value is: " + marketValue);
int count = 0;
for(String player : vals.keySet()){
totalVals[count] = totalVals[count].add(vals.get(player)[0].add(vals.get(player)[1].multiply(marketValue)));
count++;
}
newValue = BigInteger.valueOf(100);
}
for(int i = 0; i < players.size(); i++){
System.out.println("[" + players.get(i) + "]:$" + (totalVals[i].divide(BigInteger.valueOf(numRounds))));
}
} catch (Exception e) {
System.err.println("An exception occured while running the controller.");
e.printStackTrace();
}
}
public static Random r = new Random(new Date().getTime());
public static int getRandInt(int min, int max){
return r.nextInt(max - min) + min;
}
}
Compile this with java Controller.java
and run from a directory containing a directory like below:
+-- resources
| +-- config
| +-- players
| +-- Player1Folder
| +-- Player1Program
| +-- Player2Folder
| +-- Player2Program
The file config
should look something like this:
3
50
java&-cp&"TestPlayer"&Test1
python&daydreamer/daydreamer.py
java&-cp&"DayTrader"&DayTrader
First number is number of rounds, second number is turns per round, followed by the commands to run each player.
Replace spaces with ampersands! ('&')
~Let me know if I can improve the wording of this post at all, and happy trading!
1I've voted to close since despite attempts to rewrite the controller it appears there is still no way to judge the answers, and there is no meaningful leaderboard. – trichoplax – 2015-08-23T12:20:48.293
4It seems that the long-term trend in a busy market will see the share have a negative value. – Peter Taylor – 2014-08-20T15:47:14.037
Price cannot go below 1. Forgot to mention that. – spocot – 2014-08-20T15:47:53.087
I'm not so sure prices will fall. Players can only buy, not sell, in the first round, right? – xnor – 2014-08-20T16:07:12.107
@xnor you can only sell shares if you own them, the controller checks this, and since the round starts you with 0 shares, you don't have any to sell. If you try to sell more than what you own it is effectively the same as skipping the turn. – spocot – 2014-08-20T16:12:46.627
What happens if you buy more than you can afford? Your very own Test1 program, included in the challenge, does not check for this. It just blindly tries to buy 10 shares. – Rainbolt – 2014-08-20T16:24:42.277
@Rainbolt Controller does a check, and if you can't afford the number you requested, it doesn't sell you any and you effectively skip your turn. My test program is very rudamentary, it willingly skips its turn until it can buy 10 at a time. – spocot – 2014-08-20T16:25:45.277
2@xnor, every share bought increases the price by an average of 3, and every share sold decreases it by an average of 3.5. In the long term in a busy market, the total number of shares currently held will be less than 1/7 of the total number bought in the entire history. – Peter Taylor – 2014-08-20T16:38:59.317
What kind of OS will you be running this on? If it's Linux, can I create a system variable? – Beta Decay – 2014-08-20T16:52:36.513
@BetaDecay It will be running on a fresh Ubuntu VM, so yah go ahead :) – spocot – 2014-08-20T16:58:54.810
@spocot Ah, cheers! – Beta Decay – 2014-08-20T17:00:02.143
Hey, as part of the leaderboard, can you post the stock price? – Beta Decay – 2014-08-20T18:21:10.797
1You could make an accent pun and name this challenge "Stack Market". – cjfaure – 2014-08-20T18:21:57.157
6Could you put your controller program on something like Github and include a link so that we can test at home? – Peter Taylor – 2014-08-20T19:13:06.820
1Is there a way to indicate if it is the first move (or last move) so we could clear our storage? – Moop – 2014-08-20T19:41:40.207
$4713199930331196453 is a very large amount of money - is that an exponential growth rate? – isaacg – 2014-08-20T20:01:47.947
6Looking at the leaderboard, I think the game is currently flawed. E.g., start at $14/sh, buy 357 (own 357, $0 in bank). Choose random number (3). Price goes up by 3357 to $1085/sh. Next round. Sell all 357 shares (own 0, $387,345 in bank). Choose random number (3). Price goes down by 3357 to $14/sh. Next round. After two rounds, the price has not changed and your bank has increased 77x (similar but less dramatic results can be had with other starting random variables). I propose changing to for each transaction instead of for each share for more reasonable values. – None – 2014-08-20T20:28:03.993
agree with @tolos about the flaw. I propose that traders are given a number of shares to start with so that there isn't an inevitable share value increase after the first round. Otherwise, there is no way to disrupt traders using this strategy e.g. traders start with $5000 + 200 shares. – rdans – 2014-08-20T20:34:55.520
@Moop store the number 1 on disk and increment it on every turn. When the file doesn't exist (or the counter is on 151 if the file is still there from a previous run) its the first turn and when your counter gets to 150, its the last turn – rdans – 2014-08-20T20:57:55.670
@ryan I could imagine a bug or two could happen during the competition and then spocot would restart it and mess up the counting system. – Moop – 2014-08-20T21:01:58.867
@Moop (and to the OP) I would expect the text environment to be clean between trials. – Rainbolt – 2014-08-20T21:57:18.973
I suggest to aggressively increase the number of rounds from 3 to 1000000 (or so) to rule out random effects. – Bob Genom – 2014-08-20T21:59:13.667
1In my eyes the minimum price of $1 is a strategic game fault. Any (float) value between $1 and $0 (inclusive) should be allowed as well. Once $0 is touched the price should be trapped at $0 (no escape == crash). – Bob Genom – 2014-08-20T22:14:45.670
1Suggestion for game improvement: To more realistically simulate the market, and prevent the runaway effects we are seeing, I propose that the price be changed after each share, not at day end. Order would be determined by randomly selecting a trader from all traders still attempting to buy or sell, after each share is bought or sold. – isaacg – 2014-08-21T06:11:13.430
4Or just build a real order book. – o0'. – 2014-08-21T08:21:26.783
You should also support selling short, that way there is something to do in the first round besides buying. – durron597 – 2014-08-21T14:10:47.123
1The control program is made in Java. It creates a Process from a ProcessBuilder and passes in the data as args to your program. It then attaches a BufferedReader to the process in order to read the std output from the program. It checks the output to see if it matches 'S>amnt<' or 'B>amnt<'. If it does, it performs checks to make sure the operation is valid. If the operation is not valid or the trader's output does not match what it is looking for, it skips that trader's turn. – spocot – 2014-08-21T14:40:32.603
2@ Everyone
I am in the process of redoing the money system to make exponential growth and market corruption from one trader preventable. I will post the code on github asap. – spocot – 2014-08-21T14:41:49.023
Ok, added Controller program to post. If anyone wants to suggest a better way to calculate something let me know. – spocot – 2014-08-21T15:39:17.850
Traders now start with a random num of shares between 20 and 30 – spocot – 2014-08-21T15:40:35.053
3Why start with a random num of shares? – Averroes – 2014-08-22T06:50:09.287
Do the people who have already played a round start afresh or is it just the newcomers? – Beta Decay – 2014-08-22T07:17:17.027
1Notes: 1. People wanting to test under Linux will need to remove the double-quotes from the Java examples in the config file. 2. The market price changes aren't what's documented in the spec. 3. If
Controller.getRandInt
is intended to be able to returnmax
then it's buggy. 4.MAX_RUNNING
is pointless because there's no multi-threading. 5. The controller has no real error handling, so when TestPlayer starts dying because the stock price is too hard, the controller spams error messages to the console. – Peter Taylor – 2014-08-22T23:05:55.9402And where's the new leaderboard? – Sean D – 2014-08-22T23:38:23.413
I've tidied up the code and done three runs.
– Peter Taylor – 2014-08-23T00:30:11.810@PeterTaylor The amount of money people are earning seems amazingly high... – Beta Decay – 2014-08-23T08:16:00.547
@BetaDecay, the market is broken. It takes 4 turns for the share value to start bouncing between the two caps (
1
and2147483646
). – Peter Taylor – 2014-08-23T08:36:40.527I've created a chat to discuss how the market should be fixed
– Peter Taylor – 2014-08-23T08:49:20.870When's the next run going to be? – Beta Decay – 2014-08-27T09:46:31.387