Skip to main content

Cliente ligero para API de Schwab: OAuth, REST y Streaming WebSocket

Project description

Schwab SDK (Python)

Cliente ligero para la API de Schwab: OAuth, REST (Trader/Market Data) y Streaming WebSocket.

  • Enfoque: wrappers directos, sin validaciones complejas; manejo robusto de tokens, refresh y reintentos.
  • Cobertura: Accounts, Orders, Market Data y Streaming (Level One, Book, Chart, Screener, Account Activity).

Instalación

Paquete PyPI no oficial (nombre de distribución):

pip install schwab_sdk_unofficial

Import en código (módulo):

from schwab_sdk import Client

⚠️ Nota: El nombre del paquete en PyPI es schwab_sdk_unofficial, pero el import se mantiene como schwab_sdk para una API limpia.

Tabla de contenidos

Requisitos

  • Python 3.9+
  • Instalar dependencias:
pip install requests websocket-client flask

Configuración

Crear .env (o exportar variables de entorno):

SCHWAB_CLIENT_ID=tu_client_id
SCHWAB_CLIENT_SECRET=tu_client_secret
SCHWAB_REDIRECT_URI=https://127.0.0.1:8080/callback

Los tokens se guardan en schwab_tokens.json y se rotan automáticamente (access ~29min; aviso de re-login al expirar refresh ~7d).

Inicio rápido

from schwab_sdk import Client
import os

client = Client(
    os.environ['SCHWAB_CLIENT_ID'],
    os.environ['SCHWAB_CLIENT_SECRET'],
    os.environ.get('SCHWAB_REDIRECT_URI','https://127.0.0.1:8080/callback')
)

# Primer uso: login OAuth (abre el navegador)
client.login()

# REST
quotes = client.market.get_quotes(["AAPL","MSFT"])  # Market Data
accounts = client.account.get_accounts()               # Cuentas

# Streaming (Level One equities)
ws = client.streaming
ws.on_data(lambda f: print("DATA", f))
ws.connect(); ws.login()
ws.equities_subscribe(["AAPL"]) 

Autenticación (OAuth)

  • client.login(timeout=300, auto_open_browser=True)
  • Útiles: client.has_valid_token(), client.refresh_token_now(), client.logout()
  • Internamente: servidor HTTPS callback adhoc (dev), intercambio de code por tokens, auto-refresh y aviso al expirar refresh.

Manejo de requests y errores (REST)

Todas las llamadas REST usan Client._request() con:

  • Headers de Authorization automáticos
  • Reintento de refresh en 401 (una vez) y reenvío inmediato
  • Reintentos con backoff para 429/5xx (exponencial con factor 0.5)

Accounts (accounts.py)

get_account_numbers() -> List[dict]

  • GET /accounts/accountNumbers
  • Devuelve pares accountNumber y hashValue.
  • Ejemplo de respuesta:
[
  {"accountNumber":"12345678","hashValue":"827C...AC12"}
]

get_accounts(params: dict|None=None) -> dict

  • GET /accounts
  • Parámetros (query):
    • fields (opcional): actualmente la API acepta positions para devolver posiciones. Ej.: fields=positions.

get_account_by_id(account_hash: str, params: dict|None=None) -> dict

  • GET /accounts/{accountNumber}
  • account_hash: identificador cifrado de la cuenta (hashValue).
  • Parámetros (query):
    • fields (opcional): positions para incluir posiciones. Ej.: fields=positions.

find_account(last_4_digits: str) -> dict|None

  • Helper que usa get_account_numbers() y filtra por los últimos 4 dígitos para luego llamar get_account_by_id.

get_transactions(account_hash: str, from_date: str|None, to_date: str|None, filters: dict|None=None) -> dict

  • GET /accounts/{accountHash}/transactions

  • UNA FECHA REQUERIDA: puedes pasar solo from_date o solo to_date. Si pasas una sola fecha, el SDK completará la otra para el mismo día:

    • Formato corto YYYY-MM-DD: start → YYYY-MM-DDT00:00:00.000Z, end → YYYY-MM-DDT23:59:59.000Z
    • Formato ISO UTC completo YYYY-MM-DDTHH:MM:SS.ffffffZ: se usa tal cual; si falta la otra fecha, se deriva con las 00:00:00.000Z o 23:59:59.000Z del mismo día.
  • Params:

    • startDate: ISO UTC - YYYY-MM-DDTHH:MM:SS.ffffffZ (o corto YYYY-MM-DD)
    • endDate: ISO UTC - YYYY-MM-DDTHH:MM:SS.ffffffZ (o corto YYYY-MM-DD)
    • filters: dict opcional:
      • types: string con tipos válidos: TRADE, RECEIVE_AND_DELIVER, DIVIDEND_OR_INTEREST, ACH_RECEIPT, ACH_DISBURSEMENT, CASH_RECEIPT, CASH_DISBURSEMENT, ELECTRONIC_FUND, WIRE_OUT, WIRE_IN, JOURNAL
      • symbol: símbolo específico
      • status: estado de transacciones

    Nota: En algunas configuraciones de la API types puede considerarse obligatorio. El SDK no lo exige y lo trata como filtro opcional.

