diff --git a/accounts.go b/accounts.go index dbbcf6c..b1ebf1d 100644 --- a/accounts.go +++ b/accounts.go @@ -2,12 +2,14 @@ package main import ( "container/ring" + "encoding/json" "errors" "fmt" "sync" "time" "github.com/distributeddesigns/currency" + types "github.com/distributeddesigns/shared_types" "github.com/gorilla/websocket" ) @@ -39,6 +41,10 @@ func newAccountForUser(userID string) *account { return &ac } +func (ac *account) toCSV() string { + return fmt.Sprintf("%s,%.2f", ac.userID, ac.balance.ToFloat()) +} + // AddFunds : Increases the balance of the account func (ac *account) AddFunds(amount currency.Currency) { consoleLog.Debugf("Old balance for %s is %s", ac.userID, ac.balance) @@ -118,13 +124,77 @@ func (ac *account) PruneExpiredTxs() { } } +type pendingATXState struct { + Stock string `json:"stock"` + Amount string `json:"amount"` + Trigger string `json:"trigger"` + Action string `json:"action"` +} + +func serializeATX(autoTx types.AutoTxInit) pendingATXState { + return pendingATXState{ + Stock: autoTx.AutoTxKey.Stock, + Action: autoTx.AutoTxKey.Action, + Amount: autoTx.Amount.String(), + Trigger: autoTx.Trigger.String(), + } +} + +type accountState struct { + Balance string `json:"balance"` + Portfolio map[string]uint64 `json:"portfolio"` + PendingBuys []pendingTxState `json:"pendingBuys"` + PendingSells []pendingTxState `json:"pendingSells"` + AutoTx []pendingATXState `json:"pendingATX"` +} + +func (ac *account) GetState() accountState { + pendingBuys := make([]pendingTxState, len(ac.pendingBuys)) + for i, pb := range ac.pendingBuys { + pendingBuys[i] = pb.GetState() + } + + pendingSells := make([]pendingTxState, len(ac.pendingSells)) + for i, ps := range ac.pendingSells { + pendingSells[i] = ps.GetState() + } + + pendingATX := make([]pendingATXState, 0) + for k, v := range workATXStore { + if k.UserID == ac.userID { + serTx := serializeATX(v) + pendingATX = append(pendingATX, serTx) + } + } + + return accountState{ + Balance: ac.balance.String(), + Portfolio: ac.portfolio, + PendingBuys: pendingBuys, + PendingSells: pendingSells, + AutoTx: pendingATX, + } +} + +type eventMessage struct { + Account accountState `json:"account"` + Message string `json:"message"` +} + func (ac *account) PushEvent(message string) { socket, found := userSocketmap[ac.userID] if !found { consoleLog.Errorf("User %s is not subscribed to a socket connection", ac.userID) return } - socket.WriteMessage(websocket.TextMessage, []byte(message)) + + payload, err := json.Marshal(&eventMessage{ac.GetState(), message}) + if err != nil { + consoleLog.Errorf("Failed to json-ify %+v, %s", ac, message) + return + } + + socket.WriteMessage(websocket.TextMessage, payload) } type summaryItem struct { diff --git a/commands_buy.go b/commands_buy.go index ac6a894..9e6a7ee 100644 --- a/commands_buy.go +++ b/commands_buy.go @@ -143,3 +143,11 @@ func (b buyCmd) RollBack() { func (b buyCmd) IsExpired() bool { return time.Now().After(b.expiresAt) } + +func (b buyCmd) GetState() pendingTxState { + return pendingTxState{ + Stock: b.stock, + Amount: b.purchaseAmount.String(), + ExpiresAt: b.expiresAt.Format("15:04:05 Jan _2"), + } +} diff --git a/commands_sell.go b/commands_sell.go index 3bebd7e..b800202 100644 --- a/commands_sell.go +++ b/commands_sell.go @@ -143,3 +143,11 @@ func (s sellCmd) RollBack() { func (s sellCmd) IsExpired() bool { return time.Now().After(s.expiresAt) } + +func (s sellCmd) GetState() pendingTxState { + return pendingTxState{ + Stock: s.stock, + Amount: s.profit.String(), + ExpiresAt: s.expiresAt.Format("15:04:05 Jan _2"), + } +} diff --git a/incomingTxWatcher.go b/incomingTxWatcher.go index 1a71f71..8b7c419 100644 --- a/incomingTxWatcher.go +++ b/incomingTxWatcher.go @@ -102,13 +102,20 @@ func wsHandler(w http.ResponseWriter, r *http.Request) { } // frontend handshake to get user and hook them into the userMap for sockets _, message, err := conn.ReadMessage() + userID := string(message) failOnError(err, "Failed to handshake") - fmt.Printf("Handshake from client is %s\n", message) - userSocket, found := userSocketmap[string(message)] + fmt.Printf("Handshake from client is %s\n", userID) + userSocket, found := userSocketmap[string(userID)] if found { userSocket.Close() } - userSocketmap[string(message)] = conn + userSocketmap[string(userID)] = conn + if _, accountExists := accountStore[userID]; !accountExists { + consoleLog.Infof("Creating account for %s", userID) + accountStore[userID] = newAccountForUser(userID) + } + accountStore[userID].PushEvent("") //shove for userdata + //conn.WriteMessage(websocket.TextMessage, []byte(`{"account": "`+accountStore[userID].toCSV()+`", "message": null}`)) } func incomingTxWatcher() { diff --git a/pendingTx.go b/pendingTx.go index 703f141..6b43662 100644 --- a/pendingTx.go +++ b/pendingTx.go @@ -1,7 +1,14 @@ package main +type pendingTxState struct { + Stock string `json:"stock"` + Amount string `json:"amount"` + ExpiresAt string `json:"expiresAt"` +} + type pendingTx interface { Commit() RollBack() IsExpired() bool + GetState() pendingTxState } diff --git a/receiveAutoTx.go b/receiveAutoTx.go index 0e5ba4c..406852f 100644 --- a/receiveAutoTx.go +++ b/receiveAutoTx.go @@ -69,7 +69,7 @@ func receiveAutoTx() { autoTxFilled, err := types.ParseAutoTxFilled(string(d.Body[:])) failOnError(err, "Failed to parse autoTxInit") consoleLog.Debugf("AutoTxFilled is : %+v\n", autoTxFilled) - fulfilAutoTx(autoTxFilled) + updateAccount(autoTxFilled) } fmt.Printf("AutoTxWorker Receiver Spinning\n") } diff --git a/sendAutoTx.go b/sendAutoTx.go index 13c5d12..3ca0a78 100644 --- a/sendAutoTx.go +++ b/sendAutoTx.go @@ -6,12 +6,11 @@ import ( "github.com/streadway/amqp" ) -func fulfilAutoTx(autoTxFilled types.AutoTxFilled) { - return -} - func updateAccount(autoTxFilled types.AutoTxFilled) { - // TODO: Do account add and lock here, needs rebase + accountStore[autoTxFilled.AutoTxKey.UserID].Lock() + accountStore[autoTxFilled.AutoTxKey.UserID].AddFunds(autoTxFilled.AddFunds) + accountStore[autoTxFilled.AutoTxKey.UserID].AddStock(autoTxFilled.AutoTxKey.Stock, uint64(autoTxFilled.AddStocks)) + accountStore[autoTxFilled.AutoTxKey.UserID].Unlock() return } @@ -37,12 +36,23 @@ func sendAutoTx() { } if validTrigger { - // TODO: DO MATH FOR FILLED TRANS - curr, err := currency.NewFromFloat(0.00) - failOnError(err, "Failed to parse currency") + var filledStock uint + var filledCash currency.Currency + if autoTxKey.Action == "Buy" { + filledStock, filledCash = quote.Price.FitsInto(autoTxInit.Amount) + failOnError(err, "Failed to parse currency") + } else { + numStock, remCash := autoTxInit.Trigger.FitsInto(autoTxInit.Amount) // amount of stock we reserved from their port + filledCash = quote.Price + err = filledCash.Mul(float64(numStock)) + filledCash.Add(remCash) // Re-add the unfilled value + filledStock = 0 + failOnError(err, "Failed to parse currency") + } + autoTxFilled := types.AutoTxFilled{ - AddFunds: curr, - AddStocks: uint(0), + AddFunds: filledCash, + AddStocks: filledStock, AutoTxKey: autoTxKey, } updateAccount(autoTxFilled) diff --git a/static/index.html b/static/index.html index 32c67c0..81fd3c3 100644 --- a/static/index.html +++ b/static/index.html @@ -5,245 +5,28 @@ + + -
- -