package ofxgo
import (
"reflect"
"strings"
"testing"
"time"
"github.com/aclindsa/xml"
)
func TestMarshalInvStatementRequest(t *testing.T) {
var expectedString string = `
20160224131905.000[-5:EST]
1998124
Sup3eSekrit
ENG
First Bank
01
MYAPP
1234
382827d6-e2d0-4396-bf3b-665979285420
fi.example.com
82736664
20160101000000.000[-5:EST]
Y
Y
Y
Y
`
var client = BasicClient{
AppID: "MYAPP",
AppVer: "1234",
SpecVersion: OfxVersion203,
}
var request Request
request.Signon.UserID = "1998124"
request.Signon.UserPass = "Sup3eSekrit"
request.Signon.Org = "First Bank"
request.Signon.Fid = "01"
EST := time.FixedZone("EST", -5*60*60)
statementRequest := InvStatementRequest{
TrnUID: "382827d6-e2d0-4396-bf3b-665979285420",
InvAcctFrom: InvAcct{
BrokerID: "fi.example.com",
AcctID: "82736664",
},
DtStart: NewDate(2016, 1, 1, 0, 0, 0, 0, EST),
Include: true,
IncludeOO: true,
IncludePos: true,
IncludeBalance: true,
}
request.InvStmt = append(request.InvStmt, &statementRequest)
request.SetClientFields(&client)
// Overwrite the DtClient value set by SetClientFields to time.Now()
request.Signon.DtClient = *NewDate(2016, 2, 24, 13, 19, 5, 0, EST)
marshalCheckRequest(t, &request, expectedString)
}
func TestUnmarshalInvStatementResponse(t *testing.T) {
responseReader := strings.NewReader(`
0
INFO
20170401201244
ENG
INVSTRUS
9999
1a0117ad-692b-4c6a-a21b-020d37d34d49
0
INFO
20170331000000
USD
invstrus.com
91827364
20170101000000
20170331000000
729483191
20170203
20170207
78462F103
CUSIP
100
229.00
9.00
-22909.00
CASH
CASH
BUY
CREDIT
20170120
20170118
20170123
22000.00
993838
DEPOSIT
CHECK 19980
CASH
78462F103
CUSIP
CASH
LONG
200
235.74
47148.00
20170331160000
Price as of previous close
129887339
CUSIP
CASH
LONG
1
3
300
20170331160000
16.73
-819.20
0
Sweep Int Rate
Current interest rate for sweep account balances
PERCENT
0.25
20170401
76464632
922908645
CUSIP
20170310124445
10
CASH
GOODTILCANCEL
NONE
168.50
BUY
SHARES
999387423
899422348
CUSIP
20170324031900
25
CASH
GOODTILCANCEL
ALLORNONE
19.75
BUY
78462F103
CUSIP
S&P 500 ETF
SPY
99184
1.92
OTHER
129887339
CUSIP
John's Fertilizer Puts
FERTP
882919
PUT
79.00
20170901
100
983322180
CUSIP
LARGESTOCK
899422348
CUSIP
Whatchamacallit, Inc.
WHAT
883897
17
SMALLSTOCK
922908645
CUSIP
Mid-Cap Index Fund Admiral Shares
VIMAX
99182828
CUSIP
Someone's Class B Debt
100.29
COUPON
20170901
QUARTERLY
88181818
CUSIP
Foo Bar
Don't know what this is
`)
var expected Response
expected.Version = OfxVersion203
expected.Signon.Status.Code = 0
expected.Signon.Status.Severity = "INFO"
expected.Signon.DtServer = *NewDateGMT(2017, 4, 1, 20, 12, 44, 0)
expected.Signon.Language = "ENG"
expected.Signon.Org = "INVSTRUS"
expected.Signon.Fid = "9999"
var units1, unitprice1, commission1, total1, amount2 Amount
units1.SetFrac64(100, 1)
unitprice1.SetFrac64(229, 1)
commission1.SetFrac64(9, 1)
total1.SetFrac64(-22909, 1)
amount2.SetFrac64(22000, 1)
invtranlist := InvTranList{
DtStart: *NewDateGMT(2017, 1, 1, 0, 0, 0, 0),
DtEnd: *NewDateGMT(2017, 3, 31, 0, 0, 0, 0),
InvTransactions: []InvTransaction{
BuyStock{
InvBuy: InvBuy{
InvTran: InvTran{
FiTID: "729483191",
DtTrade: *NewDateGMT(2017, 2, 3, 0, 0, 0, 0),
DtSettle: NewDateGMT(2017, 2, 7, 0, 0, 0, 0),
},
SecID: SecurityID{
UniqueID: "78462F103",
UniqueIDType: "CUSIP",
},
Units: units1,
UnitPrice: unitprice1,
Commission: commission1,
Total: total1,
SubAcctSec: SubAcctTypeCash,
SubAcctFund: SubAcctTypeCash,
},
BuyType: BuyTypeBuy,
},
},
BankTransactions: []InvBankTransaction{
{
Transactions: []Transaction{
{
TrnType: TrnTypeCredit,
DtPosted: *NewDateGMT(2017, 1, 20, 0, 0, 0, 0),
DtUser: NewDateGMT(2017, 1, 18, 0, 0, 0, 0),
DtAvail: NewDateGMT(2017, 1, 23, 0, 0, 0, 0),
TrnAmt: amount2,
FiTID: "993838",
Name: "DEPOSIT",
Memo: "CHECK 19980",
},
},
SubAcctFund: SubAcctTypeCash,
},
},
}
var availcash, marginbalance, shortbalance, balvalue Amount
availcash.SetFrac64(1673, 100)
marginbalance.SetFrac64(-8192, 10)
shortbalance.SetFrac64(0, 1)
balvalue.SetFrac64(25, 100)
invbalance := InvBalance{
AvailCash: availcash,
MarginBalance: marginbalance,
ShortBalance: shortbalance,
BalList: []Balance{
{
Name: "Sweep Int Rate",
Desc: "Current interest rate for sweep account balances",
BalType: BalTypePercent,
Value: balvalue,
DtAsOf: NewDateGMT(2017, 4, 1, 0, 0, 0, 0),
},
},
}
var balamt, availbalamt, posunits1, posunitprice1, posmktval1, posunits2, posunitprice2, posmktval2, oounits1, oolimitprice1, oounits2, oolimitprice2 Amount
balamt.SetFrac64(20029, 100)
availbalamt.SetFrac64(20029, 100)
posunits1.SetFrac64(200, 1)
posunitprice1.SetFrac64(23574, 100)
posmktval1.SetFrac64(47148, 1)
posunits2.SetFrac64(1, 1)
posunitprice2.SetFrac64(3, 1)
posmktval2.SetFrac64(300, 1)
oounits1.SetFrac64(10, 1)
oolimitprice1.SetFrac64(16850, 100)
oounits2.SetFrac64(25, 1)
oolimitprice2.SetFrac64(1975, 100)
usd, err := NewCurrSymbol("USD")
if err != nil {
t.Fatalf("Unexpected error creating CurrSymbol for USD\n")
}
statementResponse := InvStatementResponse{
TrnUID: "1a0117ad-692b-4c6a-a21b-020d37d34d49",
Status: Status{
Code: 0,
Severity: "INFO",
},
DtAsOf: *NewDateGMT(2017, 3, 31, 0, 0, 0, 0),
CurDef: *usd,
InvAcctFrom: InvAcct{
BrokerID: "invstrus.com",
AcctID: "91827364",
},
InvTranList: &invtranlist,
InvPosList: PositionList{
StockPosition{
InvPos: InvPosition{
SecID: SecurityID{
UniqueID: "78462F103",
UniqueIDType: "CUSIP",
},
HeldInAcct: SubAcctTypeCash,
PosType: PosTypeLong,
Units: posunits1,
UnitPrice: posunitprice1,
MktVal: posmktval1,
DtPriceAsOf: *NewDateGMT(2017, 3, 31, 16, 0, 0, 0),
Memo: "Price as of previous close",
},
},
OptPosition{
InvPos: InvPosition{
SecID: SecurityID{
UniqueID: "129887339",
UniqueIDType: "CUSIP",
},
HeldInAcct: SubAcctTypeCash,
PosType: PosTypeLong,
Units: posunits2,
UnitPrice: posunitprice2,
MktVal: posmktval2,
DtPriceAsOf: *NewDateGMT(2017, 3, 31, 16, 0, 0, 0),
},
},
},
InvBal: &invbalance,
InvOOList: OOList{
OOBuyMF{
OO: OO{
FiTID: "76464632",
SecID: SecurityID{
UniqueID: "922908645",
UniqueIDType: "CUSIP",
},
DtPlaced: *NewDateGMT(2017, 3, 10, 12, 44, 45, 0),
Units: oounits1,
SubAcct: SubAcctTypeCash,
Duration: DurationGoodTilCancel,
Restriction: RestrictionNone,
LimitPrice: oolimitprice1,
},
BuyType: BuyTypeBuy,
UnitType: UnitTypeShares,
},
OOBuyStock{
OO: OO{
FiTID: "999387423",
SecID: SecurityID{
UniqueID: "899422348",
UniqueIDType: "CUSIP",
},
DtPlaced: *NewDateGMT(2017, 3, 24, 3, 19, 0, 0),
Units: oounits2,
SubAcct: SubAcctTypeCash,
Duration: DurationGoodTilCancel,
Restriction: RestrictionAllOrNone,
LimitPrice: oolimitprice2,
},
BuyType: BuyTypeBuy,
},
},
}
expected.InvStmt = append(expected.InvStmt, &statementResponse)
var yield1, yield2, strikeprice, parvalue Amount
yield1.SetFrac64(192, 100)
yield2.SetFrac64(17, 1)
strikeprice.SetFrac64(79, 1)
parvalue.SetFrac64(10029, 100)
seclist := SecurityList{
Securities: []Security{
StockInfo{
SecInfo: SecInfo{
SecID: SecurityID{
UniqueID: "78462F103",
UniqueIDType: "CUSIP",
},
SecName: "S&P 500 ETF",
Ticker: "SPY",
FiID: "99184",
},
Yield: yield1,
AssetClass: AssetClassOther,
},
OptInfo{
SecInfo: SecInfo{
SecID: SecurityID{
UniqueID: "129887339",
UniqueIDType: "CUSIP",
},
SecName: "John's Fertilizer Puts",
Ticker: "FERTP",
FiID: "882919",
},
OptType: OptTypePut,
StrikePrice: strikeprice,
DtExpire: *NewDateGMT(2017, 9, 1, 0, 0, 0, 0),
ShPerCtrct: 100,
SecID: &SecurityID{
UniqueID: "983322180",
UniqueIDType: "CUSIP",
},
AssetClass: AssetClassLargeStock,
},
StockInfo{
SecInfo: SecInfo{
SecID: SecurityID{
UniqueID: "899422348",
UniqueIDType: "CUSIP",
},
SecName: "Whatchamacallit, Inc.",
Ticker: "WHAT",
FiID: "883897",
},
Yield: yield2,
AssetClass: AssetClassSmallStock,
},
MFInfo{
SecInfo: SecInfo{
SecID: SecurityID{
UniqueID: "922908645",
UniqueIDType: "CUSIP",
},
SecName: "Mid-Cap Index Fund Admiral Shares",
Ticker: "VIMAX",
},
},
DebtInfo{
SecInfo: SecInfo{
SecID: SecurityID{
UniqueID: "99182828",
UniqueIDType: "CUSIP",
},
SecName: "Someone's Class B Debt",
},
ParValue: parvalue,
DebtType: DebtTypeCoupon,
DtCoupon: NewDateGMT(2017, 9, 1, 0, 0, 0, 0),
CouponFreq: CouponFreqQuarterly,
},
OtherInfo{
SecInfo: SecInfo{
SecID: SecurityID{
UniqueID: "88181818",
UniqueIDType: "CUSIP",
},
SecName: "Foo Bar",
},
TypeDesc: "Don't know what this is",
},
},
}
expected.SecList = append(expected.SecList, &seclist)
response, err := ParseResponse(responseReader)
if err != nil {
t.Fatalf("Unexpected error unmarshalling response: %s\n", err)
}
checkResponsesEqual(t, &expected, response)
checkResponseRoundTrip(t, response)
}
func TestUnmarshalInvStatementResponse102(t *testing.T) {
responseReader := strings.NewReader(`OFXHEADER: 100
DATA: OFXSGML
VERSION: 102
SECURITY: NONE
ENCODING: USASCII
CHARSET: 1252
COMPRESSION: NONE
OLDFILEUID: NONE
NEWFILEUID: NONE
0
INFO
20170403120000
ENG
VV
1000
1000
1283719872
0
INFO
20170403120000
USD
www.exampletrader.com
12341234
20161206120000
20170403120000
12341234-20161207-1
20161207120000
20161208120000
SPY161216C00226000
CUSIP
-1.0000
0.3500
8.8500
0.2600
200.8900
CASH
CASH
SELLTOOPEN
100
12341234-20161215-1
20161215120000
20161220120000
78462F10
CUSIP
ASSIGN
-100.0000
100
CASH
12341234-20161215-2
20161215120000
20161215120000
SPY161216C00226000
CUSIP
ASSIGN
1.0000
100
CASH
04956010
CUSIP
CASH
LONG
100
79.0000
79000
20170403120000
36960410
CUSIP
CASH
LONG
100.00
29.8700
2987.00
20170403120000
0.0
-0.00
0.00
78462F10
CUSIP
SPDR S&P 500 ETF TRUST
SPY
SPY161216C00226000
CUSIP
SPY Dec 16 2016 226.00 Call
SPY 161216C00226000
CALL
226.00
20161216120000
100
`)
var expected Response
expected.Version = OfxVersion102
expected.Signon.Status.Code = 0
expected.Signon.Status.Severity = "INFO"
expected.Signon.DtServer = *NewDateGMT(2017, 4, 3, 12, 0, 0, 0)
expected.Signon.Language = "ENG"
expected.Signon.Org = "VV"
expected.Signon.Fid = "1000"
// Ignored 1000
var units1, unitprice1, commission1, fees1, total1, units2, units3 Amount
units1.SetFrac64(-1, 1)
unitprice1.SetFrac64(35, 100)
commission1.SetFrac64(885, 100)
fees1.SetFrac64(26, 100)
total1.SetFrac64(20089, 100)
units2.SetFrac64(-100, 1)
units3.SetFrac64(1, 1)
invtranlist := InvTranList{
DtStart: *NewDateGMT(2016, 12, 6, 12, 0, 0, 0),
DtEnd: *NewDateGMT(2017, 4, 3, 12, 0, 0, 0),
InvTransactions: []InvTransaction{
SellOpt{
InvSell: InvSell{
InvTran: InvTran{
FiTID: "12341234-20161207-1",
DtTrade: *NewDateGMT(2016, 12, 7, 12, 0, 0, 0),
DtSettle: NewDateGMT(2016, 12, 8, 12, 0, 0, 0),
},
SecID: SecurityID{
UniqueID: "SPY161216C00226000",
UniqueIDType: "CUSIP",
},
Units: units1,
UnitPrice: unitprice1,
Commission: commission1,
Fees: fees1,
Total: total1,
SubAcctSec: SubAcctTypeCash,
SubAcctFund: SubAcctTypeCash,
},
OptSellType: OptSellTypeSellToOpen,
ShPerCtrct: 100,
},
ClosureOpt{
InvTran: InvTran{
FiTID: "12341234-20161215-1",
DtTrade: *NewDateGMT(2016, 12, 15, 12, 0, 0, 0),
DtSettle: NewDateGMT(2016, 12, 20, 12, 0, 0, 0),
},
SecID: SecurityID{
UniqueID: "78462F10",
UniqueIDType: "CUSIP",
},
OptAction: OptActionAssign,
Units: units2,
ShPerCtrct: 100,
SubAcctSec: SubAcctTypeCash,
},
ClosureOpt{
InvTran: InvTran{
FiTID: "12341234-20161215-2",
DtTrade: *NewDateGMT(2016, 12, 15, 12, 0, 0, 0),
DtSettle: NewDateGMT(2016, 12, 15, 12, 0, 0, 0),
},
SecID: SecurityID{
UniqueID: "SPY161216C00226000",
UniqueIDType: "CUSIP",
},
OptAction: OptActionAssign,
Units: units3,
ShPerCtrct: 100,
SubAcctSec: SubAcctTypeCash,
},
},
}
var availcash, marginbalance, shortbalance Amount
availcash.SetFrac64(0, 1)
marginbalance.SetFrac64(-0, 1)
shortbalance.SetFrac64(0, 1)
invbalance := InvBalance{
AvailCash: availcash,
MarginBalance: marginbalance,
ShortBalance: shortbalance,
}
var posunits1, posunitprice1, posmktval1, posunits2, posunitprice2, posmktval2 Amount
posunits1.SetFrac64(100, 1)
posunitprice1.SetFrac64(79, 1)
posmktval1.SetFrac64(79000, 1)
posunits2.SetFrac64(100, 1)
posunitprice2.SetFrac64(2987, 100)
posmktval2.SetFrac64(2987, 1)
usd, err := NewCurrSymbol("USD")
if err != nil {
t.Fatalf("Unexpected error creating CurrSymbol for USD\n")
}
statementResponse := InvStatementResponse{
TrnUID: "1283719872",
Status: Status{
Code: 0,
Severity: "INFO",
},
DtAsOf: *NewDateGMT(2017, 4, 3, 12, 0, 0, 0),
CurDef: *usd,
InvAcctFrom: InvAcct{
BrokerID: "www.exampletrader.com",
AcctID: "12341234",
},
InvTranList: &invtranlist,
InvPosList: PositionList{
StockPosition{
InvPos: InvPosition{
SecID: SecurityID{
UniqueID: "04956010",
UniqueIDType: "CUSIP",
},
HeldInAcct: SubAcctTypeCash,
PosType: PosTypeLong,
Units: posunits1,
UnitPrice: posunitprice1,
MktVal: posmktval1,
DtPriceAsOf: *NewDateGMT(2017, 4, 3, 12, 0, 0, 0),
},
},
StockPosition{
InvPos: InvPosition{
SecID: SecurityID{
UniqueID: "36960410",
UniqueIDType: "CUSIP",
},
HeldInAcct: SubAcctTypeCash,
PosType: PosTypeLong,
Units: posunits2,
UnitPrice: posunitprice2,
MktVal: posmktval2,
DtPriceAsOf: *NewDateGMT(2017, 4, 3, 12, 0, 0, 0),
},
},
},
InvBal: &invbalance,
}
expected.InvStmt = append(expected.InvStmt, &statementResponse)
var strikeprice Amount
strikeprice.SetFrac64(226, 1)
seclist := SecurityList{
Securities: []Security{
StockInfo{
SecInfo: SecInfo{
SecID: SecurityID{
UniqueID: "78462F10",
UniqueIDType: "CUSIP",
},
SecName: "SPDR S&P 500 ETF TRUST",
Ticker: "SPY",
},
},
OptInfo{
SecInfo: SecInfo{
SecID: SecurityID{
UniqueID: "SPY161216C00226000",
UniqueIDType: "CUSIP",
},
SecName: "SPY Dec 16 2016 226.00 Call",
Ticker: "SPY 161216C00226000",
},
OptType: OptTypeCall,
StrikePrice: strikeprice,
DtExpire: *NewDateGMT(2016, 12, 16, 12, 0, 0, 0),
ShPerCtrct: 100,
},
},
}
expected.SecList = append(expected.SecList, &seclist)
response, err := ParseResponse(responseReader)
if err != nil {
t.Fatalf("Unexpected error unmarshalling response: %s\n", err)
}
checkResponsesEqual(t, &expected, response)
checkResponseRoundTrip(t, response)
}
func TestUnmarshalInvTranList(t *testing.T) {
input := `
20170101000000
20170331000000
81818
20170203
20170207
78462F103
CUSIP
100
229.00
9.00
.26
-22090.26
CASH
CASH
101.2
81818
20170203
Something to make a memo about
78462F103
CUSIP
100
229.00
-22090.26
CASH
CASH
BUYTOOPEN
100
129837-1111
20170203
78462F103
CUSIP
0.26
CASH
CASH
129837-1112
20170203
78462F103
CUSIP
2300
CASH
CASH
129837-1112
20170203
2300
CASH
CASH
81818
20170203
78462F103
CUSIP
100
229.00
-22090.26
CASH
CASH
129837-1112
20170203
2300
CASH
129837-1111
20170203
78462F103
CUSIP
100
229.00
-22090.26
CASH
CASH
SELL
129837-1111
20170203
78462F103
CUSIP
2300.00
CASH
CASH
129837-1111
20170203
78462F103
CUSIP
CASH
100
200
2
1
129837-1111
20170203
78462F103
CUSIP
100
229.00
-22090.26
CASH
CASH
`
var units1, unitprice1, commission1, fees1, total1, accrdint, total2, oldunits1, newunits1 Amount
units1.SetFrac64(100, 1)
unitprice1.SetFrac64(229, 1)
commission1.SetFrac64(9, 1)
fees1.SetFrac64(26, 100)
total1.SetFrac64(-2209026, 100)
accrdint.SetFrac64(1012, 10)
total2.SetFrac64(2300, 1)
oldunits1.SetFrac64(100, 1)
newunits1.SetFrac64(200, 1)
expected := InvTranList{
DtStart: *NewDateGMT(2017, 1, 1, 0, 0, 0, 0),
DtEnd: *NewDateGMT(2017, 3, 31, 0, 0, 0, 0),
InvTransactions: []InvTransaction{
BuyDebt{
InvBuy: InvBuy{
InvTran: InvTran{
FiTID: "81818",
DtTrade: *NewDateGMT(2017, 2, 3, 0, 0, 0, 0),
DtSettle: NewDateGMT(2017, 2, 7, 0, 0, 0, 0),
},
SecID: SecurityID{
UniqueID: "78462F103",
UniqueIDType: "CUSIP",
},
Units: units1,
UnitPrice: unitprice1,
Commission: commission1,
Fees: fees1,
Total: total1,
SubAcctSec: SubAcctTypeCash,
SubAcctFund: SubAcctTypeCash,
},
AccrdInt: accrdint,
},
BuyOpt{
InvBuy: InvBuy{
InvTran: InvTran{
FiTID: "81818",
DtTrade: *NewDateGMT(2017, 2, 3, 0, 0, 0, 0),
Memo: "Something to make a memo about",
},
SecID: SecurityID{
UniqueID: "78462F103",
UniqueIDType: "CUSIP",
},
Units: units1,
UnitPrice: unitprice1,
Total: total1,
SubAcctSec: SubAcctTypeCash,
SubAcctFund: SubAcctTypeCash,
},
OptBuyType: OptBuyTypeBuyToOpen,
ShPerCtrct: 100,
},
InvExpense{
InvTran: InvTran{
FiTID: "129837-1111",
DtTrade: *NewDateGMT(2017, 2, 3, 0, 0, 0, 0),
},
SecID: SecurityID{
UniqueID: "78462F103",
UniqueIDType: "CUSIP",
},
Total: fees1,
SubAcctSec: SubAcctTypeCash,
SubAcctFund: SubAcctTypeCash,
},
JrnlSec{
InvTran: InvTran{
FiTID: "129837-1112",
DtTrade: *NewDateGMT(2017, 2, 3, 0, 0, 0, 0),
},
SecID: SecurityID{
UniqueID: "78462F103",
UniqueIDType: "CUSIP",
},
Units: total2,
SubAcctTo: SubAcctTypeCash,
SubAcctFrom: SubAcctTypeCash,
},
JrnlFund{
InvTran: InvTran{
FiTID: "129837-1112",
DtTrade: *NewDateGMT(2017, 2, 3, 0, 0, 0, 0),
},
Total: total2,
SubAcctTo: SubAcctTypeCash,
SubAcctFrom: SubAcctTypeCash,
},
BuyOther{
InvBuy: InvBuy{
InvTran: InvTran{
FiTID: "81818",
DtTrade: *NewDateGMT(2017, 2, 3, 0, 0, 0, 0),
},
SecID: SecurityID{
UniqueID: "78462F103",
UniqueIDType: "CUSIP",
},
Units: units1,
UnitPrice: unitprice1,
Total: total1,
SubAcctSec: SubAcctTypeCash,
SubAcctFund: SubAcctTypeCash,
},
},
MarginInterest{
InvTran: InvTran{
FiTID: "129837-1112",
DtTrade: *NewDateGMT(2017, 2, 3, 0, 0, 0, 0),
},
Total: total2,
SubAcctFund: SubAcctTypeCash,
},
SellDebt{
InvSell: InvSell{
InvTran: InvTran{
FiTID: "129837-1111",
DtTrade: *NewDateGMT(2017, 2, 3, 0, 0, 0, 0),
},
SecID: SecurityID{
UniqueID: "78462F103",
UniqueIDType: "CUSIP",
},
Units: units1,
UnitPrice: unitprice1,
Total: total1,
SubAcctSec: SubAcctTypeCash,
SubAcctFund: SubAcctTypeCash,
},
SellReason: SellReasonSell,
},
RetOfCap{
InvTran: InvTran{
FiTID: "129837-1111",
DtTrade: *NewDateGMT(2017, 2, 3, 0, 0, 0, 0),
},
SecID: SecurityID{
UniqueID: "78462F103",
UniqueIDType: "CUSIP",
},
Total: total2,
SubAcctSec: SubAcctTypeCash,
SubAcctFund: SubAcctTypeCash,
},
Split{
InvTran: InvTran{
FiTID: "129837-1111",
DtTrade: *NewDateGMT(2017, 2, 3, 0, 0, 0, 0),
},
SecID: SecurityID{
UniqueID: "78462F103",
UniqueIDType: "CUSIP",
},
SubAcctSec: SubAcctTypeCash,
OldUnits: oldunits1,
NewUnits: newunits1,
Numerator: 2,
Denominator: 1,
},
SellOther{
InvSell: InvSell{
InvTran: InvTran{
FiTID: "129837-1111",
DtTrade: *NewDateGMT(2017, 2, 3, 0, 0, 0, 0),
},
SecID: SecurityID{
UniqueID: "78462F103",
UniqueIDType: "CUSIP",
},
Units: units1,
UnitPrice: unitprice1,
Total: total1,
SubAcctSec: SubAcctTypeCash,
SubAcctFund: SubAcctTypeCash,
},
},
},
}
var actual InvTranList
err := xml.Unmarshal([]byte(input), &actual)
if err != nil {
t.Fatalf("Unexpected error unmarshalling InvTranList: %s\n", err)
}
checkEqual(t, "InvTranList", reflect.ValueOf(&expected), reflect.ValueOf(&actual))
}
func TestUnmarshalPositionList(t *testing.T) {
input := `
78462F103
CUSIP
CASH
LONG
1
3
300
20170331160000
78462F103
CUSIP
CASH
SHORT
200
235.74
47148.00
20170331160000
Price as of previous close
Y
129887339
CUSIP
CASH
LONG
1
3
300
20170331160000
129887339
CUSIP
CASH
LONG
1
3
300
20170331160000
78462F103
CUSIP
CASH
LONG
200
235.74
47148.00
20170331160000
Price as of previous close
Y
N
`
var posunits1, posunitprice1, posmktval1, posunits2, posunitprice2, posmktval2 Amount
posunits1.SetFrac64(200, 1)
posunitprice1.SetFrac64(23574, 100)
posmktval1.SetFrac64(47148, 1)
posunits2.SetFrac64(1, 1)
posunitprice2.SetFrac64(3, 1)
posmktval2.SetFrac64(300, 1)
expected := PositionList{
OtherPosition{
InvPos: InvPosition{
SecID: SecurityID{
UniqueID: "78462F103",
UniqueIDType: "CUSIP",
},
HeldInAcct: SubAcctTypeCash,
PosType: PosTypeLong,
Units: posunits2,
UnitPrice: posunitprice2,
MktVal: posmktval2,
DtPriceAsOf: *NewDateGMT(2017, 3, 31, 16, 0, 0, 0),
},
},
StockPosition{
InvPos: InvPosition{
SecID: SecurityID{
UniqueID: "78462F103",
UniqueIDType: "CUSIP",
},
HeldInAcct: SubAcctTypeCash,
PosType: PosTypeShort,
Units: posunits1,
UnitPrice: posunitprice1,
MktVal: posmktval1,
DtPriceAsOf: *NewDateGMT(2017, 3, 31, 16, 0, 0, 0),
Memo: "Price as of previous close",
},
ReinvDiv: true,
},
DebtPosition{
InvPos: InvPosition{
SecID: SecurityID{
UniqueID: "129887339",
UniqueIDType: "CUSIP",
},
HeldInAcct: SubAcctTypeCash,
PosType: PosTypeLong,
Units: posunits2,
UnitPrice: posunitprice2,
MktVal: posmktval2,
DtPriceAsOf: *NewDateGMT(2017, 3, 31, 16, 0, 0, 0),
},
},
OptPosition{
InvPos: InvPosition{
SecID: SecurityID{
UniqueID: "129887339",
UniqueIDType: "CUSIP",
},
HeldInAcct: SubAcctTypeCash,
PosType: PosTypeLong,
Units: posunits2,
UnitPrice: posunitprice2,
MktVal: posmktval2,
DtPriceAsOf: *NewDateGMT(2017, 3, 31, 16, 0, 0, 0),
},
},
MFPosition{
InvPos: InvPosition{
SecID: SecurityID{
UniqueID: "78462F103",
UniqueIDType: "CUSIP",
},
HeldInAcct: SubAcctTypeCash,
PosType: PosTypeLong,
Units: posunits1,
UnitPrice: posunitprice1,
MktVal: posmktval1,
DtPriceAsOf: *NewDateGMT(2017, 3, 31, 16, 0, 0, 0),
Memo: "Price as of previous close",
},
ReinvDiv: true,
ReinvCG: false,
},
}
var actual PositionList
err := xml.Unmarshal([]byte(input), &actual)
if err != nil {
t.Fatalf("Unexpected error unmarshalling PositionList: %s\n", err)
}
checkEqual(t, "PositionList", reflect.ValueOf(&expected), reflect.ValueOf(&actual))
}
func TestUnmarshalOOList(t *testing.T) {
input := `
76464632
922908645
CUSIP
20170310124445
10
CASH
DAY
NONE
Y
76464632
922908645
CUSIP
20170310124445
10
CASH
GOODTILCANCEL
NONE
168.50
BUY
SHARES
999387423
899422348
CUSIP
20170324031900
25
CASH
GOODTILCANCEL
ALLORNONE
19.75
BUYTOCLOSE
999387423
899422348
CUSIP
20170324031900
25
CASH
GOODTILCANCEL
ALLORNONE
19.75
BUY
999387423
899422348
CUSIP
20170324031900
25
CASH
GOODTILCANCEL
ALLORNONE
19.75
CURRENCY
999387423
899422348
CUSIP
20170324031900
25
CASH
GOODTILCANCEL
ALLORNONE
999387423
899422348
CUSIP
20170324031900
25
CASH
GOODTILCANCEL
ALLORNONE
SELLSHORT
SHARES
Y
999387423
899422348
CUSIP
20170324031900
25
CASH
GOODTILCANCEL
ALLORNONE
SELLTOOPEN
999387423
899422348
CUSIP
20170324031900
25
CASH
GOODTILCANCEL
ALLORNONE
SHARES
999387423
899422348
CUSIP
20170324031900
25
CASH
GOODTILCANCEL
ALLORNONE
SELL
999387423
899422348
CUSIP
20170324031900
25
CASH
GOODTILCANCEL
ALLORNONE
899422389
CUSIP
CURRENCY
N
`
var oounits1, oolimitprice1, oounits2, oolimitprice2 Amount
oounits1.SetFrac64(10, 1)
oolimitprice1.SetFrac64(16850, 100)
oounits2.SetFrac64(25, 1)
oolimitprice2.SetFrac64(1975, 100)
expected := OOList{
OOBuyDebt{
OO: OO{
FiTID: "76464632",
SecID: SecurityID{
UniqueID: "922908645",
UniqueIDType: "CUSIP",
},
DtPlaced: *NewDateGMT(2017, 3, 10, 12, 44, 45, 0),
Units: oounits1,
SubAcct: SubAcctTypeCash,
Duration: DurationDay,
Restriction: RestrictionNone,
},
Auction: true,
},
OOBuyMF{
OO: OO{
FiTID: "76464632",
SecID: SecurityID{
UniqueID: "922908645",
UniqueIDType: "CUSIP",
},
DtPlaced: *NewDateGMT(2017, 3, 10, 12, 44, 45, 0),
Units: oounits1,
SubAcct: SubAcctTypeCash,
Duration: DurationGoodTilCancel,
Restriction: RestrictionNone,
LimitPrice: oolimitprice1,
},
BuyType: BuyTypeBuy,
UnitType: UnitTypeShares,
},
OOBuyOpt{
OO: OO{
FiTID: "999387423",
SecID: SecurityID{
UniqueID: "899422348",
UniqueIDType: "CUSIP",
},
DtPlaced: *NewDateGMT(2017, 3, 24, 3, 19, 0, 0),
Units: oounits2,
SubAcct: SubAcctTypeCash,
Duration: DurationGoodTilCancel,
Restriction: RestrictionAllOrNone,
LimitPrice: oolimitprice2,
},
OptBuyType: OptBuyTypeBuyToClose,
},
OOBuyStock{
OO: OO{
FiTID: "999387423",
SecID: SecurityID{
UniqueID: "899422348",
UniqueIDType: "CUSIP",
},
DtPlaced: *NewDateGMT(2017, 3, 24, 3, 19, 0, 0),
Units: oounits2,
SubAcct: SubAcctTypeCash,
Duration: DurationGoodTilCancel,
Restriction: RestrictionAllOrNone,
LimitPrice: oolimitprice2,
},
BuyType: BuyTypeBuy,
},
OOBuyOther{
OO: OO{
FiTID: "999387423",
SecID: SecurityID{
UniqueID: "899422348",
UniqueIDType: "CUSIP",
},
DtPlaced: *NewDateGMT(2017, 3, 24, 3, 19, 0, 0),
Units: oounits2,
SubAcct: SubAcctTypeCash,
Duration: DurationGoodTilCancel,
Restriction: RestrictionAllOrNone,
LimitPrice: oolimitprice2,
},
UnitType: UnitTypeCurrency,
},
OOSellDebt{
OO: OO{
FiTID: "999387423",
SecID: SecurityID{
UniqueID: "899422348",
UniqueIDType: "CUSIP",
},
DtPlaced: *NewDateGMT(2017, 3, 24, 3, 19, 0, 0),
Units: oounits2,
SubAcct: SubAcctTypeCash,
Duration: DurationGoodTilCancel,
Restriction: RestrictionAllOrNone,
},
},
OOSellMF{
OO: OO{
FiTID: "999387423",
SecID: SecurityID{
UniqueID: "899422348",
UniqueIDType: "CUSIP",
},
DtPlaced: *NewDateGMT(2017, 3, 24, 3, 19, 0, 0),
Units: oounits2,
SubAcct: SubAcctTypeCash,
Duration: DurationGoodTilCancel,
Restriction: RestrictionAllOrNone,
},
SellType: SellTypeSellShort,
UnitType: UnitTypeShares,
SellAll: true,
},
OOSellOpt{
OO: OO{
FiTID: "999387423",
SecID: SecurityID{
UniqueID: "899422348",
UniqueIDType: "CUSIP",
},
DtPlaced: *NewDateGMT(2017, 3, 24, 3, 19, 0, 0),
Units: oounits2,
SubAcct: SubAcctTypeCash,
Duration: DurationGoodTilCancel,
Restriction: RestrictionAllOrNone,
},
OptSellType: OptSellTypeSellToOpen,
},
OOSellOther{
OO: OO{
FiTID: "999387423",
SecID: SecurityID{
UniqueID: "899422348",
UniqueIDType: "CUSIP",
},
DtPlaced: *NewDateGMT(2017, 3, 24, 3, 19, 0, 0),
Units: oounits2,
SubAcct: SubAcctTypeCash,
Duration: DurationGoodTilCancel,
Restriction: RestrictionAllOrNone,
},
UnitType: UnitTypeShares,
},
OOSellStock{
OO: OO{
FiTID: "999387423",
SecID: SecurityID{
UniqueID: "899422348",
UniqueIDType: "CUSIP",
},
DtPlaced: *NewDateGMT(2017, 3, 24, 3, 19, 0, 0),
Units: oounits2,
SubAcct: SubAcctTypeCash,
Duration: DurationGoodTilCancel,
Restriction: RestrictionAllOrNone,
},
SellType: SellTypeSell,
},
OOSwitchMF{
OO: OO{
FiTID: "999387423",
SecID: SecurityID{
UniqueID: "899422348",
UniqueIDType: "CUSIP",
},
DtPlaced: *NewDateGMT(2017, 3, 24, 3, 19, 0, 0),
Units: oounits2,
SubAcct: SubAcctTypeCash,
Duration: DurationGoodTilCancel,
Restriction: RestrictionAllOrNone,
},
SecID: SecurityID{
UniqueID: "899422389",
UniqueIDType: "CUSIP",
},
UnitType: UnitTypeCurrency,
SwitchAll: false,
},
}
var actual OOList
err := xml.Unmarshal([]byte(input), &actual)
if err != nil {
t.Fatalf("Unexpected error unmarshalling OOList: %s\n", err)
}
checkEqual(t, "OOList", reflect.ValueOf(&expected), reflect.ValueOf(&actual))
}
func TestSecurityInfo(t *testing.T) {
secInfo := SecInfo{
Ticker: "ABC",
}
tests := []Security{
DebtInfo{SecInfo: secInfo},
MFInfo{SecInfo: secInfo},
OptInfo{SecInfo: secInfo},
OtherInfo{SecInfo: secInfo},
StockInfo{SecInfo: secInfo},
}
for _, tc := range tests {
t.Run(tc.SecurityType(), func(t *testing.T) {
info := tc.SecurityInfo()
if info.Ticker != secInfo.Ticker {
t.Errorf("got %v, want %v", info, secInfo)
}
})
}
}
func TestInvPosition(t *testing.T) {
invPos := InvPosition{
Memo: "stuff",
}
tests := []Position{
DebtPosition{InvPos: invPos},
MFPosition{InvPos: invPos},
OptPosition{InvPos: invPos},
OtherPosition{InvPos: invPos},
StockPosition{InvPos: invPos},
}
for _, tc := range tests {
t.Run(tc.PositionType(), func(t *testing.T) {
pos := tc.InvPosition()
if pos.Memo != invPos.Memo {
t.Errorf("got %v, want %v", pos, invPos)
}
})
}
}
func TestInvTransaction(t *testing.T) {
invTran := InvTran{
Memo: "stuff",
}
tests := []InvTransaction{
BuyDebt{InvBuy: InvBuy{InvTran: invTran}},
BuyMF{InvBuy: InvBuy{InvTran: invTran}},
BuyOpt{InvBuy: InvBuy{InvTran: invTran}},
BuyOther{InvBuy: InvBuy{InvTran: invTran}},
BuyStock{InvBuy: InvBuy{InvTran: invTran}},
ClosureOpt{InvTran: invTran},
Income{InvTran: invTran},
InvExpense{InvTran: invTran},
JrnlFund{InvTran: invTran},
JrnlSec{InvTran: invTran},
MarginInterest{InvTran: invTran},
Reinvest{InvTran: invTran},
RetOfCap{InvTran: invTran},
SellDebt{InvSell: InvSell{InvTran: invTran}},
SellMF{InvSell: InvSell{InvTran: invTran}},
SellOpt{InvSell: InvSell{InvTran: invTran}},
SellOther{InvSell: InvSell{InvTran: invTran}},
SellStock{InvSell: InvSell{InvTran: invTran}},
Split{InvTran: invTran},
Transfer{InvTran: invTran},
}
for _, tc := range tests {
t.Run(tc.TransactionType(), func(t *testing.T) {
tran := tc.InvTransaction()
if tran.Memo != invTran.Memo {
t.Errorf("got %v, want %v", tran, invTran)
}
})
}
}