changeset 411:681fb377235e

Implementing insider transactions
author Dennis Concepción Martín <66180929+denniscm190@users.noreply.github.com>
date Mon, 07 Jun 2021 20:59:52 +0200
parents e24c9ca71824
children a7c9dd0c5822
files LazyBear.xcodeproj/project.pbxproj LazyBear.xcodeproj/project.xcworkspace/xcuserdata/dennis.xcuserdatad/UserInterfaceState.xcuserstate LazyBear/Global Models/InsiderTransactionModel.swift LazyBear/Global functions/ConvertStringToDate.swift LazyBear/Global functions/GetDateComponents.swift LazyBear/Views/Company/Helpers/InsiderList.swift LazyBear/Views/Company/Helpers/TransactionList.swift LazyBear/Views/Company/Helpers/TransactionRow.swift LazyBear/Views/Company/Insiders.swift LazyBear/Views/Company/Networking/InsidersResponse.swift LazyBear/Views/Home/Helpers/TradingDatesItem.swift LazyBear/Views/Home/TradingDates.swift
diffstat 12 files changed, 278 insertions(+), 44 deletions(-) [+]
line wrap: on
line diff
--- a/LazyBear.xcodeproj/project.pbxproj	Sun Jun 06 19:07:52 2021 +0200
+++ b/LazyBear.xcodeproj/project.pbxproj	Mon Jun 07 20:59:52 2021 +0200
@@ -72,6 +72,12 @@
 		95BD2FAE26341BD1008B6752 /* TextfieldAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95BD2FAD26341BD1008B6752 /* TextfieldAlert.swift */; };
 		95BD2FB326341D36008B6752 /* BlurBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95BD2FB226341D36008B6752 /* BlurBackground.swift */; };
 		95C8C0E0262A369F0082D1D9 /* ProfileResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C8C0DF262A369F0082D1D9 /* ProfileResponse.swift */; };
+		95CCFB56266E7A0F00C384A1 /* InsiderTransactionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95CCFB55266E7A0F00C384A1 /* InsiderTransactionModel.swift */; };
+		95CCFB58266E7F4F00C384A1 /* InsiderList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95CCFB57266E7F4F00C384A1 /* InsiderList.swift */; };
+		95CCFB5A266E841B00C384A1 /* TransactionRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95CCFB59266E841B00C384A1 /* TransactionRow.swift */; };
+		95CCFB5C266E842000C384A1 /* TransactionList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95CCFB5B266E842000C384A1 /* TransactionList.swift */; };
+		95CCFB5E266E855800C384A1 /* GetDateComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95CCFB5D266E855800C384A1 /* GetDateComponents.swift */; };
+		95CCFB60266E864C00C384A1 /* ConvertStringToDate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95CCFB5F266E864C00C384A1 /* ConvertStringToDate.swift */; };
 		95D308F82624B3A400A39F77 /* CurrencyItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95D308F72624B3A400A39F77 /* CurrencyItem.swift */; };
 		95E31C0D26472CA000106B98 /* CompanyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95E31C0C26472CA000106B98 /* CompanyView.swift */; };
 		95E31C122647304100106B98 /* PriceViewStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95E31C112647304100106B98 /* PriceViewStyle.swift */; };
@@ -169,6 +175,12 @@
 		95BD2FAD26341BD1008B6752 /* TextfieldAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextfieldAlert.swift; sourceTree = "<group>"; };
 		95BD2FB226341D36008B6752 /* BlurBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurBackground.swift; sourceTree = "<group>"; };
 		95C8C0DF262A369F0082D1D9 /* ProfileResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileResponse.swift; sourceTree = "<group>"; };
+		95CCFB55266E7A0F00C384A1 /* InsiderTransactionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsiderTransactionModel.swift; sourceTree = "<group>"; };
+		95CCFB57266E7F4F00C384A1 /* InsiderList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsiderList.swift; sourceTree = "<group>"; };
+		95CCFB59266E841B00C384A1 /* TransactionRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionRow.swift; sourceTree = "<group>"; };
+		95CCFB5B266E842000C384A1 /* TransactionList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionList.swift; sourceTree = "<group>"; };
+		95CCFB5D266E855800C384A1 /* GetDateComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetDateComponents.swift; sourceTree = "<group>"; };
+		95CCFB5F266E864C00C384A1 /* ConvertStringToDate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConvertStringToDate.swift; sourceTree = "<group>"; };
 		95D308F72624B3A400A39F77 /* CurrencyItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyItem.swift; sourceTree = "<group>"; };
 		95E31C0C26472CA000106B98 /* CompanyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompanyView.swift; sourceTree = "<group>"; };
 		95E31C112647304100106B98 /* PriceViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PriceViewStyle.swift; sourceTree = "<group>"; };
@@ -266,6 +278,8 @@
 			children = (
 				952045142610C7C600A76362 /* ConvertEpoch.swift */,
 				95A7C0732616409D003E2EC1 /* ParseJSON.swift */,
