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
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tty-table"
4
+ require "bigdecimal"
5
+
6
+ module Tastytrade
7
+ # Formatter for displaying positions in various formats
8
+ class PositionsFormatter
9
+ def initialize(pastel: nil)
10
+ @pastel = pastel || Pastel.new
11
+ end
12
+
13
+ # Format positions as a table
14
+ def format_table(positions)
15
+ return if positions.empty?
16
+
17
+ headers = ["Symbol", "Quantity", "Type", "Avg Price", "Current Price", "P/L", "P/L %"]
18
+ rows = build_table_rows(positions)
19
+
20
+ table = TTY::Table.new(headers, rows)
21
+
22
+ begin
23
+ puts table.render(:unicode, padding: [0, 1])
24
+ rescue StandardError
25
+ # Fallback for testing or non-TTY environments
26
+ puts headers.join(" | ")
27
+ puts "-" * 80
28
+ rows.each { |row| puts row.join(" | ") }
29
+ end
30
+
31
+ display_summary(positions)
32
+ end
33
+
34
+ private
35
+
36
+ def build_table_rows(positions)
37
+ positions.map do |position|
38
+ [
39
+ format_symbol(position),
40
+ format_quantity(position),
41
+ position.instrument_type,
42
+ format_currency(position.average_open_price),
43
+ format_currency(position.close_price || BigDecimal("0")),
44
+ format_pl(position.unrealized_pnl),
45
+ format_pl_percentage(position.unrealized_pnl_percentage)
46
+ ]
47
+ end
48
+ end
49
+
50
+ def format_symbol(position)
51
+ if position.option?
52
+ position.display_symbol
53
+ else
54
+ position.symbol
55
+ end
56
+ end
57
+
58
+ def format_quantity(position)
59
+ quantity = position.quantity.to_i
60
+ if position.short?
61
+ "-#{quantity}"
62
+ else
63
+ quantity.to_s
64
+ end
65
+ end
66
+
67
+ def format_currency(amount)
68
+ return "$0.00" unless amount
69
+
70
+ formatted = "$#{"%.2f" % amount.to_f}"
71
+ formatted = "-#{formatted}" if amount < 0
72
+ formatted
73
+ end
74
+
75
+ def format_pl(amount)
76
+ return "$0.00" unless amount
77
+
78
+ formatted = format_currency(amount.abs)
79
+
80
+ if amount > 0
81
+ @pastel.green("+#{formatted}")
82
+ elsif amount < 0
83
+ @pastel.red("-#{formatted}")
84
+ else
85
+ formatted
86
+ end
87
+ end
88
+
89
+ def format_pl_percentage(percentage)
90
+ return "0.00%" unless percentage
91
+
92
+ formatted = "#{"%.2f" % percentage.to_f}%"
93
+
94
+ if percentage > 0
95
+ @pastel.green("+#{formatted}")
96
+ elsif percentage < 0
97
+ @pastel.red(formatted.to_s)
98
+ else
99
+ formatted
100
+ end
101
+ end
102
+
103
+ def display_summary(positions)
104
+ total_pl = positions.sum { |p| p.unrealized_pnl || BigDecimal("0") }
105
+ winners = positions.count { |p| p.unrealized_pnl && p.unrealized_pnl > 0 }
106
+ losers = positions.count { |p| p.unrealized_pnl && p.unrealized_pnl < 0 }
107
+
108
+ puts
109
+ summary = "Summary: #{positions.size} positions | Total P/L: #{format_pl(total_pl)} | "
110
+ summary += "Winners: #{winners}, Losers: #{losers}"
111
+ puts @pastel.dim(summary)
112
+ end
113
+ end
114
+ end