trade-o-matic 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
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