Giter VIP home page Giter VIP logo

alpaca-ts's Introduction

alpaca

version code build prettier

A TypeScript Node.js library for the https://alpaca.markets REST API and WebSocket streams.

Contents

Features

  • Fully typed.
  • Fully asynchronous promise based API.
  • Extensible AlpacaClient and AlpacaStream classes.
  • Built-in rate limiting.
  • Built-in number and date parsing.
  • A 1:1 mapping of the official Alpaca docs.
  • Auto-transpiled modern ESM alternative.
  • OAuth integration support.
  • Minified and non-minified bundles.
  • Various bundles provided:
    • alpaca.js - ESM bundle (for node)
    • alpaca.bundle.js - ESM bundle with dependencies (for node)
    • alpaca.browser.js - UMD bundle (for browser)
    • alpaca.browser.modern.js - ESM modern bundle (for browser)

Install

From NPM:

> npm i @master-chief/alpaca

From GitHub:

From these popular CDNs:

Import

Import with CommonJS:

let { AlpacaClient, AlpacaStream } = require('@master-chief/alpaca');

Import with ESM:

import { AlpacaClient, AlpacaStream } from '@master-chief/alpaca';

Import as script:

<script src="https://unpkg.com/@master-chief/alpaca/dist/alpaca.browser.min.js"></script>

Import as module:

<script type="module">
  import alpaca from 'alpaca.browser.modern.min.js';
</script>

Client

Creating a new client

If you wish to use env vars, populate these fields with process.env on your own. Paper account key detection is automatic. Using OAuth? Simply pass an access_token in the credentials object.

const client = new AlpacaClient({
  credentials: {
    key: 'xxxxxx',
    secret: 'xxxxxxxxxxxx',
    // access_token: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
    paper: true,
  },
  rate_limit: true,
});

Built-in parsing

Alpaca provides numbers as strings. From their docs:

Decimal numbers are returned as strings to preserve full precision across platforms. When making a request, it is recommended that you also convert your numbers to strings to avoid truncation and precision errors.

This package provides numbers as number instead, and date strings as Date objects which is what most developers want out of the box. If you want the original data, as it came from Alpaca, you can call raw() on any entity.

const account = await client.getAccount();

console.log(typeof account.buying_power); // number
console.log(typeof account.raw().buying_power); // string

Methods

The following methods are available on the client.

Account

Market Data v1

Market Data v2

isAuthenticated

await client.isAuthenticated();

getAccount

await client.getAccount();

getOrder

await client.getOrder({ order_id: '6187635d-04e5-485b-8a94-7ce398b2b81c' });

getOrders

await client.getOrders({ limit: 25, status: 'all' });

placeOrder

await client.placeOrder({
  symbol: 'SPY',
  qty: 1,
  // or
  // notional: 100,
  side: 'buy',
  type: 'market',
  time_in_force: 'day',
});

replaceOrder

await client.replaceOrder({
  order_id: '69a3db8b-cc63-44da-a26a-e3cca9490308',
  limit_price: 9.74,
});

cancelOrder

await client.cancelOrder({ order_id: '69a3db8b-cc63-44da-a26a-e3cca9490308' });

cancelOrders

await client.cancelOrders();

getPosition

await client.getPosition({ symbol: 'SPY' });

getPositions

await client.getPositions();

closePosition

await client.closePosition({ symbol: 'SPY' });

closePositions

await client.closePositions();

getAsset

await client.getAsset({ asset_id_or_symbol: 'SPY' });

getAssets

await client.getAssets({ status: 'active' });

getWatchlist

await client.getWatchlist({ uuid: '2000e463-6f87-41c0-a8ba-3e40cbf67128' });

getWatchlists

await client.getWatchlists();

createWatchlist

await client.createWatchlist({
  name: 'my watchlist',
  symbols: ['SPY', 'DIA', 'EEM', 'XLF'],
});

updateWatchlist

await client.updateWatchlist({
  uuid: '2000e463-6f87-41c0-a8ba-3e40cbf67128',
  name: 'new watchlist name',
  symbols: ['TSLA', 'AAPL'],
});

addToWatchlist

await client.addToWatchlist({
  uuid: '2000e463-6f87-41c0-a8ba-3e40cbf67128',
  symbol: 'F',
});