Ejemplo correcto:

from datetime import datetime, timezone, timedelta

# Obtener hashValue
hash_value = client.account.get_account_numbers()[0]['hashValue']

# Crear fechas en formato UTC
start = datetime.now(timezone.utc) - timedelta(days=7)
start = start.replace(hour=0, minute=0, second=0, microsecond=0)
end = datetime.now(timezone.utc).replace(hour=23, minute=59, second=59, microsecond=999999)

# Llamada correcta
transactions = client.account.get_transactions(
    account_number=hash_value,  # ¡Usar hashValue!
    from_date=start.strftime('%Y-%m-%dT%H:%M:%S.%fZ'),
    to_date=end.strftime('%Y-%m-%dT%H:%M:%S.%fZ'),
    filters={"types": "TRADE,DIVIDEND_OR_INTEREST"}  # Opcional
)

get_transaction(account_hash: str, transaction_id: str) -> dict

  • GET /accounts/{accountHash}/transactions/{transactionId}
  • Parámetros (path):
    • account_hash (requerido)
    • transaction_id (requerido): ID numérico de la transacción
  • Devuelve el detalle de una transacción específica.

get_user_preferences() -> dict

  • GET /userPreference
  • Devuelve preferencias del usuario y, cuando aplica, información del streamer necesaria para WebSocket:
    • streamerSocketUrl
    • schwabClientCustomerId
    • schwabClientCorrelId
    • SchwabClientChannel
    • SchwabClientFunctionId
  • Útil para inicializar client.streaming (LOGIN y suscripciones).

Orders (orders.py)

Todas las respuestas incluyen metadatos HTTP y los datos nativos:

{
  "status_code": 200,
  "success": true,
  "headers": {"...": "..."},
  "url": "https://...",
  "elapsed_seconds": 0.42,
  "method": "GET|POST|PUT|DELETE",
  "params": {"...": "..."},
  "data": {},
  "order_id": "..."   
}

get_orders(account_hash, from_entered_time=None, to_entered_time=None, status=None, max_results=None) -> dict

  • GET /accounts/{accountNumber}/orders
  • Requisitos de la API: fromEnteredTime y toEnteredTime son obligatorios (ISO-8601). El SDK los completa a "últimos 60 días" si no los pasas.
  • status (case-insensitive): se normaliza a mayúsculas. Valores aceptados por la API: AWAITING_PARENT_ORDER, AWAITING_CONDITION, AWAITING_STOP_CONDITION, AWAITING_MANUAL_REVIEW, ACCEPTED, AWAITING_UR_OUT, PENDING_ACTIVATION, QUEUED, WORKING, REJECTED, PENDING_CANCEL, CANCELED, PENDING_REPLACE, REPLACED, FILLED, EXPIRED, NEW, AWAITING_RELEASE_TIME, PENDING_ACKNOWLEDGEMENT, PENDING_RECALL, UNKNOWN.
  • maxResults (opcional): límite de registros (por defecto API 3000).
  • Formato de fecha/hora: ISO-8601 YYYY-MM-DDTHH:MM:SS.000Z.

get_all_orders(from_entered_time=None, to_entered_time=None, status=None, max_results=None) -> dict

  • GET /orders
  • Requisitos de la API: fromEnteredTime y toEnteredTime son obligatorios (ISO-8601). Si no los pasas, el SDK usa "últimos 60 días".
  • Filtros idénticos a get_orders (incluye normalización de status).
  • maxResults (opcional): límite de registros (por defecto API 3000).

place_order(account_number: str, order_data: dict) -> dict

  • POST /accounts/{accountNumber}/orders
  • Extrae order_id del header Location cuando está presente.

