crypto-service 0.0.0 → 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: caeb3d4bf826a92cf335e649c50fb7abadd7b6175c021f438641781f070f3e05
4
- data.tar.gz: 9b5a35056f03f7701f8482cd28b2a55edbb0d587548192859bcfd2c0b5cc7c49
3
+ metadata.gz: 42f3bc09e96cae2487fc28602311063ed0d80ec1fbf7a3617b55b1dac90ca755
4
+ data.tar.gz: 54acf3951a8b663e41dfa98965b3c064218d4b070f0b7930cb5a4176200dcf5f
5
5
  SHA512:
6
- metadata.gz: 91558907b161b5cde84533d8df178cf2c20e31d570e029045a7eea9cf85a80868b39d7f0f51f4d9fdb03f712209affe94fc6f296c97b6db609ee18ca0fb7aea4
7
- data.tar.gz: 4024d5c5eaa55c595a46f0404d778eabdcaa20cd27eba183c9b996f40f6053def3d78be7e41cf4bbafb0f3fab4fcb67a45b3100acba536a5ae398dc93cec4c18
6
+ metadata.gz: bee095306e6b1ee2d8baf40ec285295851fb92dd4fc842e65d1c464a23f8c60b6ba54a29ca66331623f6b56d7cd7fe42794954707baf7d91ea3ba1d4b37b8a64
7
+ data.tar.gz: 483f2a06607e4499e7de5659eca9d064bacf6befccf407c18271a1d6e1c12eda21b0ca568fb7fbeec86ece9329906a20f0d52b82a79ba1f82891cae504ee2419
data/lib/crypto/base.rb CHANGED
@@ -4,13 +4,46 @@ 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'
11
+ require 'request'
12
+
7
13
  module Crypto
8
14
  class Base
9
15
  class << self
10
- attr_accessor :app_info, :path_prefix
16
+ attr_accessor :app_info, :path_prefix, :development, :testing
17
+
18
+ def test(fixture)
19
+ load_fixture(fixture)
20
+ end
21
+
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))
25
+ end
26
+
27
+ def correct(result)
28
+ { 'status' => :success, 'code' => 200, 'payload' => result }
29
+ end
30
+
31
+ def error(result)
32
+ { 'status' => :failed, 'code' => result['code'], 'message' => result['message'] }
33
+ end
34
+
35
+ def binance?(exchanges)
36
+ exchanges.include?('Binance')
37
+ end
38
+
39
+ def coinex?(exchanges)
40
+ exchanges.include?('CoinEx')
41
+ end
11
42
  end
12
43
 
13
44
  self.app_info = ::ENV['APP_INFO'] || "#{::Crypto::NAME} V#{::Crypto.version}"
14
45
  self.path_prefix = 'https://www.binance.com/bapi/composite/v1/public/promo/cmc/cryptocurrency'
46
+ self.development = false # For rspec & test
47
+ self.testing = false # For real call, but return development payload
15
48
  end
16
49
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Class that buy crypto, set TP & SL and return their responses
4
+ # Parameter of order:
5
+ # Payload: Hash of Crypto::Listing.info or Crypto::Top.info
6
+ # Settings: contact settings
7
+ # Exchange: exchange where crypto is listed or buy, (Binance → 20%, Other exchange → 10% )
8
+
9
+ module Crypto
10
+ class Buy < Operation
11
+ class << self
12
+ # rubocop: disable Metrics/MethodLength, Lint/MissingCopEnableDirective
13
+ def order(data)
14
+ return test('crypto_buy.json') if Crypto.testing
15
+
16
+ operations(data: data, index: nil)
17
+ end
18
+
19
+ private
20
+
21
+ def divert(data)
22
+ operations = []
23
+ data[:result]['operation'] = 'B'
24
+ operations << data[:result]
25
+
26
+ loss = Crypto::Loss.limit(data: data, index: 0)
27
+ loss['operation'] = 'SL'
28
+ operations << loss
29
+ return operations if data.dig(:payload, :mode) == :top
30
+
31
+ profit = Crypto::Profit.limit(data: data, index: 1)
32
+ profit['operation'] = 'TP'
33
+ operations << profit
34
+
35
+ operations
36
+ end
37
+ end
38
+ end
39
+ end
@@ -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
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Exchange -> Coinex
4
+
5
+ module Crypto
6
+ module Exchanges
7
+ class Cx < Base
8
+ class << self
9
+ def access_id(access_id)
10
+ return unless ping
11
+
12
+ Coinex.access_id = access_id
13
+ end
14
+
15
+ def secret_key(secret_key)
16
+ return unless ping
17
+
18
+ Coinex.secret_key = secret_key
19
+ end
20
+
21
+ def clean_keys
22
+ return unless ping
23
+
24
+ Coinex.access_id = nil
25
+ Coinex.secret_key = nil
26
+ end
27
+
28
+ def ping
29
+ Coinex.ping == { status: 200 }
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
114
+ end
115
+ end
116
+ end
117
+ end
@@ -2,6 +2,8 @@
2
2
 
