Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package co.nilin.opex.api.core.inout

data class Currency(val symbol: String, val name: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package co.nilin.opex.api.core.inout

import java.math.BigDecimal

data class CurrencyImplementation(
val currency: Currency,
val implCurrency: Currency,
val chain: Chain,
val token: Boolean,
val tokenAddress: String?,
val tokenName: String?,
val withdrawEnabled: Boolean,
val withdrawFee: BigDecimal,
val withdrawMin: BigDecimal,
val decimal: Int
)
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package co.nilin.opex.api.core.spi

import co.nilin.opex.api.core.inout.AssignResponse
import co.nilin.opex.api.core.inout.CurrencyImplementation
import co.nilin.opex.api.core.inout.DepositDetails

interface BlockchainGatewayProxy {
Expand All @@ -9,4 +10,6 @@ interface BlockchainGatewayProxy {

suspend fun getDepositDetails(refs: List<String>): List<DepositDetails>

suspend fun getCurrencyImplementations(currency: String? = null): List<CurrencyImplementation>

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface MarketUserDataProxy {

suspend fun queryOrder(principal: Principal, symbol: String, orderId: Long?, origClientOrderId: String?): Order?

suspend fun openOrders(principal: Principal, symbol: String?): List<Order>
suspend fun openOrders(principal: Principal, symbol: String?, limit: Int?): List<Order>

suspend fun allOrders(
principal: Principal,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class SecurityConfig(private val webClient: WebClient) {
.pathMatchers("/v3/trades").permitAll()
.pathMatchers("/v3/ticker/**").permitAll()
.pathMatchers("/v3/exchangeInfo").permitAll()
.pathMatchers("/v3/currencyInfo").permitAll()
.pathMatchers("/v3/klines").permitAll()
.pathMatchers("/socket").permitAll()
.pathMatchers("/v1/landing/**").permitAll()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,41 +51,41 @@ class AccountController(
)
)
suspend fun createNewOrder(
@RequestParam(name = "symbol")
@RequestParam
symbol: String,
@RequestParam(name = "side")
@RequestParam
side: OrderSide,
@RequestParam(name = "type")
@RequestParam
type: OrderType,
@RequestParam(name = "timeInForce", required = false)
@RequestParam(required = false)
timeInForce: TimeInForce?,
@RequestParam(name = "quantity", required = false)
@RequestParam(required = false)
quantity: BigDecimal?,
@RequestParam(name = "quoteOrderQty", required = false)
@RequestParam(required = false)
quoteOrderQty: BigDecimal?,
@RequestParam(name = "price", required = false)
@RequestParam(required = false)
price: BigDecimal?,
@ApiParam(
value = "A unique id among open orders. Automatically generated if not sent.\n" +
"Orders with the same newClientOrderID can be accepted only when the previous one is filled, otherwise the order will be rejected."
)
@RequestParam(name = "newClientOrderId", required = false)
@RequestParam(required = false)
newClientOrderId: String?, /* A unique id among open orders. Automatically generated if not sent.
Orders with the same newClientOrderID can be accepted only when the previous one is filled, otherwise the order will be rejected.
*/
@ApiParam(value = "Used with STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, and TAKE_PROFIT_LIMIT orders.")
@RequestParam(name = "stopPrice", required = false)
@RequestParam(required = false)
stopPrice: BigDecimal?, //Used with STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, and TAKE_PROFIT_LIMIT orders.
@RequestParam(name = "icebergQty", required = false)
@RequestParam(required = false)
@ApiParam(value = "Used with LIMIT, STOP_LOSS_LIMIT, and TAKE_PROFIT_LIMIT to create an iceberg order.")
icebergQty: BigDecimal?, //Used with LIMIT, STOP_LOSS_LIMIT, and TAKE_PROFIT_LIMIT to create an iceberg order.
@RequestParam(name = "newOrderRespType", required = false)
@RequestParam(required = false)
@ApiParam(value = "Set the response JSON. ACK, RESULT, or FULL; MARKET and LIMIT order types default to FULL, all other orders default to ACK.")
newOrderRespType: OrderResponseType?, //Set the response JSON. ACK, RESULT, or FULL; MARKET and LIMIT order types default to FULL, all other orders default to ACK.
@ApiParam(value = "The value cannot be greater than 60000")
@RequestParam(name = "recvWindow", required = false)
@RequestParam(required = false)
recvWindow: Long?, //The value cannot be greater than 60000
@RequestParam(name = "timestamp")
@RequestParam
timestamp: Long,
@CurrentSecurityContext securityContext: SecurityContext
): NewOrderResponse {
Expand Down Expand Up @@ -125,18 +125,18 @@ class AccountController(
)
suspend fun cancelOrder(
principal: Principal,
@RequestParam(name = "symbol")
@RequestParam
symbol: String,
@RequestParam(name = "orderId", required = false)
@RequestParam(required = false)
orderId: Long?, //Either orderId or origClientOrderId must be sent.
@RequestParam(name = "origClientOrderId", required = false)
@RequestParam(required = false)
origClientOrderId: String?,
@RequestParam(name = "newClientOrderId", required = false)
@RequestParam(required = false)
newClientOrderId: String?,
@ApiParam(value = "The value cannot be greater than 60000")
@RequestParam(name = "recvWindow", required = false)
@RequestParam(required = false)
recvWindow: Long?, //The value cannot be greater than 60000
@RequestParam(name = "timestamp")
@RequestParam
timestamp: Long,
@CurrentSecurityContext securityContext: SecurityContext
): CancelOrderResponse {
Expand Down Expand Up @@ -203,16 +203,16 @@ class AccountController(
)
suspend fun queryOrder(
principal: Principal,
@RequestParam(name = "symbol")
@RequestParam
symbol: String,
@RequestParam(name = "orderId", required = false)
@RequestParam(required = false)
orderId: Long?,
@RequestParam(name = "origClientOrderId", required = false)
@RequestParam(required = false)
origClientOrderId: String?,
@ApiParam(value = "The value cannot be greater than 60000")
@RequestParam(name = "recvWindow", required = false)
@RequestParam(required = false)
recvWindow: Long?, //The value cannot be greater than 60000
@RequestParam(name = "timestamp")
@RequestParam
timestamp: Long
): QueryOrderResponse {
val internalSymbol = symbolMapper.toInternalSymbol(symbol) ?: throw OpexException(OpexError.SymbolNotFound)
Expand Down Expand Up @@ -246,16 +246,18 @@ class AccountController(
)
suspend fun fetchOpenOrders(
principal: Principal,
@RequestParam(name = "symbol", required = false)
@RequestParam(required = false)
symbol: String?,
@ApiParam(value = "The value cannot be greater than 60000")
@RequestParam(name = "recvWindow", required = false)
@RequestParam(required = false)
recvWindow: Long?, //The value cannot be greater than 60000
@RequestParam(name = "timestamp")
timestamp: Long
@RequestParam
timestamp: Long,
@RequestParam(required = false)
limit: Int?
): List<QueryOrderResponse> {
val internalSymbol = symbolMapper.toInternalSymbol(symbol) ?: throw OpexException(OpexError.SymbolNotFound)
return queryHandler.openOrders(principal, internalSymbol).map {
return queryHandler.openOrders(principal, internalSymbol, limit).map {
it.asQueryOrderResponse().apply { symbol?.let { s -> this.symbol = s } }
}
}
Expand All @@ -282,19 +284,19 @@ class AccountController(
)
suspend fun fetchAllOrders(
principal: Principal,
@RequestParam(name = "symbol", required = false)
@RequestParam(required = false)
symbol: String?,
@RequestParam(name = "startTime", required = false)
@RequestParam(required = false)
startTime: Date?,
@RequestParam(name = "endTime", required = false)
@RequestParam(required = false)
endTime: Date?,
@ApiParam(value = "Default 500; max 1000.")
@RequestParam(name = "limit", required = false)
limit: Int? = 500, //Default 500; max 1000.
@RequestParam(required = false)
limit: Int?, //Default 500; max 1000.
@ApiParam(value = "The value cannot be greater than 60000")
@RequestParam(name = "recvWindow", required = false)
@RequestParam(required = false)
recvWindow: Long?, //The value cannot be greater than 60000
@RequestParam(name = "timestamp")
@RequestParam
timestamp: Long
): List<QueryOrderResponse> {
val internalSymbol = symbolMapper.toInternalSymbol(symbol) ?: throw OpexException(OpexError.SymbolNotFound)
Expand Down Expand Up @@ -326,22 +328,22 @@ class AccountController(
)
suspend fun fetchAllTrades(
principal: Principal,
@RequestParam(name = "symbol")
@RequestParam
symbol: String?,
@RequestParam(name = "startTime", required = false)
@RequestParam(required = false)
startTime: Date?,
@RequestParam(name = "endTime", required = false)
@RequestParam(required = false)
endTime: Date?,
@ApiParam(value = "TradeId to fetch from. Default gets most recent trades.")
@RequestParam(name = "fromId", required = false)
@RequestParam(required = false)
fromId: Long?,//TradeId to fetch from. Default gets most recent trades.
@ApiParam(value = "Default 500; max 1000.")
@RequestParam(name = "limit", required = false)
limit: Int? = 500, //Default 500; max 1000.
@RequestParam(required = false)
limit: Int?, //Default 500; max 1000.
@ApiParam(value = "The value cannot be greater than 60000")
@RequestParam(name = "recvWindow", required = false)
@RequestParam(required = false)
recvWindow: Long?, //The value cannot be greater than 60000
@RequestParam(name = "timestamp")
@RequestParam
timestamp: Long
): List<TradeResponse> {
val internalSymbol = symbolMapper.toInternalSymbol(symbol) ?: throw OpexException(OpexError.SymbolNotFound)
Expand Down Expand Up @@ -384,9 +386,9 @@ class AccountController(
suspend fun accountInfo(
@CurrentSecurityContext securityContext: SecurityContext,
@ApiParam(value = "The value cannot be greater than 60000")
@RequestParam(name = "recvWindow", required = false)
@RequestParam(required = false)
recvWindow: Long?, //The value cannot be greater than 60000
@RequestParam(name = "timestamp")
@RequestParam
timestamp: Long
): AccountInfoResponse {
val auth = securityContext.jwtAuthentication()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package co.nilin.opex.api.ports.binance.controller

import co.nilin.opex.api.core.inout.TradeVolumeStat
import co.nilin.opex.api.core.spi.GlobalMarketProxy
import co.nilin.opex.api.core.spi.MarketDataProxy
import co.nilin.opex.api.core.spi.MarketStatProxy
Expand All @@ -8,11 +9,14 @@ import co.nilin.opex.api.core.utils.Interval
import co.nilin.opex.api.ports.binance.data.GlobalPriceResponse
import co.nilin.opex.api.ports.binance.data.MarketInfoResponse
import co.nilin.opex.api.ports.binance.data.MarketStatResponse
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import org.slf4j.LoggerFactory
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import java.math.BigDecimal

@RestController // Custom service
@RequestMapping("/v1/landing")
Expand Down Expand Up @@ -42,21 +46,40 @@ class LandingController(
suspend fun getMarketStats(
@RequestParam interval: String,
@RequestParam(required = false) limit: Int?
): MarketStatResponse {
): MarketStatResponse = coroutineScope {
val since = (Interval.findByLabel(interval) ?: Interval.Week).getDate().time
val validLimit = getValidLimit(limit)
val symbols = symbolMapper.symbolToAliasMap()


return MarketStatResponse(
val mostIncreased = async {
marketStatProxy.getMostIncreasedInPricePairs(since, validLimit).onEach {
symbols[it.symbol]?.let { s -> it.symbol = s }
},
}
}

val mostDecreased = async {
marketStatProxy.getMostDecreasedInPricePairs(since, validLimit).onEach {
symbols[it.symbol]?.let { s -> it.symbol = s }
},
marketStatProxy.getHighestVolumePair(since)?.apply { symbols[symbol]?.let { symbol = it } },
marketStatProxy.getTradeCountPair(since)?.apply { symbols[symbol]?.let { symbol = it } }
}
}

val highestVolume = async {
marketStatProxy.getHighestVolumePair(since)
?.apply { symbols[symbol]?.let { symbol = it } }
?: TradeVolumeStat(symbols.entries.random().value, BigDecimal.ZERO, BigDecimal.ZERO, 0.0)
}

val mostTrades = async {
marketStatProxy.getTradeCountPair(since)
?.apply { symbols[symbol]?.let { symbol = it } }
?: TradeVolumeStat(symbols.entries.random().value, BigDecimal.ZERO, BigDecimal.ZERO, 0.0)
}

MarketStatResponse(
mostIncreased.await(),
mostDecreased.await(),
highestVolume.await(),
mostTrades.await()
)
}

Expand Down
Loading