-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.js
More file actions
186 lines (171 loc) · 6.4 KB
/
server.js
File metadata and controls
186 lines (171 loc) · 6.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/*
* Created by: Laurens (github.com/razpudding)
*/
const express = require('express')
const session = require('express-session')
const bodyParser = require('body-parser')
const path = require('path');
const SSE = require('express-sse')
const moment = require('moment')
const sse = new SSE([]);
require('dotenv').config()
const config = {
tickInterval : 1000,
upTick: 2.5,
downTick: 8,
historyLength: 100,
maxMultiplier: 200,
dataPointLimit: 100
}
let data = [
{
stocks: [
{name:"communism", close:.5, trend:"off"},
{name:"capitalism", close:.7, trend:"off"},
{name:"egocentrism", close:.8, trend:"off"},
{name:"love", close:1.1, trend:"up"},
{name:"oil", close:.9, trend:"up"},
{name:"bitcoin", close:1.0, trend:"up"},
{name:"lsd", close:1.1, trend:"up"}
],
date: moment().add(0,'minutes'),
avg: '0.67',
},
]
let manips = {} //TODO: have this be not global but a param to a function
let maxVals = {}
data[0].stocks.forEach( stock => maxVals[stock.name] = stock.close* config.maxMultiplier)
console.log(maxVals)
//maxVals.stocks.forEach( stock => stock.close *= 10)
express()
.use(session({
secret: process.env.PASS,
cookie: { maxAge: 60000 },
resave: false,
saveUninitialized: true
}))
.use(express.static('static'))
.set('view engine', 'ejs')
.set('views', 'views')
.use(bodyParser.urlencoded({extended: true}))
.get('/', home)
.get('/stats', stats)
.get('/stream', stream)
.get('/change', changePage)
.post('/', changeData)
.listen(8000)
//Send home.html
function home(req, res){
res.sendFile(path.join(__dirname + '/static/home.html'));
}
//Send home.html
function changePage(req, res){
res.render('change.ejs', {
stocks: data[data.length-1].stocks, //Send the last datapoint over
trends: ["up","down","off", "on"],
})
}
function changeData(req,res)
{
let input = req.body
console.log(input)
console.log(input.trend)
if (input.password == process.env.PASS){
req.session.authenticated = true
console.log("ID", req.sessionID, "is now authenticated")
}
if (req.session.authenticated === true){
manips[input.stocks] = {trend: input.trend, ticks: input.ticks}
console.log(manips)
if (input.trend == "off" || input.trend == "on"){
setTimeout(() => {
sse.send(1, "reload") //send a reload event to the client AFTER the next tick has been sent so it know which graphs to show
}, config.tickInterval + 1000)
}
}
else { console.log("wrong password") }
changePage(req,res) //Reload the page
//TODO: finish this functionality. It should work something like this:
// simple version: follow trend for var ticks or seconds
// better version: follow trend until %change has been reached
// Improve frontend so it will allow for more elaborate manipulations.
// Add a secret code to .env that needs to be entered every time manip is done
// Also the add button shouldnt try to route away from the page
}
//Send stats.html
function stats(req, res){
console.log("serving stats")
res.sendFile(path.join(__dirname + '/static/stats.html'));
}
//Initialise server-sent-events and send the initial data
function stream(req,res){
console.log("Sending initial data")
sse.init(req,res)
sse.send(data.slice(data.length - config.historyLength));
//Start the trend generator
try {
trendGenerator()
}
catch(error){
console.log(error)
}
}
let trend //Initialise trend
//This function will generate a trend and send the next "tick" of data.
// TODO: Add major events (that will cause a collapse or sprint of 20-70%)
// Make the actual growth percentage semi-random for more natural patterns
function trendGenerator(type){
if (trend) { throw 'Error: Already running a simulation'; }
trend = "up" //TODO: old functionality, change the name
setInterval(() => {
let last = data[data.length -1]
let tick = {}
tick.date = last.date.add(1,'minutes')
tick.stocks = last.stocks.map(stock => getTickValue(stock))
console.log("tick", tick.stocks)
let sum = 0;
tick.stocks.forEach(stock => sum+= stock.close)
tick.avg = Math.round(sum/tick.stocks.length * 10) / 10 //TODO: set up dynamic round through config -> DRY
data.push(tick)
//Makes sure theres no memory leak caused by filling up memory with unneeded data
if (data.length > config.dataPointLimit) {
data.shift()
}
sse.send(data[data.length -1], "tick");
}, config.tickInterval);
}
//Should prob be a mini fnction for determining the trend and one for the calc. close value
//TODO: rewrite this function so it's less ugly. Pass manips as an object
function getTickValue(stock){
if (manips[stock.name] && manips[stock.name].trend == "on") stock.trend = "up" //Turn the stock on!
if (manips[stock.name] && manips[stock.name].trend == "off") stock.trend = "off" //Turn the stock off!
if(stock.trend == "off") return stock //If a stock is turned off, dont generate a new value for it
if (manips[stock.name] && manips[stock.name].ticks > 0 && (manips[stock.name].trend == "up" || manips[stock.name].trend == "down")) {
stock.trend = manips[stock.name].trend
console.log("manip trend", stock.name, stock.trend, manips[stock.name].ticks)
manips[stock.name].ticks --
}
//Basic logic for manipulating stocks
if (rn(30) ){ //About 3/10 loops will trigger a reroll for the trend
//console.log("trendSwitch?", trend)
stock.trend = rn(20) ? "down" : "up" //80% chance the stock will go up
}
if (stock.trend == "up"){
stock.close *= 1 + (config.upTick + (rn(50) ? r(config.upTick) : - r(config.upTick) ))/100 //uptick +/- random() * uptick
stock.close = stock.close >= maxVals[stock.name] ? maxVals[stock.name] : stock.close
//console.log((config.upTick + (rn(50) ? r(config.upTick) : - r(config.upTick)))/100)
}
else {
stock.close /= 1 + (config.downTick + (rn(50) ? r(config.downTick) : - r(config.downTick) ))/100
stock.close = stock.close < 1.1 ? 1.1 : stock.close //Had to add this line because if the close < 1 for some reason it wont correct up anymore, maybe a rounding issue?
//console.log((config.downTick + (rn(50) ? r(config.downTick) : - r(config.downTick) ))/100)
}
stock.close = Math.round(stock.close * 100) / 100 //Do this to round to one decimal
return stock
}
function rn(chance){
return Math.random() < chance/100
}
function r(high, low = 0){
return low + Math.random() * (high - low)
}