tastytrade 0.2.0 → 0.3.1

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/commands/plan.md +13 -0
  3. data/.claude/commands/release-pr.md +12 -0
  4. data/CHANGELOG.md +180 -0
  5. data/README.md +424 -3
  6. data/ROADMAP.md +17 -17
  7. data/lib/tastytrade/cli/history_formatter.rb +304 -0
  8. data/lib/tastytrade/cli/orders.rb +749 -0
  9. data/lib/tastytrade/cli/positions_formatter.rb +114 -0
  10. data/lib/tastytrade/cli.rb +701 -12
  11. data/lib/tastytrade/cli_helpers.rb +111 -14
  12. data/lib/tastytrade/client.rb +7 -0
  13. data/lib/tastytrade/file_store.rb +83 -0
  14. data/lib/tastytrade/instruments/equity.rb +42 -0
  15. data/lib/tastytrade/models/account.rb +160 -2
  16. data/lib/tastytrade/models/account_balance.rb +46 -0
  17. data/lib/tastytrade/models/buying_power_effect.rb +61 -0
  18. data/lib/tastytrade/models/live_order.rb +272 -0
  19. data/lib/tastytrade/models/order_response.rb +106 -0
  20. data/lib/tastytrade/models/order_status.rb +84 -0
  21. data/lib/tastytrade/models/trading_status.rb +200 -0
  22. data/lib/tastytrade/models/transaction.rb +151 -0
  23. data/lib/tastytrade/models.rb +6 -0
  24. data/lib/tastytrade/order.rb +191 -0
  25. data/lib/tastytrade/order_validator.rb +355 -0
  26. data/lib/tastytrade/session.rb +26 -1
  27. data/lib/tastytrade/session_manager.rb +43 -14
  28. data/lib/tastytrade/version.rb +1 -1
  29. data/lib/tastytrade.rb +43 -0
  30. data/spec/exe/tastytrade_spec.rb +1 -1
  31. data/spec/tastytrade/cli/positions_spec.rb +267 -0
  32. data/spec/tastytrade/cli_auth_spec.rb +5 -0
  33. data/spec/tastytrade/cli_env_login_spec.rb +199 -0
  34. data/spec/tastytrade/cli_helpers_spec.rb +3 -26
  35. data/spec/tastytrade/cli_orders_spec.rb +168 -0
  36. data/spec/tastytrade/cli_status_spec.rb +153 -164
  37. data/spec/tastytrade/file_store_spec.rb +126 -0
  38. data/spec/tastytrade/models/account_balance_spec.rb +103 -0
  39. data/spec/tastytrade/models/account_order_history_spec.rb +229 -0
  40. data/spec/tastytrade/models/account_order_management_spec.rb +271 -0
  41. data/spec/tastytrade/models/account_place_order_spec.rb +125 -0
  42. data/spec/tastytrade/models/account_spec.rb +86 -15
  43. data/spec/tastytrade/models/buying_power_effect_spec.rb +250 -0
  44. data/spec/tastytrade/models/live_order_json_spec.rb +144 -0
  45. data/spec/tastytrade/models/live_order_spec.rb +295 -0
  46. data/spec/tastytrade/models/order_response_spec.rb +96 -0
  47. data/spec/tastytrade/models/order_status_spec.rb +113 -0
  48. data/spec/tastytrade/models/trading_status_spec.rb +260 -0
  49. data/spec/tastytrade/models/transaction_spec.rb +236 -0
  50. data/spec/tastytrade/order_edge_cases_spec.rb +163 -0
  51. data/spec/tastytrade/order_spec.rb +201 -0
  52. data/spec/tastytrade/order_validator_spec.rb +347 -0
  53. data/spec/tastytrade/session_env_spec.rb +169 -0
  54. data/spec/tastytrade/session_manager_spec.rb +43 -33
  55. metadata +34 -18
  56. data/lib/tastytrade/keyring_store.rb +0 -72
  57. data/spec/tastytrade/keyring_store_spec.rb +0 -168
data/README.md CHANGED
@@ -20,9 +20,11 @@ This Ruby gem provides a simple interface to interact with the Tastytrade API, a
20
20
  - Secure authentication with Tastytrade API