removeFromWatchlist

await client.removeFromWatchlist({
  uuid: '2000e463-6f87-41c0-a8ba-3e40cbf67128',
  symbol: 'F',
});

deleteWatchlist

await client.deleteWatchlist({ uuid: '2000e463-6f87-41c0-a8ba-3e40cbf67128' });

getCalendar

await client.getCalendar({ start: new Date(), end: new Date() });

getClock

await client.getClock();

getAccountConfigurations

await client.getAccountConfigurations();

updateAccountConfigurations

await client.updateAccountConfigurations({
  no_shorting: true,
  suspend_trade: true,
});

getAccountActivities

await client.getAccountActivities({ activity_type: 'FILL' });

getPortfolioHistory

await client.getPortfolioHistory({ period: '1D', timeframe: '1Min' });

getLastTrade_v1

await client.getLastTrade_v1({ symbol: 'SPY' });

getLastQuote_v1

await client.getLastQuote_v1({ symbol: 'SPY' });

getBars_v1

await client.getBars_v1({ symbols: ['SPY', 'DIA', 'XLF'], timeframe: '1Min' });

getLatestTrade

await client.getLatestTrade({ symbol: 'SPY' });

getTrades

Basic
await client.getTrades({
  symbol: 'SPY',
  start: new Date('2021-02-26T14:30:00.007Z'),
  end: new Date('2021-02-26T14:35:00.007Z'),
});
Paginated
let trades = []
let page_token = ''

// until the next token we receive is null
while (page_token != null) {
  let resp = await client.getTrades({ ..., page_token })
  trades.push(...resp.trades)
  page_token = resp.next_page_token
}

// wooh! we have collected trades from multiple pages
console.log(trades.length)

getQuotes

Basic
await client.getQuotes({
  symbol: 'SPY',
  start: new Date('2021-02-26T14:30:00.007Z'),
  end: new Date('2021-02-26T14:35:00.007Z'),
});
Paginated
let quotes = []
let page_token = ''

// until the next token we receive is null
while (page_token != null) {
  let resp = await client.getQuotes({ ..., page_token })
  quotes.push(...resp.quotes)
  page_token = resp.next_page_token
}

// wooh! we have collected quotes from multiple pages
console.log(quotes.length)

getBars

Basic
await client.getBars({
  symbol: 'SPY',
  start: new Date('2021-02-26T14:30:00.007Z'),
  end: new Date('2021-02-26T14:35:00.007Z'),
  timeframe: '1Min',
  // page_token: "MjAyMS0wMi0wNlQxMzowOTo0Mlo7MQ=="
});
Paginated
let bars = []
let page_token = ''

// until the next token we receive is null
while (page_token != null) {
  let resp = await client.getBars({ ..., page_token })
  bars.push(...resp.bars)
  page_token = resp.next_page_token
}

// wooh! we have collected bars from multiple pages
console.log(bars.length)

getSnapshot

await client.getSnapshot({ symbol: 'SPY' });

getSnapshots

await client.getSnapshots({ symbols: ['SPY', 'DIA'] });

getNews

await client.getNews({ symbols: ['SPY'] });

Stream

Creating a new stream

If you wish to use env vars, populate these fields with process.env on your own.

import { AlpacaStream } from '@master-chief/alpaca';

const stream = new AlpacaStream({
  credentials: {
    key: 'xxxxxx',
    secret: 'xxxxxxxxxxxx',
    paper: true,
  },
  type: 'market_data', // or "account"
  source: 'iex', // or "sip" depending on your subscription
});

Methods

The following methods are available on the stream.

Channels

Channel Type
trade_updates account
trades market_data
quotes market_data
bars market_data

subscribe

stream.once('authenticated', () =>
  stream.subscribe('bars', ['SPY', 'AAPL', 'TSLA']),
);

unsubscribe

stream.unsubscribe('bars', ['SPY']);

on

stream.on('message', (message) => console.log(message));
stream.on('trade', (trade) => console.log(trade));
stream.on('bar', (bar) => console.log(bar));
stream.on('quote', (quote) => console.log(quote));
stream.on('trade_updates', (update) => console.log(update));
stream.on('error', (error) => console.warn(error));

