Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CT-52] 52-1,2,3 리뷰 후 develop 에 merge #63

Merged
merged 36 commits into from
Jun 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
9603cf0
[CT-52]domain 모듈에 JUnit 의존성 추가
nosorae Jun 21, 2024
e763129
[CT-52] 매수매도 테스트하고 버그 수정 및 리팩터링
nosorae Jun 21, 2024
6086c3c
Merge branch 'feature/ct-55-model-seperation' into feature/ct-52-mode…
nosorae Jun 21, 2024
c8cd640
[CT-52] style: ktlintFormat
nosorae Jun 21, 2024
d675ffe
[CT-52] 도메인 모델 객체 생성 돕는 메서드 필드 모아놓은 클래스 분리, 테스트 설명 주석 추가
nosorae Jun 21, 2024
5c29e2a
[CT-52] fix: ownedStockCount mapping 누락 건 추가
nosorae Jun 21, 2024
e29b171
[CT-52] test: ChartGame 매매동작 후 상태 검증 - 손절한 케이스 + 버그 수정
nosorae Jun 22, 2024
28d32ea
[CT-52] test: ChartGame 매매동작 후 상태 검증 - 익절한 케이스
nosorae Jun 22, 2024
3220f21
[CT-52] test: ChartGame 매매동작 후 상태 검증 - 매수한 케이스 + 버그 수정, 수수료 오기 수정
nosorae Jun 22, 2024
db63eed
[CT-52] style: 차트게임 유닛테스트 함수 이름 수정
nosorae Jun 22, 2024
444a239
[CT-52] test: ChartGame 다음 턴 변경동작 후 상태 검증 + 종가 고려하게 수정
nosorae Jun 22, 2024
77308de
[CT-52] style: ktlintFormat
nosorae Jun 22, 2024
a08494c
[CT-52] chore: TODO 남기기
nosorae Jun 22, 2024
c1b7de4
[CT-52] test: ChartGame 차트변경 상태 검증 + baseTest 모델의 기본값을 의미없는 값으로 수정
nosorae Jun 22, 2024
89370a3
[CT-52] test: ChartGame 게임종료 후 상태 검증
nosorae Jun 22, 2024
36417b9
[CT-52] fix: totalStockPrice 를 ChartGame 생성자에서 제거하고 발생한 컴파일 에러 수정
nosorae Jun 22, 2024
854f3a2
[CT-52] chore: 테스트 코드 패키지 위치 변경
nosorae Jun 22, 2024
455e8e2
[CT-52] test: User 초기값 검증
nosorae Jun 22, 2024
acdfcfe
[CT-52] User 테스트 객체 추가, 기본값들 변수화
nosorae Jun 22, 2024
70d5621
[CT-52] test: User 승패율 검증
nosorae Jun 22, 2024
091143a
[CT-52] test: 게임 종료 후 User 데이터 검증
nosorae Jun 22, 2024
239ca28
[CT-52] style: ktlintFormat
nosorae Jun 22, 2024
a0f570e
Merge branch 'refs/heads/feature/ct-55-model-seperation' into feature…
nosorae Jun 27, 2024
c496ff4
Merge branch 'refs/heads/feature/ct-55-model-seperation' into feature…
nosorae Jun 27, 2024
da1cb84
[CT-52-1] 리뷰반영: 테스트 데이터 만드는 것을 copy 방식이 아닌 함수로 변경
nosorae Jun 27, 2024
95a71e3
[CT-52-1] 리뷰반영: 주석 대신 함수명으로 테스트를 표현
nosorae Jun 27, 2024
15cd6cb
[CT-52-1] 리뷰반영: Money 객체 생성 확장함수로 가독성 높이기
nosorae Jun 27, 2024
3b074f6
Merge branch 'refs/heads/feature/ct-52-1-trade-model-test' into featu…
nosorae Jun 27, 2024
4378af1
[CT-52-2] merge 후 빌드에러 수정
nosorae Jun 27, 2024
feec458
[CT-52-2] 블필요 코드 제거
nosorae Jun 27, 2024
576d9a8
Merge branch 'refs/heads/feature/ct-52-2-cbartgame-model-test' into f…
nosorae Jun 27, 2024
2f70a1c
[CT-52-3] merge 후 빌드 에러 수정
nosorae Jun 27, 2024
e4f65a8
[CT-52-3] style: ktlintFormat
nosorae Jun 27, 2024
1278721
Merge pull request #60 from f-lab-edu/feature/ct-52-2-cbartgame-model…
f-lab-nathan Jun 28, 2024
56ed400
Merge pull request #61 from f-lab-edu/feature/ct-52-3-user-model-test
f-lab-nathan Jun 28, 2024
64a77c4
Merge branch 'refs/heads/feature/ct-52-2-cbartgame-model-test' into f…
nosorae Jun 29, 2024
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 @@ -2,10 +2,11 @@ package com.yessorae.data.source.local.database.converter