get_order(account_hash: str, order_id: str) -> dict

  • GET /accounts/{accountNumber}/orders/{orderId}

cancel_order(account_hash: str, order_id: str) -> dict

  • DELETE /accounts/{accountNumber}/orders/{orderId}
  • Intenta extraer order_id desde Location si el servidor lo devuelve.

replace_order(account_hash: str, order_id: str, new_order_data: dict) -> dict

  • PUT /accounts/{accountNumber}/orders/{orderId}
  • Devuelve nuevo order_id (de Location) cuando aplica.

preview_order(account_hash: str, order_data: dict) -> dict

  • POST /accounts/{accountNumber}/previewOrder
  • Intenta extraer order_id desde Location si el servidor lo devuelve.

Helpers de payload

  • build_limit_order(symbol, quantity, price, instruction="BUY")
  • build_market_order(symbol, quantity, instruction="BUY")
  • build_bracket_order(symbol, quantity, entry_price, take_profit_price, stop_loss_price)

Ejemplo (preview):

acc = client.account.get_account_numbers()[0]['hashValue']
order = client.orders.build_limit_order("AAPL", 1, 100.00)
preview = client.orders.preview_order(acc, order)

Market Data (market.py)

get_quotes(symbols: str|List[str], params: dict|None=None) -> dict

  • GET /quotes?symbols=...
  • Parámetros:
    • symbols (requerido): str o lista de símbolos separados por coma. Ej.: AAPL,AMZN,$DJI,/ESH23.
    • params (opcional):
      • fields: subconjunto de datos. Valores: quote, fundamental, extended, reference, regular. Por defecto: todos.
      • indicative: boolean (true|false) para incluir cotizaciones indicativas (p.ej., ETFs). Ej.: indicative=false.

get_quote(symbol: str, params: dict|None=None) -> dict

  • GET /{symbol}/quotes
  • Parámetros:
    • symbol (requerido): símbolo único (ej.: TSLA).
    • params (opcional):
      • fields: igual que en get_quotes.

get_option_chain(symbol: str, params: dict|None=None) -> dict

  • GET /chains

  • Parámetros:

    • symbol (requerido)
    • contractType (opcional): CALL, PUT, ALL
    • strikeCount (opcional): int (nº de strikes arriba/abajo del ATM)
    • includeUnderlyingQuote (opcional): boolean
    • strategy (opcional): SINGLE, ANALYTICAL, COVERED, VERTICAL, STRADDLE, etc.
    • interval (opcional): número (intervalo entre strikes para spreads)
    • strike (opcional): número (strike específico)
    • range (opcional): ITM, NTM, OTM, ALL
    • fromDate / toDate (opcional): YYYY-MM-DD (el SDK también acepta ISO y recorta a fecha)
    • volatility, underlyingPrice, interestRate, daysToExpiration (opcionales): para ANALYTICAL
    • expMonth (opcional): JANDEC, ALL
    • optionType (opcional)
    • entitlement (opcional): tipo de cliente (PN, NP, PP)
  • GET /expirationchain

  • Parámetros:

    • symbol (requerido)

get_price_history(symbol, periodType="month", period=1, frequencyType="daily", frequency=1, startDate=None, endDate=None, params=None) -> dict

  • GET /pricehistory
  • Parámetros:
    • periodType: day|month|year|ytd
    • period: int
    • frequencyType: minute|daily|weekly|monthly
    • frequency: int
    • startDate/endDate (ms desde epoch)
    • Extra opcionales (needExtendedHoursData, etc., según permisos)

get_movers(symbol_id: str, params: dict|None=None) -> dict

  • GET /movers/{symbol_id} (p.ej., $DJI, $SPX, NASDAQ)
  • Parámetros (si aplica):
    • sort: VOLUME, TRADES, PERCENT_CHANGE_UP, PERCENT_CHANGE_DOWN
    • frequency: 0,1,5,10,30,60 (min). Por defecto 0.

get_markets(params: dict|None=None) -> dict

  • GET /markets
  • Parámetros (query):
    • markets (requerido por API): array de equity, option, bond, future, forex (el SDK acepta params={"markets": ...})
    • date (opcional): YYYY-MM-DD (si envías ISO, el SDK recorta a fecha)

get_market_hours(market_id: str, params: dict|None=None) -> dict

  • GET /markets/{market_id} (equity, option, bond, forex)
  • Parámetros (query):
    • date (opcional): YYYY-MM-DD (si envías ISO, el SDK recorta a fecha)

