draft
This commit is contained in:
+32
@@ -0,0 +1,32 @@
|
|||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**/target/
|
||||||
|
!**/src/test/**/target/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package dev.darktale
|
||||||
|
|
||||||
|
import dev.darktale.model.Asset
|
||||||
|
import dev.darktale.model.CompoundInterestDeposit
|
||||||
|
import dev.darktale.model.Deposit
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import java.time.LocalDate
|
||||||
|
|
||||||
|
interface AccrualCalculator {
|
||||||
|
fun calculateAccrual(
|
||||||
|
asset: Asset,
|
||||||
|
dateFrom: LocalDate? = null,
|
||||||
|
dateTo: LocalDate
|
||||||
|
): BigDecimal
|
||||||
|
}
|
||||||
|
|
||||||
|
class DepositAccrualCalculator : AccrualCalculator {
|
||||||
|
override fun calculateAccrual(
|
||||||
|
asset: Asset,
|
||||||
|
dateFrom: LocalDate?,
|
||||||
|
dateTo: LocalDate,
|
||||||
|
): BigDecimal {
|
||||||
|
val deposit = asset as Deposit
|
||||||
|
val startDate = asset.dateFrom
|
||||||
|
var curDateFrom = startDate
|
||||||
|
var curDateTo: LocalDate? = deposit.nextAccrualDate(curDateFrom)
|
||||||
|
var calculatedAccrual = BigDecimal.ZERO
|
||||||
|
while (curDateTo != null && curDateTo <= dateTo) {
|
||||||
|
if (curDateFrom.year == curDateTo.year)
|
||||||
|
calculatedAccrual += accrualBetween(
|
||||||
|
curDateFrom, curDateTo,
|
||||||
|
asset.quantity + calculatedAccrual,
|
||||||
|
deposit.percent
|
||||||
|
)
|
||||||
|
else
|
||||||
|
calculatedAccrual += (accrualBetween(
|
||||||
|
curDateFrom, LocalDate.of(curDateFrom.year, 12, 31),
|
||||||
|
asset.quantity + calculatedAccrual,
|
||||||
|
deposit.percent
|
||||||
|
) + accrualBetween(
|
||||||
|
LocalDate.of(curDateTo.year, 1, 1), curDateTo,
|
||||||
|
asset.quantity + calculatedAccrual,
|
||||||
|
deposit.percent
|
||||||
|
))
|
||||||
|
curDateFrom = curDateTo
|
||||||
|
curDateTo = deposit.nextAccrualDate(curDateFrom)
|
||||||
|
}
|
||||||
|
return calculatedAccrual - if (dateFrom != null && dateFrom > asset.dateFrom)
|
||||||
|
calculateAccrual(asset, asset.dateFrom, startDate) else BigDecimal.ZERO
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun accrualBetween(
|
||||||
|
dateFrom: LocalDate,
|
||||||
|
dateTo: LocalDate,
|
||||||
|
baseAmount: BigDecimal,
|
||||||
|
percent: BigDecimal,
|
||||||
|
) = baseAmount * percent *
|
||||||
|
BigDecimal.valueOf((dateTo.dayOfYear - dateFrom.dayOfYear + 1).toLong()) /
|
||||||
|
BigDecimal.valueOf(dateFrom.lengthOfYear().toLong())
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
package org.example
|
package dev.darktale
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
println("Hello World!")
|
println("Hello World!")
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package dev.darktale.model
|
||||||
|
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.LocalTime
|
||||||
|
|
||||||
|
interface Asset {
|
||||||
|
val assetType: AssetType
|
||||||
|
var quantity: BigDecimal
|
||||||
|
var storagePlace: StoragePlace
|
||||||
|
val dateFrom: LocalDate
|
||||||
|
var dateTo: LocalDate?
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class AssetType {
|
||||||
|
TRADING_INSTRUMENT,
|
||||||
|
DEPOSIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Deposit : Asset {
|
||||||
|
override val assetType: AssetType
|
||||||
|
get() = AssetType.DEPOSIT
|
||||||
|
var percent: BigDecimal
|
||||||
|
|
||||||
|
fun nextAccrualDate(onDate: LocalDate): LocalDate?
|
||||||
|
}
|
||||||
|
|
||||||
|
class CompoundInterestDeposit(
|
||||||
|
override var quantity: BigDecimal,
|
||||||
|
override var storagePlace: StoragePlace,
|
||||||
|
override var dateFrom: LocalDate,
|
||||||
|
override var dateTo: LocalDate?,
|
||||||
|
override var percent: BigDecimal,
|
||||||
|
var accrualDay: Int,
|
||||||
|
) : Deposit {
|
||||||
|
override fun nextAccrualDate(onDate: LocalDate): LocalDate? {
|
||||||
|
val currentMonthAccrualDate = LocalDate.of(
|
||||||
|
onDate.year,
|
||||||
|
onDate.month,
|
||||||
|
accrualDay.coerceAtMost(onDate.lengthOfMonth())
|
||||||
|
)
|
||||||
|
val calculatedDate = if (currentMonthAccrualDate > onDate) currentMonthAccrualDate
|
||||||
|
else currentMonthAccrualDate.plusMonths(1)
|
||||||
|
return calculatedDate.takeIf { it < dateTo }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SimpleInterestDeposit(
|
||||||
|
override var quantity: BigDecimal,
|
||||||
|
override var storagePlace: StoragePlace,
|
||||||
|
override var dateFrom: LocalDate,
|
||||||
|
override var dateTo: LocalDate?,
|
||||||
|
override var percent: BigDecimal,
|
||||||
|
) : Deposit {
|
||||||
|
override fun nextAccrualDate(onDate: LocalDate) = dateTo.takeIf { it != null && it > onDate }
|
||||||
|
|
||||||
|
override fun toString() = "$quantity since $dateFrom till $dateTo with ${percent*BigDecimal.valueOf(100L)}%"
|
||||||
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.example.model
|
package dev.darktale.model
|
||||||
|
|
||||||
interface StoragePlace {
|
interface StoragePlace {
|
||||||
var name: String
|
var name: String
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package org.example.model
|
|
||||||
|
|
||||||
import java.math.BigDecimal
|
|
||||||
|
|
||||||
interface Asset {
|
|
||||||
var quantity: BigDecimal
|
|
||||||
var assetType: AssetType
|
|
||||||
var storagePlace: StoragePlace
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class AssetType {
|
|
||||||
TRADING_INSTRUMENT,
|
|
||||||
DEPOSIT;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package dev.darktale
|
||||||
|
|
||||||
|
import dev.darktale.model.Deposit
|
||||||
|
import dev.darktale.model.SimpleInterestDeposit
|
||||||
|
import dev.darktale.model.StoragePlace
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.extension.ExtensionContext
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest
|
||||||
|
import org.junit.jupiter.params.provider.Arguments
|
||||||
|
import org.junit.jupiter.params.provider.ArgumentsProvider
|
||||||
|
import org.junit.jupiter.params.provider.ArgumentsSource
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.LocalTime
|
||||||
|
import java.util.stream.Stream
|
||||||
|
|
||||||
|
class DepositAccrualCalculatorTest {
|
||||||
|
@ParameterizedTest
|
||||||
|
@ArgumentsSource(DepositProvider::class)
|
||||||
|
fun testAccrual(deposit: Deposit, dateFrom: LocalDate, dateTo:LocalDate, expected: BigDecimal) {
|
||||||
|
val accrued = DepositAccrualCalculator().calculateAccrual(deposit, dateFrom, dateTo).stripTrailingZeros()
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
0, expected.compareTo(accrued),
|
||||||
|
"expected $expected, actual $accrued"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DepositProvider : ArgumentsProvider {
|
||||||
|
private val testStoragePlace = object : StoragePlace {
|
||||||
|
override var name: String = "Qwe"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun provideArguments(context: ExtensionContext): Stream<Arguments> {
|
||||||
|
val yearStart = LocalDate.of(2024, 1, 1)
|
||||||
|
val yearEnd = LocalDate.of(2024, 12, 31)
|
||||||
|
|
||||||
|
val deposit = SimpleInterestDeposit(
|
||||||
|
BigDecimal.valueOf(1000L),
|
||||||
|
testStoragePlace,
|
||||||
|
yearStart,
|
||||||
|
yearEnd,
|
||||||
|
BigDecimal.valueOf(0.1),
|
||||||
|
)
|
||||||
|
|
||||||
|
return Stream.of(
|
||||||
|
Arguments.of(deposit, yearStart, yearEnd, BigDecimal.valueOf(100L)),
|
||||||
|
Arguments.of(deposit, yearStart, yearEnd.minusDays(1), BigDecimal.ZERO),
|
||||||
|
Arguments.of(deposit, yearEnd.minusDays(1), yearEnd, BigDecimal.valueOf(100L)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user