crypto-service 0.0.1 → 0.0.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
  SHA256:
3
- metadata.gz: ed5769c8ed79cd6a12e10aabd5035776ea6ef5cdcc2f32812e65e86a1464ab1d
4
- data.tar.gz: 9e547251610c7f066564c4af4d7ff87cb2b5c1a0e0a3ec90ee9bdad3aee464ea
3
+ metadata.gz: 42f3bc09e96cae2487fc28602311063ed0d80ec1fbf7a3617b55b1dac90ca755
4
+ data.tar.gz: 54acf3951a8b663e41dfa98965b3c064218d4b070f0b7930cb5a4176200dcf5f
5
5
  SHA512:
6
- metadata.gz: 1a06fc3a72160c2ce570e91725fcf8b3d749088a13b322b55e630a70a6ba40c2cc075f003f9dbe6e1ca29445782227b89f0a3b7902a55abdc296c915a9eae0f7
7
- data.tar.gz: c3c7dc4526dc42383a968db113aad034aaf7da46e310e0436e6c4ed00a893ef7e897ac87a49b6999f5ffa130295f6ac8abe6986821f9d87e8f742421f93f9beb
6
+ metadata.gz: bee095306e6b1ee2d8baf40ec285295851fb92dd4fc842e65d1c464a23f8c60b6ba54a29ca66331623f6b56d7cd7fe42794954707baf7d91ea3ba1d4b37b8a64
7
+ data.tar.gz: 483f2a06607e4499e7de5659eca9d064bacf6befccf407c18271a1d6e1c12eda21b0ca568fb7fbeec86ece9329906a20f0d52b82a79ba1f82891cae504ee2419
data/lib/crypto/base.rb CHANGED
@@ -3,39 +3,25 @@
3
3
  require 'uri'
4
4
  require 'net/http'
5
5
  require 'json'
6
+
6
7
  require 'coinex'
8
+ require 'coinpaprika'
9
+ require 'gecko'
10
+ require 'string/similarity'
7
11
  require 'request'
8
12
 
9
13
  module Crypto
10
14
  class Base
11
15
  class << self
12
- attr_accessor :app_info, :path_prefix, :development
13
-
14
- def calculate_win_or_lost(avg_price:, exchange:, settings:)
15
- percentages = percentage(exchange: exchange, settings: settings)
16
- profit = percentages.first
17
- lost = percentages[1]
18
-
19
- win_price = avg_price.to_f + ((avg_price.to_f * profit.to_f) / 100)
20
- lost_price = avg_price.to_f - ((avg_price.to_f * lost.to_f) / 100)
21
-
22
- [win_price, lost_price]
23
- end
16
+ attr_accessor :app_info, :path_prefix, :development, :testing
24
17
 
25
- def binance?(exchange)
26
- exchange == 'Binance'
18
+ def test(fixture)
19
+ load_fixture(fixture)
27
20
  end
28
21
 
29
- def percentage(exchange:, settings:)
30
- if binance?(exchange)
31
- profit = settings[:perc_profit_exchange]
32
- loss = settings[:perc_lost_exchange]
33
- else
34
- profit = settings[:perc_profit_all]
35
- loss = settings[:perc_lost_all]
36
- end
37
-
38
- [profit, loss]
22
+ def load_fixture(fixture)
23
+ fixture_path = File.expand_path(File.join(__dir__, '../../spec', 'fixtures/test', fixture))
24
+ JSON.parse(File.read(fixture_path))
39
25
  end
40
26
 
41
27
  def correct(result)
@@ -46,27 +32,18 @@ module Crypto
46
32
  { 'status' => :failed, 'code' => result['code'], 'message' => result['message'] }
47
33
  end
48
34
 
49
- def coinex_keys(settings)
50
- Crypto::Exchanges::Cx.access_id(settings[:access_id])
51
- Crypto::Exchanges::Cx.secret_key(settings[:secret_key])
52
- end
53
-
54
- def response_correct?(result)
55
- (result['code']).zero?
35
+ def binance?(exchanges)
36
+ exchanges.include?('Binance')
56
37
  end
