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,49 @@
# frozen_string_literal: true
module API
module V2
module CoinGecko
module Entities
class Orderbook < API::V2::Entities::Base
expose(
:ticker_id,
documentation: {
type: String,
desc: 'A pair such as "BTC_ETH", with delimiter between different cryptoassets.'
}
) do |orderbook|
orderbook.market.underscore_name
end
expose(
:timestamp,
documentation: {
type: Integer,
desc: 'Unix timestamp in milliseconds for when the last updated time occurred'
}
) do
DateTime.now.strftime('%Q').to_i
end
expose(
:asks,
documentation: {
type: BigDecimal,
is_array: true,
desc: 'An array containing 2 elements. The offer price and quantity for each bid order.'
}
)
expose(
:bids,
documentation: {
type: BigDecimal,
is_array: true,
desc: 'An array containing 2 elements. The ask price and quantity for each ask order.'
}
)
end
end
end
end
end

View File

@@ -0,0 +1,41 @@
# frozen_string_literal: true
module API
module V2
module CoinGecko
module Entities
class Pair < API::V2::Entities::Base
expose(
:ticker_id,
documentation: {
type: String,
desc: 'Identifier of a ticker with delimiter to separate base/target, eg. BTC_ETH.'
}
) do |market|
market.underscore_name
end
expose(
:base,
documentation: {
type: String,
desc: 'Symbol/currency code of a the base cryptoasset, eg. BTC.'
}
) do |market|
market[:base_unit].upcase
end
expose(
:target,
documentation: {
type: String,
desc: 'Symbol/currency code of the target cryptoasset, eg. ETH.'
}
) do |market|
market[:quote_unit].upcase
end
end
end
end
end
end

View File

@@ -0,0 +1,111 @@
# frozen_string_literal: true
module API
module V2
module CoinGecko
module Entities
class Ticker < API::V2::Entities::Base
expose(
:ticker_id,
documentation: {
type: String,
desc: 'Identifier of a ticker with delimiter to separate base/target, eg. BTC_ETH.'
}
) do |ticker|
ticker[:market].underscore_name
end
expose(
:base_currency,
documentation: {
type: String,
desc: 'Symbol/currency code of base pair, eg. BTC.'
}
) do |ticker|
ticker[:market][:base_unit].upcase
end
expose(
:target_currency,
documentation: {
type: String,
desc: 'Symbol/currency code of target pair, eg. ETH.'
}
) do |ticker|
ticker[:market][:quote_unit].upcase
end
expose(
:last_price,
documentation: {
type: BigDecimal,
desc: 'Last transacted price of base currency based on given target currency.'
}
) do |ticker|
ticker[:last]
end
expose(
:base_volume,
documentation: {
type: BigDecimal,
desc: '24 hour trading volume in base pair volume.'
}
) do |ticker|
ticker[:amount]
end
expose(
:target_volume,
documentation: {
type: BigDecimal,
desc: '24 hour trading volume in base pair volume.'
}
) do |ticker|
ticker[:volume]
end
expose(
:bid,
documentation: {
type: BigDecimal,
desc: 'Current highest bid price.'
}
) do |ticker|
OrderBid.get_depth(ticker[:market].id).flatten.first.to_d
end
expose(
:ask,
documentation: {
type: BigDecimal,
desc: 'Current lowest ask price.'
}
) do |ticker|
OrderAsk.get_depth(ticker[:market].id).flatten.first.to_d
end
expose(
:high,
documentation: {
type: BigDecimal,
desc: 'The highest trade price during last 24 hours (0.0 if no trades executed during last 24 hours).'
}
) do |ticker|
ticker[:high]
end
expose(
:low,
documentation: {
type: BigDecimal,
desc: 'The lowest trade price during last 24 hours (0.0 if no trades executed during last 24 hours).'
}
) do |ticker|
ticker[:low]
end
end
end
end
end
end

View File

