mirror of
https://github.com/aclindsa/ofxgo.git
synced 2024-11-22 11:30:05 -05:00
Add simple investment requests/responses
This is lacking (at least) parsing the list of securities frequently sent with investment statements
This commit is contained in:
parent
81814feaff
commit
f59f3713c2
@ -167,7 +167,7 @@ type StatementResponse struct {
|
|||||||
CashAdvBalAmt Amount `xml:"STMTRS>CASHADVBALAMT,omitempty"` // Only for CREDITLINE accounts, available balance for cash advances
|
CashAdvBalAmt Amount `xml:"STMTRS>CASHADVBALAMT,omitempty"` // Only for CREDITLINE accounts, available balance for cash advances
|
||||||
IntRate Amount `xml:"STMTRS>INTRATE,omitempty"` // Current interest rate
|
IntRate Amount `xml:"STMTRS>INTRATE,omitempty"` // Current interest rate
|
||||||
BalList []Balance `xml:"STMTRS>BALLIST>BAL,omitempty"`
|
BalList []Balance `xml:"STMTRS>BALLIST>BAL,omitempty"`
|
||||||
MktgInfo String `xml:"STMTRS>MKTGINFO"` // Marketing information
|
MktgInfo String `xml:"STMTRS>MKTGINFO,omitempty"` // Marketing information
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sr StatementResponse) Name() string {
|
func (sr StatementResponse) Name() string {
|
||||||
@ -198,7 +198,7 @@ type CCStatementResponse struct {
|
|||||||
RewardBal Amount `xml:"CCSTMTRS>REWARDINFO>REWARDBAL,omitempty"` // Current balance of the reward program
|
RewardBal Amount `xml:"CCSTMTRS>REWARDINFO>REWARDBAL,omitempty"` // Current balance of the reward program
|
||||||
RewardEarned Amount `xml:"CCSTMTRS>REWARDINFO>REWARDEARNED,omitempty"` // Reward amount earned YTD
|
RewardEarned Amount `xml:"CCSTMTRS>REWARDINFO>REWARDEARNED,omitempty"` // Reward amount earned YTD
|
||||||
BalList []Balance `xml:"CCSTMTRS>BALLIST>BAL,omitempty"`
|
BalList []Balance `xml:"CCSTMTRS>BALLIST>BAL,omitempty"`
|
||||||
MktgInfo String `xml:"CCSTMTRS>MKTGINFO"` // Marketing information
|
MktgInfo String `xml:"CCSTMTRS>MKTGINFO,omitempty"` // Marketing information
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sr CCStatementResponse) Name() string {
|
func (sr CCStatementResponse) Name() string {
|
||||||
|
23
common.go
23
common.go
@ -1,6 +1,7 @@
|
|||||||
package ofxgo
|
package ofxgo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"github.com/golang/go/src/encoding/xml"
|
"github.com/golang/go/src/encoding/xml"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -11,6 +12,22 @@ type Message interface {
|
|||||||
// it's unmarshaled to ensure the request or response is valid
|
// it's unmarshaled to ensure the request or response is valid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Status struct {
|
||||||
|
XMLName xml.Name `xml:"STATUS"`
|
||||||
|
Code Int `xml:"CODE"`
|
||||||
|
Severity String `xml:"SEVERITY"`
|
||||||
|
Message String `xml:"MESSAGE,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Status) Valid() (bool, error) {
|
||||||
|
switch s.Severity {
|
||||||
|
case "INFO", "WARN", "ERROR":
|
||||||
|
return true, nil
|
||||||
|
default:
|
||||||
|
return false, errors.New("Invalid STATUS>SEVERITY")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type BankAcct struct {
|
type BankAcct struct {
|
||||||
XMLName xml.Name // BANKACCTTO or BANKACCTFROM
|
XMLName xml.Name // BANKACCTTO or BANKACCTFROM
|
||||||
BankId String `xml:"BANKID"`
|
BankId String `xml:"BANKID"`
|
||||||
@ -25,3 +42,9 @@ type CCAcct struct {
|
|||||||
AcctId String `xml:"ACCTID"`
|
AcctId String `xml:"ACCTID"`
|
||||||
AcctKey String `xml:"ACCTKEY,omitempty"` // Unused in USA
|
AcctKey String `xml:"ACCTKEY,omitempty"` // Unused in USA
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InvAcct struct {
|
||||||
|
XMLName xml.Name // INVACCTTO or INVACCTFROM
|
||||||
|
BrokerId String `xml:"BROKERID"`
|
||||||
|
AcctId String `xml:"ACCTID"`
|
||||||
|
}
|
||||||
|
111
investments.go
Normal file
111
investments.go
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
package ofxgo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/golang/go/src/encoding/xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InvStatementRequest struct {
|
||||||
|
XMLName xml.Name `xml:"INVSTMTTRNRQ"`
|
||||||
|
TrnUID UID `xml:"TRNUID"`
|
||||||
|
CltCookie String `xml:"CLTCOOKIE,omitempty"`
|
||||||
|
TAN String `xml:"TAN,omitempty"` // Transaction authorization number
|
||||||
|
// TODO `xml:"OFXEXTENSION,omitempty"`
|
||||||
|
InvAcctFrom InvAcct `xml:"INVSTMTRQ>INVACCTFROM"`
|
||||||
|
DtStart Date `xml:"INVSTMTRQ>INCTRAN>DTSTART,omitempty"`
|
||||||
|
DtEnd Date `xml:"INVSTMTRQ>INCTRAN>DTEND,omitempty"`
|
||||||
|
Include Boolean `xml:"INVSTMTRQ>INCTRAN>INCLUDE"` // Include transactions (instead of just balance)
|
||||||
|
IncludeOO Boolean `xml:"INVSTMTRQ>INCOO"` // Include open orders
|
||||||
|
PosDtAsOf Date `xml:"INVSTMTRQ>INCPOS>DTASOF,omitempty"` // Date that positions should be sent down for, if present
|
||||||
|
IncludePos Boolean `xml:"INVSTMTRQ>INCPOS>INCLUDE"` // Include position data in response
|
||||||
|
IncludeBalance Boolean `xml:"INVSTMTRQ>INCBAL"` // Include investment balance in response
|
||||||
|
Include401K Boolean `xml:"INVSTMTRQ>INC401K,omitempty"` // Include 401k information
|
||||||
|
Include401KBal Boolean `xml:"INVSTMTRQ>INC401KBAL,omitempty"` // Include 401k balance information
|
||||||
|
IncludeTranImage Boolean `xml:"INVSTMTRQ>INCTRANIMAGE,omitempty"` // Include transaction images
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *InvStatementRequest) Name() string {
|
||||||
|
return "INVSTMTTRNRQ"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *InvStatementRequest) Valid() (bool, error) {
|
||||||
|
if ok, err := r.TrnUID.Valid(); !ok {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type InvBankTransaction struct {
|
||||||
|
XMLName xml.Name `xml:"INVBANKTRAN"`
|
||||||
|
Transactions []Transaction `xml:"STMTTRN,omitempty"`
|
||||||
|
SubAcctFund String `xml:"SUBACCTFUND"` // Where did the money for the transaction come from or go to? CASH, MARGIN, SHORT, OTHER
|
||||||
|
}
|
||||||
|
|
||||||
|
type InvTransactionList struct {
|
||||||
|
XMLName xml.Name `xml:"INVTRANLIST"`
|
||||||
|
DtStart Date `xml:"DTSTART"`
|
||||||
|
DtEnd Date `xml:"DTEND"`
|
||||||
|
Transactions []InvBankTransaction `xml:"INVBANKTRAN,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type InvBalance struct {
|
||||||
|
XMLName xml.Name `xml:"INVBAL"`
|
||||||
|
AvailCash Amount `xml:"AVAILCASH"` // Available cash across all sub-accounts, including sweep funds
|
||||||
|
MarginBalance Amount `xml:"MARGINBALANCE"` // Negative means customer has borrowed funds
|
||||||
|
ShortBalance Amount `xml:"SHORTBALANCE"` // Always positive, market value of all short positions
|
||||||
|
BuyPower Amount `xml:"BUYPOWER"`
|
||||||
|
BalList []Balance `xml:"BALLIST>BAL,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type InvStatementResponse struct {
|
||||||
|
XMLName xml.Name `xml:"INVSTMTTRNRS"`
|
||||||
|
TrnUID UID `xml:"TRNUID"`
|
||||||
|
Status Status `xml:"STATUS"`
|
||||||
|
CltCookie String `xml:"CLTCOOKIE,omitempty"`
|
||||||
|
// TODO OFXEXTENSION
|
||||||
|
DtAsOf Date `xml:"INVSTMTRS>DTASOF"`
|
||||||
|
CurDef String `xml:"INVSTMTRS>CURDEF"`
|
||||||
|
InvAcctFrom InvAcct `xml:"INVSTMTRS>INVACCTFROM"`
|
||||||
|
InvTranList InvTransactionList `xml:"INVSTMTRS>INVTRANLIST,omitempty"`
|
||||||
|
// TODO INVPOSLIST
|
||||||
|
InvBal InvBalance `xml:"INVSTMTRS>INVBAL,omitempty"`
|
||||||
|
// TODO INVOOLIST
|
||||||
|
MktgInfo String `xml:"INVSTMTRS>MKTGINFO,omitempty"` // Marketing information
|
||||||
|
// TODO INV401K
|
||||||
|
// TODO INV401KBAL
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sr InvStatementResponse) Name() string {
|
||||||
|
return "INVSTMTTRNRS"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sr InvStatementResponse) Valid() (bool, error) {
|
||||||
|
//TODO implement
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecodeInvestmentsMessageSet(d *xml.Decoder, start xml.StartElement) ([]Message, error) {
|
||||||
|
var msgs []Message
|
||||||
|
for {
|
||||||
|
tok, err := nextNonWhitespaceToken(d)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if end, ok := tok.(xml.EndElement); ok && end.Name.Local == start.Name.Local {
|
||||||
|
// If we found the end of our starting element, we're done parsing
|
||||||
|
return msgs, nil
|
||||||
|
} else if startElement, ok := tok.(xml.StartElement); ok {
|
||||||
|
switch startElement.Name.Local {
|
||||||
|
case "INVSTMTTRNRS":
|
||||||
|
var info InvStatementResponse
|
||||||
|
if err := d.DecodeElement(&info, &startElement); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
msgs = append(msgs, Message(info))
|
||||||
|
default:
|
||||||
|
return nil, errors.New("Unsupported investments response tag: " + startElement.Name.Local)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("Didn't find an opening element")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,7 +14,7 @@ type Request struct {
|
|||||||
Banking []Message //<BANKMSGSETV1>
|
Banking []Message //<BANKMSGSETV1>
|
||||||
//<CREDITCARDMSGSETV1>
|
//<CREDITCARDMSGSETV1>
|
||||||
//<LOANMSGSETV1>
|
//<LOANMSGSETV1>
|
||||||
//<INVSTMTMSGSETV1>
|
Investments []Message //<INVSTMTMSGSETV1>
|
||||||
//<INTERXFERMSGSETV1>
|
//<INTERXFERMSGSETV1>
|
||||||
//<WIREXFERMSGSETV1>
|
//<WIREXFERMSGSETV1>
|
||||||
//<BILLPAYMSGSETV1>
|
//<BILLPAYMSGSETV1>
|
||||||
@ -106,6 +106,9 @@ NEWFILEUID:NONE
|
|||||||
if err := marshalMessageSet(encoder, oq.Banking, "BANKMSGSRQV1"); err != nil {
|
if err := marshalMessageSet(encoder, oq.Banking, "BANKMSGSRQV1"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if err := marshalMessageSet(encoder, oq.Investments, "INVSTMTMSGSRQV1"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if err := marshalMessageSet(encoder, oq.Profile, "PROFMSGSRQV1"); err != nil {
|
if err := marshalMessageSet(encoder, oq.Profile, "PROFMSGSRQV1"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ type Response struct {
|
|||||||
Banking []Message //<BANKMSGSETV1>
|
Banking []Message //<BANKMSGSETV1>
|
||||||
//<CREDITCARDMSGSETV1>
|
//<CREDITCARDMSGSETV1>
|
||||||
//<LOANMSGSETV1>
|
//<LOANMSGSETV1>
|
||||||
//<INVSTMTMSGSETV1>
|
Investments []Message //<INVSTMTMSGSETV1>
|
||||||
//<INTERXFERMSGSETV1>
|
//<INTERXFERMSGSETV1>
|
||||||
//<WIREXFERMSGSETV1>
|
//<WIREXFERMSGSETV1>
|
||||||
//<BILLPAYMSGSETV1>
|
//<BILLPAYMSGSETV1>
|
||||||
@ -271,7 +271,12 @@ func ParseResponse(reader io.Reader) (*Response, error) {
|
|||||||
or.Banking = msgs
|
or.Banking = msgs
|
||||||
//case "CREDITCARDMSGSRSV1":
|
//case "CREDITCARDMSGSRSV1":
|
||||||
//case "LOANMSGSRSV1":
|
//case "LOANMSGSRSV1":
|
||||||
//case "INVSTMTMSGSRSV1":
|
case "INVSTMTMSGSRSV1":
|
||||||
|
msgs, err := DecodeInvestmentsMessageSet(decoder, start)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
or.Investments = msgs
|
||||||
//case "INTERXFERMSGSRSV1":
|
//case "INTERXFERMSGSRSV1":
|
||||||
//case "WIREXFERMSGSRSV1":
|
//case "WIREXFERMSGSRSV1":
|
||||||
//case "BILLPAYMSGSRSV1":
|
//case "BILLPAYMSGSRSV1":
|
||||||
|
16
signon.go
16
signon.go
@ -55,22 +55,6 @@ func (r *SignonRequest) Valid() (bool, error) {
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Status struct {
|
|
||||||
XMLName xml.Name `xml:"STATUS"`
|
|
||||||
Code Int `xml:"CODE"`
|
|
||||||
Severity String `xml:"SEVERITY"`
|
|
||||||
Message String `xml:"MESSAGE,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Status) Valid() (bool, error) {
|
|
||||||
switch s.Severity {
|
|
||||||
case "INFO", "WARN", "ERROR":
|
|
||||||
return true, nil
|
|
||||||
default:
|
|
||||||
return false, errors.New("Invalid STATUS>SEVERITY")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type SignonResponse struct {
|
type SignonResponse struct {
|
||||||
XMLName xml.Name `xml:"SONRS"`
|
XMLName xml.Name `xml:"SONRS"`
|
||||||
Status Status `xml:"STATUS"`
|
Status Status `xml:"STATUS"`
|
||||||
|
@ -76,15 +76,9 @@ func (ci *CCAcctInfo) String() string {
|
|||||||
return fmt.Sprintf("%+v", *ci)
|
return fmt.Sprintf("%+v", *ci)
|
||||||
}
|
}
|
||||||
|
|
||||||
type InvAcct struct {
|
|
||||||
XMLName xml.Name // INVACCTTO or INVACCTFROM
|
|
||||||
BrokerId String `xml:"BROKERID"`
|
|
||||||
AcctId String `xml:"ACCTID"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type InvAcctInfo struct {
|
type InvAcctInfo struct {
|
||||||
XMLName xml.Name `xml:"INVACCTINFO"`
|
XMLName xml.Name `xml:"INVACCTINFO"`
|
||||||
INVAcctFrom InvAcct `xml:"INVACCTFROM"`
|
InvAcctFrom InvAcct `xml:"INVACCTFROM"`
|
||||||
UsProductType String `xml:"USPRODUCTTYPE"` // One of 401K, 403B, IRA, KEOGH, OTHER, SARSEP, SIMPLE, NORMAL, TDA, TRUST, UGMA
|
UsProductType String `xml:"USPRODUCTTYPE"` // One of 401K, 403B, IRA, KEOGH, OTHER, SARSEP, SIMPLE, NORMAL, TDA, TRUST, UGMA
|
||||||
Checking Boolean `xml:"CHECKING"` // Has check-writing privileges
|
Checking Boolean `xml:"CHECKING"` // Has check-writing privileges
|
||||||
SvcStatus String `xml:"SVCSTATUS"` // One of AVAIL (available, but not yet requested), PEND (requested, but not yet available), ACTIVE
|
SvcStatus String `xml:"SVCSTATUS"` // One of AVAIL (available, but not yet requested), PEND (requested, but not yet available), ACTIVE
|
||||||
|
Loading…
Reference in New Issue
Block a user