added dividend db table and insert by trade
This commit is contained in:
BIN
Portifolio
BIN
Portifolio
Binary file not shown.
@@ -21,12 +21,24 @@ func InitDB(db *sql.DB) {
|
||||
symbol TEXT NOT NULL,
|
||||
currency_code TEXT NOT NULL,
|
||||
shares INTEGER NOT NULL,
|
||||
product INTEGER NOT NULL CHECK(product IN (0, 1, 2, 3)),
|
||||
type INTEGER NOT NULL CHECK(type IN (0, 1)),
|
||||
product INTEGER NOT NULL CHECK(product IN (0, 1, 2, 3, 4)), -- added 4 for BondTrade
|
||||
type INTEGER NOT NULL CHECK(type IN (0, 1)), -- Buy=0, Sell=1 only; Dividend has its own table
|
||||
price REAL NOT NULL,
|
||||
traded_at DATETIME NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS dividends (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
symbol TEXT NOT NULL,
|
||||
currency_code TEXT NOT NULL,
|
||||
product INTEGER NOT NULL CHECK(product IN (0, 1, 2, 3, 4)),
|
||||
value REAL NOT NULL,
|
||||
tax_amount REAL NOT NULL DEFAULT 0,
|
||||
tax_rate REAL NOT NULL DEFAULT 0,
|
||||
net_value REAL NOT NULL,
|
||||
payment_date DATETIME NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS position (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
company_id INTEGER NOT NULL UNIQUE,
|
||||
|
||||
@@ -25,14 +25,7 @@ func GetTrades(db *sql.DB) ([]model.Trade, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch typeInt {
|
||||
case 0:
|
||||
t.Type = model.TradeType(false)
|
||||
case 1:
|
||||
t.Type = model.TradeType(true)
|
||||
default:
|
||||
return nil, fmt.Errorf("failed to convert given Type int to bool of trade type.")
|
||||
}
|
||||
t.Type = model.TradeType(typeInt)
|
||||
|
||||
trades = append(trades, t)
|
||||
}
|
||||
@@ -81,6 +74,21 @@ func InsertTrade(db *sql.DB, trade model.Trade) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func InsertDividend(db *sql.DB, div model.Dividend) error {
|
||||
_, err := db.Exec(
|
||||
"INSERT INTO trades (symbol, currency_code, shares, product, value, tax_amount, tax_rate, net_value, payment_date) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||||
div.Symbol,
|
||||
div.CurrencyCode,
|
||||
div.Product,
|
||||
div.Value,
|
||||
div.TaxAmount,
|
||||
div.TaxRate,
|
||||
div.NetValue,
|
||||
div.PaymentDate,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func UpdatePositions(db *sql.DB, positions []model.Position) error {
|
||||
// Complete overwrite of the db positions
|
||||
_, err := db.Exec("DELETE FROM position")
|
||||
|
||||
@@ -20,45 +20,57 @@ func AddTradeHandler(db *sql.DB) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
err := req.Validate()
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("failed to validate trade: %s", err), http.StatusInternalServerError)
|
||||
if err := req.Validate(); err != nil {
|
||||
http.Error(w, fmt.Sprintf("failed to validate trade: %s", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// check if currency is in the db.
|
||||
currency, err := database.GetCurrencyByCode(db, req.CurrencyCode)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("failed to find currency: %s", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
trade := model.Trade{
|
||||
Symbol: req.Symbol,
|
||||
Shares: req.Shares,
|
||||
Product: model.TradeProduct(req.Product),
|
||||
Type: model.TradeType(req.Type),
|
||||
Price: req.Price,
|
||||
CurrencyCode: currency.Code,
|
||||
Date: req.Date,
|
||||
}
|
||||
|
||||
err = database.InsertTrade(db, trade)
|
||||
switch model.TradeType(req.Type) {
|
||||
case model.DividendType:
|
||||
dividend, err := req.ToDividend()
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("failed to insert trade into db: %s", err), http.StatusInternalServerError)
|
||||
http.Error(w, fmt.Sprintf("failed to build dividend: %s", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
dividend.CurrencyCode = currency.Code
|
||||
|
||||
if err := database.InsertDividend(db, dividend); err != nil {
|
||||
http.Error(w, fmt.Sprintf("failed to insert dividend: %s", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
err = service.UpdatePositionByTradeList(db)
|
||||
update := true
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]any{"success": true})
|
||||
|
||||
case model.BuyType, model.SellType:
|
||||
trade, err := req.ToTrade()
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("failed to build trade: %s", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
trade.CurrencyCode = currency.Code
|
||||
|
||||
if err := database.InsertTrade(db, trade); err != nil {
|
||||
http.Error(w, fmt.Sprintf("failed to insert trade: %s", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
update := true
|
||||
if err := service.UpdatePositionByTradeList(db); err != nil {
|
||||
update = false
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if err := json.NewEncoder(w).Encode(map[string]any{"success": true, "position update": update}); err != nil {
|
||||
http.Error(w, fmt.Sprintf("failed to encode trades: %s", err), http.StatusInternalServerError)
|
||||
return
|
||||
json.NewEncoder(w).Encode(map[string]any{"success": true, "position_update": update})
|
||||
|
||||
default:
|
||||
http.Error(w, fmt.Sprintf("unknown trade type: %d", req.Type), http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package model
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -55,21 +56,28 @@ const (
|
||||
BondTrade
|
||||
)
|
||||
|
||||
type TradeType bool
|
||||
type TradeType int
|
||||
|
||||
const (
|
||||
Buy TradeType = true
|
||||
Sell TradeType = false
|
||||
BuyType TradeType = iota // 0
|
||||
SellType // 1
|
||||
DividendType // 2
|
||||
)
|
||||
|
||||
type AddTradeRequest struct {
|
||||
Symbol string `json:"symbol"`
|
||||
Shares int `json:"shares"`
|
||||
Product int `json:"product"`
|
||||
Type bool `json:"type"`
|
||||
Type int `json:"type"` // was bool, now int
|
||||
Price float64 `json:"price"`
|
||||
CurrencyCode string `json:"currency_code"`
|
||||
Date time.Time `json:"date"`
|
||||
|
||||
// Dividend-specific fields (only populated when Type == 2)
|
||||
TaxAmount float64 `json:"tax_amount,omitempty"`
|
||||
TaxRate float64 `json:"tax_rate,omitempty"`
|
||||
NetValue float64 `json:"net_value,omitempty"`
|
||||
PaymentDate time.Time `json:"payment_date,omitempty"`
|
||||
}
|
||||
|
||||
type Trade struct {
|
||||
@@ -107,6 +115,38 @@ func (r *AddTradeRequest) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r AddTradeRequest) ToDividend() (Dividend, error) {
|
||||
if TradeType(r.Type) != DividendType {
|
||||
return Dividend{}, fmt.Errorf("trade type is not a dividend")
|
||||
}
|
||||
return Dividend{
|
||||
Symbol: r.Symbol,
|
||||
CurrencyCode: r.CurrencyCode,
|
||||
Product: TradeProduct(r.Product),
|
||||
Value: r.Price, // gross value
|
||||
PaymentDate: r.PaymentDate,
|
||||
TaxAmount: r.TaxAmount,
|
||||
TaxRate: r.TaxRate,
|
||||
NetValue: r.NetValue,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r AddTradeRequest) ToTrade() (Trade, error) {
|
||||
t := TradeType(r.Type)
|
||||
if t != BuyType && t != SellType {
|
||||
return Trade{}, fmt.Errorf("trade type is not buy or sell")
|
||||
}
|
||||
return Trade{
|
||||
Symbol: r.Symbol,
|
||||
CurrencyCode: r.CurrencyCode,
|
||||
Shares: r.Shares,
|
||||
Product: TradeProduct(r.Product),
|
||||
Type: t,
|
||||
Price: r.Price,
|
||||
Date: r.Date,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// for now trades and none stock position will not be supported.
|
||||
type Portifolio struct {
|
||||
Positions []Position
|
||||
|
||||
@@ -19,7 +19,7 @@ func UpdatePositionByTradeList(db *sql.DB) error {
|
||||
TradeSum := make(map[string]model.Position)
|
||||
|
||||
for _, trade := range trades {
|
||||
if trade.Type == model.Buy {
|
||||
if trade.Type == model.BuyType {
|
||||
TradeSum[trade.Symbol] = model.Position{
|
||||
Symbol: trade.Symbol,
|
||||
CurrencyCode: trade.CurrencyCode,
|
||||
|
||||
Reference in New Issue
Block a user