tastytrade 0.2.0
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.
- checksums.yaml +7 -0
- data/.claude/commands/release-pr.md +108 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +29 -0
- data/.github/ISSUE_TEMPLATE/roadmap_task.md +34 -0
- data/.github/dependabot.yml +11 -0
- data/.github/workflows/main.yml +75 -0
- data/.rspec +3 -0
- data/.rubocop.yml +101 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +100 -0
- data/CLAUDE.md +78 -0
- data/CODE_OF_CONDUCT.md +81 -0
- data/CONTRIBUTING.md +89 -0
- data/DISCLAIMER.md +54 -0
- data/LICENSE.txt +24 -0
- data/README.md +235 -0
- data/ROADMAP.md +157 -0
- data/Rakefile +17 -0
- data/SECURITY.md +48 -0
- data/docs/getting_started.md +48 -0
- data/docs/python_sdk_analysis.md +181 -0
- data/exe/tastytrade +8 -0
- data/lib/tastytrade/cli.rb +604 -0
- data/lib/tastytrade/cli_config.rb +79 -0
- data/lib/tastytrade/cli_helpers.rb +178 -0
- data/lib/tastytrade/client.rb +117 -0
- data/lib/tastytrade/keyring_store.rb +72 -0
- data/lib/tastytrade/models/account.rb +129 -0
- data/lib/tastytrade/models/account_balance.rb +75 -0
- data/lib/tastytrade/models/base.rb +47 -0
- data/lib/tastytrade/models/current_position.rb +155 -0
- data/lib/tastytrade/models/user.rb +23 -0
- data/lib/tastytrade/models.rb +7 -0
- data/lib/tastytrade/session.rb +164 -0
- data/lib/tastytrade/session_manager.rb +160 -0
- data/lib/tastytrade/version.rb +5 -0
- data/lib/tastytrade.rb +31 -0
- data/sig/tastytrade.rbs +4 -0
- data/spec/exe/tastytrade_spec.rb +104 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/tastytrade/cli_accounts_spec.rb +166 -0
- data/spec/tastytrade/cli_auth_spec.rb +216 -0
- data/spec/tastytrade/cli_config_spec.rb +180 -0
- data/spec/tastytrade/cli_helpers_spec.rb +248 -0
- data/spec/tastytrade/cli_interactive_spec.rb +54 -0
- data/spec/tastytrade/cli_logout_spec.rb +121 -0
- data/spec/tastytrade/cli_select_spec.rb +174 -0
- data/spec/tastytrade/cli_status_spec.rb +206 -0
- data/spec/tastytrade/client_spec.rb +210 -0
- data/spec/tastytrade/keyring_store_spec.rb +168 -0
- data/spec/tastytrade/models/account_balance_spec.rb +247 -0
- data/spec/tastytrade/models/account_spec.rb +206 -0
- data/spec/tastytrade/models/base_spec.rb +61 -0
- data/spec/tastytrade/models/current_position_spec.rb +444 -0
- data/spec/tastytrade/models/user_spec.rb +58 -0
- data/spec/tastytrade/session_manager_spec.rb +296 -0
- data/spec/tastytrade/session_spec.rb +392 -0
- data/spec/tastytrade_spec.rb +9 -0
- metadata +303 -0
@@ -0,0 +1,181 @@
|
|
1
|
+
# Python Tastytrade SDK Analysis
|
2
|
+
|
3
|
+
## Overview
|
4
|
+
|
5
|
+
This document analyzes the Python Tastytrade SDK's Session implementation to extract patterns and best practices for our Ruby implementation.
|
6
|
+
|
7
|
+
## Key Components
|
8
|
+
|
9
|
+
### 1. Session Class Structure
|
10
|
+
|
11
|
+
The Python SDK's `Session` class is a standalone class (no inheritance) that:
|
12
|
+
- Manages authentication state
|
13
|
+
- Provides both sync and async methods
|
14
|
+
- Implements context manager protocol (`__enter__`/`__exit__`)
|
15
|
+
- Handles both production and test environments
|
16
|
+
|
17
|
+
```python
|
18
|
+
class Session:
|
19
|
+
def __init__(self, username=None, password=None, remember_me=False,
|
20
|
+
remember_token=None, two_factor_code=None, dxfeed_tos_compliant=True,
|
21
|
+
proxy=None, is_test=False):
|
22
|
+
# Initialize with credentials or remember token
|
23
|
+
# Supports 2FA and proxy configuration
|
24
|
+
```
|
25
|
+
|
26
|
+
### 2. Authentication Flow
|
27
|
+
|
28
|
+
**Login Process:**
|
29
|
+
1. POST to `/sessions` endpoint with credentials
|
30
|
+
2. Receive session token and user data
|
31
|
+
3. Store session token for subsequent requests
|
32
|
+
4. Optionally store remember token for future sessions
|
33
|
+
|
34
|
+
**Key Features:**
|
35
|
+
- Username/password authentication
|
36
|
+
- Remember token support for persistent sessions
|
37
|
+
- Two-factor authentication support
|
38
|
+
- Session expiration tracking
|
39
|
+
|
40
|
+
### 3. State Management
|
41
|
+
|
42
|
+
The session maintains:
|
43
|
+
- `session_token`: Primary authentication credential
|
44
|
+
- `remember_token`: For persistent login
|
45
|
+
- `user`: User information object
|
46
|
+
- `session_expiration`: Token validity timestamp
|
47
|
+
- `is_test`: Environment flag (production vs certification)
|
48
|
+
|
49
|
+
### 4. HTTP Client Pattern
|
50
|
+
|
51
|
+
**Client Library:** Uses `httpx` for HTTP requests
|
52
|
+
|
53
|
+
**Request Methods:**
|
54
|
+
```python
|
55
|
+
def _get(self, path, params=None):
|
56
|
+
url = f'{self.base_url}{path}'
|
57
|
+
return self._session.get(url, params=params, headers=self._headers())
|
58
|
+
|
59
|
+
def _post(self, path, data=None, json=None):
|
60
|
+
url = f'{self.base_url}{path}'
|
61
|
+
return self._session.post(url, data=data, json=json, headers=self._headers())
|
62
|
+
```
|
63
|
+
|
64
|
+
**Key Patterns:**
|
65
|
+
- Centralized request methods (`_get`, `_post`, `_put`, `_delete`)
|
66
|
+
- Automatic header injection
|
67
|
+
- Base URL management based on environment
|
68
|
+
- 30-second timeout for all requests
|
69
|
+
|
70
|
+
### 5. Error Handling
|
71
|
+
|
72
|
+
**Validation Pattern:**
|
73
|
+
```python
|
74
|
+
def validate_response(response: httpx.Response) -> None:
|
75
|
+
if response.status_code // 100 != 2:
|
76
|
+
try:
|
77
|
+
content = response.json()
|
78
|
+
error = content.get('error', {})
|
79
|
+
raise TastytradeError(
|
80
|
+
error.get('message', 'Unknown error'),
|
81
|
+
error.get('code'),
|
82
|
+
error.get('errors')
|
83
|
+
)
|
84
|
+
except JSONDecodeError:
|
85
|
+
response.raise_for_status()
|
86
|
+
```
|
87
|
+
|
88
|
+
**Key Features:**
|
89
|
+
- Custom `TastytradeError` exception
|
90
|
+
- Detailed error parsing from API responses
|
91
|
+
- Graceful handling of malformed responses
|
92
|
+
- HTTP status code validation
|
93
|
+
|
94
|
+
### 6. Response Parsing
|
95
|
+
|
96
|
+
**Standard Pattern:**
|
97
|
+
```python
|
98
|
+
def validate_and_parse(response: httpx.Response) -> dict:
|
99
|
+
validate_response(response)
|
100
|
+
content = response.json()
|
101
|
+
data = content.get('data', {})
|
102
|
+
if not data:
|
103
|
+
raise TastytradeError('No data in response')
|
104
|
+
return dict(data)
|
105
|
+
```
|
106
|
+
|
107
|
+
**Characteristics:**
|
108
|
+
- Validates response before parsing
|
109
|
+
- Extracts 'data' field from JSON response
|
110
|
+
- Type casting to dictionary
|
111
|
+
- Handles missing data gracefully
|
112
|
+
|
113
|
+
## Patterns to Adopt for Ruby Implementation
|
114
|
+
|
115
|
+
### 1. Session Architecture
|
116
|
+
- Single Session class managing all authentication state
|
117
|
+
- Support for multiple authentication methods (password, remember token)
|
118
|
+
- Environment switching (production/test)
|
119
|
+
- Context manager pattern (Ruby: implement with blocks)
|
120
|
+
|
121
|
+
### 2. HTTP Client Management
|
122
|
+
- Use a robust HTTP client (e.g., Faraday in Ruby)
|
123
|
+
- Centralized request methods with automatic header injection
|
124
|
+
- Configurable timeouts
|
125
|
+
- Proxy support
|
126
|
+
|
127
|
+
### 3. Error Handling Strategy
|
128
|
+
- Custom exception hierarchy (TastytradeError base class)
|
129
|
+
- Parse API error responses for detailed error information
|
130
|
+
- Graceful degradation for malformed responses
|
131
|
+
- Consistent error reporting
|
132
|
+
|
133
|
+
### 4. Authentication State
|
134
|
+
- Store session token as instance variable
|
135
|
+
- Support remember tokens for persistent sessions
|
136
|
+
- Track session expiration
|
137
|
+
- Automatic token refresh (if supported by API)
|
138
|
+
|
139
|
+
### 5. Configuration Options
|
140
|
+
- Environment selection (production/test)
|
141
|
+
- Proxy configuration
|
142
|
+
- Timeout settings
|
143
|
+
- Optional 2FA support
|
144
|
+
|
145
|
+
### 6. API Request Pattern
|
146
|
+
```ruby
|
147
|
+
# Ruby equivalent pattern
|
148
|
+
def get(path, params = nil)
|
149
|
+
response = http_client.get(build_url(path), params: params, headers: headers)
|
150
|
+
validate_and_parse(response)
|
151
|
+
end
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
def validate_and_parse(response)
|
156
|
+
validate_response(response)
|
157
|
+
data = JSON.parse(response.body)['data']
|
158
|
+
raise TastytradeError, 'No data in response' unless data
|
159
|
+
data
|
160
|
+
end
|
161
|
+
```
|
162
|
+
|
163
|
+
## Implementation Recommendations
|
164
|
+
|
165
|
+
1. **Use Faraday** for HTTP client with middleware support
|
166
|
+
2. **Implement retry logic** using Faraday middleware
|
167
|
+
3. **Add logging** for debugging API interactions
|
168
|
+
4. **Support both sync and async** operations (using Async gem)
|
169
|
+
5. **Type safety** with Sorbet or RBS type signatures
|
170
|
+
6. **Configuration** via environment variables or config object
|
171
|
+
7. **Testing** with VCR for recording/replaying HTTP interactions
|
172
|
+
|
173
|
+
## Next Steps
|
174
|
+
|
175
|
+
1. Create base `Tastytrade::Session` class
|
176
|
+
2. Implement authentication methods
|
177
|
+
3. Add HTTP client configuration
|
178
|
+
4. Create error handling hierarchy
|
179
|
+
5. Add response validation and parsing
|
180
|
+
6. Implement specific API endpoints
|
181
|
+
7. Add comprehensive test coverage
|