schwab_rb 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 (71) hide show
  1. checksums.yaml +7 -0
  2. data/.copilotignore +4 -0
  3. data/.rspec +2 -0
  4. data/.rspec_status +292 -0
  5. data/.rubocop.yml +41 -0
  6. data/.rubocop_todo.yml +105 -0
  7. data/CHANGELOG.md +28 -0
  8. data/LICENSE.txt +23 -0
  9. data/README.md +271 -0
  10. data/Rakefile +12 -0
  11. data/doc/notes/data_objects_analysis.md +223 -0
  12. data/doc/notes/data_objects_refactoring_plan.md +82 -0
  13. data/examples/fetch_account_numbers.rb +49 -0
  14. data/examples/fetch_user_preferences.rb +49 -0
  15. data/lib/schwab_rb/account.rb +9 -0
  16. data/lib/schwab_rb/auth/auth_context.rb +23 -0
  17. data/lib/schwab_rb/auth/init_client_easy.rb +45 -0
  18. data/lib/schwab_rb/auth/init_client_login.rb +201 -0
  19. data/lib/schwab_rb/auth/init_client_token_file.rb +30 -0
  20. data/lib/schwab_rb/auth/login_flow_server.rb +55 -0
  21. data/lib/schwab_rb/auth/token.rb +24 -0
  22. data/lib/schwab_rb/auth/token_manager.rb +105 -0
  23. data/lib/schwab_rb/clients/async_client.rb +122 -0
  24. data/lib/schwab_rb/clients/base_client.rb +887 -0
  25. data/lib/schwab_rb/clients/client.rb +97 -0
  26. data/lib/schwab_rb/configuration.rb +39 -0
  27. data/lib/schwab_rb/constants.rb +7 -0
  28. data/lib/schwab_rb/data_objects/account.rb +281 -0
  29. data/lib/schwab_rb/data_objects/account_numbers.rb +68 -0
  30. data/lib/schwab_rb/data_objects/instrument.rb +156 -0
  31. data/lib/schwab_rb/data_objects/market_hours.rb +275 -0
  32. data/lib/schwab_rb/data_objects/option.rb +147 -0
  33. data/lib/schwab_rb/data_objects/option_chain.rb +95 -0
  34. data/lib/schwab_rb/data_objects/option_expiration_chain.rb +134 -0
  35. data/lib/schwab_rb/data_objects/order.rb +186 -0
  36. data/lib/schwab_rb/data_objects/order_leg.rb +68 -0
  37. data/lib/schwab_rb/data_objects/order_preview.rb +237 -0
  38. data/lib/schwab_rb/data_objects/position.rb +100 -0
  39. data/lib/schwab_rb/data_objects/price_history.rb +187 -0
  40. data/lib/schwab_rb/data_objects/quote.rb +276 -0
  41. data/lib/schwab_rb/data_objects/transaction.rb +132 -0
  42. data/lib/schwab_rb/data_objects/user_preferences.rb +129 -0
  43. data/lib/schwab_rb/market_hours.rb +13 -0
  44. data/lib/schwab_rb/movers.rb +35 -0
  45. data/lib/schwab_rb/option.rb +64 -0
  46. data/lib/schwab_rb/orders/builder.rb +202 -0
  47. data/lib/schwab_rb/orders/destination.rb +19 -0
  48. data/lib/schwab_rb/orders/duration.rb +9 -0
  49. data/lib/schwab_rb/orders/equity_instructions.rb +10 -0
  50. data/lib/schwab_rb/orders/errors.rb +5 -0
  51. data/lib/schwab_rb/orders/instruments.rb +35 -0
  52. data/lib/schwab_rb/orders/option_instructions.rb +10 -0
  53. data/lib/schwab_rb/orders/order.rb +77 -0
  54. data/lib/schwab_rb/orders/price_link_basis.rb +15 -0
  55. data/lib/schwab_rb/orders/price_link_type.rb +9 -0
  56. data/lib/schwab_rb/orders/session.rb +14 -0
  57. data/lib/schwab_rb/orders/special_instruction.rb +10 -0
  58. data/lib/schwab_rb/orders/stop_price_link_basis.rb +15 -0
  59. data/lib/schwab_rb/orders/stop_price_link_type.rb +9 -0
  60. data/lib/schwab_rb/orders/stop_type.rb +11 -0
  61. data/lib/schwab_rb/orders/tax_lot_method.rb +13 -0
  62. data/lib/schwab_rb/price_history.rb +55 -0
  63. data/lib/schwab_rb/quote.rb +13 -0
  64. data/lib/schwab_rb/transaction.rb +23 -0
  65. data/lib/schwab_rb/utils/enum_enforcer.rb +73 -0
  66. data/lib/schwab_rb/utils/logger.rb +70 -0
  67. data/lib/schwab_rb/utils/redactor.rb +104 -0
  68. data/lib/schwab_rb/version.rb +5 -0
  69. data/lib/schwab_rb.rb +48 -0
  70. data/sig/schwab_rb.rbs +4 -0
  71. metadata +289 -0
