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.
- checksums.yaml +4 -4
- data/.claude/commands/plan.md +13 -0
- data/.claude/commands/release-pr.md +12 -0
- data/CHANGELOG.md +180 -0
- data/README.md +424 -3
- data/ROADMAP.md +17 -17
- data/lib/tastytrade/cli/history_formatter.rb +304 -0
- data/lib/tastytrade/cli/orders.rb +749 -0
- data/lib/tastytrade/cli/positions_formatter.rb +114 -0
- data/lib/tastytrade/cli.rb +701 -12
- data/lib/tastytrade/cli_helpers.rb +111 -14
- data/lib/tastytrade/client.rb +7 -0
- data/lib/tastytrade/file_store.rb +83 -0
- data/lib/tastytrade/instruments/equity.rb +42 -0
- data/lib/tastytrade/models/account.rb +160 -2
- data/lib/tastytrade/models/account_balance.rb +46 -0
- data/lib/tastytrade/models/buying_power_effect.rb +61 -0
- data/lib/tastytrade/models/live_order.rb +272 -0
- data/lib/tastytrade/models/order_response.rb +106 -0
- data/lib/tastytrade/models/order_status.rb +84 -0
- data/lib/tastytrade/models/trading_status.rb +200 -0
- data/lib/tastytrade/models/transaction.rb +151 -0
- data/lib/tastytrade/models.rb +6 -0
- data/lib/tastytrade/order.rb +191 -0
- data/lib/tastytrade/order_validator.rb +355 -0
- data/lib/tastytrade/session.rb +26 -1
- data/lib/tastytrade/session_manager.rb +43 -14
- data/lib/tastytrade/version.rb +1 -1
- data/lib/tastytrade.rb +43 -0
- data/spec/exe/tastytrade_spec.rb +1 -1
- data/spec/tastytrade/cli/positions_spec.rb +267 -0
- data/spec/tastytrade/cli_auth_spec.rb +5 -0
- data/spec/tastytrade/cli_env_login_spec.rb +199 -0
- data/spec/tastytrade/cli_helpers_spec.rb +3 -26
- data/spec/tastytrade/cli_orders_spec.rb +168 -0
- data/spec/tastytrade/cli_status_spec.rb +153 -164
- data/spec/tastytrade/file_store_spec.rb +126 -0
- data/spec/tastytrade/models/account_balance_spec.rb +103 -0
- data/spec/tastytrade/models/account_order_history_spec.rb +229 -0
- data/spec/tastytrade/models/account_order_management_spec.rb +271 -0
- data/spec/tastytrade/models/account_place_order_spec.rb +125 -0
- data/spec/tastytrade/models/account_spec.rb +86 -15
- data/spec/tastytrade/models/buying_power_effect_spec.rb +250 -0
- data/spec/tastytrade/models/live_order_json_spec.rb +144 -0
- data/spec/tastytrade/models/live_order_spec.rb +295 -0
- data/spec/tastytrade/models/order_response_spec.rb +96 -0
- data/spec/tastytrade/models/order_status_spec.rb +113 -0
- data/spec/tastytrade/models/trading_status_spec.rb +260 -0
- data/spec/tastytrade/models/transaction_spec.rb +236 -0
- data/spec/tastytrade/order_edge_cases_spec.rb +163 -0
- data/spec/tastytrade/order_spec.rb +201 -0
- data/spec/tastytrade/order_validator_spec.rb +347 -0
- data/spec/tastytrade/session_env_spec.rb +169 -0
- data/spec/tastytrade/session_manager_spec.rb +43 -33
- metadata +34 -18
- data/lib/tastytrade/keyring_store.rb +0 -72
- 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
|