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.
- checksums.yaml +7 -0
- data/.copilotignore +4 -0
- data/.rspec +2 -0
- data/.rspec_status +292 -0
- data/.rubocop.yml +41 -0
- data/.rubocop_todo.yml +105 -0
- data/CHANGELOG.md +28 -0
- data/LICENSE.txt +23 -0
- data/README.md +271 -0
- data/Rakefile +12 -0
- data/doc/notes/data_objects_analysis.md +223 -0
- data/doc/notes/data_objects_refactoring_plan.md +82 -0
- data/examples/fetch_account_numbers.rb +49 -0
- data/examples/fetch_user_preferences.rb +49 -0
- data/lib/schwab_rb/account.rb +9 -0
- data/lib/schwab_rb/auth/auth_context.rb +23 -0
- data/lib/schwab_rb/auth/init_client_easy.rb +45 -0
- data/lib/schwab_rb/auth/init_client_login.rb +201 -0
- data/lib/schwab_rb/auth/init_client_token_file.rb +30 -0
- data/lib/schwab_rb/auth/login_flow_server.rb +55 -0
- data/lib/schwab_rb/auth/token.rb +24 -0
- data/lib/schwab_rb/auth/token_manager.rb +105 -0
- data/lib/schwab_rb/clients/async_client.rb +122 -0
- data/lib/schwab_rb/clients/base_client.rb +887 -0
- data/lib/schwab_rb/clients/client.rb +97 -0
- data/lib/schwab_rb/configuration.rb +39 -0
- data/lib/schwab_rb/constants.rb +7 -0
- data/lib/schwab_rb/data_objects/account.rb +281 -0
- data/lib/schwab_rb/data_objects/account_numbers.rb +68 -0
- data/lib/schwab_rb/data_objects/instrument.rb +156 -0
- data/lib/schwab_rb/data_objects/market_hours.rb +275 -0
- data/lib/schwab_rb/data_objects/option.rb +147 -0
- data/lib/schwab_rb/data_objects/option_chain.rb +95 -0
- data/lib/schwab_rb/data_objects/option_expiration_chain.rb +134 -0
- data/lib/schwab_rb/data_objects/order.rb +186 -0
- data/lib/schwab_rb/data_objects/order_leg.rb +68 -0
- data/lib/schwab_rb/data_objects/order_preview.rb +237 -0
- data/lib/schwab_rb/data_objects/position.rb +100 -0
- data/lib/schwab_rb/data_objects/price_history.rb +187 -0
- data/lib/schwab_rb/data_objects/quote.rb +276 -0
- data/lib/schwab_rb/data_objects/transaction.rb +132 -0
- data/lib/schwab_rb/data_objects/user_preferences.rb +129 -0
- data/lib/schwab_rb/market_hours.rb +13 -0
- data/lib/schwab_rb/movers.rb +35 -0
- data/lib/schwab_rb/option.rb +64 -0
- data/lib/schwab_rb/orders/builder.rb +202 -0
- data/lib/schwab_rb/orders/destination.rb +19 -0
- data/lib/schwab_rb/orders/duration.rb +9 -0
- data/lib/schwab_rb/orders/equity_instructions.rb +10 -0
- data/lib/schwab_rb/orders/errors.rb +5 -0
- data/lib/schwab_rb/orders/instruments.rb +35 -0
- data/lib/schwab_rb/orders/option_instructions.rb +10 -0
- data/lib/schwab_rb/orders/order.rb +77 -0
- data/lib/schwab_rb/orders/price_link_basis.rb +15 -0
- data/lib/schwab_rb/orders/price_link_type.rb +9 -0
- data/lib/schwab_rb/orders/session.rb +14 -0
- data/lib/schwab_rb/orders/special_instruction.rb +10 -0
- data/lib/schwab_rb/orders/stop_price_link_basis.rb +15 -0
- data/lib/schwab_rb/orders/stop_price_link_type.rb +9 -0
- data/lib/schwab_rb/orders/stop_type.rb +11 -0
- data/lib/schwab_rb/orders/tax_lot_method.rb +13 -0
- data/lib/schwab_rb/price_history.rb +55 -0
- data/lib/schwab_rb/quote.rb +13 -0
- data/lib/schwab_rb/transaction.rb +23 -0
- data/lib/schwab_rb/utils/enum_enforcer.rb +73 -0
- data/lib/schwab_rb/utils/logger.rb +70 -0
- data/lib/schwab_rb/utils/redactor.rb +104 -0
- data/lib/schwab_rb/version.rb +5 -0
- data/lib/schwab_rb.rb +48 -0
- data/sig/schwab_rb.rbs +4 -0
- metadata +289 -0
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
require "net/http"
|
5
|
+
require "uri"
|
6
|
+
require_relative "base_client"
|
7
|
+
require_relative "../utils/logger"
|
8
|
+
require_relative "../utils/redactor"
|
9
|
+
|
10
|
+
module SchwabRb
|
11
|
+
class Client < BaseClient
|
12
|
+
BASE_URL = "https://api.schwabapi.com"
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def get(path, params = {})
|
17
|
+
dest = URI(URI::DEFAULT_PARSER.escape("#{BASE_URL}#{path}"))
|
18
|
+
dest.query = URI.encode_www_form(params) if params.any?
|
19
|
+
|
20
|
+
req_num = req_num()
|
21
|
+
log_request("GET", req_num, dest, params)
|
22
|
+
response = session.get(dest)
|
23
|
+
|
24
|
+
log_response(response, req_num)
|
25
|
+
response
|
26
|
+
end
|
27
|
+
|
28
|
+
def post(path, data = {})
|
29
|
+
dest = URI(URI::DEFAULT_PARSER.escape("#{BASE_URL}#{path}"))
|
30
|
+
|
31
|
+
req_num = req_num()
|
32
|
+
log_request("POST", req_num, dest, data)
|
33
|
+
|
34
|
+
response = session.post(
|
35
|
+
dest,
|
36
|
+
{
|
37
|
+
:body => data.to_json,
|
38
|
+
:headers => { "Content-Type" => "application/json" }
|
39
|
+
}
|
40
|
+
)
|
41
|
+
log_response(response, req_num)
|
42
|
+
response
|
43
|
+
end
|
44
|
+
|
45
|
+
def put(path, data = {})
|
46
|
+
dest = URI(URI::DEFAULT_PARSER.escape("#{BASE_URL}#{path}"))
|
47
|
+
|
48
|
+
req_num = req_num()
|
49
|
+
log_request("PUT", req_num, dest, data)
|
50
|
+
|
51
|
+
response = session.put(
|
52
|
+
dest,
|
53
|
+
{
|
54
|
+
:body => data.to_json,
|
55
|
+
:headers => { "Content-Type" => "application/json" }
|
56
|
+
}
|
57
|
+
)
|
58
|
+
log_response(response, req_num)
|
59
|
+
response
|
60
|
+
end
|
61
|
+
|
62
|
+
def delete(path)
|
63
|
+
dest = URI(URI::DEFAULT_PARSER.escape("#{BASE_URL}#{path}"))
|
64
|
+
|
65
|
+
req_num = req_num()
|
66
|
+
log_request("DELETE", req_num, dest)
|
67
|
+
|
68
|
+
response = session.delete(dest)
|
69
|
+
log_response(response, req_num)
|
70
|
+
response
|
71
|
+
end
|
72
|
+
|
73
|
+
def log_request(method, req_num, dest, data = nil)
|
74
|
+
redacted_dest = SchwabRb::Redactor.redact_url(dest.to_s)
|
75
|
+
SchwabRb::Logger.logger.info("Req #{req_num}: #{method} to #{redacted_dest}")
|
76
|
+
|
77
|
+
if data
|
78
|
+
redacted_data = SchwabRb::Redactor.redact_data(data)
|
79
|
+
SchwabRb::Logger.logger.debug("Payload: #{JSON.pretty_generate(redacted_data)}")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def log_response(response, req_num)
|
84
|
+
SchwabRb::Logger.logger.info("Resp #{req_num}: Status #{response.status}")
|
85
|
+
|
86
|
+
if SchwabRb::Logger.logger.level == ::Logger::DEBUG
|
87
|
+
redacted_body = SchwabRb::Redactor.redact_response_body(response)
|
88
|
+
SchwabRb::Logger.logger.debug("Response body: #{JSON.pretty_generate(redacted_body)}") if redacted_body
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def req_num
|
93
|
+
@request_counter ||= 0
|
94
|
+
@request_counter += 1
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module SchwabRb
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :logger, :log_file, :log_level, :silence_output
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@logger = nil
|
7
|
+
@log_file = ENV['SCHWAB_LOGFILE']
|
8
|
+
@log_level = ENV.fetch('SCHWAB_LOG_LEVEL', 'WARN').upcase
|
9
|
+
@silence_output = ENV.fetch('SCHWAB_SILENCE_OUTPUT', 'false').downcase == 'true'
|
10
|
+
end
|
11
|
+
|
12
|
+
def has_external_logger?
|
13
|
+
!@logger.nil?
|
14
|
+
end
|
15
|
+
|
16
|
+
def should_create_logger?
|
17
|
+
!has_external_logger? && !@silence_output
|
18
|
+
end
|
19
|
+
|
20
|
+
def effective_log_file
|
21
|
+
@log_file || (ENV['LOGFILE'] if ENV['LOGFILE'] && !ENV['LOGFILE'].empty?)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class << self
|
26
|
+
def configuration
|
27
|
+
@configuration ||= Configuration.new
|
28
|
+
end
|
29
|
+
|
30
|
+
def configure
|
31
|
+
yield(configuration) if block_given?
|
32
|
+
SchwabRb::Logger.reset! if defined?(SchwabRb::Logger)
|
33
|
+
end
|
34
|
+
|
35
|
+
def reset_configuration!
|
36
|
+
@configuration = Configuration.new
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,281 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'instrument'
|
4
|
+
require_relative 'position'
|
5
|
+
|
6
|
+
module SchwabRb
|
7
|
+
module DataObjects
|
8
|
+
class InitialBalances
|
9
|
+
attr_reader :accrued_interest, :cash_balance,
|
10
|
+
:cash_receipts, :long_option_market_value,
|
11
|
+
:liquidation_value, :money_market_fund,
|
12
|
+
:available_funds_non_marginable_trade,
|
13
|
+
:bond_value, :buying_power, :cash_available_for_trading,
|
14
|
+
:day_trading_buying_power, :day_trading_buying_power_call,
|
15
|
+
:day_trading_equity_call, :equity, :equity_percentage,
|
16
|
+
:long_margin_value, :long_stock_value, :maintenance_call, :maintenance_requirement,
|
17
|
+
:margin, :margin_equity, :mutual_fund_value, :reg_t_call, :short_margin_value,
|
18
|
+
:short_option_market_value, :short_stock_value, :total_cash, :is_in_call,
|
19
|
+
:pending_deposits, :margin_balance, :short_balance, :account_value
|
20
|
+
|
21
|
+
class << self
|
22
|
+
def build(data)
|
23
|
+
new(
|
24
|
+
accrued_interest: data[:accruedInterest],
|
25
|
+
cash_balance: data[:cashBalance],
|
26
|
+
cash_receipts: data[:cashReceipts],
|
27
|
+
long_option_market_value: data[:longOptionMarketValue],
|
28
|
+
liquidation_value: data[:liquidationValue],
|
29
|
+
money_market_fund: data[:moneyMarketFund],
|
30
|
+
available_funds_non_marginable_trade: data[:availableFundsNonMarginableTrade],
|
31
|
+
bond_value: data[:bondValue],
|
32
|
+
buying_power: data[:buyingPower],
|
33
|
+
cash_available_for_trading: data[:cashAvailableForTrading],
|
34
|
+
day_trading_buying_power: data[:dayTradingBuyingPower],
|
35
|
+
day_trading_buying_power_call: data[:dayTradingBuyingPowerCall],
|
36
|
+
day_trading_equity_call: data[:dayTradingEquityCall],
|
37
|
+
equity: data[:equity],
|
38
|
+
equity_percentage: data[:equityPercentage],
|
39
|
+
long_margin_value: data[:longMarginValue],
|
40
|
+
long_stock_value: data[:longStockValue],
|
41
|
+
maintenance_call: data[:maintenanceCall],
|
42
|
+
maintenance_requirement: data[:maintenanceRequirement],
|
43
|
+
margin: data[:margin],
|
44
|
+
margin_equity: data[:marginEquity],
|
45
|
+
mutual_fund_value: data[:mutualFundValue],
|
46
|
+
reg_t_call: data[:regTCall],
|
47
|
+
short_margin_value: data[:shortMarginValue],
|
48
|
+
short_option_market_value: data[:shortOptionMarketValue],
|
49
|
+
short_stock_value: data[:shortStockValue],
|
50
|
+
total_cash: data[:totalCash],
|
51
|
+
is_in_call: data[:isInCall],
|
52
|
+
pending_deposits: data[:pendingDeposits],
|
53
|
+
margin_balance: data[:marginBalance],
|
54
|
+
short_balance: data[:shortBalance],
|
55
|
+
account_value: data[:accountValue]
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def initialize(
|
61
|
+
accrued_interest:, cash_balance:, cash_receipts:, long_option_market_value:, liquidation_value:,
|
62
|
+
money_market_fund:, available_funds_non_marginable_trade:, bond_value:, buying_power:,
|
63
|
+
cash_available_for_trading:, day_trading_buying_power:, day_trading_buying_power_call:,
|
64
|
+
day_trading_equity_call:, equity:, equity_percentage:, long_margin_value:, long_stock_value:,
|
65
|
+
maintenance_call:, maintenance_requirement:, margin:, margin_equity:, mutual_fund_value:,
|
66
|
+
reg_t_call:, short_margin_value:, short_option_market_value:, short_stock_value:, total_cash:,
|
67
|
+
is_in_call:, pending_deposits:, margin_balance:, short_balance:, account_value:
|
68
|
+
)
|
69
|
+
@accrued_interest = accrued_interest
|
70
|
+
@cash_balance = cash_balance
|
71
|
+
@cash_receipts = cash_receipts
|
72
|
+
@long_option_market_value = long_option_market_value
|
73
|
+
@liquidation_value = liquidation_value
|
74
|
+
@money_market_fund = money_market_fund
|
75
|
+
@available_funds_non_marginable_trade = available_funds_non_marginable_trade
|
76
|
+
@bond_value = bond_value
|
77
|
+
@buying_power = buying_power
|
78
|
+
@cash_available_for_trading = cash_available_for_trading
|
79
|
+
@day_trading_buying_power = day_trading_buying_power
|
80
|
+
@day_trading_buying_power_call = day_trading_buying_power_call
|
81
|
+
@day_trading_equity_call = day_trading_equity_call
|
82
|
+
@equity = equity
|
83
|
+
@equity_percentage = equity_percentage
|
84
|
+
@long_margin_value = long_margin_value
|
85
|
+
@long_stock_value = long_stock_value
|
86
|
+
@maintenance_call = maintenance_call
|
87
|
+
@maintenance_requirement = maintenance_requirement
|
88
|
+
@margin = margin
|
89
|
+
@margin_equity = margin_equity
|
90
|
+
@mutual_fund_value = mutual_fund_value
|
91
|
+
@reg_t_call = reg_t_call
|
92
|
+
@short_margin_value = short_margin_value
|
93
|
+
@short_option_market_value = short_option_market_value
|
94
|
+
@short_stock_value = short_stock_value
|
95
|
+
@total_cash = total_cash
|
96
|
+
@is_in_call = is_in_call
|
97
|
+
@pending_deposits = pending_deposits
|
98
|
+
@margin_balance = margin_balance
|
99
|
+
@short_balance = short_balance
|
100
|
+
@account_value = account_value
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class CurrentBalances
|
105
|
+
attr_reader :accrued_interest, :cash_balance, :cash_receipts, :long_option_market_value,
|
106
|
+
:liquidation_value, :long_market_value, :money_market_fund, :savings,
|
107
|
+
:short_market_value, :pending_deposits, :mutual_fund_value, :bond_value,
|
108
|
+
:short_option_market_value, :available_funds, :available_funds_non_marginable_trade,
|
109
|
+
:buying_power, :buying_power_non_marginable_trade, :day_trading_buying_power,
|
110
|
+
:equity, :equity_percentage, :long_margin_value, :maintenance_call,
|
111
|
+
:maintenance_requirement, :margin_balance, :reg_t_call, :short_balance,
|
112
|
+
:short_margin_value, :sma
|
113
|
+
|
114
|
+
class << self
|
115
|
+
def build(data)
|
116
|
+
new(
|
117
|
+
accrued_interest: data[:accruedInterest],
|
118
|
+
cash_balance: data[:cashBalance],
|
119
|
+
cash_receipts: data[:cashReceipts],
|
120
|
+
long_option_market_value: data[:longOptionMarketValue],
|
121
|
+
liquidation_value: data[:liquidationValue],
|
122
|
+
long_market_value: data[:longMarketValue],
|
123
|
+
money_market_fund: data[:moneyMarketFund],
|
124
|
+
savings: data[:savings],
|
125
|
+
short_market_value: data[:shortMarketValue],
|
126
|
+
pending_deposits: data[:pendingDeposits],
|
127
|
+
mutual_fund_value: data[:mutualFundValue],
|
128
|
+
bond_value: data[:bondValue],
|
129
|
+
short_option_market_value: data[:shortOptionMarketValue],
|
130
|
+
available_funds: data[:availableFunds],
|
131
|
+
available_funds_non_marginable_trade: data[:availableFundsNonMarginableTrade],
|
132
|
+
buying_power: data[:buyingPower],
|
133
|
+
buying_power_non_marginable_trade: data[:buyingPowerNonMarginableTrade],
|
134
|
+
day_trading_buying_power: data[:dayTradingBuyingPower],
|
135
|
+
equity: data[:equity],
|
136
|
+
equity_percentage: data[:equityPercentage],
|
137
|
+
long_margin_value: data[:longMarginValue],
|
138
|
+
maintenance_call: data[:maintenanceCall],
|
139
|
+
maintenance_requirement: data[:maintenanceRequirement],
|
140
|
+
margin_balance: data[:marginBalance],
|
141
|
+
reg_t_call: data[:regTCall],
|
142
|
+
short_balance: data[:shortBalance],
|
143
|
+
short_margin_value: data[:shortMarginValue],
|
144
|
+
sma: data[:sma]
|
145
|
+
)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def initialize(
|
150
|
+
accrued_interest:, cash_balance:, cash_receipts:, long_option_market_value:, liquidation_value:,
|
151
|
+
long_market_value:, money_market_fund:, savings:, short_market_value:, pending_deposits:,
|
152
|
+
mutual_fund_value:, bond_value:, short_option_market_value:, available_funds:,
|
153
|
+
available_funds_non_marginable_trade:, buying_power:, buying_power_non_marginable_trade:,
|
154
|
+
day_trading_buying_power:, equity:, equity_percentage:, long_margin_value:, maintenance_call:,
|
155
|
+
maintenance_requirement:, margin_balance:, reg_t_call:, short_balance:, short_margin_value:,
|
156
|
+
sma:
|
157
|
+
)
|
158
|
+
@accrued_interest = accrued_interest
|
159
|
+
@cash_balance = cash_balance
|
160
|
+
@cash_receipts = cash_receipts
|
161
|
+
@long_option_market_value = long_option_market_value
|
162
|
+
@liquidation_value = liquidation_value
|
163
|
+
@long_market_value = long_market_value
|
164
|
+
@money_market_fund = money_market_fund
|
165
|
+
@savings = savings
|
166
|
+
@short_market_value = short_market_value
|
167
|
+
@pending_deposits = pending_deposits
|
168
|
+
@mutual_fund_value = mutual_fund_value
|
169
|
+
@bond_value = bond_value
|
170
|
+
@short_option_market_value = short_option_market_value
|
171
|
+
@available_funds = available_funds
|
172
|
+
@available_funds_non_marginable_trade = available_funds_non_marginable_trade
|
173
|
+
@buying_power = buying_power
|
174
|
+
@buying_power_non_marginable_trade = buying_power_non_marginable_trade
|
175
|
+
@day_trading_buying_power = day_trading_buying_power
|
176
|
+
@equity = equity
|
177
|
+
@equity_percentage = equity_percentage
|
178
|
+
@long_margin_value = long_margin_value
|
179
|
+
@maintenance_call = maintenance_call
|
180
|
+
@maintenance_requirement = maintenance_requirement
|
181
|
+
@margin_balance = margin_balance
|
182
|
+
@reg_t_call = reg_t_call
|
183
|
+
@short_balance = short_balance
|
184
|
+
@short_margin_value = short_margin_value
|
185
|
+
@sma = sma
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
class ProjectedBalances
|
190
|
+
attr_reader :available_funds, :available_funds_non_marginable_trade, :buying_power,
|
191
|
+
:day_trading_buying_power, :day_trading_buying_power_call, :maintenance_call,
|
192
|
+
:reg_t_call, :is_in_call, :stock_buying_power
|
193
|
+
|
194
|
+
class << self
|
195
|
+
def build(data)
|
196
|
+
new(
|
197
|
+
available_funds: data[:availableFunds],
|
198
|
+
available_funds_non_marginable_trade: data[:availableFundsNonMarginableTrade],
|
199
|
+
buying_power: data[:buyingPower],
|
200
|
+
day_trading_buying_power: data[:dayTradingBuyingPower],
|
201
|
+
day_trading_buying_power_call: data[:dayTradingBuyingPowerCall],
|
202
|
+
maintenance_call: data[:maintenanceCall],
|
203
|
+
reg_t_call: data[:regTCall],
|
204
|
+
is_in_call: data[:isInCall],
|
205
|
+
stock_buying_power: data[:stockBuyingPower]
|
206
|
+
)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def initialize(
|
211
|
+
available_funds:, available_funds_non_marginable_trade:, buying_power:, day_trading_buying_power:,
|
212
|
+
day_trading_buying_power_call:, maintenance_call:, reg_t_call:, is_in_call:, stock_buying_power:
|
213
|
+
)
|
214
|
+
@available_funds = available_funds
|
215
|
+
@available_funds_non_marginable_trade = available_funds_non_marginable_trade
|
216
|
+
@buying_power = buying_power
|
217
|
+
@day_trading_buying_power = day_trading_buying_power
|
218
|
+
@day_trading_buying_power_call = day_trading_buying_power_call
|
219
|
+
@maintenance_call = maintenance_call
|
220
|
+
@reg_t_call = reg_t_call
|
221
|
+
@is_in_call = is_in_call
|
222
|
+
@stock_buying_power = stock_buying_power
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
class AggregatedBalance
|
227
|
+
class << self
|
228
|
+
def build(data)
|
229
|
+
new(
|
230
|
+
current_liquidation_value: data.fetch(:currentLiquidationValue),
|
231
|
+
liquidation_value: data.fetch(:liquidationValue)
|
232
|
+
)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def initialize(current_liquidation_value:, liquidation_value:)
|
237
|
+
@current_liquidation_value = current_liquidation_value
|
238
|
+
@liquidation_value = liquidation_value
|
239
|
+
end
|
240
|
+
|
241
|
+
attr_reader :current_liquidation_value, :liquidation_value
|
242
|
+
end
|
243
|
+
|
244
|
+
class Account
|
245
|
+
class << self
|
246
|
+
def build(data)
|
247
|
+
data = data[:securitiesAccount] if data.key?(:securitiesAccount)
|
248
|
+
new(
|
249
|
+
type: data.fetch(:type),
|
250
|
+
account_number: data.fetch(:accountNumber),
|
251
|
+
round_trips: data.fetch(:roundTrips),
|
252
|
+
is_day_trader: data.fetch(:isDayTrader),
|
253
|
+
is_closing_only_restricted: data.fetch(:isClosingOnlyRestricted),
|
254
|
+
pfcb_flag: data.fetch(:pfcbFlag),
|
255
|
+
positions: data.fetch(:positions).map { |position| Position.build(position) },
|
256
|
+
initial_balances: InitialBalances.build(data.fetch(:initialBalances)),
|
257
|
+
current_balances: CurrentBalances.build(data.fetch(:currentBalances)),
|
258
|
+
projected_balances: ProjectedBalances.build(data.fetch(:projectedBalances))
|
259
|
+
)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def initialize(type:, account_number:, round_trips:, is_day_trader:, is_closing_only_restricted:,
|
264
|
+
pfcb_flag:, initial_balances:, current_balances:, projected_balances:, positions: [])
|
265
|
+
@type = type
|
266
|
+
@account_number = account_number
|
267
|
+
@round_trips = round_trips
|
268
|
+
@is_day_trader = is_day_trader
|
269
|
+
@is_closing_only_restricted = is_closing_only_restricted
|
270
|
+
@pfcb_flag = pfcb_flag
|
271
|
+
@positions = positions
|
272
|
+
@initial_balances = initial_balances
|
273
|
+
@current_balances = current_balances
|
274
|
+
@projected_balances = projected_balances
|
275
|
+
end
|
276
|
+
|
277
|
+
attr_reader :type, :account_number, :round_trips, :is_day_trader, :is_closing_only_restricted,
|
278
|
+
:pfcb_flag, :positions, :initial_balances, :current_balances, :projected_balances
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SchwabRb
|
4
|
+
module DataObjects
|
5
|
+
class AccountNumbers
|
6
|
+
attr_reader :accounts
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def build(data)
|
10
|
+
new(data)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(data)
|
15
|
+
@accounts = data.map { |account_data| AccountNumber.new(account_data) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_h
|
19
|
+
@accounts.map(&:to_h)
|
20
|
+
end
|
21
|
+
|
22
|
+
def find_by_account_number(account_number)
|
23
|
+
@accounts.find { |account| account.account_number == account_number }
|
24
|
+
end
|
25
|
+
|
26
|
+
def find_hash_value(account_number)
|
27
|
+
account = find_by_account_number(account_number)
|
28
|
+
account&.hash_value
|
29
|
+
end
|
30
|
+
|
31
|
+
def account_numbers
|
32
|
+
@accounts.map(&:account_number)
|
33
|
+
end
|
34
|
+
|
35
|
+
def hash_values
|
36
|
+
@accounts.map(&:hash_value)
|
37
|
+
end
|
38
|
+
|
39
|
+
def size
|
40
|
+
@accounts.size
|
41
|
+
end
|
42
|
+
|
43
|
+
def empty?
|
44
|
+
@accounts.empty?
|
45
|
+
end
|
46
|
+
|
47
|
+
def each(&block)
|
48
|
+
@accounts.each(&block)
|
49
|
+
end
|
50
|
+
|
51
|
+
class AccountNumber
|
52
|
+
attr_reader :account_number, :hash_value
|
53
|
+
|
54
|
+
def initialize(data)
|
55
|
+
@account_number = data[:accountNumber]
|
56
|
+
@hash_value = data[:hashValue]
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_h
|
60
|
+
{
|
61
|
+
accountNumber: @account_number,
|
62
|
+
hashValue: @hash_value
|
63
|
+
}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SchwabRb
|
4
|
+
module DataObjects
|
5
|
+
class Asset
|
6
|
+
attr_reader :asset_type, :status, :symbol, :instrument_id, :closing_price, :type, :description,
|
7
|
+
:active_contract, :expiration_date, :last_trading_date, :multiplier, :future_type
|
8
|
+
|
9
|
+
def self.build(data)
|
10
|
+
new(
|
11
|
+
asset_type: data.fetch(:assetType, nil),
|
12
|
+
status: data.fetch(:status, nil),
|
13
|
+
symbol: data.fetch(:symbol, nil),
|
14
|
+
instrument_id: data.fetch(:instrumentId, nil),
|
15
|
+
closing_price: data.fetch(:closingPrice, nil),
|
16
|
+
type: data.fetch(:type, nil),
|
17
|
+
description: data.fetch(:description, nil),
|
18
|
+
active_contract: data.fetch(:activeContract, nil),
|
19
|
+
expiration_date: data.fetch(:expirationDate, nil),
|
20
|
+
last_trading_date: data.fetch(:lastTradingDate, nil),
|
21
|
+
multiplier: data.fetch(:multiplier, nil),
|
22
|
+
future_type: data.fetch(:futureType, nil)
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(
|
27
|
+
asset_type: nil, status: nil, symbol: nil, instrument_id: nil, closing_price: nil, type: nil, description: nil, active_contract: nil, expiration_date: nil, last_trading_date: nil, multiplier: nil, future_type: nil
|
28
|
+
)
|
29
|
+
@asset_type = asset_type
|
30
|
+
@status = status
|
31
|
+
@symbol = symbol
|
32
|
+
@instrument_id = instrument_id
|
33
|
+
@closing_price = closing_price
|
34
|
+
@type = type
|
35
|
+
@description = description
|
36
|
+
@active_contract = active_contract
|
37
|
+
@expiration_date = expiration_date
|
38
|
+
@last_trading_date = last_trading_date
|
39
|
+
@multiplier = multiplier
|
40
|
+
@future_type = future_type
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_h
|
44
|
+
{
|
45
|
+
assetType: @asset_type,
|
46
|
+
status: @status,
|
47
|
+
symbol: @symbol,
|
48
|
+
instrumentId: @instrument_id,
|
49
|
+
closingPrice: @closing_price,
|
50
|
+
type: @type,
|
51
|
+
description: @description,
|
52
|
+
activeContract: @active_contract,
|
53
|
+
expirationDate: @expiration_date,
|
54
|
+
lastTradingDate: @last_trading_date,
|
55
|
+
multiplier: @multiplier,
|
56
|
+
futureType: @future_type
|
57
|
+
}.compact
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class OptionDeliverable
|
62
|
+
attr_reader :root_symbol, :symbol, :deliverable_units, :deliverable_number, :strike_percent, :deliverable
|
63
|
+
|
64
|
+
def self.build(data)
|
65
|
+
new(
|
66
|
+
root_symbol: data.fetch(:rootSymbol, nil),
|
67
|
+
symbol: data.fetch(:symbol, nil),
|
68
|
+
strike_percent: data.fetch(:strikePercent, nil),
|
69
|
+
deliverable_number: data.fetch(:deliverableNumber, nil),
|
70
|
+
deliverable_units: data.fetch(:deliverableUnits),
|
71
|
+
deliverable: data.fetch(:deliverable, nil).then { |d| d.nil? ? nil : Instrument.build(d) }
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
def initialize(root_symbol:, symbol:, deliverable_units:, deliverable_number:, strike_percent:, deliverable:)
|
76
|
+
@root_symbol = root_symbol
|
77
|
+
@symbol = symbol
|
78
|
+
@deliverable_units = deliverable_units
|
79
|
+
@deliverable_number = deliverable_number
|
80
|
+
@strike_percent = strike_percent
|
81
|
+
@deliverable = deliverable
|
82
|
+
end
|
83
|
+
|
84
|
+
def to_h
|
85
|
+
{
|
86
|
+
rootSymbol: @root_symbol,
|
87
|
+
symbol: @symbol,
|
88
|
+
strikePercent: @strike_percent,
|
89
|
+
deliverableNumber: @deliverable_number,
|
90
|
+
deliverableUnits: @deliverable_units,
|
91
|
+
deliverable: @deliverable&.to_h
|
92
|
+
}.compact
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class Instrument
|
97
|
+
attr_reader :asset_type, :cusip, :symbol, :description, :net_change, :type, :put_call,
|
98
|
+
:underlying_symbol, :status, :instrument_id, :closing_price, :option_deliverables
|
99
|
+
|
100
|
+
def self.build(data)
|
101
|
+
new(
|
102
|
+
asset_type: data.fetch(:assetType),
|
103
|
+
symbol: data.fetch(:symbol, nil),
|
104
|
+
description: data.fetch(:description, nil),
|
105
|
+
cusip: data.fetch(:cusip, nil),
|
106
|
+
net_change: data.fetch(:netChange, nil),
|
107
|
+
type: data.fetch(:type, nil),
|
108
|
+
put_call: data.fetch(:putCall, nil),
|
109
|
+
underlying_symbol: data.fetch(:underlyingSymbol, nil),
|
110
|
+
status: data.fetch(:status, nil),
|
111
|
+
instrument_id: data.fetch(:instrumentId, nil),
|
112
|
+
closing_price: data.fetch(:closingPrice, nil),
|
113
|
+
option_deliverables: data.fetch(:optionDeliverables, []).map { |d| OptionDeliverable.build(d) }
|
114
|
+
)
|
115
|
+
end
|
116
|
+
|
117
|
+
def initialize(
|
118
|
+
symbol:, description:, asset_type: nil, cusip: nil, net_change: nil, type: nil, put_call: nil, underlying_symbol: nil, status: nil, instrument_id: nil, closing_price: nil, option_deliverables: []
|
119
|
+
)
|
120
|
+
@asset_type = asset_type
|
121
|
+
@cusip = cusip
|
122
|
+
@symbol = symbol
|
123
|
+
@description = description
|
124
|
+
@net_change = net_change
|
125
|
+
@type = type
|
126
|
+
@put_call = put_call
|
127
|
+
@underlying_symbol = underlying_symbol
|
128
|
+
@status = status
|
129
|
+
@instrument_id = instrument_id
|
130
|
+
@closing_price = closing_price
|
131
|
+
@option_deliverables = option_deliverables
|
132
|
+
end
|
133
|
+
|
134
|
+
def option?
|
135
|
+
asset_type == 'OPTION'
|
136
|
+
end
|
137
|
+
|
138
|
+
def to_h
|
139
|
+
{
|
140
|
+
assetType: @asset_type,
|
141
|
+
symbol: @symbol,
|
142
|
+
description: @description,
|
143
|
+
cusip: @cusip,
|
144
|
+
netChange: @net_change,
|
145
|
+
type: @type,
|
146
|
+
putCall: @put_call,
|
147
|
+
underlyingSymbol: @underlying_symbol,
|
148
|
+
status: @status,
|
149
|
+
instrumentId: @instrument_id,
|
150
|
+
closingPrice: @closing_price,
|
151
|
+
optionDeliverables: @option_deliverables.map(&:to_h)
|
152
|
+
}.compact
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|