@@ -0,0 +1,187 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SchwabRb
4
+ module DataObjects
5
+ class PriceHistory
6
+ attr_reader :symbol, :empty, :candles
7
+
8
+ class << self
9
+ def build(data)
10
+ new(data)
11
+ end
12
+ end
13
+
14
+ def initialize(data)
15
+ @symbol = data['symbol']
16
+ @empty = data['empty']
17
+ @candles = data['candles']&.map { |candle_data| Candle.new(candle_data) } || []
18
+ end
19
+
20
+ def to_h
21
+ {
22
+ 'symbol' => @symbol,
23
+ 'empty' => @empty,
24
+ 'candles' => @candles.map(&:to_h)
25
+ }
26
+ end
27
+
28
+ def empty?
29
+ @empty == true || @candles.empty?
30
+ end
31
+
32
+ def count
33
+ @candles.length
34
+ end
35
+ alias size count
36
+ alias length count
37
+
38
+ def first_candle
39
+ @candles.first
40
+ end
41
+
42
+ def last_candle
43
+ @candles.last
44
+ end
45
+
46
+ def candles_for_date_range(start_date, end_date)
47
+ start_timestamp = start_date.to_time.to_i * 1000
48
+ end_timestamp = end_date.to_time.to_i * 1000
49
+
50
+ @candles.select do |candle|
51
+ candle.datetime >= start_timestamp && candle.datetime <= end_timestamp
52
+ end
53
+ end
54
+
55
+ def highest_price
56
+ return nil if @candles.empty?
57
+ @candles.map(&:high).max
58
+ end
59
+
60
+ def lowest_price
61
+ return nil if @candles.empty?
62
+ @candles.map(&:low).min
63
+ end
64
+
65
+ def highest_volume
66
+ return nil if @candles.empty?
67
+ @candles.map(&:volume).max
68
+ end
69
+
70
+ def total_volume
71
+ return 0 if @candles.empty?
72
+ @candles.map(&:volume).sum
73
+ end
74
+
75
+ def average_price
76
+ return nil if @candles.empty?
77
+ total_price = @candles.map(&:close).sum
78
+ total_price / @candles.length.to_f
79
+ end
80
+
81
+ def price_range
82
+ return nil if @candles.empty?
83
+ {
84
+ high: highest_price,
85
+ low: lowest_price,
86
+ range: highest_price - lowest_price
87
+ }
88
+ end
89
+
90
+ def each(&block)
91
+ return enum_for(:each) unless block_given?
92
+ @candles.each(&block)
93
+ end
94
+
95
+ include Enumerable
96
+
97
+ class Candle
98
+ attr_reader :open, :high, :low, :close, :volume, :datetime
99
+
100
+ def initialize(data)
101
+ @open = data['open']
102
+ @high = data['high']
103
+ @low = data['low']
104
+ @close = data['close']
105
+ @volume = data['volume']
106
+ @datetime = data['datetime']
107
+ end
108
+
109
+ def to_h
110
+ {
111
+ 'open' => @open,
112
+ 'high' => @high,
113
+ 'low' => @low,
114
+ 'close' => @close,
115
+ 'volume' => @volume,
116
+ 'datetime' => @datetime
117
+ }
118
+ end
119
+
120
+ def date_time
121
+ Time.at(@datetime / 1000.0) if @datetime
122
+ end
123
+
124
+ def date
125
+ date_time&.to_date
126
+ end
127
+
128
+ def price_change
129
+ @close - @open
130
+ end
131
+
132
+ def price_change_percent
133
+ return 0 if @open == 0
134
+ ((price_change / @open) * 100).round(4)
135
+ end
136
+
137
+ def is_green?
138
+ @close > @open
139
+ end
140
+
141
+ def is_red?
142
+ @close < @open
143
+ end
144
+
145
+ def is_doji?
146
+ @close == @open
147
+ end
148
+
149
+ def body_size
150
+ (@close - @open).abs
151
+ end
152
+
153
+ def wick_size
154
+ upper_wick + lower_wick
155
+ end
156
+
157
+ def upper_wick
158
+ @high - [@open, @close].max
159
+ end
160
+
161
+ def lower_wick
162
+ [@open, @close].min - @low
163
+ end
164
+
165
+ def true_range
166
+ [
167
+ @high - @low,
168
+ (@high - @close).abs,
169
+ (@low - @close).abs
170
+ ].max
171
+ end
172
+
173
+ def ohlc_average
174
+ (@open + @high + @low + @close) / 4.0
175
+ end
176
+
177
+ def typical_price
178
+ (@high + @low + @close) / 3.0
179
+ end
180
+
181
+ def weighted_price
182
+ (@high + @low + @close + @close) / 4.0
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,276 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SchwabRb
4
+ module DataObjects
5
+ class QuoteFactory
6
+ def self.build(quote_data)
7
+ # Extract the data from nested structure (symbol is the key)
8
+ symbol = quote_data.keys.first
9
+ data = quote_data[symbol]
10
+ data[:symbol] ||= symbol
11
+
12
+ case data[:assetMainType]
13
+ when 'OPTION'
14
+ OptionQuote.new(data)
15
+ when 'INDEX'
16
+ IndexQuote.new(data)
17
+ when 'EQUITY'
18
+ EquityQuote.new(data)
19
+ else
20
+ raise "Unknown assetMainType: #{data[:assetMainType]}"
21
+ end
22
+ end
23
+ end
24
+
25
+ class OptionQuote
26
+ attr_reader :symbol, :asset_main_type, :realtime, :ssid, :quote_52_week_high, :quote_52_week_low, :ask_price,
27
+ :ask_size, :bid_price, :bid_size, :close_price, :delta, :gamma, :high_price, :ind_ask_price, :ind_bid_price, :ind_time, :implied_yield, :last_price, :last_size, :low_price, :mark, :mark_change, :mark_percent_change, :money_intrinsic_value, :net_change, :net_percent_change, :open_interest, :open_price, :time, :rho, :security_status, :theoretical_option_value, :theta, :time_value, :total_volume, :trade_time, :underlying_price, :vega, :volatility, :contract_type, :days_to_expiration, :deliverables, :description, :exchange, :exchange_name, :exercise_type, :expiration_day, :expiration_month, :expiration_type, :expiration_year, :is_penny_pilot, :last_trading_day, :multiplier, :settlement_type, :strike_price, :underlying, :underlying_asset_type
28
+
29
+ def initialize(data)
30
+ @symbol = data[:symbol]
31
+ @asset_main_type = data[:assetMainType]
32
+ @realtime = data[:realtime]
33
+ @ssid = data[:ssid]
34
+ @quote_52_week_high = data.dig(:quote, :"52WeekHigh")
35
+ @quote_52_week_low = data.dig(:quote, :"52WeekLow")
36
+ @ask_price = data.dig(:quote, :askPrice)
37
+ @ask_size = data.dig(:quote, :askSize)
38
+ @bid_price = data.dig(:quote, :bidPrice)
39
+ @bid_size = data.dig(:quote, :bidSize)
40
+ @close_price = data.dig(:quote, :closePrice)
41
+ @delta = data.dig(:quote, :delta)
42
+ @gamma = data.dig(:quote, :gamma)
43
+ @high_price = data.dig(:quote, :highPrice)
44
+ @ind_ask_price = data.dig(:quote, :indAskPrice)
45
+ @ind_bid_price = data.dig(:quote, :indBidPrice)
46
+ @ind_quote_time = data.dig(:quote, :indQuoteTime)
47
+ @implied_yield = data.dig(:quote, :impliedYield)
48
+ @last_price = data.dig(:quote, :lastPrice)
49
+ @last_size = data.dig(:quote, :lastSize)
50
+ @low_price = data.dig(:quote, :lowPrice)
51
+ @mark = data.dig(:quote, :mark)
52
+ @mark_change = data.dig(:quote, :markChange)
53
+ @mark_percent_change = data.dig(:quote, :markPercentChange)
54
+ @money_intrinsic_value = data.dig(:quote, :moneyIntrinsicValue)
55
+ @net_change = data.dig(:quote, :netChange)
56
+ @net_percent_change = data.dig(:quote, :netPercentChange)
57
+ @open_interest = data.dig(:quote, :openInterest)
58
+ @open_price = data.dig(:quote, :openPrice)
59
+ @quote_time = data.dig(:quote, :quoteTime)
60
+ @rho = data.dig(:quote, :rho)
61
+ @security_status = data.dig(:quote, :securityStatus)
62
+ @theoretical_option_value = data.dig(:quote, :theoreticalOptionValue)
63
+ @theta = data.dig(:quote, :theta)
64
+ @time_value = data.dig(:quote, :timeValue)
65
+ @total_volume = data.dig(:quote, :totalVolume)
66
+ @trade_time = data.dig(:quote, :tradeTime)
67
+ @underlying_price = data.dig(:quote, :underlyingPrice)
68
+ @vega = data.dig(:quote, :vega)
69
+ @volatility = data.dig(:quote, :volatility)
70
+ @contract_type = data.dig(:reference, :contractType)
71
+ @days_to_expiration = data.dig(:reference, :daysToExpiration)
72
+ @deliverables = data.dig(:reference, :deliverables)
73
+ @description = data.dig(:reference, :description)
74
+ @exchange = data.dig(:reference, :exchange)
75
+ @exchange_name = data.dig(:reference, :exchangeName)
76
+ @exercise_type = data.dig(:reference, :exerciseType)
77
+ @expiration_day = data.dig(:reference, :expirationDay)
78
+ @expiration_month = data.dig(:reference, :expirationMonth)
79
+ @expiration_type = data.dig(:reference, :expirationType)
80
+ @expiration_year = data.dig(:reference, :expirationYear)
81
+ @is_penny_pilot = data.dig(:reference, :isPennyPilot)
82
+ @last_trading_day = data.dig(:reference, :lastTradingDay)
83
+ @multiplier = data.dig(:reference, :multiplier)
84
+ @settlement_type = data.dig(:reference, :settlementType)
85
+ @strike_price = data.dig(:reference, :strikePrice)
86
+ @underlying = data.dig(:reference, :underlying)
87
+ @underlying_asset_type = data.dig(:reference, :underlyingAssetType)
88
+ end
89
+
90
+ def zone
91
+ if quote_delta.abs > 0.3
92
+ 'DANGER'
93
+ elsif quote_delta.abs > 0.15
94
+ 'AT_RISK'
95
+ else
96
+ 'SAFE'
97
+ end
98
+ end
99
+ end
100
+
101
+ class IndexQuote
102
+ attr_reader :symbol, :asset_main_type, :realtime, :ssid, :avg_10_days_volume, :avg_1_year_volume, :div_amount,
103
+ :div_freq, :div_pay_amount, :div_yield, :eps, :fund_leverage_factor, :pe_ratio, :quote_52_week_high, :quote_52_week_low, :close_price, :high_price, :last_price, :low_price, :net_change, :net_percent_change, :open_price, :security_status, :total_volume, :trade_time, :description, :exchange, :exchange_name
104
+
105
+ def initialize(data)
106
+ @symbol = data[:symbol]
107
+ @asset_main_type = data[:assetMainType]
108
+ @realtime = data[:realtime]
109
+ @ssid = data[:ssid]
110
+ @avg_10_days_volume = data.dig(:fundamental, :avg10DaysVolume)
111
+ @avg_1_year_volume = data.dig(:fundamental, :avg1YearVolume)
112
+ @div_amount = data.dig(:fundamental, :divAmount)
113
+ @div_freq = data.dig(:fundamental, :divFreq)
114
+ @div_pay_amount = data.dig(:fundamental, :divPayAmount)
115
+ @div_yield = data.dig(:fundamental, :divYield)
116
+ @eps = data.dig(:fundamental, :eps)
117
+ @fund_leverage_factor = data.dig(:fundamental, :fundLeverageFactor)
118
+ @pe_ratio = data.dig(:fundamental, :peRatio)
119
+ @quote_52_week_high = data.dig(:quote, :"52WeekHigh")
120
+ @quote_52_week_low = data.dig(:quote, :"52WeekLow")
121
+ @close_price = data.dig(:quote, :closePrice)
122
+ @high_price = data.dig(:quote, :highPrice)
123
+ @last_price = data.dig(:quote, :lastPrice)
124
+ @low_price = data.dig(:quote, :lowPrice)
125
+ @net_change = data.dig(:quote, :netChange)
126
+ @net_percent_change = data.dig(:quote, :netPercentChange)
127
+ @open_price = data.dig(:quote, :openPrice)
128
+ @security_status = data.dig(:quote, :securityStatus)
129
+ @total_volume = data.dig(:quote, :totalVolume)
130
+ @trade_time = data.dig(:quote, :tradeTime)
131
+ @description = data.dig(:reference, :description)
132
+ @exchange = data.dig(:reference, :exchange)
133
+ @exchange_name = data.dig(:reference, :exchangeName)
134
+ end
135
+
136
+ def mark
137
+ (@high_price + @low_price) / 2.0
138
+ end
139
+ end
140
+
141
+ class EquityQuote
142
+ attr_reader :symbol, :asset_main_type, :asset_sub_type, :quote_type, :realtime, :ssid, :extended_ask_price,
143
+ :extended_ask_size, :extended_bid_price, :extended_bid_size, :extended_last_price, :extended_last_size, :extended_mark, :extended_quote_time, :extended_total_volume, :extended_trade_time, :avg_10_days_volume, :avg_1_year_volume, :declaration_date, :div_amount, :div_ex_date, :div_freq, :div_pay_amount, :div_pay_date, :div_yield, :eps, :fund_leverage_factor, :last_earnings_date, :next_div_ex_date, :next_div_pay_date, :pe_ratio, :quote_52_week_high, :quote_52_week_low, :ask_mic_id, :ask_price, :ask_size, :ask_time, :bid_mic_id, :bid_price, :bid_size, :bid_time, :close_price, :high_price, :last_mic_id, :last_price, :last_size, :low_price, :mark, :mark_change, :mark_percent_change, :net_change, :net_percent_change, :open_price, :post_market_change, :post_market_percent_change, :time, :security_status, :total_volume, :trade_time, :cusip, :description, :exchange, :exchange_name, :is_hard_to_borrow, :is_shortable, :htb_rate, :market_last_price, :market_last_size, :market_net_change, :market_percent_change, :market_trade_time
144
+
145
+ def initialize(data)
146
+ @symbol = data[:symbol]
147
+ @asset_main_type = data[:assetMainType]
148
+ @asset_sub_type = data[:assetSubType]
149
+ @quote_type = data[:quoteType]
150
+ @realtime = data[:realtime]
151
+ @ssid = data[:ssid]
152
+ @extended_ask_price = data.dig(:extended, :askPrice)
153
+ @extended_ask_size = data.dig(:extended, :askSize)
154
+ @extended_bid_price = data.dig(:extended, :bidPrice)
155
+ @extended_bid_size = data.dig(:extended, :bidSize)
156
+ @extended_last_price = data.dig(:extended, :lastPrice)
157
+ @extended_last_size = data.dig(:extended, :lastSize)
158
+ @extended_mark = data.dig(:extended, :mark)
159
+ @extended_quote_time = data.dig(:extended, :quoteTime)
160
+ @extended_total_volume = data.dig(:extended, :totalVolume)
161
+ @extended_trade_time = data.dig(:extended, :tradeTime)
162
+ @avg_10_days_volume = data.dig(:fundamental, :avg10DaysVolume)
163
+ @avg_1_year_volume = data.dig(:fundamental, :avg1YearVolume)
164
+ @declaration_date = data.dig(:fundamental, :declarationDate)
165
+ @div_amount = data.dig(:fundamental, :divAmount)
166
+ @div_ex_date = data.dig(:fundamental, :divExDate)
167
+ @div_freq = data.dig(:fundamental, :divFreq)
168
+ @div_pay_amount = data.dig(:fundamental, :divPayAmount)
169
+ @div_pay_date = data.dig(:fundamental, :divPayDate)
170
+ @div_yield = data.dig(:fundamental, :divYield)
171
+ @eps = data.dig(:fundamental, :eps)
172
+ @fund_leverage_factor = data.dig(:fundamental, :fundLeverageFactor)
173
+ @last_earnings_date = data.dig(:fundamental, :lastEarningsDate)
174
+ @next_div_ex_date = data.dig(:fundamental, :nextDivExDate)
175
+ @next_div_pay_date = data.dig(:fundamental, :nextDivPayDate)
176
+ @pe_ratio = data.dig(:fundamental, :peRatio)
177
+ @quote_52_week_high = data.dig(:quote, :"52WeekHigh")
178
+ @quote_52_week_low = data.dig(:quote, :"52WeekLow")
179
+ @ask_mic_id = data.dig(:quote, :askMICId)
180
+ @ask_price = data.dig(:quote, :askPrice)
181
+ @ask_size = data.dig(:quote, :askSize)
182
+ @ask_time = data.dig(:quote, :askTime)
183
+ @bid_mic_id = data.dig(:quote, :bidMICId)
184
+ @bid_price = data.dig(:quote, :bidPrice)
185
+ @bid_size = data.dig(:quote, :bidSize)
186
+ @bid_time = data.dig(:quote, :bidTime)
187
+ @close_price = data.dig(:quote, :closePrice)
188
+ @high_price = data.dig(:quote, :highPrice)
189
+ @last_mic_id = data.dig(:quote, :lastMICId)
190
+ @last_price = data.dig(:quote, :lastPrice)
191
+ @last_size = data.dig(:quote, :lastSize)
192
+ @low_price = data.dig(:quote, :lowPrice)
193
+ @mark = data.dig(:quote, :mark)
194
+ @mark_change = data.dig(:quote, :markChange)
195
+ @mark_percent_change = data.dig(:quote, :markPercentChange)
196
+ @net_change = data.dig(:quote, :netChange)
197
+ @net_percent_change = data.dig(:quote, :netPercentChange)
198
+ @open_price = data.dig(:quote, :openPrice)
199
+ @post_market_change = data.dig(:quote, :postMarketChange)
200
+ @post_market_percent_change = data.dig(:quote, :postMarketPercentChange)
201
+ @quote_time = data.dig(:quote, :quoteTime)
202
+ @security_status = data.dig(:quote, :securityStatus)
203
+ @total_volume = data.dig(:quote, :totalVolume)
204
+ @trade_time = data.dig(:quote, :tradeTime)
205
+ @cusip = data.dig(:reference, :cusip)
206
+ @description = data.dig(:reference, :description)
207
+ @exchange = data.dig(:reference, :exchange)
208
+ @exchange_name = data.dig(:reference, :exchangeName)
209
+ @is_hard_to_borrow = data.dig(:reference, :isHardToBorrow)
210
+ @is_shortable = data.dig(:reference, :isShortable)
211
+ @htb_rate = data.dig(:reference, :htbRate)
212
+ @market_last_price = data.dig(:regular, :regularMarketLastPrice)
213
+ @market_last_size = data.dig(:regular, :regularMarketLastSize)
214
+ @market_net_change = data.dig(:regular, :regularMarketNetChange)
215
+ @market_percent_change = data.dig(:regular, :regularMarketPercentChange)
216
+ @market_trade_time = data.dig(:regular, :regularMarketTradeTime)
217
+ end
218
+
219
+ def to_h
220
+ {
221
+ symbol: @symbol,
222
+ asset_main_type: @asset_main_type,
223
+ asset_sub_type: @asset_sub_type,
224
+ quote_type: @quote_type,
225
+ realtime: @realtime,
226
+ ssid: @ssid,
227
+ extended_ask_price: @extended_ask_price,
228
+ extended_ask_size: @extended_ask_size,
229
+ extended_bid_price: @extended_bid_price,
230
+ extended_bid_size: @extended_bid_size,
231
+ extended_last_price: @extended_last_price,
232
+ extended_last_size: @extended_last_size,
233
+ extended_mark: @extended_mark,
234
+ extended_quote_time: @extended_quote_time,
235
+ extended_total_volume: @extended_total_volume,
236
+ extended_trade_time: @extended_trade_time,
237
+ avg_10_days_volume: @avg_10_days_volume,
238
+ avg_1_year_volume: @avg_1_year_volume,
239
+ declaration_date: @declaration_date,
240
+ div_amount: @div_amount,
241
+ div_ex_date: @div_ex_date,
242
+ div_freq: @div_freq,
243
+ div_pay_amount: @div_pay_amount,
244
+ div_pay_date: @div_pay_date,
245
+ div_yield: @div_yield,
246
+ eps: @eps,
247
+ fund_leverage_factor: @fund_leverage_factor,
248
+ last_earnings_date: @last_earnings_date,
249
+ next_div_ex_date: @next_div_ex_date,
250
+ next_div_pay_date: @next_div_pay_date,
251
+ pe_ratio: @pe_ratio,
252
+ quote_52_week_high: @quote_52_week_high,
253
+ quote_52_week_low: @quote_52_week_low,
254
+ ask_mic_id: @ask_mic_id,
255
+ ask_price: @ask_price,
256
+ ask_size: @ask_size,
257
+ ask_time: @ask_time,
258
+ bid_mic_id: @bid_mic_id,
259
+ bid_price: @bid_price,
260
+ bid_size: @bid_size,
261
+ bid_time: @bid_time,
262
+ close_price: @close_price,
263
+ high_price: @high_price,
264
+ last_mic_id: @last_mic_id,
265
+ last_price: @last_price,
266
+ last_size: @last_size,
267
+ low_price: @low_price
268
+ }
269
+ end
270
+
271
+ def to_s
272
+ "<EquityQuote symbol: #{@symbol}, last_price: #{@last_price}, mark: #{@mark}, market_last_price: #{@market_last_price}, extended_bid_price: #{@extended_bid_price}>"
273
+ end
274
+ end
275
+ end
276
+ end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'instrument'
4
+
5
+ module SchwabRb
6
+ module DataObjects
7
+ class TransferItem
8
+ class << self
9
+ def build(data)
10
+ TransferItem.new(
11
+ instrument: Instrument.build(data.fetch(:instrument)),
12
+ amount: data.fetch(:amount),
13
+ cost: data.fetch(:cost),
14
+ fee_type: data.fetch(:feeType, nil),
15
+ position_effect: data.fetch(:positionEffect, nil)
16
+ )
17
+ end
18
+ end
19
+
20
+ attr_reader :instrument, :amount, :cost, :fee_type, :position_effect
21
+
22
+ def initialize(instrument:, amount:, cost:, fee_type:, position_effect:)
23
+ @instrument = instrument
24
+ @amount = amount
25
+ @cost = cost
26
+ @fee_type = fee_type
27
+ @position_effect = position_effect
28
+ end
29
+
30
+ def symbol
31
+ option? ? instrument.symbol : ''
32
+ end
33
+
34
+ def underlying_symbol
35
+ option? ? instrument.underlying_symbol : ''
36
+ end
37
+
38
+ def description
39
+ option? ? instrument.description : ''
40
+ end
41
+
42
+ def option?
43
+ instrument.option?
44
+ end
45
+
46
+ def put_call
47
+ instrument.put_call
48
+ end
49
+
50
+ def fee?
51
+ %w[OPT_REG_FEE TAF_FEE SEC_FEE].include?(fee_type)
52
+ end
53
+
54
+ def commission?
55
+ fee_type == 'COMMISSION'
56
+ end
57
+
58
+ def to_h
59
+ {
60
+ instrument: instrument.to_h,
61
+ amount: amount,
62
+ cost: cost,
63
+ fee_type: fee_type,
64
+ position_effect: position_effect
65
+ }
66
+ end
67
+ end
68
+
69
+ class Transaction
70
+ class << self
71
+ def build(data)
72
+ Transaction.new(
73
+ activity_id: data.fetch(:activityId),
74
+ time: data.fetch(:time),
75
+ type: data.fetch(:type),
76
+ status: data.fetch(:status),
77
+ sub_account: data.fetch(:subAccount),
78
+ trade_date: data.fetch(:tradeDate),
79
+ position_id: data.fetch(:positionId, nil),
80
+ order_id: data.fetch(:orderId, nil),
81
+ net_amount: data.fetch(:netAmount, nil),
82
+ transfer_items: data.fetch(:transferItems).map { |ti| TransferItem.build(ti) }
83
+ )
84
+ end
85
+ end
86
+
87
+ def initialize(activity_id:, time:, type:, status:, sub_account:, trade_date:, position_id:, order_id:,
88
+ net_amount:, transfer_items: [])
89
+ @activity_id = activity_id
90
+ @time = time
91
+ @type = type
92
+ @status = status
93
+ @sub_account = sub_account
94
+ @trade_date = trade_date
95
+ @position_id = position_id
96
+ @order_id = order_id
97
+ @net_amount = net_amount
98
+ @transfer_items = transfer_items
99
+ end
100
+
101
+ attr_reader :activity_id, :time, :type, :status, :sub_account, :trade_date, :position_id, :order_id, :net_amount,
102
+ :transfer_items
103
+
104
+ def trade?
105
+ type == 'TRADE'
106
+ end
107
+
108
+ def symbols
109
+ transfer_items.map { |ti| ti.instrument.symbol }
110
+ end
111
+
112
+ def option_symbol
113
+ transfer_items.find { |ti| ti.instrument.option? }.instrument.symbol
114
+ end
115
+
116
+ def to_h
117
+ {
118
+ activity_id: @activity_id,
119
+ time: @time,
120
+ type: @type,
121
+ status: @status,
122
+ sub_account: @sub_account,
123
+ trade_date: @trade_date,
124
+ position_id: @position_id,
125
+ order_id: @order_id,
126
+ net_amount: @net_amount,
127
+ transfer_items: @transfer_items.map(&:to_h)
128
+ }
129
+ end
130
+ end
131
+ end
132
+ end