Skip to content

Commit

Permalink
added some examples and did some doc tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
slimandslam committed Jan 5, 2025
1 parent 39e684c commit 9cb7da8
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 57 deletions.
39 changes: 19 additions & 20 deletions docs/DeveloperReference.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# schwab-client-js Developer Reference

### This document is an overview of the classes, methods, and functions implemented in schwab-client-js. You may still need to look at the Schwab API documentation on developer.schwab.com to see what elements are returned by API calls, details of error messages, and various structures such as order objects and streaming subscription formats.
### This document is an overview of the classes, methods, and functions implemented in schwab-client-js. You may still need to look at the Schwab API documentation on developer.schwab.com to see what elements are returned by API calls, details of error messages, and various structures such as streaming subscription parameters.

## Contents

Expand Down Expand Up @@ -46,20 +46,19 @@ schwab-client-js defines a class ` SchwabAPIclient` and three subclasses
- `MarketApiClient()` - retrieve all sorts of market data
- `StreamingApiClient()` - real-time streaming of market data

Once your `.env` file is setup (see the [Schwab API Configuration](SchwabConfig.md) guide), running any of the methods associated with these classes is straightforward.
Once your `.env` file is setup (see the [Readme](../Readme.md) for setup details), running any of the methods associated with these classes is straightforward.

[Note: For situtations where using a ```.env```file or environment variables may not be optimal (possibly AWS Lambda, for example), schwab-client-js also supports injecting your security tokens directly e.g. ```const mktclient = new MarketApiClient(appKey,appSecret,appRefresh);```]

### See the [examples directory](examples) for usage examples

## Subclasses and Methods for Class SchwabAPIclient

### Two Minor Changes To The API That I Made
### One Minor Change To An API Call That I Made

In general, I pass through anything that Schwab API calls return without modifying anything. There are two exceptions. In two methods, **when the call succeeds**, I return slightly different things than what the Schwab documentation says:
In general, I pass through anything that Schwab API calls return without modifying anything. There is one exception. In one method, **when the call succeeds**, I return a slightly different thing than what the Schwab documentation says:

- `placeOrderByAcct()` - This is the method for placing trades. When you place an order, I return a JSON object containing the orderId (the underlying Schwab API call puts the orderID in a header and returns null).
- `orderDelete()` - I return null when you successfully delete an order.

### Using Dates In Methods

Expand All @@ -72,7 +71,7 @@ In various methods, you may have to specify day/time in order to retrieve data f

All of the `MarketApiClient()` calls are HTTP GET calls, so they are simply fetching market data for you. There is a wide range of data you can fetch. The calls are listed in Table 1 below.

Here's an example of getting the price history for the stock ticker "AMD". The call `priceHistory()` uses millseconds since the epoch to represent date/time. The Schwab developer docs explain each parameter in detail.
Here's an example of getting the price history for the stock ticker "AMD". The call `priceHistory()` uses millseconds since the epoch to represent date/time.

```
import { MarketApiClient } from "schwab-client-js";
Expand Down Expand Up @@ -185,14 +184,14 @@ Partial output:

The `TradingApiClient()` is where you can make equity and options orders of various styles as well as access your personal current and historical trading data and account information.

If you are going to make orders or look at your historical transaction data, you probably need to specify your hashed account number. Use `accountNumbers()` to retrieve all authorized Schwab account numbers and their hashes (you may have more than one):
If you are going to make orders or look at your historical transaction data, you probably need to specify your hashed account number. Use `accountNumbers()` to retrieve all authorized Schwab account numbers and their hashes (you may have more than one Schwab account):

```
import { TradingApiClient } from "schwab-client-js";
const trdclient = new TradingApiClient();
const accounts = await trdclient.accountsNumbers();
// If you have two accounts, like this one, you'll need
// to do a test to make sure you're using the
// If you have authorized two accounts, as shown here,
// you'll need to do a test to make sure you're using the
// correct hashValue. With only one account, it will
// be the first array value: accounts[0].hashValue
console.log(JSON.stringify(accounts, null, 2));
Expand Down Expand Up @@ -255,13 +254,13 @@ One rejected order was found. The output (edited for brevity):
]
```

We need to create an order object (JSON structure) in order to make an order. I'll use one of the helper functions to create a JSON structure that opens an equity sell limit order, `equitySellLimit()`
We need to create an order object (JSON structure) in order to submit an order. I'll use one of the helper functions to create a JSON structure that opens an equity limit sell order, `equitySellLimit()`

```
import { equitySellLimit } from "schwab-client-js/orderhelp";
// Equity short sell market order
let tradeObj = equitySellLimit("IBM", "SPY", 10, "55.25");
// Submit a limit sell order
let tradeObj = equitySellLimit("IBM", 10, "55.25");
console.log(JSON.stringify(tradeObj, null, 2));
```

Expand All @@ -287,7 +286,7 @@ Output:
}
```

