trade-o-matic 0.3.1 → 0.3.2

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
  SHA1:
3
- metadata.gz: 2e3ca1570f412bde6d4c5f68a6645e6d9fed6cb3
4
- data.tar.gz: 1c070414b51612d93db781c98ffd4d6893de7f7f
3
+ metadata.gz: ffa9eaa9a1e1e4a7ae37faf694d4882fb95a356c
4
+ data.tar.gz: c184b0499a3414f1155ef69ddd22717456093075
5
5
  SHA512:
6
- metadata.gz: bf4fea6ae9e13c29f3df4546a8ffdceadcff26575acec5f01dd30a181e91780652d2a39f4c244d6bea56773142477555a10d339bc01cd322c879c5dbaa408c8d
7
- data.tar.gz: d80321f50158a5c28878754b0395ed6eb162cd1c0d867293c5cad64d238107177a497d9785b7d9a7b9ca43ccce99f2619b2ba3a1b13e508bb8c7f726b6ffbffc
6
+ metadata.gz: 5f8ba8f453091a648e7f953f9f7375e55c1c223ea15508867122371786b414aa0c03c1c2b467aba83f5136a37dfdffa17c201b172b2d25012d12be8a67939ede
7
+ data.tar.gz: c4caec6d3f480b81e0f81bc2e692cb63d4c8e0af80bc2883247ad826bbc8f5e35cb9ffe4fbcc294a9e1a1097e0ba8a40bf7ad2084dbb746d8815bd90bbf60371
@@ -0,0 +1,162 @@
1
+ require 'rest-client'
2
+ require 'base64'
3
+ require 'json'
4
+ require 'trade-o-matic/adapters/base/raw_account_order'
5
+ require 'trade-o-matic/adapters/base/raw_balance'
6
+
7
+ module Trader
8
+ class BitfinexBackend
9
+ BASE_CUR = Currency.for_code(:BTC)
10
+ QUOTE_CUR = Currency.for_code(:BITFINEX_USD)
11
+ MAIN_MARKET = CurrencyPair.new BASE_CUR, QUOTE_CUR
12
+
13
+ CUR_EQUIV = {
14
+ 'usd' => QUOTE_CUR,
15
+ 'btc' => BASE_CUR
16
+ }
17
+
18
+ class BalanceAdaptor < RawBalance
19
+ attr_mapped(:amount)
20
+ attr_mapped(:available_amount, 'available')
21
+ end
22
+
23
+ class OrderAdaptor < RawAccountOrder
24
+ attr_mapped(:id)
25
+ attr_mapped(:pair) { |r| MAIN_MARKET }
26
+ attr_mapped(:price)
27
+ attr_mapped(:volume, 'original_amount')
28
+ attr_mapped(:executed_volume, 'executed_amount')
29
+ attr_mapped(:instruction) { |r| r['side'] == 'buy' ? Order::BID : Order::ASK }
30
+ attr_mapped(:status) do |r|
31
+ if r['is_live']
32
+ AccountOrder::OPEN
33
+ elsif r['is_cancelled']
34
+ AccountOrder::CANCELED
35
+ else
36
+ AccountOrder::CLOSED
37
+ end
38
+ end
39
+
40
+ def limit?
41
+ true
42
+ end
43
+ end
44
+
45
+ def initialize(_session=nil)
46
+ @session = _session
47
+ end
48
+
49
+ def get_available_markets
50
+ [MAIN_MARKET]
51
+ end
52
+
53
+ def fill_book(_book)
54
+ # TODO: consider book pair
55
+
56
+ _book.prepare Time.now
57
+
58
+ ob = public_get('book/BTCUSD')
59
+ ob['bids'].each { |o| _book.add_bid(o['price'], o['amount']) }
60
+ ob['asks'].each { |o| _book.add_ask(o['price'], o['amount']) }
61
+
62
+ tx = public_get('trades/BTCUSD', { timestamp: one_hour_ago })
63
+ tx.each do |t|
64
+ _book.add_transaction t['price'], t['amount'], Time.at(t['timestamp'])
65
+ end
66
+ end
67
+
68
+ def get_session(_credentials)
69
+ _credentials
70
+ end
71
+
72
+ def get_balance(_session, _currency)
73
+ raise "#{_currency} not supported" unless _currency == BASE_CUR || _currency == QUOTE_CUR
74
+
75
+ raw = auth_post(_session, 'balances')
76
+ wrap_balance raw, _currency
77
+ end
78
+
79
+ def get_orders(_session, _pair)
80
+ raise 'market not supported' unless _pair == MAIN_MARKET
81
+
82
+ raw = auth_post(_session, 'orders')
83
+ raw = raw.select { |o| o['type'].include? 'limit' }
84
+ raw.map { |o| wrap_order o }
85
+ end
86
+
87
+ def create_order(_session, _pair, _volume, _price, _type)
88
+ raise 'market not supported' unless _pair == MAIN_MARKET
89
+ raise 'market orders not supported' if _price.nil?
90
+
91
+ wrap_order auth_post(_session, 'order/new', {
92
+ exchange: "bitfinex",
93
+ symbol: "BTCUSD",
94
+ amount: _volume.to_s('F'),
95
+ price: _price.to_s('F'),
96
+ side: _type == Order::BID ? 'buy' : 'sell',
97
+ type: "exchange limit"
98
+ })
99
+ end
100
+
101
+ def fetch_order(_session, _id)
102
+ wrap_order auth_post(_session, 'order/status', { order_id: _id })
103
+ end
104
+
105
+ def cancel_order(_session, _id)
106
+ order = wrap_order auth_post(_session, 'order/cancel', { order_id: _id })
107
+ order = fetch_order(_session, _id) while order.status == AccountOrder::OPEN
108
+ order
109
+ end
110
+
111
+ private
112
+
113
+ attr_reader :session
114
+
115
+ def public_get(_url, _query = {})
116
+ resp = RestClient.get "https://api.bitfinex.com/v1/#{_url}/", params: _query
117
+ JSON.parse resp
118
+ end
119
+
120
+ def auth_post(_creds, _url, _data = {})
121
+ raise ArgumentError if !_creds.key?(:api_key) or !_creds.key?(:secret)
122
+
123
+ payload = build_payload("/v1/#{_url}", _data)
124
+ headers = {
125
+ 'Content-Type' => 'application/json',
126
+ 'Accept' => 'application/json',
127
+ 'X-BFX-PAYLOAD' => payload,
128
+ 'X-BFX-SIGNATURE' => sign_payload(_creds, payload),
129
+ 'X-BFX-APIKEY' => _creds[:api_key],
130
+ }
131
+
132
+ resp = RestClient.post "https://api.bitfinex.com/v1/#{_url}/", nil, headers
133
+ JSON.parse resp
134
+ end
135
+
136
+ def build_payload(_url, _params = {})
137
+ payload = {}
138
+ payload['nonce'] = (Time.now.to_f * 10_000).to_i.to_s
139
+ payload['request'] = _url
140
+ payload.merge!(_params) if _params
141
+ Base64.strict_encode64(payload.to_json)
142
+ end
143
+
144
+ def sign_payload(_creds, _payload)
145
+ OpenSSL::HMAC.hexdigest('sha384', _creds[:secret], _payload)
146
+ end
147
+
148
+ def one_hour_ago
149
+ Time.now.to_i - 3600
150
+ end
151
+
152
+ def wrap_balance(_raw, _currency)
153
+ _raw = _raw.find { |b| b["type"] == "exchange" && CUR_EQUIV[b["currency"]] == _currency }
154
+ _raw = { "amount"=> "0.0", "available" => "0.0" } if _raw.nil?
155
+ BalanceAdaptor.new _raw
156
+ end
157
+
158
+ def wrap_order(_raw)
159
+ OrderAdaptor.new _raw
160
+ end
161
+ end
162
+ end
@@ -9,11 +9,11 @@ module Trader
9
9
  end
