finatic-server-python 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.
- finatic_server/__init__.py +127 -0
- finatic_server/core/__init__.py +6 -0
- finatic_server/core/api_client.py +868 -0
- finatic_server/core/client.py +577 -0
- finatic_server/types/__init__.py +103 -0
- finatic_server/types/auth.py +142 -0
- finatic_server/types/broker.py +147 -0
- finatic_server/types/common.py +177 -0
- finatic_server/types/orders.py +74 -0
- finatic_server/types/portfolio.py +61 -0
- finatic_server/utils/__init__.py +17 -0
- finatic_server/utils/errors.py +62 -0
- finatic_server_python-0.1.0.dist-info/METADATA +330 -0
- finatic_server_python-0.1.0.dist-info/RECORD +16 -0
- finatic_server_python-0.1.0.dist-info/WHEEL +5 -0
- finatic_server_python-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Custom exception classes for the Finatic Server SDK."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FinaticError(Exception):
|
|
7
|
+
"""Base exception for all Finatic SDK errors."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, message: str, code: Optional[str] = None):
|
|
10
|
+
self.message = message
|
|
11
|
+
self.code = code or "UNKNOWN_ERROR"
|
|
12
|
+
super().__init__(self.message)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ApiError(FinaticError):
|
|
16
|
+
"""Raised when an API request fails."""
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
message: str,
|
|
21
|
+
status_code: Optional[int] = None,
|
|
22
|
+
response_data: Optional[Dict[str, Any]] = None
|
|
23
|
+
):
|
|
24
|
+
self.status_code = status_code
|
|
25
|
+
self.response_data = response_data or {}
|
|
26
|
+
super().__init__(message, f"API_ERROR_{status_code}" if status_code else "API_ERROR")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class AuthenticationError(FinaticError):
|
|
30
|
+
"""Raised when authentication fails."""
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
class AuthorizationError(FinaticError):
|
|
34
|
+
"""Raised when access is denied due to insufficient permissions."""
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
class ValidationError(FinaticError):
|
|
38
|
+
"""Raised when request validation fails."""
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class RateLimitError(ApiError):
|
|
43
|
+
"""Raised when rate limits are exceeded."""
|
|
44
|
+
|
|
45
|
+
def __init__(self, message: str, retry_after: Optional[int] = None):
|
|
46
|
+
self.retry_after = retry_after
|
|
47
|
+
super().__init__(message, 429)
|
|
48
|
+
self.code = "RATE_LIMIT_ERROR"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class NetworkError(FinaticError):
|
|
52
|
+
"""Raised when network connectivity issues occur."""
|
|
53
|
+
|
|
54
|
+
def __init__(self, message: str):
|
|
55
|
+
super().__init__(message, "NETWORK_ERROR")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class TimeoutError(FinaticError):
|
|
59
|
+
"""Raised when requests timeout."""
|
|
60
|
+
|
|
61
|
+
def __init__(self, message: str):
|
|
62
|
+
super().__init__(message, "TIMEOUT_ERROR")
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: finatic-server-python
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for Finatic Server API
|
|
5
|
+
Author-email: Finatic <support@finatic.dev>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/finatic/finatic-server-python
|
|
8
|
+
Project-URL: Documentation, https://docs.finatic.com/python
|
|
9
|
+
Project-URL: Repository, https://github.com/finatic/finatic-server-python
|
|
10
|
+
Project-URL: Issues, https://github.com/finatic/finatic-server-python/issues
|
|
11
|
+
Keywords: finatic,trading,finance,api,sdk
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
23
|
+
Requires-Python: >=3.8
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
Requires-Dist: requests>=2.28.0
|
|
26
|
+
Requires-Dist: aiohttp>=3.8.0
|
|
27
|
+
Requires-Dist: pydantic>=2.0.0
|
|
28
|
+
Requires-Dist: typing-extensions>=4.0.0
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
33
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
34
|
+
Requires-Dist: isort>=5.12.0; extra == "dev"
|
|
35
|
+
Requires-Dist: flake8>=6.0.0; extra == "dev"
|
|
36
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
37
|
+
Requires-Dist: pre-commit>=3.0.0; extra == "dev"
|
|
38
|
+
|
|
39
|
+
# Finatic Server Python SDK
|
|
40
|
+
|
|
41
|
+
A Python SDK for integrating with Finatic's server-side trading and portfolio management APIs.
|
|
42
|
+
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install finatic-server-python
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Quick Start
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
import asyncio
|
|
53
|
+
from finatic_server import FinaticServerClient
|
|
54
|
+
|
|
55
|
+
async def main():
|
|
56
|
+
# Initialize with API key
|
|
57
|
+
client = FinaticServerClient("your-api-key")
|
|
58
|
+
|
|
59
|
+
# Option 1: Portal Authentication
|
|
60
|
+
await client.start_session()
|
|
61
|
+
portal_url = await client.get_portal_url()
|
|
62
|
+
print(f"User should visit: {portal_url}")
|
|
63
|
+
|
|
64
|
+
# After user completes authentication in portal, get user info
|
|
65
|
+
user_info = await client.get_session_user()
|
|
66
|
+
print(f"Authenticated user: {user_info['user_id']}")
|
|
67
|
+
|
|
68
|
+
# Option 2: Direct Authentication (if you know the user ID)
|
|
69
|
+
# client = FinaticServerClient("your-api-key", user_id="user123")
|
|
70
|
+
# await client.start_session()
|
|
71
|
+
|
|
72
|
+
# Now you can access broker data
|
|
73
|
+
brokers = await client.get_broker_list()
|
|
74
|
+
print(f"Available brokers: {len(brokers)}")
|
|
75
|
+
|
|
76
|
+
# Get all orders across all pages
|
|
77
|
+
all_orders = await client.get_all_broker_orders()
|
|
78
|
+
print(f"Total orders: {len(all_orders)}")
|
|
79
|
+
|
|
80
|
+
# Run the example
|
|
81
|
+
asyncio.run(main())
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Authentication Flow
|
|
85
|
+
|
|
86
|
+
The SDK supports two authentication methods:
|
|
87
|
+
|
|
88
|
+
### 1. Portal Authentication (User completes auth in browser)
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
client = FinaticServerClient("your-api-key")
|
|
92
|
+
|
|
93
|
+
# Start session
|
|
94
|
+
await client.start_session()
|
|
95
|
+
|
|
96
|
+
# Get portal URL for user authentication
|
|
97
|
+
portal_url = await client.get_portal_url()
|
|
98
|
+
print(f"User should visit: {portal_url}")
|
|
99
|
+
|
|
100
|
+
# After user completes authentication in portal
|
|
101
|
+
user_info = await client.get_session_user()
|
|
102
|
+
print(f"User ID: {user_info['user_id']}")
|
|
103
|
+
print(f"Access Token: {user_info['access_token']}")
|
|
104
|
+
|
|
105
|
+
# Now you can make authenticated requests
|
|
106
|
+
brokers = await client.get_broker_list()
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### 2. Direct Authentication (Server-side with known user ID)
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
client = FinaticServerClient("your-api-key", user_id="user123")
|
|
113
|
+
|
|
114
|
+
# Start session (automatically authenticates with user ID)
|
|
115
|
+
await client.start_session()
|
|
116
|
+
|
|
117
|
+
# Now you can make authenticated requests immediately
|
|
118
|
+
brokers = await client.get_broker_list()
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Core Features
|
|
122
|
+
|
|
123
|
+
- **API Key Authentication**: Secure server-side authentication
|
|
124
|
+
- **Portal Integration**: Get portal URLs for user authentication
|
|
125
|
+
- **Automatic Token Management**: Handles access/refresh tokens automatically
|
|
126
|
+
- **Pagination Support**: Built-in pagination for large datasets
|
|
127
|
+
- **Type-safe API**: Full Pydantic model support
|
|
128
|
+
- **Async/await Support**: Non-blocking operations
|
|
129
|
+
- **Comprehensive Error Handling**: Detailed error types
|
|
130
|
+
|
|
131
|
+
## API Reference
|
|
132
|
+
|
|
133
|
+
### Initialization
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
client = FinaticServerClient(
|
|
137
|
+
api_key="your-api-key",
|
|
138
|
+
user_id="user123", # Optional - for direct authentication
|
|
139
|
+
)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Authentication Methods
|
|
143
|
+
|
|
144
|
+
- `start_session()` - Start a new session (authenticates directly if user_id provided)
|
|
145
|
+
- `get_portal_url()` - Get portal URL for user authentication (portal flow only)
|
|
146
|
+
- `get_session_user()` - Get user info and tokens after portal completion (portal flow only)
|
|
147
|
+
|
|
148
|
+
### Broker Data Methods
|
|
149
|
+
|
|
150
|
+
#### Basic Methods (with pagination support)
|
|
151
|
+
- `get_broker_list()` - Get list of available brokers
|
|
152
|
+
- `get_broker_connections()` - Get broker connections
|
|
153
|
+
- `get_broker_accounts(page=1, per_page=100, options=None, filters=None)` - Get broker accounts
|
|
154
|
+
- `get_broker_orders(page=1, per_page=100, options=None, filters=None)` - Get broker orders
|
|
155
|
+
- `get_broker_positions(page=1, per_page=100, options=None, filters=None)` - Get broker positions
|
|
156
|
+
|
|
157
|
+
#### Get All Methods (automatically handles pagination)
|
|
158
|
+
- `get_all_broker_accounts(options=None, filters=None)` - Get all broker accounts across all pages
|
|
159
|
+
- `get_all_broker_orders(options=None, filters=None)` - Get all broker orders across all pages
|
|
160
|
+
- `get_all_broker_positions(options=None, filters=None)` - Get all broker positions across all pages
|
|
161
|
+
|
|
162
|
+
### Filter Options
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from finatic_server.types.broker import BrokerDataOptions, OrdersFilter, PositionsFilter, AccountsFilter
|
|
166
|
+
|
|
167
|
+
# Basic filtering
|
|
168
|
+
options = BrokerDataOptions(
|
|
169
|
+
broker_name="robinhood",
|
|
170
|
+
account_id="123456",
|
|
171
|
+
symbol="AAPL"
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# Advanced filtering for orders
|
|
175
|
+
order_filters = OrdersFilter(
|
|
176
|
+
status="filled",
|
|
177
|
+
side="buy",
|
|
178
|
+
asset_type="stock",
|
|
179
|
+
created_after="2024-01-01T00:00:00Z"
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
# Advanced filtering for positions
|
|
183
|
+
position_filters = PositionsFilter(
|
|
184
|
+
symbol="AAPL",
|
|
185
|
+
side="long",
|
|
186
|
+
asset_type="stock"
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# Advanced filtering for accounts
|
|
190
|
+
account_filters = AccountsFilter(
|
|
191
|
+
account_type="margin",
|
|
192
|
+
status="active",
|
|
193
|
+
currency="USD"
|
|
194
|
+
)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Usage Examples
|
|
198
|
+
|
|
199
|
+
### Get All Orders with Filtering
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
# Get all filled orders for a specific symbol
|
|
203
|
+
all_filled_orders = await client.get_all_broker_orders(
|
|
204
|
+
filters=OrdersFilter(
|
|
205
|
+
status="filled",
|
|
206
|
+
symbol="AAPL"
|
|
207
|
+
)
|
|
208
|
+
)
|
|
209
|
+
print(f"Found {len(all_filled_orders)} filled AAPL orders")
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Pagination Example
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
# Get first page of 10 orders
|
|
216
|
+
first_page = await client.get_broker_orders(page=1, per_page=10)
|
|
217
|
+
print(f"First page: {len(first_page)} orders")
|
|
218
|
+
|
|
219
|
+
# Get second page
|
|
220
|
+
second_page = await client.get_broker_orders(page=2, per_page=10)
|
|
221
|
+
print(f"Second page: {len(second_page)} orders")
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Get All Data for Analysis
|
|
225
|
+
|
|
226
|
+
```python
|
|
227
|
+
# Get all accounts, orders, and positions
|
|
228
|
+
all_accounts = await client.get_all_broker_accounts()
|
|
229
|
+
all_orders = await client.get_all_broker_orders()
|
|
230
|
+
all_positions = await client.get_all_broker_positions()
|
|
231
|
+
|
|
232
|
+
print(f"Total accounts: {len(all_accounts)}")
|
|
233
|
+
print(f"Total orders: {len(all_orders)}")
|
|
234
|
+
print(f"Total positions: {len(all_positions)}")
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Filter by Broker
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
# Get all orders from Robinhood
|
|
241
|
+
robinhood_orders = await client.get_all_broker_orders(
|
|
242
|
+
options=BrokerDataOptions(broker_name="robinhood")
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
# Get all positions from Tasty Trade
|
|
246
|
+
tasty_positions = await client.get_all_broker_positions(
|
|
247
|
+
options=BrokerDataOptions(broker_name="tasty_trade")
|
|
248
|
+
)
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Error Handling
|
|
252
|
+
|
|
253
|
+
The SDK provides comprehensive error handling:
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
from finatic_server import AuthenticationError, ApiError, NetworkError
|
|
257
|
+
|
|
258
|
+
try:
|
|
259
|
+
orders = await client.get_broker_orders()
|
|
260
|
+
except AuthenticationError as e:
|
|
261
|
+
print(f"Authentication failed: {e}")
|
|
262
|
+
except NetworkError as e:
|
|
263
|
+
print(f"Network error: {e}")
|
|
264
|
+
except ApiError as e:
|
|
265
|
+
print(f"API error: {e}")
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Complete Example
|
|
269
|
+
|
|
270
|
+
```python
|
|
271
|
+
import asyncio
|
|
272
|
+
from finatic_server import FinaticServerClient
|
|
273
|
+
from finatic_server.types.broker import OrdersFilter, BrokerDataOptions
|
|
274
|
+
|
|
275
|
+
async def main():
|
|
276
|
+
# Option 1: Portal Authentication (user completes auth in browser)
|
|
277
|
+
client = FinaticServerClient("your-api-key")
|
|
278
|
+
|
|
279
|
+
try:
|
|
280
|
+
# Start session
|
|
281
|
+
await client.start_session()
|
|
282
|
+
|
|
283
|
+
# Get portal URL
|
|
284
|
+
portal_url = await client.get_portal_url()
|
|
285
|
+
print(f"Please visit: {portal_url}")
|
|
286
|
+
|
|
287
|
+
# Wait for user to complete authentication
|
|
288
|
+
input("Press Enter after completing authentication...")
|
|
289
|
+
|
|
290
|
+
# Get user info
|
|
291
|
+
user_info = await client.get_session_user()
|
|
292
|
+
print(f"Authenticated as: {user_info['user_id']}")
|
|
293
|
+
|
|
294
|
+
# Option 2: Direct Authentication (if you know the user ID)
|
|
295
|
+
# client = FinaticServerClient("your-api-key", user_id="user123")
|
|
296
|
+
# await client.start_session()
|
|
297
|
+
# print("Directly authenticated!")
|
|
298
|
+
|
|
299
|
+
# Get broker information
|
|
300
|
+
brokers = await client.get_broker_list()
|
|
301
|
+
print(f"Available brokers: {[b.name for b in brokers]}")
|
|
302
|
+
|
|
303
|
+
# Get all filled orders
|
|
304
|
+
filled_orders = await client.get_all_broker_orders(
|
|
305
|
+
filters=OrdersFilter(status="filled")
|
|
306
|
+
)
|
|
307
|
+
print(f"Total filled orders: {len(filled_orders)}")
|
|
308
|
+
|
|
309
|
+
# Get all positions
|
|
310
|
+
positions = await client.get_all_broker_positions()
|
|
311
|
+
print(f"Total positions: {len(positions)}")
|
|
312
|
+
|
|
313
|
+
# Get accounts with cash balance
|
|
314
|
+
accounts = await client.get_all_broker_accounts()
|
|
315
|
+
for account in accounts:
|
|
316
|
+
cash = account.cash_balance or 0.0
|
|
317
|
+
print(f"{account.account_name}: ${cash:,.2f}")
|
|
318
|
+
|
|
319
|
+
except Exception as e:
|
|
320
|
+
print(f"Error: {e}")
|
|
321
|
+
finally:
|
|
322
|
+
await client.close()
|
|
323
|
+
|
|
324
|
+
if __name__ == "__main__":
|
|
325
|
+
asyncio.run(main())
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## License
|
|
329
|
+
|
|
330
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
finatic_server/__init__.py,sha256=M6DZ75v0qK4xOAVLYPzCfCiyNFiFVPTqCIv2yf6LqX8,2558
|
|
2
|
+
finatic_server/core/__init__.py,sha256=j47dRgumMlE0CDAJw-VgFinGwgrxrYeXq1r65gKD3tg,190
|
|
3
|
+
finatic_server/core/api_client.py,sha256=aCGaQFdqd-SggNPptIrNVSeS6eS14N1GrPw_TurU6G0,34131
|
|
4
|
+
finatic_server/core/client.py,sha256=kR9uT1RtjKloDrgDsDtLTSgoR7QInv_j-IuqnZM6SGM,22202
|
|
5
|
+
finatic_server/types/__init__.py,sha256=9tqj5qIUXWmPvvNxdzZ_1ju2dXb6XiV7u986USH66Ew,2002
|
|
6
|
+
finatic_server/types/auth.py,sha256=toeeWxkx_0jLUxJpaANBrqVo7Rys5aQ9DD-biWijXw4,5975
|
|
7
|
+
finatic_server/types/broker.py,sha256=mOA7JKUQpssnji-plJ011U-9izA-K90RHa7wd_PFq2g,8286
|
|
8
|
+
finatic_server/types/common.py,sha256=rrfpA9g3oMZp32sJ7dhhetVGEV442hXYWrEJ-D9ksIc,7472
|
|
9
|
+
finatic_server/types/orders.py,sha256=TW0j6g7eS3cex3SEs1fu8MF9QFViirxMF6S1C3VgQmo,3345
|
|
10
|
+
finatic_server/types/portfolio.py,sha256=_EtUwQy6GmvU9UAMD59d4KePHJkxbQYA0xX5de2D4L8,3031
|
|
11
|
+
finatic_server/utils/__init__.py,sha256=qL0pv3fto3Jy2h8FY3iRg1IMVm9D1-1Oy3w7aNHh_bI,327
|
|
12
|
+
finatic_server/utils/errors.py,sha256=Z_-UVDnorwHfAiZ819BSxki2xVHn6AnOi9_9hpDwW48,1802
|
|
13
|
+
finatic_server_python-0.1.0.dist-info/METADATA,sha256=1q6MBj9arHc9tLGiRAuNTvrv5_T5M9Av8NFGucLPN14,10361
|
|
14
|
+
finatic_server_python-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
15
|
+
finatic_server_python-0.1.0.dist-info/top_level.txt,sha256=5eAfjxxwueQwIup4GCHEyKWX8PNvqhx1BV7C7TA4z50,15
|
|
16
|
+
finatic_server_python-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
finatic_server
|