Last active
November 25, 2022 07:17
-
-
Save CyJimmy264/3b705581926d99d8cb781e7845ff0e64 to your computer and use it in GitHub Desktop.
cryptoexchange usage example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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