10
10
 
11
11
  def expired?
12
- @last_sync < Time.new - @ttl
12
+ @rate.nil? || @last_sync < (Time.now - @ttl)
13
13
  end
14
14
 
15
15
  def rate
16
- if @last_sync.nil? or expired?
16
+ if expired?
17
17
  @rate = @block.call
18
18
  @last_sync = Time.now
19
19
  end
@@ -0,0 +1,20 @@
1
+ require 'pincers'
2
+
3
+ module Trader
4
+ class WebConverter < SyncConverter
5
+ attr_reader :url, :path
6
+
7
+ def initialize(_url, _path, _ttl=30)
8
+ @url = _url
9
+ @path = _path
10
+
11
+ super(_ttl) do
12
+ pincers = Pincers.for_chenso
13
+ pincers.goto _url
14
+ el = pincers.search(_path).first
15
+ raise "No element found at #{_path}" if el.nil?
16
+ Standard.amount el.text.gsub(/[^\d\s\.\,]+/,'').strip
17
+ end
18
+ end
19
+ end
20
+ end
@@ -33,6 +33,11 @@ module Trader
33
33
  BitstampBackend.new
34
34
  end
35
35
 
36
+ def build_bitfinex
37
+ require_relative '../adapters/bitfinex_backend'
38
+ BitfinexBackend.new
39
+ end
40
+
36
41
  def build_surbtc