get_instruments(symbols: str|List[str], projection: str, extra_params: dict|None=None) -> dict

  • GET /instruments
  • Parámetros:
    • symbols (requerido): símbolo único o lista separada por comas
    • projection (requerido por API): symbol-search, symbol-regex, desc-search, desc-regex, search, fundamental
      Nota: el SDK no lo exige en la firma pero se recomienda proveerlo.

get_instrument_by_cusip(cusip_id: str, params: dict|None=None) -> dict

  • GET /instruments/{cusip_id}

Streaming WebSocket (streaming.py)

Callbacks

  • on_data(fn) (frames de datos)
  • on_response(fn) (confirmaciones/errores de comandos)
  • on_notify(fn) (heartbeats/avisos)

Flujo básico

ws = client.streaming
ws.on_data(lambda f: print("DATA", f))
ws.on_response(lambda f: print("RESP", f))
ws.on_notify(lambda f: print("NOTIFY", f))
ws.connect(); ws.login()  # Authorization = access token sin "Bearer"
ws.equities_subscribe(["AAPL","MSFT"])             # LEVELONE_EQUITIES
ws.options_subscribe(["AAPL  250926C00257500"])      # LEVELONE_OPTIONS
ws.nasdaq_book(["MSFT"])                             # NASDAQ_BOOK
ws.chart_equity(["AAPL"])                            # CHART_EQUITY
ws.screener_equity(["NYSE_VOLUME_5"])                # SCREENER_EQUITY

Formatos de claves (tabla rápida)

Tipo Formato Ejemplo Notas
Equities Ticker AAPL, MSFT Mayúsculas
Options RRRRRR␣␣YYMMDD[C/P]STRIKE AAPL  250926C00257500 Dos espacios; strike acolchonado (8+ dígitos)
Futures /<root><month><yy> /ESZ25 Root/mes/año en mayúsculas
FuturesOptions ./<root><month><year><C/P><strike> ./OZCZ23C565 Dependiente del feed
Forex PAIR EUR/USD, USD/JPY Separador /
Screener PREFIX_SORTFIELD_FREQUENCY NYSE_VOLUME_5 Prefijo/criterio/frecuencia

Ejemplos por servicio

Level One Options

ws.options_subscribe(["AAPL  250926C00257500"])  # cadena estándar de opciones
# Campos por defecto: 0,2,3,4,8,16,17,18,20,28,29,30,31,37,44

Level One Futures

ws.futures_subscribe(["/ESZ25"])  # E-mini S&P 500 Dec 2025
# Campos por defecto: 0,1,2,3,4,5,8,12,13,18,19,20,24,33

Level One Forex

ws.forex_subscribe(["EUR/USD","USD/JPY"])  
# Campos por defecto: 0,1,2,3,4,5,6,7,8,9,10,11,15,16,17,20,21,27,28,29

Book (Level II)

ws.nasdaq_book(["MSFT"])  # También: ws.nyse_book, ws.options_book
# Campos por defecto: 0 (Symbol), 1 (BookTime), 2 (Bids), 3 (Asks)

Chart (Series)

ws.chart_equity(["AAPL"])      # 0..7: key, open, high, low, close, volume, sequence, chartTime
ws.chart_futures(["/ESZ25"])   # 0..5

Screener

ws.screener_equity(["NYSE_VOLUME_5"])   
ws.screener_options(["CBOE_VOLUME_5"]) 

Account Activity

ws.account_activity()  # Obtiene accountHash y se suscribe

Utilidades

  • subscribe(service, keys, fields=None)
  • add(service, keys)
  • unsubscribe(service, keys) / unsubscribe_service(service, keys)
  • view(service, fields)

Campos recomendados

  • LEVELONE_EQUITIES: 0,1,2,3,4,5,8,10,18,42,33,34,35
  • LEVELONE_OPTIONS: 0,2,3,4,8,16,17,18,20,28,29,30,31,37,44
  • LEVELONE_FUTURES: 0,1,2,3,4,5,8,12,13,18,19,20,24,33
  • CHART_EQUITY: 0,1,2,3,4,5,6,7

Tabla rápida de fields (copiar/pegar)

