2017-10-04 19:35:59 -04:00
package handlers
2017-07-13 21:32:25 -04:00
import (
2017-11-10 20:13:49 -05:00
"encoding/json"
"log"
"net/http"
"strings"
2017-07-13 21:32:25 -04:00
"time"
)
type Price struct {
PriceId int64
SecurityId int64
CurrencyId int64
Date time . Time
Value string // String representation of decimal price of Security in Currency units, suitable for passing to big.Rat.SetString()
RemoteId string // unique ID from source, for detecting duplicates
}
2017-11-10 20:13:49 -05:00
type PriceList struct {
Prices * [ ] * Price ` json:"prices" `
}
func ( p * Price ) Read ( json_str string ) error {
dec := json . NewDecoder ( strings . NewReader ( json_str ) )
return dec . Decode ( p )
}
func ( p * Price ) Write ( w http . ResponseWriter ) error {
enc := json . NewEncoder ( w )
return enc . Encode ( p )
}
func ( pl * PriceList ) Read ( json_str string ) error {
dec := json . NewDecoder ( strings . NewReader ( json_str ) )
return dec . Decode ( pl )
}
func ( pl * PriceList ) Write ( w http . ResponseWriter ) error {
enc := json . NewEncoder ( w )
return enc . Encode ( pl )
2017-07-13 21:32:25 -04:00
}
2017-10-14 19:41:13 -04:00
func CreatePriceIfNotExist ( tx * Tx , price * Price ) error {
2017-07-13 21:32:25 -04:00
if len ( price . RemoteId ) == 0 {
// Always create a new price if we can't match on the RemoteId
2017-11-10 20:13:49 -05:00
err := tx . Insert ( price )
2017-07-13 21:32:25 -04:00
if err != nil {
return err
}
return nil
}
var prices [ ] * Price
2017-10-14 19:41:13 -04:00
_ , err := tx . Select ( & prices , "SELECT * from prices where SecurityId=? AND CurrencyId=? AND Date=? AND Value=?" , price . SecurityId , price . CurrencyId , price . Date , price . Value )
2017-07-13 21:32:25 -04:00
if err != nil {
return err
}
if len ( prices ) > 0 {
return nil // price already exists
}
2017-11-10 20:13:49 -05:00
err = tx . Insert ( price )
2017-07-13 21:32:25 -04:00
if err != nil {
return err
}
return nil
}
2017-11-16 19:17:51 -05:00
func GetPrice ( tx * Tx , priceid , securityid int64 ) ( * Price , error ) {
2017-11-10 20:13:49 -05:00
var p Price
2017-11-16 19:17:51 -05:00
err := tx . SelectOne ( & p , "SELECT * from prices where PriceId=? AND SecurityId=?" , priceid , securityid )
2017-11-10 20:13:49 -05:00
if err != nil {
return nil , err
}
return & p , nil
}
2017-11-16 19:17:51 -05:00
func GetPrices ( tx * Tx , securityid int64 ) ( * [ ] * Price , error ) {
2017-11-10 20:13:49 -05:00
var prices [ ] * Price
2017-11-16 19:17:51 -05:00
_ , err := tx . Select ( & prices , "SELECT * from prices where SecurityId=?" , securityid )
2017-11-10 20:13:49 -05:00
if err != nil {
return nil , err
}
return & prices , nil
}
2017-07-13 21:32:25 -04:00
// Return the latest price for security in currency units before date
2017-10-14 19:41:13 -04:00
func GetLatestPrice ( tx * Tx , security , currency * Security , date * time . Time ) ( * Price , error ) {
2017-07-13 21:32:25 -04:00
var p Price
2017-10-14 19:41:13 -04:00
err := tx . SelectOne ( & p , "SELECT * from prices where SecurityId=? AND CurrencyId=? AND Date <= ? ORDER BY Date DESC LIMIT 1" , security . SecurityId , currency . SecurityId , date )
2017-07-13 21:32:25 -04:00
if err != nil {
return nil , err
}
return & p , nil
}
// Return the earliest price for security in currency units after date
2017-10-14 19:41:13 -04:00
func GetEarliestPrice ( tx * Tx , security , currency * Security , date * time . Time ) ( * Price , error ) {
2017-07-13 21:32:25 -04:00
var p Price
2017-10-14 19:41:13 -04:00
err := tx . SelectOne ( & p , "SELECT * from prices where SecurityId=? AND CurrencyId=? AND Date >= ? ORDER BY Date ASC LIMIT 1" , security . SecurityId , currency . SecurityId , date )
2017-07-13 21:32:25 -04:00
if err != nil {
return nil , err
}
return & p , nil
}
// Return the price for security in currency closest to date
2017-10-14 19:41:13 -04:00
func GetClosestPrice ( tx * Tx , security , currency * Security , date * time . Time ) ( * Price , error ) {
earliest , _ := GetEarliestPrice ( tx , security , currency , date )
latest , err := GetLatestPrice ( tx , security , currency , date )
2017-07-13 21:32:25 -04:00
// Return early if either earliest or latest are invalid
if earliest == nil {
return latest , err
} else if err != nil {
return earliest , nil
}
howlate := earliest . Date . Sub ( * date )
howearly := date . Sub ( latest . Date )
if howearly < howlate {
return latest , nil
} else {
return earliest , nil
}
}
2017-11-10 20:13:49 -05:00
2017-11-16 19:17:51 -05:00
func PriceHandler ( r * http . Request , context * Context , user * User , securityid int64 ) ResponseWriterWriter {
security , err := GetSecurity ( context . Tx , securityid , user . UserId )
2017-11-10 20:13:49 -05:00
if err != nil {
2017-11-16 19:17:51 -05:00
return NewError ( 3 /*Invalid Request*/ )
2017-11-10 20:13:49 -05:00
}
if r . Method == "POST" {
var price Price
2017-11-13 20:48:19 -05:00
if err := ReadJSON ( r , & price ) ; err != nil {
2017-11-10 20:13:49 -05:00
return NewError ( 3 /*Invalid Request*/ )
}
price . PriceId = - 1
2017-11-16 19:17:51 -05:00
if price . SecurityId != security . SecurityId {
2017-11-10 20:13:49 -05:00
return NewError ( 3 /*Invalid Request*/ )
}
2017-11-12 20:17:27 -05:00
_ , err = GetSecurity ( context . Tx , price . CurrencyId , user . UserId )
2017-11-10 20:13:49 -05:00
if err != nil {
return NewError ( 3 /*Invalid Request*/ )
}
2017-11-12 20:17:27 -05:00
err = context . Tx . Insert ( & price )
2017-11-10 20:13:49 -05:00
if err != nil {
log . Print ( err )
return NewError ( 999 /*Internal Error*/ )
}
return ResponseWrapper { 201 , & price }
} else if r . Method == "GET" {
2017-11-12 21:12:49 -05:00
if context . LastLevel ( ) {
2017-11-16 19:17:51 -05:00
//Return all this security's prices
2017-11-10 20:13:49 -05:00
var pl PriceList
2017-11-16 19:17:51 -05:00
prices , err := GetPrices ( context . Tx , security . SecurityId )
2017-11-10 20:13:49 -05:00
if err != nil {
log . Print ( err )
return NewError ( 999 /*Internal Error*/ )
}
pl . Prices = prices
return & pl
2017-11-12 21:12:49 -05:00
}
2017-11-10 20:13:49 -05:00
2017-11-12 21:12:49 -05:00
priceid , err := context . NextID ( )
if err != nil {
return NewError ( 3 /*Invalid Request*/ )
2017-11-10 20:13:49 -05:00
}
2017-11-12 21:12:49 -05:00
2017-11-16 19:17:51 -05:00
price , err := GetPrice ( context . Tx , priceid , security . SecurityId )
2017-11-12 21:12:49 -05:00
if err != nil {
return NewError ( 3 /*Invalid Request*/ )
}
return price
2017-11-10 20:13:49 -05:00
} else {
2017-11-12 21:12:49 -05:00
priceid , err := context . NextID ( )
2017-11-10 20:13:49 -05:00
if err != nil {
return NewError ( 3 /*Invalid Request*/ )
}
if r . Method == "PUT" {
var price Price
2017-11-13 20:48:19 -05:00
if err := ReadJSON ( r , & price ) ; err != nil || price . PriceId != priceid {
2017-11-10 20:13:49 -05:00
return NewError ( 3 /*Invalid Request*/ )
}
2017-11-12 20:17:27 -05:00
_ , err = GetSecurity ( context . Tx , price . SecurityId , user . UserId )
2017-11-10 20:13:49 -05:00
if err != nil {
return NewError ( 3 /*Invalid Request*/ )
}
2017-11-12 20:17:27 -05:00
_ , err = GetSecurity ( context . Tx , price . CurrencyId , user . UserId )
2017-11-10 20:13:49 -05:00
if err != nil {
return NewError ( 3 /*Invalid Request*/ )
}
2017-11-12 20:17:27 -05:00
count , err := context . Tx . Update ( & price )
2017-11-10 20:13:49 -05:00
if err != nil || count != 1 {
log . Print ( err )
return NewError ( 999 /*Internal Error*/ )
}
return & price
} else if r . Method == "DELETE" {
2017-11-16 19:17:51 -05:00
price , err := GetPrice ( context . Tx , priceid , security . SecurityId )
2017-11-10 20:13:49 -05:00
if err != nil {
return NewError ( 3 /*Invalid Request*/ )
}
2017-11-12 20:17:27 -05:00
count , err := context . Tx . Delete ( price )
2017-11-10 20:13:49 -05:00
if err != nil || count != 1 {
log . Print ( err )
return NewError ( 999 /*Internal Error*/ )
}
return SuccessWriter { }
}
}
return NewError ( 3 /*Invalid Request*/ )
}