@@ -0,0 +1,22 @@
# frozen_string_literal: true
module API
module V2
module CoinGecko
module Helpers
MILLISECONDS_IN_SECOND = 1000
def format_trade(trade)
{
trade_id: trade[:id],
price: trade[:price],
base_volume: trade[:amount],
target_volume: trade[:total],
trade_timestamp: trade[:created_at] * MILLISECONDS_IN_SECOND,
type: trade[:taker_type]
}
end
end
end
end
end

View File

@@ -0,0 +1,43 @@
# frozen_string_literal: true
module API
module V2
module CoinGecko
class HistoricalTrades < Grape::API
desc 'Get recent trades on market'
params do
requires :ticker_id,
type: String,
desc: 'A pair such as "LTC_BTC"',
coerce_with: ->(name) { name.strip.split('_').join.downcase }
optional :type,
type: String,
values: { value: %w(buy sell), message: 'coingecko.historical_trades.invalid_type' },
desc: 'To indicate nature of trade - buy/sell'
optional :limit,
type: Integer,
values: { value: 0..1000, message: 'coingecko.historical_trades.invalid_limit' },
desc: 'Number of historical trades to retrieve from time of query. [0, 200, 500...]. 0 returns full history'
optional :start_time,
type: Integer,
desc: '',
coerce_with: ->(start_time) { Time.parse(start_time).to_i }
optional :end_time,
type: Integer,
desc: '',
coerce_with: ->(end_time) { Time.parse(end_time).to_i }
end
get '/historical_trades' do
market = ::Market.find(params[:ticker_id])
filters = declared(params, include_missing: false)
.except(:ticker_id, :limit)
Trade.public_from_influx(market.id, params[:limit], filters).each_with_object({'buy' => [], 'sell' => []}) do |trade, hash|
hash[trade[:taker_type]] << format_trade(trade)
end
end
end
end
end
end

View File

@@ -0,0 +1,20 @@
# frozen_string_literal: true
module API
module V2
module CoinGecko
class Mount < Grape::API
PREFIX = '/coingecko'
before { set_ets_context! }
helpers CoinGecko::Helpers
mount CoinGecko::Pairs
mount CoinGecko::Tickers
mount CoinGecko::Orderbook
mount CoinGecko::HistoricalTrades
end
end
end
end

View File

@@ -0,0 +1,39 @@
# frozen_string_literal: true
module API
module V2
module CoinGecko
class Orderbook < Grape::API
class OrderBook < Struct.new(:market, :asks, :bids); end
desc 'Get depth or specified market'
params do
requires :ticker_id,
type: String,
desc: 'A pair such as "LTC_BTC"',
coerce_with: ->(name) { name.strip.split('_').join.downcase }
optional :depth,
type: { value: Integer, message: 'coingecko.market_depth.non_integer_depth' },
values: { value: 0..1000, message: 'coingecko.market_depth.invalid_depth' },
desc: 'Orders depth quantity: [0, 100, 200, 500...]'
end
get '/orderbook' do
market = ::Market.find(params[:ticker_id])
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
orderbook = OrderBook.new market, asks, bids
present orderbook, with: API::V2::CoinGecko::Entities::Orderbook
end
end
end
end
end

View File

@@ -0,0 +1,16 @@
# encoding: UTF-8
# frozen_string_literal: true
module API
module V2
module CoinGecko
class Pairs < Grape::API
desc 'Get list of all available trading pairs'
get "/pairs" do
present ::Rails.cache.fetch(:markets_coingecko, expires_in: 60) { ::Market.enabled.ordered },
with: API::V2::CoinGecko::Entities::Pair
end
end
end
end
end

View File

@@ -0,0 +1,21 @@
# encoding: UTF-8
# frozen_string_literal: true
module API
module V2
module CoinGecko
class Tickers < Grape::API
desc 'Get list of all available trading pairs'
get "/tickers" do
tickers = ::Rails.cache.fetch(:markets_tickers_coingecko, expires_in: 60) do
::Market.enabled.ordered.inject([]) do |hash, market|
hash << TickersService[market].ticker.merge(market: market)
end
end
present tickers, with: API::V2::CoinGecko::Entities::Ticker
end
end
end
end
end