Servicio CSV de fields
LEVELONE_EQUITIES 0,1,2,3,4,5,8,10,18,42,33,34,35
LEVELONE_OPTIONS 0,2,3,4,8,16,17,18,20,28,29,30,31,37,44
LEVELONE_FUTURES 0,1,2,3,4,5,8,12,13,18,19,20,24,33
LEVELONE_FUTURES_OPTIONS 0,1,2,3,4,5,8,12,13,18,19,20,24,33
LEVELONE_FOREX 0,1,2,3,4,5,6,7,8,9,10,11,15,16,17,20,21,27,28,29
NASDAQ_BOOK 0,1,2,3
NYSE_BOOK 0,1,2,3
OPTIONS_BOOK 0,1,2,3
CHART_EQUITY 0,1,2,3,4,5,6,7
CHART_FUTURES 0,1,2,3,4,5
SCREENER_EQUITY 0,1,2,3,4
SCREENER_OPTION 0,1,2,3,4
ACCT_ACTIVITY 0,1,2

Ejemplos SUBS / ADD / VIEW / UNSUBS por servicio

ws = client.streaming
ws.on_data(lambda f: print("DATA", f))
ws.on_response(lambda f: print("RESP", f))
ws.connect(); ws.login()

# LEVELONE_EQUITIES
ws.equities_subscribe(["AAPL","TSLA"], fields=[0,1,2,3,4,5,8,10,18,42,33,34,35])
ws.equities_add(["MSFT"])                          # agrega sin reemplazar
ws.equities_view([0,1,2,3,5,8,18])                  # cambia campos
ws.equities_unsubscribe(["TSLA"])                  # elimina símbolos

# LEVELONE_OPTIONS
ws.options_subscribe(["AAPL  250926C00257500"], fields=[0,2,3,4,8,16,17,18,20,28,29,30,31,37,44])
ws.options_add(["AAPL  250926P00257500"])
ws.options_view([0,2,3,4,8,16,17,20,28,29,30,31,37,44])
ws.options_unsubscribe(["AAPL  250926C00257500"])

# LEVELONE_FUTURES
ws.futures_subscribe(["/ESZ25"], fields=[0,1,2,3,4,5,8,12,13,18,19,20,24,33])
ws.futures_add(["/NQZ25"]) 
ws.futures_view([0,1,2,3,4,5,8,12,13,18,19,20,24,33])
ws.futures_unsubscribe(["/ESZ25"])

# BOOK (Level II)
ws.nasdaq_book(["MSFT"], fields=[0,1,2,3])
ws.add("NASDAQ_BOOK", ["AAPL"])                    # genérico ADD
ws.view("NASDAQ_BOOK", [0,1,2,3])                   # genérico VIEW
ws.unsubscribe_service("NASDAQ_BOOK", ["MSFT"])    # genérico UNSUBS

# CHART (Series)
ws.chart_equity(["AAPL"], fields=[0,1,2,3,4,5,6,7])
ws.add("CHART_EQUITY", ["MSFT"])                  # genérico ADD
ws.view("CHART_EQUITY", [0,1,2,3,4,5,6,7])          # genérico VIEW
ws.unsubscribe("CHART_EQUITY", ["AAPL"])           # genérico UNSUBS

# SCREENER
ws.screener_equity(["EQUITY_ALL_VOLUME_5"], fields=[0,1,2,3,4])
ws.add("SCREENER_EQUITY", ["NYSE_TRADES_1"])      
ws.view("SCREENER_EQUITY", [0,1,2,3,4])
ws.unsubscribe("SCREENER_EQUITY", ["EQUITY_ALL_VOLUME_5"]) 

# ACCT_ACTIVITY
ws.account_activity(fields=[0,1,2])                  # suscribe actividad
# Para UNSUBS necesitas la misma key usada en la SUBS (account_hash)
account_hash = getattr(client, "_account_hash", None)
if account_hash:
    ws.unsubscribe_service("ACCT_ACTIVITY", [account_hash])

Guía rápida de campos (IDs → significado)

Nota: los mapeos exactos pueden variar según permisos/versión. A continuación, equivalencias prácticas observadas en los frames.

LEVELONE_EQUITIES

ID Campo
0 symbol/key
1 bidPrice
2 askPrice
3 lastPrice
4 bidSize
5 askSize
8 totalVolume
10 referencePrice (open/mark)
18 netChange
42 percentChange

LEVELONE_OPTIONS

ID Campo
0 symbol/key
2 bidPrice
3 askPrice
4 lastPrice
8 totalVolume
16 openInterest
17 daysToExpiration
20 strikePrice
28 delta
29 gamma
30 theta
31 vega
44 impliedVolatility (si provisto)

