Skip to content

[Feat/#22] portfolio UI 구현 #23

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

Merged
merged 28 commits into from
Sep 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
73c091e
feat: period 선택 추가
mooyoung2309 Sep 25, 2023
5929510
feat: summary chart view 추가
mooyoung2309 Sep 25, 2023
f7a045c
feat: trade date chart 추가
mooyoung2309 Sep 25, 2023
1f95f9f
feat: chart color 변경
mooyoung2309 Sep 25, 2023
3064ab7
feat: ticker list 추가
mooyoung2309 Sep 25, 2023
7242af1
feat: ticker detail 로 이동 구현
mooyoung2309 Sep 25, 2023
f589815
feat: dark mode 수정
mooyoung2309 Sep 25, 2023
b0b7340
feat: chart 추가
mooyoung2309 Sep 25, 2023
d86fa0d
feat: trade 분리
mooyoung2309 Sep 25, 2023
d6596f1
refactor: rename add -> edit
mooyoung2309 Sep 25, 2023
734efb3
refactor: rename editXXX -> XXXEdit
mooyoung2309 Sep 25, 2023
b38375e
refactor: 폴더 정리
mooyoung2309 Sep 25, 2023
ba6676b
feat: 마이페이지 탈리 연동
mooyoung2309 Sep 25, 2023
5132748
feat: edit ticker mode 추가
mooyoung2309 Sep 26, 2023
1fa9777
feat: trade edit mode 추가
mooyoung2309 Sep 26, 2023
26a7240
feat: trade edit view refactoring
mooyoung2309 Sep 26, 2023
b8b96e7
feat: ticker 업데이트 삭제 로직 구현
mooyoung2309 Sep 26, 2023
5223900
feat: delete rule cascade 추가
mooyoung2309 Sep 26, 2023
ddce04d
feat: trade ticker client 분리
mooyoung2309 Sep 26, 2023
e204cf6
feat: storage container 추가
mooyoung2309 Sep 26, 2023
7d0bfda
feat: ticker data 추가
mooyoung2309 Sep 26, 2023
4800123
feat: ticker item cell 재사용
mooyoung2309 Sep 26, 2023
6ba60da
feat: trade detail view에서 new button 클릭 로직 구현
mooyoung2309 Sep 26, 2023
941811f
feat: trade detail empty view 추가
mooyoung2309 Sep 26, 2023
e17678a
feat: calendar refresh 로직 구현
mooyoung2309 Sep 26, 2023
013b0c4
feat: core data model 제거
mooyoung2309 Sep 26, 2023
8e4ceaf
feat: trade save 로직 dto로 변경
mooyoung2309 Sep 27, 2023
cab2130
feat: date time 변경
mooyoung2309 Sep 27, 2023
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
14 changes: 13 additions & 1 deletion Plugins/ModulePlugin/ProjectDescriptionHelpers/Module.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ public extension Module {
return [
(.Toolinder, .Calendar),
(.Toolinder, .Portfolio),
(.Toolinder, .MyPage)
(.Toolinder, .MyPage),
(.Toolinder, .Trade),
]
case .Folio: return []
}
Expand Down Expand Up @@ -187,12 +188,23 @@ public extension Module {

case Calendar
case Portfolio
case Trade

public static let name: String = "Feature"

public var microTargetTypes: [MicroTargetType] {
return MicroTargetType.allCases
}

func dependencies(_ product: Product) -> [Feature] {
if product == .Toolinder && (self == .Calendar || self == .Portfolio) {
return [
.Trade
]
}

return []
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ public extension Target {

if type == .interface {
dependencies += [.domain(product)]
dependencies += module.dependencies(product).map {
.feature(product, module: $0, type: .interface)
}
}

dependencies += type.dependencies().map {
Expand Down
51 changes: 51 additions & 0 deletions Projects/Folio/Shared/DesignSystem/Sources/MinimalButton.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// MinimalButton.swift
// FolioSharedDesignSystem
//
// Created by 송영모 on 2023/09/26.
//

import SwiftUI


public struct MinimalButton: View {
public let title: String
public let isActive: Bool

public var action: () -> ()

public init(
title: String = "",
isActive: Bool = false,
action: @escaping () -> Void
) {
self.title = title
self.isActive = isActive
self.action = action
}

public var body: some View {
Button(action: {
action()
}, label: {
HStack {
Spacer()

Text(self.title)
.font(.title3)
.fontWeight(.semibold)
.foregroundStyle(.white)

Spacer()
}
.padding(.vertical, 10)
})
.background(.black)
.clipShape(
RoundedRectangle(
cornerRadius: 8,
style: .continuous
)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// TickerDTO.swift
// ToolinderDomainTradeInterface
//
// Created by 송영모 on 2023/09/26.
//

import Foundation
import SwiftData

public class TickerDTO {
public var type: TickerType?
public var currency: Currency?
public var name: String?

public init(
type: TickerType,
currency: Currency,
name: String
) {
self.type = type
self.currency = currency
self.name = name
}

func toDomain() -> Ticker {
return .init(
type: type,
currency: currency,
name: name
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,17 @@ public struct TradeDTO {
self.date = date
self.ticker = ticker
}

func toDomain() -> Trade {
return .init(
side: side,
price: price,
volume: volume,
images: images,
note: note,
date: date,
ticker: ticker
)
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//
// TickerSummaryDataEntity.swift
// ToolinderDomainTradeInterface
//
// Created by 송영모 on 2023/09/26.
//

import Foundation

public struct TickerSummaryDataEntity: Equatable {
public let avgPrice: Double
public let currentVolume: Double
public let buyVolume: Double
public let sellVolume: Double
public let yield: Double
public let profit: Double
}

public extension Ticker {
func toTickerSummaryDataEntity() -> TickerSummaryDataEntity {
let trades = (self.trades ?? []).sorted(by: { $0.date < $1.date })

var totalAmount = 0.0
var buyVolume = 0.0
var sellVoume = 0.0
var totalVolume = 0.0
var avgPrice = 0.0
var profit = 0.0
var yield = 0.0

for trade in trades {
// 평단가 계산
if trade.side == .buy {
totalAmount += (trade.price ?? 0) * (trade.volume ?? 0)
buyVolume += (trade.volume ?? 0)
} else {
sellVoume += (trade.volume ?? 0)
}
totalVolume = buyVolume - sellVoume

if !totalVolume.isZero {
avgPrice = totalAmount / totalVolume
}

// 수익 계산
if trade.side == .sell {
profit += ((trade.price ?? 0) - avgPrice) * (trade.volume ?? 0)
}
}
// 수익률 계산
if !totalAmount.isZero {
yield = (profit / totalAmount) * 100
}

let result = TickerSummaryDataEntity(
avgPrice: avgPrice,
currentVolume: totalVolume,
buyVolume: buyVolume,
sellVolume: sellVoume,
yield: yield,
profit: profit
)

return result
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// TickerTypeChartDataEntity.swift
// ToolinderDomainTradeInterface
//
// Created by 송영모 on 2023/09/25.
//

import Foundation

public struct TickerTypeChartData: Equatable, Hashable {
public let tickerType: TickerType
public let hold: Double
}

public typealias TickerTypeChartDataEntity = [TickerTypeChartData]

public extension [Trade] {
func toTickerTypeChartDataEntity() -> TickerTypeChartDataEntity {
return TickerType.allCases.map { type in
let trades = self.filter({ $0.ticker?.type == type })
let hold = trades.map { trade in
let sum: Double = (trade.price ?? 0.0) * (trade.volume ?? 0.0)
let sign: Double = trade.side == .buy ? 1.0 : -1.0

return sum * sign
}.reduce(0.0, +)

return .init(
tickerType: type,
hold: hold
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// TradeDateChartDataEntity.swift
// ToolinderDomainTradeInterface
//
// Created by 송영모 on 2023/09/25.
//

import Foundation

public struct TradeDateChartData: Equatable, Hashable {
public let date: Date
public let buyCount: Int
public let sellCount: Int
}

public typealias TradeDateChartDataEntity = [TradeDateChartData]

public extension [Trade] {
func toTradeDateChartDataEntity(from fromDate: Date, to toDate: Date) -> TradeDateChartDataEntity {
let trades = self.sorted(by: { $0.date < $1.date })
let dates = Date.dates(from: fromDate, to: toDate)

return dates.map { date in
let trades = trades.filter { $0.date.isEqual(date: date) }

return .init(
date: date,
buyCount: trades.filter { $0.side == .buy }.count,
sellCount: trades.filter { $0.side == .sell }.count
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ public class Ticker {
public var currency: Currency? = Currency.dollar
public var name: String? = ""

@Relationship(inverse: \Trade.ticker) public var trades: [Trade]? = []
@Relationship(deleteRule: .cascade, inverse: \Trade.ticker) public var trades: [Trade]? = []

public init(
type: TickerType,
currency: Currency,
name: String
type: TickerType?,
currency: Currency?,
name: String?
) {
self.type = type
self.currency = currency
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// StorageRepository.swift
// ToolinderDomainTradeInterface
//
// Created by 송영모 on 2023/09/26.
//

import Foundation
import SwiftData

public class StorageContainer {
public static let shared = StorageContainer()

private var container: ModelContainer?
public var context: ModelContext?

private init() {
do {
container = try ModelContainer(for: Ticker.self, Trade.self)

if let container {
context = ModelContext(container)
}
} catch {
print(error)
}
}
}
Loading