57
38
 
58
- def calculate_signature(params)
59
- data_signature = Coinex::Signature.calculate(params: params)
60
-
61
- params[:tonce] = Crypto.development ? '1672669323219' : data_signature[:tonce]
62
- params[:signature] = data_signature[:signature]
63
-
64
- params
39
+ def coinex?(exchanges)
40
+ exchanges.include?('CoinEx')
65
41
  end
66
42
  end
67
43
 
68
44
  self.app_info = ::ENV['APP_INFO'] || "#{::Crypto::NAME} V#{::Crypto.version}"
69
45
  self.path_prefix = 'https://www.binance.com/bapi/composite/v1/public/promo/cmc/cryptocurrency'
70
- self.development = false
46
+ self.development = false # For rspec & test
47
+ self.testing = false # For real call, but return development payload
71
48
  end
72
49
  end
@@ -7,48 +7,29 @@
7
7
  # Exchange: exchange where crypto is listed or buy, (Binance → 20%, Other exchange → 10% )
8
8
 
9
9
  module Crypto
10
- class Buy < Base
10
+ class Buy < Operation
11
11
  class << self
12
12
  # rubocop: disable Metrics/MethodLength, Lint/MissingCopEnableDirective
13
- def order(payload:, settings:, exchange:, count:)
14
- coinex_keys(settings)
15
- result = Coinex::Order.market(params: params(
16
- symbol: payload['symbol'],
17
- count: count
18
- ))
19
-
20
- Crypto::Exchanges::Cx.clean_keys
21
-
22
- if response_correct?(result)
23
- prices = calculate_win_or_lost(avg_price: result['data']['avg_price'], exchange: exchange, settings: settings)
24
- divert(payload: payload, result: result, prices: prices, settings: settings)
25
- else
26
- error(result)
27
- end
28
- end
29
-
30
- def balance(count)
31
- params = Coinex::Signature.calculate
32
- params[:tonce] = '1672669323219' if Crypto.development
13
+ def order(data)
14
+ return test('crypto_buy.json') if Crypto.testing
33
15
 
34
- balance = Coinex::Balance.info(params: params)
35
- balance['data']['USDT']['available'].to_i / count.to_i
16
+ operations(data: data, index: nil)
36
17
  end
37
18
 
38
- def params(symbol:, count:)
39
- params = { market: "#{symbol}USDT", type: 'buy', amount: balance(count) }
40
- calculate_signature(params)
41
- end
19
+ private
42
20
 
43
- def divert(payload:, result:, prices:, settings:)
21
+ def divert(data)
44
22
  operations = []
45
- operations << result
23
+ data[:result]['operation'] = 'B'
24
+ operations << data[:result]
46
25
 
47
- loss = Crypto::Loss.limit(payload: payload, result: result, prices: prices, settings: settings)
26
+ loss = Crypto::Loss.limit(data: data, index: 0)
27
+ loss['operation'] = 'SL'
48
28
  operations << loss
49
- return operations if payload['mode'] == :top
29
+ return operations if data.dig(:payload, :mode) == :top
50
30
 
51
- profit = Crypto::Profit.limit(payload: payload, result: result, prices: prices, settings: settings)
31
+ profit = Crypto::Profit.limit(data: data, index: 1)
32
+ profit['operation'] = 'TP'
52
33
  operations << profit
53
34
 