getConnection

stream.getConnection();

Common Issues

If you are having difficulty getting Jest to work with this library, add this to your configuration:

moduleNameMapper: {
  '@master-chief/alpaca': '<rootDir>/node_modules/@master-chief/alpaca/dist/cjs/index.cjs',
},

Credit to @calvintwr and @wailinkyaww for finding this solution.

Examples

Don't know where to start? Check out our community-made examples here.

Contributing

Feel free to contribute and PR to your 💖's content.

alpaca-ts's People

Contributors

117 avatar alexmayol avatar anback avatar anonrose avatar aqilc avatar dependabot[bot] avatar hkang1 avatar husayt avatar jdgriffith avatar jerrami avatar kalebmills avatar kevindra avatar lbstr avatar samwilkins333 avatar tclare avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

alpaca-ts's Issues

Jest encountered an unexpected token

Description
I've been trying to fix this error that started happening with my unit tests when I installed @master-chief/alpaca.

 FAIL  test/controllers/middlewares/auth.middleware.test.ts
  ● Test suite failed to run

    Jest encountered an unexpected token

    Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.

    Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.

    By default "node_modules" folder is ignored by transformers.

    Here's what you can do:
     • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/configuration
    For information about custom transformations, see:
    https://jestjs.io/docs/code-transformation

    Details:

    /Users/kevindra/workplace/tradingbot/tradingbotx-api/node_modules/@master-chief/alpaca/dist/mjs/index.js:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){export { AlpacaClient } from './client.js';
                                                                                      ^^^^^^

    SyntaxError: Unexpected token 'export'

    > 1 | import { AlpacaClient, PageOfBars } from "@master-chief/alpaca";

Expected
No such errors.

Reproduction
Any Jest tests would fail if this library is imported.

Looking for what might be the fix. Thanks

Unnecessary `reconnect` options.

I am very sure these options only add clutter to the class. Anyone can just put a listener on the close event and just re-initialize anyways, so there is literally no point. I would like to see a use case if otherwise.

Fails to build in nodejs environment

Hi! I'm trying to use this library with firebase functions (node 12 environment), but I'm getting a build error from tsc.

It's a newly create firebase functions project (so fairly standard settings). This is my tsconfig file:

{
  "compilerOptions": {
    "module": "commonjs",
    "noImplicitReturns": true,
    "noUnusedLocals": true,
    "outDir": "lib",
    "sourceMap": true,
    "strict": true,
    "target": "es2017",
    "typeRoots": ["node_modules/@types"]
  },
  "compileOnSave": true,
  "include": [
    "src"
  ]
}

This is the output:


node_modules/@master-chief/alpaca/@types/stream.d.ts:1:23 - error TS2688: Cannot find type definition file for 'ws'.

1 /// <reference types="ws" />
                        ~~

node_modules/@master-chief/alpaca/@types/stream.d.ts:2:8 - error TS1259: Module '"/Users/fredrik/github/garagefunds/functions/node_modules/isomorphic-ws/index"' can only be default-imported using the 'esModuleInterop' flag

2 import WebSocket from 'isomorphic-ws';
         ~~~~~~~~~

  node_modules/isomorphic-ws/index.d.ts:8:1
    8 export = WebSocket
      ~~~~~~~~~~~~~~~~~~
    This module is declared with using 'export =', and can only be used with a default import when using the 'esModuleInterop' flag.

node_modules/@master-chief/alpaca/@types/stream.d.ts:3:8 - error TS1259: Module '"/Users/fredrik/github/garagefunds/functions/node_modules/eventemitter3/index"' can only be default-imported using the 'esModuleInterop' flag

3 import EventEmitter from 'eventemitter3';
         ~~~~~~~~~~~~

  node_modules/eventemitter3/index.d.ts:134:1
    134 export = EventEmitter;
        ~~~~~~~~~~~~~~~~~~~~~~
    This module is declared with using 'export =', and can only be used with a default import when using the 'esModuleInterop' flag.

node_modules/isomorphic-ws/index.d.ts:6:28 - error TS7016: Could not find a declaration file for module 'ws'. '/Users/fredrik/github/garagefunds/functions/node_modules/ws/index.js' implicitly has an 'any' type.
  Try `npm install @types/ws` if it exists or add a new declaration (.d.ts) file containing `declare module 'ws';`

6 import WebSocket = require('ws')
                             ~~~~


Found 4 errors.

Any idea what this might be about?

Exception while getting json() of DELETE responses

Description
There are a few methods in Alpaca api which are done through DELETE call. It is not standard for DELETE calls to return result, but here they do. The problem is when we try to do fetch().json() they throw an exception. This library currently wraps that exception and returns {}, but then we are missing the original result.

Expected
To return the received response from function.

Reproduction
Steps we can take to reproduce the bug:
For exampe, trying to cancel an order with cancelOrder(id) you don't know what was the result.

Additional
This is due to fetch handling DELETE messages (at least in a browser). Need to find a proper way of handling it

Paper API trade_updates stream

I noticed the Paper API websockets is not working. My live key authenticates to AlpacaStream, but I get the error below when I try to connect with my paper key:

Error: There was an error in authorizing your websocket connection. Object received: { "stream": "authorization", "data": { "action": "authenticate", "message": "access key verification failed", "status": "unauthorized" } }

I checked the urls.js in your code, the paper websockets endpoint is not there, so I figured either paper websockets trade_updates is probably not implemented or Alpaca made one of their confusing changes again. Paper and live accounts share the same wss endpoint for data (wss://data.alpaca.markets/stream), but the trade_updates for paper accounts is at this wss endpoint:

wss://paper-api.alpaca.markets/stream

I will appreciate if this can be integrated.

Stream.onAuthentication

Again, thanks for the great job you did on this library.

I ran this code

stream.subscribe(["AM_SPY"]); stream.on("aggregate_minute", function (message) { console.log(message); });

and got this error after which I got "Connected to websocket" in the console:
Error: You can't send a message until you are authenticated!

Is it possible to have an event that fires upon authentication? Alternatively, I will like to see how to get around this in a Readme sample code.

PUT requests don't work

Description
PUT requests appear to be broken.

Trying to use client.updateWatchlist complains about a malformed request.

It looks like there is a conditional in the request method that needs to be updated

CURRENT: if (params.method != 'POST' && params.method != 'PATCH') {
CHANGE TO: if (!['POST','PATCH','PUT'].includes(params.method)) {

Expected
A clear and concise description of what you expected to happen.

The watchlist should get updated properly.

Reproduction
Steps we can take to reproduce the bug:

Just try using client.updateWatchlist and it will generate an error from the server due to a malformed request.

Logs
If applicable, add logs to help explain the bug.

Additional
Add any other context about the problem here.

Parsing strings

Question
Given that the Alpaca API provides dates and numbers as strings, where do you feel the responsibility lies for parsing those values?

Background
Right now this package provides types that match the Alpaca API 1:1. For example, in Account, we have buying_power, which is a string. From the developer's perspective, this is nice because there are no surprises. I can look at the Alpaca Docs and trust that this package will provide me data in the same fashion.

If I want to use buying_power, I need to safely convert it to a number. From a typing perspective, we would ideally have two types: one that matches the Alpaca API and one that has numbers and dates parsed. This package provides the former, but I'm curious about the latter.

Right now I'm basically wrapping your client and doing the mapping myself. For example, I have an AccountParsed type which extends Account and has buying_power_num: number.

Since everyone has to do this, I feel that it should be shared. I would love your feedback this because I'm not sure if it belongs here or in a separate package.

Types for streams

Looks like here a generic emitter is used when something is received over the socket connection. This bit of code looks handy, but it's not useful as it pertains to types. Seems since this is supposed to be a Typescript superset of the alpacas library, these should be individually typed.

My suggestion is to add overloads the allow types to be had for these emitted values.

Project looks awesome

Hey, I was looking through this project and it's really solid.
I lead engineering at Fey and we're looking for engineers. Wondering if you'd be interested?
Regardless, awesome project. Thanks for maintaining it.

Compilation error

Description
Trying to upgrade to 4.2.2 I'm getting the following compilation error:

node_modules/@master-chief/alpaca/@types/stream.d.ts:30:5 - error TS2416: Property 'on' in type 'AlpacaStream' is not assignable to the same property in base type 'EventEmitter<string | symbol, any>'.
  Type '<U extends "open" | "close" | "error" | "message" | "authenticated" | "trade" | "trade_updates" | "account_updates" | "quote" | "aggregate_minute">(event: U, listener: AlpacaStreamEvents[U]) => this' is not assignable to type '<T extends string | symbol>(event: T, fn: (...args: any[]) => void, context?: any) => this'.
    Types of parameters 'event' and 'event' are incompatible.
      Type 'T' is not assignable to type '"open" | "close" | "error" | "message" | "authenticated" | "trade" | "trade_updates" | "account_updates" | "quote" | "aggregate_minute"'.
        Type 'string | symbol' is not assignable to type '"open" | "close" | "error" | "message" | "authenticated" | "trade" | "trade_updates" | "account_updates" | "quote" | "aggregate_minute"'.
          Type 'string' is not assignable to type '"open" | "close" | "error" | "message" | "authenticated" | "trade" | "trade_updates" | "account_updates" | "quote" | "aggregate_minute"'.
            Type 'T' is not assignable to type '"aggregate_minute"'.
              Type 'string | symbol' is not assignable to type '"aggregate_minute"'.
                Type 'string' is not assignable to type '"aggregate_minute"'.

30     on<U extends keyof AlpacaStreamEvents>(event: U, listener: AlpacaStreamEvents[U]): this;

Any ideas?

I was able to build against 4.0.5 and was trying to upgrade from that to 4.2.2

Expected

Would like to be able to upgrade ;)

Reproduction

I'll keep digging a bit and try to trim down a way to reproduce if we can't figure it out -- hoping you might have a clue

Logs
If applicable, add logs to help explain the bug.

Additional
Add any other context about the problem here.

Does the streaming API still work?

I've followed the docs on your main github page, but my subscriptions to quotes/updates/etc doesn't work. I receive a successful authentication message, but when I try to subscribe for quotes, I never get any notifications.

The docs on alpaca's site it shows something like this, but I don't see anything related to subscribing for updates.

{
    "action": "listen",
    "data": {
        "streams": ["trade_updates"]
    }
}

But in your example, the action is "subscribe" and the data doesn't appear to be formatted in a similar format. I'm not sure what I'm missing..

Here is my code for connecting to the socket

this.socketConnection.once("authenticated", () => {
      this.socketConnection.subscribe("quotes", ["AAPL, FB, SPY"]);

      this.socketConnection.on("quote", (quote) => {
        console.log(quote);
      });

      this.socketConnection.on("bar", (bar) => {
        console.log(bar);
      });

      this.socketConnection.on("trade", (trade) => {
        console.log(trade);
      });

      this.socketConnection.on("trade_updates", (update) => {
        console.log(update);
      });
});`

Expand timeframe options for getBars

First, thanks so much for putting this together and maintaining it. I'm a big fan & it's saved me a TON of time in doing a typescript integration with Alpaca APIs.

Is your feature request related to a problem? Please describe.
In using the getBars API, I noticed that you're missing a decent amount of documented and undocumented accepted timeframes. Currently, the timeframe accepts '1Sec' | '1Min' | '1Hour' | '1Day' options. After researching the Alpaca docs and some trial and error, there are a handful of other values that are acceptable.

Not all-inclusive, but some of the more common (maybe a better word is more useful) ones are :

  • '5Min'
  • '15Min'
  • '30Min'
  • '2Hour'
  • '4Hour'

At least the 15Min one is referenced at Alpaca bars v2. Additional testing (via Postman) has shown that you can seemingly input any number followed by 'Min' and get actual formatted responses back - e.g., '37Min' actually works and spaces the bars by 37 minutes.

Describe the solution you'd like
I would like the ability to input at least the listed above values (5min, 15min, etc.) as viable union type selections for the GetBars timeframe.

Describe alternatives you've considered
It is absolutely easily possible to add "as ..." for the timeframe options. But since timeframes that you support are not individually exported, it is not currently feasible to do timeframe: '5min' as TimeFrame for example, which would also suffice.

Additional context
Would greatly appreciate it if selectable timeframes included additional values or were implemented as an individual exported type that could be imported & extended. Again, thanks so much for the work on this as a whole!.

Rename Client and Stream

I feel that these class names are too broad. In user code, you may have many clients, so you might have to do something like this:

import { Client as AlpacaClient } from '@master-chief/alpaca';

How about AlpacaClient and AlpacaStreamingClient? Or maybe AlpacaStreamer?

Consider adding a CHANGELOG

Is your feature request related to a problem? Please describe.

Knowing what's changed in new releases.

Consider adding a CHANGELOG

Describe the solution you'd like

Perhaps something like https://github.com/conventional-changelog/standard-version to generate it. It's pretty quick and easy to set up and use.

Describe alternatives you've considered

Generating it by hand; but wouldn't recommend this option.

Additional context

Think it's definitely worth the small effort to set up and will pay that investment back quickly.

Cannot discern which stock the bar update is for with stream API

Description
When I subscribe to the "bars" updates for a set of stocks, I want to be able to listen to updates with stream.on("bar", (bar) => {}); but the issue is that the Bar type does not contain any information about which stock this bar is for. So when I listen for bar updates I have no idea which stock these updates are for.

In the alpaca documentation however, it seems to say that the JSON returned for Bar objects contains a property S which is the symbol name.

Wrong typings

image

For the getBars method, this is not a Map, because TS won't let us doing a bars[symbol] thing, he wants to use the Map .get method... so we have to force TS with a as like that otherwise we have an any type:

image

CORS Paper mode issue?

Description
CORS policy error when using API on Paper mode

Expected
Works as normal

Reproduction
Steps we can take to reproduce the bug:

  1. Use paper: true and add paper keys to constructor

Logs
Screen Shot 2022-04-02 at 5 52 54 PM

Additional
Am I doing something wrong here? I am developing my system and want to utilize the paper account for testing runs. But I am not able to use any features since I am getting this CORS policy - I don't get it when using non-paper mode.

AlpacaStream for type "account" is busted for paper accounts

Description
When using type account for AlpacaStream it complains about malformed json.

I did some research and it looks like according to the documentation, the account stream uses binary frames for the paper api while the live stream uses text frames. I'm not sure why, but that seems to be causing an issue.

See note from - https://alpaca.markets/docs/api-documentation/api-v2/streaming/

Expected

Expecting paper support to work just like normal support.

Reproduction
Steps we can take to reproduce the bug:

Just create a stream using a paper account and trade_updates channel and it will complain about bad json

Logs
If applicable, add logs to help explain the bug.

Additional
Add any other context about the problem here.

Parsing dates

I'm splitting the topic of dates out of issue #9 since it warrants its own discussion. @117 and I have discussed this somewhat, but I'm not sure we drew a conclusion.

Background

Alpaca provides dates as ISO-8601 strings. During transport, this is the ideal format since it is always in UTC and won't change as it travels across machines.

For users to actually use these dates, they need to convert to Date or use a library like date-fns, moment, etc. Should we save them this step and automatically convert the ISO-8601 strings to Date?

Considerations

  1. Timezones are a common source of bugs. While the JavaScript Date internally stores the value as UTC, most methods present the date to you in the local machine's timezone. People tend to screw this up.

  2. Alpaca has a mixture of date-time and date data. For dates, like in the Calendar entity, you get YYYY-MM-DD. We would not want to convert that as a Date since it does not represent a point in time, but rather a whole day. So if we decide to convert ISO-8601 strings to Date, but leave the YYYY-MM-DD strings alone, is that confusing to the user?

I can't get the module to compile within a Typescript node project

Hi,

I have a typescript node project. I've used NPM to install the module.

Using import { AlpacaClient, AlpacaStream } from "@master-chief/alpaca"; gives me the error below:

image

It appears it's trying to use the index.js from the dist/mjs folder, although my module config in compiler options is set to use commonjs. Any idea on how I can get this working? Should I be using one of the bundles?

Cannot use compiled package with jest due to ESM errors

Description
Running into ESM related error trying to use the package with jest

Expected
Package should work with jest on node v14. My suspicion is something isn't transpiled correctly?

Reproduction
Example repo that reproduces the error:
https://github.com/neeschit/alpaca-jest-issue

Logs

 Details:

    /Users/nranganath/playground/alpaca-jest-test/node_modules/@master-chief/alpaca/dist/mjs/index.js:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){export { AlpacaClient } from './client.js';
                                                                                             ^^^^^^

    SyntaxError: Unexpected token 'export'

    > 1 | import { AlpacaClient } from "@master-chief/alpaca";
        | ^
      2 |
      3 | export const client = new AlpacaClient({
      4 |     credentials: {

      at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1350:14)
      at Object.<anonymous> (index.ts:1:1)
      

Screen Shot 2021-05-09 at 1 08 18 AM

Additional
I've tried using transformIgnorePatterns as specified here, but no luck

FYI, I can get it to run just fine with node directly, just jest that's a problem

CancelAll doesn't return as expected

Description
Data passed by CancelAll is not being returned by call

Reproduction
Call cancelAll()

image

Logs
If applicable, add logs to help explain the bug.

Additional
Add any other context about the problem here.

Browser build

Is your feature request related to a problem? Please describe.
Currently library only runs on server side, It can't be run in browser.

Describe the solution you'd like
I don't see a reason this library couldn't work in browser. For instance, instead of node-fetch we could just use fetch in browser.
Instead of ws we could use isomorphic-ws
Same goes for other dependencies.

Additional context
instead of node-fetch we could also use ky-universal.

[Question]: Pagination

Can you add documentation on how to continue resolving requests that contain a next_page_token?

Streaming using Paper Credentials

Description
For market data, it appears that streaming using paper account won't work, since this codebase always defaults to the following

    switch (params.type) {
      case 'account':
        this.host = params.credentials.paper
          ? urls.websocket.account.replace('api.', 'paper-api.')
          : urls.websocket.account
        break
      case 'market_data':
        this.host = urls.websocket.market_data(this.params.source)
        break
      default:
        this.host = 'unknown'
    }
export default {
  rest: {
    account: 'https://api.alpaca.markets/v2',
    market_data_v2: 'https://data.alpaca.markets/v2',
    market_data_v1: 'https://data.alpaca.markets/v1',
  },
  websocket: {
    account: 'wss://api.alpaca.markets/stream',
    market_data: (source: DataSource = 'iex') =>
      `wss://stream.data.alpaca.markets/v2/${source}`,
  },
}

Should the url be pointed to paper endpoint for streaming too? It does not seem to work for me using paper credentials as of now.

A more streamlined API.

Client.getAsset(), Client.getAssets(), Client.getWatchlist(), Client.createWatchlist(), etc. seems like a really outdated way to do a modern API. I would suggest an Client.assets property that has an assets cache and the required methods built-in like this:

Client.assets.get(parameters?: GetAssetsParameters): Promise<Asset|Asset[]> {
    return this.request(method.GET, BaseURL.Account, parameters.asset_id_or_symbol ? "assets/" + parameters.asset_id_or_symbol : "assets?" + qs.stringify(parameters));
}

which gets all assets without parameters and specific assets using given parameters otherwise. This can be done with watchlists, account configurations, clocks, calendars, and many other methods/objects.

Add Functionality to Close AlpacaStream?

Hi there @117! 👋 firstly: fantastic job with this. I've started with Alpaca recently and being able to integrate with Typescript is a lifesaver 🙂 great work!

In building out a strategy for my specific use case, I have recently started thinking about integrating the stream feature. For cleanup purposes, I, as a user of this package would like control over when to close the underlying Websocket, so that I can control when to exit the calling process. Am I missing somewhere this is already built in? Or how would you feel about incorporating a feature like this? Something like AlpacaStream.close()?

Would be happy to take point on this also! Thanks for reading 🚀

Upcoming breaking API change

Hello! This is not an issue per se and more a heads up that on September 9th, Alpaca will be implementing a breaking change to the API.

The change affects "unpriceable" assets, specifically, positions in an account that are not priceable. This can occur when certain corporate actions are processed or when a stock gets delisted. In this scenario, API endpoints that call functions that use asset prices are returning an error response.

In order to avoid the error response and keep the API available even in the event that one of your positions’ underlying asset is missing a price, Alpaca will introduce a minor breaking change to the API.

Starting on September 9, 2021, the following API response fields will change from non-nullable to nullable. They will appear null in the response in the case that the underlying asset is missing the price for some reason.

image

Additionally, the following API response fields’ values will exclude the account’s position in the unpriceable asset.

image

Thanks so much for your work building and maintaining this excellent SDK! Please let me know if there are any questions the Alpaca team can help answer in regards to these changes.

Authentication issue

There's something wrong with how the credentials object is being handled. i am not skilled enough to troubleshoot and debug. I am using the official SDK with no authentication issues.

Build Output Not Reflecting Recent PR

Description
Hey there! 🙂 Would you mind running the build output again to incorporate my most recent change (parsing the trade updates stream?) I don't think the .d.ts files are in sync with the most recent change. Led to a bug I caught recently where the code was sending a parsed form (TradeUpdate) to the handler but the old types were still in effect, TradeUpdate really referred to what is now known as a RawTradeUpdate. If you're low on time I can even do it, I can also open a PR tonight on the diff from running npm run build on the most recent version of master.

Expected
Type declaration files (and thus IDE autocomplete) should sync up with my most recent changes parsing the Trade Update stream.

Reproduction
Verify that the latest version of master inside does not have newly created interfaces in it. For instance, the @types/entities.d.ts file does not haveTradeUpdateEvent and RawTradeUpdate interfaces, and the @types/parse.d.ts file does not have the trade_update parser function type.

Logs
n/a

Additional
n/a

Alternative fetch library - ohmyfetch

We have tried multiple isomorphic fetch libraries (ky,node-fetch-isomorphic, isomorphic-fetch and others) for last release and stopped on isomorphic-unfetch. But that was kind of rush decision and I stopped on the first thing that worked. It will be also nice to check out ohmyfetch which works fine on webworkers too and stop on the one which performs best.

I will update here how this goes.

Broken build

Description
@117 when I merged latest commits, build got broken for me.

I don't see anything which could have broken the build in those commits. Maybe something changed on my side? Could you please try npm run build on your side?

New module related issues when upgrading

Description

Upgrading from @master-chief/alpaca ^3.6.3 → ^4.2.3

I've just begin to dig on these to... but in case they are some other quick ones. Both of these are new for me (the cdk synth one involves bundling with esbuild):

eikeon@iMac matic % npm run build

> [email protected] build /Users/eikeon/matic
> tsc

eikeon@iMac matic % npm run cdk synth

> [email protected] cdk /Users/eikeon/matic
> cdk "synth"

Bundling asset MaticPipeline/PreProd/Matic/handler/Code/Stage...
 > ../node_modules/node-gyp-build/index.js: warning: Indirect calls to "require" will not be bundled (surround with a try/catch to silence this warning)
    6 │ var runtimeRequire = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require // eslint-disable-line
      ╵                                                                                            ~~~~~~~

1 warning
Successfully synthesized to /Users/eikeon/matic/cdk.out
Supply a stack id (MaticPipeline, MaticRepo) to display its template.
eikeon@iMac matic % node_modules/.bin/ts-node t.ts                                                                                                                                             
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /Users/eikeon/matic/node_modules/@master-chief/alpaca/dist/mjs/index.js
require() of ES modules is not supported.
require() of /Users/eikeon/matic/node_modules/@master-chief/alpaca/dist/mjs/index.js from /Users/eikeon/matic/lib/matic-stack-handler.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /Users/eikeon/matic/node_modules/@master-chief/alpaca/package.json.

    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1080:13)
    at Module.load (internal/modules/cjs/loader.js:928:32)
    at Function.Module._load (internal/modules/cjs/loader.js:769:14)
    at Module.require (internal/modules/cjs/loader.js:952:19)
    at require (internal/modules/cjs/helpers.js:88:18)
    at Object.<anonymous> (/Users/eikeon/matic/lib/matic-stack-handler.ts:2:1)
    at Module._compile (internal/modules/cjs/loader.js:1063:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
    at Module.load (internal/modules/cjs/loader.js:928:32)
    at Function.Module._load (internal/modules/cjs/loader.js:769:14)
eikeon@iMac matic % 

Crypto Support

Would love to see support added for crypto endpoints, especially for market data streams.

The API doesn't seem to be much different, I'm guessing most of the differences are in the endpoints.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.