tradier-api-client 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. tradier_api_client-0.1.1/MANIFEST.in +7 -0
  2. tradier_api_client-0.1.1/PKG-INFO +203 -0
  3. tradier_api_client-0.1.1/README.md +191 -0
  4. tradier_api_client-0.1.1/pyproject.toml +27 -0
  5. tradier_api_client-0.1.1/setup.cfg +4 -0
  6. tradier_api_client-0.1.1/tradier_api_client/__init__.py +12 -0
  7. tradier_api_client-0.1.1/tradier_api_client/decorators.py +21 -0
  8. tradier_api_client-0.1.1/tradier_api_client/enc_dec.py +103 -0
  9. tradier_api_client-0.1.1/tradier_api_client/helper_functions.py +41 -0
  10. tradier_api_client-0.1.1/tradier_api_client/rest/__init__.py +6 -0
  11. tradier_api_client-0.1.1/tradier_api_client/rest/extensions/__init__.py +4 -0
  12. tradier_api_client-0.1.1/tradier_api_client/rest/extensions/options.py +454 -0
  13. tradier_api_client-0.1.1/tradier_api_client/rest/extensions/orders.py +475 -0
  14. tradier_api_client-0.1.1/tradier_api_client/rest/models/__init__.py +3 -0
  15. tradier_api_client-0.1.1/tradier_api_client/rest/models/orders.py +106 -0
  16. tradier_api_client-0.1.1/tradier_api_client/rest/models/orders_fixed.py +241 -0
  17. tradier_api_client-0.1.1/tradier_api_client/rest/rest_client.py +740 -0
  18. tradier_api_client-0.1.1/tradier_api_client/streaming/__init__.py +6 -0
  19. tradier_api_client-0.1.1/tradier_api_client/streaming/streaming_client.py +530 -0
  20. tradier_api_client-0.1.1/tradier_api_client/streaming/websocket_stream.py +136 -0
  21. tradier_api_client-0.1.1/tradier_api_client.egg-info/PKG-INFO +203 -0
  22. tradier_api_client-0.1.1/tradier_api_client.egg-info/SOURCES.txt +23 -0
  23. tradier_api_client-0.1.1/tradier_api_client.egg-info/dependency_links.txt +1 -0
  24. tradier_api_client-0.1.1/tradier_api_client.egg-info/requires.txt +3 -0
  25. tradier_api_client-0.1.1/tradier_api_client.egg-info/top_level.txt +1 -0
