robin-sdk 0.1.0__py3-none-any.whl
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.
- robin_sdk/__init__.py +37 -0
- robin_sdk/_compat.py +31 -0
- robin_sdk/_types.py +231 -0
- robin_sdk/auth.py +524 -0
- robin_sdk/client.py +621 -0
- robin_sdk/exceptions.py +65 -0
- robin_sdk/tools/__init__.py +1 -0
- robin_sdk/tools/accounts.py +61 -0
- robin_sdk/tools/equities.py +228 -0
- robin_sdk/tools/market_data.py +86 -0
- robin_sdk/tools/options.py +260 -0
- robin_sdk/tools/watchlists.py +264 -0
- robin_sdk/transport.py +309 -0
- robin_sdk-0.1.0.dist-info/LICENSE +190 -0
- robin_sdk-0.1.0.dist-info/METADATA +174 -0
- robin_sdk-0.1.0.dist-info/RECORD +18 -0
- robin_sdk-0.1.0.dist-info/WHEEL +5 -0
- robin_sdk-0.1.0.dist-info/top_level.txt +1 -0
robin_sdk/__init__.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Robin SDK — Python SDK for Robinhood MCP Trading API.
|
|
3
|
+
|
|
4
|
+
Zero third-party runtime dependencies. Uses only the Python standard library.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
>>> from robin_sdk import RobinClient
|
|
8
|
+
>>> async with RobinClient(client_id="your_id") as client:
|
|
9
|
+
... accounts = await client.get_accounts()
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
__version__ = "0.1.0"
|
|
13
|
+
|
|
14
|
+
from robin_sdk.client import RobinClient
|
|
15
|
+
from robin_sdk.exceptions import (
|
|
16
|
+
RobinSDKError,
|
|
17
|
+
AuthenticationError,
|
|
18
|
+
TokenExpiredError,
|
|
19
|
+
TransportError,
|
|
20
|
+
APIError,
|
|
21
|
+
OrderError,
|
|
22
|
+
AccountError,
|
|
23
|
+
ValidationError,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"RobinClient",
|
|
28
|
+
"RobinSDKError",
|
|
29
|
+
"AuthenticationError",
|
|
30
|
+
"TokenExpiredError",
|
|
31
|
+
"TransportError",
|
|
32
|
+
"APIError",
|
|
33
|
+
"OrderError",
|
|
34
|
+
"AccountError",
|
|
35
|
+
"ValidationError",
|
|
36
|
+
"__version__",
|
|
37
|
+
]
|
robin_sdk/_compat.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Python version compatibility shims.
|
|
3
|
+
|
|
4
|
+
Keeps the rest of the codebase clean by centralising version checks here.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
PYTHON_39 = sys.version_info >= (3, 9)
|
|
12
|
+
PYTHON_310 = sys.version_info >= (3, 10)
|
|
13
|
+
PYTHON_311 = sys.version_info >= (3, 11)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def removeprefix(s: str, prefix: str) -> str:
|
|
17
|
+
"""str.removeprefix backport for Python < 3.9."""
|
|
18
|
+
if PYTHON_39:
|
|
19
|
+
return s.removeprefix(prefix) # type: ignore[attr-defined]
|
|
20
|
+
if s.startswith(prefix):
|
|
21
|
+
return s[len(prefix):]
|
|
22
|
+
return s
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def removesuffix(s: str, suffix: str) -> str:
|
|
26
|
+
"""str.removesuffix backport for Python < 3.9."""
|
|
27
|
+
if PYTHON_39:
|
|
28
|
+
return s.removesuffix(suffix) # type: ignore[attr-defined]
|
|
29
|
+
if suffix and s.endswith(suffix):
|
|
30
|
+
return s[:-len(suffix)]
|
|
31
|
+
return s
|
robin_sdk/_types.py
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Type definitions for Robin SDK.
|
|
3
|
+
|
|
4
|
+
Uses TypedDict for structured response data so callers get editor
|
|
5
|
+
autocompletion without requiring runtime dependencies.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import sys
|
|
11
|
+
from typing import Any, Dict, List, Optional
|
|
12
|
+
|
|
13
|
+
if sys.version_info >= (3, 12):
|
|
14
|
+
from typing import TypedDict, NotRequired
|
|
15
|
+
elif sys.version_info >= (3, 11):
|
|
16
|
+
from typing import TypedDict, NotRequired
|
|
17
|
+
else:
|
|
18
|
+
from typing import TypedDict
|
|
19
|
+
|
|
20
|
+
# Python 3.9-3.10 backport for NotRequired
|
|
21
|
+
class _NotRequiredMeta(type):
|
|
22
|
+
def __getitem__(cls, item):
|
|
23
|
+
return item
|
|
24
|
+
|
|
25
|
+
class NotRequired(metaclass=_NotRequiredMeta): # type: ignore[no-redef]
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
# OAuth / Auth types
|
|
31
|
+
# ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
class TokenData(TypedDict):
|
|
34
|
+
"""Cached token data stored on disk."""
|
|
35
|
+
access_token: str
|
|
36
|
+
refresh_token: Optional[str]
|
|
37
|
+
timestamp: str
|
|
38
|
+
expires_in: int
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class OAuthEndpoints(TypedDict):
|
|
42
|
+
"""Discovered OAuth2 endpoint URLs."""
|
|
43
|
+
auth_url: str
|
|
44
|
+
token_url: str
|
|
45
|
+
scopes: List[str]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# ---------------------------------------------------------------------------
|
|
49
|
+
# Account & Portfolio types
|
|
50
|
+
# ---------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
class Account(TypedDict, total=False):
|
|
53
|
+
"""A Robinhood brokerage account."""
|
|
54
|
+
account_number: str
|
|
55
|
+
id: str
|
|
56
|
+
brokerage_account_type: str
|
|
57
|
+
type: str
|
|
58
|
+
nickname: str
|
|
59
|
+
is_default: bool
|
|
60
|
+
agentic_allowed: bool
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class PortfolioData(TypedDict, total=False):
|
|
64
|
+
"""Portfolio snapshot values."""
|
|
65
|
+
portfolio_value: float
|
|
66
|
+
cash_value: float
|
|
67
|
+
buying_power: float
|
|
68
|
+
equity_value: float
|
|
69
|
+
market_value: float
|
|
70
|
+
total_value: float
|
|
71
|
+
total_equity: float
|
|
72
|
+
extended_hours_equity: float
|
|
73
|
+
dividend_total: float
|
|
74
|
+
total_return: float
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# ---------------------------------------------------------------------------
|
|
78
|
+
# Equity types
|
|
79
|
+
# ---------------------------------------------------------------------------
|
|
80
|
+
|
|
81
|
+
class EquityPosition(TypedDict, total=False):
|
|
82
|
+
"""An open equity position."""
|
|
83
|
+
symbol: str
|
|
84
|
+
ticker: str
|
|
85
|
+
quantity: float
|
|
86
|
+
average_buy_price: float
|
|
87
|
+
value: float
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class EquityQuote(TypedDict, total=False):
|
|
91
|
+
"""Real-time equity quote."""
|
|
92
|
+
symbol: str
|
|
93
|
+
last_trade_price: float
|
|
94
|
+
previous_close: float
|
|
95
|
+
bid_price: float
|
|
96
|
+
ask_price: float
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class EquityOrder(TypedDict, total=False):
|
|
100
|
+
"""An equity order record."""
|
|
101
|
+
order_id: str
|
|
102
|
+
id: str
|
|
103
|
+
state: str
|
|
104
|
+
status: str
|
|
105
|
+
side: str
|
|
106
|
+
symbol: str
|
|
107
|
+
quantity: str
|
|
108
|
+
price: str
|
|
109
|
+
average_fill_price: str
|
|
110
|
+
created_at: str
|
|
111
|
+
updated_at: str
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class TradabilityInfo(TypedDict, total=False):
|
|
115
|
+
"""Whether a symbol can be traded."""
|
|
116
|
+
symbol: str
|
|
117
|
+
tradable: bool
|
|
118
|
+
fractional: bool
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# ---------------------------------------------------------------------------
|
|
122
|
+
# Option types
|
|
123
|
+
# ---------------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
class OptionChain(TypedDict, total=False):
|
|
126
|
+
"""Option chain data for a symbol."""
|
|
127
|
+
symbol: str
|
|
128
|
+
expiration_dates: List[str]
|
|
129
|
+
chain_id: str
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class OptionInstrument(TypedDict, total=False):
|
|
133
|
+
"""An individual option contract."""
|
|
134
|
+
instrument_id: str
|
|
135
|
+
symbol: str
|
|
136
|
+
expiration_date: str
|
|
137
|
+
strike_price: float
|
|
138
|
+
option_type: str
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class OptionQuote(TypedDict, total=False):
|
|
142
|
+
"""Real-time option quote."""
|
|
143
|
+
instrument_id: str
|
|
144
|
+
mark_price: float
|
|
145
|
+
bid_price: float
|
|
146
|
+
ask_price: float
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class OptionPosition(TypedDict, total=False):
|
|
150
|
+
"""An open or closed option position."""
|
|
151
|
+
instrument_id: str
|
|
152
|
+
symbol: str
|
|
153
|
+
quantity: float
|
|
154
|
+
average_price: float
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class OptionOrder(TypedDict, total=False):
|
|
158
|
+
"""An option order record."""
|
|
159
|
+
order_id: str
|
|
160
|
+
id: str
|
|
161
|
+
state: str
|
|
162
|
+
side: str
|
|
163
|
+
quantity: str
|
|
164
|
+
price: str
|
|
165
|
+
created_at: str
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
# ---------------------------------------------------------------------------
|
|
169
|
+
# Watchlist types
|
|
170
|
+
# ---------------------------------------------------------------------------
|
|
171
|
+
|
|
172
|
+
class Watchlist(TypedDict, total=False):
|
|
173
|
+
"""A user watchlist."""
|
|
174
|
+
watchlist_id: str
|
|
175
|
+
id: str
|
|
176
|
+
name: str
|
|
177
|
+
item_count: int
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class WatchlistItem(TypedDict, total=False):
|
|
181
|
+
"""An item in a watchlist."""
|
|
182
|
+
symbol: str
|
|
183
|
+
instrument_id: str
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
# ---------------------------------------------------------------------------
|
|
187
|
+
# Market Data types
|
|
188
|
+
# ---------------------------------------------------------------------------
|
|
189
|
+
|
|
190
|
+
class HistoricalBar(TypedDict, total=False):
|
|
191
|
+
"""An OHLCV price bar."""
|
|
192
|
+
begins_at: str
|
|
193
|
+
open_price: float
|
|
194
|
+
close_price: float
|
|
195
|
+
high_price: float
|
|
196
|
+
low_price: float
|
|
197
|
+
volume: int
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class IndexData(TypedDict, total=False):
|
|
201
|
+
"""Market index information."""
|
|
202
|
+
symbol: str
|
|
203
|
+
name: str
|
|
204
|
+
value: float
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
# ---------------------------------------------------------------------------
|
|
208
|
+
# JSON-RPC types (internal)
|
|
209
|
+
# ---------------------------------------------------------------------------
|
|
210
|
+
|
|
211
|
+
class JSONRPCRequest(TypedDict):
|
|
212
|
+
"""A JSON-RPC 2.0 request."""
|
|
213
|
+
jsonrpc: str
|
|
214
|
+
id: int
|
|
215
|
+
method: str
|
|
216
|
+
params: Dict[str, Any]
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class JSONRPCError(TypedDict, total=False):
|
|
220
|
+
"""A JSON-RPC 2.0 error object."""
|
|
221
|
+
code: int
|
|
222
|
+
message: str
|
|
223
|
+
data: Any
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class JSONRPCResponse(TypedDict, total=False):
|
|
227
|
+
"""A JSON-RPC 2.0 response."""
|
|
228
|
+
jsonrpc: str
|
|
229
|
+
id: int
|
|
230
|
+
result: Any
|
|
231
|
+
error: JSONRPCError
|