JSFiddle - React, Tailwind, and code Playground
by Gwyn Milcote
HTML
<h2>Stock Market Simulator</h2>
<p>Demo made for Ittermat's game. Heavily based on the Neopets stock market minigame. You begin with a random portfolio and ten shops to browse. Buy and sell stocks each day to make a profit.</p>
Day <span id='sm-status-day'>7</span>.
<button id='sm-advance-btn'>Advance 1 day</button>
<button id='sm-reset-btn'>Reset simulation</button>
<br>
You have <span id='sm-status-coins'>500</span> coins, and have bought <span id='sm-status-stocksToday'>0</span>/100 stocks today.
<br>
In total, you have spent <span id='sm-status-totalSpent'>0</span> and earned <span id='sm-status-totalEarned'>0</span> coins. That's a profit of <span id='sm-status-profitCoins'>0</span> coins, or <span id='sm-status-profitPercent'>0%</span>.
<hr>
<span id='sm-message'>[Messages will show here]</span>
<hr>
<b>Current Shop Status</b>
<p>Click on a shop name to see its stock history for the last 7 days.</p>
<table id='sm-shops'></table>
<ul id='sm-history-price'></ul>
<ul id='sm-history-buysell'></ul>
<hr>
<b>My Portfolio</b>
<p>To sell stocks, click a row. You purchased the stocks in batches at a time, so can sell stocks from each batch.</p>
<table id='sm-portfolio'></table>
<table id='sm-sell-batches'></table>
<button id='sm-sell-btn'>Sell</button>
<hr>
<b>My History</b>
<p>Everything you've bought and sold in the last 14 days. (First 7 days are randomly generated, you begin with those shares)</p>
<ul id='sm-user-history'></ul>
CSS
table {
border-collapse: collapse;
width: 100%;
max-width: 900px;
margin: 20px auto;
}
table th {
background-color: silver;
}
table td {
padding: 5px;
border: 1px solid silver;
}
table tr:nth-child(even) {
background-color: #eee;
}
table input {
width: 50px;
}
#sm-shops td:nth-child(3) {
width: 50px;
}
#sm-shops td:nth-child(4) {
width: 50px;
}
#sm-shops td:nth-child(5) {
width: 70px;
}
/* Reveal later, when data-filled */
#sm-sell-btn, #sm-sell-batches, #sm-history-price, #sm-history-buysell {
display: none;
}
.sm-shop-name:hover {
cursor: pointer;
background-color: lightblue;
}
.sm-text-positive {
color: green;
}
.sm-text-negative {
color: red;
}
.sm-bg-positive {
background-color: lightgreen;
}
.sm-bg-negative {
background-color: lightpink;
}
JavaScript
let db = {
// Virtual companies. Will have randomised prices.
shops: [
{id: 1, name: "Brie the Blacksmith", stockPrice: 0},
{id: 2, name: "Shining Armoury", stockPrice: 0},
{id: 3, name: "The Fruit Bowl", stockPrice: 0},
{id: 4, name: "Onyx and Pearl", stockPrice: 0},
{id: 5, name: "Everything Equine", stockPrice: 0},
{id: 6, name: "Sky Emporium", stockPrice: 0},
{id: 7, name: "Dragon's Dungeon", stockPrice: 0},
{id: 8, name: "Saddles and Shoes", stockPrice: 0},
{id: 9, name: "The Beautiful Boys", stockPrice: 0},
{id: 10, name: "The Sweaty Smelter", stockPrice: 0}
],
// Which shares are owned by which users.
// They're grouped at the time of purchase, so
// price-paid-at-the-time can be saved for later.
stocksOwned: [
{id: 1, userId: 1, shopId: 2, qty: 6, price: 0, day: 1},
{id: 2, userId: 1, shopId: 2, qty: 25, price: 0, day: 3}
],
// Just keeping track.
stocksSold: [
{id: 1, userId: 1, shopId: 5, qty: 17, price: 53, day: 4}
],
// Track how % and prices changed each day.
// Old records are deleted after 7 days.
stockHistory: [
{shopId: 1, percentChange: 2, newPrice: 5, day: 0},
{shopId: 2, percentChange: -3, newPrice: 7, day: 0}
],
// Simulate currency, buying limits.
users: [
{id: 1, coins: 500, stocksToday: 0, totalSpent: 0, totalEarned: 0}
]
};
function randomNumber(n1, n2){
if(n1 == n2){ return n1; }
let min = n1, max = n2;
if(n1 > n2){ min = n2; max = n1; }
return Math.floor(Math.random() * (max - min + 1) + min);
}
function randomArray(array){
return array[Math.floor(Math.random() * array.length)];
}
class StockMarketDemo {
constructor(){
this.limitPerShop = 300;
this.limitPerDay = 100;
this.minimumQty = 5;
// Fill tables with random starting data.
// Generate 7 days...