21
21
  - Real-time market data access
22
22
  - Account management and portfolio tracking
23
- - Order placement and management
23
+ - Order placement and management with dry-run support
24
24
  - Position monitoring
25
- - Transaction history
25
+ - Transaction history with filtering and grouping
26
+ - Buying power calculations and monitoring
27
+ - CLI with interactive mode and rich formatting
26
28
 
27
29
  ## Roadmap
28
30
 
@@ -48,6 +50,30 @@ Or install it yourself as:
48
50
  gem install tastytrade
49
51
  ```
50
52
 
53
+ ## Configuration
54
+
55
+ ### Environment Variables
56
+
57
+ The tastytrade gem supports authentication via environment variables, which is recommended for automation and CI/CD environments:
58
+
59
+ ```bash
60
+ # Required for environment variable authentication
61
+ export TASTYTRADE_USERNAME="your_email@example.com"
62
+ export TASTYTRADE_PASSWORD="your_password"
63
+
64
+ # Optional environment variables
65
+ export TASTYTRADE_ENVIRONMENT="sandbox" # Use "sandbox" for test environment
66
+ export TASTYTRADE_REMEMBER="true" # Enable remember token
67
+
68
+ # Alternative shorter variable names
69
+ export TT_USERNAME="your_email@example.com"
70
+ export TT_PASSWORD="your_password"
71
+ export TT_ENVIRONMENT="sandbox"
72
+ export TT_REMEMBER="true"
73
+ ```
74
+
75
+ When environment variables are set, the CLI will automatically use them for authentication without prompting for credentials.
76
+
51
77
  ## Usage
52
78
 
53
79
  ### Authentication
@@ -75,19 +101,165 @@ session.authenticated? # => true
75
101
 
76
102
  The gem includes a command-line interface for common operations:
77
103
 
104
+ #### Authentication
105
+
78
106
  ```bash
79
- # Login to your account
107
+ # Login to your account interactively
80
108
  tastytrade login
81
109
 
82
110
  # Login with remember option for automatic session refresh
83
111
  tastytrade login --remember
84
112
 
113
+ # Login using environment variables (recommended for automation)
114
+ export TASTYTRADE_USERNAME="your_email@example.com"
115
+ export TASTYTRADE_PASSWORD="your_password"
116
+ tastytrade login
117
+
118
+ # Or use shorter variable names
119
+ export TT_USERNAME="your_email@example.com"
120
+ export TT_PASSWORD="your_password"
121
+ tastytrade login
122
+
123
+ # Use sandbox environment for testing
124
+ export TASTYTRADE_ENVIRONMENT="sandbox"
125
+ tastytrade login
126
+
127
+ # Enable remember token via environment
128
+ export TASTYTRADE_REMEMBER="true"
129
+ tastytrade login
130
+
131
+ ```
132
+
133
+ #### Account Operations
134
+
135
+ ```bash
85
136
  # View account balances
86
137
  tastytrade balance
87
138
 
88
139
  # View balances for all accounts
89
140
  tastytrade balance --all
90
141
 