Taking the account hash from the previous code above, as well as the trading object just created, we can execute an equity short sell market order:
Taking the account hash from the previous code above, as well as the trading object just created, we can execute an equity limit sell order:

```
import { TradingApiClient } from "schwab-client-js";
Expand Down Expand Up @@ -352,7 +351,7 @@ await streamclient.streamInit();
await streamclient.streamSchwabLogin();
```

At this point, you're ready to tell Schwab which equities, options, futures, and forex data that you want to stream data about. The choices are fairly large and are beyond the scope of this document, but you can see the choices on developer.schwab.com [in this section](https://developer.schwab.com/products/trader-api--individual/details/documentation/Market%20Data%20Production)
At this point, you're ready to tell Schwab what equities, options, futures, or forex data that you want to stream. The choices are fairly large and are beyond the scope of this document, but you can see the choices on developer.schwab.com [in this section](https://developer.schwab.com/products/trader-api--individual/details/documentation/Market%20Data%20Production)

As illustrated below, the `streamSchwabRequest()` takes three parameters:

Expand Down Expand Up @@ -451,30 +450,30 @@ schwab-client-js has helper functions that make it easier to create order object

- `placeOrderByAcct()`
- `updateOrderById()`
- `orderPreview()` -- not yet implemented by Schwab but still in the Schwab docs
- `orderPreview()` -- not yet implemented by Schwab but still listed in the Schwab docs

### Creating Option Symbols

All of the helper functions create order objects except for `optionSymbol()` which helps you create the Schwab format for an option symbol, which is (from the Schwab docs): <br /><br />
All of the helper functions create order objects except for `optionSymbol()` which mereley helps you create the Schwab format for an option symbol, which is (from the Schwab docs): <br /><br />
**Symbol (6 characters including spaces) + Expiration (6 characters, YYMMDD) + Call/Put (1 character: 'C' or 'P') + Strike Price (5+3=8 characters)**

The Symbol is left-justified and padded with spaces, the expiration date is in the form YYMMDD, and the Strike Price has three zeros to the right of the decimal point and is padded on the left with zeroes. <br />
**Example:<br /><br />
Stock Symbol: XYZ<br />
Expiration: 2021/01/15<br />
Type: Call<br />
Strike Price: $62.50**<br />
Strike Price: $62.50**

```
import { optionSymbol } from "schwab-client-js/orderhelp";
const optsymbol = optionSymbol("XYZ", "210115", "C", "62.50");
```

