Initial commit

This commit is contained in:
Yaser
2026-08-13 19:50:53 +03:30
commit 38084458fe
879 changed files with 95198 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
# frozen_string_literal: true
module API
module V2
module CoinMarketCap
class Assets < Grape::API
desc 'Details on crypto currencies available on the exchange'
get '/assets' do
::Rails.cache.fetch(:currencies_cmc, expires_in: 600) do
format_currencies(Currency.visible.coins.ordered.map)
end
end
end
end
end
end

View File

@@ -0,0 +1,93 @@
# frozen_string_literal: true
module API
module V2
module CoinMarketCap
module Helpers
MILLISECONDS_IN_SECOND = 1000
def format_summary(ticker, market)
lowest_ask = OrderAsk.get_depth(market.id)
highest_bid = OrderBid.get_depth(market.id)
{
trading_pairs: market.underscore_name, # mandatory [string]: Identifier of a ticker with delimiter to separate base/quote
base_currency: market.base_unit.upcase, # recommended [string]: Symbol/currency code of base currency
quote_currency: market.quote_unit.upcase, # recommended [string]: Symbol/currency code of base currency
last_price: ticker[:last], # mandatory [decimal]: Last transacted price of base currency based on given quote currency
lowest_ask: lowest_ask.flatten.first.to_d, # mandatory [decimal]: Lowest Ask price of base currency based on given quote currency
highest_bid: highest_bid.flatten.first.to_d, # mandatory [decimal]: Highest bid price of base currency based on given quote currency
base_volume: ticker[:amount], # mandatory [decimal]: 24-hr volume of market pair denoted in BASE currency
quote_volume: ticker[:volume], # mandatory [decimal]: 24-hr volume of market pair denoted in QUOTE currency
price_change_percent_24h: price_change_percent_24h(ticker), # mandatory [decimal]: 24-hr % price change of market pair
highest_price_24h: ticker[:high], # mandatory [decimal]: Highest price of base currency based on given quote currency in the last 24-hrs
lowest_price_24h: ticker[:low] # mandatory [decimal]: Lowest price of base currency based on given quote currency in the last 24-hrs
}
end
def format_tickers(markets)
markets.each_with_object({}) do |market, h|
ticker = TickersService[market].ticker
unified_base_crypto_id = market.base.coin? ? unified_cryptoasset_id(market.base_unit) : nil
unified_quote_crypto_id = market.quote.coin? ? unified_cryptoasset_id(market.quote_unit) : nil
h[market.underscore_name.to_s] = {
base_id: unified_base_crypto_id, # recommended [integer]: The quote pair Unified Cryptoasset ID
quote_id: unified_quote_crypto_id, # recommended [integer]: The base pair Unified Cryptoasset ID
last_price: ticker[:last], # mandatory [decimal]: Last transacted price of base currency based on given quote currenc
base_volume: ticker[:amount], # mandatory [decimal]: 24-hour trading volume denoted in BASE currency
quote_volume: ticker[:volume], # mandatory [decimal]: 24 hour trading volume denoted in QUOTE currency
isFrozen: market.state == 'enabled' ? 0 : 1 # recommended [integer]: Indicates if the market is currently enabled (0) or disabled (1)
}.compact
end
end
def format_trade(trade)
{
trade_id: trade[:id], # mandatory [integer]: A unique ID associated with the trade for the currency pair transaction
price: trade[:price], # mandatory [decimal]: Last transacted price of base currency based on given quote currency
base_volume: trade[:amount], # mandatory [decimal]: Transaction amount in BASE currency
quote_volume: trade[:total], # mandatory [decimal]: Transaction amount in QUOTE currency
timestamp: trade[:created_at] * MILLISECONDS_IN_SECOND, # mandatory [integer]: Unix timestamp in milliseconds for when the transaction occurred
type: trade[:taker_type] # mandatory [string]: Used to determine whether or not the transaction originated as a buy or sell
}
end
def format_orderbook(asks, bids)
{
timestamp: DateTime.now.strftime('%Q').to_i, # mandotory [decimal]: Unix timestamp in milliseconds for when the last updated time occurred
asks: asks, # mandotory [decimal]: The offer price and quantity for each bid order
bids: bids # mandotory [decimal]: The ask price and quantity for each ask order.
}
end
def format_currencies(currencies)
currencies.each_with_object({}) do |currency, h|
h[currency.id.upcase.to_s] = {
name: currency.name, # recommended [string]: Full name of cryptocurrency
unified_cryptoasset_id: unified_cryptoasset_id(currency.id), # recommended [integer]: Unique ID of cryptocurrency assigned by Unified Cryptoasset ID
can_withdraw: currency.withdrawal_enabled, # recommended [boolean]: Identifies whether withdrawals are enabled or disabled
can_deposit: currency.deposit_enabled, # recommended [boolean]: Identifies whether deposits are enabled or disabled
min_withdraw: currency.min_withdraw_amount # recommended [decimal]: Identifies the single minimum withdrawal amount of a cryptocurrency
}.compact
end
end
# Only for crypto currencies
def unified_cryptoasset_id(currency_id)
# System will get response in such format [{:id=>1,:name=>"Bitcoin",:symbol=>"BTC"}]
::CoinMarketCap.default_client.get(symbol: currency_id)[0][:id]
rescue ::Faraday::Error, ::CoinMarketCap::Error => e
Rails.logger.warn e
# In format methods we skipped all nil values with compact method
# As unified_cryptoasset_id is only recommended field
nil
end
# System use this methods instead of ticker[:price_change_percent]
# because we dont need percentage symbols here
def price_change_percent_24h(ticker)
ticker[:open].to_d.zero? ? '0.0' : (ticker[:last].to_d - ticker[:open].to_d) / ticker[:open].to_d
end
end
end
end
end