54
35
  operations
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Class that return exchanges of a crypto
4
+
5
+ module Crypto
6
+ class Exchange < Base
7
+ PERCENTAGE = 70
8
+
9
+ class << self
10
+ def info(symbol:, name:)
11
+ exchanges = gecko(symbol: symbol, name: name)
12
+ return exchanges unless exchanges.nil?
13
+
14
+ coinpaprika(symbol: symbol, name: name)
15
+ end
16
+
17
+ private
18
+
19
+ def exchange(symbol:, service:, name:)
20
+ payload = Object.const_get(service).info(symbol)
21
+ return nil if payload.empty?
22
+
23
+ if hash?(payload) || (array?(payload) && payload.size <= 1)
24
+ return simple_exchange(service: service,
25
+ payload: payload)
26
+ end
27
+
28
+ # If array & size > 1, calculate max similar string
29
+ max_similarity(name: name, service: service, payload: payload)
30
+ end
31
+
32
+ def gecko(symbol:, name:)
33
+ exchange(symbol: symbol, service: 'Gecko::Coin', name: name)
34
+ end
35
+
36
+ def coinpaprika(symbol:, name:)
37
+ exchange(symbol: symbol, service: 'Coinpaprika::Coin', name: name)
38
+ end
39
+
40
+ def hash?(payload)
41
+ payload.instance_of?(::Hash)
42
+ end
43
+
44
+ def array?(payload)
45
+ payload.instance_of?(::Array)
46
+ end
47
+
48
+ def simple_exchange(service:, payload:)
49
+ if array?(payload)
50
+ Object.const_get(service).exchanges(payload.first['id'])
51
+ else
52
+ Object.const_get(service).exchanges(payload[:id])
53
+ end
54
+ end
55
+
56
+ def max_similarity(name:, service:, payload:)
57
+ similarity = calculate_similarity(name: name, payload: payload)
58
+ similarity = similarity.sort_by { |h| h[:percentage] }.reverse
59
+
60
+ crypto = similarity.select { |x| (x[:percentage] * 100).to_i > PERCENTAGE }.first
61
+ return nil if similarity.nil? || similarity.empty?
62
+
63
+ simple_exchange(service: service, payload: crypto)
64
+ end
65
+
66
+ def calculate_similarity(name:, payload:)
67
+ similarity = []
68
+ payload.each do |p|
69
+ similarity.push(
70
+ 'id': p['id'],
71
+ 'percentage': String::Similarity.cosine(name, p['name'])
72
+ )
73
+ end
74
+
75
+ similarity
76
+ end
77
+ end
78
+ end
79
+ end
@@ -28,6 +28,89 @@ module Crypto
28
28
  def ping
29
29
  Coinex.ping == { status: 200 }
30
30
  end
31
+
32
+ private
33
+
34
+ # rubocop: disable Lint/MissingCopEnableDirective
35
+ def order(data:, index: nil)
36
+ if data[:result].nil? && data[:prices].nil? # Buy
37
+ result = Coinex::Order.market(params: params(data: data, index: index))
38
+ return correct(result) if cex_response_correct?(result) && index == 2
39
+
40
+ order_market(data: data, result: result)
41
+ else # Limit (SL & TP)
42
+ result = Coinex::Order.limit(params: params(data: data, index: index))
43
+ order_limit(result: result)
44
+ end
45
+ end
46
+
47
+ # rubocop: disable Metrics/MethodLength
48
+ def order_market(data:, result:)
49
+ if cex_response_correct?(result) # If correct, call SL & TP
50
+ prices = Crypto::Operation.send :calculate_win_or_lost,
51
+ avg_price: result['data']['avg_price'],
52
+ exchanges: data[:exchanges],
53
+ settings: data[:settings]
54
+
55
+ data[:prices] = prices
56
+ data[:result] = result
57
+ Crypto::Buy.send :divert, data
58
+ else
59
+ error(result)
60
+ end
61
+ end
62
+
63
+ def order_limit(result:)
64
+ if cex_response_correct?(result)
65
+ correct(result)
66
+ else
67
+ error(result)
68
+ end
69
+ end
70
+
71
+ def balance(count)
72
+ params = Coinex::Signature.calculate
73
+ params[:tonce] = '1672669323219' if Crypto.development
74
+
75
+ balance = Coinex::Balance.info(params: params)
76
+ balance['data']['USDT']['available'].to_i / count.to_i
77
+ end
78
+
79
+ def params(data:, index: nil)
80
+ params = if index.nil?
81
+ { market: "#{data.dig(:payload, :symbol)}USDT", type: 'buy', amount: balance(data[:count]) }
82
+ else
83
+ limit_params(data: data, index: index)
84
+ end
85
+
86
+ calculate_signature(params)
87
+ end
88
+
89
+ def limit_params(data:, index:)
90
+ case index
91
+ when 0 # SL
92
+ { market: "#{data.dig(:payload, :symbol)}USDT", type: 'sell', amount: data.dig(:result, 'data', 'amount'),
93
+ stop_price: Crypto::Loss.send(:what_price, data[:prices]) }
94
+ when 1 # TP
95
+ { market: "#{data.dig(:payload, :symbol)}USDT", type: 'sell', amount: data.dig(:result, 'data', 'amount'),
96
+ stop_price: Crypto::Profit.send(:what_price, data[:prices]) }
97
+ when 2 # SELL
98
+ { market: "#{data.dig(:payload, :symbol)}USDT", type: 'sell', amount: data[:balance] }
99
+ end
100
+ end
101
+
102
+ def calculate_signature(params)
103
+ data_signature = Coinex::Signature.calculate(params: params)
104
+
105
+ params[:tonce] = Crypto.development ? '1672669323219' : data_signature[:tonce]
106
+ params[:signature] = data_signature[:signature]
107
+
108
+ params
109
+ end
110
+
111
+ def cex_response_correct?(result)
112
+ (result['code']).zero?
113
+ end
31
114
  end
