From c719ec45fe052197cb11387055541ae20b3b4e78 Mon Sep 17 00:00:00 2001 From: ns-cyang <146307081+ns-cyang@users.noreply.github.com> Date: Wed, 10 Sep 2025 21:14:55 +0800 Subject: [PATCH 1/2] Tracking the target price --- Dockerfile | 18 +++++++--- go.mod | 6 ++++ internal/asset/asset.go | 72 +++++++++++++++++++++++++++++++++++---- internal/common/common.go | 2 ++ 4 files changed, 87 insertions(+), 11 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2a428d12..1d68e2f5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,19 @@ -FROM alpine:3 +FROM golang:1.20-alpine +# Set environment variable for terminal colors ENV TERM=xterm-256color -COPY ticker /ticker +# Set the working directory +WORKDIR /ticker -VOLUME ["/.ticker.yaml"] +# Download dependencies (this happens once, not every build) +COPY go.mod ./ +COPY go.sum ./ +RUN go mod download + +# Expose the volume for configuration file +VOLUME ["/ticker/.ticker.yaml"] + +# Default entrypoint +CMD ["sh"] -ENTRYPOINT ["/ticker"] \ No newline at end of file diff --git a/go.mod b/go.mod index d6127f63..0fc4a427 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,10 @@ module github.com/achannarasappa/ticker/v5 +<<<<<<< HEAD go 1.24.3 +======= +go 1.19 +>>>>>>> fcb122a (Tracking the target price) require ( github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d @@ -60,3 +64,5 @@ require ( google.golang.org/protobuf v1.36.6 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +// replace github.com/achannarasappa/ticker => ./ \ No newline at end of file diff --git a/internal/asset/asset.go b/internal/asset/asset.go index 26ec7109..7452e763 100644 --- a/internal/asset/asset.go +++ b/internal/asset/asset.go @@ -4,6 +4,9 @@ import ( "strings" c "github.com/achannarasappa/ticker/v5/internal/common" + "fmt" + "github.com/achannarasappa/ticker/v4/internal/currency" + // "github.com/achannarasappa/ticker/internal/alert" ) // AggregatedLot represents a cost basis lot of an asset grouped by symbol @@ -12,6 +15,7 @@ type AggregatedLot struct { Cost float64 Quantity float64 OrderIndex int + TargetPrice *float64 } // HoldingSummary represents a summary of all asset holdings at a point in time @@ -73,6 +77,9 @@ func GetAssets(ctx c.Context, assetGroupQuote c.AssetGroupQuote) ([]c.Asset, Hol assets = updateHoldingWeights(assets, holdingSummary) + // Evaluate assets and trigger alerts if necessary + processAssets(assets) + return assets, holdingSummary } @@ -118,8 +125,23 @@ func updateHoldingWeights(assets []c.Asset, holdingSummary HoldingSummary) []c.A } -func getHoldingFromAssetQuote(assetQuote c.AssetQuote, lotsBySymbol map[string]AggregatedLot, currencyRateByUse currencyRateByUse) c.Holding { +func processAssets(assets []c.Asset) { + for _, asset := range assets { + if asset.Holding.TargetPrice == nil { + continue // No target price, so no alert is required + } + + currentPrice := asset.QuotePrice.Price + targetPrice := *asset.Holding.TargetPrice + // Trigger an alert if the current price exceeds the target price + if currentPrice >= targetPrice { + // fmt.Printf("📢 Alert: %s has reached the target price! Current: $%.2f, Target: $%.2f\n", asset.Symbol, currentPrice, targetPrice) + } + } +} + +func getHoldingFromAssetQuote(assetQuote c.AssetQuote, lotsBySymbol map[string]AggregatedLot, currencyRateByUse currency.CurrencyRateByUse) c.Holding { if aggregatedLot, ok := lotsBySymbol[assetQuote.Symbol]; ok { value := aggregatedLot.Quantity * assetQuote.QuotePrice.Price * currencyRateByUse.QuotePrice cost := aggregatedLot.Cost * currencyRateByUse.PositionCost @@ -127,11 +149,11 @@ func getHoldingFromAssetQuote(assetQuote c.AssetQuote, lotsBySymbol map[string]A totalChangePercent := (totalChangeAmount / cost) * 100 return c.Holding{ - Value: value, - Cost: cost, - Quantity: aggregatedLot.Quantity, - UnitValue: value / aggregatedLot.Quantity, - UnitCost: cost / aggregatedLot.Quantity, + Value: value, + Cost: cost, + Quantity: aggregatedLot.Quantity, + UnitValue: value / aggregatedLot.Quantity, + UnitCost: cost / aggregatedLot.Quantity, DayChange: c.HoldingChange{ Amount: assetQuote.QuotePrice.Change * aggregatedLot.Quantity * currencyRateByUse.QuotePrice, Percent: assetQuote.QuotePrice.ChangePercent, @@ -140,14 +162,49 @@ func getHoldingFromAssetQuote(assetQuote c.AssetQuote, lotsBySymbol map[string]A Amount: totalChangeAmount, Percent: totalChangePercent, }, - Weight: 0, + Weight: 0, + TargetPrice: aggregatedLot.TargetPrice, // Assign TargetPrice from AggregatedLot } } return c.Holding{} +} + +func getLots(lots []c.Lot) map[string]AggregatedLot { + if lots == nil { + return map[string]AggregatedLot{} + } + + aggregatedLots := map[string]AggregatedLot{} + + for i, lot := range lots { + aggregatedLot, ok := aggregatedLots[lot.Symbol] + if !ok { + aggregatedLots[lot.Symbol] = AggregatedLot{ + Symbol: lot.Symbol, + Cost: (lot.UnitCost * lot.Quantity) + lot.FixedCost, + Quantity: lot.Quantity, + OrderIndex: i, + TargetPrice: lot.TargetPrice, // Store the target price from the lot + } + } else { + aggregatedLot.Quantity += lot.Quantity + aggregatedLot.Cost += lot.Quantity * lot.UnitCost + + // If the TargetPrice is not set, set it from the lot + if aggregatedLot.TargetPrice == nil && lot.TargetPrice != nil { + aggregatedLot.TargetPrice = lot.TargetPrice + } + + aggregatedLots[lot.Symbol] = aggregatedLot + } + } + + return aggregatedLots } +/* func getLots(lots []c.Lot) map[string]AggregatedLot { if lots == nil { @@ -182,3 +239,4 @@ func getLots(lots []c.Lot) map[string]AggregatedLot { return aggregatedLots } +*/ \ No newline at end of file diff --git a/internal/common/common.go b/internal/common/common.go index c7e40055..7e963449 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -102,6 +102,7 @@ type Lot struct { Quantity float64 `yaml:"quantity"` FixedCost float64 `yaml:"fixed_cost"` // FixedProperties LotFixedProperties `yaml:"fixed_properties"` + TargetPrice *float64 `yaml:"target_price"` } // type LotFixedProperties struct { @@ -155,6 +156,7 @@ type Holding struct { DayChange HoldingChange TotalChange HoldingChange Weight float64 + TargetPrice *float64 // New field for tracking the target price } // Currency is the original and converted currency if applicable From 389b6de66b194f4d39f2e6f3f01b60d3056bd971 Mon Sep 17 00:00:00 2001 From: ns-cyang <146307081+ns-cyang@users.noreply.github.com> Date: Wed, 17 Sep 2025 21:53:52 +0800 Subject: [PATCH 2/2] Fixed UT error --- go.mod | 6 ------ 1 file changed, 6 deletions(-) diff --git a/go.mod b/go.mod index 0fc4a427..d6127f63 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,6 @@ module github.com/achannarasappa/ticker/v5 -<<<<<<< HEAD go 1.24.3 -======= -go 1.19 ->>>>>>> fcb122a (Tracking the target price) require ( github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d @@ -64,5 +60,3 @@ require ( google.golang.org/protobuf v1.36.6 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) - -// replace github.com/achannarasappa/ticker => ./ \ No newline at end of file