+				95CCFB5D266E855800C384A1 /* GetDateComponents.swift */,
+				95CCFB5F266E864C00C384A1 /* ConvertStringToDate.swift */,
 			);
 			path = "Global functions";
 			sourceTree = "<group>";
@@ -300,6 +314,7 @@
 				95613ADE264FC6FD00D4CE8F /* LatestNewsModel.swift */,
 				9594F03F2651355B00CFA8D4 /* HistoricalPricesModel.swift */,
 				95602703265ABB990046F97E /* InsiderRosterModel.swift */,
+				95CCFB55266E7A0F00C384A1 /* InsiderTransactionModel.swift */,
 			);
 			path = "Global Models";
 			sourceTree = "<group>";
@@ -477,6 +492,9 @@
 				95613AE0264FD34100D4CE8F /* NewsRow.swift */,
 				950857A8266BD2C6005357BA /* SFSafariViewWrapper.swift */,
 				950857D0266BE54B005357BA /* InsiderRow.swift */,
+				95CCFB57266E7F4F00C384A1 /* InsiderList.swift */,
+				95CCFB59266E841B00C384A1 /* TransactionRow.swift */,
+				95CCFB5B266E842000C384A1 /* TransactionList.swift */,
 			);
 			path = Helpers;
 			sourceTree = "<group>";
@@ -673,6 +691,7 @@
 				950C36E3260FB6180081CF53 /* HapticsManager.swift in Sources */,
 				95FBE0DC2619CA7200440386 /* ProfileView.swift in Sources */,
 				95A5188626186F590002D27C /* PriceView.swift in Sources */,
+				95CCFB5C266E842000C384A1 /* TransactionList.swift in Sources */,
 				95613AE1264FD34100D4CE8F /* NewsRow.swift in Sources */,
 				95613ADD264FC6A200D4CE8F /* ChartResponse.swift in Sources */,
 				9550444926111FC9000E0BCB /* StockRow.swift in Sources */,
@@ -685,11 +704,13 @@
 				950857D1266BE54B005357BA /* InsiderRow.swift in Sources */,
 				95A7C0742616409D003E2EC1 /* ParseJSON.swift in Sources */,
 				95E8BAA32656D86E0016AE72 /* RequestType.swift in Sources */,
+				95CCFB56266E7A0F00C384A1 /* InsiderTransactionModel.swift in Sources */,
 				954D7EA8260BBA6600A13C50 /* WatchlistCompany+CoreDataProperties.swift in Sources */,
 				951566E72613A2B6007C0F36 /* TradingDates.swift in Sources */,
 				955E73392623568F005652FF /* Home.swift in Sources */,
 				95E31C122647304100106B98 /* PriceViewStyle.swift in Sources */,
 				95721DA6262761E700EC527B /* CurrencyRow.swift in Sources */,
+				95CCFB58266E7F4F00C384A1 /* InsiderList.swift in Sources */,
 				9562404C263C766D00C6C511 /* WatchlistCreator.swift in Sources */,
 				95672B8F25DDA54700DCBE4A /* LazyBearApp.swift in Sources */,
 				95A7C066261639E0003E2EC1 /* SearchView.swift in Sources */,
@@ -722,13 +743,16 @@
 				95C8C0E0262A369F0082D1D9 /* ProfileResponse.swift in Sources */,
 				95A4B935263EA31C0056F036 /* WatchlistCreatorSearchBar.swift in Sources */,
 				95AD4A2D26078C1400498079 /* ContentView.swift in Sources */,
+				95CCFB5A266E841B00C384A1 /* TransactionRow.swift in Sources */,
 				95629DA02645298E007AF020 /* Profile.swift in Sources */,
