The project has been moving along well, with progress being done on all fronts. The simple Node backend has been complete with the Alpaca trading API, and looks as follows:
import express from 'express'
import cors from 'cors'
const Alpaca = require('@alpacahq/alpaca-trade-api')
const fs = require('fs')
require('dotenv').config()
const app = express()
const port = 8000
const stocklist = [];
let alpaca;
let startval;
if(process.env.ENV==='DEVELOPMENT'){
alpaca = new Alpaca({
keyId: process.env.TEST_ALPACA_KEY_ID,
secretKey: process.env.TEST_ALPACA_SECRET_KEY,
paper: true,
})
startval = 100000;
}else if(process.env.ENV==='PRODUCTION'){
alpaca = new Alpaca({
keyId: process.env.ALPACA_KEY_ID,
secretKey: process.env.ALPACA_SECRET_KEY,
paper: false,
})
}else{
throw new Error("Error: Environment Not Properly Selected")
}
//read stocklist
fs.readFile('stocklist.txt', function(err, data) {
if(err) throw err;
var array = data.toString().split("\n");
for(const stock of array) {
const [symbol, name] = stock.split(' ')
if(symbol&&name){
let currstock ={
symbol,
name:name.replace("\"","")
}
stocklist.push(currstock)
}
}
});
app.use(express.json())
app.use(cors())
app.post('/buy_order', async(req, res)=>{
try{
const {ticker} = req.body;
const order = await alpaca.createOrder({
symbol:ticker,
qty:1,
side: 'buy',
type:'market',
time_in_force:'day'
})
res.json({order})
}catch(e){
console.log(e)
res.status(400).send('err')
}
})
app.post('/sell_all', async(req, res)=>{
try{
await alpaca.closeAllPositions();
res.json({'success':'success'})
}catch(e){
console.log(e)
}
})
app.get('/timeline', async(req, res)=>{
try{
const history = await alpaca.getPortfolioHistory({
period: '1D',
timeframe:'1Min'
})
const last_elem = await history.equity.findIndex(e=>e===null);
const cut_history = {
equity:history.equity.slice(0,last_elem),
profit_loss: history.profit_loss[last_elem-1],
profit_loss_pct: history.profit_loss_pct[last_elem-1]
};
res.json({...cut_history})
}catch(e){
console.log(e)
res.send('err')
}
})
app.get('/positions', async(req, res)=>{
try{
const positions = await alpaca.getPositions();
res.json(positions)
}catch(e){
res.send('err')
}
})
app.get('/randtickers', async(req,res)=>{
try{
const rand1 = Math.floor(stocklist.length * Math.random());
let rand2 = Math.floor(stocklist.length * Math.random());
while(rand2===rand1){
rand2 = Math.floor(stocklist.length * Math.random());
}
res.json({tickers: [stocklist[rand1],stocklist[rand2]]})
}catch(e){
res.status(400).send('err')
}
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
The data for the stocks is manipulated and returned from the server between 5:30pm-12:00am GST.
On the p5js end, we have created the functions to get, post, and display data. It looks like this when the stock market is open:
let equity;
let profit_loss;
let profit_loss_pct;
let tickers = [{symbol:"",name:""},{symbol:"",name:""}];
const FPS = 60;
function setup() {
updateEquity()
.then(()=>getTickers())
.then(data=>tickers=data.tickers)
createCanvas(640,320)
}
function draw() {
background(220);
if(frameCount%FPS*60===0){
updateEquity();
}
if(frameCount%FPS===0){
getTickers().then(data=>{tickers=data.tickers;});
}
push();
fill(255,0,0)
rect(0,20+height/4,1*width/3,3*height/4-20)
fill(100,100,100)
rect(width/3,20+height/4,1*width/3,3*height/4-20)
fill(0,0,255)
rect(2*width/3,20+height/4,1*width/3,3*height/4-20)
pop();
push();
fill(255)
noStroke();
textSize(24)
textAlign(CENTER)
text(tickers[0].symbol, width/6, 2*height/4)
text(tickers[0].name, width/6, 3*height/4)
text("SKIP", 2*width/4, 11*height/16)
fill(255)
text(tickers[1].symbol, 5*width/6, 2*height/4)
text(tickers[1].name, 5*width/6, 3*height/4)
pop();
plotTimeline(equity);
push();
textSize(18);
fill(profit_loss<0?255:0,profit_loss>0?255:0,0)
text(`${profit_loss?'$'+profit_loss.toString():""}`, 5*width/6+10, height/7)
text(`${profit_loss_pct?(profit_loss_pct*100).toString().slice(0,7)+"%":""}`, 5*width/6+10, height/7+20)
pop();
}
async function makeOrder(ticker){
try{
const data = await fetch('http://localhost:8000/buy_order',{
method:"POST",
headers : {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body:JSON.stringify({ticker})
})
jsonData = await data.json();
return jsonData;
}catch(e){
console.log(e)
}
}
async function sellAll(){
try{
await fetch('http://localhost:8000/sell_all',{
method:"POST",
headers : {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
})
return 'success'
}catch(e){
console.log(e)
}
}
async function getTimeline(){
try{
const data = await fetch('http://localhost:8000/timeline')
const jsonData = await data.json()
return jsonData;
}catch(e){
console.log(e)
}
}
async function getTickers(){
try{
const data = await fetch('http://localhost:8000/randtickers')
return data.json()
}catch(e){
console.log(e)
}
}
function plotTimeline(equity){
if(!equity){
return;
}
const Emin = Math.min(...equity);
const Emax = Math.max(...equity);
const maxGraphHeight = height/4;
const plotEquity = [];
push();
noFill();
stroke(0)
beginShape();
for(let i =0; i<equity.length; i++){
vertex(map(i,0,equity.length, 10, 5*width/6),map(equity[i], Emin, Emax, 10, maxGraphHeight))
}
endShape();
pop();
}
function updateEquity(){
getTimeline().then(resp=>{
equity = resp.equity;
profit_loss = resp.profit_loss
profit_loss_pct = resp.profit_loss_pct
})
}
The graph at the top shows the progress made, and the numbers on the right show current profits. The 3 bars in the middle display the current options.
We have been also working on the physical computing end, where we’re testing implementations for our beloved airhorn.
To wrap up our project, we have to hook up the hardware and software end, which is the “hardest” 10%, as the professor mentioned in class