Skip to content

Commit

Permalink
Added tests for position
Browse files Browse the repository at this point in the history
  • Loading branch information
achannarasappa committed Jan 16, 2021
1 parent d5f3d3b commit c811221
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 77 deletions.
60 changes: 0 additions & 60 deletions internal/position/lot.go

This file was deleted.

99 changes: 82 additions & 17 deletions internal/position/position.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package position

import (
"io"
"log"
. "ticker-tape/internal/quote"

"github.com/novalagung/gubrak/v2"
"gopkg.in/yaml.v2"
)

type Position struct {
Expand All @@ -13,27 +16,89 @@ type Position struct {
DayChangePercent float64
}

func GetPositions(quotes []Quote) map[string]Position {
type Lot struct {
Symbol string `yaml:"symbol"`
UnitCost float64 `yaml:"unit_cost"`
Quantity float64 `yaml:"quantity"`
}

type AggregatedLot struct {
Symbol string
Cost float64
Quantity float64
}

func GetLots(handle io.Reader) map[string]AggregatedLot {

lots := []Lot{}
decoderLots := yaml.NewDecoder(handle)
err := decoderLots.Decode(&lots)

aggregatedLots := aggregateLotsBySymbol(lots)
if err != nil {
log.Fatalf("error: %v", err)
}

positions := gubrak.
From(quotes).
Reduce(func(acc []Position, quote Quote) []Position {
if _, ok := aggregatedLots[quote.Symbol]; ok {
return append(acc, Position{
AggregatedLot: aggregatedLots[quote.Symbol],
Value: quote.Price * aggregatedLots[quote.Symbol].Quantity,
DayChange: quote.Change * aggregatedLots[quote.Symbol].Quantity,
DayChangePercent: quote.ChangePercent * aggregatedLots[quote.Symbol].Quantity,
})
aggregatedLots := gubrak.
From(lots).
Reduce(func(acc map[string]AggregatedLot, lot Lot) map[string]AggregatedLot {

aggregatedLot, ok := acc[lot.Symbol]
if !ok {
acc[lot.Symbol] = AggregatedLot{
Symbol: lot.Symbol,
Cost: lot.UnitCost * lot.Quantity,
Quantity: lot.Quantity,
}
return acc
}

aggregatedLot.Quantity = aggregatedLot.Quantity + lot.Quantity
aggregatedLot.Cost = aggregatedLot.Cost + (lot.Quantity * lot.UnitCost)

acc[lot.Symbol] = aggregatedLot

return acc
}, make([]Position, 0)).
KeyBy(func(position Position) string {
return position.Symbol
}).

}, make(map[string]AggregatedLot)).
Result()

return (positions).(map[string]Position)
return (aggregatedLots).(map[string]AggregatedLot)
}

func GetSymbols(aggregatedLots map[string]AggregatedLot) []string {

symbols := make([]string, 0)
for k := range aggregatedLots {
symbols = append(symbols, k)
}

return symbols

}

func GetPositions(aggregatedLots map[string]AggregatedLot) func([]Quote) map[string]Position {
return func(quotes []Quote) map[string]Position {

positions := gubrak.
From(quotes).
Reduce(func(acc []Position, quote Quote) []Position {
if _, ok := aggregatedLots[quote.Symbol]; ok {
dayChange := quote.Change * aggregatedLots[quote.Symbol].Quantity
valuePreviousClose := quote.RegularMarketPreviousClose * aggregatedLots[quote.Symbol].Quantity
return append(acc, Position{
AggregatedLot: aggregatedLots[quote.Symbol],
Value: quote.Price * aggregatedLots[quote.Symbol].Quantity,
DayChange: dayChange,
DayChangePercent: (dayChange / valuePreviousClose) * 100,
})
}
return acc
}, make([]Position, 0)).
KeyBy(func(position Position) string {
return position.Symbol
}).
Result()

return (positions).(map[string]Position)
}
}
13 changes: 13 additions & 0 deletions internal/position/position_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package position_test

import (
"testing"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

func TestPosition(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Position Suite")
}
97 changes: 97 additions & 0 deletions internal/position/position_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package position_test

import (
"strings"

. "ticker-tape/internal/position"
. "ticker-tape/internal/quote"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("Position", func() {

var (
lotFixture = `
- symbol: "ABNB"
quantity: 35.0
unit_cost: 146.00
- symbol: "ARKW"
quantity: 20.0
unit_cost: 152.25
- symbol: "ARKW"
quantity: 20.0
unit_cost: 152.25
`
)

Describe("GetLots", func() {
It("should return a map of aggregated lots", func() {
input := strings.NewReader(lotFixture)
output := GetLots(input)
expected := map[string]AggregatedLot{
"ABNB": {Symbol: "ABNB", Cost: 5110, Quantity: 35},
"ARKW": {Symbol: "ARKW", Cost: 6090, Quantity: 40},
}
Expect(output).To(Equal(expected))
})
})

Describe("GetSymbols", func() {
It("should return a slice of symbols", func() {
input := map[string]AggregatedLot{
"ABNB": {Symbol: "ABNB", Cost: 5110, Quantity: 35},
"ARKW": {Symbol: "ARKW", Cost: 6090, Quantity: 40},
}
output := GetSymbols(input)
expected := []string{
"ABNB",
"ARKW",
}
Expect(output).To(Equal(expected))
})
})

Describe("GetPositions", func() {
It("should return a map of positions", func() {
inputAggregatedLots := map[string]AggregatedLot{
"ABNB": {Symbol: "ABNB", Cost: 5110, Quantity: 35},
"ARKW": {Symbol: "ARKW", Cost: 6090, Quantity: 40},
}
inputQuotes := []Quote{
{
ResponseQuote: ResponseQuote{
Symbol: "ARKW",
RegularMarketPreviousClose: 100,
},
Price: 200.0,
Change: 50.0,
},
{
ResponseQuote: ResponseQuote{
Symbol: "RBLX",
RegularMarketPreviousClose: 50,
},
Price: 50.0,
Change: 0.0,
},
}
output := GetPositions(inputAggregatedLots)(inputQuotes)
expected := map[string]Position{
"ARKW": {
AggregatedLot: AggregatedLot{
Symbol: "ARKW",
Cost: 6090,
Quantity: 40,
},
Value: 8000,
DayChange: 2000,
DayChangePercent: 50,
},
}
Expect(output).To(Equal(expected))
})
})

})

0 comments on commit c811221

Please sign in to comment.