schwab_rb 0.3.2 → 0.3.3
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/.rspec_status +193 -193
- data/.rubocop.yml +2 -0
- data/examples/fetch_account_numbers.rb +1 -1
- data/examples/fetch_user_preferences.rb +1 -1
- data/lib/schwab_rb/auth/auth_context.rb +19 -15
- data/lib/schwab_rb/auth/init_client_easy.rb +36 -32
- data/lib/schwab_rb/auth/init_client_login.rb +165 -161
- data/lib/schwab_rb/auth/init_client_token_file.rb +27 -23
- data/lib/schwab_rb/auth/login_flow_server.rb +39 -36
- data/lib/schwab_rb/auth/token.rb +25 -21
- data/lib/schwab_rb/auth/token_manager.rb +88 -84
- data/lib/schwab_rb/clients/async_client.rb +2 -0
- data/lib/schwab_rb/clients/base_client.rb +2 -0
- data/lib/schwab_rb/configuration.rb +2 -0
- data/lib/schwab_rb/constants.rb +6 -4
- data/lib/schwab_rb/data_objects/account_numbers.rb +1 -0
- data/lib/schwab_rb/data_objects/market_hours.rb +1 -1
- data/lib/schwab_rb/data_objects/market_movers.rb +1 -0
- data/lib/schwab_rb/data_objects/option_expiration_chain.rb +1 -1
- data/lib/schwab_rb/data_objects/order_preview.rb +21 -49
- data/lib/schwab_rb/data_objects/price_history.rb +1 -1
- data/lib/schwab_rb/orders/builder.rb +162 -158
- data/lib/schwab_rb/orders/destination.rb +2 -0
- data/lib/schwab_rb/orders/duration.rb +14 -8
- data/lib/schwab_rb/orders/equity_instructions.rb +2 -0
- data/lib/schwab_rb/orders/errors.rb +2 -0
- data/lib/schwab_rb/orders/instruments.rb +2 -0
- data/lib/schwab_rb/orders/option_instructions.rb +2 -0
- data/lib/schwab_rb/orders/order.rb +2 -0
- data/lib/schwab_rb/orders/price_link_basis.rb +2 -0
- data/lib/schwab_rb/orders/price_link_type.rb +2 -0
- data/lib/schwab_rb/orders/session.rb +16 -10
- data/lib/schwab_rb/orders/special_instruction.rb +2 -0
- data/lib/schwab_rb/orders/stop_price_link_basis.rb +2 -0
- data/lib/schwab_rb/orders/stop_price_link_type.rb +2 -0
- data/lib/schwab_rb/orders/stop_type.rb +2 -0
- data/lib/schwab_rb/orders/tax_lot_method.rb +2 -0
- data/lib/schwab_rb/utils/enum_enforcer.rb +3 -4
- data/lib/schwab_rb/utils/logger.rb +3 -1
- data/lib/schwab_rb/utils/redactor.rb +3 -5
- data/lib/schwab_rb/version.rb +1 -1
- metadata +2 -2
@@ -1,201 +1,205 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative "../utils/enum_enforcer"
|
2
4
|
|
3
|
-
module SchwabRb
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
5
|
+
module SchwabRb
|
6
|
+
module Orders
|
7
|
+
class Builder
|
8
|
+
# Helper class to create arbitrarily complex orders. Note this class simply
|
9
|
+
# implements the order schema defined in the `documentation
|
10
|
+
# <https://developer.schwabmeritrade.com/account-access/apis/post/accounts/
|
11
|
+
# %7BaccountId%7D/orders-0>`__, with no attempts to validate the result.
|
12
|
+
# Orders created using this class may be rejected or may never fill. Use at
|
13
|
+
# your own risk.
|
14
|
+
|
15
|
+
include EnumEnforcer
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def build(obj)
|
19
|
+
case obj
|
20
|
+
when String, Integer, Float
|
21
|
+
obj
|
22
|
+
when Hash
|
23
|
+
obj.each_with_object({}) do |(key, val), acc|
|
24
|
+
acc[camel_case(key)] = build(val)
|
25
|
+
end
|
26
|
+
when Array
|
27
|
+
obj.map { |i| build(i) }
|
28
|
+
else
|
29
|
+
ret = {}
|
30
|
+
obj.instance_variables.each do |var|
|
31
|
+
value = obj.instance_variable_get(var)
|
32
|
+
next if value.nil?
|
33
|
+
|
34
|
+
name = var.to_s[1..]
|
35
|
+
ret[camel_case(name)] = build(value)
|
36
|
+
end
|
37
|
+
ret
|
22
38
|
end
|
23
|
-
|
24
|
-
obj.map { |i| build(i) }
|
25
|
-
else
|
26
|
-
ret = {}
|
27
|
-
obj.instance_variables.each do |var|
|
28
|
-
value = obj.instance_variable_get(var)
|
29
|
-
next if value.nil?
|
39
|
+
end
|
30
40
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
ret
|
41
|
+
def camel_case(snake_str)
|
42
|
+
camel_case_str = snake_str.split("_").map(&:capitalize).join
|
43
|
+
camel_case_str[0].downcase + camel_case_str[1..]
|
35
44
|
end
|
36
45
|
end
|
37
46
|
|
38
|
-
def
|
39
|
-
|
40
|
-
|
47
|
+
def initialize(enforce_enums: true)
|
48
|
+
@session = nil
|
49
|
+
@account_number = nil
|
50
|
+
@duration = nil
|
51
|
+
@order_type = nil
|
52
|
+
@complex_order_strategy_type = nil
|
53
|
+
@quantity = nil
|
54
|
+
@destination_link_name = nil
|
55
|
+
@stop_price = nil
|
56
|
+
@stop_price_link_basis = nil
|
57
|
+
@stop_price_link_type = nil
|
58
|
+
@stop_price_offset = nil
|
59
|
+
@stop_type = nil
|
60
|
+
@price_link_basis = nil
|
61
|
+
@price_link_type = nil
|
62
|
+
@price = nil
|
63
|
+
@order_leg_collection = nil
|
64
|
+
@activation_price = nil
|
65
|
+
@special_instruction = nil
|
66
|
+
@order_strategy_type = nil
|
67
|
+
@child_order_strategies = nil
|
41
68
|
end
|
42
|
-
end
|
43
69
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
@duration = nil
|
48
|
-
@order_type = nil
|
49
|
-
@complex_order_strategy_type = nil
|
50
|
-
@quantity = nil
|
51
|
-
@destination_link_name = nil
|
52
|
-
@stop_price = nil
|
53
|
-
@stop_price_link_basis = nil
|
54
|
-
@stop_price_link_type = nil
|
55
|
-
@stop_price_offset = nil
|
56
|
-
@stop_type = nil
|
57
|
-
@price_link_basis = nil
|
58
|
-
@price_link_type = nil
|
59
|
-
@price = nil
|
60
|
-
@order_leg_collection = nil
|
61
|
-
@activation_price = nil
|
62
|
-
@special_instruction = nil
|
63
|
-
@order_strategy_type = nil
|
64
|
-
@child_order_strategies = nil
|
65
|
-
end
|
70
|
+
def set_session(session)
|
71
|
+
@session = convert_enum(session, SchwabRb::Orders::Session)
|
72
|
+
end
|
66
73
|
|
67
|
-
|
68
|
-
|
69
|
-
|
74
|
+
def clear_session
|
75
|
+
@session = nil
|
76
|
+
end
|
70
77
|
|
71
|
-
|
72
|
-
|
73
|
-
|
78
|
+
def set_account_number(account_number)
|
79
|
+
@account_number = account_number
|
80
|
+
end
|
74
81
|
|
75
|
-
|
76
|
-
|
77
|
-
|
82
|
+
def clear_account_number
|
83
|
+
@account_number = nil
|
84
|
+
end
|
78
85
|
|
79
|
-
|
80
|
-
|
81
|
-
|
86
|
+
def set_duration(duration)
|
87
|
+
@duration = convert_enum(duration, SchwabRb::Orders::Duration)
|
88
|
+
end
|
82
89
|
|
83
|
-
|
84
|
-
|
85
|
-
|
90
|
+
def clear_duration
|
91
|
+
@duration = nil
|
92
|
+
end
|
86
93
|
|
87
|
-
|
88
|
-
|
89
|
-
|
94
|
+
def set_order_type(order_type)
|
95
|
+
@order_type = convert_enum(order_type, SchwabRb::Order::Types)
|
96
|
+
end
|
90
97
|
|
91
|
-
|
92
|
-
|
93
|
-
|
98
|
+
def clear_order_type
|
99
|
+
@order_type = nil
|
100
|
+
end
|
94
101
|
|
95
|
-
|
96
|
-
|
97
|
-
end
|
102
|
+
def set_quantity(quantity)
|
103
|
+
raise "quantity must be positive" if quantity <= 0
|
98
104
|
|
99
|
-
|
100
|
-
|
105
|
+
@quantity = quantity
|
106
|
+
end
|
101
107
|
|
102
|
-
|
103
|
-
|
108
|
+
def clear_quantity
|
109
|
+
@quantity = nil
|
110
|
+
end
|
104
111
|
|
105
|
-
|
106
|
-
|
107
|
-
|
112
|
+
def set_price(price)
|
113
|
+
@price = price
|
114
|
+
end
|
108
115
|
|
109
|
-
|
110
|
-
|
111
|
-
|
116
|
+
def clear_price
|
117
|
+
@price = nil
|
118
|
+
end
|
112
119
|
|
113
|
-
|
114
|
-
|
115
|
-
|
120
|
+
def set_stop_price(stop_price)
|
121
|
+
@stop_price = stop_price.is_a?(String) ? stop_price : truncate_float(stop_price)
|
122
|
+
end
|
116
123
|
|
117
|
-
|
118
|
-
|
119
|
-
|
124
|
+
def copy_stop_price(stop_price)
|
125
|
+
@stop_price = stop_price
|
126
|
+
end
|
120
127
|
|
121
|
-
|
122
|
-
|
123
|
-
|
128
|
+
def clear_stop_price
|
129
|
+
@stop_price = nil
|
130
|
+
end
|
124
131
|
|
125
|
-
|
126
|
-
|
127
|
-
|
132
|
+
def set_order_strategy_type(order_strategy_type = "SINGLE")
|
133
|
+
@order_strategy_type = order_strategy_type
|
134
|
+
end
|
128
135
|
|
129
|
-
|
130
|
-
|
131
|
-
|
136
|
+
def clear_order_strategy_type
|
137
|
+
@order_strategy_type = nil
|
138
|
+
end
|
132
139
|
|
133
|
-
|
134
|
-
|
135
|
-
|
140
|
+
def set_complex_order_strategy_type(complex_order_strategy_type)
|
141
|
+
@complex_order_strategy_type = convert_enum(
|
142
|
+
complex_order_strategy_type,
|
143
|
+
SchwabRb::Order::ComplexOrderStrategyTypes
|
144
|
+
)
|
145
|
+
end
|
136
146
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
SchwabRb::Order::ComplexOrderStrategyTypes
|
141
|
-
)
|
142
|
-
end
|
147
|
+
def clear_complex_order_strategy_type
|
148
|
+
@complex_order_strategy_type = nil
|
149
|
+
end
|
143
150
|
|
144
|
-
|
145
|
-
|
146
|
-
|
151
|
+
def add_child_order_strategy(child_order_strategy)
|
152
|
+
raise "child order must be OrderBuilder or Hash" unless [Builder, Hash].any? do |type|
|
153
|
+
child_order_strategy.is_a? type
|
154
|
+
end
|
147
155
|
|
148
|
-
|
149
|
-
|
150
|
-
child_order_strategy.is_a? type
|
156
|
+
@child_order_strategies ||= []
|
157
|
+
@child_order_strategies << child_order_strategy
|
151
158
|
end
|
152
159
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
def clear_child_order_strategies
|
158
|
-
@child_order_strategies = nil
|
159
|
-
end
|
160
|
+
def clear_child_order_strategies
|
161
|
+
@child_order_strategies = nil
|
162
|
+
end
|
160
163
|
|
161
|
-
|
162
|
-
|
164
|
+
def add_option_leg(instruction, symbol, quantity)
|
165
|
+
raise "quantity must be positive" if quantity <= 0
|
163
166
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
167
|
+
@order_leg_collection ||= []
|
168
|
+
@order_leg_collection << {
|
169
|
+
"instruction" => convert_enum(instruction, SchwabRb::Orders::OptionInstructions),
|
170
|
+
"instrument" => SchwabRb::Orders::OptionInstrument.new(symbol),
|
171
|
+
"quantity" => quantity
|
172
|
+
}
|
173
|
+
end
|
171
174
|
|
172
|
-
|
173
|
-
|
175
|
+
def add_equity_leg(instruction, symbol, quantity)
|
176
|
+
raise "quantity must be positive" if quantity <= 0
|
174
177
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
178
|
+
@order_leg_collection ||= []
|
179
|
+
@order_leg_collection << {
|
180
|
+
"instruction" => convert_enum(instruction, SchwabRb::Orders::EquityInstructions),
|
181
|
+
"instrument" => SchwabRb::Orders::EquityInstrument.new(symbol),
|
182
|
+
"quantity" => quantity
|
183
|
+
}
|
184
|
+
end
|
182
185
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
186
|
+
def clear_order_legs
|
187
|
+
@order_leg_collection = nil
|
188
|
+
self
|
189
|
+
end
|
187
190
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
+
def build
|
192
|
+
Builder.build(self)
|
193
|
+
end
|
191
194
|
|
192
|
-
|
195
|
+
private
|
193
196
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
197
|
+
def truncate_float(flt)
|
198
|
+
if flt.abs < 1 && flt != 0.0
|
199
|
+
format("%.4f", (flt * 10_000).to_i / 10_000.0)
|
200
|
+
else
|
201
|
+
format("%.2f", (flt * 100).to_i / 100.0)
|
202
|
+
end
|
199
203
|
end
|
200
204
|
end
|
201
205
|
end
|
@@ -1,9 +1,15 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SchwabRb
|
4
|
+
module Orders
|
5
|
+
module Duration
|
6
|
+
DAY = "DAY"
|
7
|
+
GOOD_TILL_CANCEL = "GOOD_TILL_CANCEL"
|
8
|
+
FILL_OR_KILL = "FILL_OR_KILL"
|
9
|
+
IMMEDIATE_OR_CANCEL = "IMMEDIATE_OR_CANCEL"
|
10
|
+
END_OF_WEEK = "END_OF_WEEK"
|
11
|
+
END_OF_MONTH = "END_OF_MONTH"
|
12
|
+
NEXT_END_OF_MONTH = "NEXT_END_OF_MONTH"
|
13
|
+
end
|
14
|
+
end
|
9
15
|
end
|
@@ -1,14 +1,20 @@
|
|
1
|
-
|
2
|
-
# Normal market hours, from 9:30am to 4:00pm Eastern.
|
3
|
-
NORMAL = "NORMAL"
|
1
|
+
# frozen_string_literal: true
|
4
2
|
|
5
|
-
|
6
|
-
|
3
|
+
module SchwabRb
|
4
|
+
module Orders
|
5
|
+
module Session
|
6
|
+
# Normal market hours, from 9:30am to 4:00pm Eastern.
|
7
|
+
NORMAL = "NORMAL"
|
7
8
|
|
8
|
-
|
9
|
-
|
9
|
+
# Premarket session, from 8:00am to 9:30am Eastern.
|
10
|
+
AM = "AM"
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
12
|
+
# After-market session, from 4:00pm to 8:00pm Eastern.
|
13
|
+
PM = "PM"
|
14
|
+
|
15
|
+
# Orders are active during all trading sessions except the overnight
|
16
|
+
# session. This is the union of ``NORMAL``, ``AM``, and ``PM``.
|
17
|
+
SEAMLESS = "SEAMLESS"
|
18
|
+
end
|
19
|
+
end
|
14
20
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module EnumEnforcer
|
2
4
|
def enforce_enums?
|
3
5
|
@enforce_enums ||= false
|
@@ -17,10 +19,7 @@ module EnumEnforcer
|
|
17
19
|
end
|
18
20
|
|
19
21
|
if possible_members.any?
|
20
|
-
possible_members_message = "Did you mean "
|
21
|
-
possible_members[0..-2].join(", ") +
|
22
|
-
(possible_members.size > 1 ? " or " : "") +
|
23
|
-
possible_members[-1].to_s + "? "
|
22
|
+
possible_members_message = "Did you mean #{possible_members[0..-2].join(', ')}#{possible_members.size > 1 ? ' or ' : ''}#{possible_members[-1]}? "
|
24
23
|
end
|
25
24
|
end
|
26
25
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "logger"
|
2
4
|
require "fileutils"
|
3
5
|
|
@@ -25,7 +27,7 @@ module SchwabRb
|
|
25
27
|
return null_logger if config.silence_output
|
26
28
|
return null_logger unless config.should_create_logger?
|
27
29
|
|
28
|
-
log_destination = config.effective_log_file ||
|
30
|
+
log_destination = config.effective_log_file || $stdout
|
29
31
|
|
30
32
|
return null_logger if [:null, "/dev/null"].include?(log_destination)
|
31
33
|
|
@@ -4,9 +4,8 @@ require "json"
|
|
4
4
|
|
5
5
|
module SchwabRb
|
6
6
|
class Redactor
|
7
|
-
|
8
|
-
|
9
|
-
ACCOUNT_HASH_PATTERN = /\b[A-Z0-9]{32}\b/
|
7
|
+
ACCOUNT_NUMBER_PATTERN = /\b\d{8,12}\b/.freeze
|
8
|
+
ACCOUNT_HASH_PATTERN = /\b[A-Z0-9]{32}\b/.freeze
|
10
9
|
|
11
10
|
# JSON keys that commonly contain sensitive account information
|
12
11
|
SENSITIVE_KEYS = %w[
|
@@ -45,7 +44,7 @@ module SchwabRb
|
|
45
44
|
end
|
46
45
|
|
47
46
|
def self.redact_response_body(response)
|
48
|
-
return unless response
|
47
|
+
return unless response.respond_to?(:body)
|
49
48
|
|
50
49
|
body = response.body
|
51
50
|
return unless body
|
@@ -55,7 +54,6 @@ module SchwabRb
|
|
55
54
|
parsed = JSON.parse(body)
|
56
55
|
redact_data(parsed)
|
57
56
|
elsif body.respond_to?(:read)
|
58
|
-
# Handle IO-like objects
|
59
57
|
content = body.read
|
60
58
|
body.rewind if body.respond_to?(:rewind)
|
61
59
|
parsed = JSON.parse(content)
|
data/lib/schwab_rb/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: schwab_rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joseph Platta
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-07-
|
10
|
+
date: 2025-07-28 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: async
|