142
+ # View account positions
143
+ tastytrade positions
144
+
145
+ # Filter positions by symbol
146
+ tastytrade positions --symbol AAPL
147
+
148
+ # Filter positions by underlying symbol (for options)
149
+ tastytrade positions --underlying-symbol SPY
150
+
151
+ # View trading status and permissions
152
+ tastytrade trading_status
153
+
154
+ # View status for specific account
155
+ tastytrade trading_status --account 5WT0001
156
+
157
+ # Include closed positions
158
+ tastytrade positions --include-closed
159
+ ```
160
+
161
+ #### Transaction History
162
+
163
+ ```bash
164
+ # View all transactions
165
+ tastytrade history
166
+
167
+ # Filter by date range
168
+ tastytrade history --start-date 2024-01-01 --end-date 2024-12-31
169
+
170
+ # Filter by symbol
171
+ tastytrade history --symbol AAPL
172
+
173
+ # Group transactions by symbol, type, or date
174
+ tastytrade history --group-by symbol
175
+ tastytrade history --group-by type
176
+ tastytrade history --group-by date
177
+
178
+ # Limit number of transactions
179
+ tastytrade history --limit 50
180
+
181
+ # Combine filters
182
+ tastytrade history --symbol AAPL --start-date 2024-01-01 --group-by date
183
+ ```
184
+
185
+ #### Buying Power Status
186
+
187
+ ```bash
188
+ # View buying power status
189
+ tastytrade buying_power
190
+
191
+ # View buying power for specific account
192
+ tastytrade buying_power --account 5WX12345
193
+ ```
194
+
195
+ #### Order Management
196
+
197
+ ##### Order Placement
198
+
199
+ ```bash
200
+ # Place a limit buy order
201
+ tastytrade order place --symbol AAPL --action buy_to_open --quantity 100 --price 150.50
202
+
203
+ # Place a market order
204
+ tastytrade order place --symbol SPY --action buy_to_open --quantity 10 --type market
205
+
206
+ # Sell to close a position
207
+ tastytrade order place --symbol AAPL --action sell_to_close --quantity 100 --price 155.00
208
+
209
+ # Dry-run validation (validate without placing)
210
+ tastytrade order place --symbol MSFT --action buy_to_open --quantity 50 --price 300 --dry-run
211
+
212
+ # Skip confirmation prompt
213
+ tastytrade order place --symbol TSLA --action buy_to_open --quantity 10 --price 200 --skip-confirmation
214
+
215
+ # Supported actions:
216
+ # - buy_to_open (bto)
217
+ # - sell_to_close (stc)
218
+ # - sell_to_open (sto)
219
+ # - buy_to_close (btc)
220
+
221
+ # Note: Orders that would use >80% of buying power will prompt for confirmation
222
+ ```
223
+
224
+ ##### Order Status and History
225
+
226
+ ```bash
227
+ # List all live orders (open + last 24 hours)
228
+ tastytrade order list
229
+
230
+ # List orders with filters
231
+ tastytrade order list --status Live
232
+ tastytrade order list --symbol AAPL
233
+ tastytrade order list --all # Show for all accounts
234
+
235
+ # Output orders in JSON format
236
+ tastytrade order list --format json
237
+
238
+ # Get historical orders (beyond 24 hours)
239
+ tastytrade order history
240
+ tastytrade order history --status Filled
241
+ tastytrade order history --symbol AAPL
242
+ tastytrade order history --from 2024-01-01 --to 2024-12-31
243
+ tastytrade order history --limit 100
244
+ tastytrade order history --format json
245
+
246
+ # Get details for a specific order
247
+ tastytrade order get ORDER_ID
248
+ tastytrade order get ORDER_ID --format json
249
+
250
+ # Cancel an order
251
+ tastytrade order cancel ORDER_ID
252
+ tastytrade order cancel ORDER_ID --account 5WX12345
253
+
254
+ # Replace/modify an order
255
+ tastytrade order replace ORDER_ID # Interactive prompts for new price/quantity
256
+ tastytrade order replace ORDER_ID --price 155.00
257
+ tastytrade order replace ORDER_ID --quantity 50
258
+ ```
259
+
260
+ #### Account Management
261
+
262
+ ```bash
91
263
  # List all accounts
92
264
  tastytrade accounts
93
265
 
@@ -129,11 +301,21 @@ balance.cash_balance # => BigDecimal("10000.50")
129
301
  balance.net_liquidating_value # => BigDecimal("42001.00")
130
302
  balance.equity_buying_power # => BigDecimal("20000.00")
131
303
  balance.available_trading_funds # => BigDecimal("12000.00")
304
+ balance.day_trading_buying_power # => BigDecimal("40000.00")
305
+ balance.derivative_buying_power # => BigDecimal("20000.00")
132
306
 
133
307
  # Check buying power usage
134
308
  balance.buying_power_usage_percentage # => BigDecimal("40.00")
309
+ balance.derivative_buying_power_usage_percentage # => BigDecimal("25.00")
135
310
  balance.high_buying_power_usage? # => false (checks if > 80%)
136
311
 
312
+ # Check if sufficient buying power for order
313
+ balance.sufficient_buying_power?(15000) # => true
314
+ balance.sufficient_buying_power?(15000, buying_power_type: :derivative) # => true
315
+
316
+ # Calculate buying power impact
317
+ balance.buying_power_impact_percentage(15000) # => BigDecimal("75.00")
318
+
137
319
  # Calculate totals