import androidx.room.TypeConverter
import com.yessorae.domain.entity.value.Money
import com.yessorae.domain.entity.value.asMoney

class MoneyConverter {
@TypeConverter
fun valueToMoney(value: Double?): Money? = value?.let { Money(it) }
fun valueToMoney(value: Double?): Money? = value?.let { it.asMoney() }

@TypeConverter
fun moneyToValue(money: Money?): Double? = money?.value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ fun ChartGameEntity.asDomainModel() =
isQuit = isQuit,
closeStockPrice = closeStockPrice,
totalStockCount = totalStockCount,
totalStockPrice = totalStockPrice,
averageStockPrice = averageStockPrice,
accumulatedTotalProfit = accumulatedTotalProfit
)
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ data class TradeEntity(
val id: Long = 0,
@ColumnInfo(name = COL_GAME_ID)
val gameId: Long,
@ColumnInfo(name = COL_OWNED_STOCK_COUNT)
val ownedStockCount: Int,
@ColumnInfo(name = COL_OWNED_AVERAGE_STOCK_PRICE)
val ownedAverageStockPrice: Money,
@ColumnInfo(name = COL_STOCK_PRICE)
Expand All @@ -29,6 +31,7 @@ data class TradeEntity(
companion object {
const val NAME = "trade_table"
const val COL_GAME_ID = "game_id"
const val COL_OWNED_STOCK_COUNT = "owned_stock_count"
const val COL_OWNED_AVERAGE_STOCK_PRICE = "owned_average_price"
const val COL_STOCK_PRICE = "stock_price"
const val COL_COUNT = "count"
Expand All @@ -42,6 +45,7 @@ fun Trade.asEntity() =
TradeEntity(
id = id,
gameId = gameId,
ownedStockCount = ownedStockCount,
ownedAverageStockPrice = ownedAverageStockPrice,
stockPrice = stockPrice,
count = count,
Expand All @@ -54,6 +58,7 @@ fun TradeEntity.asDomainModel() =
Trade(
id = id,
gameId = gameId,
ownedStockCount = ownedStockCount,
ownedAverageStockPrice = ownedAverageStockPrice,
stockPrice = stockPrice,
count = count,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.yessorae.data.source.network.polygon.model.chart
import com.yessorae.data.util.toLocalDateTime
import com.yessorae.domain.entity.tick.Tick
import com.yessorae.domain.entity.value.Money
import com.yessorae.domain.entity.value.asMoney
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

Expand All @@ -28,10 +29,10 @@ data class TickDto(

internal fun TickDto.asDomainModel() =
Tick(
openPrice = Money(openPrice),
closePrice = Money(closePrice),
maxPrice = Money(maxPrice),
minPrice = Money(minPrice),
openPrice = openPrice.asMoney(),
closePrice = closePrice.asMoney(),
maxPrice = maxPrice.asMoney(),
minPrice = minPrice.asMoney(),
transactionCount = transactionCount,
startTimestamp = startTimestamp.toLocalDateTime(),
tradingVolume = tradingVolume.toInt(),
Expand Down
1 change: 1 addition & 0 deletions domain/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ dependencies {
implementation(Dependency.PlatformIndependent.KOTLINX_COROUTINSE)
implementation(Dependency.Common.KOTOIN_SERIALIZATION_JSON)
implementation(Dependency.Common.PAGING_COMMON)
testImplementation(Dependency.Common.JUNIT)
}
33 changes: 13 additions & 20 deletions domain/src/main/java/com/yessorae/domain/entity/ChartGame.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.yessorae.domain.entity

import com.yessorae.domain.entity.trade.Trade
import com.yessorae.domain.entity.value.Money
import com.yessorae.domain.entity.value.asMoney

data class ChartGame(
val id: Long = 0,
Expand All @@ -21,13 +22,13 @@ data class ChartGame(
val isQuit: Boolean,
// 현재 보유 주식 수량
val totalStockCount: Int,
// 현재 보유 주식 가격의 총합
val totalStockPrice: Money,
// 현재 보유 주식 평단가
val averageStockPrice: Money,
// 누적 수익
// 누적 실현 손익
val accumulatedTotalProfit: Money
) {
// 현재 보유 주식 가격의 총합
val totalStockPrice: Money = closeStockPrice * totalStockCount

// 누적 수익률
val accumulatedRateOfProfit: Double = (accumulatedTotalProfit / startBalance).value
Expand All @@ -41,10 +42,11 @@ data class ChartGame(
// 정상종료이든 강제종료이든 종료된 경우 true
val isGameEnd: Boolean = isQuit || isGameComplete

internal fun getNextTurnResult(): ChartGame {
internal fun getNextTurnResult(closeStockPrice: Money): ChartGame {
val nextTurn = currentTurn + 1
return this.copy(
currentTurn = nextTurn
currentTurn = nextTurn,
closeStockPrice = closeStockPrice
)
}

Expand All @@ -56,23 +58,16 @@ data class ChartGame(
}

return copy(
currentBalance = currentBalance + Money.of(
currentBalance = currentBalance +
if (newTrade.type.isBuy()) {
-newTrade.totalTradeMoney.value
-(newTrade.totalTradeMoney + newTrade.commission).value
} else {
newTrade.totalTradeMoney.value
}
),
(newTrade.totalTradeMoney - newTrade.commission).value
}.asMoney(),
totalStockCount = newTotalStockCount,
totalStockPrice = totalStockPrice + Money.of(
if (newTrade.type.isBuy()) {
newTrade.totalTradeMoney.value
} else {
-newTrade.totalTradeMoney.value
}
),
averageStockPrice = if (newTrade.type.isBuy()) {
(totalStockPrice + newTrade.totalTradeMoney) / newTotalStockCount
(averageStockPrice * totalStockCount + newTrade.totalTradeMoney) /
newTotalStockCount
} else {
averageStockPrice
},
Expand All @@ -86,7 +81,6 @@ data class ChartGame(
currentBalance = startBalance,
closeStockPrice = closeStockPrice,
totalStockCount = 0,
totalStockPrice = Money.ZERO,
averageStockPrice = Money.ZERO,
accumulatedTotalProfit = Money.ZERO
)
Expand Down Expand Up @@ -116,7 +110,6 @@ data class ChartGame(
closeStockPrice = closeStockPrice,
isQuit = false,
totalStockCount = 0,
totalStockPrice = Money.ZERO,
averageStockPrice = Money.ZERO,
accumulatedTotalProfit = Money.ZERO
)
Expand Down
3 changes: 2 additions & 1 deletion domain/src/main/java/com/yessorae/domain/entity/User.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.yessorae.domain.entity

import com.yessorae.domain.common.DefaultValues
import com.yessorae.domain.entity.value.Money
import com.yessorae.domain.entity.value.asMoney
import kotlinx.serialization.Serializable

@Serializable
Expand Down Expand Up @@ -39,7 +40,7 @@ data class User(
): User {
val oldTotalGameCount = winCount + loseCount
return User(
balance = balance + Money(profit),
balance = balance + profit.asMoney(),
winCount = winCount + (if (profit > 0) 1 else 0),
loseCount = loseCount + (if (profit < 0) 1 else 0),
averageRateOfProfit =
Expand Down
14 changes: 11 additions & 3 deletions domain/src/main/java/com/yessorae/domain/entity/trade/Trade.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.yessorae.domain.entity.trade

import com.yessorae.domain.entity.value.Money
import com.yessorae.domain.entity.value.asMoney

data class Trade(
val id: Long = 0,
// 차트게임 아이디
val gameId: Long,
// 현재 가지고 있는 주식 개수
val ownedStockCount: Int,
// 현재 가지고 있는 가격
val ownedAverageStockPrice: Money,
// 1 주당 가격
Expand All @@ -24,18 +27,22 @@ data class Trade(
val totalTradeMoney: Money = stockPrice * count

// 수수료
val commission: Money = totalTradeMoney * commissionRate
val commission: Money = totalTradeMoney * commissionRate / 100

// 실현 손익, 매도할 때만 유효
val profit: Money = if (type.isBuy()) {
Money(0.0) // TODO::CT-52 버그 수정 필요. 도메인 모델 Unit Testing 브랜치에서 수정 예정.
(-commission.value).asMoney()
} else {
((stockPrice - ownedAverageStockPrice) * count) - commission
val sellProfit = totalTradeMoney - commission
val totalOwnedStockPrice = ownedAverageStockPrice * count

sellProfit - totalOwnedStockPrice
}

companion object {
internal fun new(
gameId: Long,
ownedStockCount: Int,
ownedAverageStockPrice: Money,
stockPrice: Money,
count: Int,
Expand All @@ -45,6 +52,7 @@ data class Trade(
): Trade {
return Trade(
gameId = gameId,
ownedStockCount = ownedStockCount,
ownedAverageStockPrice = ownedAverageStockPrice,
stockPrice = stockPrice,
count = count,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.yessorae.domain.entity.trade

enum class TradeType {
Buy,
Sell;
BUY,
SELL;

fun isBuy(): Boolean = this == Buy
fun isSell(): Boolean = this == Sell
fun isBuy(): Boolean = this == BUY
fun isSell(): Boolean = this == SELL
}
24 changes: 15 additions & 9 deletions domain/src/main/java/com/yessorae/domain/entity/value/Money.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,41 +9,47 @@ data class Money(
// TODO::LATER 화폐단위 바꿔주는 함수 추가

operator fun plus(other: Money): Money {
return Money(value + other.value)
return (value + other.value).asMoney()
}

operator fun times(other: Money): Money {
return Money(value * other.value)
return (value * other.value).asMoney()
}

operator fun times(count: Int): Money {
return Money(value * count)
return (value * count).asMoney()
}

operator fun times(count: Double): Money {
return Money(value * count)
return (value * count).asMoney()
}

operator fun minus(other: Money): Money {
return Money(value - other.value)
return (value - other.value).asMoney()
}

operator fun div(other: Money): Money {
return Money(value / other.value)
return (value / other.value).asMoney()
}

operator fun div(other: Double): Money {
return Money(value / other)
return (value / other).asMoney()
}

operator fun div(other: Int): Money {
return Money(value / other)
return (value / other).asMoney()
}

companion object {
val ZERO = Money(0.0)
val ZERO = 0.asMoney()
fun of(value: Double): Money {
return Money(value)
}
}
}

fun Int.asMoney() = Money.of(value = this.toDouble())

fun Float.asMoney() = Money.of(value = this.toDouble())

fun Double.asMoney() = Money.of(value = this)
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class TradeStockUseCase @Inject constructor(
with(param) {
val trade = Trade.new(
gameId = gameId,
ownedStockCount = ownedStockCount,
ownedAverageStockPrice = ownedAverageStockPrice,
stockPrice = stockPrice,
count = count,
Expand All @@ -39,6 +40,7 @@ class TradeStockUseCase @Inject constructor(

data class Param(
val gameId: Long,
val ownedStockCount: Int,
val ownedAverageStockPrice: Money,
val stockPrice: Money,
val count: Int,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import com.yessorae.domain.common.delegateEmptyResultFlow
import com.yessorae.domain.entity.User
import com.yessorae.domain.exception.ChartGameException
import com.yessorae.domain.repository.ChartGameRepository
import com.yessorae.domain.repository.ChartRepository
import com.yessorae.domain.repository.UserRepository
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow

class UpdateNextTickUseCase @Inject constructor(
private val chartGameRepository: ChartGameRepository,
private val chartRepository: ChartRepository,
private val userRepository: UserRepository
) {
operator fun invoke(gameId: Long): Flow<Result<Unit>> =
Expand All @@ -24,7 +26,20 @@ class UpdateNextTickUseCase @Inject constructor(
)
}

val newChartGame = oldChartGame.getNextTurnResult()
val chart = chartRepository.fetchChart(gameId = gameId)
val lastVisibleTickIndex = (chart.ticks.size - 1) -
(oldChartGame.totalTurn - (oldChartGame.currentTurn + 1))

if (lastVisibleTickIndex < 0) {
throw ChartGameException.CanNotCreateChartGame(
message = "can't change chart because new chart has not enough ticks"
)
}

// getNextTurnResult 에 Chart 를 전달하고 인덱스 검사하는 부분을 getNextTurnResult 에 포함할지 고민중
val newChartGame = oldChartGame.getNextTurnResult(
closeStockPrice = chart.ticks[lastVisibleTickIndex].closePrice
)

if (newChartGame.isGameEnd) {
val oldUser: User = userRepository.fetchUser()
Expand Down
Loading
Loading