alpaca-trade-api 0.1.0 → 0.2.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 +7 -1
- data/lib/alpaca/trade/api.rb +5 -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 +108 -2
- data/lib/alpaca/trade/api/clock.rb +18 -0
- data/lib/alpaca/trade/api/errors.rb +6 -0
- data/lib/alpaca/trade/api/order.rb +39 -0
- data/lib/alpaca/trade/api/position.rb +33 -0
- data/lib/alpaca/trade/api/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eb5dd189cbc0f77a733ae7a00b6a3448f8071b89bc47f90b749d64c1a34d2709
|
4
|
+
data.tar.gz: f5a81f245c4bd2de6b2670cb07d3dfa80988cb1e7eeec8e794969a35493a890e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9e7090ca6b7ab5dbcb57903d3a90538f76abe585c147294ecc00a366c94e07cce9ac4107bcc537396553ddf5ab9dff8d4138da27d0404635ff9651cdee54755e
|
7
|
+
data.tar.gz: fc7b23fa07cb3dad7b9f705ac812a9370c78f92434bdca44beb30e3c891eecd5424aea72fa0173d898e5bb2c16759387fa594763132e60cad9d9c44130540a99
|
data/CHANGELOG.md
CHANGED
@@ -4,7 +4,13 @@ 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.2.0] - 2019-07-10
|
8
|
+
### Added
|
9
|
+
- Implemented Client#calendar.
|
10
|
+
- Implemented Client#clock.
|
11
|
+
- Added Client#assets.
|
12
|
+
- Implemented new methods in Client: new_order, order, orders, position, positions.
|
13
|
+
- Implemented Client#cancel_order.
|
8
14
|
|
9
15
|
## [0.1.0] - 2019-07-04
|
10
16
|
### Added
|
data/lib/alpaca/trade/api.rb
CHANGED
@@ -6,6 +6,11 @@ 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/order'
|
12
|
+
require 'alpaca/trade/api/position'
|
13
|
+
|
9
14
|
require 'alpaca/trade/api/client'
|
10
15
|
require 'alpaca/trade/api/errors'
|
11
16
|
|
@@ -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'], 2)
|
12
|
+
@high = BigDecimal(json['h'], 2)
|
13
|
+
@low = BigDecimal(json['l'], 2)
|
14
|
+
@close = BigDecimal(json['c'], 2)
|
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
|
@@ -27,6 +28,12 @@ module Alpaca
|
|
27
28
|
Asset.new(JSON.parse(response.body))
|
28
29
|
end
|
29
30
|
|
31
|
+
def assets(status: nil, asset_class: nil)
|
32
|
+
response = get_request(endpoint, "v2/assets", { status: status, asset_class: asset_class }.compact)
|
33
|
+
json = JSON.parse(response.body)
|
34
|
+
json.map { |item| Asset.new(item) }
|
35
|
+
end
|
36
|
+
|
30
37
|
def bars(timeframe, symbols)
|
31
38
|
response = get_request(data_endpoint, "v1/bars/#{timeframe}", symbols: symbols.join(','))
|
32
39
|
json = JSON.parse(response.body)
|
@@ -35,8 +42,85 @@ module Alpaca
|
|
35
42
|
end
|
36
43
|
end
|
37
44
|
|
45
|
+
def calendar(start_date: Date.today, end_date: (Date.today + 30))
|
46
|
+
params = { "start" => start_date.iso8601, "end" => end_date.iso8601 }
|
47
|
+
response = get_request(endpoint, "v2/calendar", params)
|
48
|
+
json = JSON.parse(response.body)
|
49
|
+
json.map { |item| Calendar.new(item) }
|
50
|
+
end
|
51
|
+
|
52
|
+
def cancel_order(id:)
|
53
|
+
response = delete_request(endpoint, "v2/orders/#{id}")
|
54
|
+
raise InvalidOrderId, JSON.parse(response.body)['message'] if response.status == 404
|
55
|
+
raise OrderNotCancelable if response.status == 422
|
56
|
+
end
|
57
|
+
|
58
|
+
def clock
|
59
|
+
response = get_request(endpoint, 'v2/clock')
|
60
|
+
Clock.new(JSON.parse(response.body))
|
61
|
+
end
|
62
|
+
|
63
|
+
def new_order(symbol:, qty:, side:, type:, time_in_force:, limit_price: nil,
|
64
|
+
stop_price: nil, extended_hours:, client_order_id: nil)
|
65
|
+
|
66
|
+
params = {
|
67
|
+
symbol: symbol,
|
68
|
+
qty: qty,
|
69
|
+
side: side,
|
70
|
+
type: type,
|
71
|
+
time_in_force: time_in_force,
|
72
|
+
limit_price: limit_price,
|
73
|
+
stop_price: stop_price,
|
74
|
+
extended_hours: extended_hours,
|
75
|
+
client_order_id: client_order_id
|
76
|
+
}
|
77
|
+
response = post_request(endpoint, 'v2/orders', params.compact)
|
78
|
+
raise InsufficientFunds, JSON.parse(response.body)['message'] if response.status == 403
|
79
|
+
raise MissingParameters, JSON.parse(response.body)['message'] if response.status == 422
|
80
|
+
|
81
|
+
Order.new(JSON.parse(response.body))
|
82
|
+
end
|
83
|
+
|
84
|
+
def order(id:)
|
85
|
+
response = get_request(endpoint, "v2/orders/#{id}")
|
86
|
+
raise InvalidOrderId, JSON.parse(response.body)['message'] if response.status == 404
|
87
|
+
|
88
|
+
Order.new(JSON.parse(response.body))
|
89
|
+
end
|
90
|
+
|
91
|
+
def orders(status: nil, after: nil, until_time: nil, direction: nil, limit: 50)
|
92
|
+
params = { status: status, after: after, until: until_time, direction: direction, limit: limit }
|
93
|
+
response = get_request(endpoint, "v2/orders", params.compact)
|
94
|
+
json = JSON.parse(response.body)
|
95
|
+
json.map { |item| Order.new(item) }
|
96
|
+
end
|
97
|
+
|
98
|
+
def position(symbol: nil)
|
99
|
+
response = get_request(endpoint, ["v2/positions", symbol].compact.join('/'))
|
100
|
+
raise NoPositionForSymbol, JSON.parse(response.body)['message'] if response.status == 404
|
101
|
+
|
102
|
+
Position.new(JSON.parse(response.body))
|
103
|
+
end
|
104
|
+
|
105
|
+
def positions(symbol: nil)
|
106
|
+
response = get_request(endpoint, ["v2/positions", symbol].compact.join('/'))
|
107
|
+
json = JSON.parse(response.body)
|
108
|
+
json.map { |item| Position.new(item) }
|
109
|
+
end
|
110
|
+
|
38
111
|
private
|
39
112
|
|
113
|
+
def delete_request(endpoint, uri)
|
114
|
+
conn = Faraday.new(url: endpoint)
|
115
|
+
response = conn.delete(uri) do |req|
|
116
|
+
req.headers['APCA-API-KEY-ID'] = key_id
|
117
|
+
req.headers['APCA-API-SECRET-KEY'] = key_secret
|
118
|
+
end
|
119
|
+
|
120
|
+
possibly_raise_exception(response)
|
121
|
+
response
|
122
|
+
end
|
123
|
+
|
40
124
|
def get_request(endpoint, uri, params = {})
|
41
125
|
conn = Faraday.new(url: endpoint)
|
42
126
|
response = conn.get(uri) do |req|
|
@@ -45,11 +129,33 @@ module Alpaca
|
|
45
129
|
req.headers['APCA-API-SECRET-KEY'] = key_secret
|
46
130
|
end
|
47
131
|
|
48
|
-
|
49
|
-
|
132
|
+
possibly_raise_exception(response)
|
133
|
+
response
|
134
|
+
end
|
135
|
+
|
136
|
+
def post_request(endpoint, uri, params = {})
|
137
|
+
conn = Faraday.new(url: endpoint)
|
138
|
+
response = conn.post(uri) do |req|
|
139
|
+
req.body = params.to_json
|
140
|
+
req.headers['APCA-API-KEY-ID'] = key_id
|
141
|
+
req.headers['APCA-API-SECRET-KEY'] = key_secret
|
142
|
+
end
|
50
143
|
|
144
|
+
possibly_raise_exception(response)
|
51
145
|
response
|
52
146
|
end
|
147
|
+
|
148
|
+
def possibly_raise_exception(response)
|
149
|
+
if response.status == 401
|
150
|
+
raise UnauthorizedError, JSON.parse(response.body)['message']
|
151
|
+
end
|
152
|
+
if response.status == 429
|
153
|
+
raise RateLimitedError, JSON.parse(response.body)['message']
|
154
|
+
end
|
155
|
+
if response.status == 500
|
156
|
+
raise InternalServerError, JSON.parse(response.body)['message']
|
157
|
+
end
|
158
|
+
end
|
53
159
|
end
|
54
160
|
end
|
55
161
|
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, :open, :next_open, :next_close
|
8
|
+
|
9
|
+
def initialize(json)
|
10
|
+
@timestamp = json['timestamp']
|
11
|
+
@open = json['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,12 @@ 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 MissingParameters < Error; end
|
11
|
+
class NoPositionForSymbol < Error; end
|
12
|
+
class OrderNotCancelable < Error; end
|
7
13
|
class RateLimitedError < Error; end
|
8
14
|
class UnauthorizedError < Error; end
|
9
15
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Alpaca
|
4
|
+
module Trade
|
5
|
+
module Api
|
6
|
+
class Order
|
7
|
+
attr_reader :id, :client_order_id, :created_at, :updated_at, :submitted_at,
|
8
|
+
:filled_at, :expired_at, :canceled_at, :failed_at, :asset_id, :symbol,
|
9
|
+
:asset_class, :qty, :filled_qty, :type, :side, :time_in_force, :limit_price,
|
10
|
+
:stop_price, :filled_avg_price, :status, :extended_hours
|
11
|
+
|
12
|
+
def initialize(json)
|
13
|
+
@id = json['id']
|
14
|
+
@client_order_id = json['client_order_id']
|
15
|
+
@created_at = json['created_at']
|
16
|
+
@updated_at = json['updated_at']
|
17
|
+
@submitted_at = json['submitted_at']
|
18
|
+
@filled_at = json['filled_at']
|
19
|
+
@expired_at = json['expired_at']
|
20
|
+
@canceled_at = json['canceled_at']
|
21
|
+
@failed_at = json['failed_at']
|
22
|
+
@asset_id = json['asset_id']
|
23
|
+
@symbol = json['symbol']
|
24
|
+
@asset_class = json['asset_class']
|
25
|
+
@qty = json['qty']
|
26
|
+
@filled_qty = json['filled_qty']
|
27
|
+
@type = json['type']
|
28
|
+
@side = json['side']
|
29
|
+
@time_in_force = json['time_in_force']
|
30
|
+
@limit_price = json['limit_price']
|
31
|
+
@stop_price = json['stop_price']
|
32
|
+
@filled_avg_price = json['filled_avg_price']
|
33
|
+
@status = json['status']
|
34
|
+
@extended_hours = json['extended_hours']
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
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.2.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: 2019-07-
|
11
|
+
date: 2019-07-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -160,9 +160,13 @@ 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/order.rb
|
169
|
+
- lib/alpaca/trade/api/position.rb
|
166
170
|
- lib/alpaca/trade/api/version.rb
|
167
171
|
homepage: https://github.com/ccjr/alpaca-trade-api
|
168
172
|
licenses:
|