degiro 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3fdf274ef1ecfe828dbd3bce645f48902385ecc3
4
+ data.tar.gz: a9bcf0d7f10142414210e1959cbb122e8e5622fb
5
+ SHA512:
6
+ metadata.gz: 670df722f90df0bf0dc21220ebfaae01dfbcd5ff040a0bf110b97f8362655406e4ecb80302e3077c1ac306d67fc0257e839402b5ecff0eaf2d4f62882494a272
7
+ data.tar.gz: 9c90dbb6cc8e70f7616a68e15a8d1f7f370f1de79bfc83d3f8c3f4b885f3159a4fd69e9d9be186997016b968a522f802236a24719c0b5c493c9d114956173e9d
data/lib/degiro.rb ADDED
@@ -0,0 +1 @@
1
+ require_relative 'degiro/client.rb'
@@ -0,0 +1,36 @@
1
+ require 'forwardable'
2
+
3
+ require_relative 'connection.rb'
4
+ require_relative 'create_order.rb'
5
+ require_relative 'find_product_by_id.rb'
6
+ require_relative 'find_products.rb'
7
+ require_relative 'get_cash_funds.rb'
8
+ require_relative 'get_orders.rb'
9
+ require_relative 'get_portfolio.rb'
10
+ require_relative 'get_transactions.rb'
11
+
12
+ module DeGiro
13
+ class Client
14
+ extend Forwardable
15
+
16
+ def_delegators :@create_order, :create_buy_order, :create_sell_order
17
+ def_delegators :@find_product_by_id, :find_product_by_id
18
+ def_delegators :@find_products, :find_products
19
+ def_delegators :@get_cash_funds, :get_cash_funds
20
+ def_delegators :@get_orders, :get_orders
21
+ def_delegators :@get_portfolio, :get_portfolio
22
+ def_delegators :@get_transactions, :get_transactions
23
+
24
+ def initialize(login:, password:)
25
+ connection = Connection.new(login, password)
26
+
27
+ @create_order = CreateOrder.new(connection)
28
+ @find_product_by_id = FindProductById.new(connection)
29
+ @find_products = FindProducts.new(connection)
30
+ @get_cash_funds = GetCashFunds.new(connection)
31
+ @get_orders = GetOrders.new(connection)
32
+ @get_portfolio = GetPortfolio.new(connection)
33
+ @get_transactions = GetTransactions.new(connection)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,43 @@
1
+ require 'json'
2
+ require 'faraday'
3
+ require 'faraday-cookie_jar'
4
+
5
+ require_relative 'urls_map.rb'
6
+ require_relative 'user_data.rb'
7
+ require_relative 'errors.rb'
8
+
9
+ module DeGiro
10
+ class Connection
11
+ extend Forwardable
12
+
13
+ def_delegators :@conn, :get, :post
14
+ attr_reader :urls_map, :user_data, :session_id
15
+
16
+ BASE_TRADER_URL = 'https://trader.degiro.nl'.freeze
17
+
18
+ def initialize(login, password)
19
+ @conn = Faraday.new(url: BASE_TRADER_URL) do |builder|
20
+ builder.use :cookie_jar
21
+ builder.use Faraday::Response::RaiseError
22
+ builder.adapter Faraday.default_adapter
23
+ end
24
+
25
+ response = @conn.post('/login/secure/login') do |req|
26
+ req.headers['Content-Type'] = 'application/json'
27
+ req.body = {
28
+ username: login,
29
+ password: password,
30
+ isPassCodeReset: false,
31
+ isRedirectToMobile: false,
32
+ queryParams: { reason: 'session_expired' }
33
+ }.to_json
34
+ end
35
+
36
+ @session_id = response.headers['set-cookie'][/JSESSIONID=(.*?);/m, 1]
37
+ raise MissingSessionIdError, 'Could not find valid session id' if @session_id == '' || @session_id.nil?
38
+
39
+ @urls_map = UrlsMap.new(JSON.parse(@conn.get('/login/secure/config').body))
40
+ @user_data = UserData.new(JSON.parse(@conn.get("#{@urls_map['pa_url']}/client?sessionId=#{@session_id}").body))
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,66 @@
1
+ require 'json'
2
+
3
+ module DeGiro
4
+ class CreateOrder
5
+ BUY_SELL = { buy: 0, sell: 1 }.freeze
6
+ ORDER_TYPES = { limited: 0, stop_limited: 1, market_order: 2, stop_loss: 3 }.freeze
7
+ TIME_TYPES = { day: 1, permanent: 3 }.freeze
8
+
9
+ def initialize(connection)
10
+ @connection = connection
11
+ end
12
+
13
+ def create_buy_order(product_id:, size:, price:)
14
+ order = order(BUY_SELL[:buy], product_id, size, price)
15
+ confirmation_id = JSON.parse(check_order(order).body)['confirmationId']
16
+ confirm_order(order, confirmation_id)
17
+ end
18
+
19
+ def create_sell_order(product_id:, size:, price:)
20
+ order = order(BUY_SELL[:sell], product_id, size, price)
21
+ confirmation_id = JSON.parse(check_order(order).body)['confirmationId']
22
+ confirm_order(order, confirmation_id)
23
+ end
24
+
25
+ private
26
+
27
+ def order(type, product_id, size, price)
28
+ {
29
+ buysell: type,
30
+ orderType: ORDER_TYPES[:limited],
31
+ productId: product_id,
32
+ size: size,
33
+ timeType: TIME_TYPES[:permanent],
34
+ price: price
35
+ }
36
+ end
37
+
38
+ def check_order(order)
39
+ @connection.post(check_order_url) do |req|
40
+ req.headers['Content-Type'] = 'application/json; charset=UTF-8'
41
+ req.body = order.to_json
42
+ end
43
+ end
44
+
45
+ def confirm_order(order, confirmation_id)
46
+ @connection.post(confirm_order_url(confirmation_id)) do |req|
47
+ req.headers['Content-Type'] = 'application/json; charset=UTF-8'
48
+ req.body = order.to_json
49
+ end
50
+ end
51
+
52
+ def check_order_url
53
+ "#{@connection.urls_map['trading_url']}/v5/checkOrder" \
54
+ ";jsessionid=#{@connection.session_id}" \
55
+ "?intAccount=#{@connection.user_data['int_account']}" \
56
+ "&sessionId=#{@connection.session_id}"
57
+ end
58
+
59
+ def confirm_order_url(confirmation_id)
60
+ "#{@connection.urls_map['trading_url']}/v5/order/#{confirmation_id}" \
61
+ ";jsessionid=#{@connection.session_id}" \
62
+ "?intAccount=#{@connection.user_data['int_account']}" \
63
+ "&sessionId=#{@connection.session_id}"
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,9 @@
1
+ module DeGiro
2
+ class MissingSessionIdError < StandardError; end
3
+
4
+ class MissingUrlError < StandardError; end
5
+ class IncorrectUrlError < StandardError; end
6
+
7
+ class MissingUserFieldError < StandardError; end
8
+ class IncorrectUserFieldError < StandardError; end
9
+ end
@@ -0,0 +1,37 @@
1
+ require 'json'
2
+
3
+ module DeGiro
4
+ class FindProductById
5
+ def initialize(connection)
6
+ @connection = connection
7
+ end
8
+
9
+ def find_product_by_id(id:)
10
+ parse_product(JSON.parse(find_by_id(id).body))
11
+ end
12
+
13
+ private
14
+
15
+ def find_by_id(product_id)
16
+ @connection.post(url) do |req|
17
+ req.headers['Content-Type'] = 'application/json; charset=UTF-8'
18
+ req.body = [product_id].to_json
19
+ end
20
+ end
21
+
22
+ def parse_product(response)
23
+ {
24
+ id: response['data'].values[0]['id'].to_s,
25
+ ticker: response['data'].values[0]['symbol'].to_s,
26
+ exchange_id: response['data'].values[0]['exchangeId'].to_s,
27
+ isin: response['data'].values[0]['isin'].to_s
28
+ }
29
+ end
30
+
31
+ def url
32
+ "#{@connection.urls_map['product_search_url']}/v5/products/info" \
33
+ "?intAccount=#{@connection.user_data['int_account']}" \
34
+ "&sessionId=#{@connection.session_id}"
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,34 @@
1
+ require 'json'
2
+
3
+ module DeGiro
4
+ class FindProducts
5
+ def initialize(connection)
6
+ @connection = connection
7
+ end
8
+
9
+ def find_products(search_text:, limit: 7)
10
+ params = URI.encode_www_form(searchText: search_text, limit: limit)
11
+ parse_products(JSON.parse(@connection.get(url(params)).body))
12
+ end
13
+
14
+ private
15
+
16
+ def parse_products(response)
17
+ response['products'].map do |product|
18
+ {
19
+ id: product['id'].to_s,
20
+ ticker: product['symbol'],
21
+ exchange_id: product['exchangeId'],
22
+ isin: product['isin']
23
+ }
24
+ end
25
+ end
26
+
27
+ def url(params)
28
+ "#{@connection.urls_map['product_search_url']}/v5/products/lookup" \
29
+ "?intAccount=#{@connection.user_data['int_account']}" \
30
+ "&sessionId=#{@connection.session_id}" \
31
+ "&#{params}"
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,32 @@
1
+ require 'json'
2
+
3
+ module DeGiro
4
+ class GetCashFunds
5
+ def initialize(connection)
6
+ @connection = connection
7
+ end
8
+
9
+ def get_cash_funds
10
+ params = URI.encode_www_form(cashFunds: 0)
11
+ parse_cash_funds(JSON.parse(@connection.get(url(params)).body))
12
+ end
13
+
14
+ private
15
+
16
+ def parse_cash_funds(response)
17
+ funds = response['cashFunds']['value'].map do |cash|
18
+ {
19
+ currency: cash['value'].find { |field| field['name'] == 'currencyCode' }['value'],
20
+ amount: cash['value'].find { |field| field['name'] == 'value' }['value']
21
+ }
22
+ end
23
+ Hash[funds.map { |cash| [cash[:currency], cash[:amount]] }]
24
+ end
25
+
26
+ def url(params)
27
+ "#{@connection.urls_map['trading_url']}/v5/update/" \
28
+ "#{@connection.user_data['int_account']};jsessionid=#{@connection.session_id}" \
29
+ "?#{params}"
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,35 @@
1
+ require 'json'
2
+
3
+ module DeGiro
4
+ class GetOrders
5
+ def initialize(connection)
6
+ @connection = connection
7
+ end
8
+
9
+ def get_orders
10
+ params = URI.encode_www_form(orders: 0, historicalOrders: 0, transactions: 0)
11
+ parse_orders(JSON.parse(@connection.get(url(params)).body))
12
+ end
13
+
14
+ private
15
+
16
+ def parse_orders(response)
17
+ response['orders']['value'].map do |order|
18
+ {
19
+ id: order['id'],
20
+ type: order['value'].find { |field| field['name'] == 'buysell' }['value'],
21
+ size: order['value'].find { |field| field['name'] == 'size' }['value'],
22
+ price: order['value'].find { |field| field['name'] == 'price' }['value'],
23
+ product_id: order['value'].find { |field| field['name'] == 'productId' }['value'].to_s,
24
+ product: order['value'].find { |field| field['name'] == 'product' }['value']
25
+ }
26
+ end
27
+ end
28
+
29
+ def url(params)
30
+ "#{@connection.urls_map['trading_url']}/v5/update/" \
31
+ "#{@connection.user_data['int_account']};jsessionid=#{@connection.session_id}" \
32
+ "?#{params}"
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,34 @@
1
+ require 'json'
2
+
3
+ module DeGiro
4
+ class GetPortfolio
5
+ def initialize(connection)
6
+ @connection = connection
7
+ end
8
+
9
+ def get_portfolio
10
+ params = URI.encode_www_form(portfolio: 0)
11
+ parse_portfolio(JSON.parse(@connection.get(url(params)).body))
12
+ end
13
+
14
+ private
15
+
16
+ def parse_portfolio(response)
17
+ portfolio = response['portfolio']['value'].map do |order|
18
+ {
19
+ size: order['value'].find { |field| field['name'] == 'size' }['value'],
20
+ value: order['value'].find { |field| field['name'] == 'price' }['value'],
21
+ product_id: order['value'].find { |field| field['name'] == 'id' }['value'].to_s,
22
+ product: order['value'].find { |field| field['name'] == 'product' }['value']
23
+ }
24
+ end
25
+ portfolio.select { |entry| entry[:size] > 0 }
26
+ end
27
+
28
+ def url(params)
29
+ "#{@connection.urls_map['trading_url']}/v5/update/" \
30
+ "#{@connection.user_data['int_account']};jsessionid=#{@connection.session_id}" \
31
+ "?#{params}"
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,40 @@
1
+ require 'date'
2
+ require 'json'
3
+
4
+ module DeGiro
5
+ class GetTransactions
6
+ def initialize(connection)
7
+ @connection = connection
8
+ end
9
+
10
+ def get_transactions(from: (Date.today - (365 * 5)).strftime('%d/%m/%Y'), to: Date.today.strftime('%d/%m/%Y'))
11
+ params = URI.encode_www_form(fromDate: from, toDate: to)
12
+ parse_transactions(JSON.parse(@connection.get(url(params)).body))
13
+ end
14
+
15
+ private
16
+
17
+ def parse_transactions(response)
18
+ response['data']
19
+ .sort_by { |transaction| transaction['date'] }
20
+ .reverse
21
+ .map do |transaction|
22
+ {
23
+ id: transaction['id'],
24
+ type: transaction['buysell'],
25
+ size: transaction['quantity'].abs,
26
+ price: transaction['price'],
27
+ product_id: transaction['productId'].to_s
28
+ }
29
+ end
30
+ end
31
+
32
+ def url(params)
33
+ "#{@connection.urls_map['reporting_url']}/v4/transactions" \
34
+ '?orderId=&product=&groupTransactionsByOrder=false' \
35
+ "&intAccount=#{@connection.user_data['int_account']}" \
36
+ "&sessionId=#{@connection.session_id}" \
37
+ "&#{params}"
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,26 @@
1
+ require_relative 'errors.rb'
2
+
3
+ module DeGiro
4
+ class UrlsMap
5
+ URL_NAMES = [
6
+ 'paUrl',
7
+ 'productSearchUrl',
8
+ 'productTypesUrl',
9
+ 'reportingUrl',
10
+ 'tradingUrl',
11
+ 'vwdQuotecastServiceUrl'
12
+ ].freeze
13
+
14
+ def initialize(data)
15
+ @map = URL_NAMES.each_with_object({}) do |url_name, acc|
16
+ raise MissingUrlError, "Could not find url '#{url_name}'" unless data.key?(url_name)
17
+ acc[url_name.gsub(/(.)([A-Z])/, '\1_\2').downcase] = data[url_name]
18
+ end
19
+ end
20
+
21
+ def [](url_name)
22
+ raise IncorrectUrlError, "Could not find url '#{url_name}'" unless @map.key?(url_name)
23
+ @map[url_name]
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,22 @@
1
+ require_relative 'errors.rb'
2
+
3
+ module DeGiro
4
+ class UserData
5
+ USER_FIELDS = [
6
+ 'id',
7
+ 'intAccount'
8
+ ].freeze
9
+
10
+ def initialize(data)
11
+ @map = USER_FIELDS.each_with_object({}) do |user_field, acc|
12
+ raise MissingUserFieldError, "Could not find user field '#{user_field}'" unless data.key?(user_field)
13
+ acc[user_field.gsub(/(.)([A-Z])/, '\1_\2').downcase] = data[user_field]
14
+ end
15
+ end
16
+
17
+ def [](user_field)
18
+ raise IncorrectUserFieldError, "Could not find user field '#{user_field}'" unless @map.key?(user_field)
19
+ @map[user_field]
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,3 @@
1
+ module DeGiro
2
+ VERSION = '0.0.1'.freeze
3
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: degiro
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Tom Van Eyck
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-11-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.13.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.13.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday-cookie_jar
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.0.6
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.0.6
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.51.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.51.0
55
+ description: Ruby Client for the unofficial DeGiro API
56
+ email:
57
+ - tomvaneyck@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - lib/degiro.rb
63
+ - lib/degiro/client.rb
64
+ - lib/degiro/connection.rb
65
+ - lib/degiro/create_order.rb
66
+ - lib/degiro/errors.rb
67
+ - lib/degiro/find_product_by_id.rb
68
+ - lib/degiro/find_products.rb
69
+ - lib/degiro/get_cash_funds.rb
70
+ - lib/degiro/get_orders.rb
71
+ - lib/degiro/get_portfolio.rb
72
+ - lib/degiro/get_transactions.rb
73
+ - lib/degiro/urls_map.rb
74
+ - lib/degiro/user_data.rb
75
+ - lib/degiro/version.rb
76
+ homepage: https://github.com/vaneyckt/degiro
77
+ licenses:
78
+ - MIT
79
+ metadata: {}
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ requirements: []
95
+ rubyforge_project:
96
+ rubygems_version: 2.6.11
97
+ signing_key:
98
+ specification_version: 4
99
+ summary: Ruby Client for the unofficial DeGiro API
100
+ test_files: []