diff --git a/pkg/service/position.go b/pkg/service/position.go index 30e02ffb7..5fb38eaa7 100644 --- a/pkg/service/position.go +++ b/pkg/service/position.go @@ -64,6 +64,8 @@ func (s *PositionService) Insert(position *types.Position, trade types.Trade, pr quote, profit, trade_id, + exchange, + side, traded_at ) VALUES ( :strategy, @@ -76,17 +78,24 @@ func (s *PositionService) Insert(position *types.Position, trade types.Trade, pr :quote, :profit, :trade_id, + :exchange, + :side, :traded_at )`, - map[string]interface{} { - "strategy": "", - "strategy_instance_id": "", - "symbol": position.Symbol, - "quote_currency": position.QuoteCurrency, - "base_currency": position.BaseCurrency, - "average_cost": position.AverageCost, - "base": position.Base, - "quote": position.Quote, + map[string]interface{}{ + "strategy": position.Strategy, + "strategy_instance_id": position.StrategyInstanceID, + "symbol": position.Symbol, + "quote_currency": position.QuoteCurrency, + "base_currency": position.BaseCurrency, + "average_cost": position.AverageCost, + "base": position.Base, + "quote": position.Quote, + "profit": profit, + "trade_id": trade.ID, + "exchange": trade.Exchange, + "side": trade.Side, + "traded_at": trade.Time, }) return err } diff --git a/pkg/service/position_test.go b/pkg/service/position_test.go index 072a2ddd8..7c58cc7eb 100644 --- a/pkg/service/position_test.go +++ b/pkg/service/position_test.go @@ -17,18 +17,45 @@ func TestPositionService(t *testing.T) { t.Fatal(err) } - defer db.Close() + defer func() { + err := db.Close() + assert.NoError(t, err) + }() xdb := sqlx.NewDb(db.DB, "sqlite3") service := &PositionService{DB: xdb} - err = service.Insert(&types.Position{ - Symbol: "BTCUSDT", - BaseCurrency: "BTC", - QuoteCurrency: "USDT", - AverageCost: fixedpoint.NewFromFloat(44000), - ChangedAt: time.Now(), - }, types.Trade{}, fixedpoint.NewFromFloat(10.9)) - assert.NoError(t, err) + t.Run("minimal fields", func(t *testing.T) { + err = service.Insert(&types.Position{ + Symbol: "BTCUSDT", + BaseCurrency: "BTC", + QuoteCurrency: "USDT", + AverageCost: fixedpoint.NewFromFloat(44000), + ChangedAt: time.Now(), + }, types.Trade{ + Time: types.Time(time.Now()), + }, 0) + assert.NoError(t, err) + }) + + t.Run("full fields", func(t *testing.T) { + err = service.Insert(&types.Position{ + Symbol: "BTCUSDT", + BaseCurrency: "BTC", + QuoteCurrency: "USDT", + AverageCost: fixedpoint.NewFromFloat(44000), + Base: fixedpoint.NewFromFloat(0.1), + Quote: fixedpoint.NewFromFloat(-44000.0), + ChangedAt: time.Now(), + Strategy: "bollmaker", + StrategyInstanceID: "bollmaker-BTCUSDT-1m", + }, types.Trade{ + ID: 9, + Exchange: types.ExchangeBinance, + Side: types.SideTypeSell, + Time: types.Time(time.Now()), + }, fixedpoint.NewFromFloat(10.9)) + assert.NoError(t, err) + }) } diff --git a/pkg/types/position.go b/pkg/types/position.go index 777ce7d07..82a63ec3a 100644 --- a/pkg/types/position.go +++ b/pkg/types/position.go @@ -30,15 +30,15 @@ type PositionRisk struct { } type Position struct { - Symbol string `json:"symbol"` - BaseCurrency string `json:"baseCurrency"` - QuoteCurrency string `json:"quoteCurrency"` + Symbol string `json:"symbol" db:"symbol"` + BaseCurrency string `json:"baseCurrency" db:"base"` + QuoteCurrency string `json:"quoteCurrency" db:"quote"` Market Market `json:"market,omitempty"` - Base fixedpoint.Value `json:"base"` - Quote fixedpoint.Value `json:"quote"` - AverageCost fixedpoint.Value `json:"averageCost"` + Base fixedpoint.Value `json:"base" db:"base"` + Quote fixedpoint.Value `json:"quote" db:"quote"` + AverageCost fixedpoint.Value `json:"averageCost" db:"average_cost"` // ApproximateAverageCost adds the computed fee in quote in the average cost // This is used for calculating net profit @@ -48,13 +48,15 @@ type Position struct { ExchangeFeeRates map[ExchangeName]ExchangeFee `json:"exchangeFeeRates"` // TotalFee stores the fee currency -> total fee quantity - TotalFee map[string]fixedpoint.Value `json:"totalFee"` + TotalFee map[string]fixedpoint.Value `json:"totalFee" db:"-"` - ChangedAt time.Time `json:"changedAt,omitempty"` + ChangedAt time.Time `json:"changedAt,omitempty" db:"changed_at"` Strategy string `json:"strategy,omitempty" db:"strategy"` StrategyInstanceID string `json:"strategyInstanceID,omitempty" db:"strategy_instance_id"` + AccumulatedProfit fixedpoint.Value `json:"accumulatedProfit,omitempty" db:"accumulated_profit"` + sync.Mutex } @@ -306,6 +308,7 @@ func (p *Position) AddTrade(td Trade) (profit fixedpoint.Value, netProfit fixedp fee := td.Fee // calculated fee in quote (some exchange accounts may enable platform currency fee discount, like BNB) + // convert platform fee token into USD values var feeInQuote fixedpoint.Value = fixedpoint.Zero switch td.FeeCurrency { @@ -353,6 +356,7 @@ func (p *Position) AddTrade(td Trade) (profit fixedpoint.Value, netProfit fixedp p.Quote = p.Quote.Sub(quoteQuantity) p.AverageCost = price p.ApproximateAverageCost = price + p.AccumulatedProfit = p.AccumulatedProfit.Add(profit) return profit, netProfit, true } else { // covering short position @@ -360,6 +364,7 @@ func (p *Position) AddTrade(td Trade) (profit fixedpoint.Value, netProfit fixedp p.Quote = p.Quote.Sub(quoteQuantity) profit = p.AverageCost.Sub(price).Mul(quantity) netProfit = p.ApproximateAverageCost.Sub(price).Mul(quantity).Sub(feeInQuote) + p.AccumulatedProfit = p.AccumulatedProfit.Add(profit) return profit, netProfit, true } } @@ -385,12 +390,14 @@ func (p *Position) AddTrade(td Trade) (profit fixedpoint.Value, netProfit fixedp p.Quote = p.Quote.Add(quoteQuantity) p.AverageCost = price p.ApproximateAverageCost = price + p.AccumulatedProfit = p.AccumulatedProfit.Add(profit) return profit, netProfit, true } else { p.Base = p.Base.Sub(quantity) p.Quote = p.Quote.Add(quoteQuantity) profit = price.Sub(p.AverageCost).Mul(quantity) netProfit = price.Sub(p.ApproximateAverageCost).Mul(quantity).Sub(feeInQuote) + p.AccumulatedProfit = p.AccumulatedProfit.Add(profit) return profit, netProfit, true } } diff --git a/pkg/types/time.go b/pkg/types/time.go index 47ef9ff72..a4e70b8e5 100644 --- a/pkg/types/time.go +++ b/pkg/types/time.go @@ -136,6 +136,7 @@ func convertFloat64ToTime(vt string, f float64) (time.Time, error) { return time.Time{}, fmt.Errorf("the floating point value %f is out of the timestamp range", f) } +// Time type implements the driver value for sqlite type Time time.Time var layout = "2006-01-02 15:04:05.999Z07:00"