**The resulting option symbol is: `"XYZ 210115C00062500"`** (there are three spaces between "XYZ" and "210"
**The resulting option symbol is: `"XYZ 210115C00062500"`** (there are three spaces between "XYZ" and "210115"

### Creating Order Objects

The table below contains all the helper functions for creating order objects (as well as optionSymbol() ). The helper functions have various parameters but they all return a single JSON object which you can use directly in your orders or modify further as needed before using.
The table below contains all the helper functions for creating order objects (as well as optionSymbol() ). The helper functions have various parameters but they all return a single JSON object which you can use directly in your orders or you can modify the order object further as needed before using.

Example:

Expand Down Expand Up @@ -534,7 +533,7 @@ Output:

| Description | Function | Parameters |
|---------------------------------------------------------------------------------------------------------------|-------------------------|-----------------------------------------------------------------------------------------------|
| Create an option symbol in the format required by Schwab. The expiration date should be in the format "YYMMDD". | `optionSymbol()` | `symbol: string`, `expirationDate: string`, `contractType: "C" | "P"`, `strikePrice: string` |
| Create an option symbol in the format required by Schwab. The expiration date should be in the format "YYMMDD". | `optionSymbol()` | `symbol: string`, `expirationDate: string`, `contractType: "C" or "P"`, `strikePrice: string` |
| Returns a pre-filled JSON order object for an equity buy limit order. | `equityBuyLimit()` | `symbol: string`, `quantity: number`, `price: string` |
| Returns a pre-filled JSON order object for an equity buy market order. | `equityBuyMarket()` | `symbol: string`, `quantity: number` |
| Returns a pre-filled JSON order object for an equity sell market order. | `equitySellMarket()` | `symbol: string`, `quantity: number` |
Expand Down
14 changes: 7 additions & 7 deletions docs/SchwabConfig.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@


## Creating Your Schwab App
I'm assuming that you've logged into developer.schwab.com, went to your "dashboard" and are creating an app.
I'm assuming that you've logged into developer.schwab.com, went to your "[dashboard](https://developer.schwab.com/dashboard/apps)," and are creating an app.

![Create A Schwab App](images/CreateApp.jpg)

Expand All @@ -30,15 +30,15 @@ When you create your app on developer.schwab.com, you have to make some choices
- Accounts and Trading Production
- Market Data Production

I suggest adding both from the "Select and API Product" dropdown menu above. If you know that all you want is market data, you can add only `Market Data Production` but I think that's typically rare. Similarly, if you only want to make trades and look at data associated with your trades, you could just add `Accounts and Trading Production`.
I suggest adding both from the "Select an API Product" dropdown menu above. If you know that all you want is market data, you can add only `Market Data Production` but I think that's typically rare. Similarly, if you only want to make trades and look at data associated with your trades, you could just add `Accounts and Trading Production`.

### Callback URL(s)

Specifying a callback URL is required. You'll need it when you create -- or re-create -- your Schwab refresh token **which is necessary every seven days**. More on that later.

#### If you only want to add one callback URL, I recommend `https://127.0.0.1:5556` You can always change this, or add more URLs, later. But, once your app is qpproved, changing the callback URL(s) requires that your app be approved again, so you'll have to wait 1-3 days to use the API if you change them. For that reason, it's good to set your callback URLs once and not have to change it later.

The assumption here is that you're creating a web application, and that the callback URL will be the domain name of your application e.g. `https://mycoolapp.com/maybe/with/a/path`. I'm not aware of any developers who are using the Schwab API with a public-facing website, but, even if they are, this still seems like a sub-optimal way of handling token authentication.
The assumption here is that you're creating a web application, and that the callback URL will be the domain name of your application e.g. `https://mycoolapp.com/maybe/with/a/path`. However, I'm not aware of any developers who are using the Schwab API for a web app, but, even if they are, this still seems like a sub-optimal way of handling token authentication.

## Modifying Your Schwab App

Expand All @@ -52,15 +52,15 @@ Note the **App Key** and **Secret** at the bottom of your app. You'll need to ad

### API Rate Limits

You can adjust the rate limit for orders which is listed as `Order Limit` in Figure 2. You can set this to a maximum of 120 orders per minute and it's not clear why you'd want any value other than the maximum of 120. The overall rate limit for the Schwab API is 120 API calls per minute. When you exceed the rate limit, calls should return the HTTP 429 response **"Rate Limit Exceeded"**
You can adjust the rate limit for orders which is listed as `Order Limit` in Figure 2. You can set this to a maximum of 120 orders per minute and it's not clear why you'd want any value other than the maximum of 120. Regardless of the number you put here, the overall rate limit for the Schwab API is 120 API calls per minute. When you exceed the rate limit, calls should return the HTTP 429 response **"Rate Limit Exceeded"**

![Modify A Schwab App](images/OrderLimit.jpg)

### Figure 3: Changing the rate limit for orders

## Schwab API Refresh Token

Your Schwab App Key and Secret (see Figure 2 above), which I call `SCHWAB_APP_KEY` and `SCHWAB_SECRET`, never change. However, as part of Schwab's implementation of **three-legged OAuth** (see [developer.schwab.com here](https://developer.schwab.com/user-guides/get-started/authenticate-with-oauth) and [here](https://developer.schwab.com/products/trader-api--individual/details/documentation/Retail%20Trader%20API%20Production)), there are two other tokens that change regularly. Every 30 minutes, your access token expires and has to be refreshed. This is handled for you automatically within Schwab-client-js. However, your refresh token, which I call `SCHWAB_REFRESH_TOKEN` **must be refreshed every 7 days**.
Your Schwab App Key and Secret (see Figure 2 above), which I call `SCHWAB_APP_KEY` and `SCHWAB_SECRET`, never change. However, as part of Schwab's implementation of **three-legged OAuth** (see [developer.schwab.com here](https://developer.schwab.com/user-guides/get-started/authenticate-with-oauth) and [here](https://developer.schwab.com/products/trader-api--individual/details/documentation/Retail%20Trader%20API%20Production)), there are two other tokens that change regularly. Every 30 minutes, your access token expires and has to be refreshed. This is handled for you automatically within schwab-client-js. However, your refresh token, which I call `SCHWAB_REFRESH_TOKEN` **must be refreshed every 7 days**.

## How The Schwab Refresh Token Is Created

Expand All @@ -87,9 +87,9 @@ As you can imagine, getting the `SCHWAB_REFRESH_TOKEN` manually would be a bit t

## Using schwab-authorize.js

`schwab-authorize.js` is a fully automated script. When you run it, it creates the special URL above and opens it in your default web browser. You then manually login at Schwab.com and go through the flow. When you get to the end, your web browser will show a warning because the script created an unsigned SSL certificate in order to read the contents of the returned URL in order to get the `AUTHORIZATION_CODE`. `schwab-authorize.js` then makes the special API call to get the new `SCWHAB_REFRESH_TOKEN` and then adds it to your `.env` file
`schwab-authorize.js` is a fully automated script. When you run it, it creates the special URL from the section above and opens it in your default web browser. You then manually login at Schwab.com and go through the flow. When you get to the end, your web browser will show a warning because the script created an unsigned SSL certificate in order to read the contents of the returned URL in order to get the `AUTHORIZATION_CODE`. `schwab-authorize.js` then makes the special API call to get the new `SCWHAB_REFRESH_TOKEN` and then adds it to your `.env` file

On MacOS and Linux, you should be able to run it from the command line when you're at the root of your project by typing `schwab-authorize` On Windows, you probably have to spell everything out: `C:\> node node_modules/schwab-client-js/bin/schwab-authorize.js`
On MacOS and Linux, you should be able to run it from the command line when you're at the root of your project by typing `schwab-authorize` On Windows, you probably have to run the command manually like this: `C:\> node node_modules/schwab-client-js/bin/schwab-authorize.js`

### Figures 4, 5, 6, and 7 below show you the last few steps of running `schwab-authorize.js` if your default browser is Chrome on MacOS. The reason for adding a port number (in this case: 5556) to the localhost URL is so that `schwab-authorize.js` can read the URL returned by Schwab. Port numbers 0-1024 require superuser privileges to read (using no port number defaults to port 443 or 80).

Expand Down
10 changes: 10 additions & 0 deletions examples/market-data/marketData.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,13 @@ let params = {
data = await mktclient.priceHistory("AMD", params);
console.log("priceHistory DATA=", JSON.stringify(data));
console.log("\n\n");

// Create a Schwab-formatted option symbol
import { optionSymbol } from "schwab-client-js/orderhelp";
const optsymbol = optionSymbol("TSLA", "250207", "C", "415.00");
// optsymbol is now: "TSLA 250207C00415000"
// Fetch info about the options symbol
data = await mktclient.quotes(optsymbol, null, null);
console.log("quotes DATA=", JSON.stringify(data));
console.log("\n\n");

2 changes: 1 addition & 1 deletion examples/trading/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# trading

## Fetching trading data using schwab-client-js
# Fetching trading data and making a trade using schwab-client-js

### **schwab-client-js** gives you complete access to the Schwab REST API using convenient classes and methods. You can stream real-time market data, create and track orders, and retrieve information about your account as well as retrieve different types of market data.

Expand Down
Loading

0 comments on commit 9cb7da8

Please sign in to comment.