Skip to content

Commit

Permalink
Merge pull request #23 from folio-world/feat/#22-portfolio-ui
Browse files Browse the repository at this point in the history
[Feat/#22] portfolio UI 구현
  • Loading branch information
mooyoung2309 authored Sep 27, 2023
2 parents f09896d + cab2130 commit 5e06165
Show file tree
Hide file tree
Showing 71 changed files with 1,880 additions and 773 deletions.
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

0 comments on commit 5e06165

Please sign in to comment.