138
320
  balance.total_equity_value # => BigDecimal("30001.00")
139
321
  balance.total_derivative_value # => BigDecimal("4500.00")
@@ -168,6 +350,245 @@ positions.each do |position|
168
350
  end
169
351
  ```
170
352
 
353
+ ### Order Placement
354
+
355
+ ```ruby
356
+ # Create an order leg for buying stock
357
+ leg = Tastytrade::OrderLeg.new(
358
+ action: Tastytrade::OrderAction::BUY_TO_OPEN,
359
+ symbol: 'AAPL',
360
+ quantity: 100
361
+ )
362
+
363
+ # Create a market order
364
+ market_order = Tastytrade::Order.new(
365
+ type: Tastytrade::OrderType::MARKET,
366
+ legs: leg
367
+ )
368
+
369
+ # Create a limit order
370
+ limit_order = Tastytrade::Order.new(
371
+ type: Tastytrade::OrderType::LIMIT,
372
+ legs: leg,
373
+ price: 150.50 # Will be converted to BigDecimal
374
+ )
375
+
376
+ # Place the order
377
+ response = account.place_order(session, market_order)
378
+
379
+ # Dry run (simulate order without placing)
380
+ response = account.place_order(session, limit_order, dry_run: true)
381
+
382
+ # Check order response
383
+ puts response.order_id # => "123456"
384
+ puts response.status # => "Filled"
385
+
386
+ # Dry run orders return a BuyingPowerEffect object
387
+ if response.buying_power_effect.is_a?(Tastytrade::Models::BuyingPowerEffect)
388
+ bp_effect = response.buying_power_effect
389
+ puts bp_effect.buying_power_change_amount # => BigDecimal("15050.00")
390
+ puts bp_effect.buying_power_usage_percentage # => BigDecimal("75.25")
391
+ puts bp_effect.exceeds_threshold?(80) # => false
392
+ puts bp_effect.debit? # => true
393
+ else
394
+ puts response.buying_power_effect # => BigDecimal("-15050.00")
395
+ end
396
+
397
+ puts response.warnings # => [] or warning messages
398
+ ```
399
+
400
+ ### Order Management
401
+
402
+ ```ruby
403
+ # Get live orders (open orders + orders from last 24 hours)
404
+ orders = account.get_live_orders(session)
405
+
406
+ # Filter orders by status
407
+ live_orders = account.get_live_orders(session, status: "Live")
408
+ filled_orders = account.get_live_orders(session, status: "Filled")
409
+
410
+ # Filter orders by symbol
411
+ aapl_orders = account.get_live_orders(session, underlying_symbol: "AAPL")
412
+
413
+ # Filter by time range
414
+ recent_orders = account.get_live_orders(session,
415
+ from_time: Time.now - 86400, # Last 24 hours
416
+ to_time: Time.now
417
+ )
418
+
419
+ # Work with order details
420
+ orders.each do |order|
421
+ puts order.id # => "12345"
422
+ puts order.status # => "Live"
423
+ puts order.underlying_symbol # => "AAPL"
424
+ puts order.order_type # => "Limit"
425
+ puts order.price # => BigDecimal("150.50")
426
+
427
+ # Check order capabilities
428
+ puts order.cancellable? # => true
429
+ puts order.editable? # => true
430
+ puts order.terminal? # => false
431
+ puts order.working? # => true
432
+
433
+ # Check fill status
434
+ puts order.remaining_quantity # => 100
435
+ puts order.filled_quantity # => 0
436
+
437
+ # Work with order legs
438
+ order.legs.each do |leg|
439
+ puts leg.symbol # => "AAPL"
440
+ puts leg.action # => "Buy"
441
+ puts leg.quantity # => 100
442
+ puts leg.remaining_quantity # => 100
443
+ puts leg.partially_filled? # => false
444
+ end
445
+ end
446
+
447
+ # Cancel an order
448
+ account.cancel_order(session, "12345")
449
+
450
+ # Replace an order with new parameters
451
+ new_order = Tastytrade::Order.new(
452
+ type: Tastytrade::OrderType::LIMIT,
453
+ legs: leg,
454
+ price: 155.00 # New price
455
+ )
456
+ response = account.replace_order(session, "12345", new_order)
457
+ ```
458
+
459
+ ### Order Validation
460
+
461
+ The SDK includes comprehensive order validation to prevent submission errors and ensure orders meet all requirements before reaching the API.
462
+
463
+ #### Validation Features
464
+
465
+ ```ruby
466
+ # Orders are automatically validated before submission
467
+ order = Tastytrade::Order.new(
468
+ type: Tastytrade::OrderType::LIMIT,
469
+ legs: leg,
470
+ price: 150.00
471
+ )
472
+
473
+ # Validation happens automatically when placing orders
474
+ begin
475
+ response = account.place_order(session, order)
476
+ rescue Tastytrade::OrderValidationError => e
477
+ puts "Validation failed:"
478
+ e.errors.each { |error| puts " - #{error}" }
479
+ end
480
+
481
+ # You can also validate manually
482
+ order.validate!(session, account) # Raises if invalid
483
+
484
+ # Or perform a dry-run validation
485
+ dry_run_response = order.dry_run(session, account)
486
+ puts dry_run_response.buying_power_effect
487
+ puts dry_run_response.warnings
488
+ ```
489
+
490
+ #### Validation Rules
491
+
492
+ The following validations are performed:
493
+
494
+ 1. **Symbol Validation**
495
+ - Verifies symbol exists and is tradeable
496
+ - Checks instrument type compatibility
497
+
498
+ 2. **Quantity Validation**
499
+ - Minimum quantity: 1
500
+ - Maximum quantity: 999,999
501
+ - No fractional shares (whole numbers only)
502
+
503
+ 3. **Price Validation**
504
+ - Price must be positive for limit orders
505
+ - Price is rounded to appropriate tick size
506
+ - Price reasonableness checks
507
+
508
+ 4. **Account Permissions**
509
+ - Validates trading permissions for instrument type
510
+ - Checks for account restrictions (frozen, closing-only, etc.)
511
+ - Verifies options/futures permissions if applicable
512
+
513
+ 5. **Buying Power Validation**
514
+ - Ensures sufficient buying power via dry-run
515
+ - Warns if order uses >50% of available buying power
516
+ - Checks margin requirements
517
+
518
+ 6. **Market Hours Validation**
519
+ - Warns about market orders outside regular hours
520
+ - Alerts for weekend submissions
521
+
522
+ #### Validation Errors
523
+
524
+ ```ruby
525
+ # Specific validation error types
526
+ Tastytrade::OrderValidationError # General validation failure
527
+ Tastytrade::InvalidSymbolError # Symbol doesn't exist
528
+ Tastytrade::InsufficientBuyingPowerError # Not enough buying power
529
+ Tastytrade::AccountRestrictedError # Account has restrictions
530
+ Tastytrade::InvalidQuantityError # Quantity out of range
531
+ Tastytrade::InvalidPriceError # Price validation failure
532
+ Tastytrade::MarketClosedError # Market is closed
533
+ ```
534
+
535
+ #### Using the OrderValidator
536
+
537
+ ```ruby
538
+ # Direct use of OrderValidator for custom validation
539
+ validator = Tastytrade::OrderValidator.new(session, account, order)
540
+
541
+ # Perform full validation
542
+ validator.validate! # Raises if invalid
543
+
544
+ # Or just dry-run validation
545
+ dry_run_response = validator.dry_run_validate!
546
+
547
+ # Check warnings and errors
548
+ puts validator.warnings # Array of warning messages
549
+ puts validator.errors # Array of error messages
550
+ ```
551
+
552
+ #### Skip Validation (Use with Caution)
553
+
554
+ ```ruby
555
+ # Skip validation when you're certain the order is valid
556
+ response = account.place_order(session, order, skip_validation: true)
557
+ ```
558
+
559
+ ### Transaction History
560
+
561
+ ```ruby
562
+ # Get all transactions
563
+ transactions = account.get_transactions(session)
564
+
565
+ # Filter transactions
566
+ transactions = account.get_transactions(session,
567
+ start_date: Date.new(2024, 1, 1),
568
+ end_date: Date.new(2024, 12, 31),
569
+ symbol: 'AAPL',
570
+ transaction_types: ['Trade'],
571
+ per_page: 100
572
+ )
573
+
574
+ # Work with transactions
575
+ transactions.each do |transaction|
576
+ puts transaction.symbol # => "AAPL"
577
+ puts transaction.transaction_type # => "Trade"
578
+ puts transaction.transaction_sub_type # => "Buy"
579
+ puts transaction.quantity # => BigDecimal("100")
580
+ puts transaction.price # => BigDecimal("150.00")
581
+ puts transaction.value # => BigDecimal("-15000.00")
582
+ puts transaction.net_value # => BigDecimal("-15007.00")
583
+ puts transaction.executed_at # => Time object
584
+
585
+ # Fee breakdown
586
+ puts transaction.commission # => BigDecimal("5.00")
587
+ puts transaction.clearing_fees # => BigDecimal("1.00")
588
+ puts transaction.regulatory_fees # => BigDecimal("0.50")
589
+ end
590
+ ```
591
+
171
592
  ## Development
172
593
 
173
594
  After checking out the repo, run `bin/setup` to install dependencies and verify your environment is configured correctly.
data/ROADMAP.md CHANGED
@@ -24,16 +24,16 @@ This document outlines the development roadmap for the unofficial Tastytrade Rub
24
24
  #### Account Operations
25
25
  - [x] Fetch account info and balances (closes #6)
26
26
  - [x] Get positions
27
- - [ ] Get transaction history
28
- - [ ] Calculate buying power
29
- - [ ] Account status and trading permissions
27
+ - [x] Get transaction history (closes #8)
28
+ - [x] Calculate buying power (closes #9)
29
+ - [x] Account status and trading permissions (closes #10)
30
30
 
31
31
  #### Basic Trading
32
- - [ ] Place equity orders (market, limit)
33
- - [ ] Cancel/replace orders
34
- - [ ] Get order status
35
- - [ ] Order validation
36
- - [ ] Basic order types (day, GTC)
32
+ - [x] Place equity orders (market, limit) (closes #11)
33
+ - [x] Cancel/replace orders (closes #12)
34
+ - [x] Get order status (closes #13)
35
+ - [x] Order validation (closes #14)
36
+ - [x] Basic order types (day, GTC) (closes #15)
37
37
 
38
38
  #### Core Infrastructure
39
39
  - [x] HTTP client setup (Faraday) (closes #16)
@@ -77,11 +77,11 @@ This document outlines the development roadmap for the unofficial Tastytrade Rub
77
77
  **Target: Q1 2026 (January - March)**
78
78
 
79
79
  #### Core CLI Commands
80
- - [ ] Authentication (`tt login`)
81
- - [ ] Account info (`tt account`)
82
- - [ ] Portfolio view (`tt portfolio`)
83
- - [ ] Basic trading (`tt trade`)
84
- - [ ] Order management (`tt orders`)
80
+ - [x] Authentication (`tt login`)
81
+ - [x] Account info (`tt account`)
82
+ - [x] Portfolio view (`tt portfolio`)
83
+ - [x] Basic trading (`tt trade`)
84
+ - [x] Order management (`tt orders`)
85
85
 
86
86
  #### Options CLI
87
87
  - [ ] Option chains (`tt option chain`)
@@ -94,13 +94,13 @@ This document outlines the development roadmap for the unofficial Tastytrade Rub
94
94
  - [ ] Portfolio analysis (`tt analyze`)
95
95
  - [ ] Real-time quotes (`tt quote`)
96
96
  - [ ] Configuration management (`tt config`)
97
- - [ ] Interactive mode
97
+ - [x] Interactive mode
98
98
 
99
99
  #### CLI Enhancements
100
- - [ ] Rich terminal output (TTY gems)
100
+ - [x] Rich terminal output (TTY gems)
101
101
  - [ ] Progress indicators
102
- - [ ] Confirmation prompts
103
- - [ ] Output formatting (JSON, CSV, table)
102
+ - [x] Confirmation prompts
103
+ - [x] Output formatting (JSON, CSV, table)
104
104
  - [ ] Shell completion
105
105
 
106
106
  ### Phase 4: Advanced Features & Polish