View File

@@ -0,0 +1,19 @@
# frozen_string_literal: true
module API
module V2
module CoinMarketCap
class Mount < Grape::API
before { set_ets_context! }
helpers CoinMarketCap::Helpers
mount CoinMarketCap::Summary
mount CoinMarketCap::Assets
mount CoinMarketCap::Ticker
mount CoinMarketCap::Trades
mount CoinMarketCap::Orderbook
end
end
end
end

View File

@@ -0,0 +1,35 @@
# frozen_string_literal: true
module API
module V2
module CoinMarketCap
class Orderbook < Grape::API
desc 'Get depth or specified market'
params do
requires :market_pair,
type: String,
desc: 'A pair such as "LTC_BTC"',
coerce_with: ->(name) { name.strip.split('_').join.downcase }
optional :depth,
type: { value: Integer, message: 'coinmarketcap.market_depth.non_integer_depth' },
values: { value: 0..500, message: 'coinmarketcap.market_depth.invalid_depth' },
desc: 'Orders depth quantity: [0,5,10,20,50,100,500]'
end
get "/orderbook/:market_pair" do
market = ::Market.find(params[:market_pair])
asks = OrderAsk.get_depth(market.id)
bids = OrderBid.get_depth(market.id)
# Depth = 100 means 50 for each bid/ask side
# Not defined or 0 = full order book
unless params[:depth].to_d.zero?
asks = asks[0, params[:depth]/2]
bids = bids[0, params[:depth]/2]
end
format_orderbook(asks, bids)
end
end
end
end
end

View File

@@ -0,0 +1,18 @@
# frozen_string_literal: true
module API
module V2
module CoinMarketCap
class Summary < Grape::API
desc 'Overview of market data for all tickers and all market pairs on the exchange'
get '/summary' do
::Rails.cache.fetch(:markets_summary_cmc, expires_in: 60) do
::Market.enabled.ordered.map do |market|
format_summary(TickersService[market].ticker, market)
end
end
end
end
end
end
end

View File

@@ -0,0 +1,16 @@
# frozen_string_literal: true
module API
module V2
module CoinMarketCap
class Ticker < Grape::API
desc 'Get 24-hour pricing and volume summary for each market pair'
get '/ticker' do
::Rails.cache.fetch(:markets_tickers_cmc, expires_in: 60) do
format_tickers(::Market.enabled.ordered)
end
end
end
end
end
end

View File

@@ -0,0 +1,21 @@
# frozen_string_literal: true
module API
module V2
module CoinMarketCap
class Trades < Grape::API
desc 'Get recent trades on market'
params do
requires :market_pair,
type: String,
desc: 'A pair such as "LTC_BTC"',
coerce_with: ->(name) { name.strip.split('_').join.downcase }
end
get "/trades/:market_pair" do
market = ::Market.find(params[:market_pair])
Trade.public_from_influx(market.id).map { |trade| format_trade(trade) }
end
end
end
end
end