2017-12-10 20:50:37 -05:00
|
|
|
package reports
|
2017-01-27 11:04:39 -05:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
2017-12-02 06:14:47 -05:00
|
|
|
"github.com/aclindsa/moneygo/internal/models"
|
2017-12-09 05:56:45 -05:00
|
|
|
"github.com/aclindsa/moneygo/internal/store"
|
2017-01-27 11:04:39 -05:00
|
|
|
"github.com/yuin/gopher-lua"
|
2017-12-10 20:50:37 -05:00
|
|
|
"time"
|
2017-01-27 11:04:39 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
const luaSecurityTypeName = "security"
|
|
|
|
|
2017-12-03 06:38:22 -05:00
|
|
|
func luaContextGetSecurities(L *lua.LState) (map[int64]*models.Security, error) {
|
|
|
|
var security_map map[int64]*models.Security
|
2017-01-27 11:04:39 -05:00
|
|
|
|
|
|
|
ctx := L.Context()
|
|
|
|
|
2017-12-09 05:56:45 -05:00
|
|
|
tx, ok := ctx.Value(dbContextKey).(store.Tx)
|
2017-10-04 08:05:51 -04:00
|
|
|
if !ok {
|
2017-10-14 14:20:50 -04:00
|
|
|
return nil, errors.New("Couldn't find tx in lua's Context")
|
2017-10-04 08:05:51 -04:00
|
|
|
}
|
|
|
|
|
2017-12-03 06:38:22 -05:00
|
|
|
security_map, ok = ctx.Value(securitiesContextKey).(map[int64]*models.Security)
|
2017-01-27 11:04:39 -05:00
|
|
|
if !ok {
|
2017-12-02 06:14:47 -05:00
|
|
|
user, ok := ctx.Value(userContextKey).(*models.User)
|
2017-01-27 11:04:39 -05:00
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Couldn't find User in lua's Context")
|
|
|
|
}
|
|
|
|
|
2017-12-07 20:08:43 -05:00
|
|
|
securities, err := tx.GetSecurities(user.UserId)
|
2017-01-27 11:04:39 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-12-03 06:38:22 -05:00
|
|
|
security_map = make(map[int64]*models.Security)
|
2017-01-27 11:04:39 -05:00
|
|
|
for i := range *securities {
|
|
|
|
security_map[(*securities)[i].SecurityId] = (*securities)[i]
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx = context.WithValue(ctx, securitiesContextKey, security_map)
|
|
|
|
L.SetContext(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
return security_map, nil
|
|
|
|
}
|
|
|
|
|
2017-12-03 06:38:22 -05:00
|
|
|
func luaContextGetDefaultCurrency(L *lua.LState) (*models.Security, error) {
|
2017-06-25 06:14:44 -04:00
|
|
|
security_map, err := luaContextGetSecurities(L)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := L.Context()
|
|
|
|
|
2017-12-02 06:14:47 -05:00
|
|
|
user, ok := ctx.Value(userContextKey).(*models.User)
|
2017-06-25 06:14:44 -04:00
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Couldn't find User in lua's Context")
|
|
|
|
}
|
|
|
|
|
|
|
|
if security, ok := security_map[user.DefaultCurrency]; ok {
|
|
|
|
return security, nil
|
|
|
|
} else {
|
|
|
|
return nil, errors.New("DefaultCurrency not in lua security_map")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func luaGetDefaultCurrency(L *lua.LState) int {
|
|
|
|
defcurrency, err := luaContextGetDefaultCurrency(L)
|
|
|
|
if err != nil {
|
|
|
|
panic("luaGetDefaultCurrency couldn't fetch default currency")
|
|
|
|
}
|
|
|
|
|
|
|
|
L.Push(SecurityToLua(L, defcurrency))
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
2017-01-27 11:04:39 -05:00
|
|
|
func luaGetSecurities(L *lua.LState) int {
|
|
|
|
security_map, err := luaContextGetSecurities(L)
|
|
|
|
if err != nil {
|
|
|
|
panic("luaGetSecurities couldn't fetch securities")
|
|
|
|
}
|
|
|
|
|
|
|
|
table := L.NewTable()
|
|
|
|
|
|
|
|
for securityid := range security_map {
|
|
|
|
table.RawSetInt(int(securityid), SecurityToLua(L, security_map[securityid]))
|
|
|
|
}
|
|
|
|
|
|
|
|
L.Push(table)
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
func luaRegisterSecurities(L *lua.LState) {
|
|
|
|
mt := L.NewTypeMetatable(luaSecurityTypeName)
|
|
|
|
L.SetGlobal("security", mt)
|
|
|
|
L.SetField(mt, "__index", L.NewFunction(luaSecurity__index))
|
|
|
|
L.SetField(mt, "__tostring", L.NewFunction(luaSecurity__tostring))
|
|
|
|
L.SetField(mt, "__eq", L.NewFunction(luaSecurity__eq))
|
|
|
|
L.SetField(mt, "__metatable", lua.LString("protected"))
|
|
|
|
getSecuritiesFn := L.NewFunction(luaGetSecurities)
|
|
|
|
L.SetField(mt, "get_all", getSecuritiesFn)
|
2017-06-25 06:14:44 -04:00
|
|
|
getDefaultCurrencyFn := L.NewFunction(luaGetDefaultCurrency)
|
|
|
|
L.SetField(mt, "get_default", getDefaultCurrencyFn)
|
2017-01-27 11:04:39 -05:00
|
|
|
|
2017-06-25 06:14:44 -04:00
|
|
|
// also register the get_securities and get_default functions as globals in
|
|
|
|
// their own right
|
2017-01-27 11:04:39 -05:00
|
|
|
L.SetGlobal("get_securities", getSecuritiesFn)
|
2017-06-25 06:14:44 -04:00
|
|
|
L.SetGlobal("get_default_currency", getDefaultCurrencyFn)
|
2017-01-27 11:04:39 -05:00
|
|
|
}
|
|
|
|
|
2017-12-03 06:38:22 -05:00
|
|
|
func SecurityToLua(L *lua.LState, security *models.Security) *lua.LUserData {
|
2017-01-27 11:04:39 -05:00
|
|
|
ud := L.NewUserData()
|
|
|
|
ud.Value = security
|
|
|
|
L.SetMetatable(ud, L.GetTypeMetatable(luaSecurityTypeName))
|
|
|
|
return ud
|
|
|
|
}
|
|
|
|
|
|
|
|
// Checks whether the first lua argument is a *LUserData with *Security and returns this *Security.
|
2017-12-03 06:38:22 -05:00
|
|
|
func luaCheckSecurity(L *lua.LState, n int) *models.Security {
|
2017-01-27 11:04:39 -05:00
|
|
|
ud := L.CheckUserData(n)
|
2017-12-03 06:38:22 -05:00
|
|
|
if security, ok := ud.Value.(*models.Security); ok {
|
2017-01-27 11:04:39 -05:00
|
|
|
return security
|
|
|
|
}
|
|
|
|
L.ArgError(n, "security expected")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func luaSecurity__index(L *lua.LState) int {
|
|
|
|
a := luaCheckSecurity(L, 1)
|
|
|
|
field := L.CheckString(2)
|
|
|
|
|
|
|
|
switch field {
|
|
|
|
case "SecurityId", "securityid":
|
|
|
|
L.Push(lua.LNumber(float64(a.SecurityId)))
|
|
|
|
case "Name", "name":
|
|
|
|
L.Push(lua.LString(a.Name))
|
|
|
|
case "Description", "description":
|
|
|
|
L.Push(lua.LString(a.Description))
|
|
|
|
case "Symbol", "symbol":
|
|
|
|
L.Push(lua.LString(a.Symbol))
|
|
|
|
case "Precision", "precision":
|
|
|
|
L.Push(lua.LNumber(float64(a.Precision)))
|
|
|
|
case "Type", "type":
|
|
|
|
L.Push(lua.LNumber(float64(a.Type)))
|
2017-07-13 21:32:25 -04:00
|
|
|
case "ClosestPrice", "closestprice":
|
|
|
|
L.Push(L.NewFunction(luaClosestPrice))
|
2017-11-05 20:52:36 -05:00
|
|
|
case "AlternateId", "alternateid":
|
|
|
|
L.Push(lua.LString(a.AlternateId))
|
2017-01-27 11:04:39 -05:00
|
|
|
default:
|
|
|
|
L.ArgError(2, "unexpected security attribute: "+field)
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
2017-12-10 20:50:37 -05:00
|
|
|
// Return the price for security in currency closest to date
|
|
|
|
func getClosestPrice(tx store.Tx, security, currency *models.Security, date *time.Time) (*models.Price, error) {
|
|
|
|
earliest, _ := tx.GetEarliestPrice(security, currency, date)
|
|
|
|
latest, err := tx.GetLatestPrice(security, currency, date)
|
|
|
|
|
|
|
|
// 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-07-13 21:32:25 -04:00
|
|
|
func luaClosestPrice(L *lua.LState) int {
|
|
|
|
s := luaCheckSecurity(L, 1)
|
|
|
|
c := luaCheckSecurity(L, 2)
|
|
|
|
date := luaCheckTime(L, 3)
|
|
|
|
|
2017-10-04 08:05:51 -04:00
|
|
|
ctx := L.Context()
|
2017-12-09 05:56:45 -05:00
|
|
|
tx, ok := ctx.Value(dbContextKey).(store.Tx)
|
2017-10-04 08:05:51 -04:00
|
|
|
if !ok {
|
2017-10-14 14:20:50 -04:00
|
|
|
panic("Couldn't find tx in lua's Context")
|
2017-10-04 08:05:51 -04:00
|
|
|
}
|
|
|
|
|
2017-12-10 20:50:37 -05:00
|
|
|
p, err := getClosestPrice(tx, s, c, date)
|
2017-07-13 21:32:25 -04:00
|
|
|
if err != nil {
|
|
|
|
L.Push(lua.LNil)
|
|
|
|
} else {
|
|
|
|
L.Push(PriceToLua(L, p))
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
2017-01-27 11:04:39 -05:00
|
|
|
func luaSecurity__tostring(L *lua.LState) int {
|
|
|
|
s := luaCheckSecurity(L, 1)
|
|
|
|
|
|
|
|
L.Push(lua.LString(s.Name + " - " + s.Description + " (" + s.Symbol + ")"))
|
|
|
|
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
func luaSecurity__eq(L *lua.LState) int {
|
|
|
|
a := luaCheckSecurity(L, 1)
|
|
|
|
b := luaCheckSecurity(L, 2)
|
|
|
|
|
|
|
|
L.Push(lua.LBool(a.SecurityId == b.SecurityId))
|
|
|
|
|
|
|
|
return 1
|
|
|
|
}
|