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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d0745d9ff9c3a0a67985cbccd1f39490dc2cbd5ad4e49fc84bfe5ce303b91820
4
- data.tar.gz: 4c6958398bbbedad4e86fb229d36377a922dbb4b16bd747d8013949c616ec06e
3
+ metadata.gz: eb5dd189cbc0f77a733ae7a00b6a3448f8071b89bc47f90b749d64c1a34d2709
4
+ data.tar.gz: f5a81f245c4bd2de6b2670cb07d3dfa80988cb1e7eeec8e794969a35493a890e
5
5
  SHA512:
6
- metadata.gz: 1d3ddbc1adc6fb0a78e400fa52505413170ef6adfe19f03dd203d5d90823f0b81cb5d9f909902756e2b5b4d13104d99ead36a7e200b21e0d590f9c195b612faa
7
- data.tar.gz: ea47ae148c14210bcd49c5c40d6a810a7593a45df0e35606964626b0d4b5b9b9956148795f5606077a88aabc14dfb8086b7f928771afb77fa27c2c6174b386c4
6
+ metadata.gz: 9e7090ca6b7ab5dbcb57903d3a90538f76abe585c147294ecc00a366c94e07cce9ac4107bcc537396553ddf5ab9dff8d4138da27d0404635ff9651cdee54755e
7
+ data.tar.gz: fc7b23fa07cb3dad7b9f705ac812a9370c78f92434bdca44beb30e3c891eecd5424aea72fa0173d898e5bb2c16759387fa594763132e60cad9d9c44130540a99
@@ -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
- ## [Unreleased]
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
@@ -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
@@ -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
- raise UnauthorizedError, JSON.parse(response.body)['message'] if response.status == 401
49
- raise RateLimitedError, JSON.parse(response.body)['message'] if response.status == 429
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
@@ -3,7 +3,7 @@
3
3
  module Alpaca
4
4
  module Trade
5
5
  module Api
6
- VERSION = '0.1.0'
6
+ VERSION = '0.2.0'
7
7
  end
8
8
  end
9
9
  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.1.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-05 00:00:00.000000000 Z
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: