Connecting LumiBot to Schwab for Trading and Order Management
Summary
This guide explains how to connect LumiBot to Charles Schwab’s Trader API for equities and single-leg options. It covers API approval, application credentials, callback URLs, environment settings, first-time OAuth login, and token renewal. It also outlines supported order types and market data, and clarifies that multi-leg option spreads and futures trading are not implemented. The operational guidance describes how to check fresh order and account state, handle replacement and cancellation, and manage partial fills through lifecycle callbacks.
For short cancellation deadlines, it recommends using a monotonic local deadline, processing queued events while waiting, and avoiding frequent broker reads that consume request capacity. It distinguishes dispatching a cancel from receiving a broker response and a terminal order outcome, and advises idempotent handling of callbacks and later reconciliation. The guide also discusses rate limits, unknown broker records, and stale or unavailable account values. These details describe integration behavior, not trading performance; advanced order support is experimental, token lifetimes and API behavior are broker-dependent, and users should validate their setup.
Key ideas
- Schwab access requires an API-approved brokerage account, application credentials, an account number, and a matching callback URL.
- LumiBot supports equities and ETFs, single-leg options, streaming quotes, and historical bars, while multi-leg spreads and futures orders are unavailable.
- OAuth tokens require periodic renewal, with login needed again if the refresh token expires.
- Order cancellation has separate local dispatch, HTTP response, and broker-terminal stages that should be tracked independently.
- Use bounded broker reads and aggregate request budgeting, and treat unknown or unavailable account state carefully.
Tags
Full text
# brokers.schwab
Charles Schwab API Trading with LumiBot
=======================================
.. meta::
:description: Connect LumiBot to the Charles Schwab Trader API for equities and options. Configure OAuth, callback URLs, account settings, and paper or live execution.
Lumibot integrates directly with Charles Schwab's *Trader* API for equities and options. Everything you need is built-in; no external wrapper is required.
Order lifecycle and rejection details
------------------------------------
Broker order observations preserve Schwab's rejection description, including
observations recovered by a direct order lookup and errors deferred during
synchronization. A rejected cancellation is reconciled against the broker's
order state: an already-filled order produces fill evidence, while an
already-canceled order produces cancellation evidence. Other cancellation
errors remain visible. Partial fills reported with a cancellation are processed
before the cancellation callback.
Repeated immediate account-stream disconnects use increasing retry delays,
capped at 30 seconds. A login alone does not reset the retry delay; a connection
must remain healthy for at least 60 seconds. This reduces reconnect storms but
does not remove provider connection or authentication restrictions.
Go directly to :ref:`Schwab prerequisites <schwab-prerequisites>`,
:ref:`credentials <schwab-environment>`, :ref:`first-time OAuth login
<schwab-first-login>`, or :ref:`token renewal <schwab-token-lifecycle>`.
If you want the managed path, `BotSpot <https://botspot.trade/sales?showLogin=1&utm_source=documentation&utm_medium=schwab&utm_campaign=lumibot&utm_content=managed_schwab_text&prompt=I%20want%20to%20connect%20Schwab%20and%20run%20a%20Lumibot%20strategy%20on%20BotSpot.%20Please%20help%20me%20set%20up%20broker%20connections%2C%20monitoring%2C%20and%20paper%20or%20live%20deployment.>`_ can help you connect supported brokers through the website, run paper or live Lumibot strategies, and monitor logs, account state, alerts, audit history, and kill-switch controls without maintaining your own deployment server.
.. _schwab-prerequisites:
Prerequisites
-------------
1. A Schwab brokerage account that is **approved for API access** (apply once in the Schwab Developer Portal).
2. A Schwab **App Key** (sometimes called *Consumer Key*) generated inside your Developer Portal application.
3. The brokerage **account number** you want the bot to trade in.
4. A **callback URL** (HTTPS) you entered when creating the app. For local testing just use ``https://127.0.0.1:8182``.
.. _schwab-environment:
Environment variables
---------------------
.. note::
**Easy Setup with .env File**
LumiBot automatically loads your API credentials from a `.env` file! Simply create a `.env` file in the same folder as your trading strategy and add your Schwab credentials. LumiBot will automatically detect and use these credentials - no additional configuration required.
**Example .env file:**
.. code-block:: bash
# Schwab Configuration
SCHWAB_ACCOUNT_NUMBER=your_account_number
SCHWAB_TOKEN=your_token_if_needed
TRADING_BROKER=schwab
That's it! LumiBot handles the rest automatically.
Set the following before running your strategy (``.env`` file, Render secret, Replit secret, Docker env, etc.):
.. list-table::
:widths: 30 70
:header-rows: 1
* - **Variable**
- **Purpose**
* - ``SCHWAB_ACCOUNT_NUMBER``
- The brokerage account to trade.
* - ``SCHWAB_TOKEN`` *(optional)*
- Base-64 payload pasted from the **first** OAuth login screen – only needed for headless deploys.
* - ``TRADING_BROKER`` *(optional)*
- Force Lumibot to select Schwab (``schwab``) even when other creds are present.
.. _schwab-first-login:
First-time login
----------------
• **Desktop** – run your bot, a browser pops up, log in, click *Allow*. A ``token.json`` file is written next to your strategy. Restart and you're done.
• **Headless / Render / Replit** – the console prints a one-time URL. Open it on any device, log in, copy the payload string that appears, set it as ``SCHWAB_TOKEN`` and restart.
.. _schwab-token-lifecycle:
Token life-cycle
----------------
* Access token ≈ 30 min; refresh token ≈ 7 days.
* Lumibot automatically refreshes the token every ~25 min and rewrites ``token.json``.
* As long as the bot is running (or restarted at least once a week) you will **never see the login page again**.
* If the bot is **offline > 7 days** the refresh token expires – simply run the login flow once more.
Supported functionality
-----------------------
* Equities and ETF trading (market, limit, stop, stop-limit).
* Single-leg options (buy/sell, open/close).
* OTO / one-triggers-other orders for single stock/ETF and option parent/child orders (experimental; live broker validation recommended before production use).
* OCO / one-cancels-other and bracket orders for single stock/ETF and option orders (experimental; live broker validation recommended before production use).
* Streaming quotes for equities/options.
* Historical bars for equities, ETFs, and option contracts (OCC symbol). Futures candle history is not requested. Crypto is not on this API.
Multi-leg option spreads and futures trades are not yet implemented.
Order and position freshness
----------------------------
In live trading, LumiBot refreshes broker state before returning from:
* ``self.get_order(order_id)``
* ``self.get_orders(...)``
* ``self.get_position(asset)``
* ``self.get_positions()``
* ``self.get_cash()``
* ``self.get_portfolio_value()``
After submitting an order, use direct order lookup when checking that exact
order:
.. code-block:: python
submitted = self.submit_order(order)
latest = self.get_order(submitted.identifier)
Schwab can expose a just-submitted order through direct lookup before it appears
in the broad account order list. If your strategy must use ``self.get_orders(...)``
immediately after submitting, add a short LumiBot sleep first:
.. code-block:: python
submitted = self.submit_order(order)
self.sleep(1, process_pending_orders=True)
active_orders = self.get_orders(statuses=Order.ACTIVE_STATUSES)
Use enum status filters for active/open order checks:
.. code-block:: python
active_orders = self.get_orders(statuses=Order.ACTIVE_STATUSES)
Do not use raw string status filters. For duplicate-order guards, filter by
active status and by the exact asset or option contract.
Simple stock and single-leg option ``self.modify_order(...)`` calls use Schwab's
replace-order endpoint. Schwab returns a new broker order id for the replacement;
LumiBot updates the order object's ``identifier`` and keeps the old id in
``previous_identifiers``. For timeout logic where the intended behavior is to
remove the order, prefer ``self.cancel_order(order)`` instead of modifying the
order into a different price. A successful cancel response is acceptance, not
terminal confirmation: the order remains active with ``CANCELLING`` status
until Schwab later reports ``CANCELED``, ``FILLED``, ``EXPIRED``, or a
rejection/error. Do not put an immediate direct order read in front of the
cancellation deadline.
Schwab account history can include rows that are not ordinary strategy orders,
such as mutual funds, sweep/cash-equivalent records, bonds, option exercise
records, or future Schwab asset/order/status values. LumiBot preserves
representable unknown Schwab records as ``Asset.AssetType.UNKNOWN``,
``Order.OrderType.UNKNOWN``, ``Order.OrderSide.UNKNOWN``, or
``Order.OrderStatus.UNKNOWN`` instead of failing the whole broker refresh.
If Schwab cannot return fresh cash or portfolio value, ``self.get_cash()`` and
``self.get_portfolio_value()`` return ``None`` and leave cached values unchanged.
Do not treat ``None`` as zero.
Fast cancellation and request budgets
-------------------------------------
For rejected or expired orders, the strategy's error callback receives the
original Schwab status and its rejection description when the broker supplies
one. If no description is available, the error reports the status alone;
LumiBot does not infer a rejection reason. Repeated terminal snapshots do not
produce duplicate error callbacks.
Separate three measurements when implementing a cancel-after deadline:
* the local time at which the strategy dispatches ``cancel_order``;
* the HTTP response time for the cancel request;
* the later broker-terminal outcome such as ``CANCELED`` or ``FILLED``.
The strategy controls the first measurement. It cannot guarantee the other two
at an exact deadline.
Use a configurable ``cancel_after_seconds`` policy and an absolute
``time.monotonic()`` deadline. Do not use one fixed timeout for every strategy.
Finish expensive chain, quote, and liquidity work before submission; then
process local pending order events while waiting for the deadline:
.. code-block:: python
deadline = time.monotonic() + cancel_after_seconds
while time.monotonic() < deadline and order.is_active():
remaining = deadline - time.monotonic()
self.sleep(min(0.05, remaining), process_pending_orders=True)
if order.is_active():
self.cancel_order(order)
The short ``self.sleep`` calls above process local queued events. They are not
broker polls. Avoid calling ``self.get_order`` every fraction of a second or
placing a broker read immediately before the deadline. Use a bounded exact-order
read later for a missed callback, restart/reconnect, or ambiguous cancel result.
``on_filled_order`` is the fast path for a fill and already includes the filled
order. ``on_canceled_order`` reports terminal cancellation; it does not initiate
the cancel. ``cancel_order`` may return before the queued
``on_canceled_order`` callback runs. Route callbacks and later reconciliation
through one idempotent reducer keyed by the broker order identifier or a stable
causal group.
These ``on_*`` methods are lifecycle callback methods: LumiBot invokes them
after broker observations. ``on_partially_filled_order(position, order, price,
quantity, multiplier)`` receives the newly observed fill delta in ``quantity``;
it is not cumulative across callbacks. A later ``on_filled_order`` receives the
remaining fill delta and must share the same idempotency state.
LumiBot uses Schwab account-activity WebSocket messages to wake exact reads of
locally tracked active orders. REST snapshots and stream-triggered observations
feed one serialized transition reducer, including after login or reconnect. A
30-second broad history poll remains as a healing fallback. This is deliberately
not one-second broad polling: one-second polling multiplies request pressure and
does not remove fill/cancel races.
The REST and account-activity clients share the same refreshable OAuth token
metadata. A token refresh updates the shared token object in place, so a later
stream login or reconnect uses the current access token instead of maintaining
a second credential owner.
Scope blocking to the strategy's actual risk invariant. A cancel-pending order
must block a conflicting replacement for the same exposure. Independent symbols
may continue when capital and risk policy permit. Unknown broker state is not
terminal, but it does not automatically require a strategy-wide freeze.
Schwab developer applications have an application-level order limit for make,
cancel, and replace requests per minute. Treat throttling as an aggregate
broker-call budget across market data, order lists, exact reads, submits,
cancels, and replaces. Do not infer a universal requests-per-second guarantee.
See the `schwab-py order-limit documentation
<https://schwab-py.readthedocs.io/en/latest/getting-started.html#order-limit>`_
and :doc:`lifecycle_methods.on_canceled_order`.
When Schwab returns HTTP 429, LumiBot honors ``Retry-After`` when present and
otherwise applies bounded exponential backoff with jitter for that endpoint
family. A throttled read returns no new observation and never converts the
tracked order to a terminal status.
An authorized local sample of 16 successful cancel HTTP responses ranged from
228 ms to 444 ms, with a 302.5 ms median and 444 ms 95th percentile. This small
sample is not a Schwab service-level guarantee and does not measure terminal
callback visibility. Do not choose a strategy deadline by multiplying these
observations.
Example ``.env``
----------------
.. code-block:: bash
TRADING_BROKER=schwab
SCHWAB_ACCOUNT_NUMBER=12345678
# optional if deploying headless
SCHWAB_TOKEN=YOUR_TOKEN
Example strategy snippet
------------------------
.. code-block:: python
from lumibot.entities import Asset
last = self.get_last_price("SPY")
chains = self.get_chains(Asset("SPY"))
first_expiry = chains.expirations("CALL")[0]
atm_strike = min(chains.strikes(first_expiry), key=lambda s: abs(s-last))
contract = Asset(
symbol="SPY",
asset_type=Asset.AssetType.OPTION,
expiration=first_expiry,
strike=atm_strike,
right=Asset.OptionRight.CALL,
)
order = self.create_order(contract, 1, side="buy")
self.submit_order(order)
Troubleshooting
---------------
* **401/400 errors** at login usually mean your callback URL does not match the value in the Developer Portal **exactly**.
* Keep ``token.json`` out of version control.
* Schwab's API still evolves; join the Lumibot Discord for the latest community fixes.
.. note::
Schwab API access requires a developer account and application approval. You must apply for API access and set up your app in the Schwab Developer Portal.
API Credentials
---------------
To use Schwab with Lumibot, you need to set the following environment variables in your `.env` file:
.. list-table:: Schwab API Credentials
:widths: 30 50 20
:header-rows: 1
* - **Variable**
- **Description**
- **Example**
* - `SCHWAB_API_KEY`
- (old name) – **use `SCHWAB_APP_KEY` instead**. Back-compat supported but
new projects should switch.
- `abc123xyz`
* - `SCHWAB_APP_SECRET`
- Your Schwab API secret (Consumer Secret).
- `supersecret`
* - `SCHWAB_ACCOUNT_NUMBER`
- Your Schwab brokerage account number.
- `12345678`
* - `SCHWAB_BACKEND_CALLBACK_URL`
- The **exact** OAuth2 callback URL that you registered in the Developer
Portal. Defaults to `https://127.0.0.1:8182` for local flows.
- `https://yourdomain.com/callback`
* - `TRADING_BROKER`
- (Optional) Set to `schwab` to force Schwab as the broker.
- `schwab`
* - `SCHWAB_TOKEN`
*(optional)*
- Base64url payload string returned by the **first** OAuth login. Use it
when running in head-less environments (Render, Replit, Docker) so the
bot can bootstrap itself without an interactive prompt.
- `<big-string>`
.. important::
`SCHWAB_TOKEN` is only read **once** (on first run) to build `token.json`.
After that, automatic refresh keeps the file current; you do **not** need to
rotate the env-var every 7 days.
Token Life-cycle & Auto-refresh
-------------------------------
* Access-token ≈ 30 min, refresh-token ≈ 7 days (per Schwab policy).
* Lumibot configures an `OAuth2Session` with ``auto_refresh_url`` so that tokens
refresh themselves quietly in the background every ~25 min.
* The refreshed token is written back to `token.json`; it rolls the 7-day window
forward. As long as the bot is running (or restarted at least once a week)
you never need to log in again.
* In managed environments where a trusted parent process owns broker OAuth
refresh, set ``LUMIBOT_OAUTH_REFRESH_MODE=external``. In that mode LumiBot does
not call the Schwab OAuth refresh endpoint; it reloads an access-token-only
token file when the parent atomically replaces it. Refresh-token fields are
stripped from LumiBot's child broker state in this mode.
* Only if the service is **offline for >7 days** will the refresh-token expire.
In that case repeat the browser login once and redeploy the new payload or
token file.
Creating an App & Getting Keys
------------------------------
1. Register on the `Schwab Developer Portal <https://developer.schwab.com/>`_.
2. Go to **Dashboard → Apps → Create App**.
3. Enter an app name and a **Callback URL** (must be HTTPS, ≤ 256 chars, matches exactly).
4. Request the **Trader API** product, accept terms, and submit.
5. Wait for manual approval (typically 1–3 business days).
6. Once approved, copy your **API Key (Consumer Key)** and **API Secret** from the app details.
OAuth2 Authentication Flow
--------------------------
Schwab uses OAuth2 for authentication. The first time you run your strategy, a browser window will open for you to log in and approve access. A `token.json` file will be created in your strategy directory (or at `SCHWAB_TOKEN_PATH` if set).
- **Access tokens** last 30 minutes; **refresh tokens** last 7 days.
- The `schwab-py` library will auto-refresh tokens as needed.
- If running on a server, run the login flow once locally and copy `token.json` to the server.
- For headless/cloud environments, use the CLI/manual login helper (`schwab.auth.client_from_manual_flow`), which prints a URL to paste into any browser.
- Keep `token.json` secure and out of version control.
- If you delete or move `token.json`, you will need to re-authorize.
.. warning::
If your refresh token expires (after 7 days without re-auth), you must repeat the browser login flow.
**First-time Schwab login (cloud or local)**
- **Cloud (Replit, Render, etc.):**
Deploy the bot and watch the logs for a green line:
`Open https://…/schwab-login in your browser`
Click, sign in, hit **Allow**, wait for "✅ Schwab token saved", then restart the bot.
That's it—no weekly re-login as long as the bot stays active.
- **Local laptop:**
Deploy the bot and Lumibot opens a browser window automatically (same as before).
Complete the login and you're set.
As long as your bot checks Schwab at least once per day, the token
auto-refreshes and you will *not* be asked to log in again.
If the service is stopped for 7+ days, redeploy and repeat the link.
(Optional) override the callback route with
`SCHWAB_REDIRECT_URI=https://YOUR_DOMAIN/schwab-login`.
Sandbox vs Production
---------------------
Schwab offers a **Sandbox** environment for safe testing with synthetic accounts and data.
- Enable Sandbox when creating your app, or promote your app later in the Developer Portal.
- Use the same credentials; only the API base URL changes.
- Use separate apps for production and sandbox to avoid confusion.
Supported Assets & Order Types
------------------------------
.. list-table:: Supported Asset Classes and Order Types
:widths: 20 15 15 15 15 20
:header-rows: 1
* - **Asset**
- **Market**
- **Limit**
- **Stop**
- **Stop-Limit**
- **Advanced**
* - Stocks/ETFs
- ✔
- ✔
- ✔
- ✔
- OTO/OCO/bracket (experimental)
* - Options
- ✔ (buy/sell, open/close)
- ✔
- —
- —
- OTO/OCO/bracket (experimental)
* - Futures
- ✖ (quotes only)
- ✖
- ✖
- ✖
- ✖
* - Crypto
- ✖
- ✖
- ✖
- ✖
- ✖
- Multi-leg/spread options are not yet implemented in Lumibot.
- Schwab OTO, OCO, and bracket orders use Schwab's trigger and one-cancels-other support and should be live-tested with the target account and order shape before relying on them in production.
- **Futures trading is not supported; only streaming quotes are available.**
Market Data
-----------
- Real-time quotes, last price, and option chains work for equities and options. A live read on 2026-09-22 returned SPY quotes around 774 and an SPY 775 call quote around 0.10. Current quotes include bid and ask. Price-history candles do not.
- Daily candles for SPY went back through a 20-year request (2006-09-27 to 2026-09-22, 5027 bars). The every-day helper, which asks for twenty years of daily bars, returned 8469 bars from 1993-01-29 through 2026-09-22. One-minute bars capped at 2026-08-07 even when 90 days were requested. Thirty-minute bars for a one-year request started 2026-01-05. Schwab has no 60-minute frequency and no second bars. LumiBot's hour timestep asks for 30-minute candles and says so. A second timestep returns no bars.
- Price-history candles are last-trade OHLC plus volume and datetime. A live read of SPY stock daily bars and of an SPY option daily series returned only those keys. There is no historical bid or ask on the candle. Days with no trade come back as gaps. Live ``get_quote`` still returns bid and ask. LumiBot does not invent quote bars to fill those gaps.
- Option quotes, last price, chains, and candles work. History is the life of that contract, not a fixed bar cap and not the years requested. The request has to use the OCC symbol. The SPY 775 call expiring 2026-09-22 had 11 daily bars from 2026-09-08. The SPY 775 call expiring 2027-09-17 quoted at 68.31 (bid 67.75, ask 69.63). A 30-day daily request returned 20 bars from 2026-08-25. One-year, two-year, and five-year daily requests all stopped at 95 bars from 2026-03-26. One-minute bars existed and were thin: 227 bars back to 2026-08-06. The Jan 2029 775 call quoted at 126.11 and had 3 daily bars from 2026-09-18.
- A Tesla check on the same day showed less history, not more. The TSLA 380 call expiring 2027-09-17 quoted at bid 76.6, ask 77.3, last 77.14. A 30-day daily request returned 21 bars from 2026-08-24. One-year, two-year, and five-year daily requests all stopped at 72 bars from 2026-05-19. One-minute bars over 48 days returned 194 bars from 2026-08-06. The TSLA 380 call expiring 2029-01-19 quoted at bid 120.0, ask 126.0, last 124.58, and had 5 daily bars from 2026-09-16 plus 34 one-minute bars from the same day. The SPY 95-bar stop is that contract's listing, not an API limit.
- Equity tickers stay equity tickers. ``Asset("BTC")`` and ``Asset("ETH")`` still request ``BTC`` and ``ETH``, and those can be priced and traded as stocks. A live read on 2026-09-22 showed ticker ``BTC`` is the Grayscale Bitcoin Mini Trust ETF (last price 38.265, asset type EQUITY) and ticker ``ETH`` is the Grayscale Ethereum Staking Mini ETF (last price 26.355). ``DOGE`` and ``SOL`` had no instrument. ``LTC`` and ``BCH`` are unrelated stocks. Description search found no spot bitcoin or ethereum pair. The ``$BLX`` and ``$ELX`` indexes stopped on 2023-10-30.
- Only ``asset_type`` crypto is refused. ``get_historical_prices`` and ``get_quote`` for a crypto asset return no bars and do not call Schwab. Spot bitcoin cannot be quoted, charted, or bought through this API. LumiBot's order builder rejects ``asset_type`` crypto before any order HTTP call. A stock order for ticker ``BTC`` is an ETF order. No order was sent.
- Futures quotes can stream. Futures candle history is not requested.
- No extra entitlements required for individual developers.
Rate Limits & Token Expiry
--------------------------
- Schwab developer applications expose a configurable **order limit**: the
number of make, cancel, and replace requests the app may place per minute.
The application's configured value is authoritative for that app.
- Schwab may return HTTP 429 when a request budget or burst limit is exceeded.
Respect ``Retry-After`` when present; otherwise use bounded exponential
backoff with jitter.
- Do not rely on a universal data-requests-per-minute or trade-requests-per-second
value. Budget all broker endpoint families and measure the target app's
actual behavior.
- Access tokens expire after 30 minutes; refresh tokens after 7 days.
Known Issues & Best Practices
-----------------------------
- Initial OAuth requires browser login every 7 days.
- `token.json` must be unique per account/app.
- OTO, OCO, and bracket advanced orders are experimental.
- Callback URL must match exactly (including trailing slash).
- Access tokens last about 30 minutes. While the process is running and ``SCHWAB_APP_SECRET`` is set, LumiBot refreshes in the background and writes the new token back to the token file. Do not also set ``SCHWAB_TOKEN``: every start overwrites that file with the original payload. A live check on 2026-09-22 forced the access token to look expired, and LumiBot refreshed it without another login.
- Secure `token.json` (chmod 600) and rotate secrets regularly.
- Use separate apps for sandbox and production.
- **Attempting to place a futures order returns HTTP 400 "Unsupported instrument".**
- **No official docs for futures endpoints—implementation subject to change.**
Example Strategy
----------------
You can provide your Schwab credentials in several ways:
- By creating a `.env` file in the same directory as your strategy (recommended for local development).
- By setting them as secrets in Replit, or as environment variables in cloud platforms like Render.
- By exporting them as environment variables in your shell.
**Example `.env` file:**
.. code-block:: bash
# .env
TRADING_BROKER=schwab
SCHWAB_ACCOUNT_NUMBER=XXXXXXXX
Then, create your `main.py` (or `strategy.py`) file:
.. code-block:: python
from lumibot.traders import Trader
from lumibot.strategies.strategy import Strategy
class MyStrategy(Strategy):
def initialize(self):
self.sleeptime = "1D"
self.symbol = "SPY"
def on_trading_iteration(self):
last = self.get_last_price(self.symbol)
self.log_message(f"Last price for {self.symbol}: {last}")
asset = self.create_asset(self.symbol)
order = self.create_order(asset, 1, "buy")
self.submit_order(order)
trader = Trader()
strategy = MyStrategy()
trader.add_strategy(strategy)
trader.run_all()
Support & Contact
-----------------
- Schwab Developer Portal: https://developer.schwab.com/
.. note::
For advanced usage and troubleshooting, see the `schwab-py documentation <https://schwab-py.readthedocs.io/>`_ and the Lumibot source code for `Schwab` broker and `SchwabData` data source.
.. important::
The example above shows what *our strategy* did in a sandbox environment; it is **not** investment advice.
.. warning::
This integration is for educational purposes only. Consult a qualified financial advisor before trading real funds.Shown in full with attribution under the source's licence. Licence: GPL-3.0
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.