schwab_rb 0.6.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.claude/settings.local.json +9 -0
- data/.codex/skills/schwab-rb-cli/SKILL.md +65 -0
- data/.codex/skills/schwab-rb-cli/agents/openai.yaml +3 -0
- data/.codex/skills/schwab-rb-cli/references/cli_examples.md +120 -0
- data/.codex/skills/schwab-rb-cli/scripts/run_schwab_rb.sh +9 -0
- data/AGENTS.md +137 -0
- data/CHANGELOG.md +8 -0
- data/README.md +41 -3
- data/Rakefile +26 -0
- data/doc/LOGGING.md +141 -0
- data/doc/QUICK_START.md +15 -0
- data/examples/fetch_account_numbers.rb +16 -19
- data/examples/fetch_user_preferences.rb +16 -19
- data/examples/place_oco_order.rb +13 -6
- data/exe/schwab_rb +7 -0
- data/lib/schwab_rb/auth/init_client_easy.rb +2 -0
- data/lib/schwab_rb/auth/init_client_login.rb +2 -0
- data/lib/schwab_rb/auth/init_client_token_file.rb +3 -0
- data/lib/schwab_rb/auth/token_manager.rb +5 -1
- data/lib/schwab_rb/cli/app.rb +579 -0
- data/lib/schwab_rb/cli.rb +8 -0
- data/lib/schwab_rb/configuration.rb +7 -1
- data/lib/schwab_rb/constants.rb +4 -4
- data/lib/schwab_rb/path_support.rb +19 -0
- data/lib/schwab_rb/price_history/downloader.rb +246 -0
- data/lib/schwab_rb/utils/logger.rb +1 -1
- data/lib/schwab_rb/version.rb +1 -1
- data/lib/schwab_rb.rb +3 -0
- metadata +16 -3
|
@@ -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.fetch("SCHWAB_API_KEY", nil),
|
|
18
|
+
ENV.fetch("SCHWAB_APP_SECRET", nil),
|
|
19
|
+
ENV.fetch("APP_CALLBACK_URL", nil),
|
|
20
20
|
token_path
|
|
21
21
|
)
|
|
22
22
|
end
|
|
@@ -24,26 +24,23 @@ 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
|
-
|
|
42
|
-
rescue => e
|
|
41
|
+
rescue StandardError => e
|
|
43
42
|
puts "Error fetching account numbers: #{e.message}"
|
|
44
43
|
puts e.backtrace.first(3)
|
|
45
44
|
end
|
|
46
45
|
|
|
47
|
-
if __FILE__ == $
|
|
48
|
-
fetch_account_numbers
|
|
49
|
-
end
|
|
46
|
+
fetch_account_numbers if __FILE__ == $PROGRAM_NAME
|
|
@@ -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.fetch("SCHWAB_API_KEY", nil),
|
|
18
|
+
ENV.fetch("SCHWAB_APP_SECRET", nil),
|
|
19
|
+
ENV.fetch("APP_CALLBACK_URL", nil),
|
|
20
20
|
token_path
|
|
21
21
|
)
|
|
22
22
|
end
|
|
@@ -24,26 +24,23 @@ 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
|
-
|
|
42
|
-
rescue => e
|
|
41
|
+
rescue StandardError => e
|
|
43
42
|
puts "Error fetching user preferences: #{e.message}"
|
|
44
43
|
puts e.backtrace.first(3)
|
|
45
44
|
end
|
|
46
45
|
|
|
47
|
-
if __FILE__ == $
|
|
48
|
-
fetch_user_preferences
|
|
49
|
-
end
|
|
46
|
+
fetch_user_preferences if __FILE__ == $PROGRAM_NAME
|
data/examples/place_oco_order.rb
CHANGED
|
@@ -15,6 +15,8 @@ Dotenv.load
|
|
|
15
15
|
#
|
|
16
16
|
|
|
17
17
|
# SchwabRb::Configuration.configure do |config|
|
|
18
|
+
# config.schwab_home = "/path/to/your/schwab_rb_home"
|
|
19
|
+
# config.log_level = "DEBUG"
|
|
18
20
|
# end
|
|
19
21
|
|
|
20
22
|
CURRENT_ACCT = "TRADING_BROKERAGE_ACCOUNT"
|
|
@@ -46,25 +48,30 @@ oco_order = SchwabRb::Orders::OrderFactory.build(
|
|
|
46
48
|
short_leg_symbol: "SPXW 251020P06530000",
|
|
47
49
|
long_leg_symbol: "SPXW 251020P06510000",
|
|
48
50
|
order_type: SchwabRb::Order::Types::STOP_LIMIT,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
price: 2.1,
|
|
52
|
+
stop_price: 2.0,
|
|
51
53
|
order_instruction: :close,
|
|
52
54
|
credit_debit: :debit,
|
|
53
|
-
quantity:
|
|
55
|
+
quantity: 2
|
|
54
56
|
},
|
|
55
57
|
{
|
|
56
58
|
strategy_type: SchwabRb::Order::ComplexOrderStrategyTypes::VERTICAL,
|
|
57
59
|
short_leg_symbol: "SPXW 251020C06770000",
|
|
58
60
|
long_leg_symbol: "SPXW 251020C06790000",
|
|
59
61
|
order_type: SchwabRb::Order::Types::STOP_LIMIT,
|
|
60
|
-
price:
|
|
62
|
+
price: 2.1,
|
|
63
|
+
stop_price: 2.0,
|
|
61
64
|
order_instruction: :close,
|
|
62
65
|
credit_debit: :debit,
|
|
63
|
-
quantity:
|
|
66
|
+
quantity: 2
|
|
64
67
|
}
|
|
65
68
|
]
|
|
66
69
|
)
|
|
67
70
|
|
|
68
71
|
built_order = oco_order.build
|
|
69
72
|
|
|
70
|
-
|
|
73
|
+
binding.pry
|
|
74
|
+
|
|
75
|
+
# response = client.place_order(built_order, account_name: CURRENT_ACCT)
|
|
76
|
+
|
|
77
|
+
binding.pry
|
data/exe/schwab_rb
ADDED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "oauth2"
|
|
4
4
|
require_relative "init_client_token_file"
|
|
5
5
|
require_relative "init_client_login"
|
|
6
|
+
require_relative "../path_support"
|
|
6
7
|
|
|
7
8
|
module SchwabRb
|
|
8
9
|
module Auth
|
|
@@ -17,6 +18,7 @@ module SchwabRb
|
|
|
17
18
|
interactive: true,
|
|
18
19
|
requested_browser: nil
|
|
19
20
|
)
|
|
21
|
+
token_path = SchwabRb::PathSupport.expand_path(token_path)
|
|
20
22
|
|
|
21
23
|
raise OAuth2::Error, "No token found" unless File.exist?(token_path)
|
|
22
24
|
|
|
@@ -5,6 +5,7 @@ require "uri"
|
|
|
5
5
|
require "net/http"
|
|
6
6
|
require "json"
|
|
7
7
|
require "oauth2"
|
|
8
|
+
require_relative "../path_support"
|
|
8
9
|
# require 'logger'
|
|
9
10
|
|
|
10
11
|
module SchwabRb
|
|
@@ -71,6 +72,7 @@ module SchwabRb
|
|
|
71
72
|
interactive: true,
|
|
72
73
|
requested_browser: nil
|
|
73
74
|
)
|
|
75
|
+
token_path = SchwabRb::PathSupport.expand_path(token_path)
|
|
74
76
|
|
|
75
77
|
callback_timeout = if !callback_timeout
|
|
76
78
|
0
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "oauth2"
|
|
4
|
+
require_relative "../path_support"
|
|
4
5
|
|
|
5
6
|
module SchwabRb
|
|
6
7
|
module Auth
|
|
7
8
|
def self.init_client_token_file(api_key, app_secret, token_path, enforce_enums: true)
|
|
9
|
+
token_path = SchwabRb::PathSupport.expand_path(token_path)
|
|
10
|
+
|
|
8
11
|
oauth = OAuth2::Client.new(
|
|
9
12
|
api_key,
|
|
10
13
|
app_secret,
|
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
require "oauth2"
|
|
4
4
|
require "json"
|
|
5
|
+
require_relative "../constants"
|
|
6
|
+
require_relative "../path_support"
|
|
5
7
|
|
|
6
8
|
module SchwabRb
|
|
7
9
|
module Auth
|
|
8
10
|
class TokenManager
|
|
9
11
|
class << self
|
|
10
12
|
def from_file(token_path)
|
|
13
|
+
token_path = SchwabRb::PathSupport.expand_path(token_path)
|
|
11
14
|
token_data = JSON.parse(File.read(token_path))
|
|
12
15
|
token = SchwabRb::Auth::Token.new(
|
|
13
16
|
token: token_data["token"]["access_token"],
|
|
@@ -40,7 +43,7 @@ module SchwabRb
|
|
|
40
43
|
def initialize(token, timestamp, token_path: SchwabRb::Constants::DEFAULT_TOKEN_PATH)
|
|
41
44
|
@token = token
|
|
42
45
|
@timestamp = timestamp
|
|
43
|
-
@token_path = token_path
|
|
46
|
+
@token_path = SchwabRb::PathSupport.expand_path(token_path)
|
|
44
47
|
end
|
|
45
48
|
|
|
46
49
|
attr_reader :token, :timestamp, :token_path
|
|
@@ -77,6 +80,7 @@ module SchwabRb
|
|
|
77
80
|
end
|
|
78
81
|
|
|
79
82
|
def to_file
|
|
83
|
+
SchwabRb::PathSupport.ensure_parent_directory(token_path)
|
|
80
84
|
File.write(token_path, to_json)
|
|
81
85
|
end
|
|
82
86
|
|