-
Notifications
You must be signed in to change notification settings - Fork 1
/
allocation.go
133 lines (110 loc) · 3.19 KB
/
allocation.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Copyright 2021-2023
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package data
import (
"fmt"
"os"
"sort"
"time"
"github.com/olekukonko/tablewriter"
)
type SecurityAllocation struct {
Date time.Time
Members map[Security]float64
Justifications map[string]float64
}
type PortfolioPlan []*SecurityAllocation
// Last returns the last item in the PieHistory
func (pl PortfolioPlan) Last() *SecurityAllocation {
lastIdx := len(pl) - 1
if lastIdx >= 0 {
return pl[lastIdx]
}
return nil
}
// Trim the PieHistory to only cover the time period between begin and end (inclusive)
func (pl PortfolioPlan) Trim(begin, end time.Time) PortfolioPlan {
// special case 0: requested range is invalid
if end.Before(begin) {
return PortfolioPlan{}
}
// special case 1: pie history is empty
if len(pl) == 0 {
return pl
}
// special case 2: end time is before pie history start
if end.Before(pl[0].Date) {
return PortfolioPlan{}
}
// special case 3: start time is after pie history end
if begin.After(pl.Last().Date) {
return PortfolioPlan{}
}
// Use binary search to find the index corresponding to the start and end times
beginIdx := sort.Search(len(pl), func(i int) bool {
idxVal := pl[i].Date
return (idxVal.After(begin) || idxVal.Equal(begin))
})
endIdx := sort.Search(len(pl), func(i int) bool {
idxVal := pl[i].Date
return (idxVal.After(end) || idxVal.Equal(end))
})
if endIdx != len(pl) {
endIdx++
}
return pl[beginIdx:endIdx]
}
// StartDate returns the starting date of the pie history
func (pl PortfolioPlan) StartDate() time.Time {
if len(pl) == 0 {
return time.Time{}
}
return pl[0].Date
}
// EndDate returns the ending date of the pie history
func (pl PortfolioPlan) EndDate() time.Time {
last := pl.Last()
if last != nil {
return last.Date
}
return time.Time{}
}
// Table prints an ASCII formated table to stdout
func (pl PortfolioPlan) Table() {
if len(pl) == 0 {
return // nothing to do as there is no data available in the pie history
}
// construct table header
tableCols := []string{"Date", "ticker", "Qty"}
justCols := []string{}
for title := range pl[0].Justifications {
tableCols = append(tableCols, title)
justCols = append(justCols, title)
}
// initialize table
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader(tableCols)
table.SetBorder(false) // Set Border to false
for _, pie := range pl {
for security, qty := range pie.Members {
row := []string{pie.Date.Format("2006-01-02"), security.Ticker, fmt.Sprintf("%.2f", qty)}
for _, col := range justCols {
row = append(row, fmt.Sprintf("%.2f", pie.Justifications[col]))
}
table.Append(row)
}
}
table.Render()
}