From e6bd740c3e135d7e587804f08b6ca7a188e04ed1 Mon Sep 17 00:00:00 2001 From: Valentin Stavetski Date: Sat, 28 Apr 2018 17:42:48 +0300 Subject: [PATCH] #185 use own OrderBook entity add orderbook endpint to rest api add banner for rest api --- .../kotlin/fund/cyber/markets/common/Env.kt | 3 ++ .../fund/cyber/markets/common/model/Order.kt | 8 ++-- .../connector/api/OrderbookEndpoint.kt | 6 +-- .../connector/orderbook/OrderbookConnector.kt | 2 +- .../orderbook/XchangeOrderbookConnector.kt | 37 +++++++++++++- .../configuration/RestApiConfiguration.kt | 12 +++++ .../api/rest/controller/RawDataController.kt | 48 +++++++++++++++++++ rest-api/src/main/resources/application.yml | 2 + rest-api/src/main/resources/banner.txt | 21 ++++++++ 9 files changed, 129 insertions(+), 10 deletions(-) create mode 100644 rest-api/src/main/kotlin/fund/cyber/markets/api/rest/configuration/RestApiConfiguration.kt create mode 100644 rest-api/src/main/kotlin/fund/cyber/markets/api/rest/controller/RawDataController.kt create mode 100644 rest-api/src/main/resources/application.yml create mode 100644 rest-api/src/main/resources/banner.txt diff --git a/common/src/main/kotlin/fund/cyber/markets/common/Env.kt b/common/src/main/kotlin/fund/cyber/markets/common/Env.kt index b59c7cdc..2d7f167f 100644 --- a/common/src/main/kotlin/fund/cyber/markets/common/Env.kt +++ b/common/src/main/kotlin/fund/cyber/markets/common/Env.kt @@ -9,6 +9,9 @@ const val CASSANDRA_HOSTS_DEFAULT = "localhost" const val CASSANDRA_PORT = "CASSANDRA_PORT" const val CASSANDRA_PORT_DEFAULT = 9042 +const val EXCHANGES_CONNECTOR_SERVICE_URL = "EXCHANGES_CONNECTOR_SERVICE_URL" +const val EXCHANGES_CONNECTOR_SERVICE_URL_DEFAULT = "http://localhost:8080" + const val EXCHANGES = "EXCHANGES" const val EXCHANGES_DEFAULT = "bitfinex,bitflyer,binance,bitstamp,gdax,gemini,hitbtc,okcoin,okex,poloniex,etherdelta" diff --git a/common/src/main/kotlin/fund/cyber/markets/common/model/Order.kt b/common/src/main/kotlin/fund/cyber/markets/common/model/Order.kt index 83d0a19a..7ebe77ac 100644 --- a/common/src/main/kotlin/fund/cyber/markets/common/model/Order.kt +++ b/common/src/main/kotlin/fund/cyber/markets/common/model/Order.kt @@ -8,9 +8,9 @@ data class Order ( val exchange: String, val pair: TokensPair, val type: OrderType, - val timestamp: Date, + val timestamp: Long, val epochHour: Long, - val orderId: String, + val orderId: String?, val amount: BigDecimal, val price: BigDecimal ) @@ -22,7 +22,7 @@ data class OrdersBatch ( ) enum class OrderType { - SELL, - BUY, + BID, + ASK, UNKNOWN } \ No newline at end of file diff --git a/exchanges-connector/src/main/kotlin/fund/cyber/markets/connector/api/OrderbookEndpoint.kt b/exchanges-connector/src/main/kotlin/fund/cyber/markets/connector/api/OrderbookEndpoint.kt index 472a396e..ccb893cd 100644 --- a/exchanges-connector/src/main/kotlin/fund/cyber/markets/connector/api/OrderbookEndpoint.kt +++ b/exchanges-connector/src/main/kotlin/fund/cyber/markets/connector/api/OrderbookEndpoint.kt @@ -1,7 +1,7 @@ package fund.cyber.markets.connector.api +import fund.cyber.markets.common.model.TokensPair import fund.cyber.markets.connector.ConnectorRunner -import org.knowm.xchange.currency.CurrencyPair import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping @@ -21,8 +21,8 @@ class OrderbookEndpoint( @RequestParam(value = "pair", required = true) tokensPair: String ): ResponseEntity { - val pair = CurrencyPair(tokensPair.substringBefore("_"), tokensPair.substringAfter("_")) - val orderbook = connectors[exchange.toUpperCase()]?.orderbooks?.get(pair) + val pair = TokensPair(tokensPair.substringBefore("_"), tokensPair.substringAfter("_")) + val orderbook = connectors[exchange.toUpperCase()]?.getOrderBookSnapshot(pair) return if (orderbook == null) { ResponseEntity(HttpStatus.NOT_FOUND) diff --git a/exchanges-connector/src/main/kotlin/fund/cyber/markets/connector/orderbook/OrderbookConnector.kt b/exchanges-connector/src/main/kotlin/fund/cyber/markets/connector/orderbook/OrderbookConnector.kt index 7c0a19fc..f82c41ba 100644 --- a/exchanges-connector/src/main/kotlin/fund/cyber/markets/connector/orderbook/OrderbookConnector.kt +++ b/exchanges-connector/src/main/kotlin/fund/cyber/markets/connector/orderbook/OrderbookConnector.kt @@ -7,5 +7,5 @@ import org.knowm.xchange.dto.marketdata.OrderBook interface OrderbookConnector : Connector { var orderbooks: MutableMap - fun getOrderBookSnapshot(pair: TokensPair): fund.cyber.markets.common.model.OrderBook + fun getOrderBookSnapshot(pair: TokensPair): fund.cyber.markets.common.model.OrderBook? } \ No newline at end of file diff --git a/exchanges-connector/src/main/kotlin/fund/cyber/markets/connector/orderbook/XchangeOrderbookConnector.kt b/exchanges-connector/src/main/kotlin/fund/cyber/markets/connector/orderbook/XchangeOrderbookConnector.kt index 1730c2e3..db5fc475 100644 --- a/exchanges-connector/src/main/kotlin/fund/cyber/markets/connector/orderbook/XchangeOrderbookConnector.kt +++ b/exchanges-connector/src/main/kotlin/fund/cyber/markets/connector/orderbook/XchangeOrderbookConnector.kt @@ -1,5 +1,9 @@ package fund.cyber.markets.connector.orderbook +import fund.cyber.markets.common.MILLIS_TO_HOURS +import fund.cyber.markets.common.convert +import fund.cyber.markets.common.model.Order +import fund.cyber.markets.common.model.OrderType import fund.cyber.markets.common.model.TokensPair import fund.cyber.markets.connector.AbstarctXchangeConnector import info.bitrich.xchangestream.core.ProductSubscription @@ -7,7 +11,9 @@ import info.bitrich.xchangestream.core.StreamingExchangeFactory import io.micrometer.core.instrument.MeterRegistry import org.knowm.xchange.currency.CurrencyPair import org.knowm.xchange.dto.marketdata.OrderBook +import org.knowm.xchange.dto.trade.LimitOrder import org.springframework.kafka.core.KafkaTemplate +import java.util.* class XchangeOrderbookConnector : AbstarctXchangeConnector, OrderbookConnector { override var orderbooks: MutableMap = mutableMapOf() @@ -46,8 +52,35 @@ class XchangeOrderbookConnector : AbstarctXchangeConnector, OrderbookConnector { } } - override fun getOrderBookSnapshot(pair: TokensPair): fund.cyber.markets.common.model.OrderBook { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + override fun getOrderBookSnapshot(pair: TokensPair): fund.cyber.markets.common.model.OrderBook? { + val orderBook = orderbooks[CurrencyPair(pair.base, pair.quote)] ?: return null + val timestamp: Long = orderBook.timeStamp?.time ?: Date().time + + val asks = mutableListOf() + val bids = mutableListOf() + + orderBook.asks.forEach { xchangeOrder -> + asks.add(convertOrder(xchangeOrder, pair, OrderType.ASK, timestamp)) + } + + orderBook.bids.forEach { xchangeOrder -> + asks.add(convertOrder(xchangeOrder, pair, OrderType.BID, timestamp)) + } + + return fund.cyber.markets.common.model.OrderBook(asks, bids, timestamp) + } + + private fun convertOrder(xchangeOrder: LimitOrder, pair: TokensPair, type: OrderType, orderBookTimestamp: Long): Order { + val timestamp: Long = xchangeOrder.timestamp?.time ?: orderBookTimestamp + + return Order(exchangeName, + pair, + type, + timestamp, + timestamp convert MILLIS_TO_HOURS, + xchangeOrder.id, + xchangeOrder.originalAmount, + xchangeOrder.limitPrice) } } \ No newline at end of file diff --git a/rest-api/src/main/kotlin/fund/cyber/markets/api/rest/configuration/RestApiConfiguration.kt b/rest-api/src/main/kotlin/fund/cyber/markets/api/rest/configuration/RestApiConfiguration.kt new file mode 100644 index 00000000..acd555c2 --- /dev/null +++ b/rest-api/src/main/kotlin/fund/cyber/markets/api/rest/configuration/RestApiConfiguration.kt @@ -0,0 +1,12 @@ +package fund.cyber.markets.api.rest.configuration + +import fund.cyber.markets.common.EXCHANGES_CONNECTOR_SERVICE_URL +import fund.cyber.markets.common.EXCHANGES_CONNECTOR_SERVICE_URL_DEFAULT +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Configuration + +@Configuration +class RestApiConfiguration( + @Value("\${$EXCHANGES_CONNECTOR_SERVICE_URL:$EXCHANGES_CONNECTOR_SERVICE_URL_DEFAULT}") + val exchangesConnectorServiceUrl: String +) \ No newline at end of file diff --git a/rest-api/src/main/kotlin/fund/cyber/markets/api/rest/controller/RawDataController.kt b/rest-api/src/main/kotlin/fund/cyber/markets/api/rest/controller/RawDataController.kt new file mode 100644 index 00000000..7cac9848 --- /dev/null +++ b/rest-api/src/main/kotlin/fund/cyber/markets/api/rest/controller/RawDataController.kt @@ -0,0 +1,48 @@ +package fund.cyber.markets.api.rest.controller + +import fund.cyber.markets.api.rest.configuration.RestApiConfiguration +import fund.cyber.markets.common.model.OrderBook +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController +import org.springframework.web.client.HttpClientErrorException +import org.springframework.web.client.RestTemplate +import org.springframework.web.util.UriComponentsBuilder +import reactor.core.publisher.Mono + + +const val ORDERBOOK_PATH = "/orderbook" + +@RestController +class RawDataController { + + @Autowired + private lateinit var configuration: RestApiConfiguration + + private val restTemplate = RestTemplate() + + @GetMapping("/orderbook") + fun getOrderBook( + @RequestParam exchange: String, + @RequestParam pair: String, + @RequestParam(required = false) ts: Long? + ): Mono { + + val requestUri = configuration.exchangesConnectorServiceUrl + ORDERBOOK_PATH + + val builder = UriComponentsBuilder.fromUriString(requestUri) + .queryParam("exchange", exchange) + .queryParam("pair", pair) + + var response: OrderBook? = null + try { + response = restTemplate.getForObject(builder.toUriString(), OrderBook::class.java) + } catch (e: HttpClientErrorException) { + + } + + return Mono.just(response!!) + } + +} \ No newline at end of file diff --git a/rest-api/src/main/resources/application.yml b/rest-api/src/main/resources/application.yml new file mode 100644 index 00000000..0cc0712d --- /dev/null +++ b/rest-api/src/main/resources/application.yml @@ -0,0 +1,2 @@ +server: + port: 8085 diff --git a/rest-api/src/main/resources/banner.txt b/rest-api/src/main/resources/banner.txt new file mode 100644 index 00000000..44bfd5f6 --- /dev/null +++ b/rest-api/src/main/resources/banner.txt @@ -0,0 +1,21 @@ + _____ _____ _____ _____ _____ _____ _____ + /\ \ /\ \ /\ \ /\ \ /\ \ /\ \ /\ \ + /::\ \ /::\ \ /::\ \ /::\ \ /::\ \ /::\ \ /::\ \ + /::::\ \ /::::\ \ /::::\ \ \:::\ \ /::::\ \ /::::\ \ \:::\ \ + /::::::\ \ /::::::\ \ /::::::\ \ \:::\ \ /::::::\ \ /::::::\ \ \:::\ \ + /:::/\:::\ \ /:::/\:::\ \ /:::/\:::\ \ \:::\ \ /:::/\:::\ \ /:::/\:::\ \ \:::\ \ + /:::/__\:::\ \ /:::/__\:::\ \ /:::/__\:::\ \ \:::\ \ /:::/__\:::\ \ /:::/__\:::\ \ \:::\ \ + /::::\ \:::\ \ /::::\ \:::\ \ \:::\ \:::\ \ /::::\ \ /::::\ \:::\ \ /::::\ \:::\ \ /::::\ \ + /::::::\ \:::\ \ /::::::\ \:::\ \ ___\:::\ \:::\ \ /::::::\ \ /::::::\ \:::\ \ /::::::\ \:::\ \ ____ /::::::\ \ + /:::/\:::\ \:::\____\ /:::/\:::\ \:::\ \ /\ \:::\ \:::\ \ /:::/\:::\ \ /:::/\:::\ \:::\ \ /:::/\:::\ \:::\____\ /\ \ /:::/\:::\ \ +/:::/ \:::\ \:::| |/:::/__\:::\ \:::\____\/::\ \:::\ \:::\____\ /:::/ \:::\____\ /:::/ \:::\ \:::\____\/:::/ \:::\ \:::| |/::\ \/:::/ \:::\____\ +\::/ |::::\ /:::|____|\:::\ \:::\ \::/ /\:::\ \:::\ \::/ / /:::/ \::/ / \::/ \:::\ /:::/ /\::/ \:::\ /:::|____|\:::\ /:::/ \::/ / + \/____|:::::\/:::/ / \:::\ \:::\ \/____/ \:::\ \:::\ \/____/ /:::/ / \/____/ \/____/ \:::\/:::/ / \/_____/\:::\/:::/ / \:::\/:::/ / \/____/ + |:::::::::/ / \:::\ \:::\ \ \:::\ \:::\ \ /:::/ / \::::::/ / \::::::/ / \::::::/ / + |::|\::::/ / \:::\ \:::\____\ \:::\ \:::\____\ /:::/ / \::::/ / \::::/ / \::::/____/ + |::| \::/____/ \:::\ \::/ / \:::\ /:::/ / \::/ / /:::/ / \::/____/ \:::\ \ + |::| ~| \:::\ \/____/ \:::\/:::/ / \/____/ /:::/ / ~~ \:::\ \ + |::| | \:::\ \ \::::::/ / /:::/ / \:::\ \ + \::| | \:::\____\ \::::/ / /:::/ / \:::\____\ + \:| | \::/ / \::/ / \::/ / \::/ / + \|___| \/____/ \/____/ \/____/ \/____/