32
115
  end
33
116
  end
@@ -16,6 +16,8 @@ module Crypto
16
16
 
17
17
  # rubocop: disable Metrics/MethodLength, Lint/MissingCopEnableDirective
18
18
  def info
19
+ return test('crypto_listing.json') if Crypto.testing
20
+
19
21
  payload = detail
20
22
  if status?(payload)
21
23
  news = []
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Crypto
4
+ class Operation < Base
5
+ class << self
6
+ private
7
+
8
+ def setup_keys(exchanges:, settings:)
9
+ return unless coinex?(exchanges)
10
+
11
+ coinex_keys(settings)
12
+ 'CoinEx'
13
+ end
14
+
15
+ def coinex_keys(settings)
16
+ Crypto::Exchanges::Cx.access_id(settings[:access_id])
17
+ Crypto::Exchanges::Cx.secret_key(settings[:secret_key])
18
+ end
19
+
20
+ def clean_keys(exchange)
21
+ first = exchange.chr.upcase
22
+ last = exchange[-1].downcase
23
+
24
+ class_send = "Crypto::Exchanges::#{first}#{last}"
25
+ Object.const_get(class_send).clean_keys
26
+ end
27
+
28
+ def operations(data:, index: nil)
29
+ exchange = setup_keys(exchanges: data[:exchanges], settings: data[:settings])
30
+ return nil if exchange.nil?
31
+
32
+ # Buy or TP or SL
33
+ result = buy_or_limit(data: data, exchange: exchange, index: index)
34
+ clean_keys(exchange)
35
+
36
+ result
37
+ end
38
+
39
+ def buy_or_limit(data:, exchange:, index:)
40
+ case exchange
41
+ when 'CoinEx'
42
+ Crypto::Exchanges::Cx.send(:order, data: data, index: index)
43
+ end
44
+ end
45
+
46
+ def calculate_win_or_lost(avg_price:, exchanges:, settings:)
47
+ percentages = percentage(exchanges: exchanges, settings: settings)
48
+ profit = percentages.first
49
+ lost = percentages[1]
50
+
51
+ win_price = avg_price.to_f + ((avg_price.to_f * profit.to_f) / 100)
52
+ lost_price = avg_price.to_f - ((avg_price.to_f * lost.to_f) / 100)
53
+
54
+ [win_price, lost_price]
55
+ end
56
+
57
+ def percentage(exchanges:, settings:)
58
+ if binance?(exchanges)
59
+ profit = settings[:perc_profit_exchange]
60
+ loss = settings[:perc_lost_exchange]
61
+ else
62
+ profit = settings[:perc_profit_all]
63
+ loss = settings[:perc_lost_all]
64
+ end
65
+
66
+ [profit, loss]
67
+ end
68
+ end
69
+ end
70
+ end
@@ -1,26 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Crypto
4
- class Sell < Base
4
+ class Sell < Operation
5
5
  class << self