3
3
  require 'active_support/all'
4
4
 
5
+ # Class that return crypto listed in a interval of MINUTES
6
+
5
7
  module Crypto
6
8
  class Listing < Base
7
9
  MINUTES = 15
@@ -14,6 +16,8 @@ module Crypto
14
16
 
15
17
  # rubocop: disable Metrics/MethodLength, Lint/MissingCopEnableDirective
16
18
  def info
19
+ return test('crypto_listing.json') if Crypto.testing
20
+
17
21
  payload = detail
18
22
  if status?(payload)
19
23
  news = []
@@ -34,7 +38,7 @@ module Crypto
34
38
 
35
39
  def detail
36
40
  uri = URI("#{Crypto.path}/listings/new?limit=100&start=1")
37
- Crypto::Request.request(uri: uri, method: 'GET')
41
+ Request::Http.request(uri: uri, method: 'GET')
38
42
  end
39
43
 
40
44
  def status?(payload)
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Crypto
4
+ class Loss < Stop
5
+ class << self
6
+ private
7
+
8
+ def what_price(prices)
9
+ prices[1]
10
+ end
11
+ end
12
+ end
13
+ end
@@ -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
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Crypto
4
+ class Profit < Stop
5
+ class << self
6
+ private
7
+
8
+ def what_price(prices)
9
+ prices.first
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Crypto
4
+ class Sell < Operation
5
+ class << self
6
+ def order(data)
7
+ operations(data: data, index: 2)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Crypto
4
+ class Stop < Operation
5
+ class << self
6
+ def limit(data:, index:)
7
+ operations(data: data, index: index)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Class that return crypto after a percentage in top gainers
4
+
3
5
  module Crypto
4
6
  class Top < Base
5
7
  PERCENTAGE = 20.0
@@ -32,7 +34,7 @@ module Crypto
32
34
 
33
35
  def detail
34
36
  uri = URI("#{Crypto.path}/trending/gainers-losers?limit=100&sort_dir=desc&start=1")
35
- Crypto::Request.request(uri: uri, method: 'GET')
37
+ Request::Http.request(uri: uri, method: 'GET')
36
38
  end
37
39
 
38
40
  def status?(payload)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Crypto
4
- VERSION = '0.0.0'
4
+ VERSION = '0.0.2'
5
5
  end
data/lib/crypto.rb CHANGED
@@ -27,6 +27,22 @@ module Crypto
27
27
  def version
28
28
  VERSION
29
29
  end
30
+
31
+ def development=(boolean)
32
+ @mutex.synchronize { ::Crypto::Base.development = boolean }
33
+ end
34
+
35
+ def development
36
+ @mutex.synchronize { ::Crypto::Base.development }
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
30
46
  end
31
47
  end
32
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.0
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-02 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
@@ -24,6 +24,76 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 7.0.4
27
+ - !ruby/object:Gem::Dependency
28
+ name: aga-request
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.0.0
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.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: coinex
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.0.3
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
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
27
97
  - !ruby/object:Gem::Dependency
28
98
  name: zeitwerk
29
99
  requirement: !ruby/object:Gem::Requirement
@@ -162,8 +232,15 @@ files:
162
232
  - lib/crypto.rb
163
233
  - lib/crypto/autoloader.rb
164
234
  - lib/crypto/base.rb
235
+ - lib/crypto/resources/buy.rb
236
+ - lib/crypto/resources/exchange.rb
237
+ - lib/crypto/resources/exchanges/cx.rb
165
238
  - lib/crypto/resources/listing.rb
166
- - lib/crypto/resources/request.rb
239
+ - lib/crypto/resources/loss.rb
240
+ - lib/crypto/resources/operation.rb
241
+ - lib/crypto/resources/profit.rb
242
+ - lib/crypto/resources/sell.rb
243
+ - lib/crypto/resources/stop.rb
167
244
  - lib/crypto/resources/top.rb
168
245
  - lib/crypto/version.rb
169
246
  homepage: https://github.com/Baldaz02/crypto-ruby
@@ -188,5 +265,5 @@ requirements: []
188
265
  rubygems_version: 3.3.24
189
266
  signing_key:
190
267
  specification_version: 4
191
- summary: crypto0.0.0
268
+ summary: crypto0.0.2
192
269
  test_files: []
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Crypto
4
- class Request < Base
5
- class << self
6
- def request(uri:, method:)
7
- req = get_method(name: method, uri: uri)
8
- req['Content-Type'] = 'application/json'
9
-
10
- res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
11
- http.request(req)
12
- end
13
-
14
- JSON.parse(res.body)
15
- end
16
-
17
- def get_method(name:, uri:)
18
- case name
19
- when 'GET'
20
- req = Net::HTTP::Get.new(uri)
21
- # when 'POST'
22
- # req = Net::HTTP::Post.new(uri)
23
- # when 'DELETE'
24
- # req = Net::HTTP::Delete.new(uri)
25
- end
26
-
27
- req
28
- end
29
- end
30
- end
31
- end