2017-03-19 21:08:58 -04:00
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 {
2017-03-20 21:09:08 -04:00
XMLName xml . Name ` xml:"INVTRANLIST" `
DtStart Date ` xml:"DTSTART" `
DtEnd Date ` xml:"DTEND" `
BankTransactions [ ] InvBankTransaction ` xml:"INVBANKTRAN,omitempty" `
}
type InvPosition struct {
XMLName xml . Name ` xml:"INVPOS" `
SecId SecurityId ` xml:"SECID" `
HeldInAcct String ` xml:"HELDINACCT" ` // Sub-account type, one of CASH, MARGIN, SHORT, OTHER
PosType String ` xml:"POSTYPE" ` // SHORT = Writer for options, Short for all others; LONG = Holder for options, Long for all others.
Units Amount ` xml:"UNITS" ` // For stocks, MFs, other, number of shares held. Bonds = face value. Options = number of contracts
UnitPrice Amount ` xml:"UNITPRICE" ` // For stocks, MFs, other, price per share. Bonds = percentage of par. Option = premium per share of underlying security
MktVal Amount ` xml:"MKTVAL" ` // Market value of this position
AvgCostBasis Amount ` xml:"AVGCOSTBASIS,omitempty" ` //
DtPriceAsOf Date ` xml:"DTPRICEASOF" ` // Date and time of unit price and market value, and cost basis. If this date is unknown, use 19900101 as the placeholder; do not use 0,
Currency Currency ` xml:"CURRENCY,omitempty" ` // Overriding currency for UNITPRICE
Memo String ` xml:"MEMO,omitempty" `
Inv401kSource String ` xml:"INV401KSOURCE,omitempty" ` // One of PRETAX, AFTERTAX, MATCH, PROFITSHARING, ROLLOVER, OTHERVEST, OTHERNONVEST for 401(k) accounts. Default if not present is OTHERNONVEST. The following cash source types are subject to vesting: MATCH, PROFITSHARING, and OTHERVEST
}
type Position interface {
PositionType ( ) string
}
type DebtPosition struct {
XMLName xml . Name ` xml:"POSDEBT" `
InvPos InvPosition ` xml:"INVPOS" `
}
func ( p DebtPosition ) PositionType ( ) string {
return "POSDEBT"
}
type MFPosition struct {
XMLName xml . Name ` xml:"POSMF" `
InvPos InvPosition ` xml:"INVPOS" `
UnitsStreet Amount ` xml:"UNITSSTREET,omitempty" ` // Units in the FI’ s street name
UnitsUser Amount ` xml:"UNITSUSER,omitempty" ` // Units in the user's name directly
ReinvDiv Boolean ` xml:"REINVDIV,omitempty" ` // Reinvest dividends
ReinCG Boolean ` xml:"REINVCG,omitempty" ` // Reinvest capital gains
}
func ( p MFPosition ) PositionType ( ) string {
return "POSMF"
}
type OptPosition struct {
XMLName xml . Name ` xml:"POSOPT" `
InvPos InvPosition ` xml:"INVPOS" `
Secured String ` xml:"SECURED,omitempty" ` // One of NAKED, COVERED
}
func ( p OptPosition ) PositionType ( ) string {
return "POSOPT"
}
type OtherPosition struct {
XMLName xml . Name ` xml:"POSOTHER" `
InvPos InvPosition ` xml:"INVPOS" `
}
func ( p OtherPosition ) PositionType ( ) string {
return "POSOTHER"
}
type StockPosition struct {
XMLName xml . Name ` xml:"POSSTOCK" `
InvPos InvPosition ` xml:"INVPOS" `
UnitsStreet Amount ` xml:"UNITSSTREET,omitempty" ` // Units in the FI’ s street name
UnitsUser Amount ` xml:"UNITSUSER,omitempty" ` // Units in the user's name directly
ReinvDiv Boolean ` xml:"REINVDIV,omitempty" ` // Reinvest dividends
}
func ( p StockPosition ) PositionType ( ) string {
return "POSSTOCK"
}
type PositionList [ ] Position
func ( p PositionList ) UnmarshalXML ( d * xml . Decoder , start xml . StartElement ) error {
for {
tok , err := nextNonWhitespaceToken ( d )
if err != nil {
return 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 nil
} else if startElement , ok := tok . ( xml . StartElement ) ; ok {
switch startElement . Name . Local {
case "POSDEBT" :
var position DebtPosition
if err := d . DecodeElement ( & position , & startElement ) ; err != nil {
return err
}
p = append ( p , Position ( position ) )
case "POSMF" :
var position MFPosition
if err := d . DecodeElement ( & position , & startElement ) ; err != nil {
return err
}
p = append ( p , Position ( position ) )
case "POSOPT" :
var position OptPosition
if err := d . DecodeElement ( & position , & startElement ) ; err != nil {
return err
}
p = append ( p , Position ( position ) )
case "POSOTHER" :
var position OtherPosition
if err := d . DecodeElement ( & position , & startElement ) ; err != nil {
return err
}
p = append ( p , Position ( position ) )
case "POSSTOCK" :
var position StockPosition
if err := d . DecodeElement ( & position , & startElement ) ; err != nil {
return err
}
p = append ( p , Position ( position ) )
default :
return errors . New ( "Invalid INVPOSLIST child tag: " + startElement . Name . Local )
}
} else {
return errors . New ( "Didn't find an opening element" )
}
}
2017-03-19 21:08:58 -04:00
}
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" `
2017-03-20 21:09:08 -04:00
InvPosList PositionList ` xml:"INVSTMTRS>INVPOSLIST,omitempty" `
InvBal InvBalance ` xml:"INVSTMTRS>INVBAL,omitempty" `
2017-03-19 21:08:58 -04:00
// 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" )
}
}
}