crypto-service 0.0.1 → 0.0.3

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: be4d060ae79ae7ee1ae22306b5fc62d91b89acda8265b287fbc7d2b0858acfa4
4
+ data.tar.gz: 4e1948a84ee2ab439c41b9ab15f30bb357ba3ac0214bc0d6a0a4ab3b2d7b4439
5
5
  SHA512:
6
- metadata.gz: 1a06fc3a72160c2ce570e91725fcf8b3d749088a13b322b55e630a70a6ba40c2cc075f003f9dbe6e1ca29445782227b89f0a3b7902a55abdc296c915a9eae0f7
7
- data.tar.gz: c3c7dc4526dc42383a968db113aad034aaf7da46e310e0436e6c4ed00a893ef7e897ac87a49b6999f5ffa130295f6ac8abe6986821f9d87e8f742421f93f9beb
6
+ metadata.gz: aea9190bcaeeb1fac078afbc71e0597496fc6b05b4509a458437b6a7f09b43d0e5af80c0b97d4660e57e487d3d7df1a61d9ac35270258a44ea542161ba9a4f5a
7
+ data.tar.gz: 8b557573f59ba140d4f30842ec2685ea76b970beeaeb99a8c13286232607647a9af709a3b6acd7b4e7251f56cfc068319b52ba2db6b33a2af03950cbd4b273b1
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.3'
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
 
@@ -0,0 +1,22 @@
1
+ {
2
+ "code": 0,
3
+ "data": {
4
+ "BTC": {
5
+ "available": "9080.84156954",
6
+ "frozen": "1"
7
+ },
8
+ "USDT": {
9
+ "available" : "43724611.65252752",
10
+ "frozen": "1897.84472856"
11
+ },
12
+ "CET": {
13
+ "available": "6524.13695056",
14
+ "frozen": "0"
15
+ },
16
+ "ETH": {
17
+ " available": "10000",
18
+ "frozen": "0"
19
+ }
20
+ },
21
+ "message": "Success"
22
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "code": 25,
3
+ "data": {
4
+ },
5
+ "message": "Incorrect Signature"
6
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "code": 0,
3
+ "data": {
4
+ "status": "success"
5
+ },
6
+ "message": "Success"
7
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "code": 0,
3
+ "data": {
4
+ "id": 35435973,
5
+ "create_time": 1636020409,
6
+ "finished_time": null,
7
+ "amount": 43724611,
8
+ "price": " 0",
9
+ "deal_amount": "1",
10
+ "deal_money": "54334.4024000000000000",
11
+ "deal_fee": "0",
12
+ "stock_fee": "0",
13
+ "money_fee": "0",
14
+ " asset_fee": "30.427265344000000000000000",
15
+ "fee_asset": "CET",
16
+ "fee_discount": "0.70",
17
+ "avg_price": "54334.4024",
18
+ "market": "BTCUSDT",
19
+ "left": "0.00000000 ",
20
+ "maker_fee_rate": "0",
21
+ "taker_fee_rate": "0.0016",
22
+ "order_type": "market",
23
+ "type": "sell",
24
+ "status": "done",
25
+ "client_id ": "",
26
+ "source_id": "1234"
27
+ },
28
+ "message": "Success"
29
+ }