questrade_api 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +5 -5
- data/lib/questrade_api/client.rb +2 -0
- data/lib/questrade_api/modules/market_call.rb +13 -0
- data/lib/questrade_api/modules/order_call.rb +9 -0
- data/lib/questrade_api/rest/account.rb +1 -1
- data/lib/questrade_api/rest/base.rb +12 -1
- data/lib/questrade_api/rest/execution.rb +0 -2
- data/lib/questrade_api/rest/option_quote.rb +45 -0
- data/lib/questrade_api/rest/order.rb +44 -11
- data/lib/questrade_api/rest/position.rb +0 -2
- data/lib/questrade_api/rest/quote.rb +0 -2
- data/lib/questrade_api/rest/strategy_quote.rb +45 -0
- data/lib/questrade_api/rest/symbol.rb +22 -30
- data/lib/questrade_api/rest/time.rb +0 -2
- data/lib/questrade_api/version.rb +1 -1
- data/spec/fixtures/json/create_update_order.json +50 -0
- data/spec/fixtures/json/option_quotes.json +33 -0
- data/spec/fixtures/json/strategy_quotes.json +19 -0
- data/spec/questrade_api/modules/market_call_spec.rb +14 -0
- data/spec/questrade_api/rest/option_quote_spec.rb +81 -0
- data/spec/questrade_api/rest/order_spec.rb +146 -0
- data/spec/questrade_api/rest/strategy_quote_spec.rb +75 -0
- metadata +15 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e8141f3ee50e781f3ea23d1701d46e77c6ce6201
|
4
|
+
data.tar.gz: 3002e00f9d7ab48cc1a0c575c1a54ff7ae4f30ee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4d711919248d827c1d06287dfc53995c158f463e612d1d64d1a1c798bbbec8f388724eb8b89435c6640276e85acea3280938ceba6c96888be2a180965a16a1be
|
7
|
+
data.tar.gz: c9cba6dd7b09f80af88519766594862a118cd64bf5bdc6f4bc2d14a9111c1add1125caf4a8adc808e1e7b10f3c973f2d3142a2e3f5c7e5a1eff8bf8899a82998
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -83,9 +83,9 @@ client.candles('123', startTime: DateTime.yesterday.to_s, endTime: DateTime.now.
|
|
83
83
|
# In case you already have a valid access token and its respective URL, you can use the QuestradeApi::REST objects. Example:
|
84
84
|
# authorization can be any object that responds to url and access_token
|
85
85
|
authorization = QuestradeApi::Authorization.new(access_token: 'access_token', api_server: 'url')
|
86
|
-
accounts = QuestradeApi::REST::Account.fetch(
|
86
|
+
accounts = QuestradeApi::REST::Account.fetch(authorization)
|
87
87
|
```
|
88
|
-
For more advanced options, check out our [documentation](http://www.rubydoc.info/gems/questrade_api/1.
|
88
|
+
For more advanced options, check out our [documentation](http://www.rubydoc.info/gems/questrade_api/1.1.0).
|
89
89
|
|
90
90
|
## Current Status
|
91
91
|
|
@@ -109,14 +109,14 @@ Check the tables below for more details.
|
|
109
109
|
| Endpoint | Development | Documentation |
|
110
110
|
| --- | --- | --- |
|
111
111
|
| /symbols/ | DONE | |
|
112
|
-
| /symbols/:id |
|
112
|
+
| /symbols/:id | DONE | |
|
113
113
|
| /symbols/search | DONE | |
|
114
114
|
| /symbols/:id/options | DONE | |
|
115
115
|
| /markets | DONE | |
|
116
116
|
| /markets/quotes/ | DONE | |
|
117
117
|
| /markets/quotes/:id | DONE | |
|
118
|
-
| /markets/quotes/options |
|
119
|
-
| /markets/quotes/strategies |
|
118
|
+
| /markets/quotes/options | DONE | |
|
119
|
+
| /markets/quotes/strategies | DONE | |
|
120
120
|
| /markets/candles/:id | DONE | |
|
121
121
|
|
122
122
|
### Order Calls
|
data/lib/questrade_api/client.rb
CHANGED
@@ -2,12 +2,14 @@ require 'questrade_api/authorization'
|
|
2
2
|
|
3
3
|
require 'questrade_api/modules/account_call'
|
4
4
|
require 'questrade_api/modules/market_call'
|
5
|
+
require 'questrade_api/modules/order_call'
|
5
6
|
|
6
7
|
module QuestradeApi
|
7
8
|
# @author Bruno Meira <goesmeira@gmail.com>
|
8
9
|
class Client
|
9
10
|
include QuestradeApi::AccountCall
|
10
11
|
include QuestradeApi::MarketCall
|
12
|
+
include QuestradeApi::OrderCall
|
11
13
|
|
12
14
|
attr_accessor :authorization
|
13
15
|
|
@@ -3,6 +3,8 @@ require 'questrade_api/rest/symbol'
|
|
3
3
|
require 'questrade_api/rest/quote'
|
4
4
|
require 'questrade_api/rest/candle'
|
5
5
|
require 'questrade_api/rest/option'
|
6
|
+
require 'questrade_api/rest/option_quote'
|
7
|
+
require 'questrade_api/rest/strategy_quote'
|
6
8
|
|
7
9
|
module QuestradeApi
|
8
10
|
module MarketCall
|
@@ -38,6 +40,17 @@ module QuestradeApi
|
|
38
40
|
quote
|
39
41
|
end
|
40
42
|
|
43
|
+
def quote_options(filters, option_ids)
|
44
|
+
QuestradeApi::REST::OptionQuote.fetch(authorization,
|
45
|
+
filters: filters,
|
46
|
+
optionIds: option_ids)
|
47
|
+
end
|
48
|
+
|
49
|
+
def quote_strategies(variants)
|
50
|
+
QuestradeApi::REST::StrategyQuote.fetch(authorization,
|
51
|
+
variants: variants)
|
52
|
+
end
|
53
|
+
|
41
54
|
def candles(symbol_id, params)
|
42
55
|
QuestradeApi::REST::Candle.fetch(authorization, symbol_id, params)
|
43
56
|
end
|
@@ -10,7 +10,8 @@ module QuestradeApi
|
|
10
10
|
|
11
11
|
BASE_ENDPOINT = '/v1'.freeze
|
12
12
|
attr_accessor :connection, :raw_body, :endpoint,
|
13
|
-
:authorization, :data
|
13
|
+
:authorization, :data, :account_id, :id
|
14
|
+
|
14
15
|
|
15
16
|
# Initialize an instance of QuestradeApi::REST::Base
|
16
17
|
#
|
@@ -41,6 +42,7 @@ module QuestradeApi
|
|
41
42
|
# faraday.response :logger
|
42
43
|
faraday.adapter Faraday.default_adapter
|
43
44
|
faraday.headers['Content-Type'] = 'application/json'
|
45
|
+
faraday.headers['User-Agent'] = "QuestradeApi v#{QuestradeApi::VERSION}"
|
44
46
|
faraday.headers['Authorization'] = "Bearer #{params[:access_token]}"
|
45
47
|
end
|
46
48
|
end
|
@@ -82,6 +84,15 @@ module QuestradeApi
|
|
82
84
|
end
|
83
85
|
end
|
84
86
|
end
|
87
|
+
|
88
|
+
def post(params = {})
|
89
|
+
connection = connection(params)
|
90
|
+
|
91
|
+
connection.post do |req|
|
92
|
+
req.path = params[:endpoint]
|
93
|
+
req.body = params[:body] if params[:body]
|
94
|
+
end
|
95
|
+
end
|
85
96
|
end
|
86
97
|
end
|
87
98
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'questrade_api/rest/base'
|
2
|
+
|
3
|
+
module QuestradeApi
|
4
|
+
module REST
|
5
|
+
class OptionQuote < QuestradeApi::REST::Base
|
6
|
+
def initialize(params)
|
7
|
+
@raw_body = params[:data]
|
8
|
+
build_data(params[:data]) if @raw_body
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.fetch(authorization, params)
|
12
|
+
response = post(access_token: authorization.access_token,
|
13
|
+
endpoint: endpoint,
|
14
|
+
url: authorization.url,
|
15
|
+
body: params.to_json)
|
16
|
+
|
17
|
+
if response.status == 200
|
18
|
+
result = OpenStruct.new(optionQuotes: [])
|
19
|
+
result.optionQuotes = parse_option_quotes(response.body)
|
20
|
+
response = result
|
21
|
+
end
|
22
|
+
|
23
|
+
response
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.endpoint
|
27
|
+
"#{BASE_ENDPOINT}/markets/quotes/options"
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.parse_option_quotes(body)
|
31
|
+
raw = JSON.parse(body)
|
32
|
+
|
33
|
+
options = []
|
34
|
+
|
35
|
+
raw['optionQuotes'].each do |option|
|
36
|
+
options << new(data: option)
|
37
|
+
end
|
38
|
+
|
39
|
+
options
|
40
|
+
end
|
41
|
+
|
42
|
+
private_class_method :parse_option_quotes
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -3,8 +3,6 @@ require 'questrade_api/rest/base'
|
|
3
3
|
module QuestradeApi
|
4
4
|
module REST
|
5
5
|
class Order < QuestradeApi::REST::Base
|
6
|
-
attr_accessor :account_id, :id
|
7
|
-
|
8
6
|
def initialize(authorization, params = {})
|
9
7
|
super(authorization)
|
10
8
|
|
@@ -15,20 +13,37 @@ module QuestradeApi
|
|
15
13
|
build_data(params[:data]) if @raw_body
|
16
14
|
end
|
17
15
|
|
18
|
-
def
|
16
|
+
def update(params = {})
|
17
|
+
params[:accountNumber] = account_id
|
18
|
+
|
19
|
+
response = self.class.post(access_token: authorization.access_token,
|
20
|
+
endpoint: endpoint,
|
21
|
+
url: authorization.url,
|
22
|
+
body: params.to_json)
|
23
|
+
|
24
|
+
parse_order(response.body) if response.status == 200
|
25
|
+
|
26
|
+
response
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.create(authorization, account_number, params = {})
|
30
|
+
params[:accountNumber] = account_number
|
31
|
+
|
32
|
+
response = post(access_token: authorization.access_token,
|
33
|
+
endpoint: endpoint(account_number),
|
34
|
+
url: authorization.url,
|
35
|
+
body: params.to_json)
|
19
36
|
|
37
|
+
build_orders(authorization, account_number, response)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.fetch(authorization, account_number, params)
|
20
41
|
response = super(access_token: authorization.access_token,
|
21
42
|
endpoint: endpoint(account_number),
|
22
43
|
url: authorization.url,
|
23
44
|
params: params)
|
24
45
|
|
25
|
-
|
26
|
-
|
27
|
-
if response.status == 200
|
28
|
-
result.orders = parse_orders(authorization, account_number, response.body)
|
29
|
-
end
|
30
|
-
|
31
|
-
result
|
46
|
+
build_orders(authorization, account_number, response)
|
32
47
|
end
|
33
48
|
|
34
49
|
def endpoint
|
@@ -39,6 +54,13 @@ module QuestradeApi
|
|
39
54
|
"#{BASE_ENDPOINT}/accounts/#{account_id}/orders"
|
40
55
|
end
|
41
56
|
|
57
|
+
private
|
58
|
+
|
59
|
+
def parse_order(body)
|
60
|
+
raw = JSON.parse(body)
|
61
|
+
build_data(raw['orders'].first) if raw['orders'].size > 0
|
62
|
+
end
|
63
|
+
|
42
64
|
def self.parse_orders(authorization, account_id, body)
|
43
65
|
raw = JSON.parse(body)
|
44
66
|
|
@@ -52,7 +74,18 @@ module QuestradeApi
|
|
52
74
|
orders
|
53
75
|
end
|
54
76
|
|
55
|
-
|
77
|
+
def self.build_orders(authorization, account_number, response)
|
78
|
+
result = response
|
79
|
+
|
80
|
+
if response.status == 200
|
81
|
+
result = OpenStruct.new(orders: [])
|
82
|
+
result.orders = parse_orders(authorization, account_number, response.body)
|
83
|
+
end
|
84
|
+
|
85
|
+
result
|
86
|
+
end
|
87
|
+
|
88
|
+
private_class_method :parse_orders, :build_orders
|
56
89
|
end
|
57
90
|
end
|
58
91
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'questrade_api/rest/base'
|
2
|
+
|
3
|
+
module QuestradeApi
|
4
|
+
module REST
|
5
|
+
class StrategyQuote < QuestradeApi::REST::Base
|
6
|
+
def initialize(params)
|
7
|
+
@raw_body = params[:data]
|
8
|
+
build_data(params[:data]) if @raw_body
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.fetch(authorization, params)
|
12
|
+
response = post(access_token: authorization.access_token,
|
13
|
+
endpoint: endpoint,
|
14
|
+
url: authorization.url,
|
15
|
+
body: params.to_json)
|
16
|
+
|
17
|
+
if response.status == 200
|
18
|
+
result = OpenStruct.new(strategyQuotes: [])
|
19
|
+
result.strategyQuotes = parse_strategy_quotes(response.body)
|
20
|
+
response = result
|
21
|
+
end
|
22
|
+
|
23
|
+
response
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.endpoint
|
27
|
+
"#{BASE_ENDPOINT}/markets/quotes/strategies"
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.parse_strategy_quotes(body)
|
31
|
+
raw = JSON.parse(body)
|
32
|
+
|
33
|
+
strategies = []
|
34
|
+
|
35
|
+
raw['strategyQuotes'].each do |strategy|
|
36
|
+
strategies << new(data: strategy)
|
37
|
+
end
|
38
|
+
|
39
|
+
strategies
|
40
|
+
end
|
41
|
+
|
42
|
+
private_class_method :parse_strategy_quotes
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -3,8 +3,6 @@ require 'questrade_api/rest/base'
|
|
3
3
|
module QuestradeApi
|
4
4
|
module REST
|
5
5
|
class Symbol < QuestradeApi::REST::Base
|
6
|
-
attr_accessor :id
|
7
|
-
|
8
6
|
def initialize(authorization, params = {})
|
9
7
|
super(authorization)
|
10
8
|
|
@@ -38,13 +36,7 @@ module QuestradeApi
|
|
38
36
|
url: authorization.url,
|
39
37
|
params: params)
|
40
38
|
|
41
|
-
|
42
|
-
result = OpenStruct.new(symbols: [])
|
43
|
-
result.symbols = parse_symbols(authorization, response.body)
|
44
|
-
response = result
|
45
|
-
end
|
46
|
-
|
47
|
-
response
|
39
|
+
build_symbols(authorization, response)
|
48
40
|
end
|
49
41
|
|
50
42
|
def self.fetch(authorization, params = {})
|
@@ -56,29 +48,39 @@ module QuestradeApi
|
|
56
48
|
url: authorization.url,
|
57
49
|
params: params)
|
58
50
|
|
51
|
+
build_symbols(authorization, response)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def parse_symbols(body)
|
57
|
+
raw = JSON.parse(body)
|
58
|
+
|
59
|
+
raw['symbols'].each do |symbol|
|
60
|
+
build_data(symbol)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.build_symbols(authorization, response)
|
65
|
+
result = response
|
66
|
+
|
59
67
|
if response.status == 200
|
60
68
|
result = OpenStruct.new(symbols: [])
|
61
69
|
result.symbols = parse_symbols(authorization, response.body)
|
62
|
-
response = result
|
63
70
|
end
|
64
71
|
|
65
|
-
|
72
|
+
result
|
66
73
|
end
|
67
74
|
|
68
|
-
# TODO: Review this later
|
69
75
|
def self.parse_symbols(authorization, body)
|
70
76
|
raw = JSON.parse(body)
|
71
77
|
|
72
78
|
symbols = []
|
73
79
|
|
74
|
-
|
75
|
-
raw['symbols'].each do |symbol|
|
76
|
-
symbols << new(authorization, id: symbol['symbolId'], data: symbol)
|
77
|
-
end
|
78
|
-
end
|
80
|
+
results = raw['symbols'] || raw['symbol']
|
79
81
|
|
80
|
-
if
|
81
|
-
|
82
|
+
if results
|
83
|
+
results.each do |symbol|
|
82
84
|
symbols << new(authorization, id: symbol['symbolId'], data: symbol)
|
83
85
|
end
|
84
86
|
end
|
@@ -86,17 +88,7 @@ module QuestradeApi
|
|
86
88
|
symbols
|
87
89
|
end
|
88
90
|
|
89
|
-
private_class_method :parse_symbols
|
90
|
-
|
91
|
-
private
|
92
|
-
|
93
|
-
def parse_symbols(body)
|
94
|
-
raw = JSON.parse(body)
|
95
|
-
|
96
|
-
raw['symbols'].each do |symbol|
|
97
|
-
build_data(symbol)
|
98
|
-
end
|
99
|
-
end
|
91
|
+
private_class_method :parse_symbols, :build_symbols
|
100
92
|
end
|
101
93
|
end
|
102
94
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
{
|
2
|
+
"orderId": 177106005,
|
3
|
+
"orders": [
|
4
|
+
{
|
5
|
+
"id": 177106005,
|
6
|
+
"symbol": "AAPL",
|
7
|
+
"symbolId": 8049,
|
8
|
+
"totalQuantity": 10,
|
9
|
+
"openQuantity": 10,
|
10
|
+
"filledQuantity": 0,
|
11
|
+
"canceledQuantity": 0,
|
12
|
+
"side": "Buy",
|
13
|
+
"orderType": "Limit",
|
14
|
+
"limitPrice": 537,
|
15
|
+
"stopPrice": null,
|
16
|
+
"isAllOrNone": true,
|
17
|
+
"isAnonymous": false,
|
18
|
+
"icebergQty": 1,
|
19
|
+
"minQuantity": null,
|
20
|
+
"avgExecPrice": null,
|
21
|
+
"lastExecPrice": null,
|
22
|
+
"source": "TradingAPI",
|
23
|
+
"timeInForce": "GoodTillCanceled",
|
24
|
+
"gtdDate": null,
|
25
|
+
"state": "Pending",
|
26
|
+
"clientReasonStr": "",
|
27
|
+
"chainId": 177106005,
|
28
|
+
"creationTime": "2014-10-24T17:48:20.546000-04:00",
|
29
|
+
"updateTime": "2014-10-24T17:48:20.876000-04:00",
|
30
|
+
"notes": "",
|
31
|
+
"primaryRoute": "LAMP",
|
32
|
+
"secondaryRoute": "AUTO",
|
33
|
+
"orderRoute": "LAMP",
|
34
|
+
"venueHoldingOrder": "",
|
35
|
+
"comissionCharged": 0,
|
36
|
+
"exchangeOrderId": "",
|
37
|
+
"isSignificantShareHolder": false,
|
38
|
+
"isInsider": false,
|
39
|
+
"isLimitOffsetInDollar": false,
|
40
|
+
"userId": 3000124,
|
41
|
+
"placementCommission": null,
|
42
|
+
"legs": [],
|
43
|
+
"strategyType": "SingleLeg",
|
44
|
+
"triggerStopPrice": null,
|
45
|
+
"orderGroupId": 0,
|
46
|
+
"orderClass": null,
|
47
|
+
"mainChainId": 0
|
48
|
+
}
|
49
|
+
]
|
50
|
+
}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
{
|
2
|
+
"optionQuotes": [
|
3
|
+
{
|
4
|
+
"underlying": "MSFT",
|
5
|
+
"underlyingId": 27426,
|
6
|
+
"symbol": "MSFT20Jan17C70.00",
|
7
|
+
"symbolId": 7413503,
|
8
|
+
"bidPrice": 4.90,
|
9
|
+
"bidSize": 0,
|
10
|
+
"askPrice": 4.95,
|
11
|
+
"askSize": 0,
|
12
|
+
"lastTradePriceTrHrs": 4.93,
|
13
|
+
"lastTradePrice": 4.93,
|
14
|
+
"lastTradeSize": 0,
|
15
|
+
"lastTradeTick": "Equal",
|
16
|
+
"lastTradeTime": "2015-08-17T00:00:00.000000-04:00",
|
17
|
+
"volume": 0,
|
18
|
+
"openPrice": 0,
|
19
|
+
"highPrice": 4.93,
|
20
|
+
"lowPrice": 0,
|
21
|
+
"volatility": 52.374257,
|
22
|
+
"delta": 0.06985,
|
23
|
+
"gamma": 0.01038,
|
24
|
+
"theta": -0.001406,
|
25
|
+
"vega": 0.074554,
|
26
|
+
"rho": 0.04153,
|
27
|
+
"openInterest": 2292,
|
28
|
+
"delay": 0,
|
29
|
+
"isHalted": false,
|
30
|
+
"VWAP": 0
|
31
|
+
}
|
32
|
+
]
|
33
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
{
|
2
|
+
"strategyQuotes": [
|
3
|
+
{
|
4
|
+
"variantId": 1,
|
5
|
+
"bidPrice": 27.2,
|
6
|
+
"askPrice": 27.23,
|
7
|
+
"underlying": "MSFT",
|
8
|
+
"underlyingId": 27426,
|
9
|
+
"openPrice": null,
|
10
|
+
"volatility": 0,
|
11
|
+
"delta": 1,
|
12
|
+
"gamma": 0,
|
13
|
+
"theta": 0,
|
14
|
+
"vega": 0,
|
15
|
+
"rho": 0,
|
16
|
+
"isRealTime": true
|
17
|
+
}
|
18
|
+
]
|
19
|
+
}
|
@@ -59,6 +59,20 @@ describe QuestradeApi::MarketCall do
|
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
+
context '.quote_options' do
|
63
|
+
it 'calls proper endpoint' do
|
64
|
+
expect(QuestradeApi::REST::OptionQuote).to receive(:fetch).and_return([])
|
65
|
+
expect(subject.quote_options([], [])).to eq([])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context '.quote_strategies' do
|
70
|
+
it 'calls proper endpoint' do
|
71
|
+
expect(QuestradeApi::REST::StrategyQuote).to receive(:fetch).and_return([])
|
72
|
+
expect(subject.quote_strategies([])).to eq([])
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
62
76
|
context '.candles' do
|
63
77
|
it 'calls proper endpoint' do
|
64
78
|
expect(QuestradeApi::REST::Candle).to receive(:fetch).and_return([])
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'questrade_api/rest/option_quote'
|
3
|
+
|
4
|
+
describe QuestradeApi::REST::OptionQuote do
|
5
|
+
include JSONFixtures
|
6
|
+
|
7
|
+
let(:access_token) { 'XXXX' }
|
8
|
+
let(:url) { 'http://test.com'}
|
9
|
+
let(:authorization) { OpenStruct.new(access_token: access_token, url: url) }
|
10
|
+
|
11
|
+
context '.fetch' do
|
12
|
+
it 'fetches option chain for an specific symbol' do
|
13
|
+
params = {
|
14
|
+
filters: [
|
15
|
+
{
|
16
|
+
optionType: "Call",
|
17
|
+
underlyingId: 27426,
|
18
|
+
expiryDate: "2017-01-20T00:00:00.000000-05:00",
|
19
|
+
minstrikePrice: 70,
|
20
|
+
maxstrikePrice: 80
|
21
|
+
}
|
22
|
+
],
|
23
|
+
optionIds: [ 9907637, 9907638 ]
|
24
|
+
}
|
25
|
+
|
26
|
+
headers = { 'Accept' => '*/*',
|
27
|
+
'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
|
28
|
+
'Authorization' => 'Bearer XXXX',
|
29
|
+
'Content-Type' => 'application/json',
|
30
|
+
'User-Agent' => "QuestradeApi v#{QuestradeApi::VERSION}" }
|
31
|
+
|
32
|
+
stub_request(:post, 'http://test.com/v1/markets/quotes/options')
|
33
|
+
.with(body: params.to_json, headers: headers)
|
34
|
+
.to_return(status: 200, body: json_string('option_quotes.json'))
|
35
|
+
|
36
|
+
response = QuestradeApi::REST::OptionQuote.fetch(authorization, params)
|
37
|
+
|
38
|
+
expect(response.optionQuotes.size).to be(1)
|
39
|
+
|
40
|
+
first_option = response.optionQuotes.first
|
41
|
+
expect(first_option.data.to_h).to eq(
|
42
|
+
{
|
43
|
+
underlying: "MSFT",
|
44
|
+
underlying_id: 27426,
|
45
|
+
symbol: "MSFT20Jan17C70.00",
|
46
|
+
symbol_id: 7413503,
|
47
|
+
bid_price: 4.90,
|
48
|
+
bid_size: 0,
|
49
|
+
ask_price: 4.95,
|
50
|
+
ask_size: 0,
|
51
|
+
last_trade_price_tr_hrs: 4.93,
|
52
|
+
last_trade_price: 4.93,
|
53
|
+
last_trade_size: 0,
|
54
|
+
last_trade_tick: "Equal",
|
55
|
+
last_trade_time: "2015-08-17T00:00:00.000000-04:00",
|
56
|
+
volume: 0,
|
57
|
+
open_price: 0,
|
58
|
+
high_price: 4.93,
|
59
|
+
low_price: 0,
|
60
|
+
volatility: 52.374257,
|
61
|
+
delta: 0.06985,
|
62
|
+
gamma: 0.01038,
|
63
|
+
theta: -0.001406,
|
64
|
+
vega: 0.074554,
|
65
|
+
rho: 0.04153,
|
66
|
+
open_interest: 2292,
|
67
|
+
delay: 0,
|
68
|
+
is_halted: false,
|
69
|
+
vwap: 0
|
70
|
+
}
|
71
|
+
)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context '.endpoint' do
|
76
|
+
it 'returns the right endpoint' do
|
77
|
+
url = "/v1/markets/quotes/options"
|
78
|
+
expect(QuestradeApi::REST::OptionQuote.endpoint).to eq(url)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -9,6 +9,152 @@ describe QuestradeApi::REST::Order do
|
|
9
9
|
let(:account_id) { '123456' }
|
10
10
|
let(:url) { 'http://test.com'}
|
11
11
|
let(:authorization) { OpenStruct.new(access_token: access_token, url: url) }
|
12
|
+
let(:params) {
|
13
|
+
{
|
14
|
+
accountNumber: account_id,
|
15
|
+
symbolId: 8049,
|
16
|
+
quantity: 10,
|
17
|
+
icebergQuantity: 1,
|
18
|
+
limitPrice: 537,
|
19
|
+
isAllOrNone: true,
|
20
|
+
isAnonymous: false,
|
21
|
+
orderType: "Limit",
|
22
|
+
timeInForce: "GoodTillCanceled",
|
23
|
+
action: "Buy",
|
24
|
+
primaryRoute: "AUTO",
|
25
|
+
secondaryRoute: "AUTO"
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
let(:headers) {
|
30
|
+
{ 'Accept' => '*/*',
|
31
|
+
'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
|
32
|
+
'Authorization' => 'Bearer XXXX',
|
33
|
+
'Content-Type' => 'application/json',
|
34
|
+
'User-Agent' => "QuestradeApi v#{QuestradeApi::VERSION}" }
|
35
|
+
}
|
36
|
+
|
37
|
+
context '#update' do
|
38
|
+
let(:id) { 177106005 }
|
39
|
+
subject { QuestradeApi::REST::Order.new(authorization,
|
40
|
+
id: id, account_id: account_id ) }
|
41
|
+
|
42
|
+
it 'updates an existing order' do
|
43
|
+
url = "http://test.com/v1/accounts/#{account_id}/orders/#{id}"
|
44
|
+
stub_request(:post, url)
|
45
|
+
.with(body: params.to_json, headers: headers)
|
46
|
+
.to_return(status: 200, body: json_string('create_update_order.json'))
|
47
|
+
|
48
|
+
subject.update(params)
|
49
|
+
|
50
|
+
expect(subject.data.to_h).to eq(
|
51
|
+
id: 177106005,
|
52
|
+
symbol: "AAPL",
|
53
|
+
symbol_id: 8049,
|
54
|
+
total_quantity: 10,
|
55
|
+
open_quantity: 10,
|
56
|
+
filled_quantity: 0,
|
57
|
+
canceled_quantity: 0,
|
58
|
+
side: "Buy",
|
59
|
+
order_type: "Limit",
|
60
|
+
limit_price: 537,
|
61
|
+
stop_price: nil,
|
62
|
+
is_all_or_none: true,
|
63
|
+
is_anonymous: false,
|
64
|
+
iceberg_qty: 1,
|
65
|
+
min_quantity: nil,
|
66
|
+
avg_exec_price: nil,
|
67
|
+
last_exec_price: nil,
|
68
|
+
source: "TradingAPI",
|
69
|
+
time_in_force: "GoodTillCanceled",
|
70
|
+
gtd_date: nil,
|
71
|
+
state: "Pending",
|
72
|
+
client_reason_str: "",
|
73
|
+
chain_id: 177106005,
|
74
|
+
creation_time: "2014-10-24T17:48:20.546000-04:00",
|
75
|
+
update_time: "2014-10-24T17:48:20.876000-04:00",
|
76
|
+
notes: "",
|
77
|
+
primary_route: "LAMP",
|
78
|
+
secondary_route: "AUTO",
|
79
|
+
order_route: "LAMP",
|
80
|
+
venue_holding_order: "",
|
81
|
+
comission_charged: 0,
|
82
|
+
exchange_order_id: "",
|
83
|
+
is_significant_share_holder: false,
|
84
|
+
is_insider: false,
|
85
|
+
is_limit_offset_in_dollar: false,
|
86
|
+
user_id: 3000124,
|
87
|
+
placement_commission: nil,
|
88
|
+
legs: [],
|
89
|
+
strategy_type: "SingleLeg",
|
90
|
+
trigger_stop_price: nil,
|
91
|
+
order_group_id: 0,
|
92
|
+
order_class: nil,
|
93
|
+
main_chain_id: 0
|
94
|
+
)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context '.create' do
|
99
|
+
it "places an order" do
|
100
|
+
url = "http://test.com/v1/accounts/#{account_id}/orders"
|
101
|
+
stub_request(:post, url)
|
102
|
+
.with(body: params.to_json, headers: headers)
|
103
|
+
.to_return(status: 200, body: json_string('create_update_order.json'))
|
104
|
+
|
105
|
+
response = QuestradeApi::REST::Order.create(authorization, account_id, params)
|
106
|
+
|
107
|
+
expect(response.orders.size).to be(1)
|
108
|
+
|
109
|
+
first_order = response.orders.first
|
110
|
+
expect(first_order.account_id).to eq(account_id)
|
111
|
+
expect(first_order.data.to_h).to eq(
|
112
|
+
id: 177106005,
|
113
|
+
symbol: "AAPL",
|
114
|
+
symbol_id: 8049,
|
115
|
+
total_quantity: 10,
|
116
|
+
open_quantity: 10,
|
117
|
+
filled_quantity: 0,
|
118
|
+
canceled_quantity: 0,
|
119
|
+
side: "Buy",
|
120
|
+
order_type: "Limit",
|
121
|
+
limit_price: 537,
|
122
|
+
stop_price: nil,
|
123
|
+
is_all_or_none: true,
|
124
|
+
is_anonymous: false,
|
125
|
+
iceberg_qty: 1,
|
126
|
+
min_quantity: nil,
|
127
|
+
avg_exec_price: nil,
|
128
|
+
last_exec_price: nil,
|
129
|
+
source: "TradingAPI",
|
130
|
+
time_in_force: "GoodTillCanceled",
|
131
|
+
gtd_date: nil,
|
132
|
+
state: "Pending",
|
133
|
+
client_reason_str: "",
|
134
|
+
chain_id: 177106005,
|
135
|
+
creation_time: "2014-10-24T17:48:20.546000-04:00",
|
136
|
+
update_time: "2014-10-24T17:48:20.876000-04:00",
|
137
|
+
notes: "",
|
138
|
+
primary_route: "LAMP",
|
139
|
+
secondary_route: "AUTO",
|
140
|
+
order_route: "LAMP",
|
141
|
+
venue_holding_order: "",
|
142
|
+
comission_charged: 0,
|
143
|
+
exchange_order_id: "",
|
144
|
+
is_significant_share_holder: false,
|
145
|
+
is_insider: false,
|
146
|
+
is_limit_offset_in_dollar: false,
|
147
|
+
user_id: 3000124,
|
148
|
+
placement_commission: nil,
|
149
|
+
legs: [],
|
150
|
+
strategy_type: "SingleLeg",
|
151
|
+
trigger_stop_price: nil,
|
152
|
+
order_group_id: 0,
|
153
|
+
order_class: nil,
|
154
|
+
main_chain_id: 0
|
155
|
+
)
|
156
|
+
end
|
157
|
+
end
|
12
158
|
|
13
159
|
context '.fetch' do
|
14
160
|
it "returns an object that contains a list of all user's orders" do
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'questrade_api/rest/strategy_quote'
|
3
|
+
|
4
|
+
describe QuestradeApi::REST::StrategyQuote do
|
5
|
+
include JSONFixtures
|
6
|
+
|
7
|
+
let(:access_token) { 'XXXX' }
|
8
|
+
let(:url) { 'http://test.com'}
|
9
|
+
let(:authorization) { OpenStruct.new(access_token: access_token, url: url) }
|
10
|
+
|
11
|
+
context '.fetch' do
|
12
|
+
it 'fetches strategy chain for an specific symbol' do
|
13
|
+
params = {
|
14
|
+
variants: [
|
15
|
+
{
|
16
|
+
variantId: 1,
|
17
|
+
strategy: "Custom",
|
18
|
+
legs: [
|
19
|
+
{
|
20
|
+
symbolId: 27426,
|
21
|
+
ratio: 1000,
|
22
|
+
action: "Buy"
|
23
|
+
},
|
24
|
+
{
|
25
|
+
symbolId: 10550014,
|
26
|
+
ratio: 10,
|
27
|
+
action: "Sell"
|
28
|
+
}
|
29
|
+
]
|
30
|
+
}
|
31
|
+
]
|
32
|
+
}
|
33
|
+
|
34
|
+
headers = { 'Accept' => '*/*',
|
35
|
+
'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
|
36
|
+
'Authorization' => 'Bearer XXXX',
|
37
|
+
'Content-Type' => 'application/json',
|
38
|
+
'User-Agent' => "QuestradeApi v#{QuestradeApi::VERSION}" }
|
39
|
+
|
40
|
+
stub_request(:post, 'http://test.com/v1/markets/quotes/strategies')
|
41
|
+
.with(body: params.to_json, headers: headers)
|
42
|
+
.to_return(status: 200, body: json_string('strategy_quotes.json'))
|
43
|
+
|
44
|
+
response = QuestradeApi::REST::StrategyQuote.fetch(authorization, params)
|
45
|
+
|
46
|
+
expect(response.strategyQuotes.size).to be(1)
|
47
|
+
|
48
|
+
first_strategy = response.strategyQuotes.first
|
49
|
+
expect(first_strategy.data.to_h).to eq(
|
50
|
+
{
|
51
|
+
variant_id: 1,
|
52
|
+
bid_price: 27.2,
|
53
|
+
ask_price: 27.23,
|
54
|
+
underlying: "MSFT",
|
55
|
+
underlying_id: 27426,
|
56
|
+
open_price: nil,
|
57
|
+
volatility: 0,
|
58
|
+
delta: 1,
|
59
|
+
gamma: 0,
|
60
|
+
theta: 0,
|
61
|
+
vega: 0,
|
62
|
+
rho: 0,
|
63
|
+
is_real_time: true
|
64
|
+
}
|
65
|
+
)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context '.endpoint' do
|
70
|
+
it 'returns the right endpoint' do
|
71
|
+
url = "/v1/markets/quotes/strategies"
|
72
|
+
expect(QuestradeApi::REST::StrategyQuote.endpoint).to eq(url)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: questrade_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bruno Meira
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-03-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -88,6 +88,7 @@ files:
|
|
88
88
|
- lib/questrade_api/client.rb
|
89
89
|
- lib/questrade_api/modules/account_call.rb
|
90
90
|
- lib/questrade_api/modules/market_call.rb
|
91
|
+
- lib/questrade_api/modules/order_call.rb
|
91
92
|
- lib/questrade_api/modules/util.rb
|
92
93
|
- lib/questrade_api/rest/account.rb
|
93
94
|
- lib/questrade_api/rest/activity.rb
|
@@ -97,9 +98,11 @@ files:
|
|
97
98
|
- lib/questrade_api/rest/execution.rb
|
98
99
|
- lib/questrade_api/rest/market.rb
|
99
100
|
- lib/questrade_api/rest/option.rb
|
101
|
+
- lib/questrade_api/rest/option_quote.rb
|
100
102
|
- lib/questrade_api/rest/order.rb
|
101
103
|
- lib/questrade_api/rest/position.rb
|
102
104
|
- lib/questrade_api/rest/quote.rb
|
105
|
+
- lib/questrade_api/rest/strategy_quote.rb
|
103
106
|
- lib/questrade_api/rest/symbol.rb
|
104
107
|
- lib/questrade_api/rest/time.rb
|
105
108
|
- lib/questrade_api/version.rb
|
@@ -108,13 +111,16 @@ files:
|
|
108
111
|
- spec/fixtures/json/activities.json
|
109
112
|
- spec/fixtures/json/balances.json
|
110
113
|
- spec/fixtures/json/candles.json
|
114
|
+
- spec/fixtures/json/create_update_order.json
|
111
115
|
- spec/fixtures/json/executions.json
|
112
116
|
- spec/fixtures/json/markets.json
|
117
|
+
- spec/fixtures/json/option_quotes.json
|
113
118
|
- spec/fixtures/json/options.json
|
114
119
|
- spec/fixtures/json/orders.json
|
115
120
|
- spec/fixtures/json/positions.json
|
116
121
|
- spec/fixtures/json/quote_01.json
|
117
122
|
- spec/fixtures/json/quotes.json
|
123
|
+
- spec/fixtures/json/strategy_quotes.json
|
118
124
|
- spec/fixtures/json/symbol_01.json
|
119
125
|
- spec/fixtures/json/symbols.json
|
120
126
|
- spec/fixtures/json/symbols_search.json
|
@@ -129,10 +135,12 @@ files:
|
|
129
135
|
- spec/questrade_api/rest/candle_spec.rb
|
130
136
|
- spec/questrade_api/rest/execution_spec.rb
|
131
137
|
- spec/questrade_api/rest/market_spec.rb
|
138
|
+
- spec/questrade_api/rest/option_quote_spec.rb
|
132
139
|
- spec/questrade_api/rest/option_spec.rb
|
133
140
|
- spec/questrade_api/rest/order_spec.rb
|
134
141
|
- spec/questrade_api/rest/position_spec.rb
|
135
142
|
- spec/questrade_api/rest/quote_spec.rb
|
143
|
+
- spec/questrade_api/rest/strategy_quote_spec.rb
|
136
144
|
- spec/questrade_api/rest/symbol_spec.rb
|
137
145
|
- spec/questrade_api/rest/time_spec.rb
|
138
146
|
- spec/spec_helper.rb
|
@@ -166,13 +174,16 @@ test_files:
|
|
166
174
|
- spec/fixtures/json/activities.json
|
167
175
|
- spec/fixtures/json/balances.json
|
168
176
|
- spec/fixtures/json/candles.json
|
177
|
+
- spec/fixtures/json/create_update_order.json
|
169
178
|
- spec/fixtures/json/executions.json
|
170
179
|
- spec/fixtures/json/markets.json
|
180
|
+
- spec/fixtures/json/option_quotes.json
|
171
181
|
- spec/fixtures/json/options.json
|
172
182
|
- spec/fixtures/json/orders.json
|
173
183
|
- spec/fixtures/json/positions.json
|
174
184
|
- spec/fixtures/json/quote_01.json
|
175
185
|
- spec/fixtures/json/quotes.json
|
186
|
+
- spec/fixtures/json/strategy_quotes.json
|
176
187
|
- spec/fixtures/json/symbol_01.json
|
177
188
|
- spec/fixtures/json/symbols.json
|
178
189
|
- spec/fixtures/json/symbols_search.json
|
@@ -187,10 +198,12 @@ test_files:
|
|
187
198
|
- spec/questrade_api/rest/candle_spec.rb
|
188
199
|
- spec/questrade_api/rest/execution_spec.rb
|
189
200
|
- spec/questrade_api/rest/market_spec.rb
|
201
|
+
- spec/questrade_api/rest/option_quote_spec.rb
|
190
202
|
- spec/questrade_api/rest/option_spec.rb
|
191
203
|
- spec/questrade_api/rest/order_spec.rb
|
192
204
|
- spec/questrade_api/rest/position_spec.rb
|
193
205
|
- spec/questrade_api/rest/quote_spec.rb
|
206
|
+
- spec/questrade_api/rest/strategy_quote_spec.rb
|
194
207
|
- spec/questrade_api/rest/symbol_spec.rb
|
195
208
|
- spec/questrade_api/rest/time_spec.rb
|
196
209
|
- spec/spec_helper.rb
|