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.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/.claude/commands/release-pr.md +108 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +29 -0
  4. data/.github/ISSUE_TEMPLATE/roadmap_task.md +34 -0
  5. data/.github/dependabot.yml +11 -0
  6. data/.github/workflows/main.yml +75 -0
  7. data/.rspec +3 -0
  8. data/.rubocop.yml +101 -0
  9. data/.ruby-version +1 -0
  10. data/CHANGELOG.md +100 -0
  11. data/CLAUDE.md +78 -0
  12. data/CODE_OF_CONDUCT.md +81 -0
  13. data/CONTRIBUTING.md +89 -0
  14. data/DISCLAIMER.md +54 -0
  15. data/LICENSE.txt +24 -0
  16. data/README.md +235 -0
  17. data/ROADMAP.md +157 -0
  18. data/Rakefile +17 -0
  19. data/SECURITY.md +48 -0
  20. data/docs/getting_started.md +48 -0
  21. data/docs/python_sdk_analysis.md +181 -0
  22. data/exe/tastytrade +8 -0
  23. data/lib/tastytrade/cli.rb +604 -0
  24. data/lib/tastytrade/cli_config.rb +79 -0
  25. data/lib/tastytrade/cli_helpers.rb +178 -0
  26. data/lib/tastytrade/client.rb +117 -0
  27. data/lib/tastytrade/keyring_store.rb +72 -0
  28. data/lib/tastytrade/models/account.rb +129 -0
  29. data/lib/tastytrade/models/account_balance.rb +75 -0
  30. data/lib/tastytrade/models/base.rb +47 -0
  31. data/lib/tastytrade/models/current_position.rb +155 -0
  32. data/lib/tastytrade/models/user.rb +23 -0
  33. data/lib/tastytrade/models.rb +7 -0
  34. data/lib/tastytrade/session.rb +164 -0
  35. data/lib/tastytrade/session_manager.rb +160 -0
  36. data/lib/tastytrade/version.rb +5 -0
  37. data/lib/tastytrade.rb +31 -0
  38. data/sig/tastytrade.rbs +4 -0
  39. data/spec/exe/tastytrade_spec.rb +104 -0
  40. data/spec/spec_helper.rb +26 -0
  41. data/spec/tastytrade/cli_accounts_spec.rb +166 -0
  42. data/spec/tastytrade/cli_auth_spec.rb +216 -0
  43. data/spec/tastytrade/cli_config_spec.rb +180 -0
  44. data/spec/tastytrade/cli_helpers_spec.rb +248 -0
  45. data/spec/tastytrade/cli_interactive_spec.rb +54 -0
  46. data/spec/tastytrade/cli_logout_spec.rb +121 -0
  47. data/spec/tastytrade/cli_select_spec.rb +174 -0
  48. data/spec/tastytrade/cli_status_spec.rb +206 -0
  49. data/spec/tastytrade/client_spec.rb +210 -0
  50. data/spec/tastytrade/keyring_store_spec.rb +168 -0
  51. data/spec/tastytrade/models/account_balance_spec.rb +247 -0
  52. data/spec/tastytrade/models/account_spec.rb +206 -0
  53. data/spec/tastytrade/models/base_spec.rb +61 -0
  54. data/spec/tastytrade/models/current_position_spec.rb +444 -0
  55. data/spec/tastytrade/models/user_spec.rb +58 -0
  56. data/spec/tastytrade/session_manager_spec.rb +296 -0
  57. data/spec/tastytrade/session_spec.rb +392 -0
  58. data/spec/tastytrade_spec.rb +9 -0
  59. 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
data/exe/tastytrade ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "tastytrade"
6
+ require "tastytrade/cli"
7
+
8
+ Tastytrade::CLI.start(ARGV)