LEVELONE_FUTURES

ID Campo
0 symbol/key
1 bidPrice
2 askPrice
3 lastPrice
4 bidSize
5 askSize
8 totalVolume
12 openInterest
13 contractDepth/series info (según feed)
18 netChange
19 sessionChange (o días/indicador según feed)
20 percentChange/ratio (según feed)
24 lastSettlement/mark
33 priorSettle

Estructura de frames

  • Confirmaciones (response): { "response": [ { "service":"ADMIN","command":"LOGIN","content":{"code":0,"msg":"..."}} ] }
  • Datos (data): { "service":"LEVELONE_EQUITIES","timestamp":...,"command":"SUBS","content":[{"key":"AAPL",...}] }
  • Notificaciones (notify): { "notify": [ { "heartbeat": "..." } ] }

Cheat-sheet Streamer API (parámetros y comandos)

  1. Conexión y prerequisitos
  • Auth: usa el Access Token del flujo OAuth.
  • IDs de sesión (de GET /userPreference): schwabClientCustomerId, schwabClientCorrelId, SchwabClientChannel, SchwabClientFunctionId.
  • Transporte: WebSocket JSON. Un solo stream por usuario (si abres más: código 12 CLOSE_CONNECTION).
  1. Envoltura de cada comando
  • Campos comunes:
    • service (req.): ADMIN, LEVELONE_EQUITIES, LEVELONE_OPTIONS, LEVELONE_FUTURES, LEVELONE_FUTURES_OPTIONS, LEVELONE_FOREX, NYSE_BOOK, NASDAQ_BOOK, OPTIONS_BOOK, CHART_EQUITY, CHART_FUTURES, SCREENER_EQUITY, SCREENER_OPTION, ACCT_ACTIVITY.
    • command (req.): LOGIN, SUBS, ADD, UNSUBS, VIEW, LOGOUT.
    • requestid (req.): identificador único del request.
    • SchwabClientCustomerId y SchwabClientCorrelId (recomendados): de userPreference.
    • parameters (opcional): depende del servicio/comando.
  • Notas: SUBS sobrescribe lista; ADD agrega; UNSUBS quita; VIEW cambia fields.
  1. ADMIN (sesión)
  • LOGIN (service=ADMIN, command=LOGIN)
    • parameters: Authorization (token sin "Bearer"), SchwabClientChannel, SchwabClientFunctionId.
  • LOGOUT (service=ADMIN, command=LOGOUT)
    • parameters: vacío.
  1. LEVEL ONE (cotizaciones L1)
  • parameters comunes: keys (req., lista CSV), fields (opcional, índices).
  • LEVELONE_EQUITIES: keys tickers en mayúsculas (ej.: AAPL,TSLA).
  • LEVELONE_OPTIONS: keys formato Schwab opciones RRRRRR YYMMDD[C/P]STRIKE.
  • LEVELONE_FUTURES: keys /<root><monthCode><yearCode> (mes: F,G,H,J,K,M,N,Q,U,V,X,Z; año 2 dígitos), ej.: /ESZ25.
  • LEVELONE_FUTURES_OPTIONS: keys ./<root><month><yy><C|P><strike>, ej.: ./OZCZ23C565.
  • LEVELONE_FOREX: keys pares BASE/QUOTE CSV (ej.: EUR/USD,USD/JPY).
  1. BOOK (Level II)
  • servicios: NYSE_BOOK, NASDAQ_BOOK, OPTIONS_BOOK.
  • parameters: keys (req., tickers), fields (opcional, índices de niveles).
  1. CHART (velas streaming)
  • CHART_EQUITY: keys equities; fields índices (OHLCV, time, seq).
  • CHART_FUTURES: keys futuros (mismo formato que L1 futuros); fields índices.
  1. SCREENER (gainers/losers/activos)
  • servicios: SCREENER_EQUITY, SCREENER_OPTION.
  • keys patrón PREFIX_SORTFIELD_FREQUENCY, ej.: EQUITY_ALL_VOLUME_5.
    • PREFIX ejemplos: $COMPX, $DJI, $SPX, INDEX_ALL, NYSE, NASDAQ, OTCBB, EQUITY_ALL, OPTION_PUT, OPTION_CALL, OPTION_ALL.
    • SORTFIELD: VOLUME, TRADES, PERCENT_CHANGE_UP, PERCENT_CHANGE_DOWN, AVERAGE_PERCENT_VOLUME.
    • FREQUENCY: 0,1,5,10,30,60 (min; 0=día completo).
  • fields (opcional): índices de campos del screener.
  1. ACCOUNT (actividad de cuenta)
  • servicio: ACCT_ACTIVITY (SUBS/UNSUBS).
  • keys (req.): identificador arbitario de tu sub; si envías varios, usa el primero.
  • fields (recomendado): 0 (o 0,1,2,3 según ejemplo y necesidad).
  1. Respuestas del servidor
  • Tipos: response (a tus requests), notify (heartbeats), data (flujo de mercado).
  • Códigos clave: 0 SUCCESS, 3 LOGIN_DENIED, 11 SERVICE_NOT_AVAILABLE, 12 CLOSE_CONNECTION, 19 REACHED_SYMBOL_LIMIT, 20 STREAM_CONN_NOT_FOUND, 21 BAD_COMMAND_FORMAT, 26/27/28/29 éxitos de SUBS/UNSUBS/ADD/VIEW.
  1. Delivery Types
  • All Sequence: todo con número de secuencia.
  • Change: sólo campos cambiados (conflado).
  • Whole: mensajes completos con throttling.
  1. Buenas prácticas
  • Haz LOGIN y espera code=0 antes de SUBS/ADD.
  • Para agregar símbolos sin perder los existentes, usa ADD (no SUBS).
  • Cambia fields con VIEW para performance.
  • Maneja notify (heartbeats) y reconecta si se pierden.
  • Reutiliza tu SchwabClientCorrelId durante la sesión.
  • Si ves 19 (límite de símbolos), distribuye cargas (sharding) por servicio/sesión.

