-
Notifications
You must be signed in to change notification settings - Fork 8
/
prices.go
84 lines (73 loc) · 2.36 KB
/
prices.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
// Copyright 2021 Silvio Böhler
//
// 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 journal
import (
"fmt"
"github.com/shopspring/decimal"
)
// Prices stores the price for a commodity to a target commodity
// Outer map: target commodity
// Inner map: commodity
// value: price in (target commodity / commodity)
type Prices map[*Commodity]map[*Commodity]decimal.Decimal
var one = decimal.NewFromInt(1)
// Insert inserts a new price.
func (p Prices) Insert(commodity *Commodity, price decimal.Decimal, target *Commodity) {
p.addPrice(target, commodity, price)
p.addPrice(commodity, target, one.Div(price).Truncate(8))
}
func (p Prices) addPrice(target, commodity *Commodity, pr decimal.Decimal) {
i, ok := p[target]
if !ok {
i = make(map[*Commodity]decimal.Decimal)
p[target] = i
}
i[commodity] = pr
}
// Normalize creates a normalized price map for the given commodity.
func (p Prices) Normalize(c *Commodity) NormalizedPrices {
// prices in (target commodity / commodity)
var (
todo = NormalizedPrices{c: one}
done = make(NormalizedPrices)
currentC *Commodity
currentP decimal.Decimal
)
for len(todo) > 0 {
// we're interested in an arbitrary element of the map
for currentC, currentP = range todo {
break
}
done[currentC] = currentP
for neighbor, price := range p[currentC] {
if _, ok := done[neighbor]; ok {
continue
}
todo[neighbor] = price.Mul(currentP).Truncate(8)
}
delete(todo, currentC)
}
return done
}
// NormalizedPrices is a map representing the price of
// commodities in some base commodity.
type NormalizedPrices map[*Commodity]decimal.Decimal
// Valuate valuates the given amount.
func (n NormalizedPrices) Valuate(c *Commodity, a decimal.Decimal) (decimal.Decimal, error) {
price, ok := n[c]
if !ok {
return decimal.Zero, fmt.Errorf("no price found for %v in %v", c, n)
}
return a.Mul(price).Truncate(8), nil
}