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
4 changes: 2 additions & 2 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ jobs:
java-version: ${{ matrix.java }}
# cache: maven
- name: Build
run: mvn -B -T 1C clean install -Potc
run: mvn -B clean install -Potc
- name: Run Tests
run: mvn -B -T 1C -Dskip.unit.tests=false surefire:test
run: mvn -B -Dskip.unit.tests=false surefire:test
- name: Build Docker images
env:
TAG: pr
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ data class OrderData(
val quantity: BigDecimal,
val takerFee: BigDecimal,
val makerFee: BigDecimal,
val status: OrderStatus,
val status: Int,
val appearance: Int,
val createDate: LocalDateTime,
val updateDate: LocalDateTime,
)
)
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,42 @@ enum class TimeInForce {
FOK, //Fill or Kill, An order will expire if the full order cannot be filled upon execution.
}

enum class OrderStatus(val code: Int) {
enum class OrderStatus(val code: Int, val orderOfAppearance: Int) {

REQUESTED(0),
NEW(1), //The order has been accepted by the engine.
PARTIALLY_FILLED(4), //A part of the order has been filled.
FILLED(5), //The order has been completed.
CANCELED(2), //The order has been canceled by the user.
REJECTED(3), //The order was not accepted by the engine and not processed.
EXPIRED(6); //The order was canceled according to the order type's rules (e.g. LIMIT FOK orders with no fill, LIMIT IOC or MARKET orders that partially fill) or by the exchange, (e.g. orders canceled during liquidation, orders canceled during maintenance)
REQUESTED(0, 0),
NEW(1, 1), //The order has been accepted by the engine.
PARTIALLY_FILLED(4, 2), //A part of the order has been filled.
FILLED(5, 3), //The order has been completed.
CANCELED(2, 3), //The order has been canceled by the user.
REJECTED(3, 3), //The order was not accepted by the engine and not processed.
EXPIRED(
6,
3
); //The order was canceled according to the order type's rules (e.g. LIMIT FOK orders with no fill, LIMIT IOC or MARKET orders that partially fill) or by the exchange, (e.g. orders canceled during liquidation, orders canceled during maintenance)

fun comesBefore(status: OrderStatus?): Boolean {
if (status == null)
return false
return orderOfAppearance < status.orderOfAppearance
}

fun comesAfter(status: OrderStatus?): Boolean {
if (status == null)
return false
return orderOfAppearance > status.orderOfAppearance
}

fun isOpenOrder(): Boolean {
return this == NEW || this == PARTIALLY_FILLED
}

companion object {
fun fromCode(code: Int?): OrderStatus? {
if (code == null)
return null
return values().find { it.code == code }
}
}
fun isWorking(): Boolean {
return listOf(NEW, PARTIALLY_FILLED).contains(this)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package co.nilin.opex.api.core.inout

import java.math.BigDecimal
import java.time.LocalDateTime

data class SwapResponse(
var reserveNumber: String,
var sourceSymbol: String,
var destSymbol: String,
var uuid: String,
var sourceAmount: BigDecimal,
var reservedDestAmount: BigDecimal,
var reserveDate: LocalDateTime? = LocalDateTime.now(),
var expDate: LocalDateTime? = null,
var status: ReservedStatus? = null,
val rate: BigDecimal? = null,
)
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
package co.nilin.opex.api.core.inout

data class UserTransactionRequest(
val userId: String? = null,
val currency: String?,
val sourceSymbol: String?,
val destSymbol: String?,
val category: UserTransactionCategory?,
val startTime: Long? = null,
val endTime: Long? = null,
val limit: Int? = 10,
val offset: Int? = 0,
val ascendingByTime: Boolean? = false,
)
val ascendingByTime: Boolean = false,
val status: ReservedStatus? = ReservedStatus.Committed
)

enum class ReservedStatus {
Created, Expired, Committed,
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,5 +123,6 @@ interface WalletProxy {

suspend fun getQuoteCurrencies(): List<QuoteCurrency>

suspend fun getSwapTransactionsCount(token: String, request: UserTransactionRequest):Long
suspend fun getSwapTransactions(token: String, request: UserTransactionRequest): List<SwapResponse>
suspend fun getSwapTransactionsCount(token: String, request: UserTransactionRequest): Long
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,13 @@ package co.nilin.opex.api.ports.opex.controller
import co.nilin.opex.api.core.inout.*
import co.nilin.opex.api.core.spi.MarketUserDataProxy
import co.nilin.opex.api.core.spi.WalletProxy
import co.nilin.opex.api.ports.opex.data.OrderDataResponse
import co.nilin.opex.api.ports.opex.util.jwtAuthentication
import co.nilin.opex.api.ports.opex.util.toResponse
import co.nilin.opex.api.ports.opex.util.tokenValue
import org.springframework.security.core.annotation.CurrentSecurityContext
import org.springframework.security.core.context.SecurityContext
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping("/opex/v1/user")
Expand All @@ -31,7 +28,7 @@ class UserHistoryController(
@RequestParam limit: Int?,
@RequestParam offset: Int?,
@CurrentSecurityContext securityContext: SecurityContext,
): List<OrderData> {
): List<OrderDataResponse> {
return marketUserDataProxy.getOrderHistory(
securityContext.authentication.name,
symbol,
Expand All @@ -41,7 +38,7 @@ class UserHistoryController(
direction,
limit ?: 10,
offset ?: 0,
)
).map { it.toResponse() }
}

@GetMapping("/history/order/count")
Expand Down Expand Up @@ -256,6 +253,15 @@ class UserHistoryController(
limit,
)
}

@PostMapping("/history/swap")
suspend fun getSwapHistory(
@CurrentSecurityContext securityContext: SecurityContext,
@RequestBody request: UserTransactionRequest
): List<SwapResponse> {
return walletProxy.getSwapTransactions(securityContext.jwtAuthentication().tokenValue(), request)
}

@PostMapping("/history/swap/count")
suspend fun getSwapHistoryCount(
@CurrentSecurityContext securityContext: SecurityContext,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package co.nilin.opex.api.ports.opex.data

import co.nilin.opex.api.core.inout.MatchingOrderType
import co.nilin.opex.api.core.inout.OrderDirection
import co.nilin.opex.api.core.inout.OrderStatus
import java.math.BigDecimal
import java.time.LocalDateTime
import java.util.*

data class OrderDataResponse(
val symbol: String,
val orderType: MatchingOrderType,
val side: OrderDirection,
val price: BigDecimal,
val quantity: BigDecimal,
val takerFee: BigDecimal,
val makerFee: BigDecimal,
val status: OrderStatus,
val createDate: LocalDateTime,
val updateDate: LocalDateTime,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package co.nilin.opex.api.ports.opex.util

import co.nilin.opex.api.core.inout.OrderData
import co.nilin.opex.api.core.inout.OrderStatus
import co.nilin.opex.api.ports.opex.data.OrderDataResponse

fun OrderData.toResponse(): OrderDataResponse {
return OrderDataResponse(
symbol = this.symbol,
orderType = this.orderType,
side = this.side,
price = this.price,
quantity = this.quantity,
takerFee = this.takerFee,
makerFee = this.makerFee,
status = OrderStatus.fromCode(this.status) ?: OrderStatus.REJECTED,
createDate = this.createDate,
updateDate = this.updateDate,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -181,13 +181,17 @@ class WalletProxyImpl(private val webClient: WebClient) : WalletProxy {
.body(
Mono.just(
UserTransactionRequest(
null,
currency,
null,
null,
category,
startTime,
endTime,
limit,
offset,
ascendingByTime
ascendingByTime == true,
null
)
)
)
Expand All @@ -210,7 +214,20 @@ class WalletProxyImpl(private val webClient: WebClient) : WalletProxy {
.uri("$baseUrl/v2/transaction/count")
.accept(MediaType.APPLICATION_JSON)
.header(HttpHeaders.AUTHORIZATION, "Bearer $token")
.body(Mono.just(UserTransactionRequest(currency, category, startTime, endTime)))
.body(
Mono.just(
UserTransactionRequest(
null,
currency,
null,
null,
category,
startTime,
endTime,
null
)
)
)
.retrieve()
.onStatus({ t -> t.isError }, { it.createException() })
.bodyToMono<Long>()
Expand Down Expand Up @@ -409,6 +426,19 @@ class WalletProxyImpl(private val webClient: WebClient) : WalletProxy {
}
}

override suspend fun getSwapTransactions(token: String, request: UserTransactionRequest): List<SwapResponse> {
return webClient.post()
.uri("$baseUrl/v1/swap/history")
.accept(MediaType.APPLICATION_JSON)
.header(HttpHeaders.AUTHORIZATION, "Bearer $token")
.body(Mono.just(request))
.retrieve()
.onStatus({ t -> t.isError }, { it.createException() })
.bodyToFlux<SwapResponse>()
.collectList()
.awaitFirstOrElse { emptyList() }
}

override suspend fun getSwapTransactionsCount(
token: String,
request: UserTransactionRequest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,6 @@ class UserDataController(private val userQueryHandler: UserQueryHandler) {
@RequestParam endTime: Long?,
@RequestParam orderType: MatchingOrderType?,
@RequestParam direction: OrderDirection?,
@RequestParam limit: Int?,
@RequestParam offset: Int?,
@PathVariable uuid: String,
): Long {
return userQueryHandler.getOrderHistoryCount(
Expand Down
4 changes: 4 additions & 0 deletions wallet/wallet-app/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@
<groupId>io.projectreactor.kotlin</groupId>
<artifactId>reactor-kotlin-extensions</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package co.nilin.opex.wallet.app.config

import co.nilin.opex.wallet.ports.postgres.util.CacheHelper
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import org.springframework.cache.CacheManager
import org.springframework.cache.annotation.EnableCaching
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.data.redis.cache.RedisCacheConfiguration
import org.springframework.data.redis.cache.RedisCacheManager.RedisCacheManagerBuilder
import org.springframework.data.redis.connection.RedisConnectionFactory
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer
import org.springframework.data.redis.serializer.RedisSerializationContext
import org.springframework.data.redis.serializer.StringRedisSerializer
import java.time.Duration

@Configuration
@EnableCaching
class CacheConfig {

@Bean
fun redisTemplate(connectionFactory: RedisConnectionFactory, mapper: ObjectMapper): RedisTemplate<String, Any> {
val newMapper = mapper.copy().apply {
activateDefaultTyping(mapper.polymorphicTypeValidator, ObjectMapper.DefaultTyping.EVERYTHING)
findAndRegisterModules()
registerKotlinModule()
}
return RedisTemplate<String, Any>().apply {
setConnectionFactory(connectionFactory)
val ser = GenericJackson2JsonRedisSerializer(newMapper)
valueSerializer = ser
hashValueSerializer = ser
keySerializer = StringRedisSerializer()
hashKeySerializer = StringRedisSerializer()
afterPropertiesSet()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ class WalletOwnerController(
private val walletOwnerManager: WalletOwnerManager,
private val walletManager: WalletManager,
private val environment: Environment,
private val currentUserProvider: CurrentUserProvider
private val currentUserProvider: CurrentUserProvider,
private val balanceParser: BalanceParser,
) {

@GetMapping("/{uuid}/wallets")
Expand All @@ -45,7 +46,7 @@ class WalletOwnerController(
throw OpexError.WalletOwnerNotFound.exception()
}
val wallets = walletManager.findWalletsByOwner(owner)
return BalanceParser.parse(wallets)
return balanceParser.parse(wallets)
}

@GetMapping("/{uuid}/wallets/{symbol}")
Expand All @@ -62,7 +63,7 @@ class WalletOwnerController(
suspend fun getWallet(@PathVariable uuid: String, @PathVariable symbol: String): WalletData {
val owner = walletOwnerManager.findWalletOwner(uuid) ?: throw OpexError.WalletOwnerNotFound.exception()
val wallets = walletManager.findWalletByOwnerAndSymbol(owner, symbol)
return BalanceParser.parseSingleCurrency(wallets) ?: throw OpexError.WalletNotFound.exception()
return balanceParser.parseSingleCurrency(wallets) ?: throw OpexError.WalletNotFound.exception()
}

@GetMapping("/{uuid}/limits")
Expand Down
Loading
Loading