2022-09-27 16:45:50 -04:00
const { Constants , MessageEmbed , Interaction } = require ( 'discord.js' ) ;
2022-08-18 14:25:25 -07:00
const dateFns = require ( 'date-fns' ) ;
const { formatToTimeZone } = require ( 'date-fns-timezone' ) ;
const Alpaca = require ( '@alpacahq/alpaca-trade-api' ) ;
2022-08-18 16:29:47 -07:00
const apiKeyId = process . env . alpKey || require ( '../../config.json' ) . alpKey ;
const secretKey = process . env . alpSec || require ( '../../config.json' ) . alpSec ;
2022-08-18 14:25:25 -07:00
const alpaca = new Alpaca ( {
keyId : apiKeyId ,
secretKey : secretKey ,
paper : true ,
usePolygon : false
} ) ;
//This is the same as making the following request: https://data.alpaca.markets/v2/stocks/snapshots?symbols={stock_symbols_here}
2022-09-27 16:45:50 -04:00
async function getStockData ( bot , interaction , stock , type , after ) {
2022-08-18 14:25:25 -07:00
try {
const snapshotPromise = alpaca . getSnapshot ( stock ) ;
snapshotPromise . then ( snapshot => {
const embd = new MessageEmbed ( )
. setAuthor ( { name : "Selmer Bot" , url : "" , iconURL : bot . user . displayAvatarURL ( ) } )
. setFooter ( { text : 'Selmer Bot uses Alpaca for stock information' } )
2022-09-27 16:45:50 -04:00
if ( type ) {
2022-08-18 14:25:25 -07:00
const lt = snapshot . LatestTrade ;
embd . setTitle ( ` ${ stock } Latest Trade ` )
. setTimestamp ( lt . Timestamp )
. addFields (
{ name : 'Price' , value : ` ${ lt . Price } ` } ,
{ name : 'Size' , value : ` ${ lt . Size } ` } ,
//This will always be IEX, as it is the only exchange the free version offers
{ name : 'Exchange' , value : ` IEX ( ${ lt . Exchange } ) ` } ,
)
2022-09-27 16:45:50 -04:00
} else if ( type ) {
if ( after ) {
return interaction . reply ( "Due to the markets not being open, there is no quote data available!" ) ;
2022-08-18 14:25:25 -07:00
}
const lq = snapshot . LatestQuote ;
embd . setTitle ( ` ${ stock } Latest Quote ` )
. setTimestamp ( lq . Timestamp )
. addFields (
{ name : 'Ask Price' , value : ` ${ lq . AskPrice } ` } ,
{ name : 'Ask Size' , value : ` ${ lq . AskSize } ` } ,
{ name : 'Bid price' , value : ` ${ lq . BidPrice } ` } ,
{ name : 'Bid Size' , value : ` ${ lq . BidSize } ` } ,
//This will always be IEX, as it is the only exchange the free version offers
{ name : 'Exchange' , value : ` IEX ( ${ lq . Exchange } ) ` } ,
)
2022-09-27 16:45:50 -04:00
} else if ( type ) {
2022-08-18 14:25:25 -07:00
const mb = snapshot . MinuteBar ;
const db = snapshot . DailyBar ;
const pdb = snapshot . PrevDailyBar ;
embd . setTitle ( ` ${ stock } Bars ` )
. setTimestamp ( mb . Timestamp )
. addFields (
{ name : 'Minute Bar' , value : ` Open Price: ${ mb . OpenPrice } , \n Close Price: ${ mb . ClosePrice } , \n High Price: ${ mb . HighPrice } , \n Low Price: ${ mb . LowPrice } , \n Volume: ${ mb . Volume } , \n Count: ${ mb . TradeCount } , \n VWAP: ${ mb . VWAP } ` } ,
{ name : 'Day Bar (Today)' , value : ` Open Price: ${ db . OpenPrice } , \n Close Price: ${ db . ClosePrice } , \n High Price: ${ db . HighPrice } , \n Low Price: ${ db . LowPrice } , \n Volume: ${ db . Volume } , \n Count: ${ db . TradeCount } , \n VWAP: ${ db . VWAP } ` } ,
{ name : 'Day Bar (Yesterday)' , value : ` Open Price: ${ pdb . OpenPrice } , \n Close Price: ${ pdb . ClosePrice } , \n High Price: ${ pdb . HighPrice } , \n Low Price: ${ pdb . LowPrice } , \n Volume: ${ pdb . Volume } , \n Count: ${ pdb . TradeCount } , \n VWAP: ${ pdb . VWAP } ` } ,
)
} else {
2022-09-27 16:45:50 -04:00
return interaction . reply ( "The command format is: _/stocks <stock_name, 'hours'> <trade, quote, bars> [after]_" ) ;
2022-08-18 14:25:25 -07:00
}
2022-09-27 16:45:50 -04:00
interaction . reply ( { embeds : [ embd ] } ) ;
2022-08-18 14:25:25 -07:00
} )
} catch ( err ) {
console . error ( err ) ;
2022-09-27 16:45:50 -04:00
interaction . reply ( "Uh Oh, there's been an error!" ) ;
2022-08-18 14:25:25 -07:00
}
}
2022-09-27 16:45:50 -04:00
function getData ( bot , interaction ) {
const args = interaction . options . data ;
const stock = args . filter ( ( arg ) => { return ( arg . name == 'name' ) } ) [ 0 ] . value ;
const type = args . filter ( ( arg ) => { return ( arg . name == 'type' ) } ) [ 0 ] . value ;
const after = ( args . length > 2 && args . filter ( ( arg ) => { return ( arg . name == 'after' ) } ) [ 0 ] . value ) ;
2022-08-18 14:25:25 -07:00
const format = ` yyyy-MM-dd HH:mm:ss ` ;
const date = dateFns . format ( new Date ( ) , format ) ;
alpaca . getClock ( ) . then ( ( clock ) => {
// var txt = `The market is currently ${clock.is_open ? 'open.' : 'closed.'}`;
alpaca . getCalendar ( {
start : date ,
end : date
} ) . then ( ( calendars ) => {
let temp ;
2022-09-27 16:45:50 -04:00
if ( clock . is _open || after ) {
if ( stock == 'hours' ) {
2022-08-18 14:25:25 -07:00
temp = ` The markets opened at ${ calendars [ 0 ] . open } and will close at ${ calendars [ 0 ] . close } on ${ date } . ` ;
2022-09-27 16:45:50 -04:00
return interaction . reply ( temp ) ;
2022-08-18 14:25:25 -07:00
}
2022-09-27 16:45:50 -04:00
getStockData ( bot , interaction , stock , type , after ) ;
2022-08-18 14:25:25 -07:00
} else {
// `The market is currently ${clock.is_open ? 'open.' : 'closed.'}`
//May be innacurate?
2022-09-27 16:45:50 -04:00
temp = ` _The markets closed at \` ${ calendars [ 0 ] . close } \` and will open again at \` ${ calendars [ 0 ] . open } \` on \` ${ dateFns . format ( ( new Date ( ) ) . setDate ( new Date ( ) . getDate ( ) + 1 ) , 'yyyy-MM-dd' ) } \` . \n To get the last snapshot before market closure, add the \` after \` keyword to the end of your command (trade and bars ONLY), ex: /stocks GOOG bars after_ ` ;
return interaction . reply ( temp ) ;
2022-08-18 14:25:25 -07:00
}
} ) ;
} )
}
//!stocks <stock_name, "hours"> <trade, quote, bars> [after]
module . exports = {
name : 'stocks' ,
description : "Have Selmer Bot give you \"current\" stock prices" ,
2022-09-27 16:45:50 -04:00
async execute ( interaction , Discord , Client , bot ) {
getData ( bot , interaction ) ;
} ,
options : [
{ name : 'name' , description : 'the stock name or "hours" for market hours' , type : Constants . ApplicationCommandOptionTypes . STRING , required : true } ,
{ name : 'type' , description : 'The type of data to present' , type : Constants . ApplicationCommandOptionTypes . STRING , required : true , choices : [ { name : 'trade' , value : 'trade' } , { name : 'quote' , value : 'quote' } , { name : 'bars' , value : 'bars' } ] } ,
{ name : 'after' , description : 'If the markets are closed, get the last entry' , type : Constants . ApplicationCommandOptionTypes . BOOLEAN , required : false } ,
]
2022-08-18 14:25:25 -07:00
}