scottrade 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +1 -0
- data/examples/basics.rb +5 -5
- data/lib/scottrade/brokerage.rb +68 -68
- data/lib/scottrade/helpers.rb +17 -0
- data/lib/scottrade/position.rb +16 -4
- data/lib/scottrade/session.rb +22 -21
- data/lib/scottrade/version.rb +1 -1
- data/tests/unit/auth.rb +6 -10
- data/tests/unit/brokerage.rb +9 -10
- data/tests/unit/helpers.rb +8 -0
- metadata +4 -2
data/README.md
CHANGED
@@ -4,6 +4,7 @@ Very basic gem for accessing Scottrade account information including balances an
|
|
4
4
|
|
5
5
|
**This software comes with no warranty and you use it at your own risk.**
|
6
6
|
|
7
|
+
[![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/nolanbrown/scottrade)
|
7
8
|
|
8
9
|
## Installation
|
9
10
|
|
data/examples/basics.rb
CHANGED
@@ -12,14 +12,14 @@ rescue StandardError => e
|
|
12
12
|
exit 1
|
13
13
|
end
|
14
14
|
|
15
|
-
begin
|
15
|
+
#begin
|
16
16
|
scottrade.brokerage.update_accounts
|
17
17
|
scottrade.brokerage.update_positions
|
18
18
|
|
19
|
-
rescue StandardError => e
|
20
|
-
|
21
|
-
|
22
|
-
end
|
19
|
+
# rescue StandardError => e
|
20
|
+
# puts e
|
21
|
+
# exit 1
|
22
|
+
# end
|
23
23
|
|
24
24
|
puts scottrade.brokerage.account_balance
|
25
25
|
##
|
data/lib/scottrade/brokerage.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'money'
|
2
2
|
require_relative 'account'
|
3
3
|
require_relative 'position'
|
4
|
+
require_relative 'helpers'
|
4
5
|
|
5
6
|
module Scottrade
|
6
7
|
class Brokerage
|
8
|
+
include Scottrade::Helpers
|
7
9
|
|
8
10
|
attr_reader :total_value, :total_cash_balance, :yesterday_total_cash_balance, :available_free_cash_blanace, :account_balance
|
9
11
|
attr_reader :total_market_value_with_options, :total_market_value_without_options, :yesterday_total_market_value
|
@@ -12,96 +14,94 @@ module Scottrade
|
|
12
14
|
attr_reader :accounts, :positions
|
13
15
|
attr_reader :current_market_value, :todays_percent_change, :todays_value_change
|
14
16
|
|
17
|
+
KEYS_TO_VARIABLES = {
|
18
|
+
# accounts data keys
|
19
|
+
"TotalAccountValue" => { :key => :total_value, :is_money => true },
|
20
|
+
"TotalMoneyBalance" => { :key => :total_cash_balance, :is_money => true },
|
21
|
+
"YesterdayTotalMoneyBalance" => { :key => :yesterday_total_cash_balance, :is_money => true },
|
22
|
+
"AvailableFreeCash" => { :key => :available_free_cash_blanace, :is_money => true },
|
23
|
+
"TotalMarketValueWithOptions" => { :key => :total_market_value_with_options, :is_money => true },
|
24
|
+
"TotalMarketValueNoOptions" => { :key => :total_market_value_without_options, :is_money => true },
|
25
|
+
"YesterdayTotalMarketValue" => { :key => :yesterday_total_market_value, :is_money => true },
|
26
|
+
"TotalSettledFunds" => { :key => :total_settled_funds, :is_money => true },
|
27
|
+
"TotalUnsettledSells" => { :key => :total_unsettled_sells, :is_money => true },
|
28
|
+
"FundsAvailableToBuyNonMarginables" => { :key => :funds_available_to_buy_non_marginables, :is_money => true },
|
29
|
+
"FundsAvailableToBuyMarginables" => { :key => :funds_available_to_buy_marginables, :is_money => true },
|
30
|
+
"FundsAvailableToBuyOptions" => { :key => :funds_available_to_buy_options, :is_money => true },
|
31
|
+
"FundsAvailableToBuyMutualFunds" => { :key => :funds_available_to_buy_mutual_funds, :is_money => true },
|
32
|
+
"FundsAvailableForWithdraw" => { :key => :funds_available_for_withdraw, :is_money => true },
|
33
|
+
"TodaysChangeApproxLiquidationValNoOptions" => { :key => :approximate_liquidation_value, :is_money => true },
|
34
|
+
"BrokerageAccountBalance" => { :key => :account_balance, :is_money => true },
|
35
|
+
# positions data keys
|
36
|
+
"totalMktValue" => { :key => :current_market_value, :is_money => false },
|
37
|
+
"totalPctChange" => { :key => :todays_percent_change, :is_money => false },
|
38
|
+
"toalPriceChange" => { :key => :todays_value_change, :is_money => false },
|
39
|
+
}
|
40
|
+
|
41
|
+
|
15
42
|
def initialize(session)
|
16
43
|
@session = session
|
17
44
|
end
|
18
45
|
|
19
|
-
# def positions
|
20
|
-
#
|
21
|
-
# end
|
22
|
-
# def accounts
|
23
|
-
# end
|
24
|
-
|
25
46
|
def update_accounts
|
26
|
-
params =
|
27
|
-
params["channel"] = "rc"
|
28
|
-
params["appID"] = "Scottrade"
|
29
|
-
params["rcid"] = "iPhone"
|
30
|
-
params["cacheid"] = ""
|
31
|
-
params["platform"] = "iPhone"
|
32
|
-
params["appver"] = "1.1.4"
|
33
|
-
params["useCachedData"] = "false"
|
47
|
+
params = request_parameters("GetFrontEndMoneyBalances")
|
34
48
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
@
|
42
|
-
|
43
|
-
|
44
|
-
@total_market_value_with_options = Money.parse parsed_response["TotalMarketValueWithOptions"]
|
45
|
-
@total_market_value_without_options = Money.parse parsed_response["TotalMarketValueNoOptions"]
|
46
|
-
@yesterday_total_market_value = Money.parse parsed_response["YesterdayTotalMarketValue"]
|
47
|
-
@total_settled_funds = Money.parse parsed_response["TotalSettledFunds"]
|
48
|
-
@total_unsettled_sells = Money.parse parsed_response["TotalUnsettledSells"]
|
49
|
-
@funds_available_to_buy_non_marginables = Money.parse parsed_response["FundsAvailableToBuyNonMarginables"]
|
50
|
-
@funds_available_to_buy_marginables = Money.parse parsed_response["FundsAvailableToBuyMarginables"]
|
51
|
-
@funds_available_to_buy_options = Money.parse parsed_response["FundsAvailableToBuyOptions"]
|
52
|
-
@funds_available_to_buy_mutual_funds = Money.parse parsed_response["FundsAvailableToBuyMutualFunds"]
|
53
|
-
@funds_available_for_withdraw = Money.parse parsed_response["FundsAvailableForWithdraw"]
|
54
|
-
@approximate_liquidation_value = Money.parse parsed_response["TodaysChangeApproxLiquidationValNoOptions"]
|
55
|
-
@account_balance = Money.parse parsed_response["BrokerageAccountBalance"]
|
56
|
-
|
57
|
-
@accounts = []
|
58
|
-
unparsed_accounts = parsed_response["AccTypeBalances"]
|
59
|
-
unparsed_accounts.each{|acct|
|
60
|
-
@accounts.push Account.new(acct)
|
61
|
-
}
|
62
|
-
|
63
|
-
elsif parsed_response["msg"]
|
64
|
-
raise RequestError, parsed_response["msg"]
|
65
|
-
else
|
66
|
-
raise RequestError
|
67
|
-
end
|
49
|
+
response = session_post(params)
|
50
|
+
set_variables_from_response(response)
|
51
|
+
|
52
|
+
@accounts = []
|
53
|
+
unparsed_accounts = response["AccTypeBalances"]
|
54
|
+
unparsed_accounts.each{|acct|
|
55
|
+
@accounts.push Account.new(acct)
|
56
|
+
}
|
57
|
+
|
68
58
|
end
|
69
59
|
|
70
60
|
def update_positions
|
71
|
-
params =
|
72
|
-
params["channel"] = "rc"
|
73
|
-
params["appID"] = "Scottrade"
|
74
|
-
params["rcid"] = "iPhone"
|
75
|
-
params["cacheid"] = ""
|
76
|
-
params["platform"] = "iPhone"
|
77
|
-
params["appver"] = "1.1.4"
|
78
|
-
|
79
|
-
params["useCachedData"] = "false"
|
61
|
+
params = request_parameters("GetPositions_v2")
|
80
62
|
params["startRow"] = "0"
|
81
63
|
params["noOfRows"] = "1000"
|
82
64
|
params["returnRealTimeMktValue"] = "true"
|
83
65
|
|
84
66
|
params["serviceID"] = "GetPositions_v2"
|
85
|
-
|
67
|
+
|
68
|
+
response = session_post(params)
|
69
|
+
|
70
|
+
set_variables_from_response(response)
|
71
|
+
|
72
|
+
@positions = []
|
73
|
+
all_positions = response["Positions"]
|
74
|
+
all_positions.each{|pos|
|
75
|
+
@positions.push Position.new(pos)
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
def session_post(params)
|
86
81
|
response = @session.post(params)
|
87
82
|
parsed_response = JSON.parse(response.body)
|
88
83
|
if parsed_response["error"] == "false"
|
89
|
-
|
90
|
-
@current_market_value = parsed_response["totalMktValue"]
|
91
|
-
@todays_percent_change = parsed_response["totalPctChange"]
|
92
|
-
@todays_value_change = parsed_response["toalPriceChange"]
|
93
|
-
|
94
|
-
@positions = []
|
95
|
-
all_positions = parsed_response["Positions"]
|
96
|
-
all_positions.each{|pos|
|
97
|
-
@positions.push Position.new(pos)
|
98
|
-
}
|
99
|
-
|
84
|
+
return parsed_response
|
100
85
|
elsif parsed_response["msg"]
|
101
86
|
raise RequestError, parsed_response["msg"]
|
102
87
|
else
|
103
88
|
raise RequestError
|
104
89
|
end
|
105
90
|
end
|
91
|
+
|
92
|
+
def set_variables_from_response(response)
|
93
|
+
response.each{|key,value|
|
94
|
+
settings = KEYS_TO_VARIABLES[key]
|
95
|
+
if settings
|
96
|
+
if settings[:is_money]
|
97
|
+
begin
|
98
|
+
value = Money.parse value
|
99
|
+
rescue
|
100
|
+
end
|
101
|
+
end
|
102
|
+
instance_variable_set("@#{settings[:key].to_s}", value)
|
103
|
+
end
|
104
|
+
}
|
105
|
+
end
|
106
106
|
end
|
107
107
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
module Scottrade
|
3
|
+
module Helpers
|
4
|
+
def request_parameters(serviceID)
|
5
|
+
params = {}
|
6
|
+
params["channel"] = "rc"
|
7
|
+
params["appID"] = "Scottrade"
|
8
|
+
params["rcid"] = "iPhone"
|
9
|
+
params["cacheid"] = ""
|
10
|
+
params["platform"] = "iPhone"
|
11
|
+
params["appver"] = "1.1.4"
|
12
|
+
params["useCachedData"] = "false"
|
13
|
+
params["serviceID"] = serviceID
|
14
|
+
params
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/scottrade/position.rb
CHANGED
@@ -4,19 +4,31 @@ module Scottrade
|
|
4
4
|
attr_reader :symbol, :display_symbol, :quantity, :previous_market_close_value, :account_type, :cusip, :security_description
|
5
5
|
attr_reader :security_class, :previous_close_price, :realTimePrice, :real_time_price, :real_time_market_value, :price_change
|
6
6
|
|
7
|
-
def initialize(details)
|
7
|
+
def initialize(details)
|
8
8
|
@symbol = details["symbol"]
|
9
9
|
@display_symbol = details["displaySymbol"]
|
10
10
|
@quantity = details["quantity"]
|
11
|
-
@previous_market_close_value = Money.parse(details["prevCloseMktValue"])
|
12
11
|
@account_type = details["accType"]
|
13
12
|
@cusip = details["cusip"]
|
14
13
|
@security_description = details["securityDescription"]
|
15
14
|
@security_class = details["SecurityClass"]
|
16
15
|
@previous_close_price = details["previousClosePrice"]
|
17
16
|
@real_time_price = details["realTimePrice"]
|
18
|
-
|
19
|
-
|
17
|
+
begin
|
18
|
+
@price_change = Money.parse details["priceChange"].split("\n")[0]
|
19
|
+
rescue
|
20
|
+
@price_change = Money.parse("$0.00")
|
21
|
+
end
|
22
|
+
begin
|
23
|
+
@previous_market_close_value = Money.parse(details["prevCloseMktValue"])
|
24
|
+
rescue
|
25
|
+
@previous_market_close_value = Money.parse("$0.00")
|
26
|
+
end
|
27
|
+
begin
|
28
|
+
@real_time_market_value = Money.parse(details["RealTimeMktValue"])
|
29
|
+
rescue
|
30
|
+
@previous_market_close_value = Money.parse("$0.00")
|
31
|
+
end
|
20
32
|
end
|
21
33
|
end
|
22
34
|
end
|
data/lib/scottrade/session.rb
CHANGED
@@ -2,9 +2,11 @@ require 'json'
|
|
2
2
|
|
3
3
|
require_relative 'base'
|
4
4
|
require_relative 'error'
|
5
|
+
require_relative 'helpers'
|
5
6
|
|
6
7
|
module Scottrade
|
7
8
|
class Session < Base
|
9
|
+
include Scottrade::Helpers
|
8
10
|
|
9
11
|
attr_reader :encrypted_id, :mask_id
|
10
12
|
|
@@ -17,46 +19,45 @@ module Scottrade
|
|
17
19
|
return (@cookies != nil)
|
18
20
|
end
|
19
21
|
def authenticate
|
20
|
-
params =
|
21
|
-
params["appID"] = "Scottrade"
|
22
|
+
params = request_parameters("VerifyLogin")
|
22
23
|
params["appName"] = "ScottradeMobileApplication"
|
23
|
-
params["rcid"] = "iPhone"
|
24
|
-
params["osName"] = "iPhone"
|
25
|
-
params["platform"] = "iPhone"
|
26
|
-
params["cacheid"] = ""
|
27
|
-
params["osVer"] = "6"
|
28
|
-
params["appver"] = "1.1.4"
|
29
24
|
params["appVer"] = "1.1.4"
|
30
25
|
params["isRemAcc"] = "true"
|
31
26
|
params["page"] = "LogIn"
|
32
|
-
params["serviceID"] = "VerifyLogin"
|
33
|
-
params["channel"] = "rc"
|
34
27
|
params["langId"] = "English"
|
35
28
|
|
36
29
|
params["acc"] = @account
|
37
30
|
params["pwd"] = @password
|
38
31
|
params["isEncrypted"] = "false"
|
39
32
|
|
40
|
-
response =
|
33
|
+
response = authentication_post(params)
|
41
34
|
all_cookies = response.get_fields('set-cookie') # only cookies are set on valid credentials
|
42
35
|
parsed_response = JSON.parse(response.body)
|
36
|
+
|
37
|
+
@cookies = []
|
38
|
+
all_cookies.each { | cookie |
|
39
|
+
@cookies.push(cookie.split('; ')[0])
|
40
|
+
}
|
41
|
+
@encrypted_dd = parsed_response["encryptedId"]
|
42
|
+
@mask_id = parsed_response["maskId"]
|
43
|
+
|
44
|
+
return self
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def authentication_post(params)
|
49
|
+
response = post(params)
|
50
|
+
parsed_response = JSON.parse(response.body)
|
43
51
|
if parsed_response["error"] == "false" and !parsed_response.has_key?("errmsg")
|
44
|
-
|
45
|
-
all_cookies.each { | cookie |
|
46
|
-
cookies.push(cookie.split('; ')[0])
|
47
|
-
}
|
48
|
-
@cookies = cookies
|
49
|
-
@encrypted_dd = parsed_response["encryptedId"]
|
50
|
-
@mask_id = parsed_response["maskId"]
|
51
|
-
|
52
|
-
return self
|
52
|
+
return response
|
53
53
|
elsif parsed_response["msg"]
|
54
54
|
raise AuthenticationError, parsed_response["msg"]
|
55
55
|
elsif parsed_response["errmsg"]
|
56
56
|
raise AuthenticationError, parsed_response["errmsg"]
|
57
57
|
else
|
58
58
|
raise AuthenticationError
|
59
|
-
end
|
59
|
+
end
|
60
60
|
end
|
61
|
+
|
61
62
|
end
|
62
63
|
end
|
data/lib/scottrade/version.rb
CHANGED
data/tests/unit/auth.rb
CHANGED
@@ -1,20 +1,16 @@
|
|
1
1
|
require 'test/unit'
|
2
2
|
require 'rack/test'
|
3
|
-
|
3
|
+
require_relative '../../lib/scottrade'
|
4
|
+
require_relative 'helpers'
|
4
5
|
|
5
6
|
class AuthenticationTest < Test::Unit::TestCase
|
6
7
|
include Rack::Test::Methods
|
7
|
-
|
8
|
+
include ScottradeUnitTestHelpers
|
9
|
+
|
8
10
|
def test_positive_authentication
|
9
|
-
|
10
|
-
assert_nothing_raised Scottrade::AuthenticationError do
|
11
|
-
scottrade.authenticate
|
12
|
-
end
|
11
|
+
authenticate(ENV["SCOTTRADE_ACCOUNT"],ENV["SCOTTRADE_PASSWORD"])
|
13
12
|
end
|
14
13
|
def test_negative_authentication
|
15
|
-
|
16
|
-
assert_raise Scottrade::AuthenticationError do
|
17
|
-
scottrade.authenticate
|
18
|
-
end
|
14
|
+
authenticate("555555555","password")
|
19
15
|
end
|
20
16
|
end
|
data/tests/unit/brokerage.rb
CHANGED
@@ -1,25 +1,24 @@
|
|
1
1
|
require 'test/unit'
|
2
2
|
require 'rack/test'
|
3
|
-
|
3
|
+
require_relative 'helpers'
|
4
|
+
require_relative '../../lib/scottrade'
|
5
|
+
|
4
6
|
class AuthenticationTest < Test::Unit::TestCase
|
5
7
|
include Rack::Test::Methods
|
6
|
-
|
8
|
+
include ScottradeUnitTestHelpers
|
7
9
|
def test_brokerage_balances
|
8
|
-
|
9
|
-
|
10
|
-
scottrade.authenticate
|
11
|
-
end
|
10
|
+
authenticate(ENV["SCOTTRADE_ACCOUNT"],ENV["SCOTTRADE_PASSWORD"])
|
11
|
+
|
12
12
|
assert_nothing_raised Scottrade::RequestError do
|
13
13
|
scottrade.brokerage.update_accounts
|
14
14
|
end
|
15
15
|
end
|
16
16
|
def test_brokerage_positions
|
17
|
-
|
18
|
-
|
19
|
-
scottrade.authenticate
|
20
|
-
end
|
17
|
+
authenticate(ENV["SCOTTRADE_ACCOUNT"],ENV["SCOTTRADE_PASSWORD"])
|
18
|
+
|
21
19
|
assert_nothing_raised Scottrade::RequestError do
|
22
20
|
scottrade.brokerage.update_positions
|
23
21
|
end
|
24
22
|
end
|
23
|
+
|
25
24
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scottrade
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-01-
|
12
|
+
date: 2013-01-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: money
|
@@ -45,6 +45,7 @@ files:
|
|
45
45
|
- lib/scottrade/base.rb
|
46
46
|
- lib/scottrade/brokerage.rb
|
47
47
|
- lib/scottrade/error.rb
|
48
|
+
- lib/scottrade/helpers.rb
|
48
49
|
- lib/scottrade/position.rb
|
49
50
|
- lib/scottrade/quote.rb
|
50
51
|
- lib/scottrade/session.rb
|
@@ -52,6 +53,7 @@ files:
|
|
52
53
|
- scottrade.gemspec
|
53
54
|
- tests/unit/auth.rb
|
54
55
|
- tests/unit/brokerage.rb
|
56
|
+
- tests/unit/helpers.rb
|
55
57
|
homepage: https://github.com/nolanbrown/scottrade
|
56
58
|
licenses: []
|
57
59
|
post_install_message:
|