alpaca-trade-api 0.1.0 → 0.5.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/.ruby-version +1 -1
- data/CHANGELOG.md +30 -1
- data/Gemfile.lock +5 -5
- data/README.md +38 -7
- data/alpaca-trade-api.gemspec +1 -1
- data/lib/alpaca/trade/api.rb +7 -0
- data/lib/alpaca/trade/api/account.rb +9 -9
- data/lib/alpaca/trade/api/bar.rb +4 -4
- data/lib/alpaca/trade/api/calendar.rb +17 -0
- data/lib/alpaca/trade/api/client.rb +194 -4
- data/lib/alpaca/trade/api/clock.rb +18 -0
- data/lib/alpaca/trade/api/errors.rb +7 -0
- data/lib/alpaca/trade/api/last_trade.rb +15 -0
- data/lib/alpaca/trade/api/order.rb +46 -0
- data/lib/alpaca/trade/api/position.rb +33 -0
- data/lib/alpaca/trade/api/version.rb +1 -1
- metadata +9 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7e5e11fb99efb22e866a8f2cba25f51c4461243a1e29c6a27bca7e37f6d90ab6
|
4
|
+
data.tar.gz: bf33b4ac115ec3db6d12f1f9e8463dccd0c33bef5eeba88625ee3e0ca8583f42
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c6c4b7f0efdd86c0930db2e7c4cda494faab106722afa5d4187b242750e9885f2ae8845af1ca947200be2432869f0503fc9a11bff47dde20762259b5aa44bf00
|
7
|
+
data.tar.gz: 9a9900150e64e4c9e7366578edf42fcb803c08f97a622737f55678e00227f309ae6a8718cf76fe4e7e7f5b340bbb8d914308f2bfbdcf55dfea3fae74c72e7437
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.6.
|
1
|
+
2.6.4
|
data/CHANGELOG.md
CHANGED
@@ -4,7 +4,36 @@ All notable changes to this project will be documented in this file.
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
|
-
## [
|
7
|
+
## [0.5.0] - 2020-05-25
|
8
|
+
### Added
|
9
|
+
- Implemented Client#last_trade. Thanks @nathanworden.
|
10
|
+
|
11
|
+
## [0.4.1] - 2020-04-25
|
12
|
+
### Fixed
|
13
|
+
- Added explicit `BigDecimal` require.
|
14
|
+
|
15
|
+
## [0.4.0] - 2020-02-16
|
16
|
+
### Added
|
17
|
+
- Supporting [Bracket Orders](https://docs.alpaca.markets/trading-on-alpaca/orders/#bracket-orders)
|
18
|
+
- Validating time frame in Client#bars
|
19
|
+
|
20
|
+
## [0.3.0] - 2019-09-19
|
21
|
+
### Added
|
22
|
+
- Implemented new endpoints:
|
23
|
+
* Client#cancel_orders
|
24
|
+
* Client#close_position
|
25
|
+
* Client#close_positions
|
26
|
+
* Client#replace_order
|
27
|
+
- Added limit as a parameter to Client#bars
|
28
|
+
- Renamed Clock#open to Clock#is_open and fixed assignment.
|
29
|
+
|
30
|
+
## [0.2.0] - 2019-07-10
|
31
|
+
### Added
|
32
|
+
- Implemented Client#calendar.
|
33
|
+
- Implemented Client#clock.
|
34
|
+
- Added Client#assets.
|
35
|
+
- Implemented new methods in Client: new_order, order, orders, position, positions.
|
36
|
+
- Implemented Client#cancel_order.
|
8
37
|
|
9
38
|
## [0.1.0] - 2019-07-04
|
10
39
|
### Added
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
alpaca-trade-api (0.
|
4
|
+
alpaca-trade-api (0.5.0)
|
5
5
|
faraday (~> 0.15)
|
6
6
|
|
7
7
|
GEM
|
@@ -14,13 +14,13 @@ GEM
|
|
14
14
|
safe_yaml (~> 1.0.0)
|
15
15
|
diff-lcs (1.3)
|
16
16
|
docile (1.3.2)
|
17
|
-
faraday (0.
|
17
|
+
faraday (0.17.3)
|
18
18
|
multipart-post (>= 1.2, < 3)
|
19
19
|
hashdiff (0.4.0)
|
20
20
|
json (2.2.0)
|
21
21
|
multipart-post (2.1.1)
|
22
22
|
public_suffix (3.1.1)
|
23
|
-
rake (
|
23
|
+
rake (13.0.1)
|
24
24
|
rb-readline (0.5.5)
|
25
25
|
rspec (3.8.0)
|
26
26
|
rspec-core (~> 3.8.0)
|
@@ -54,7 +54,7 @@ DEPENDENCIES
|
|
54
54
|
alpaca-trade-api!
|
55
55
|
bundler (~> 2.0)
|
56
56
|
byebug
|
57
|
-
rake (~>
|
57
|
+
rake (~> 13.0)
|
58
58
|
rb-readline
|
59
59
|
rspec (~> 3.0)
|
60
60
|
simplecov (~> 0.16)
|
@@ -62,4 +62,4 @@ DEPENDENCIES
|
|
62
62
|
webmock (~> 3.0)
|
63
63
|
|
64
64
|
BUNDLED WITH
|
65
|
-
2.0.
|
65
|
+
2.0.2
|
data/README.md
CHANGED
@@ -26,23 +26,54 @@ By default, the library is configured to use the Paper Trading host - `https://p
|
|
26
26
|
|
27
27
|
```ruby
|
28
28
|
Alpaca::Trade::Api.configure do |config|
|
29
|
-
config.endpoint = 'https://api.
|
29
|
+
config.endpoint = 'https://api.alpaca.markets'
|
30
30
|
config.key_id = 'A_KEY_ID'
|
31
31
|
config.key_secret = 'A_S3CRET'
|
32
32
|
end
|
33
33
|
```
|
34
34
|
|
35
|
-
### Account
|
36
35
|
|
37
|
-
|
36
|
+
### Example
|
38
37
|
|
39
|
-
|
38
|
+
Here's an example on how to use the library to read details of an account's orders in paper trading.
|
40
39
|
|
41
|
-
|
40
|
+
```ruby
|
41
|
+
require 'alpaca/trade/api'
|
42
42
|
|
43
|
-
|
43
|
+
Alpaca::Trade::Api.configure do |config|
|
44
|
+
config.endpoint = 'https://paper-api.alpaca.markets'
|
45
|
+
config.key_id = 'xxxxxxxx'
|
46
|
+
config.key_secret = 'xxxxx'
|
47
|
+
end
|
48
|
+
|
49
|
+
client = Alpaca::Trade::Api::Client.new
|
50
|
+
puts client.orders.last.inspect
|
51
|
+
```
|
44
52
|
|
45
|
-
|
53
|
+
### Supported endpoints
|
54
|
+
|
55
|
+
Here's a table with all currently supported endpoints in this library:
|
56
|
+
|
57
|
+
| Object | Action | Method |
|
58
|
+
|------------------------------------------------------------------------------|---------------------------------------|--------------------------------|
|
59
|
+
| [Account](https://docs.alpaca.markets/api-documentation/api-v2/account/) | [GET] Get the account | Client#account |
|
60
|
+
| [Orders](https://docs.alpaca.markets/api-documentation/api-v2/orders/) | [GET] Get a list of orders | Client#orders |
|
61
|
+
| | [POST] Request a new order | Client#new_order |
|
62
|
+
| | [GET] Get an order | Client#order(id:) |
|
63
|
+
| | [GET] Get an order by client order id | Client#order(id:) |
|
64
|
+
| | [PATCH] Replace an order | Client#replace_order |
|
65
|
+
| | [DELETE] Cancel all orders | Client#cancel_orders |
|
66
|
+
| | [DELETE] Cancel an order | Client#cancel_order(id:) |
|
67
|
+
| [Positions](https://docs.alpaca.markets/api-documentation/api-v2/positions/) | [GET] Get open positions | Client#positions |
|
68
|
+
| | [GET] Get an open position | Client#position(symbol:) |
|
69
|
+
| | [DELETE] Close all positions | Client#close_positions |
|
70
|
+
| | [DELETE] Close a position | Client#close_position(symbol:) |
|
71
|
+
| [Assets](https://docs.alpaca.markets/api-documentation/api-v2/assets/) | [GET] Get assets | Client#assets |
|
72
|
+
| | [GET] Get assets/:id | Client#asset(symbol:) |
|
73
|
+
| | [GET] Get an asset | Client#asset(symbol:) |
|
74
|
+
| [Calendar](https://docs.alpaca.markets/api-documentation/api-v2/calendar/) | [GET] Get the calendar | Client#calendar(start_date:, end_date:) |
|
75
|
+
| [Clock](https://docs.alpaca.markets/api-documentation/api-v2/clock/) | [GET] Get the clock | Client#clock |
|
76
|
+
| [Bars](https://docs.alpaca.markets/api-documentation/api-v2/market-data/bars/) | [GET] Get a list of bars | Client#bars(timeframe, symbols, limit:) |
|
46
77
|
|
47
78
|
## Development
|
48
79
|
|
data/alpaca-trade-api.gemspec
CHANGED
@@ -39,7 +39,7 @@ Gem::Specification.new do |spec|
|
|
39
39
|
spec.add_development_dependency "bundler", "~> 2.0"
|
40
40
|
spec.add_development_dependency "byebug"
|
41
41
|
spec.add_development_dependency 'rb-readline'
|
42
|
-
spec.add_development_dependency "rake", "~>
|
42
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
43
43
|
spec.add_development_dependency "rspec", "~> 3.0"
|
44
44
|
spec.add_development_dependency "simplecov", "~> 0.16"
|
45
45
|
spec.add_development_dependency "vcr", "~> 5.0"
|
data/lib/alpaca/trade/api.rb
CHANGED
@@ -6,9 +6,16 @@ require 'alpaca/trade/api/configuration'
|
|
6
6
|
require 'alpaca/trade/api/account'
|
7
7
|
require 'alpaca/trade/api/asset'
|
8
8
|
require 'alpaca/trade/api/bar'
|
9
|
+
require 'alpaca/trade/api/calendar'
|
10
|
+
require 'alpaca/trade/api/clock'
|
11
|
+
require 'alpaca/trade/api/last_trade'
|
12
|
+
require 'alpaca/trade/api/order'
|
13
|
+
require 'alpaca/trade/api/position'
|
14
|
+
|
9
15
|
require 'alpaca/trade/api/client'
|
10
16
|
require 'alpaca/trade/api/errors'
|
11
17
|
|
18
|
+
require 'bigdecimal/util'
|
12
19
|
require 'json'
|
13
20
|
|
14
21
|
module Alpaca
|
@@ -15,9 +15,9 @@ module Alpaca
|
|
15
15
|
@id = json['id']
|
16
16
|
@status = json['status']
|
17
17
|
@currency = json['currency']
|
18
|
-
@buying_power = json['buying_power']
|
19
|
-
@cash = json['cash']
|
20
|
-
@portfolio_value = json['portfolio_value']
|
18
|
+
@buying_power = BigDecimal(json['buying_power'])
|
19
|
+
@cash = BigDecimal(json['cash'])
|
20
|
+
@portfolio_value = BigDecimal(json['portfolio_value'])
|
21
21
|
@pattern_day_trader = json['pattern_day_trader']
|
22
22
|
@trade_suspended_by_user = json['trade_suspended_by_user']
|
23
23
|
@trading_blocked = json['trading_blocked']
|
@@ -25,12 +25,12 @@ module Alpaca
|
|
25
25
|
@created_at = json['created_at']
|
26
26
|
@shorting_enabled = json['shorting_enabled']
|
27
27
|
@multiplier = json['multiplier']
|
28
|
-
@long_market_value = json['long_market_value']
|
29
|
-
@short_market_value = json['short_market_value']
|
30
|
-
@equity = json['equity']
|
31
|
-
@last_equity = json['last_equity']
|
32
|
-
@initial_margin = json['initial_margin']
|
33
|
-
@maintenance_margin = json['maintenance_margin']
|
28
|
+
@long_market_value = BigDecimal(json['long_market_value'])
|
29
|
+
@short_market_value = BigDecimal(json['short_market_value'])
|
30
|
+
@equity = BigDecimal(json['equity'])
|
31
|
+
@last_equity = BigDecimal(json['last_equity'])
|
32
|
+
@initial_margin = BigDecimal(json['initial_margin'])
|
33
|
+
@maintenance_margin = BigDecimal(json['maintenance_margin'])
|
34
34
|
@daytrade_count = json['daytrade_count']
|
35
35
|
@sma = json['sma']
|
36
36
|
end
|
data/lib/alpaca/trade/api/bar.rb
CHANGED
@@ -8,10 +8,10 @@ module Alpaca
|
|
8
8
|
|
9
9
|
def initialize(json)
|
10
10
|
@time = Time.at(json['t'])
|
11
|
-
@open = json['o']
|
12
|
-
@high = json['h']
|
13
|
-
@low = json['l']
|
14
|
-
@close = json['c']
|
11
|
+
@open = BigDecimal(json['o'].to_s)
|
12
|
+
@high = BigDecimal(json['h'].to_s)
|
13
|
+
@low = BigDecimal(json['l'].to_s)
|
14
|
+
@close = BigDecimal(json['c'].to_s)
|
15
15
|
@volume = json['v']
|
16
16
|
end
|
17
17
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Alpaca
|
4
|
+
module Trade
|
5
|
+
module Api
|
6
|
+
class Calendar
|
7
|
+
attr_reader :date, :open, :close
|
8
|
+
|
9
|
+
def initialize(json)
|
10
|
+
@date = json['date']
|
11
|
+
@open = json['open']
|
12
|
+
@close = json['close']
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'date'
|
3
4
|
require 'faraday'
|
4
5
|
|
5
6
|
module Alpaca
|
@@ -8,6 +9,8 @@ module Alpaca
|
|
8
9
|
class Client
|
9
10
|
attr_reader :data_endpoint, :endpoint, :key_id, :key_secret
|
10
11
|
|
12
|
+
TIMEFRAMES = ['minute', '1Min', '5Min', '15Min', 'day', '1D']
|
13
|
+
|
11
14
|
def initialize(endpoint: Alpaca::Trade::Api.configuration.endpoint,
|
12
15
|
key_id: Alpaca::Trade::Api.configuration.key_id,
|
13
16
|
key_secret: Alpaca::Trade::Api.configuration.key_secret)
|
@@ -27,16 +30,163 @@ module Alpaca
|
|
27
30
|
Asset.new(JSON.parse(response.body))
|
28
31
|
end
|
29
32
|
|
30
|
-
def
|
31
|
-
response = get_request(
|
33
|
+
def assets(status: nil, asset_class: nil)
|
34
|
+
response = get_request(endpoint, "v2/assets", { status: status, asset_class: asset_class }.compact)
|
35
|
+
json = JSON.parse(response.body)
|
36
|
+
json.map { |item| Asset.new(item) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def bars(timeframe, symbols, limit: 100)
|
40
|
+
validate_timeframe(timeframe)
|
41
|
+
response = get_request(data_endpoint, "v1/bars/#{timeframe}", symbols: symbols.join(','), limit: limit)
|
32
42
|
json = JSON.parse(response.body)
|
33
43
|
json.keys.each_with_object({}) do |symbol, hash|
|
34
44
|
hash[symbol] = json[symbol].map { |bar| Bar.new(bar) }
|
35
45
|
end
|
36
46
|
end
|
37
47
|
|
48
|
+
def calendar(start_date: Date.today, end_date: (Date.today + 30))
|
49
|
+
# FIX, use start_date.strftime('%F')
|
50
|
+
params = { "start" => start_date.iso8601, "end" => end_date.iso8601 }
|
51
|
+
response = get_request(endpoint, "v2/calendar", params)
|
52
|
+
json = JSON.parse(response.body)
|
53
|
+
json.map { |item| Calendar.new(item) }
|
54
|
+
end
|
55
|
+
|
56
|
+
def cancel_order(id:)
|
57
|
+
response = delete_request(endpoint, "v2/orders/#{id}")
|
58
|
+
raise InvalidOrderId, JSON.parse(response.body)['message'] if response.status == 404
|
59
|
+
raise OrderNotCancelable if response.status == 422
|
60
|
+
end
|
61
|
+
|
62
|
+
def cancel_orders
|
63
|
+
response = delete_request(endpoint, 'v2/orders')
|
64
|
+
|
65
|
+
json = JSON.parse(response.body)
|
66
|
+
json.map do |item|
|
67
|
+
{
|
68
|
+
'id' => item['id'],
|
69
|
+
'status' => item['status'],
|
70
|
+
'body' => Order.new(item['body']),
|
71
|
+
}
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def clock
|
76
|
+
response = get_request(endpoint, 'v2/clock')
|
77
|
+
Clock.new(JSON.parse(response.body))
|
78
|
+
end
|
79
|
+
|
80
|
+
def close_position(symbol:)
|
81
|
+
response = delete_request(endpoint, "v2/positions/#{symbol}")
|
82
|
+
raise NoPositionForSymbol, JSON.parse(response.body)['message'] if response.status == 404
|
83
|
+
|
84
|
+
Position.new(JSON.parse(response.body))
|
85
|
+
end
|
86
|
+
|
87
|
+
def close_positions
|
88
|
+
response = delete_request(endpoint, 'v2/positions')
|
89
|
+
|
90
|
+
json = JSON.parse(response.body)
|
91
|
+
json.map do |item|
|
92
|
+
{
|
93
|
+
'symbol' => item['symbol'],
|
94
|
+
'status' => item['status'],
|
95
|
+
'body' => Position.new(item['body']),
|
96
|
+
}
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def last_trade(symbol:)
|
101
|
+
response = get_request(data_endpoint, "v1/last/stocks/#{symbol}")
|
102
|
+
raise InvalidRequest, JSON.parse(response.body)['message'] if response.status == 404
|
103
|
+
|
104
|
+
LastTrade.new(JSON.parse(response.body))
|
105
|
+
end
|
106
|
+
|
107
|
+
def new_order(symbol:, qty:, side:, type:, time_in_force:, limit_price: nil,
|
108
|
+
stop_price: nil, extended_hours: false, client_order_id: nil, order_class: nil,
|
109
|
+
take_profit: nil, stop_loss: nil)
|
110
|
+
|
111
|
+
params = {
|
112
|
+
symbol: symbol,
|
113
|
+
qty: qty,
|
114
|
+
side: side,
|
115
|
+
type: type,
|
116
|
+
time_in_force: time_in_force,
|
117
|
+
limit_price: limit_price,
|
118
|
+
order_class: order_class,
|
119
|
+
stop_price: stop_price,
|
120
|
+
take_profit: take_profit,
|
121
|
+
stop_loss: stop_loss,
|
122
|
+
extended_hours: extended_hours,
|
123
|
+
client_order_id: client_order_id
|
124
|
+
}
|
125
|
+
response = post_request(endpoint, 'v2/orders', params.compact)
|
126
|
+
raise InsufficientFunds, JSON.parse(response.body)['message'] if response.status == 403
|
127
|
+
raise MissingParameters, JSON.parse(response.body)['message'] if response.status == 422
|
128
|
+
|
129
|
+
Order.new(JSON.parse(response.body))
|
130
|
+
end
|
131
|
+
|
132
|
+
def order(id:)
|
133
|
+
response = get_request(endpoint, "v2/orders/#{id}")
|
134
|
+
raise InvalidOrderId, JSON.parse(response.body)['message'] if response.status == 404
|
135
|
+
|
136
|
+
Order.new(JSON.parse(response.body))
|
137
|
+
end
|
138
|
+
|
139
|
+
def orders(status: nil, after: nil, until_time: nil, direction: nil, limit: 50)
|
140
|
+
params = { status: status, after: after, until: until_time, direction: direction, limit: limit }
|
141
|
+
response = get_request(endpoint, "v2/orders", params.compact)
|
142
|
+
json = JSON.parse(response.body)
|
143
|
+
json.map { |item| Order.new(item) }
|
144
|
+
end
|
145
|
+
|
146
|
+
def position(symbol: nil)
|
147
|
+
response = get_request(endpoint, ["v2/positions", symbol].compact.join('/'))
|
148
|
+
raise NoPositionForSymbol, JSON.parse(response.body)['message'] if response.status == 404
|
149
|
+
|
150
|
+
Position.new(JSON.parse(response.body))
|
151
|
+
end
|
152
|
+
|
153
|
+
def positions(symbol: nil)
|
154
|
+
response = get_request(endpoint, ["v2/positions", symbol].compact.join('/'))
|
155
|
+
json = JSON.parse(response.body)
|
156
|
+
json.map { |item| Position.new(item) }
|
157
|
+
end
|
158
|
+
|
159
|
+
def replace_order(id:, qty: nil, time_in_force: nil, limit_price: nil,
|
160
|
+
stop_price: nil, client_order_id: nil)
|
161
|
+
|
162
|
+
params = {
|
163
|
+
qty: qty,
|
164
|
+
time_in_force: time_in_force,
|
165
|
+
limit_price: limit_price,
|
166
|
+
stop_price: stop_price,
|
167
|
+
client_order_id: client_order_id
|
168
|
+
}
|
169
|
+
response = patch_request(endpoint, "v2/orders/#{id}", params.compact)
|
170
|
+
raise InsufficientFunds, JSON.parse(response.body)['message'] if response.status == 403
|
171
|
+
raise InvalidOrderId, JSON.parse(response.body)['message'] if response.status == 404
|
172
|
+
raise InvalidRequest, JSON.parse(response.body)['message'] if response.status == 422
|
173
|
+
|
174
|
+
Order.new(JSON.parse(response.body))
|
175
|
+
end
|
176
|
+
|
38
177
|
private
|
39
178
|
|
179
|
+
def delete_request(endpoint, uri)
|
180
|
+
conn = Faraday.new(url: endpoint)
|
181
|
+
response = conn.delete(uri) do |req|
|
182
|
+
req.headers['APCA-API-KEY-ID'] = key_id
|
183
|
+
req.headers['APCA-API-SECRET-KEY'] = key_secret
|
184
|
+
end
|
185
|
+
|
186
|
+
possibly_raise_exception(response)
|
187
|
+
response
|
188
|
+
end
|
189
|
+
|
40
190
|
def get_request(endpoint, uri, params = {})
|
41
191
|
conn = Faraday.new(url: endpoint)
|
42
192
|
response = conn.get(uri) do |req|
|
@@ -45,11 +195,51 @@ module Alpaca
|
|
45
195
|
req.headers['APCA-API-SECRET-KEY'] = key_secret
|
46
196
|
end
|
47
197
|
|
48
|
-
|
49
|
-
|
198
|
+
possibly_raise_exception(response)
|
199
|
+
response
|
200
|
+
end
|
50
201
|
|
202
|
+
def patch_request(endpoint, uri, params = {})
|
203
|
+
conn = Faraday.new(url: endpoint)
|
204
|
+
response = conn.patch(uri) do |req|
|
205
|
+
req.body = params.to_json
|
206
|
+
req.headers['APCA-API-KEY-ID'] = key_id
|
207
|
+
req.headers['APCA-API-SECRET-KEY'] = key_secret
|
208
|
+
end
|
209
|
+
|
210
|
+
possibly_raise_exception(response)
|
51
211
|
response
|
52
212
|
end
|
213
|
+
|
214
|
+
def post_request(endpoint, uri, params = {})
|
215
|
+
conn = Faraday.new(url: endpoint)
|
216
|
+
response = conn.post(uri) do |req|
|
217
|
+
req.body = params.to_json
|
218
|
+
req.headers['APCA-API-KEY-ID'] = key_id
|
219
|
+
req.headers['APCA-API-SECRET-KEY'] = key_secret
|
220
|
+
end
|
221
|
+
|
222
|
+
possibly_raise_exception(response)
|
223
|
+
response
|
224
|
+
end
|
225
|
+
|
226
|
+
def possibly_raise_exception(response)
|
227
|
+
if response.status == 401
|
228
|
+
raise UnauthorizedError, JSON.parse(response.body)['message']
|
229
|
+
end
|
230
|
+
if response.status == 429
|
231
|
+
raise RateLimitedError, JSON.parse(response.body)['message']
|
232
|
+
end
|
233
|
+
if response.status == 500
|
234
|
+
raise InternalServerError, JSON.parse(response.body)['message']
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def validate_timeframe(timeframe)
|
239
|
+
unless TIMEFRAMES.include?(timeframe)
|
240
|
+
raise ArgumentError, "Invalid timeframe: #{timeframe}. Valid arguments are: #{TIMEFRAMES}"
|
241
|
+
end
|
242
|
+
end
|
53
243
|
end
|
54
244
|
end
|
55
245
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Alpaca
|
4
|
+
module Trade
|
5
|
+
module Api
|
6
|
+
class Clock
|
7
|
+
attr_reader :timestamp, :is_open, :next_open, :next_close
|
8
|
+
|
9
|
+
def initialize(json)
|
10
|
+
@timestamp = json['timestamp']
|
11
|
+
@is_open = json['is_open']
|
12
|
+
@next_open = json['next_open']
|
13
|
+
@next_close = json['next_close']
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -4,6 +4,13 @@ module Alpaca
|
|
4
4
|
module Trade
|
5
5
|
module Api
|
6
6
|
class Error < StandardError; end
|
7
|
+
class InsufficientFunds < Error; end
|
8
|
+
class InternalServerError < Error; end
|
9
|
+
class InvalidOrderId < Error; end
|
10
|
+
class InvalidRequest < Error; end
|
11
|
+
class MissingParameters < Error; end
|
12
|
+
class NoPositionForSymbol < Error; end
|
13
|
+
class OrderNotCancelable < Error; end
|
7
14
|
class RateLimitedError < Error; end
|
8
15
|
class UnauthorizedError < Error; end
|
9
16
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Alpaca
|
4
|
+
module Trade
|
5
|
+
module Api
|
6
|
+
class Order
|
7
|
+
attr_reader :id, :asset_class, :asset_id, :canceled_at, :client_order_id,
|
8
|
+
:created_at, :expired_at, :extended_hours, :failed_at, :filled_at, :filled_avg_price,
|
9
|
+
:filled_qty, :legs, :limit_price, :order_class, :qty, :replaced_at, :replaced_by,
|
10
|
+
:replaces, :side, :status, :stop_price, :submitted_at, :symbol, :time_in_force,
|
11
|
+
:type, :updated_at
|
12
|
+
|
13
|
+
def initialize(json)
|
14
|
+
@id = json['id']
|
15
|
+
|
16
|
+
@asset_class = json['asset_class']
|
17
|
+
@asset_id = json['asset_id']
|
18
|
+
@canceled_at = json['canceled_at']
|
19
|
+
@client_order_id = json['client_order_id']
|
20
|
+
@created_at = json['created_at']
|
21
|
+
@expired_at = json['expired_at']
|
22
|
+
@extended_hours = json['extended_hours']
|
23
|
+
@failed_at = json['failed_at']
|
24
|
+
@filled_at = json['filled_at']
|
25
|
+
@filled_avg_price = json['filled_avg_price']
|
26
|
+
@filled_qty = json['filled_qty']
|
27
|
+
@legs = (json['legs'] || []).map {|leg| Order.new(leg)}
|
28
|
+
@limit_price = json['limit_price']
|
29
|
+
@order_class = json['order_class']
|
30
|
+
@qty = json['qty']
|
31
|
+
@replaced_at = json['replaced_at']
|
32
|
+
@replaced_by = json['replaced_by']
|
33
|
+
@replaces = json['replaces']
|
34
|
+
@side = json['side']
|
35
|
+
@status = json['status']
|
36
|
+
@stop_price = json['stop_price']
|
37
|
+
@submitted_at = json['submitted_at']
|
38
|
+
@symbol = json['symbol']
|
39
|
+
@time_in_force = json['time_in_force']
|
40
|
+
@type = json['type']
|
41
|
+
@updated_at = json['updated_at']
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Alpaca
|
4
|
+
module Trade
|
5
|
+
module Api
|
6
|
+
class Position
|
7
|
+
attr_reader :asset_id, :symbol, :exchange, :asset_class, :avg_entry_price,
|
8
|
+
:qty, :side, :market_value, :cost_basis, :unrealized_pl, :unrealized_plpc,
|
9
|
+
:unrealized_intraday_pl, :unrealized_intraday_plpc, :current_price,
|
10
|
+
:lastday_price, :change_today
|
11
|
+
|
12
|
+
def initialize(json)
|
13
|
+
@asset_id = json['asset_id']
|
14
|
+
@symbol = json['symbol']
|
15
|
+
@exchange = json['exchange']
|
16
|
+
@asset_class = json['asset_class']
|
17
|
+
@avg_entry_price = json['avg_entry_price']
|
18
|
+
@qty = json['qty']
|
19
|
+
@side = json['side']
|
20
|
+
@market_value = json['market_value']
|
21
|
+
@cost_basis = json['cost_basis']
|
22
|
+
@unrealized_pl = json['unrealized_pl']
|
23
|
+
@unrealized_plpc = json['unrealized_plpc']
|
24
|
+
@unrealized_intraday_pl = json['unrealized_intraday_pl']
|
25
|
+
@unrealized_intraday_plpc = json['unrealized_intraday_plpc']
|
26
|
+
@current_price = json['current_price']
|
27
|
+
@lastday_price = json['lastday_price']
|
28
|
+
@change_today = json['change_today']
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: alpaca-trade-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cloves Carneiro Jr
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-05-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '13.0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
68
|
+
version: '13.0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rspec
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -160,9 +160,14 @@ files:
|
|
160
160
|
- lib/alpaca/trade/api/account.rb
|
161
161
|
- lib/alpaca/trade/api/asset.rb
|
162
162
|
- lib/alpaca/trade/api/bar.rb
|
163
|
+
- lib/alpaca/trade/api/calendar.rb
|
163
164
|
- lib/alpaca/trade/api/client.rb
|
165
|
+
- lib/alpaca/trade/api/clock.rb
|
164
166
|
- lib/alpaca/trade/api/configuration.rb
|
165
167
|
- lib/alpaca/trade/api/errors.rb
|
168
|
+
- lib/alpaca/trade/api/last_trade.rb
|
169
|
+
- lib/alpaca/trade/api/order.rb
|
170
|
+
- lib/alpaca/trade/api/position.rb
|
166
171
|
- lib/alpaca/trade/api/version.rb
|
167
172
|
homepage: https://github.com/ccjr/alpaca-trade-api
|
168
173
|
licenses:
|