schwab_rb 0.4.0 → 0.6.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 +4 -4
- data/CHANGELOG.md +23 -0
- data/doc/PLACE_ORDER_SAMPLES.md +1 -1
- data/examples/fetch_account_numbers.rb +19 -16
- data/examples/fetch_user_preferences.rb +19 -16
- data/examples/place_oco_order.rb +5 -10
- data/lib/schwab_rb/account_hash_manager.rb +1 -1
- data/lib/schwab_rb/clients/base_client.rb +73 -73
- data/lib/schwab_rb/data_objects/option.rb +6 -4
- data/lib/schwab_rb/data_objects/order_preview.rb +43 -39
- data/lib/schwab_rb/orders/order_factory.rb +14 -0
- data/lib/schwab_rb/orders/vertical_roll_order.rb +71 -0
- data/lib/schwab_rb/version.rb +1 -1
- data/lib/schwab_rb.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 123cd13fd78778da2f5f2d08f9dc8f7594a07b8fcd93659bd49df3b1cd28ddb7
|
|
4
|
+
data.tar.gz: 0760e50de57a1a2d207cf1cc4744441f4a08fc2fc983859317feda22df701b15
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bc5b094add1c5002f1a799ef41ca023b18e3857c39476c91282174640728fd7be0fe8f7de59a265cdf7c86a893a0c04dce91f7fb7551ee2818a5e371f9b1f792
|
|
7
|
+
data.tar.gz: f29fc656583df33e6ff787715713075fb0ffc094ca5ae81915fb37fca4c0dbbefa362488a687f6dcc9dd957dd65c65917b66ba98fc4401467139adf68ea03f4a
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.6.0] - 2025-12-11
|
|
4
|
+
|
|
5
|
+
### Breaking Changes
|
|
6
|
+
- **OrderPreview**: Replaced `projected_commission` with `commission_and_fee` structure to match current Schwab API
|
|
7
|
+
- **OrderPreview**: Commission/fee data now uses nested `commissionLegs`/`feeLegs` instead of scalar values
|
|
8
|
+
- **OrderValidationResult**: Changed from `warningMessage` string to `warns` array of objects with `activity_message` and `original_severity`
|
|
9
|
+
- **OrderValidationResult**: Updated `Reject` structure to match `Warn` structure
|
|
10
|
+
- **BaseClient**: Methods with `return_data_objects: false` now return parsed Hash/Array instead of raw `OAuth2::Response` objects
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Removed redundant `strike_price` attribute from option instruments
|
|
14
|
+
- Removed account numbers from log messages for improved security
|
|
15
|
+
|
|
16
|
+
## [0.5.0] - 2025-11-03
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- Vertical Roll Orders: Support for vertical roll orders that close an existing vertical spread and open a new one in a single order
|
|
20
|
+
- `VerticalRollOrder` class with 4-leg order structure for rolling positions
|
|
21
|
+
- Comprehensive tests for vertical roll orders including credit rolls, debit rolls, and stop limit orders
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- Fixed typo in `OrderFactory` (SchwabRbL → SchwabRb)
|
|
25
|
+
|
|
3
26
|
## [0.4.0] - 2025-10-19
|
|
4
27
|
|
|
5
28
|
### Added
|
data/doc/PLACE_ORDER_SAMPLES.md
CHANGED
|
@@ -162,7 +162,7 @@ Buy 10 shares of XYZ at a Limit price of $34.97 good for the Day. If filled, imm
|
|
|
162
162
|
|
|
163
163
|
## Conditional Order: One Cancels Another
|
|
164
164
|
|
|
165
|
-
|
|
165
|
+
Sell 2 shares of XYZ at a Limit price of $45.97 and Sell 2 shares of XYZ with a Stop Limit order where the stop price is $37.03 and limit is $37.00. Both orders are sent at the same time. If one order fills, the other order is immediately cancelled. Both orders are good for the Day. Also known as an OCO order.
|
|
166
166
|
|
|
167
167
|
```json
|
|
168
168
|
{
|
|
@@ -4,19 +4,19 @@
|
|
|
4
4
|
# Script to fetch account numbers data and save as fixture
|
|
5
5
|
# Usage: ruby examples/fetch_account_numbers.rb
|
|
6
6
|
|
|
7
|
-
require_relative
|
|
8
|
-
require
|
|
9
|
-
require
|
|
10
|
-
require
|
|
7
|
+
require_relative '../lib/schwab_rb'
|
|
8
|
+
require 'dotenv'
|
|
9
|
+
require 'json'
|
|
10
|
+
require 'fileutils'
|
|
11
11
|
|
|
12
12
|
Dotenv.load
|
|
13
13
|
|
|
14
14
|
def create_client
|
|
15
|
-
token_path = ENV[
|
|
15
|
+
token_path = ENV['TOKEN_PATH'] || 'schwab_token.json'
|
|
16
16
|
SchwabRb::Auth.init_client_easy(
|
|
17
|
-
ENV
|
|
18
|
-
ENV
|
|
19
|
-
ENV
|
|
17
|
+
ENV['SCHWAB_API_KEY'],
|
|
18
|
+
ENV['SCHWAB_APP_SECRET'],
|
|
19
|
+
ENV['APP_CALLBACK_URL'],
|
|
20
20
|
token_path
|
|
21
21
|
)
|
|
22
22
|
end
|
|
@@ -24,23 +24,26 @@ end
|
|
|
24
24
|
def fetch_account_numbers
|
|
25
25
|
client = create_client
|
|
26
26
|
puts "Fetching account numbers..."
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
response = client.get_account_numbers
|
|
29
29
|
parsed_data = JSON.parse(response.body, symbolize_names: true)
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
# Create fixtures directory if it doesn't exist
|
|
32
|
-
fixtures_dir = File.join(__dir__,
|
|
32
|
+
fixtures_dir = File.join(__dir__, '..', 'spec', 'fixtures')
|
|
33
33
|
FileUtils.mkdir_p(fixtures_dir)
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
# Save the raw response
|
|
36
|
-
fixture_file = File.join(fixtures_dir,
|
|
36
|
+
fixture_file = File.join(fixtures_dir, 'account_numbers.json')
|
|
37
37
|
File.write(fixture_file, JSON.pretty_generate(parsed_data))
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
puts "Account numbers data saved to: #{fixture_file}"
|
|
40
40
|
puts "Sample data: #{parsed_data.first(2)}"
|
|
41
|
-
|
|
41
|
+
|
|
42
|
+
rescue => e
|
|
42
43
|
puts "Error fetching account numbers: #{e.message}"
|
|
43
44
|
puts e.backtrace.first(3)
|
|
44
45
|
end
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
if __FILE__ == $0
|
|
48
|
+
fetch_account_numbers
|
|
49
|
+
end
|
|
@@ -4,19 +4,19 @@
|
|
|
4
4
|
# Script to fetch user preferences data and save as fixture
|
|
5
5
|
# Usage: ruby examples/fetch_user_preferences.rb
|
|
6
6
|
|
|
7
|
-
require_relative
|
|
8
|
-
require
|
|
9
|
-
require
|
|
10
|
-
require
|
|
7
|
+
require_relative '../lib/schwab_rb'
|
|
8
|
+
require 'dotenv'
|
|
9
|
+
require 'json'
|
|
10
|
+
require 'fileutils'
|
|
11
11
|
|
|
12
12
|
Dotenv.load
|
|
13
13
|
|
|
14
14
|
def create_client
|
|
15
|
-
token_path = ENV[
|
|
15
|
+
token_path = ENV['TOKEN_PATH'] || 'schwab_token.json'
|
|
16
16
|
SchwabRb::Auth.init_client_easy(
|
|
17
|
-
ENV
|
|
18
|
-
ENV
|
|
19
|
-
ENV
|
|
17
|
+
ENV['SCHWAB_API_KEY'],
|
|
18
|
+
ENV['SCHWAB_APP_SECRET'],
|
|
19
|
+
ENV['APP_CALLBACK_URL'],
|
|
20
20
|
token_path
|
|
21
21
|
)
|
|
22
22
|
end
|
|
@@ -24,23 +24,26 @@ end
|
|
|
24
24
|
def fetch_user_preferences
|
|
25
25
|
client = create_client
|
|
26
26
|
puts "Fetching user preferences..."
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
response = client.get_user_preferences
|
|
29
29
|
parsed_data = JSON.parse(response.body, symbolize_names: true)
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
# Create fixtures directory if it doesn't exist
|
|
32
|
-
fixtures_dir = File.join(__dir__,
|
|
32
|
+
fixtures_dir = File.join(__dir__, '..', 'spec', 'fixtures')
|
|
33
33
|
FileUtils.mkdir_p(fixtures_dir)
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
# Save the raw response
|
|
36
|
-
fixture_file = File.join(fixtures_dir,
|
|
36
|
+
fixture_file = File.join(fixtures_dir, 'user_preferences.json')
|
|
37
37
|
File.write(fixture_file, JSON.pretty_generate(parsed_data))
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
puts "User preferences data saved to: #{fixture_file}"
|
|
40
40
|
puts "Sample data keys: #{parsed_data.keys.first(5)}"
|
|
41
|
-
|
|
41
|
+
|
|
42
|
+
rescue => e
|
|
42
43
|
puts "Error fetching user preferences: #{e.message}"
|
|
43
44
|
puts e.backtrace.first(3)
|
|
44
45
|
end
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
if __FILE__ == $0
|
|
48
|
+
fetch_user_preferences
|
|
49
|
+
end
|
data/examples/place_oco_order.rb
CHANGED
|
@@ -46,30 +46,25 @@ oco_order = SchwabRb::Orders::OrderFactory.build(
|
|
|
46
46
|
short_leg_symbol: "SPXW 251020P06530000",
|
|
47
47
|
long_leg_symbol: "SPXW 251020P06510000",
|
|
48
48
|
order_type: SchwabRb::Order::Types::STOP_LIMIT,
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
stop:
|
|
50
|
+
price: 0.3,
|
|
51
51
|
order_instruction: :close,
|
|
52
52
|
credit_debit: :debit,
|
|
53
|
-
quantity:
|
|
53
|
+
quantity: 1
|
|
54
54
|
},
|
|
55
55
|
{
|
|
56
56
|
strategy_type: SchwabRb::Order::ComplexOrderStrategyTypes::VERTICAL,
|
|
57
57
|
short_leg_symbol: "SPXW 251020C06770000",
|
|
58
58
|
long_leg_symbol: "SPXW 251020C06790000",
|
|
59
59
|
order_type: SchwabRb::Order::Types::STOP_LIMIT,
|
|
60
|
-
price:
|
|
61
|
-
stop_price: 2.0,
|
|
60
|
+
price: 0.3,
|
|
62
61
|
order_instruction: :close,
|
|
63
62
|
credit_debit: :debit,
|
|
64
|
-
quantity:
|
|
63
|
+
quantity: 1
|
|
65
64
|
}
|
|
66
65
|
]
|
|
67
66
|
)
|
|
68
67
|
|
|
69
68
|
built_order = oco_order.build
|
|
70
69
|
|
|
71
|
-
binding.pry
|
|
72
|
-
|
|
73
70
|
response = client.place_order(built_order, account_name: CURRENT_ACCT)
|
|
74
|
-
|
|
75
|
-
binding.pry
|
|
@@ -49,7 +49,7 @@ module SchwabRb
|
|
|
49
49
|
if missing_accounts.any?
|
|
50
50
|
missing_accounts.each do |account|
|
|
51
51
|
SchwabRb::Logger.logger.warn(
|
|
52
|
-
"Account '#{account[:name]}'
|
|
52
|
+
"Account '#{account[:name]}' not found in API response. " \
|
|
53
53
|
"This may indicate a closed account or incorrect account number in account_names.json"
|
|
54
54
|
)
|
|
55
55
|
end
|
|
@@ -56,7 +56,7 @@ module SchwabRb
|
|
|
56
56
|
# @param account_name [String] The account name from account_names.json (takes priority)
|
|
57
57
|
# @param fields [Array] Balances displayed by default, additional fields can be
|
|
58
58
|
# added here by adding values from Account.fields.
|
|
59
|
-
# @param return_data_objects [Boolean] Whether to return data objects or
|
|
59
|
+
# @param return_data_objects [Boolean] Whether to return data objects or Hash
|
|
60
60
|
resolved_hash = resolve_account_hash(account_name: account_name, account_hash: account_hash)
|
|
61
61
|
|
|
62
62
|
with_account_hash_retry(resolved_hash) do
|
|
@@ -69,12 +69,12 @@ module SchwabRb
|
|
|
69
69
|
|
|
70
70
|
path = "/trader/v1/accounts/#{resolved_hash}"
|
|
71
71
|
response = get(path, params)
|
|
72
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
|
72
73
|
|
|
73
74
|
if return_data_objects
|
|
74
|
-
|
|
75
|
-
SchwabRb::DataObjects::Account.build(account_data)
|
|
75
|
+
SchwabRb::DataObjects::Account.build(data)
|
|
76
76
|
else
|
|
77
|
-
|
|
77
|
+
data
|
|
78
78
|
end
|
|
79
79
|
end
|
|
80
80
|
end
|
|
@@ -86,7 +86,7 @@ module SchwabRb
|
|
|
86
86
|
#
|
|
87
87
|
# @param fields [Array] Balances displayed by default, additional fields can be
|
|
88
88
|
# added here by adding values from Account.fields.
|
|
89
|
-
# @param return_data_objects [Boolean] Whether to return data objects or
|
|
89
|
+
# @param return_data_objects [Boolean] Whether to return data objects or Hash
|
|
90
90
|
refresh_token_if_needed
|
|
91
91
|
|
|
92
92
|
fields = convert_enum_iterable(fields, SchwabRb::Account::Statuses) if fields
|
|
@@ -96,12 +96,12 @@ module SchwabRb
|
|
|
96
96
|
|
|
97
97
|
path = "/trader/v1/accounts"
|
|
98
98
|
response = get(path, params)
|
|
99
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
|
99
100
|
|
|
100
101
|
if return_data_objects
|
|
101
|
-
|
|
102
|
-
accounts_data.map { |account_data| SchwabRb::DataObjects::Account.build(account_data) }
|
|
102
|
+
data.map { |account_data| SchwabRb::DataObjects::Account.build(account_data) }
|
|
103
103
|
else
|
|
104
|
-
|
|
104
|
+
data
|
|
105
105
|
end
|
|
106
106
|
end
|
|
107
107
|
|
|
@@ -109,7 +109,7 @@ module SchwabRb
|
|
|
109
109
|
# Returns a mapping from account IDs available to this token to the
|
|
110
110
|
# account hash that should be passed whenever referring to that account
|
|
111
111
|
# in API calls.
|
|
112
|
-
# @param return_data_objects [Boolean] Whether to return data objects or
|
|
112
|
+
# @param return_data_objects [Boolean] Whether to return data objects or Hash
|
|
113
113
|
refresh_token_if_needed
|
|
114
114
|
|
|
115
115
|
path = "/trader/v1/accounts/accountNumbers"
|
|
@@ -127,7 +127,7 @@ module SchwabRb
|
|
|
127
127
|
if return_data_objects
|
|
128
128
|
SchwabRb::DataObjects::AccountNumbers.build(account_numbers_data)
|
|
129
129
|
else
|
|
130
|
-
|
|
130
|
+
account_numbers_data
|
|
131
131
|
end
|
|
132
132
|
end
|
|
133
133
|
|
|
@@ -146,7 +146,7 @@ module SchwabRb
|
|
|
146
146
|
# @param order_id [String] The order ID.
|
|
147
147
|
# @param account_hash [String] The account hash (optional if account_name provided)
|
|
148
148
|
# @param account_name [String] The account name from account_names.json (takes priority)
|
|
149
|
-
# @param return_data_objects [Boolean] Whether to return data objects or
|
|
149
|
+
# @param return_data_objects [Boolean] Whether to return data objects or Hash
|
|
150
150
|
resolved_hash = resolve_account_hash(account_name: account_name, account_hash: account_hash)
|
|
151
151
|
|
|
152
152
|
with_account_hash_retry(resolved_hash) do
|
|
@@ -154,12 +154,12 @@ module SchwabRb
|
|
|
154
154
|
|
|
155
155
|
path = "/trader/v1/accounts/#{resolved_hash}/orders/#{order_id}"
|
|
156
156
|
response = get(path, {})
|
|
157
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
|
157
158
|
|
|
158
159
|
if return_data_objects
|
|
159
|
-
|
|
160
|
-
SchwabRb::DataObjects::Order.build(order_data)
|
|
160
|
+
SchwabRb::DataObjects::Order.build(data)
|
|
161
161
|
else
|
|
162
|
-
|
|
162
|
+
data
|
|
163
163
|
end
|
|
164
164
|
end
|
|
165
165
|
end
|
|
@@ -197,7 +197,7 @@ module SchwabRb
|
|
|
197
197
|
# @param from_entered_datetime [DateTime] Start of the query date range (default: 60 days ago).
|
|
198
198
|
# @param to_entered_datetime [DateTime] End of the query date range (default: now).
|
|
199
199
|
# @param status [String] Restrict query to orders with this status.
|
|
200
|
-
# @param return_data_objects [Boolean] Whether to return data objects or
|
|
200
|
+
# @param return_data_objects [Boolean] Whether to return data objects or Hash
|
|
201
201
|
resolved_hash = resolve_account_hash(account_name: account_name, account_hash: account_hash)
|
|
202
202
|
|
|
203
203
|
with_account_hash_retry(resolved_hash) do
|
|
@@ -218,12 +218,12 @@ module SchwabRb
|
|
|
218
218
|
)
|
|
219
219
|
|
|
220
220
|
response = get(path, params)
|
|
221
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
|
221
222
|
|
|
222
223
|
if return_data_objects
|
|
223
|
-
|
|
224
|
-
orders_data.map { |order_data| SchwabRb::DataObjects::Order.build(order_data) }
|
|
224
|
+
data.map { |order_data| SchwabRb::DataObjects::Order.build(order_data) }
|
|
225
225
|
else
|
|
226
|
-
|
|
226
|
+
data
|
|
227
227
|
end
|
|
228
228
|
end
|
|
229
229
|
end
|
|
@@ -241,7 +241,7 @@ module SchwabRb
|
|
|
241
241
|
# @param from_entered_datetime [DateTime] Start of the query date range (default: 60 days ago).
|
|
242
242
|
# @param to_entered_datetime [DateTime] End of the query date range (default: now).
|
|
243
243
|
# @param status [String] Restrict query to orders with this status.
|
|
244
|
-
# @param return_data_objects [Boolean] Whether to return data objects or
|
|
244
|
+
# @param return_data_objects [Boolean] Whether to return data objects or Hash
|
|
245
245
|
refresh_token_if_needed
|
|
246
246
|
|
|
247
247
|
path = "/trader/v1/orders"
|
|
@@ -253,12 +253,12 @@ module SchwabRb
|
|
|
253
253
|
)
|
|
254
254
|
|
|
255
255
|
response = get(path, params)
|
|
256
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
|
256
257
|
|
|
257
258
|
if return_data_objects
|
|
258
|
-
|
|
259
|
-
orders_data.map { |order_data| SchwabRb::DataObjects::Order.build(order_data) }
|
|
259
|
+
data.map { |order_data| SchwabRb::DataObjects::Order.build(order_data) }
|
|
260
260
|
else
|
|
261
|
-
|
|
261
|
+
data
|
|
262
262
|
end
|
|
263
263
|
end
|
|
264
264
|
|
|
@@ -312,7 +312,7 @@ module SchwabRb
|
|
|
312
312
|
# @param account_hash [String] The account hash (optional if account_name provided)
|
|
313
313
|
# @param order_spec [Hash, SchwabRb::Orders::Builder] The order specification to preview
|
|
314
314
|
# @param account_name [String] The account name from account_names.json (takes priority)
|
|
315
|
-
# @param return_data_objects [Boolean] Whether to return data objects or
|
|
315
|
+
# @param return_data_objects [Boolean] Whether to return data objects or Hash
|
|
316
316
|
resolved_hash = resolve_account_hash(account_name: account_name, account_hash: account_hash)
|
|
317
317
|
|
|
318
318
|
with_account_hash_retry(resolved_hash) do
|
|
@@ -322,12 +322,12 @@ module SchwabRb
|
|
|
322
322
|
|
|
323
323
|
path = "/trader/v1/accounts/#{resolved_hash}/previewOrder"
|
|
324
324
|
response = post(path, order_spec)
|
|
325
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
|
325
326
|
|
|
326
327
|
if return_data_objects
|
|
327
|
-
|
|
328
|
-
SchwabRb::DataObjects::OrderPreview.build(preview_data)
|
|
328
|
+
SchwabRb::DataObjects::OrderPreview.build(data)
|
|
329
329
|
else
|
|
330
|
-
|
|
330
|
+
data
|
|
331
331
|
end
|
|
332
332
|
end
|
|
333
333
|
end
|
|
@@ -349,7 +349,7 @@ module SchwabRb
|
|
|
349
349
|
# @param end_date [Date, DateTime] End date for transactions (default: now).
|
|
350
350
|
# @param transaction_types [Array] List of transaction types to filter by.
|
|
351
351
|
# @param symbol [String] Filter transactions by the specified symbol.
|
|
352
|
-
# @param return_data_objects [Boolean] Whether to return data objects or
|
|
352
|
+
# @param return_data_objects [Boolean] Whether to return data objects or Hash
|
|
353
353
|
resolved_hash = resolve_account_hash(account_name: account_name, account_hash: account_hash)
|
|
354
354
|
|
|
355
355
|
with_account_hash_retry(resolved_hash) do
|
|
@@ -382,14 +382,14 @@ module SchwabRb
|
|
|
382
382
|
|
|
383
383
|
path = "/trader/v1/accounts/#{resolved_hash}/transactions"
|
|
384
384
|
response = get(path, params)
|
|
385
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
|
385
386
|
|
|
386
387
|
if return_data_objects
|
|
387
|
-
|
|
388
|
-
transactions_data.map do |transaction_data|
|
|
388
|
+
data.map do |transaction_data|
|
|
389
389
|
SchwabRb::DataObjects::Transaction.build(transaction_data)
|
|
390
390
|
end
|
|
391
391
|
else
|
|
392
|
-
|
|
392
|
+
data
|
|
393
393
|
end
|
|
394
394
|
end
|
|
395
395
|
end
|
|
@@ -400,7 +400,7 @@ module SchwabRb
|
|
|
400
400
|
# @param account_hash [String] The account hash (optional if account_name provided)
|
|
401
401
|
# @param activity_id [String] ID of the transaction to retrieve
|
|
402
402
|
# @param account_name [String] The account name from account_names.json (takes priority)
|
|
403
|
-
# @param return_data_objects [Boolean] Whether to return data objects or
|
|
403
|
+
# @param return_data_objects [Boolean] Whether to return data objects or Hash
|
|
404
404
|
resolved_hash = resolve_account_hash(account_name: account_name, account_hash: account_hash)
|
|
405
405
|
|
|
406
406
|
with_account_hash_retry(resolved_hash) do
|
|
@@ -408,28 +408,28 @@ module SchwabRb
|
|
|
408
408
|
|
|
409
409
|
path = "/trader/v1/accounts/#{resolved_hash}/transactions/#{activity_id}"
|
|
410
410
|
response = get(path, {})
|
|
411
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
|
411
412
|
|
|
412
413
|
if return_data_objects
|
|
413
|
-
|
|
414
|
-
SchwabRb::DataObjects::Transaction.build(transaction_data)
|
|
414
|
+
SchwabRb::DataObjects::Transaction.build(data)
|
|
415
415
|
else
|
|
416
|
-
|
|
416
|
+
data
|
|
417
417
|
end
|
|
418
418
|
end
|
|
419
419
|
end
|
|
420
420
|
|
|
421
421
|
def get_user_preferences(return_data_objects: true)
|
|
422
422
|
# Get user preferences for the authenticated user.
|
|
423
|
-
# @param return_data_objects [Boolean] Whether to return data objects or
|
|
423
|
+
# @param return_data_objects [Boolean] Whether to return data objects or Hash
|
|
424
424
|
refresh_token_if_needed
|
|
425
425
|
path = "/trader/v1/userPreference"
|
|
426
426
|
response = get(path, {})
|
|
427
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
|
427
428
|
|
|
428
429
|
if return_data_objects
|
|
429
|
-
|
|
430
|
-
SchwabRb::DataObjects::UserPreferences.build(preferences_data)
|
|
430
|
+
SchwabRb::DataObjects::UserPreferences.build(data)
|
|
431
431
|
else
|
|
432
|
-
|
|
432
|
+
data
|
|
433
433
|
end
|
|
434
434
|
end
|
|
435
435
|
|
|
@@ -439,19 +439,19 @@ module SchwabRb
|
|
|
439
439
|
# @param symbol [String] Single symbol to fetch.
|
|
440
440
|
# @param fields [Array] Fields to request. If unset, return all available
|
|
441
441
|
# data (i.e., all fields). See `GetQuote::Field` for options.
|
|
442
|
-
# @param return_data_objects [Boolean] Whether to return data objects or
|
|
442
|
+
# @param return_data_objects [Boolean] Whether to return data objects or Hash
|
|
443
443
|
refresh_token_if_needed
|
|
444
444
|
|
|
445
445
|
fields = convert_enum_iterable(fields, SchwabRb::Quote::Types) if fields
|
|
446
446
|
params = fields ? { "fields" => fields.join(",") } : {}
|
|
447
447
|
path = "/marketdata/v1/#{symbol}/quotes"
|
|
448
448
|
response = get(path, params)
|
|
449
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
|
449
450
|
|
|
450
451
|
if return_data_objects
|
|
451
|
-
|
|
452
|
-
SchwabRb::DataObjects::QuoteFactory.build(quote_data)
|
|
452
|
+
SchwabRb::DataObjects::QuoteFactory.build(data)
|
|
453
453
|
else
|
|
454
|
-
|
|
454
|
+
data
|
|
455
455
|
end
|
|
456
456
|
end
|
|
457
457
|
|
|
@@ -463,7 +463,7 @@ module SchwabRb
|
|
|
463
463
|
# @param fields [Array] Fields to request. If unset, return all available data.
|
|
464
464
|
# See `GetQuote::Field` for options.
|
|
465
465
|
# @param indicative [Boolean] If set, fetch indicative quotes. Must be true or false.
|
|
466
|
-
# @param return_data_objects [Boolean] Whether to return data objects or
|
|
466
|
+
# @param return_data_objects [Boolean] Whether to return data objects or Hash
|
|
467
467
|
refresh_token_if_needed
|
|
468
468
|
|
|
469
469
|
symbols = [symbols] if symbols.is_a?(String)
|
|
@@ -481,14 +481,14 @@ module SchwabRb
|
|
|
481
481
|
|
|
482
482
|
path = "/marketdata/v1/quotes"
|
|
483
483
|
response = get(path, params)
|
|
484
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
|
484
485
|
|
|
485
486
|
if return_data_objects
|
|
486
|
-
|
|
487
|
-
quotes_data.map do |symbol, quote_data|
|
|
487
|
+
data.map do |symbol, quote_data|
|
|
488
488
|
SchwabRb::DataObjects::QuoteFactory.build({ symbol => quote_data })
|
|
489
489
|
end
|
|
490
490
|
else
|
|
491
|
-
|
|
491
|
+
data
|
|
492
492
|
end
|
|
493
493
|
end
|
|
494
494
|
|
|
@@ -531,7 +531,7 @@ module SchwabRb
|
|
|
531
531
|
# @param exp_month [String] Filter options by expiration month.
|
|
532
532
|
# @param option_type [String] Type of options to include in the chain.
|
|
533
533
|
# @param entitlement [String] Client entitlement.
|
|
534
|
-
# @param return_data_objects [Boolean] Whether to return data objects or
|
|
534
|
+
# @param return_data_objects [Boolean] Whether to return data objects or Hash
|
|
535
535
|
|
|
536
536
|
refresh_token_if_needed
|
|
537
537
|
|
|
@@ -562,28 +562,28 @@ module SchwabRb
|
|
|
562
562
|
|
|
563
563
|
path = "/marketdata/v1/chains"
|
|
564
564
|
response = get(path, params)
|
|
565
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
|
565
566
|
|
|
566
567
|
if return_data_objects
|
|
567
|
-
|
|
568
|
-
SchwabRb::DataObjects::OptionChain.build(option_chain_data)
|
|
568
|
+
SchwabRb::DataObjects::OptionChain.build(data)
|
|
569
569
|
else
|
|
570
|
-
|
|
570
|
+
data
|
|
571
571
|
end
|
|
572
572
|
end
|
|
573
573
|
|
|
574
574
|
def get_option_expiration_chain(symbol, return_data_objects: true)
|
|
575
575
|
# Get option expiration chain for a symbol.
|
|
576
576
|
# @param symbol [String] The symbol for which to get option expiration dates.
|
|
577
|
-
# @param return_data_objects [Boolean] Whether to return data objects or
|
|
577
|
+
# @param return_data_objects [Boolean] Whether to return data objects or Hash
|
|
578
578
|
refresh_token_if_needed
|
|
579
579
|
path = "/marketdata/v1/expirationchain"
|
|
580
580
|
response = get(path, { symbol: symbol })
|
|
581
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
|
581
582
|
|
|
582
583
|
if return_data_objects
|
|
583
|
-
|
|
584
|
-
SchwabRb::DataObjects::OptionExpirationChain.build(expiration_data)
|
|
584
|
+
SchwabRb::DataObjects::OptionExpirationChain.build(data)
|
|
585
585
|
else
|
|
586
|
-
|
|
586
|
+
data
|
|
587
587
|
end
|
|
588
588
|
end
|
|
589
589
|
|
|
@@ -600,7 +600,7 @@ module SchwabRb
|
|
|
600
600
|
return_data_objects: true
|
|
601
601
|
)
|
|
602
602
|
# Get price history for a symbol.
|
|
603
|
-
# @param return_data_objects [Boolean] Whether to return data objects or
|
|
603
|
+
# @param return_data_objects [Boolean] Whether to return data objects or Hash
|
|
604
604
|
refresh_token_if_needed
|
|
605
605
|
|
|
606
606
|
period_type = convert_enum(period_type, SchwabRb::PriceHistory::PeriodTypes) if period_type
|
|
@@ -621,12 +621,12 @@ module SchwabRb
|
|
|
621
621
|
path = "/marketdata/v1/pricehistory"
|
|
622
622
|
|
|
623
623
|
response = get(path, params)
|
|
624
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
|
624
625
|
|
|
625
626
|
if return_data_objects
|
|
626
|
-
|
|
627
|
-
SchwabRb::DataObjects::PriceHistory.build(price_history_data)
|
|
627
|
+
SchwabRb::DataObjects::PriceHistory.build(data)
|
|
628
628
|
else
|
|
629
|
-
|
|
629
|
+
data
|
|
630
630
|
end
|
|
631
631
|
end
|
|
632
632
|
|
|
@@ -847,12 +847,12 @@ module SchwabRb
|
|
|
847
847
|
params["frequency"] = frequency.to_s if frequency
|
|
848
848
|
|
|
849
849
|
response = get(path, params)
|
|
850
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
|
850
851
|
|
|
851
852
|
if return_data_objects
|
|
852
|
-
|
|
853
|
-
SchwabRb::DataObjects::MarketMoversFactory.build(movers_data)
|
|
853
|
+
SchwabRb::DataObjects::MarketMoversFactory.build(data)
|
|
854
854
|
else
|
|
855
|
-
|
|
855
|
+
data
|
|
856
856
|
end
|
|
857
857
|
end
|
|
858
858
|
|
|
@@ -862,7 +862,7 @@ module SchwabRb
|
|
|
862
862
|
# @param markets [Array, String] Markets for which to return trading hours.
|
|
863
863
|
# @param date [Date] Date for which to return market hours. Accepts values up to
|
|
864
864
|
# one year from today.
|
|
865
|
-
# @param return_data_objects [Boolean] Whether to return data objects or
|
|
865
|
+
# @param return_data_objects [Boolean] Whether to return data objects or Hash
|
|
866
866
|
refresh_token_if_needed
|
|
867
867
|
|
|
868
868
|
markets = convert_enum_iterable(markets, SchwabRb::MarketHours::Markets)
|
|
@@ -871,12 +871,12 @@ module SchwabRb
|
|
|
871
871
|
params["date"] = format_date_as_day("date", date) if date
|
|
872
872
|
|
|
873
873
|
response = get("/marketdata/v1/markets", params)
|
|
874
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
|
874
875
|
|
|
875
876
|
if return_data_objects
|
|
876
|
-
|
|
877
|
-
SchwabRb::DataObjects::MarketHours.build(market_hours_data)
|
|
877
|
+
SchwabRb::DataObjects::MarketHours.build(data)
|
|
878
878
|
else
|
|
879
|
-
|
|
879
|
+
data
|
|
880
880
|
end
|
|
881
881
|
end
|
|
882
882
|
|
|
@@ -887,7 +887,7 @@ module SchwabRb
|
|
|
887
887
|
# @param symbols [String, Array] For "FUNDAMENTAL" projection, the symbols to fetch.
|
|
888
888
|
# For other projections, a search term.
|
|
889
889
|
# @param projection [String] Search mode or "FUNDAMENTAL" for instrument fundamentals.
|
|
890
|
-
# @param return_data_objects [Boolean] Whether to return data objects or
|
|
890
|
+
# @param return_data_objects [Boolean] Whether to return data objects or Hash
|
|
891
891
|
refresh_token_if_needed
|
|
892
892
|
|
|
893
893
|
symbols = [symbols] unless symbols.is_a?(Array)
|
|
@@ -898,14 +898,14 @@ module SchwabRb
|
|
|
898
898
|
}
|
|
899
899
|
|
|
900
900
|
response = get("/marketdata/v1/instruments", params)
|
|
901
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
|
901
902
|
|
|
902
903
|
if return_data_objects
|
|
903
|
-
|
|
904
|
-
instruments_data.map do |instrument_data|
|
|
904
|
+
data.map do |instrument_data|
|
|
905
905
|
SchwabRb::DataObjects::Instrument.build(instrument_data)
|
|
906
906
|
end
|
|
907
907
|
else
|
|
908
|
-
|
|
908
|
+
data
|
|
909
909
|
end
|
|
910
910
|
end
|
|
911
911
|
|
|
@@ -913,18 +913,18 @@ module SchwabRb
|
|
|
913
913
|
# Get instrument information for a single instrument by CUSIP.
|
|
914
914
|
#
|
|
915
915
|
# @param cusip [String] CUSIP of the instrument to fetch. Leading zeroes must be preserved.
|
|
916
|
-
# @param return_data_objects [Boolean] Whether to return data objects or
|
|
916
|
+
# @param return_data_objects [Boolean] Whether to return data objects or Hash
|
|
917
917
|
refresh_token_if_needed
|
|
918
918
|
|
|
919
919
|
raise ArgumentError, "cusip must be passed as a string" unless cusip.is_a?(String)
|
|
920
920
|
|
|
921
921
|
response = get("/marketdata/v1/instruments/#{cusip}", {})
|
|
922
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
|
922
923
|
|
|
923
924
|
if return_data_objects
|
|
924
|
-
|
|
925
|
-
SchwabRb::DataObjects::Instrument.build(instrument_data)
|
|
925
|
+
SchwabRb::DataObjects::Instrument.build(data)
|
|
926
926
|
else
|
|
927
|
-
|
|
927
|
+
data
|
|
928
928
|
end
|
|
929
929
|
end
|
|
930
930
|
|
|
@@ -42,7 +42,6 @@ module SchwabRb
|
|
|
42
42
|
theoretical_option_value: data.fetch(:theoreticalOptionValue, nil),
|
|
43
43
|
theoretical_volatility: data.fetch(:theoreticalVolatility, nil),
|
|
44
44
|
option_deliverables_list: data.fetch(:optionDeliverablesList, nil),
|
|
45
|
-
strike_price: data.fetch(:strikePrice, nil),
|
|
46
45
|
expiration_date: Date.parse(data.fetch(:expirationDate)),
|
|
47
46
|
days_to_expiration: data.fetch(:daysToExpiration, nil),
|
|
48
47
|
expiration_type: data.fetch(:expirationType, nil),
|
|
@@ -72,7 +71,7 @@ module SchwabRb
|
|
|
72
71
|
close_price:, total_volume:, trade_time_in_long:,
|
|
73
72
|
quote_time_in_long:, net_change:, volatility:, delta:,
|
|
74
73
|
gamma:, theta:, vega:, rho:, open_interest:, time_value:,
|
|
75
|
-
theoretical_option_value:, theoretical_volatility:, option_deliverables_list:,
|
|
74
|
+
theoretical_option_value:, theoretical_volatility:, option_deliverables_list:,
|
|
76
75
|
expiration_date:, days_to_expiration:, expiration_type:, last_trading_day:, multiplier:,
|
|
77
76
|
settlement_type:, deliverable_note:, percent_change:, mark_change:, mark_percent_change:, intrinsic_value:, extrinsic_value:, option_root:, exercise_type:, high_52_week:, low_52_week:, non_standard:, in_the_money:
|
|
78
77
|
)
|
|
@@ -109,7 +108,6 @@ module SchwabRb
|
|
|
109
108
|
@theoretical_option_value = theoretical_option_value
|
|
110
109
|
@theoretical_volatility = theoretical_volatility
|
|
111
110
|
@option_deliverables_list = option_deliverables_list
|
|
112
|
-
@strike_price = strike_price
|
|
113
111
|
@expiration_date = expiration_date
|
|
114
112
|
@days_to_expiration = days_to_expiration
|
|
115
113
|
@expiration_type = expiration_type
|
|
@@ -136,12 +134,16 @@ module SchwabRb
|
|
|
136
134
|
:close_price, :total_volume, :trade_time_in_long, :quote_time_in_long,
|
|
137
135
|
:net_change, :volatility, :delta, :gamma, :theta, :vega, :rho,
|
|
138
136
|
:open_interest, :time_value, :theoretical_option_value,
|
|
139
|
-
:theoretical_volatility, :option_deliverables_list,
|
|
137
|
+
:theoretical_volatility, :option_deliverables_list,
|
|
140
138
|
:expiration_date, :days_to_expiration, :expiration_type, :last_trading_day,
|
|
141
139
|
:multiplier, :settlement_type, :deliverable_note, :percent_change,
|
|
142
140
|
:mark_change, :mark_percent_change, :intrinsic_value, :extrinsic_value,
|
|
143
141
|
:option_root, :exercise_type, :high_52_week, :low_52_week, :non_standard,
|
|
144
142
|
:in_the_money
|
|
143
|
+
|
|
144
|
+
def strike_price
|
|
145
|
+
strike
|
|
146
|
+
end
|
|
145
147
|
end
|
|
146
148
|
end
|
|
147
149
|
end
|
|
@@ -6,7 +6,7 @@ module SchwabRb
|
|
|
6
6
|
module DataObjects
|
|
7
7
|
class OrderPreview
|
|
8
8
|
attr_reader :order_id, :order_value, :order_strategy, :order_balance, :order_validation_result,
|
|
9
|
-
:
|
|
9
|
+
:commission_and_fee
|
|
10
10
|
|
|
11
11
|
class << self
|
|
12
12
|
def build(data)
|
|
@@ -20,7 +20,7 @@ module SchwabRb
|
|
|
20
20
|
@order_strategy = attrs[:orderStrategy] ? OrderStrategy.new(attrs[:orderStrategy]) : nil
|
|
21
21
|
@order_balance = attrs[:orderBalance] ? OrderBalance.new(attrs[:orderBalance]) : nil
|
|
22
22
|
@order_validation_result = attrs[:orderValidationResult] ? OrderValidationResult.new(attrs[:orderValidationResult]) : nil
|
|
23
|
-
@
|
|
23
|
+
@commission_and_fee = attrs[:commissionAndFee] ? CommissionAndFee.new(attrs[:commissionAndFee]) : nil
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def status
|
|
@@ -40,15 +40,15 @@ module SchwabRb
|
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
def commission
|
|
43
|
-
return 0.0 unless @
|
|
43
|
+
return 0.0 unless @commission_and_fee
|
|
44
44
|
|
|
45
|
-
@
|
|
45
|
+
@commission_and_fee.commission
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def fees
|
|
49
|
-
return 0.0 unless @
|
|
49
|
+
return 0.0 unless @commission_and_fee
|
|
50
50
|
|
|
51
|
-
@
|
|
51
|
+
@commission_and_fee.fee
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def to_h
|
|
@@ -58,15 +58,14 @@ module SchwabRb
|
|
|
58
58
|
orderStrategy: @order_strategy&.to_h,
|
|
59
59
|
orderBalance: @order_balance&.to_h,
|
|
60
60
|
orderValidationResult: @order_validation_result&.to_h,
|
|
61
|
-
|
|
61
|
+
commissionAndFee: @commission_and_fee&.to_h
|
|
62
62
|
}
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
class OrderStrategy
|
|
66
|
-
attr_reader :
|
|
66
|
+
attr_reader :status, :price, :quantity, :order_type, :type, :strategy_id, :order_legs
|
|
67
67
|
|
|
68
68
|
def initialize(attrs)
|
|
69
|
-
@account_number = attrs[:accountNumber]
|
|
70
69
|
@status = attrs[:status]
|
|
71
70
|
@price = attrs[:price]
|
|
72
71
|
@quantity = attrs[:quantity]
|
|
@@ -78,7 +77,6 @@ module SchwabRb
|
|
|
78
77
|
|
|
79
78
|
def to_h
|
|
80
79
|
{
|
|
81
|
-
accountNumber: @account_number,
|
|
82
80
|
status: @status,
|
|
83
81
|
price: @price,
|
|
84
82
|
quantity: @quantity,
|
|
@@ -111,75 +109,81 @@ module SchwabRb
|
|
|
111
109
|
end
|
|
112
110
|
|
|
113
111
|
class OrderValidationResult
|
|
114
|
-
attr_reader :is_valid, :
|
|
112
|
+
attr_reader :is_valid, :warns, :rejects
|
|
115
113
|
|
|
116
114
|
def initialize(attrs)
|
|
117
115
|
@is_valid = attrs[:isValid]
|
|
118
|
-
@
|
|
116
|
+
@warns = attrs[:warns]&.map { |warn| Warn.new(warn) } || []
|
|
119
117
|
@rejects = attrs[:rejects]&.map { |reject| Reject.new(reject) } || []
|
|
120
118
|
end
|
|
121
119
|
|
|
122
120
|
def to_h
|
|
123
121
|
{
|
|
124
122
|
isValid: @is_valid,
|
|
125
|
-
|
|
123
|
+
warns: @warns.map(&:to_h),
|
|
126
124
|
rejects: @rejects.map(&:to_h)
|
|
127
125
|
}
|
|
128
126
|
end
|
|
129
127
|
|
|
128
|
+
class Warn
|
|
129
|
+
attr_reader :activity_message, :original_severity
|
|
130
|
+
|
|
131
|
+
def initialize(attrs)
|
|
132
|
+
@activity_message = attrs[:activityMessage]
|
|
133
|
+
@original_severity = attrs[:originalSeverity]
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def to_h
|
|
137
|
+
{
|
|
138
|
+
activityMessage: @activity_message,
|
|
139
|
+
originalSeverity: @original_severity
|
|
140
|
+
}
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
130
144
|
class Reject
|
|
131
|
-
attr_reader :
|
|
145
|
+
attr_reader :activity_message, :original_severity
|
|
132
146
|
|
|
133
147
|
def initialize(attrs)
|
|
134
|
-
@
|
|
135
|
-
@
|
|
148
|
+
@activity_message = attrs[:activityMessage]
|
|
149
|
+
@original_severity = attrs[:originalSeverity]
|
|
136
150
|
end
|
|
137
151
|
|
|
138
152
|
def to_h
|
|
139
153
|
{
|
|
140
|
-
|
|
141
|
-
|
|
154
|
+
activityMessage: @activity_message,
|
|
155
|
+
originalSeverity: @original_severity
|
|
142
156
|
}
|
|
143
157
|
end
|
|
144
158
|
end
|
|
145
159
|
end
|
|
146
160
|
|
|
147
161
|
class CommissionAndFee
|
|
148
|
-
attr_reader :
|
|
162
|
+
attr_reader :commissions, :fees, :true_commission_legs
|
|
149
163
|
|
|
150
164
|
def initialize(attrs)
|
|
151
|
-
@
|
|
152
|
-
@
|
|
153
|
-
@
|
|
154
|
-
@fees = attrs[:fees] || []
|
|
155
|
-
@true_commission = attrs[:trueCommission]&.to_f
|
|
165
|
+
@commissions = attrs[:commission][:commissionLegs] || []
|
|
166
|
+
@fees = attrs[:fee][:feeLegs] || []
|
|
167
|
+
@true_commission_legs = attrs[:trueCommission][:commissionLegs] || []
|
|
156
168
|
end
|
|
157
169
|
|
|
158
|
-
def
|
|
170
|
+
def commission
|
|
159
171
|
calculate_total_from_legs(@commissions, "COMMISSION")
|
|
160
172
|
end
|
|
161
173
|
|
|
162
|
-
def
|
|
163
|
-
@commission || commission_total
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
def fee_total
|
|
174
|
+
def fee
|
|
167
175
|
calculate_total_from_legs(@fees, %w[OPT_REG_FEE INDEX_OPTION_FEE])
|
|
168
176
|
end
|
|
169
177
|
|
|
170
|
-
def
|
|
171
|
-
@
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
def true_commission_value
|
|
175
|
-
@true_commission || (commission_total * 2)
|
|
178
|
+
def true_commission
|
|
179
|
+
calculate_total_from_legs(@true_commission_legs, "COMMISSION")
|
|
176
180
|
end
|
|
177
181
|
|
|
178
182
|
def to_h
|
|
179
183
|
{
|
|
180
|
-
commission:
|
|
181
|
-
fee:
|
|
182
|
-
trueCommission:
|
|
184
|
+
commission: commission,
|
|
185
|
+
fee: fee,
|
|
186
|
+
trueCommission: true_commission,
|
|
183
187
|
commissions: @commissions,
|
|
184
188
|
fees: @fees
|
|
185
189
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "iron_condor_order"
|
|
4
4
|
require_relative "vertical_order"
|
|
5
|
+
require_relative "vertical_roll_order"
|
|
5
6
|
require_relative "single_order"
|
|
6
7
|
require_relative "oco_order"
|
|
7
8
|
require_relative "order"
|
|
@@ -37,6 +38,19 @@ module SchwabRb
|
|
|
37
38
|
order_instruction: options[:order_instruction] || :open,
|
|
38
39
|
quantity: options[:quantity] || 1
|
|
39
40
|
)
|
|
41
|
+
when SchwabRb::Order::ComplexOrderStrategyTypes::VERTICAL_ROLL
|
|
42
|
+
VerticalRollOrder.build(
|
|
43
|
+
close_short_leg_symbol: options[:close_short_leg_symbol],
|
|
44
|
+
close_long_leg_symbol: options[:close_long_leg_symbol],
|
|
45
|
+
open_short_leg_symbol: options[:open_short_leg_symbol],
|
|
46
|
+
open_long_leg_symbol: options[:open_long_leg_symbol],
|
|
47
|
+
price: options[:price],
|
|
48
|
+
stop_price: options[:stop_price],
|
|
49
|
+
order_type: options[:order_type],
|
|
50
|
+
duration: options[:duration] || SchwabRb::Orders::Duration::DAY,
|
|
51
|
+
credit_debit: options[:credit_debit] || :credit,
|
|
52
|
+
quantity: options[:quantity] || 1
|
|
53
|
+
)
|
|
40
54
|
when SchwabRb::Order::OrderStrategyTypes::SINGLE
|
|
41
55
|
SingleOrder.build(
|
|
42
56
|
symbol: options[:symbol],
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "schwab_rb"
|
|
4
|
+
|
|
5
|
+
module SchwabRb
|
|
6
|
+
module Orders
|
|
7
|
+
class VerticalRollOrder
|
|
8
|
+
class << self
|
|
9
|
+
def build(
|
|
10
|
+
close_short_leg_symbol:,
|
|
11
|
+
close_long_leg_symbol:,
|
|
12
|
+
open_short_leg_symbol:,
|
|
13
|
+
open_long_leg_symbol:,
|
|
14
|
+
price:,
|
|
15
|
+
stop_price: nil,
|
|
16
|
+
order_type: nil,
|
|
17
|
+
duration: SchwabRb::Orders::Duration::DAY,
|
|
18
|
+
credit_debit: :credit,
|
|
19
|
+
quantity: 1
|
|
20
|
+
)
|
|
21
|
+
schwab_order_builder.new.tap do |builder|
|
|
22
|
+
builder.set_order_strategy_type(SchwabRb::Order::OrderStrategyTypes::SINGLE)
|
|
23
|
+
builder.set_session(SchwabRb::Orders::Session::NORMAL)
|
|
24
|
+
builder.set_duration(duration)
|
|
25
|
+
builder.set_order_type(order_type || determine_order_type(credit_debit))
|
|
26
|
+
builder.set_complex_order_strategy_type(SchwabRb::Order::ComplexOrderStrategyTypes::VERTICAL_ROLL)
|
|
27
|
+
builder.set_quantity(quantity)
|
|
28
|
+
builder.set_price(price)
|
|
29
|
+
builder.set_stop_price(stop_price) if stop_price && order_type == SchwabRb::Order::Types::STOP_LIMIT
|
|
30
|
+
|
|
31
|
+
# Close the existing spread (opposite instructions)
|
|
32
|
+
builder.add_option_leg(
|
|
33
|
+
SchwabRb::Orders::OptionInstructions::BUY_TO_CLOSE,
|
|
34
|
+
close_short_leg_symbol,
|
|
35
|
+
quantity
|
|
36
|
+
)
|
|
37
|
+
builder.add_option_leg(
|
|
38
|
+
SchwabRb::Orders::OptionInstructions::SELL_TO_CLOSE,
|
|
39
|
+
close_long_leg_symbol,
|
|
40
|
+
quantity
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Open the new spread
|
|
44
|
+
builder.add_option_leg(
|
|
45
|
+
SchwabRb::Orders::OptionInstructions::SELL_TO_OPEN,
|
|
46
|
+
open_short_leg_symbol,
|
|
47
|
+
quantity
|
|
48
|
+
)
|
|
49
|
+
builder.add_option_leg(
|
|
50
|
+
SchwabRb::Orders::OptionInstructions::BUY_TO_OPEN,
|
|
51
|
+
open_long_leg_symbol,
|
|
52
|
+
quantity
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def determine_order_type(credit_debit)
|
|
58
|
+
if credit_debit == :credit
|
|
59
|
+
SchwabRb::Order::Types::NET_CREDIT
|
|
60
|
+
else
|
|
61
|
+
SchwabRb::Order::Types::NET_DEBIT
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def schwab_order_builder
|
|
66
|
+
SchwabRb::Orders::Builder
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
data/lib/schwab_rb/version.rb
CHANGED
data/lib/schwab_rb.rb
CHANGED
|
@@ -47,6 +47,7 @@ require_relative "schwab_rb/data_objects/market_movers"
|
|
|
47
47
|
require_relative "schwab_rb/orders/order_factory"
|
|
48
48
|
require_relative "schwab_rb/orders/iron_condor_order"
|
|
49
49
|
require_relative "schwab_rb/orders/vertical_order"
|
|
50
|
+
require_relative "schwab_rb/orders/vertical_roll_order"
|
|
50
51
|
require_relative "schwab_rb/orders/single_order"
|
|
51
52
|
require_relative "schwab_rb/orders/oco_order"
|
|
52
53
|
|
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.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Joseph Platta
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2025-
|
|
10
|
+
date: 2025-12-11 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: async
|
|
@@ -266,6 +266,7 @@ files:
|
|
|
266
266
|
- lib/schwab_rb/orders/stop_type.rb
|
|
267
267
|
- lib/schwab_rb/orders/tax_lot_method.rb
|
|
268
268
|
- lib/schwab_rb/orders/vertical_order.rb
|
|
269
|
+
- lib/schwab_rb/orders/vertical_roll_order.rb
|
|
269
270
|
- lib/schwab_rb/price_history.rb
|
|
270
271
|
- lib/schwab_rb/quote.rb
|
|
271
272
|
- lib/schwab_rb/transaction.rb
|