6
- def order(payload:, settings:)
7
- coinex_keys(settings)
8
- result = Coinex::Order.market(params: params(payload))
9
-
10
- Crypto::Exchanges::Cx.clean_keys
11
-
12
- if response_correct?(result)
13
- correct(result)
14
- else
15
- error(result)
16
- end
17
- end
18
-
19
- private
20
-
21
- def params(payload)
22
- params = { market: "#{payload['symbol']}USDT", type: 'sell', amount: payload['balance'] }
23
- calculate_signature(params)
6
+ def order(data)
7
+ operations(data: data, index: 2)
24
8
  end
25
9
  end
26
10
  end
@@ -1,30 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Crypto
4
- class Stop < Base
4
+ class Stop < Operation
5
5
  class << self
6
- def limit(payload:, result:, prices:, settings:)
7
- coinex_keys(settings)
8
-
9
- result = Coinex::Order.limit(params: params(
10
- payload: payload, result: result, prices: prices
11
- ))
12
-
13
- Crypto::Exchanges::Cx.clean_keys
14
- if response_correct?(result)
15
- correct(result)
16
- else
17
- error(result)
18
- end
19
- end
20
-
21
- private
22
-
23
- def params(payload:, result:, prices:)
24
- params = { market: "#{payload['symbol']}USDT", type: 'sell', amount: result['data']['amount'],
25
- stop_price: what_price(prices) }
26
-
27
- calculate_signature(params)
6
+ def limit(data:, index:)
7
+ operations(data: data, index: index)
28
8
  end
29
9
  end
30
10
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Crypto
4
- VERSION = '0.0.1'
4
+ VERSION = '0.0.2'
5
5
  end
data/lib/crypto.rb CHANGED
@@ -35,6 +35,14 @@ module Crypto
35
35
  def development
36
36
  @mutex.synchronize { ::Crypto::Base.development }
37
37
  end
38
+
39
+ def testing=(boolean)
40
+ @mutex.synchronize { ::Crypto::Base.testing = boolean }
41
+ end
42
+
43
+ def testing
44
+ @mutex.synchronize { ::Crypto::Base.testing }
45
+ end
38
46
  end
39
47
  end
40
48
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: crypto-service
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefano Baldazzi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-09 00:00:00.000000000 Z
11
+ date: 2023-01-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -52,6 +52,48 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: 0.0.3
55
+ - !ruby/object:Gem::Dependency
56
+ name: coinpaprika
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.0.2
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.0.2
69
+ - !ruby/object:Gem::Dependency
70
+ name: gecko-api
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.0.3
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.0.3
83
+ - !ruby/object:Gem::Dependency
84
+ name: string-similarity
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 2.1.0
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 2.1.0
55
97
  - !ruby/object:Gem::Dependency
56
98
  name: zeitwerk
57
99
  requirement: !ruby/object:Gem::Requirement
@@ -191,9 +233,11 @@ files:
191
233
  - lib/crypto/autoloader.rb
192
234
  - lib/crypto/base.rb
193
235
  - lib/crypto/resources/buy.rb
236
+ - lib/crypto/resources/exchange.rb
194
237
  - lib/crypto/resources/exchanges/cx.rb
195
238
  - lib/crypto/resources/listing.rb
196
239
  - lib/crypto/resources/loss.rb
240
+ - lib/crypto/resources/operation.rb
197
241
  - lib/crypto/resources/profit.rb
198
242
  - lib/crypto/resources/sell.rb
199
243
  - lib/crypto/resources/stop.rb
@@ -221,5 +265,5 @@ requirements: []
221
265
  rubygems_version: 3.3.24
222
266
  signing_key:
223
267
  specification_version: 4
224
- summary: crypto0.0.1
268
+ summary: crypto0.0.2
225
269
  test_files: []