Skip to content
Closed
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
Expand Up @@ -9,7 +9,10 @@ import co.nilin.opex.accountant.core.service.FinancialActionJobManagerImpl
import co.nilin.opex.accountant.core.service.OrderManagerImpl
import co.nilin.opex.accountant.core.service.TradeManagerImpl
import co.nilin.opex.accountant.core.spi.*
import co.nilin.opex.accountant.ports.kafka.listener.consumer.*
import co.nilin.opex.accountant.ports.kafka.listener.consumer.EventKafkaListener
import co.nilin.opex.accountant.ports.kafka.listener.consumer.OrderKafkaListener
import co.nilin.opex.accountant.ports.kafka.listener.consumer.TempEventKafkaListener
import co.nilin.opex.accountant.ports.kafka.listener.consumer.TradeKafkaListener
import co.nilin.opex.accountant.ports.kafka.listener.spi.FAResponseListener
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
Expand All @@ -36,26 +39,24 @@ class AppConfig {
@Bean
fun orderManager(
pairConfigLoader: PairConfigLoader,
userLevelLoader: UserLevelLoader,
financialActionPersister: FinancialActionPersister,
financeActionLoader: FinancialActionLoader,
orderPersister: OrderPersister,
tempEventPersister: TempEventPersister,
tempEventRepublisher: TempEventRepublisher,
richOrderPublisher: RichOrderPublisher,
financialActionPublisher: FinancialActionPublisher,
jsonMapper: JsonMapper
feeCalculator: FeeCalculator
): OrderManager {
return OrderManagerImpl(
pairConfigLoader,
userLevelLoader,
financialActionPersister,
financeActionLoader,
orderPersister,
tempEventPersister,
richOrderPublisher,
financialActionPublisher,
jsonMapper
feeCalculator
)
}

Expand All @@ -69,7 +70,8 @@ class AppConfig {
richOrderPublisher: RichOrderPublisher,
feeCalculator: FeeCalculator,
financialActionPublisher: FinancialActionPublisher,
jsonMapper: JsonMapper
currencyRatePersister: CurrencyRatePersister,
userVolumePersister: UserVolumePersister,
): TradeManager {
return TradeManagerImpl(
financeActionPersister,
Expand All @@ -80,7 +82,8 @@ class AppConfig {
richOrderPublisher,
feeCalculator,
financialActionPublisher,
jsonMapper
currencyRatePersister,
userVolumePersister
)
}

Expand Down Expand Up @@ -129,11 +132,8 @@ class AppConfig {
fun configureEventListener(
eventKafkaListener: EventKafkaListener,
accountantEventListener: AccountantEventListener,
kycLevelUpdatedKafkaListener: KycLevelUpdatedKafkaListener,
kycLevelUpdatedEventListener: KycLevelUpdatedListener
) {
eventKafkaListener.addListener(accountantEventListener)
kycLevelUpdatedKafkaListener.addEventListener(kycLevelUpdatedEventListener)
}

@Autowired
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package co.nilin.opex.accountant.app.controller

import co.nilin.opex.accountant.core.api.FeeCalculator
import co.nilin.opex.accountant.core.model.FeeConfig
import co.nilin.opex.accountant.core.model.UserFee
import co.nilin.opex.accountant.core.spi.FeeConfigService
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping("/fee")
class FeeController(
private val feeConfigService: FeeConfigService,
private val feeCalculator: FeeCalculator,
) {
@GetMapping("/config")
suspend fun getFeeConfigs(): List<FeeConfig> {
return feeConfigService.loadFeeConfigs()
}

@GetMapping("/{uuid}")
suspend fun getUserFee(@PathVariable uuid: String): UserFee {
return feeCalculator.getUserFee(uuid)
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
package co.nilin.opex.accountant.app.controller

import co.nilin.opex.accountant.app.data.PairFeeResponse
import co.nilin.opex.accountant.core.model.FeeConfig
import co.nilin.opex.accountant.core.model.PairConfig
import co.nilin.opex.accountant.core.model.PairFeeConfig
import co.nilin.opex.accountant.core.spi.FeeConfigService
import co.nilin.opex.accountant.core.spi.PairConfigLoader
import co.nilin.opex.common.OpexError
import co.nilin.opex.matching.engine.core.model.OrderDirection
import org.springframework.web.bind.annotation.*
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping("/config")
class PairConfigController(private val pairConfigLoader: PairConfigLoader) {

@GetMapping("/{pair}/fee/{direction}-{userLevel}")
suspend fun fetchPairFeeConfig(
@PathVariable("pair") pair: String,
@PathVariable("direction") direction: OrderDirection,
@PathVariable("userLevel") level: String
): PairFeeConfig {
return pairConfigLoader.load(pair, direction, level)
}
class PairConfigController(
private val pairConfigLoader: PairConfigLoader,
private val feeConfigService: FeeConfigService
) {

@GetMapping("/{pair}/{direction}")
suspend fun fetchPairConfig(
Expand All @@ -35,19 +31,7 @@ class PairConfigController(private val pairConfigLoader: PairConfigLoader) {
}

@GetMapping("/fee")
suspend fun getFeeConfigs(): List<PairFeeResponse> {
return pairConfigLoader.loadPairFeeConfigs()
.map { PairFeeResponse(it.pairConfig.pair, it.direction, it.userLevel, it.makerFee, it.takerFee) }
}

@GetMapping("/fee/{pair}")
suspend fun getFeeConfig(
@PathVariable pair: String,
@RequestParam(required = false) direction: OrderDirection?,
@RequestParam(required = false) userLevel: String?
): PairFeeResponse {
val fee = pairConfigLoader.loadPairFeeConfigs(pair, direction ?: OrderDirection.BID, userLevel ?: "*")
?: throw OpexError.PairFeeNotFound.exception()
return PairFeeResponse(fee.pairConfig.pair, fee.direction, fee.userLevel, fee.makerFee, fee.takerFee)
suspend fun getFeeConfigs(): List<FeeConfig> {
return feeConfigService.loadFeeConfigs()
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ package co.nilin.opex.accountant.core.api
import co.nilin.opex.accountant.core.model.FeeFinancialActions
import co.nilin.opex.accountant.core.model.FinancialAction
import co.nilin.opex.accountant.core.model.Order
import co.nilin.opex.accountant.core.model.UserFee
import co.nilin.opex.matching.engine.core.eventh.events.TradeEvent

interface FeeCalculator {

suspend fun getUserFee(uuid: String): UserFee

suspend fun createFeeActions(
trade: TradeEvent,
makerOrder: Order,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package co.nilin.opex.accountant.core.inout

import java.math.BigDecimal

data class UserCurrencyVolume(
val currency: String,
val volume: BigDecimal,
val valueUSDT: BigDecimal,
val valueIRT: BigDecimal
) {
companion object {
fun zero(currency: String) = UserCurrencyVolume(currency, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package co.nilin.opex.accountant.core.inout

import java.math.BigDecimal

data class UserTotalVolumeValue(
val valueUSDT: BigDecimal,
val valueIRT: BigDecimal
) {
companion object {
fun zero() = UserTotalVolumeValue(BigDecimal.ZERO, BigDecimal.ZERO)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package co.nilin.opex.accountant.core.model

import java.math.BigDecimal

data class FeeConfig(
val name: String,
val displayOrder: Int,
val minAssetVolume: BigDecimal,
val maxAssetVolume: BigDecimal? = null,
val minTradeVolume: BigDecimal,
val maxTradeVolume: BigDecimal? = null,
val makerFee: BigDecimal,
val takerFee: BigDecimal,
val condition: Condition
)
enum class Condition { AND, OR }

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package co.nilin.opex.accountant.core.model

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

data class TotalAssetsSnapshot(
val uuid: String,
val totalUSDT: BigDecimal,
val totalIRT: BigDecimal,
val snapshotDate: LocalDateTime,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package co.nilin.opex.accountant.core.model

import java.math.BigDecimal

data class UserFee(
val name: String,
val displayOrder: Int,
val makerFee: BigDecimal,
val takerFee: BigDecimal,
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,56 @@ package co.nilin.opex.accountant.core.service

import co.nilin.opex.accountant.core.api.FeeCalculator
import co.nilin.opex.accountant.core.model.*
import co.nilin.opex.accountant.core.spi.FeeConfigService
import co.nilin.opex.accountant.core.spi.JsonMapper
import co.nilin.opex.accountant.core.spi.UserVolumePersister
import co.nilin.opex.accountant.core.spi.WalletProxy
import co.nilin.opex.accountant.core.utils.CacheManager
import co.nilin.opex.matching.engine.core.eventh.events.TradeEvent
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
import java.time.LocalDateTime
import java.math.BigDecimal
import java.time.*
import java.util.concurrent.TimeUnit

@Component
class FeeCalculatorImpl(
private val walletProxy: WalletProxy,
private val feeConfigService: FeeConfigService,
private val userVolumePersister: UserVolumePersister,
private val cacheManager: CacheManager<String, UserFee>,
@Value("\${app.address}") private val platformAddress: String,
private val jsonMapper: JsonMapper
) : FeeCalculator {

private val logger = LoggerFactory.getLogger(FeeCalculatorImpl::class.java)

private fun remainingMillisUntil1AM(zone: ZoneId = ZoneId.of("GMT+03:30")): Long {
val now = ZonedDateTime.now(zone)
val target = now.toLocalDate().plusDays(1).atTime(1, 0).atZone(zone)
val remainingMillis = Duration.between(now, target).toMillis()
return if (remainingMillis > 0) remainingMillis else Duration.ofHours(1).toMillis()
}

override suspend fun getUserFee(uuid: String): UserFee {
val cached = cacheManager.get(uuid)
if (cached != null) {
return cached
}

val totalAssets = walletProxy.getUserTotalAssets(uuid)
val userVolumeData = userVolumePersister.getUserVolumeData(uuid, LocalDate.now().minusMonths(1L))

val feeConfig = feeConfigService.loadMatchingFeeConfig(
totalAssets?.totalUSDT ?: BigDecimal.ZERO,
userVolumeData?.valueUSDT ?: BigDecimal.ZERO
)
val remainingMillis = remainingMillisUntil1AM()
cacheManager.put(uuid, feeConfig, remainingMillis, TimeUnit.MILLISECONDS)
return feeConfig
}

override suspend fun createFeeActions(
trade: TradeEvent,
makerOrder: Order,
Expand Down
Loading
Loading