Files
Dena/app/api/v2/public/markets.rb
2026-08-13 19:50:53 +03:30

198 lines
10 KiB
Ruby

# encoding: UTF-8
# frozen_string_literal: true
module API
module V2
module Public
class Markets < Grape::API
helpers ::API::V2::OrderHelpers
helpers ::API::V2::ParamHelpers
class OrderBook < Struct.new(:asks, :bids); end
resource :markets do
desc 'Get all available markets.',
is_array: true,
success: API::V2::Entities::Market
params do
use :pagination
optional :ordering,
values: { value: %w(asc desc), message: 'public.markets.invalid_ordering' },
default: 'asc',
desc: 'If set, returned values will be sorted in specific order, defaults to \'asc\'.'
optional :order_by,
values: { value: %w(id position), message: 'public.markets.invalid_order_by' },
default: 'position',
desc: 'Name of the field, which result will be ordered by.'
optional :base_unit,
type: String,
values: { value: -> (v){ ::Currency.exists?(v) },
message: 'public.markets.base_unit_doesnt_exist' },
desc: 'Strict filter for base unit'
optional :quote_unit,
type: String,
values: { value: -> (v){ ::Currency.exists?(v) },
message: 'public.markets.quote_unit_doesnt_exist' },
desc: 'Strict filter for quote unit'
optional :search, type: JSON, default: {} do
optional :base_code,
type: String,
desc: 'Search base currency code using LIKE'
optional :quote_code,
type: String,
desc: 'Search qoute currency code using LIKE'
optional :base_name,
type: String,
desc: 'Search base currency name using LIKE'
optional :quote_name,
type: String,
desc: 'Search quote currency name using LIKE'
end
end
get "/" do
search_params = params[:search]
.slice(:base_code, :quote_code, :base_name, :quote_name)
.transform_keys {|k| "#{k}_cont"}
.merge(m: 'or')
search = ::Market.active
.where(params.slice(:base_unit, :quote_unit))
.ransack(search_params)
# Add default ordering (position asc) for cases markets where unit position is same.
search.sorts = ["#{params[:order_by]} #{params[:ordering]}", "position asc"]
present paginate(Rails.cache.fetch("markets_#{params}", expires_in: 600) { search.result.load.to_a }),
with: API::V2::Entities::Market,
extended: !!params[:extended]
end
desc 'Get the order book of specified market.',
is_array: true,
success: API::V2::Entities::OrderBook
params do
requires :market,
type: String,
values: { value: -> { ::Market.active.ids }, message: 'public.market.doesnt_exist' },
desc: -> { V2::Entities::Market.documentation[:id] }
optional :asks_limit,
type: { value: Integer, message: 'public.order_book.non_integer_ask_limit' },
values: { value: 1..200, message: 'public.order_book.invalid_ask_limit' },
default: 20,
desc: 'Limit the number of returned sell orders. Default to 20.'
optional :bids_limit,
type: { value: Integer, message: 'public.order_book.non_integer_bid_limit' },
values: { value: 1..200, message: 'public.order_book.invalid_bid_limit' },
default: 20,
desc: 'Limit the number of returned buy orders. Default to 20.'
end
get ":market/order-book", requirements: { market: /[\w\.\-]+/ } do
asks = OrderAsk.active.with_market(params[:market]).matching_rule.limit(params[:asks_limit])
bids = OrderBid.active.with_market(params[:market]).matching_rule.limit(params[:bids_limit])
book = OrderBook.new asks, bids
present book, with: API::V2::Entities::OrderBook
end
desc 'Get recent trades on market, each trade is included only once. Trades are sorted in reverse creation order.',
is_array: true,
success: API::V2::Entities::Trade
params do
requires :market,
type: String,
values: { value: -> { ::Market.active.ids }, message: 'public.market.doesnt_exist' },
desc: -> { V2::Entities::Market.documentation[:id] }
optional :limit,
type: { value: Integer, message: 'public.trade.non_integer_limit' },
values: { value: 1..1000, message: 'public.trade.invalid_limit' },
default: 100,
desc: 'Limit the number of returned trades. Default to 100.'
optional :timestamp,
type: { value: Integer, message: 'public.trade.non_integer_timestamp' },
desc: "An integer represents the seconds elapsed since Unix epoch."\
"If set, only trades executed before the time will be returned."
optional :order_by,
type: String,
values: { value: %w(asc desc), message: 'public.trade.invalid_order_by' },
default: 'desc',
desc: "If set, returned trades will be sorted in specific order, default to 'desc'."
end
get ":market/trades", requirements: { market: /[\w\.\-]+/ } do
present Trade.public_from_influx(params[:market], params[:limit]), with: API::V2::Entities::PublicTrade
end
desc 'Get depth or specified market. Both asks and bids are sorted from highest price to lowest.'
params do
requires :market,
type: String,
values: { value: -> { ::Market.active.ids }, message: 'public.market.doesnt_exist' },
desc: -> { V2::Entities::Market.documentation[:id] }
optional :limit,
type: { value: Integer, message: 'public.market_depth.non_integer_limit' },
values: { value: 1..1000, message: 'public.market_depth.invalid_limit' },
default: 300,
desc: 'Limit the number of returned price levels. Default to 300.'
end
get ":market/depth", requirements: { market: /[\w\.\-]+/ } do
asks = OrderAsk.get_depth(params[:market])[0, params[:limit]]
bids = OrderBid.get_depth(params[:market])[0, params[:limit]]
{ timestamp: Time.now.to_i, asks: asks, bids: bids }
end
desc 'Get OHLC(k line) of specific market.'
params do
requires :market,
type: String,
values: { value: -> { ::Market.active.ids }, message: 'public.market.doesnt_exist' },
desc: -> { V2::Entities::Market.documentation[:id] }
optional :period,
type: { value: Integer, message: 'public.k_line.non_integer_period' },
values: { value: KLineService::AVAILABLE_POINT_PERIODS, message: 'public.k_line.invalid_period' },
default: 1,
desc: "Time period of K line, default to 1. You can choose between #{KLineService::AVAILABLE_POINT_PERIODS.join(', ')}"
optional :time_from,
type: { value: Integer, message: 'public.k_line.non_integer_time_from' },
allow_blank: { value: false, c_name: 'k_line' },
desc: "An integer represents the seconds elapsed since Unix epoch. If set, only k-line data after that time will be returned."
optional :time_to,
type: { value: Integer, message: 'public.k_line.non_integer_time_to' },
allow_blank: { value: false, c_name: 'k_line' },
desc: "An integer represents the seconds elapsed since Unix epoch. If set, only k-line data till that time will be returned."
optional :limit,
type: { value: Integer, message: 'public.k_line.non_integer_limit' },
values: { value: KLineService::AVAILABLE_POINT_LIMITS, message: 'public.k_line.invalid_limit' },
default: 30,
desc: "Limit the number of returned data points default to 30. Ignored if time_from and time_to are given."
end
get ":market/k-line", requirements: { market: /[\w\.\-]+/ } do
KLineService[params[:market], params[:period]]
.get_ohlc(params.slice(:limit, :time_from, :time_to).merge(offset: true))
end
desc 'Get ticker of all markets (For response doc see /:market/tickers/ response).'
get "/tickers" do
Rails.cache.fetch(:markets_tickers, expires_in: 60) do
::Market.active.ordered.inject({}) do |h, m|
h[m.id] = format_ticker TickersService[m].ticker
h
end
end
end
desc 'Get ticker of specific market.',
success: API::V2::Entities::Ticker
params do
requires :market,
type: String,
values: { value: -> { ::Market.active.ids }, message: 'public.market.doesnt_exist' },
desc: -> { V2::Entities::Market.documentation[:id] }
end
get "/:market/tickers/", requirements: { market: /[\w\.\-]+/ } do
present format_ticker(TickersService[params[:market]].ticker),
with: API::V2::Entities::Ticker
end
end
end
end
end
end