Skip to content

Instantly share code, notes, and snippets.

@CyJimmy264
Last active November 25, 2022 07:17
Show Gist options
  • Save CyJimmy264/3b705581926d99d8cb781e7845ff0e64 to your computer and use it in GitHub Desktop.
Save CyJimmy264/3b705581926d99d8cb781e7845ff0e64 to your computer and use it in GitHub Desktop.
cryptoexchange usage example
#!/usr/bin/env ruby
require 'cryptoexchange'
require 'ruby-progressbar'
require 'awesome_print'
require 'logger'
require 'pry'
class App
MARKETS = %w[bitmax cex]
attr_reader :client, :markets, :pairs
def initialize
@client = Cryptoexchange::Client.new
@markets = {}
@pairs = {}
@failed_directions = []
end
def pair(market, base, target)
args = method(__method__).parameters.map { |arg| arg[1].to_s }
$logger.debug "METHOD | #{method(__method__).name}: " + args.map { |arg| "#{arg} = #{eval arg}" }.join(', ')
market = market.downcase
base = base.downcase
target = target.downcase
if markets[market].nil?
@markets[market] = client.pairs(market)
end
markets[market]&.select { |p|
p.base.downcase == base && p.target.downcase == target
}.first
end
# Получить книгу ордеров по паре валют
def book(market, base, target)
args = method(__method__).parameters.map { |arg| arg[1].to_s }
$logger.debug "METHOD | #{method(__method__).name}: " + args.map { |arg| "#{arg} = #{eval arg}" }.join(', ')
market = market.downcase
base = base.downcase
target = target.downcase
book_pair = pair(market, base, target)
book_pair_reverse = pair(market, target, base)
return if book_pair.nil? && book_pair_reverse.nil?
@pairs["#{market}/#{target}/#{base}"] ||=
unless book_pair.nil?
@progressbar.progress += 1
OpenStruct.new(
book: client.order_book(book_pair),
reverse: true,
)
else
@progressbar.progress += 1
OpenStruct.new(
book: client.order_book(book_pair_reverse),
reverse: false,
)
end
@pairs["#{market}/#{base}/#{target}"] ||=
unless book_pair.nil?
@progressbar.progress += 1
OpenStruct.new(
book: client.order_book(book_pair),
reverse: false,
)
else
@progressbar.progress += 1
OpenStruct.new(
book: client.order_book(book_pair_reverse),
reverse: true,
)
end
end
# Провести обмен по паре валют
def exchange(amount, market, base, target)
args = method(__method__).parameters.map { |arg| arg[1].to_s }
$logger.debug "METHOD | #{method(__method__).name}: " + args.map { |arg| "#{arg} = #{eval arg}" }.join(', ')
rest = amount
exchange_book = book(market, base, target)
reverse = exchange_book.reverse
bids = reverse ? exchange_book.book.asks.dup.sort { |a,b| a.price <=> b.price } : exchange_book.book.bids.dup
sum = 0
while rest > 0
bid = bids.shift
bid_price = bid.price.to_f
bid_amount = bid.amount.to_f
price = reverse ? 1 / bid_price : bid_price
amount = reverse ? bid_amount * bid_price : bid_amount
rest -= amount
sum += (rest > 0 ? amount : (amount + rest)) * price
end
sum
end
def mhc_market_pairs(market)
args = method(__method__).parameters.map { |arg| arg[1].to_s }
$logger.debug "METHOD | #{method(__method__).name}: " + args.map { |arg| "#{arg} = #{eval arg}" }.join(', ')
market = market.downcase
@markets[market] = client.pairs(market)
markets[market]&.select { |p|
p.base.downcase == 'mhc' ||
p.target.downcase == 'mhc'
}.map { |p|
p.base.downcase == 'mhc' ? p.target.downcase : p.base.downcase
}
end
# Провести обмен MHC→RUB по всем возможным путям обмена
# Input: количество MHC
# Output: массив [['Название пути обмена', количество RUB], ...], отсортированный по количеству RUB
def mhc_rub_list(metahash)
args = method(__method__).parameters.map { |arg| arg[1].to_s }
$logger.debug "METHOD | #{method(__method__).name}: " + args.map { |arg| "#{arg} = #{eval arg}" }.join(', ')
@mhc_rub_chains.map do |name, chain|
next if @failed_directions.include? name
sum = 0
begin
sum = chain.inject(metahash) do |sum, args|
case args
when Array
exchange(sum, *args)
when String
eval "sum#{args}"
end
end
rescue
@failed_directions.push(name).uniq!
end
[name, sum]
end.compact.sort { |a,b| b[1] <=> a[1] }
end
def rub_mhc_list(rub)
args = method(__method__).parameters.map { |arg| arg[1].to_s }
$logger.debug "METHOD | #{method(__method__).name}: " + args.map { |arg| "#{arg} = #{eval arg}" }.join(', ')
@rub_mhc_chains.map do |name, chain|
next if @failed_directions.include? name
sum = 0
begin
sum = chain.inject(rub) do |sum, args|
case args
when Array
exchange(sum, *args)
when String
eval "sum#{args}"
end
end
rescue
@failed_directions.push(name).uniq!
end
[name, sum]
end.compact.sort { |a,b| b[1] <=> a[1] }
end
def print_mhc_rub_list list, metahash
puts "MHC→RUB Sorted:"
list.each do |name, sum|
puts "%23s: %20s | %.4f" % [name, sum, (sum/metahash rescue 0)]
end
end
def print_rub_mhc_list list, rub
puts "RUB→MHC Sorted:"
list.each do |name, sum|
puts "%23s: %20s | %.4f" % [name, sum, (rub/sum rescue 0)]
end
end
def run
# Определить, с какого количества какой валюты начинать конвертирование
metahash = nil
rub = nil
case ARGV.join ' '
when /\d+\s*mhc/i
metahash = ARGV[0][/\d+/].to_i
when /\d+\s*rub/i
rub = ARGV[0][/\d+/].to_i
end
{
# MARKETS.each do |market|
# market_pairs = mhc_market_pairs(market)
# market_pairs.each do |market_pair|
# ['xlm', 'xrp'].each do |binance_deposit|
# name = "#{market}_#{market_pair}_#{binance_deposit}"
# chain = [[market, 'mhc', market_pair], [market, market_pair, binance_deposit], ['binance', binance_deposit, 'rub']]
# pp chain
# print "#{name}: "
# $stdout.flush
# begin
# sum = chain.inject(metahash) do |sum, args|
# exchange(sum, *args)
# end
# puts " #{sum}"
# rescue
# puts 'error'
# ensure
# $stdout.flush
# end
# end
# end
# end
}
# Установка всех возможныех путей обмена
@mhc_rub_chains = {
'bitmax_mhc_usdt_xrp' => [['bitmax', 'mhc', 'usdt'], ['bitmax', 'usdt', 'xrp'], ['binance', 'xrp', 'rub'], '*0.995'],
'cex_usd_xrp' => [["cex", "mhc", "usd"], ["cex", "usd", "xrp"], ["binance", "xrp", "rub"], '*0.995'],
'cex_eur_xrp' => [["cex", "mhc", "eur"], ["cex", "eur", "xrp"], ["binance", "xrp", "rub"], '*0.995'],
'cex_gbp_xrp' => [["cex", "mhc", "gbp"], ["cex", "gbp", "xrp"], ["binance", "xrp", "rub"], '*0.995'],
'cex_btc_xrp' => [["cex", "mhc", "btc"], ["cex", "btc", "xrp"], ["binance", "xrp", "rub"], '*0.995'],
'cex_eth_xrp' => [["cex", "mhc", "eth"], ["cex", "eth", "usd"], ["cex", "usd", "xrp"], ["binance", "xrp", "rub"], '*0.995'],
'bitforex_mhc_btc_xrp' => [['bitforex', 'mhc', 'btc'], ['bitforex', 'btc', 'xrp'], ['binance', 'xrp', 'rub'], '*0.995'],
'kucoin_mhc_usdt_xrp' => [['kucoin', 'mhc', 'usdt'], ['kucoin', 'usdt', 'xrp'], ['binance', 'xrp', 'rub'], '*0.995'],
'kucoin_mhc_btc_xrp' => [['kucoin', 'mhc', 'btc'], ['kucoin', 'btc', 'xrp'], ['binance', 'xrp', 'rub'], '*0.995'],
'kucoin_mhc_eth_xrp' => [['kucoin', 'mhc', 'eth'], ['kucoin', 'eth', 'xrp'], ['binance', 'xrp', 'rub'], '*0.995'],
'bit_z_mhc_usdt_xrp' => [['bit_z', 'mhc', 'usdt'], ['bit_z', 'usdt', 'xrp'], ['binance', 'xrp', 'rub'], '*0.995'],
}
@rub_mhc_chains = {
'bitmax_xrp_usdt_mhc' => [['binance', 'rub', 'xrp'], ['bitmax', 'xrp', 'usdt'], ['bitmax', 'usdt', 'mhc']],
'cex_xrp_usd' => [["binance", "rub", "xrp"], ["cex", "xrp", "usd"], ["cex", "usd", "mhc"]],
'cex_xrp_eur' => [["binance", "rub", "xrp"], ["cex", "xrp", "eur"], ["cex", "eur", "mhc"]],
'cex_xrp_gbp' => [["binance", "rub", "xrp"], ["cex", "xrp", "gbp"], ["cex", "gbp", "mhc"]],
'cex_xrp_btc' => [["binance", "rub", "xrp"], ["cex", "xrp", "btc"], ["cex", "btc", "mhc"]],
'cex_xrp_eth' => [["binance", "rub", "xrp"], ["cex", "xrp", "usd"], ["cex", "usd", "eth"], ["cex", "eth", "mhc"]],
'bitforex_xrp_btc_mhc' => [['binance', 'rub', 'xrp'], ['bitforex', 'xrp', 'btc'], ['bitforex', 'btc', 'mhc']],
'kucoin_xrp_usdt_mhc' => [['binance', 'rub', 'xrp'], ['kucoin', 'xrp', 'usdt'], ['kucoin', 'usdt', 'mhc']],
'kucoin_xrp_btc_mhc' => [['binance', 'rub', 'xrp'], ['kucoin', 'xrp', 'btc'], ['kucoin', 'btc', 'mhc']],
'kucoin_xrp_eth_mhc' => [['binance', 'rub', 'xrp'], ['kucoin', 'xrp', 'eth'], ['kucoin', 'eth', 'mhc']],
'bit_z_xrp_usdt_mhc' => [['binance', 'rub', 'xrp'], ['bit_z', 'xrp', 'usdt'], ['bit_z', 'usdt', 'mhc']],
}
# количество уникальных пар с учётом направления обмена
uniq_pairs = @mhc_rub_chains.values.flatten(1).uniq.count +
@rub_mhc_chains.values.flatten(1).uniq.count
@progressbar = ProgressBar.create(format: 'Requesting exchange pairs books | %a %e <%B> %p%% %t')
@progressbar.total = uniq_pairs
# chains = {
# 'cex_rub_btc_mhc' => ['*0.97', ['cex', 'rub', 'btc'], '*0.997', ['cex', 'btc', 'mhc'], '*0.997', '-2'],
# 'cex_rub_btc_eth_mhc' => ['*0.97', ['cex', 'rub', 'btc'], '*0.997', ['cex', 'btc', 'eth'], '*0.997', ['cex', 'eth', 'mhc'], '*0.997', '-2'],
# 'cex_rub_btc_usd_mhc' => ['*0.97', ['cex', 'rub', 'btc'], '*0.997', ['cex', 'btc', 'usd'], '*0.997', ['cex', 'usd', 'mhc'], '*0.997', '-2'],
# 'cex_rub_btc_eur_mhc' => ['*0.97', ['cex', 'rub', 'btc'], '*0.997', ['cex', 'btc', 'eur'], '*0.997', ['cex', 'eur', 'mhc'], '*0.997', '-2'],
# 'cex_rub_btc_gbp_mhc' => ['*0.97', ['cex', 'rub', 'btc'], '*0.997', ['cex', 'btc', 'gbp'], '*0.997', ['cex', 'gbp', 'mhc'], '*0.997', '-2'],
# }
if !metahash.nil?
# Получить самые выгодные курсы обмена
mhc_rub_sums = mhc_rub_list metahash
rub = mhc_rub_sums.first[1]
# Получить самые выгодные курсы обмена от максимального количества рублей
# в обратном направлении
rub_mhc_sums = rub_mhc_list rub
print_mhc_rub_list mhc_rub_sums, metahash
print_rub_mhc_list rub_mhc_sums, rub
elsif !rub.nil?
rub_mhc_sums = rub_mhc_list rub
metahash = rub_mhc_sums.first[1]
mhc_rub_sums = mhc_rub_list metahash
print_rub_mhc_list rub_mhc_sums, rub
print_mhc_rub_list mhc_rub_sums, metahash
else
progressbar = ProgressBar.create(format: 'Calculating the best arbitraging | %a %e <%B> %p%% %t')
progressbar.total = 200
arbitr = (500..100000).step(500).map do |metahash|
progressbar.progress += 1
mhc_rub_sums = mhc_rub_list metahash
rub = mhc_rub_sums.first[1]
rub_mhc_sums = rub_mhc_list rub
(0..6).map do |i|
(0..6).map do |j|
[metahash, mhc_rub_sums[i], rub_mhc_sums[j], rub_mhc_sums[j][1] - metahash]
end
end.flatten(1)
end.flatten(1).sort { |a,b| b[3] <=> a[3] }
puts "\nResult:"
results = arbitr.uniq { |chain| chain[2][0] }[0..6]
Pry::ColorPrinter.pp(results)
margin = results[0][3]
end
end
end
class OpenStruct
# Allow awesome_print to work (with patch to AwesomePrint::Inspector defined below)
if defined?(AwesomePrint)
def each_pair &block
@table.each_pair(&block)
end
end
end
## Patch inspector so it recognizes OpenStruct
if defined?(AwesomePrint::Inspector)
module AwesomePrint
class Inspector
private
alias_method :printable_original, :printable
def printable(object)
case object
when OpenStruct then :struct
else printable_original(object)
end
end
end
end
end
$stdout.sync = true
$logger = Logger.new(STDOUT)
$logger.level = Logger::WARN
$logger.progname = 'Exchange'
def shut_down
sleep 1
end
Signal.trap("INT") {
shut_down
exit
}
Signal.trap("TERM") {
shut_down
exit
}
app = App.new
margin = app.run
while ARGV.empty?
case margin
when 500..999.99999999999
# `paplay /usr/share/sounds/freedesktop/stereo/phone-incoming-call.oga`
when 1000..1999.999999999
# 3.times { `paplay /usr/share/sounds/freedesktop/stereo/phone-incoming-call.oga` }
when 2000..Float::INFINITY
10.times { `paplay /usr/share/sounds/freedesktop/stereo/phone-incoming-call.oga` }
end
sleep 60
app = App.new
margin = app.run
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment