From 2ef62782e265824f3aecd2f8af6a1734b4dbef14 Mon Sep 17 00:00:00 2001 From: Chris Vanderloo Date: Tue, 7 Aug 2018 19:59:42 -0400 Subject: [PATCH] Basic server functionality --- server/algos/index.js | 3 ++ server/api.js | 79 +++++++++++++++++++++++++++++++++ server/app.js | 100 ++++++++++++++++++++++++++++++++++++++++++ server/functions.js | 23 ++++++++++ server/trader.js | 44 +++++++++++++++++++ 5 files changed, 249 insertions(+) create mode 100644 server/algos/index.js create mode 100644 server/api.js create mode 100644 server/app.js create mode 100644 server/functions.js create mode 100644 server/trader.js diff --git a/server/algos/index.js b/server/algos/index.js new file mode 100644 index 0000000..1df103a --- /dev/null +++ b/server/algos/index.js @@ -0,0 +1,3 @@ +module.exports = { + +} \ No newline at end of file diff --git a/server/api.js b/server/api.js new file mode 100644 index 0000000..b060f80 --- /dev/null +++ b/server/api.js @@ -0,0 +1,79 @@ +const request = require("request"); +const locals = require("../locals.json"); + +module.exports = { + + // GENERAL + get: (url, next) => request(url, (err, res, body) => next(err, res, body)), + + // ROBINHOOD + robinhood: { + login: (user, pass, next) => { + request({ + uri: "https://api.robinhood.com/api-token-auth/", + method: "POST", + form: { + username: user, + password: pass + } + }, (err, res, body) => next(err, res, body)); + }, + + logout: (token, next) => { + request({ + uri: "https://api.robinhood.com/api-token-logout/", + method: "POST", + headers: { + "Authorization": "Token " + token + } + }, (err, res, body) => next(err, res, body)); + }, + + user: (token, next) => { + request({ + uri: "https://api.robinhood.com/user/", + method: "GET", + headers: { + "Authorization": "Token " + token + } + }, (err, res, body) => next(err, res, body)); + }, + + position: (token, id, next) => { + request({ + uri: "https://api.robinhood.com/accounts/"+id+"/positions", + method: "GET", + headers: { + "Authorization": "Token " + token + } + }, (err, res, body) => next(err, res, body)); + }, + + get: (token, url, next) => { + request({ + uri: url, + method: "GET", + headers: { + "Authorization": "Token " + token + } + }, (err, res, body) => next(err, res, body)); + }, + }, + + // ALPHA VANTAGE + + alphavantage: { + crypto_intra: (symbol, next) => { + request({ + uri: "https://www.alphavantage.co/query", + method: "GET", + qs: { + "function": "DIGITAL_CURRENCY_INTRADAY", + "symbol": (symbol) ? symbol : "BTC", + "market": "USD", + "apikey": locals.alpha_vantage + } + }, (err, res, body) => next(err, res, body)); + } + } +} \ No newline at end of file diff --git a/server/app.js b/server/app.js new file mode 100644 index 0000000..0cc8f7d --- /dev/null +++ b/server/app.js @@ -0,0 +1,100 @@ +const request = require("request"); +const express = require("express"); +const cookieParser = require("cookie-parser"); +const path = require('path'); +const bodyParser = require('body-parser'); +const api = require('./api.js'); +const robinhood = api.robinhood; +const trader = require('./trader.js'); + +var app = express(); + +// TEMPLATING + +app.set('views', path.join(__dirname, '../client/views')); +app.set('view engine', 'pug'); + +// GLOBAL MIDDLEWARE + +app.use(express.static(path.join(__dirname, '../client/public'))); +app.use(bodyParser.urlencoded({ extended: true })); +app.use(cookieParser()); + +// SPECIFIC MIDDLEWARE + +function verifyAuth(req,res,next) { + if (req.cookies.token !== "" && req.cookies.token) { + // We have token, start the automated trader + // trader(api, req.cookies.token); + next(); + } + else res.redirect('/login'); +} + +// PAGES + +app.get("/", verifyAuth, (req,res) => robinhood.user(req.cookies.token, (err, apiRes, body) => { + if (err) res.status(403).send(err); + else { + res.cookie("account_id",JSON.parse(body).id); + res.render('index', { + user: JSON.parse(body), + }); + } +})); + +app.get("/login", (req,res) => res.render('login')); + +// API + +app.post("/login", (req,res) => +robinhood.login(req.body.user, req.body.pass, (err, apiRes, body) => { + if (err) res.status(403).send(err); + if (!JSON.parse(body).token) + res.status(401).render('login',{ + error: JSON.parse(body).non_field_errors + }); + else { + console.log(JSON.parse(body).token) + robinhood.get(JSON.parse(body).token, "https://api.robinhood.com/accounts/", (errAc, apiResAc, bodyAc) => { + res.cookie("userID", JSON.parse(bodyAc).results[0].account_number); + res.cookie("token",JSON.parse(body).token); + res.redirect("/"); + }); + } +})); + +app.get("/logout", (req,res) => robinhood.logout(req.cookies.token, (err, apiRes, body) => { + res.cookie("token",""); + res.cookie("account_id",""); + if (err) res.status(403).send(err); + else res.redirect("/"); +})); + +app.get("/positions", (req,res) => { + robinhood.position(req.cookies.token, req.cookies.userID, (err, apiRes, body) => { + if (err) res.status(403).send(err); + else res.send(body); + }); +}); + +app.get("/accounts", (req,res) => { + robinhood.get(req.cookies.token, "https://api.robinhood.com/accounts/", (err, apiRes, body) => { + if (err) res.status(403).send(err); + else res.send(body); + }); +}); + +app.get("/instrument", (req,res) => { + robinhood.get(req.cookies.token, req.query.url, (err, apiRes, body) => { + if (err) res.status(403).send(err); + else res.send(body); + }); +}); + +// SERVER + +app.listen(8080, () => { + console.log("Listening on port 8080"); +}); + diff --git a/server/functions.js b/server/functions.js new file mode 100644 index 0000000..42d0eac --- /dev/null +++ b/server/functions.js @@ -0,0 +1,23 @@ +function round(num) { + return (Math.round(100*num)/100).toFixed(2); +} + +module.exports = { + + // PARAMETERS + // input: an array of data points to calculate upon + // size: the number of data points to create each moving average + // + // RETURNS + // an array of moving averages of the size of the input minus the + // number of data points + + sma: (input, size) => { + var result = Array.from({ length: input.length - size }, () => 0); + var count = 0; + for (var i = 0; i < size; i++) result[0] += input[i]; + for (var i = 1; i < result.length; i++) result[i] = result[i-1] - input[i-1] + input[i-1+size]; + for (var i = 0; i < result.length; i++) result[i] = round(result[i] / size); + return result; + }, +} \ No newline at end of file diff --git a/server/trader.js b/server/trader.js new file mode 100644 index 0000000..3761c48 --- /dev/null +++ b/server/trader.js @@ -0,0 +1,44 @@ +const TRADE_INTERVAL = 180 * 1000; +const SMA_TO_CALC = [50,100,200,400]; +const DRY = true; + +const f = require('./functions.js'); + +// "rh" is Robinhood API, "av" is Alpha Vantage +module.exports = (api,rh_token,callback) => { + if (DRY) rh_token = undefined; + + var rh = api.robinhood; + var av = api.alphavantage; + + console.log("Initializing automated trading...\n"); + console.log("> Trading interval set to " + TRADE_INTERVAL + "ms"); + + function processData() { + av.crypto_intra("BTC", (err,res,body) => { + var json = JSON.parse(body)["Time Series (Digital Currency Intraday)"]; + if (json) { + var array_of_nums = Array.from({ length: Object.keys(json).length }, () => 0); + var result = Array.from({ length: SMA_TO_CALC.length }, () => 0); + var i = 0; + for (var date in json) { + array_of_nums[i] = Number.parseFloat(json[date]["1a. price (USD)"]); + i++; + } + + for (var i=0; i SMA " + SMA_TO_CALC[i] + ": " + result[i][result.length-1]); + } + callback(array_of_nums, result); + } + else console.log("> ERROR: JSON was null"); + api.get("https://min-api.cryptocompare.com/data/pricemulti?fsyms=BTC&tsyms=USD", (err, res, body) => { + console.log("> Current Price: " + JSON.parse(res.body).BTC.USD); + }); + }); + }; + + processData(); + setInterval(processData, TRADE_INTERVAL); +} \ No newline at end of file -- 2.51.2