37
42
  require_relative '../adapters/surbtc_backend'
38
43
  SurbtcBackend.new
@@ -23,6 +23,11 @@ module Trader
23
23
  JsonApiConverter.new(_params['url'], _params['path'].split('.'), _params['ttl'] || 30)
24
24
  end
25
25
 
26
+ def configure_yaml_web(_params)
27
+ require 'trade-o-matic/converters/web_converter'
28
+ WebConverter.new(_params['url'], _params['path'], _params['ttl'] || 30)
29
+ end
30
+
26
31
  def invert(_converter)
27
32
  InverseConverter.new _converter
28
33
  end
@@ -1,3 +1,3 @@
1
1
  module Trader
2
- VERSION = "0.3.1"
2
+ VERSION = "0.3.2"
3
3
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trade-o-matic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ignacio Baixas
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-12 00:00:00.000000000 Z
11
+ date: 2016-04-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rest-client
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ~>
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '1.8'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ~>
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: '1.8'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: gli
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ~>
53
53
  - !ruby/object:Gem::Version
54
54
  version: '2.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pincers
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '0.7'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '0.7'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: bundler
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -239,6 +253,7 @@ files:
239
253
  - lib/trade-o-matic/adapters/base/raw_account_order.rb
240
254
  - lib/trade-o-matic/adapters/base/raw_balance.rb
241
255
  - lib/trade-o-matic/adapters/base/raw_resource.rb
256
+ - lib/trade-o-matic/adapters/bitfinex_backend.rb
242
257
  - lib/trade-o-matic/adapters/bitstamp_backend.rb
243
258
  - lib/trade-o-matic/adapters/fake_backend.rb
244
259
  - lib/trade-o-matic/adapters/game_backend.rb
@@ -256,6 +271,7 @@ files:
256
271
  - lib/trade-o-matic/converters/inverse_converter.rb
257
272
  - lib/trade-o-matic/converters/json_api_converter.rb
258
273
  - lib/trade-o-matic/converters/sync_converter.rb
274
+ - lib/trade-o-matic/converters/web_converter.rb
259
275
  - lib/trade-o-matic/core/account.rb
260
276
  - lib/trade-o-matic/core/account_order.rb
261
277
  - lib/trade-o-matic/core/account_proxy.rb