@@ -0,0 +1,7 @@
1
+ # Exclude tests from source distributions
2
+ prune tests
3
+
4
+ # Also prune common local build artifacts
5
+ prune build
6
+ prune dist
7
+ prune .pytest_cache
@@ -0,0 +1,203 @@
1
+ Metadata-Version: 2.4
2
+ Name: tradier-api-client
3
+ Version: 0.1.1
4
+ Summary: Tradier REST + Streaming API client
5
+ Author: Your Name
6
+ License: Proprietary
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: requests>=2.25.1
10
+ Requires-Dist: cryptography>=41
11
+ Requires-Dist: websocket-client>=1.5
12
+
13
+ # tradier_api_client
14
+
15
+ Python client for the Tradier Brokerage API (REST) plus optional streaming helpers.
16
+
17
+ This repo is intended to give you a lightweight, script-friendly way to call Tradier endpoints (quotes, options, orders, account data) using a single `RestClient`.
18
+
19
+ > Note: Some examples below are adapted from `tests/real_data.py` which is intended for *local* use with a real API key. Don’t commit keys.
20
+
21
+ ---
22
+
23
+ ## Installation
24
+
25
+ From source (editable):
26
+
27
+ ```bash
28
+ python -m pip install -U pip
29
+ python -m pip install -e .
30
+ ```
31
+
32
+ Regular install (from the cloned repo):
33
+
34
+ ```bash
35
+ python -m pip install .
36
+ ```
37
+
38
+ ---
39
+
40
+ ## Authentication
41
+
42
+ Set your Tradier API key in an environment variable.
43
+
44
+ ### Windows (cmd.exe)
45
+
46
+ ```bat
47
+ set API_KEY=your_key_here
48
+ ```
49
+
50
+ ### macOS/Linux
51
+
52
+ ```bash
53
+ export API_KEY=your_key_here
54
+ ```
55
+
56
+ ---
57
+
58
+ ## Quick start
59
+
60
+ ### Create a `RestClient`
61
+
62
+ The tests create a client like this:
63
+
64
+ ```python
65
+ import os
66
+ from tradier_api_client import RestClient
67
+
68
+ client = RestClient(
69
+ base_url="https://sandbox.tradier.com/v1", # use https://api.tradier.com/v1 for production
70
+ api_key=os.environ.get("API_KEY"),
71
+ verbose=True,
72
+ )
73
+
74
+ print("Account:", client.account_number)
75
+ ```
76
+
77
+ ---
78
+
79
+ ## Common REST examples
80
+
81
+ ### 1) Quotes
82
+
83
+ ```python
84
+ quotes = client.get_quotes(["AAPL", "MSFT"])
85
+ print(quotes)
86
+ ```
87
+
88
+ ### 2) Historical quotes
89
+
90
+ ```python
91
+ history = client.get_historical_quotes("AAPL", start="1979-01-01")
92
+ print(history)
93
+ ```
94
+
95
+ ### 3) Option expirations + option chains
96
+
97
+ ```python
98
+ expirations_resp = client.get_option_expirations("AAPL")
99
+ expirations = expirations_resp.get("expirations", {}).get("date", [])
100
+ print(expirations)
101
+
102
+ if expirations:
103
+ chain = client.get_option_chains("AAPL", expirations[0])
104
+ print(chain)
105
+ ```
106
+
107
+ ### 4) Options helper workflow (OCC symbols)
108
+
109
+ The repo includes an `OptionsWrapper` and helper utilities used by `tests/real_data.py`.
110
+
111
+ ```python
112
+ from tradier_api_client.rest.extensions.options import (
113
+ OptionsWrapper,
114
+ parse_occ,
115
+ filter_options_occ,
116
+ filter_for_tradable_options_strike_plus_bid,
117
+ )
118
+
119
+ ow = OptionsWrapper(client)
120
+ occ_symbols = ow.get_call_options_occ_symbols_list("AAPL")
121
+
122
+ # Parse OCC symbols into structured info
123
+ parsed = parse_occ(occ_symbols)
124
+
125
+ # Filter by expiration window and strike constraints (example)
126
+ filtered_occ = filter_options_occ(options=occ_symbols, exp_end="2027-08-30", exp_start="2027-01-01")
127
+
128
+ # Quote options and filter for tradable candidates
129
+ quotes = client.get_quotes(filtered_occ)
130
+ tradable = filter_for_tradable_options_strike_plus_bid(
131
+ quotes=quotes,
132
+ occ_symbols=filtered_occ,
133
+ target_price=5.5,
134
+ )
135
+ print(tradable)
136
+ ```
137
+
138
+ ### 5) Account endpoints (example: gain/loss)
139
+
140
+ ```python
141
+ gain_loss = client.get_gain_loss(client.account_number)
142
+ print(gain_loss)
143
+ ```
144
+
145
+ ---
146
+
147
+ ## Orders
148
+
149
+ The order helpers live under `tradier_api_client.rest.extensions.orders`.
150
+
151
+ ### Equity limit buy (example)
152
+
153
+ ```python
154
+ from tradier_api_client.rest.extensions.orders import equity_limit
155
+
156
+ order_request = equity_limit(
157
+ symbol="AAPL",
158
+ side="buy",
159
+ quantity=1,
160
+ price=150,
161
+ duration="gtc",
162
+ )
163
+
164
+ resp = client.place_order(client.account_number, order_request)
165
+ print(resp)
166
+ ```
167
+
168
+ ### Read / cancel orders
169
+
170
+ ```python
171
+ orders = client.get_orders(client.account_number)
172
+ print(orders)
173
+
174
+ # If you have an order id:
175
+ # order = client.get_order(client.account_number, "12345678")
176
+ # print(order)
177
+
178
+ # cancel_resp = client.cancel_order(client.account_number, "12345678")
179
+ # print(cancel_resp)
180
+ ```
181
+
182
+ ---
183
+
184
+ ## Running tests (local repo)
185
+
186
+ This project includes tests/integration scripts under `tests/`.
187
+
188
+ - Some scripts (like `tests/real_data.py`) may call live/sandbox endpoints and require `API_KEY`.
189
+ - The `tests/` folder is **not** intended to be installed with the client distribution.
190
+
191
+ To run tests locally:
192
+
193
+ ```bash
194
+ python -m pip install -U pytest
195
+ pytest -q
196
+ ```
197
+
198
+ ---
199
+
200
+ ## Notes / troubleshooting
201
+
202
+ - **Sandbox vs production**: use `https://sandbox.tradier.com/v1` for paper testing and `https://api.tradier.com/v1` for production.
203
+ - If you see authentication errors, confirm `API_KEY` is set in the environment used by your Python interpreter.
@@ -0,0 +1,191 @@
1
+ # tradier_api_client
2
+
3
+ Python client for the Tradier Brokerage API (REST) plus optional streaming helpers.
4
+
5
+ This repo is intended to give you a lightweight, script-friendly way to call Tradier endpoints (quotes, options, orders, account data) using a single `RestClient`.
6
+
7
+ > Note: Some examples below are adapted from `tests/real_data.py` which is intended for *local* use with a real API key. Don’t commit keys.
8
+
9
+ ---
10
+
11
+ ## Installation
12
+
13
+ From source (editable):
14
+
15
+ ```bash
16
+ python -m pip install -U pip
17
+ python -m pip install -e .
18
+ ```
19
+
20
+ Regular install (from the cloned repo):
21
+
22
+ ```bash
23
+ python -m pip install .
24
+ ```
25
+
26
+ ---
27
+
28
+ ## Authentication
29
+
30
+ Set your Tradier API key in an environment variable.
31
+
32
+ ### Windows (cmd.exe)
33
+
34
+ ```bat
35
+ set API_KEY=your_key_here
36
+ ```
37
+
38
+ ### macOS/Linux
39
+
40
+ ```bash
41
+ export API_KEY=your_key_here
42
+ ```
43
+
44
+ ---
45
+
46
+ ## Quick start
47
+
48
+ ### Create a `RestClient`
49
+
50
+ The tests create a client like this:
51
+
52
+ ```python
53
+ import os
54
+ from tradier_api_client import RestClient
55
+
56
+ client = RestClient(
57
+ base_url="https://sandbox.tradier.com/v1", # use https://api.tradier.com/v1 for production
58
+ api_key=os.environ.get("API_KEY"),
59
+ verbose=True,
60
+ )
61
+
62
+ print("Account:", client.account_number)
63
+ ```
64
+
65
+ ---
66
+
67
+ ## Common REST examples
68
+
69
+ ### 1) Quotes
70
+
71
+ ```python
72
+ quotes = client.get_quotes(["AAPL", "MSFT"])
73
+ print(quotes)
74
+ ```
75
+
76
+ ### 2) Historical quotes
77
+
78
+ ```python
79
+ history = client.get_historical_quotes("AAPL", start="1979-01-01")
80
+ print(history)
81
+ ```
82
+
83
+ ### 3) Option expirations + option chains
84
+
85
+ ```python
86
+ expirations_resp = client.get_option_expirations("AAPL")
87
+ expirations = expirations_resp.get("expirations", {}).get("date", [])
88
+ print(expirations)
89
+
90
+ if expirations:
91
+ chain = client.get_option_chains("AAPL", expirations[0])
92
+ print(chain)
93
+ ```
94
+
95
+ ### 4) Options helper workflow (OCC symbols)
96
+
97
+ The repo includes an `OptionsWrapper` and helper utilities used by `tests/real_data.py`.
98
+
99
+ ```python
100
+ from tradier_api_client.rest.extensions.options import (
101
+ OptionsWrapper,
102
+ parse_occ,
103
+ filter_options_occ,
104
+ filter_for_tradable_options_strike_plus_bid,
105
+ )
106
+
107
+ ow = OptionsWrapper(client)
108
+ occ_symbols = ow.get_call_options_occ_symbols_list("AAPL")
109
+
110
+ # Parse OCC symbols into structured info
111
+ parsed = parse_occ(occ_symbols)
112
+
113
+ # Filter by expiration window and strike constraints (example)
114
+ filtered_occ = filter_options_occ(options=occ_symbols, exp_end="2027-08-30", exp_start="2027-01-01")
115
+
116
+ # Quote options and filter for tradable candidates
117
+ quotes = client.get_quotes(filtered_occ)
118
+ tradable = filter_for_tradable_options_strike_plus_bid(
119
+ quotes=quotes,
120
+ occ_symbols=filtered_occ,
121
+ target_price=5.5,
122
+ )
123
+ print(tradable)
124
+ ```
125
+
126
+ ### 5) Account endpoints (example: gain/loss)
127
+
128
+ ```python
129
+ gain_loss = client.get_gain_loss(client.account_number)
130
+ print(gain_loss)
131
+ ```
132
+
133
+ ---
134
+
135
+ ## Orders
136
+
137
+ The order helpers live under `tradier_api_client.rest.extensions.orders`.
138
+
139
+ ### Equity limit buy (example)
140
+
141
+ ```python
142
+ from tradier_api_client.rest.extensions.orders import equity_limit
143
+
144
+ order_request = equity_limit(
145
+ symbol="AAPL",
146
+ side="buy",
147
+ quantity=1,
148
+ price=150,
149
+ duration="gtc",
150
+ )
151
+
152
+ resp = client.place_order(client.account_number, order_request)
153
+ print(resp)
154
+ ```
155
+
156
+ ### Read / cancel orders
157
+
158
+ ```python
159
+ orders = client.get_orders(client.account_number)
160
+ print(orders)
161
+
162
+ # If you have an order id:
163
+ # order = client.get_order(client.account_number, "12345678")
164
+ # print(order)
165
+
166
+ # cancel_resp = client.cancel_order(client.account_number, "12345678")
167
+ # print(cancel_resp)
168
+ ```
169
+
170
+ ---
171
+
172
+ ## Running tests (local repo)
173
+
174
+ This project includes tests/integration scripts under `tests/`.
175
+
176
+ - Some scripts (like `tests/real_data.py`) may call live/sandbox endpoints and require `API_KEY`.
177
+ - The `tests/` folder is **not** intended to be installed with the client distribution.
178
+
179
+ To run tests locally:
180
+
181
+ ```bash
182
+ python -m pip install -U pytest
183
+ pytest -q
184
+ ```
185
+
186
+ ---
187
+
188
+ ## Notes / troubleshooting
189
+
190
+ - **Sandbox vs production**: use `https://sandbox.tradier.com/v1` for paper testing and `https://api.tradier.com/v1` for production.
191
+ - If you see authentication errors, confirm `API_KEY` is set in the environment used by your Python interpreter.
@@ -0,0 +1,27 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "tradier-api-client"
7
+ dynamic=["version"]
8
+ description = "Tradier REST + Streaming API client"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { text = "Proprietary" } # or: { file = "LICENSE" }
12
+ authors = [{ name = "Your Name" }]
13
+
14
+ # This replaces install_requires from setup.py
15
+ dependencies = [
16
+ "requests>=2.25.1",
17
+ "cryptography>=41",
18
+ "websocket-client>=1.5"
19
+ ]
20
+
21
+ [tool.setuptools.packages.find]
22
+ where = ["."]
23
+ include = ["tradier_api_client*"]
24
+ exclude = ["tests", "build", "dist"]
25
+
26
+ [tool.setuptools.dynamic]
27
+ version = {attr = "tradier_api_client.__version__"}
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,12 @@
1
+ """
2
+ Tradier REST and Streaming client
3
+ """
4
+ import logging
5
+
6
+ from tradier_api_client.rest import RestClient
7
+ from tradier_api_client.streaming.streaming_client import StreamingClient
8
+
9
+ logging.getLogger(__name__).addHandler(logging.NullHandler())
10
+
11
+ __version__ = "0.1.1"
12
+ __all__ = ["StreamingClient", "RestClient"]
@@ -0,0 +1,21 @@
1
+ """
2
+ Common decorators
3
+ """
4
+ import functools
5
+
6
+
7
+ def is_authenticated():
8
+ """
9
+ Checks whether the api_key is set on the object
10
+ """
11
+
12
+ def decorator(method):
13
+ @functools.wraps(method)
14
+ def wrapper(self, *args, **kwargs):
15
+ if not getattr(self, "authenticated", None):
16
+ raise Exception("API key is not set, API call will fail")
17
+ return method(self, *args, **kwargs)
18
+
19
+ return wrapper
20
+
21
+ return decorator
@@ -0,0 +1,103 @@
1
+ """
2
+ EndDec util
3
+ """
4
+ import os
5
+ import string
6
+ from cryptography.hazmat.primitives.ciphers.aead import AESGCM
7
+ from cryptography.hazmat.primitives import hashes
8
+ from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
9
+
10
+
11
+ class EncDec:
12
+ """
13
+ EndDec util
14
+ """
15
+
16
+ def __init__(self):
17
+ # Base62 alphabet: only alphanumeric characters.
18
+ self.BASE62_ALPHABET = string.digits + string.ascii_uppercase + string.ascii_lowercase # 0-9, A-Z, a-z
19
+
20
+ def base62_encode(self, data: bytes) -> str:
21
+ """
22
+ EndDec util
23
+ """
24
+ num = int.from_bytes(data, 'big')
25
+ if num == 0:
26
+ return self.BASE62_ALPHABET[0]
27
+ base = len(self.BASE62_ALPHABET)
28
+ encoded = []
29
+ while num:
30
+ num, rem = divmod(num, base)
31
+ encoded.append(self.BASE62_ALPHABET[rem])
32
+ encoded.reverse()
33
+ return ''.join(encoded)
34
+
35
+ def base62_decode(self, s: str) -> bytes:
36
+ """
37
+ EndDec util
38
+ """
39
+ base = len(self.BASE62_ALPHABET)
40
+ num = 0
41
+ for char in s:
42
+ num = num * base + self.BASE62_ALPHABET.index(char)
43
+ # Calculate the number of bytes needed.
44
+ byte_length = (num.bit_length() + 7) // 8
45
+ return num.to_bytes(byte_length, 'big')
46
+
47
+ def derive_key(self, secret: str, salt: bytes) -> bytes:
48
+ """
49
+ EndDec util
50
+ """
51
+ # Derive a 32-byte key using PBKDF2HMAC (AES-256 needs 32 bytes).
52
+ kdf = PBKDF2HMAC(
53
+ algorithm=hashes.SHA256(),
54
+ length=32,
55
+ salt=salt,
56
+ iterations=100000,
57
+ )
58
+ return kdf.derive(secret.encode())
59
+
60
+ def encrypt_base62(self, plaintext: str, secret: str) -> str:
61
+ """
62
+ EndDec util
63
+ """
64
+ # Generate a random 16-byte salt.
65
+ salt = os.urandom(16)
66
+ key = self.derive_key(secret, salt)
67
+ aesgcm = AESGCM(key)
68
+ # Generate a random 12-byte nonce (recommended for AESGCM).
69
+ nonce = os.urandom(12)
70
+ ciphertext = aesgcm.encrypt(nonce, plaintext.encode(), None)
71
+
72
+ # Combine salt + nonce + ciphertext.
73
+ encrypted_bytes = salt + nonce + ciphertext
74
+
75
+ # Encode using Base62.
76
+ encrypted_b62 = self.base62_encode(encrypted_bytes)
77
+
78
+ # Enforce the length constraint.
79
+ if len(encrypted_b62) >= 255:
80
+ raise ValueError("Encrypted string exceeds the length limit of 255 characters.")
81
+
82
+ return encrypted_b62
83
+
84
+ def decrypt_base62(self, encrypted_b62: str, secret: str) -> str:
85
+ """
86
+ EndDec util
87
+ """
88
+ encrypted_bytes = self.base62_decode(encrypted_b62)
89
+
90
+ # Extract salt (first 16 bytes), nonce (next 12 bytes), and the ciphertext.
91
+ salt = encrypted_bytes[:16]
92
+ nonce = encrypted_bytes[16:28]
93
+ ciphertext = encrypted_bytes[28:]
94
+ key = self.derive_key(secret, salt)
95
+ aesgcm = AESGCM(key)
96
+ plaintext = aesgcm.decrypt(nonce, ciphertext, None)
97
+ return plaintext.decode('utf-8')
98
+
99
+
100
+ if __name__ == '__main__':
101
+ encrypted = EncDec().encrypt_base62("yunind7.tradier", "Xjyrmnfg@321")
102
+ print(encrypted)
103
+ print(EncDec().decrypt_base62(encrypted, "Xjyrmnfg@321"))
@@ -0,0 +1,41 @@
1
+ """
2
+ General utility functions for the Tradier API client.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Any, Optional
8
+
9
+
10
+ def log_for_level(
11
+ logger,
12
+ level: int,
13
+ message: str,
14
+ *,
15
+ exc: Optional[BaseException] = None,
16
+ exc_info: Any = None,
17
+ stack_info: bool = False,
18
+ extra: Optional[dict] = None,
19
+ ):
20
+ """Log *message* if *logger* is enabled for *level*.
21
+
22
+ This is a tiny helper to avoid building/logging messages when the level is disabled.
23
+
24
+ Args:
25
+ logger: A ``logging.Logger`` instance.
26
+ level: A stdlib logging level (e.g. ``logging.INFO``).
27
+ message: The message string to log.
28
+ exc: Optional exception instance to attach (equivalent to ``exc_info=exc``).
29
+ exc_info: Passed through to ``logger.log(..., exc_info=...)``. If both ``exc`` and
30
+ ``exc_info`` are provided, ``exc_info`` wins.
31
+ stack_info: Passed through to the logger.
32
+ extra: Passed through to the logger.
33
+ """
34
+ if not logger or not logger.isEnabledFor(level):
35
+ return
36
+
37
+ # Support passing an exception instance directly.
38
+ if exc_info is None and exc is not None:
39
+ exc_info = exc
40
+
41
+ logger.log(level, message, exc_info=exc_info, stack_info=stack_info, extra=extra)
@@ -0,0 +1,6 @@
1
+ """
2
+ Tradier Rest Client
3
+ """
4
+ from .rest_client import RestClient
5
+
6
+ __all__ = ["RestClient"]
@@ -0,0 +1,4 @@
1
+ from .options import OptionsWrapper
2
+ from .orders import OrderWrapper
3
+
4
+ __all__ = ['OptionsWrapper', 'OrderWrapper']