+				95CCFB5E266E855800C384A1 /* GetDateComponents.swift in Sources */,
 				95A4B930263E9F530056F036 /* WatchlistCreatorList.swift in Sources */,
 				9562404E263C7D8800C6C511 /* WatchlistCreatorClass.swift in Sources */,
 				95721DB4262787EF00EC527B /* ExtensiveList.swift in Sources */,
 				950857D3266BE55F005357BA /* RowShape.swift in Sources */,
 				952045152610C7C600A76362 /* ConvertEpoch.swift in Sources */,
 				95BD2FAE26341BD1008B6752 /* TextfieldAlert.swift in Sources */,
+				95CCFB60266E864C00C384A1 /* ConvertStringToDate.swift in Sources */,
 				95A07F5D26305A8F009865AA /* IntradayPricesModel.swift in Sources */,
 				95A07F7626305AE3009865AA /* TradingDatesModel.swift in Sources */,
 			);
Binary file LazyBear.xcodeproj/project.xcworkspace/xcuserdata/dennis.xcuserdatad/UserInterfaceState.xcuserstate has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LazyBear/Global Models/InsiderTransactionModel.swift	Mon Jun 07 20:59:52 2021 +0200
@@ -0,0 +1,19 @@
+//
+//  InsiderTransactionModel.swift
+//  LazyBear
+//
+//  Created by Dennis Concepción Martín on 7/6/21.
+//
+
+import SwiftUI
+
+struct InsiderTransactionModel: Codable {
+    var filingDate: String
+    var fullName: String
+    var postShares: Int
+    var reportedTitle: String?
+    var transactionCode: String
+    var transactionPrice: Float
+    var transactionShares: Int
+    var transactionValue: Float
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LazyBear/Global functions/ConvertStringToDate.swift	Mon Jun 07 20:59:52 2021 +0200
@@ -0,0 +1,18 @@
+//
+//  ConvertStringToDate.swift
+//  LazyBear
+//
+//  Created by Dennis Concepción Martín on 7/6/21.
+//
+
+import SwiftUI
+
+func convertStringToDate(_ stringDate: String) -> Date {
+    // Convert string to date
+    let dateFormatter = DateFormatter()
+    dateFormatter.dateFormat = "yyyy-MM-dd"
+    
+    let date = dateFormatter.date(from: stringDate)!
+    
+    return date
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LazyBear/Global functions/GetDateComponents.swift	Mon Jun 07 20:59:52 2021 +0200
@@ -0,0 +1,33 @@
+//
+//  GetDateComponents.swift
+//  LazyBear
+//
+//  Created by Dennis Concepción Martín on 7/6/21.
+//
+
+import SwiftUI
+
+enum Components {
+    case day, month, year
+}
+
+/*
+ Get components from a date
+ */
+func getDateComponents(_ components: Components, _ date: Date) -> String {
+    let dateComponents = Calendar.current.dateComponents([.year, .month, .day], from: date)
+    
+    switch components {
+    case .year:
+        return "\(dateComponents.year ?? 2020)"
+    case .day:
+        return "\(dateComponents.day ?? 1)"
+    case .month:
+        let dateFormatter = DateFormatter()
+        let monthNumber = dateComponents.month ?? 1
+        let monthLetters = dateFormatter.shortMonthSymbols[monthNumber-1]
+        
+        return "\(monthLetters)"
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LazyBear/Views/Company/Helpers/InsiderList.swift	Mon Jun 07 20:59:52 2021 +0200
@@ -0,0 +1,85 @@
+//
+//  InsiderList.swift
+//  LazyBear
+//
+//  Created by Dennis Concepción Martín on 7/6/21.
+//
+
+import SwiftUI
+
+struct InsiderList: View {
+    var insiderSummary: [InsiderRosterModel]
+    var numberOfRows: Int
+    
+    @State private var showFullList = false
+    
+    var body: some View {
+        VStack(alignment: .leading) {
+            HStack {
+                Text("Top 10 insiders")
+                    .font(.title3)
+                    .fontWeight(.semibold)
+                
+                Spacer()
+                Button("See all", action: { showFullList = true })
+            }
+        
+            // Get total shares owned by the top 10 insiders
+            let totalPositions =  insiderSummary.map { $0.position }.reduce(0, +)
+            VStack(alignment: .leading, spacing: 20) {
+                ForEach(insiderSummary.prefix(numberOfRows), id: \.self) { insider in
+                    
+                    // Compute percentage of ownership for each insider
+                    let percentage = Double(insider.position) / Double(totalPositions)
+                    
+                    InsiderRow(percentageOfWidth: CGFloat(percentage), insiderRoster: insider)
+                }
+            }
+        }
+        .sheet(isPresented: $showFullList) {
+            InsiderFullList(insiderSummary: insiderSummary)
+        }
+    }
+}
+
+struct InsiderList_Previews: PreviewProvider {
+    static var previews: some View {
+        InsiderList(insiderSummary:
+                    [InsiderRosterModel(entityName: "Dennis Concepcion", position: 1234, reportDate: 1234567)],
+                    numberOfRows: 3)
+    }
+}
+
+struct InsiderFullList: View {
+    var insiderSummary: [InsiderRosterModel]
+    @Environment(\.presentationMode) private var presentationInsiderFullList
+    
+    var body: some View {
+        NavigationView {
+            ScrollView {
+                // Get total shares owned by the top 10 insiders
+                let totalPositions =  insiderSummary.map { $0.position }.reduce(0, +)
+                VStack(alignment: .leading, spacing: 20) {
+                    ForEach(insiderSummary, id: \.self) { insider in
+                        
+                        // Compute percentage of ownership for each insider
+                        let percentage = Double(insider.position) / Double(totalPositions)
+                        
+                        InsiderRow(percentageOfWidth: CGFloat(percentage), insiderRoster: insider)
+                    }
+                }
+                .padding()
+            }
+            .navigationTitle("Top 10 insiders")
+            .navigationBarTitleDisplayMode(.inline)
+            .toolbar {
+                ToolbarItem(placement: .navigationBarLeading) {
+                    Button(action: { presentationInsiderFullList.wrappedValue.dismiss() }) {
+                        Image(systemName: "multiply")
+                            .imageScale(.large)
+                    }
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LazyBear/Views/Company/Helpers/TransactionList.swift	Mon Jun 07 20:59:52 2021 +0200
@@ -0,0 +1,20 @@
+//
+//  TransactionList.swift
+//  LazyBear
+//
+//  Created by Dennis Concepción Martín on 7/6/21.
+//
+
+import SwiftUI
+
+struct TransactionList: View {
+    var body: some View {
+        Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
+    }
+}
+
+struct TransactionList_Previews: PreviewProvider {
+    static var previews: some View {
+        TransactionList()
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LazyBear/Views/Company/Helpers/TransactionRow.swift	Mon Jun 07 20:59:52 2021 +0200
@@ -0,0 +1,67 @@
+//
+//  TransactionRow.swift
+//  LazyBear
+//
+//  Created by Dennis Concepción Martín on 7/6/21.
+//
+
+import SwiftUI
+
+struct TransactionRow: View {
+    var transaction: InsiderTransactionModel
+    
+    var body: some View {
+        RowShape()
+            .frame(height: 105)
+            .overlay(
+                HStack {
+                    VStack {
+                        let date = convertStringToDate(transaction.filingDate)
+                        Text(getDateComponents(.month, date))
+                            .fontWeight(.semibold)
+                        
+                        Text(getDateComponents(.day, date))
+                            .font(.title)
+                            .fontWeight(.semibold)
+                            .foregroundColor(Color("default"))
+                        
+                        Text(getDateComponents(.year, date))
+                            .font(.caption)
+                            .fontWeight(.semibold)
+                    }
+                    .padding(.trailing)
+                    
+                    VStack(alignment: .leading) {
+                        Text(transaction.fullName.capitalized)
+                            .lineLimit(1)
+                            .font(.headline)
+                        
+                        Text(transaction.reportedTitle ?? "-")
+                    }
+                    
+                    Spacer()
+                    VStack(alignment: .trailing) {
+                        Text("\(transaction.transactionShares)")
+                            .foregroundColor(transaction.transactionShares < 0 ? Color(.systemRed): Color(.systemGreen))
+                    }
+                }
+                .padding()
+            )
+    }
+}
+
+struct TransactionRow_Previews: PreviewProvider {
+    static var previews: some View {
+        TransactionRow(transaction:
+            InsiderTransactionModel(filingDate: "2020-01-01",
+                                    fullName: "Dennis Concepcion",
+                                    postShares: 1234,
+                                    reportedTitle: "Director",
+                                    transactionCode: "S",
+                                    transactionPrice: 20.08,
+                                    transactionShares: 12345,
+                                    transactionValue: 1234567.0
+            )
+        )
+    }
+}
--- a/LazyBear/Views/Company/Insiders.swift	Sun Jun 06 19:07:52 2021 +0200
+++ b/LazyBear/Views/Company/Insiders.swift	Mon Jun 07 20:59:52 2021 +0200
@@ -13,24 +13,12 @@
     
     var body: some View {
         if company.showInsidersView {
-            if let insiderSummer = company.insidersData.insiderRoster {
-                VStack(alignment: .leading) {
-                    Text("Top 10 insiders")
-                        .font(.title3)
-                        .fontWeight(.semibold)
+            if let insiderSummary = company.insidersData.insiderRoster {
+                InsiderList(insiderSummary: insiderSummary, numberOfRows: 4)
+            }
+            
+            if let insiderTransactions = company.insidersData.insiderTransactions {
                 
-                    // Get total shares owned by the top 10 insiders
-                    let totalPositions =  insiderSummer.map { $0.position }.reduce(0, +)
-                    VStack(alignment: .leading, spacing: 20) {
-                        ForEach(insiderSummer.prefix(10), id: \.self) { insider in
-                            
-                            // Compute percentage of ownership for each insider
-                            let percentage = Double(insider.position) / Double(totalPositions)
-                            
-                            InsiderRow(percentageOfWidth: CGFloat(percentage), insiderRoster: insider)
-                        }
-                    }
-                }
             }
         } else {
             ProgressView()
--- a/LazyBear/Views/Company/Networking/InsidersResponse.swift	Sun Jun 06 19:07:52 2021 +0200
+++ b/LazyBear/Views/Company/Networking/InsidersResponse.swift	Mon Jun 07 20:59:52 2021 +0200
@@ -9,8 +9,10 @@
 
 struct InsidersResponse: Codable {
     var insiderRoster: [InsiderRosterModel]?
+    var insiderTransactions: [InsiderTransactionModel]?
     
     private enum CodingKeys: String, CodingKey {
         case insiderRoster = "insider_roster"
+        case insiderTransactions
     }
 }
--- a/LazyBear/Views/Home/Helpers/TradingDatesItem.swift	Sun Jun 06 19:07:52 2021 +0200
+++ b/LazyBear/Views/Home/Helpers/TradingDatesItem.swift	Mon Jun 07 20:59:52 2021 +0200
@@ -17,42 +17,20 @@
             .frame(width: 100, height: 100)
             .overlay(
                 VStack {
-                    Text(get(.month))
+                    Text(getDateComponents(.month, date))
                         .fontWeight(.semibold)
                     
-                    Text(get(.day))
+                    Text(getDateComponents(.day, date))
                         .font(.title)
                         .fontWeight(.semibold)
                         .foregroundColor(Color("default"))
                     
-                    Text(get(.year))
+                    Text(getDateComponents(.year, date))
                         .font(.caption)
                         .fontWeight(.semibold)
                 }
             )
     }
-    
-    private enum Components {
-        case day, month, year
-    }
-    
-    private func get(_ components: Components) -> String {
-        let dateComponents = Calendar.current.dateComponents([.year, .month, .day], from: date)
-        
-        switch components {
-        case .year:
-            return "\(dateComponents.year ?? 2020)"
-        case .day:
-            return "\(dateComponents.day ?? 1)"
-        case .month:
-            let dateFormatter = DateFormatter()
-            let monthNumber = dateComponents.month ?? 1
-            let monthLetters = dateFormatter.shortMonthSymbols[monthNumber-1]
-            
-            return "\(monthLetters)"
-        }
-        
-    }
 }
 
 struct TradingDatesItem_Previews: PreviewProvider {
--- a/LazyBear/Views/Home/TradingDates.swift	Sun Jun 06 19:07:52 2021 +0200
+++ b/LazyBear/Views/Home/TradingDates.swift	Mon Jun 07 20:59:52 2021 +0200
@@ -10,7 +10,7 @@
 
 struct TradingDates: View {
     var dates: [TradingDatesModel]
-    @Environment(\.presentationMode) var tradingDatesPresent
+    @Environment(\.presentationMode) private var presentationTradingDates
     
     let columns = [GridItem(.adaptive(minimum: 100))]
     
@@ -28,7 +28,7 @@
             .navigationBarTitleDisplayMode(.inline)
             .toolbar {
                 ToolbarItem(placement: .navigationBarLeading) {
-                    Button(action: { tradingDatesPresent.wrappedValue.dismiss() }) {
+                    Button(action: { presentationTradingDates.wrappedValue.dismiss() }) {
                         Image(systemName: "multiply")
                             .imageScale(.large)
                     }