Troubleshooting avanzado

  • notify.code=12 (Only one connection): cierra otras sesiones WebSocket activas.
  • response.content.code=3 (Login denied): token inválido/expirado → client.login().
  • response.content.code=21 (Bad command formatting): revisa formato de Authorization (sin Bearer) y claves (espaciado en opciones, mayúsculas).
  • 401 REST persistente: borra schwab_tokens.json y relanza client.login().
  • Latencia alta/frames perdidos: evita reconexiones paralelas; usa la resuscripción automática del SDK.

Contribuciones

¡Tu colaboración es bienvenida! Ideas, issues y PRs ayudan a mejorar el SDK:

  • Abre un issue con detalles claros (entorno, pasos, error esperado/obtenido).
  • Propón mejoras de cobertura de endpoints y ejemplos.
  • Sigue un estilo claro y añade pruebas o ejemplos mínimos cuando sea posible.

Si quieres mantener sesiones de trabajo o discutir roadmap, abre un issue etiquetado como discussion.

Disclaimer

Este proyecto es no oficial y no está afiliado, patrocinado ni respaldado por Charles Schwab & Co., Inc. “Schwab” y otras marcas son propiedad de sus respectivos titulares. El uso de este SDK está sujeto a los términos y condiciones de las APIs de Schwab y a la normativa aplicable. Úsalo bajo tu propio criterio y responsabilidad.


Licencia

MIT (LICENSE)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

schwab_sdk_unofficial-0.1.1.tar.gz (52.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

schwab_sdk_unofficial-0.1.1-py3-none-any.whl (51.1 kB view details)

Uploaded Python 3

File details

Details for the file schwab_sdk_unofficial-0.1.1.tar.gz.

File metadata

  • Download URL: schwab_sdk_unofficial-0.1.1.tar.gz
  • Upload date:
  • Size: 52.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for schwab_sdk_unofficial-0.1.1.tar.gz
Algorithm Hash digest
SHA256 88ce66320b2919bc0c887f3132d406c1a44b6ec13a73f4783d994f6aa7676527
MD5 b3f3272a1f343ab62bc033326ad79458
BLAKE2b-256 ce98a6c827bf1a4ce349bd40eed7df2d4fdec103155737622f2f8208cc6f91de

See more details on using hashes here.

File details

Details for the file schwab_sdk_unofficial-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for schwab_sdk_unofficial-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 e9e0af2b87207ea6768265c7ec23b0398f4f86f03ac73419805acc58fe58e846
MD5 a1ce675b79dc1269de61f33badbbb15b
BLAKE2b-256 ce7cfe4121fe006b320741772cb90ba5b8e0216693885dde888ad87b309907cf

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page