· 7 years ago · Dec 06, 2018, 11:42 AM
1const Alpaca = require('@alpacahq/alpaca-trade-api')
2// minimist is used to parse command line arguments
3const minimist = require('minimist')
4const minimistOpts = require('minimist-options')
5// conf helps with saving config options to a file
6const Conf = require('conf')
7const config = new Conf({ configName: 'alpacacli' })
8
9const alpaca = new Alpaca({
10 keyId: config.get('keyId'),
11 secretKey: config.get('secretKey')
12})
13
14const cliOptions = minimistOpts({
15 type: { type: 'string' },
16 'time-in-force': { type: 'string' },
17 'limit-price': { type: 'number' },
18 'stop-price': { type: 'number' },
19 'client-order-id': { type: 'string' }
20})
21
22const args = minimist(process.argv.slice(2), cliOptions)
23
24// The exclamation here is a shortcut to call the function.
25// This is where the work actually starts.
26!async function() {
27 try {
28 // args._ is an array containing the sequential command line args
29 let [side, qty, symbol] = args._
30 if (!symbol) {
31 // allow omitting the qty, default to 1
32 symbol = qty
33 qty = 1
34 }
35 const order = await placeOrder(side, qty, symbol, args)
36 const description = await generateFancyOrderDescription(order)
37 console.log(description)
38 } catch (err) {
39 console.error(err.message)
40 }
41}()
42
43async function placeOrder(side, qty, symbol, options) {
44 return alpaca.createOrder({
45 symbol,
46 qty,
47 side,
48 type: options.type || 'market',
49 time_in_force: options['time-in-force'] || 'gtc', // good til canceled
50 limit_price: options['limit-price'],
51 stop_price: options['stop-price'],
52 client_order_id: options['client-order-id']
53 })
54}
55
56// The most complicated part is actually just making some pretty output.
57// The things we display will change based on the type of order being made.
58// If this is a market order, we will actually have to fetch the current price.
59async function generateFancyOrderDescription (order) {
60 const priceText = order.type === 'stop_limit' ? `between $${order.stop_price} and $${order.limit_price}`
61 : order.type === 'limit' ? `at $${order.limit_price}`
62 : order.type === 'stop' ? `at $${order.stop_price}`
63 // If it's a market order, we didn't actually specify a price, so we have to get that from the api
64 : `at $${await getMarketPrice(order.symbol)}`
65 const sharesText = order.qty > 1 ? 'shares' : 'share'
66
67 return `Placed a ${order.type} order to ${order.side} ${order.qty} ${sharesText} of ${order.symbol} ${priceText}.
68
69 order id: ${order.id}`
70}
71
72async function getMarketPrice (symbol) {
73 const latestBars = await alpaca.getBars('minute', symbol, { limit: 1 })
74 // getBars returns a map of arrays of price data,
75 // but we are only fetching one bar for one symbol.
76 return latestBars[symbol][0].c
77}