package ofxgo_test
import (
"github.com/aclindsa/ofxgo"
"strings"
"testing"
"time"
)
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 = ofxgo.Client{
AppId: "MYAPP",
AppVer: "1234",
SpecVersion: "203",
}
var request ofxgo.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 := ofxgo.InvStatementRequest{
TrnUID: "382827d6-e2d0-4396-bf3b-665979285420",
InvAcctFrom: ofxgo.InvAcct{
BrokerId: "fi.example.com",
AcctId: "82736664",
},
DtStart: ofxgo.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 = *ofxgo.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 ofxgo.Response
expected.Version = "203"
expected.Signon.Status.Code = 0
expected.Signon.Status.Severity = "INFO"
expected.Signon.DtServer = *ofxgo.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 ofxgo.Amount
units1.SetFrac64(100, 1)
unitprice1.SetFrac64(229, 1)
commission1.SetFrac64(9, 1)
total1.SetFrac64(-22909, 1)
amount2.SetFrac64(22000, 1)
invtranlist := ofxgo.InvTranList{
DtStart: *ofxgo.NewDateGMT(2017, 1, 1, 0, 0, 0, 0),
DtEnd: *ofxgo.NewDateGMT(2017, 3, 31, 0, 0, 0, 0),
InvTransactions: []ofxgo.InvTransaction{
ofxgo.BuyStock{
InvBuy: ofxgo.InvBuy{
InvTran: ofxgo.InvTran{
FiTId: "729483191",
DtTrade: *ofxgo.NewDateGMT(2017, 2, 3, 0, 0, 0, 0),
DtSettle: ofxgo.NewDateGMT(2017, 2, 7, 0, 0, 0, 0),
},
SecId: ofxgo.SecurityId{
UniqueId: "78462F103",
UniqueIdType: "CUSIP",
},
Units: units1,
UnitPrice: unitprice1,
Commission: commission1,
Total: total1,
SubAcctSec: ofxgo.SubAcctTypeCash,
SubAcctFund: ofxgo.SubAcctTypeCash,
},
BuyType: ofxgo.BuyTypeBuy,
},
},
BankTransactions: []ofxgo.InvBankTransaction{
ofxgo.InvBankTransaction{
Transactions: []ofxgo.Transaction{
ofxgo.Transaction{
TrnType: ofxgo.TrnTypeCredit,
DtPosted: *ofxgo.NewDateGMT(2017, 1, 20, 0, 0, 0, 0),
DtUser: ofxgo.NewDateGMT(2017, 1, 18, 0, 0, 0, 0),
DtAvail: ofxgo.NewDateGMT(2017, 1, 23, 0, 0, 0, 0),
TrnAmt: amount2,
FiTId: "993838",
Name: "DEPOSIT",
Memo: "CHECK 19980",
},
},
SubAcctFund: ofxgo.SubAcctTypeCash,
},
},
}
var availcash, marginbalance, shortbalance, balvalue ofxgo.Amount
availcash.SetFrac64(1673, 100)
marginbalance.SetFrac64(-8192, 10)
shortbalance.SetFrac64(0, 1)
balvalue.SetFrac64(25, 100)
invbalance := ofxgo.InvBalance{
AvailCash: availcash,
MarginBalance: marginbalance,
ShortBalance: shortbalance,
BalList: []ofxgo.Balance{
ofxgo.Balance{
Name: "Sweep Int Rate",
Desc: "Current interest rate for sweep account balances",
BalType: ofxgo.BalTypePercent,
Value: balvalue,
DtAsOf: ofxgo.NewDateGMT(2017, 4, 1, 0, 0, 0, 0),
},
},
}
var balamt, availbalamt, posunits1, posunitprice1, posmktval1, posunits2, posunitprice2, posmktval2, oounits1, oolimitprice1, oounits2, oolimitprice2 ofxgo.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)
statementResponse := ofxgo.InvStatementResponse{
TrnUID: "1a0117ad-692b-4c6a-a21b-020d37d34d49",
Status: ofxgo.Status{
Code: 0,
Severity: "INFO",
},
DtAsOf: *ofxgo.NewDateGMT(2017, 3, 31, 0, 0, 0, 0),
CurDef: "USD",
InvAcctFrom: ofxgo.InvAcct{
BrokerId: "invstrus.com",
AcctId: "91827364",
},
InvTranList: &invtranlist,
InvPosList: ofxgo.PositionList{
ofxgo.StockPosition{
InvPos: ofxgo.InvPosition{
SecId: ofxgo.SecurityId{
UniqueId: "78462F103",
UniqueIdType: "CUSIP",
},
HeldInAcct: ofxgo.SubAcctTypeCash,
PosType: ofxgo.PosTypeLong,
Units: posunits1,
UnitPrice: posunitprice1,
MktVal: posmktval1,
DtPriceAsOf: *ofxgo.NewDateGMT(2017, 3, 31, 16, 0, 0, 0),
Memo: "Price as of previous close",
},
},
ofxgo.OptPosition{
InvPos: ofxgo.InvPosition{
SecId: ofxgo.SecurityId{
UniqueId: "129887339",
UniqueIdType: "CUSIP",
},
HeldInAcct: ofxgo.SubAcctTypeCash,
PosType: ofxgo.PosTypeLong,
Units: posunits2,
UnitPrice: posunitprice2,
MktVal: posmktval2,
DtPriceAsOf: *ofxgo.NewDateGMT(2017, 3, 31, 16, 0, 0, 0),
},
},
},
InvBal: &invbalance,
InvOOList: ofxgo.OOList{
ofxgo.OOBuyMF{
OO: ofxgo.OO{
FiTId: "76464632",
SecId: ofxgo.SecurityId{
UniqueId: "922908645",
UniqueIdType: "CUSIP",
},
DtPlaced: *ofxgo.NewDateGMT(2017, 3, 10, 12, 44, 45, 0),
Units: oounits1,
SubAcct: ofxgo.SubAcctTypeCash,
Duration: ofxgo.DurationGoodTilCancel,
Restriction: ofxgo.RestrictionNone,
LimitPrice: oolimitprice1,
},
BuyType: ofxgo.BuyTypeBuy,
UnitType: ofxgo.UnitTypeShares,
},
ofxgo.OOBuyStock{
OO: ofxgo.OO{
FiTId: "999387423",
SecId: ofxgo.SecurityId{
UniqueId: "899422348",
UniqueIdType: "CUSIP",
},
DtPlaced: *ofxgo.NewDateGMT(2017, 3, 24, 3, 19, 0, 0),
Units: oounits2,
SubAcct: ofxgo.SubAcctTypeCash,
Duration: ofxgo.DurationGoodTilCancel,
Restriction: ofxgo.RestrictionAllOrNone,
LimitPrice: oolimitprice2,
},
BuyType: ofxgo.BuyTypeBuy,
},
},
}
expected.InvStmt = append(expected.InvStmt, &statementResponse)
var yield1, yield2, strikeprice, parvalue ofxgo.Amount
yield1.SetFrac64(192, 100)
yield2.SetFrac64(17, 1)
strikeprice.SetFrac64(79, 1)
parvalue.SetFrac64(10029, 100)
seclist := ofxgo.SecurityList{
Securities: []ofxgo.Security{
ofxgo.StockInfo{
SecInfo: ofxgo.SecInfo{
SecId: ofxgo.SecurityId{
UniqueId: "78462F103",
UniqueIdType: "CUSIP",
},
SecName: "S&P 500 ETF",
Ticker: "SPY",
FiId: "99184",
},
Yield: yield1,
AssetClass: ofxgo.AssetClassOther,
},
ofxgo.OptInfo{
SecInfo: ofxgo.SecInfo{
SecId: ofxgo.SecurityId{
UniqueId: "129887339",
UniqueIdType: "CUSIP",
},
SecName: "John's Fertilizer Puts",
Ticker: "FERTP",
FiId: "882919",
},
OptType: ofxgo.OptTypePut,
StrikePrice: strikeprice,
DtExpire: *ofxgo.NewDateGMT(2017, 9, 1, 0, 0, 0, 0),
ShPerCtrct: 100,
SecId: &ofxgo.SecurityId{
UniqueId: "983322180",
UniqueIdType: "CUSIP",
},
AssetClass: ofxgo.AssetClassLargeStock,
},
ofxgo.StockInfo{
SecInfo: ofxgo.SecInfo{
SecId: ofxgo.SecurityId{
UniqueId: "899422348",
UniqueIdType: "CUSIP",
},
SecName: "Whatchamacallit, Inc.",
Ticker: "WHAT",
FiId: "883897",
},
Yield: yield2,
AssetClass: ofxgo.AssetClassSmallStock,
},
ofxgo.MFInfo{
SecInfo: ofxgo.SecInfo{
SecId: ofxgo.SecurityId{
UniqueId: "922908645",
UniqueIdType: "CUSIP",
},
SecName: "Mid-Cap Index Fund Admiral Shares",
Ticker: "VIMAX",
},
},
ofxgo.DebtInfo{
SecInfo: ofxgo.SecInfo{
SecId: ofxgo.SecurityId{
UniqueId: "99182828",
UniqueIdType: "CUSIP",
},
SecName: "Someone's Class B Debt",
},
ParValue: parvalue,
DebtType: ofxgo.DebtTypeCoupon,
DtCoupon: ofxgo.NewDateGMT(2017, 9, 1, 0, 0, 0, 0),
CouponFreq: ofxgo.CouponFreqQuarterly,
},
ofxgo.OtherInfo{
SecInfo: ofxgo.SecInfo{
SecId: ofxgo.SecurityId{
UniqueId: "88181818",
UniqueIdType: "CUSIP",
},
SecName: "Foo Bar",
},
TypeDesc: "Don't know what this is",
},
},
}
expected.SecList = append(expected.SecList, &seclist)
response, err := ofxgo.ParseResponse(responseReader)
if err != nil {
t.Fatalf("Unexpected error unmarshalling response: %s\n", err)
}
checkResponsesEqual(t, &expected, 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 ofxgo.Response
expected.Version = "102"
expected.Signon.Status.Code = 0
expected.Signon.Status.Severity = "INFO"
expected.Signon.DtServer = *ofxgo.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 ofxgo.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 := ofxgo.InvTranList{
DtStart: *ofxgo.NewDateGMT(2016, 12, 6, 12, 0, 0, 0),
DtEnd: *ofxgo.NewDateGMT(2017, 4, 3, 12, 0, 0, 0),
InvTransactions: []ofxgo.InvTransaction{
ofxgo.SellOpt{
InvSell: ofxgo.InvSell{
InvTran: ofxgo.InvTran{
FiTId: "12341234-20161207-1",
DtTrade: *ofxgo.NewDateGMT(2016, 12, 7, 12, 0, 0, 0),
DtSettle: ofxgo.NewDateGMT(2016, 12, 8, 12, 0, 0, 0),
},
SecId: ofxgo.SecurityId{
UniqueId: "SPY161216C00226000",
UniqueIdType: "CUSIP",
},
Units: units1,
UnitPrice: unitprice1,
Commission: commission1,
Fees: fees1,
Total: total1,
SubAcctSec: ofxgo.SubAcctTypeCash,
SubAcctFund: ofxgo.SubAcctTypeCash,
},
OptSellType: ofxgo.OptSellTypeSellToOpen,
ShPerCtrct: 100,
},
ofxgo.ClosureOpt{
InvTran: ofxgo.InvTran{
FiTId: "12341234-20161215-1",
DtTrade: *ofxgo.NewDateGMT(2016, 12, 15, 12, 0, 0, 0),
DtSettle: ofxgo.NewDateGMT(2016, 12, 20, 12, 0, 0, 0),
},
SecId: ofxgo.SecurityId{
UniqueId: "78462F10",
UniqueIdType: "CUSIP",
},
OptAction: ofxgo.OptActionAssign,
Units: units2,
ShPerCtrct: 100,
SubAcctSec: ofxgo.SubAcctTypeCash,
},
ofxgo.ClosureOpt{
InvTran: ofxgo.InvTran{
FiTId: "12341234-20161215-2",
DtTrade: *ofxgo.NewDateGMT(2016, 12, 15, 12, 0, 0, 0),
DtSettle: ofxgo.NewDateGMT(2016, 12, 15, 12, 0, 0, 0),
},
SecId: ofxgo.SecurityId{
UniqueId: "SPY161216C00226000",
UniqueIdType: "CUSIP",
},
OptAction: ofxgo.OptActionAssign,
Units: units3,
ShPerCtrct: 100,
SubAcctSec: ofxgo.SubAcctTypeCash,
},
},
}
var availcash, marginbalance, shortbalance ofxgo.Amount
availcash.SetFrac64(0, 1)
marginbalance.SetFrac64(-0, 1)
shortbalance.SetFrac64(0, 1)
invbalance := ofxgo.InvBalance{
AvailCash: availcash,
MarginBalance: marginbalance,
ShortBalance: shortbalance,
}
var posunits1, posunitprice1, posmktval1, posunits2, posunitprice2, posmktval2 ofxgo.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)
statementResponse := ofxgo.InvStatementResponse{
TrnUID: "1283719872",
Status: ofxgo.Status{
Code: 0,
Severity: "INFO",
},
DtAsOf: *ofxgo.NewDateGMT(2017, 4, 3, 12, 0, 0, 0),
CurDef: "USD",
InvAcctFrom: ofxgo.InvAcct{
BrokerId: "www.exampletrader.com",
AcctId: "12341234",
},
InvTranList: &invtranlist,
InvPosList: ofxgo.PositionList{
ofxgo.StockPosition{
InvPos: ofxgo.InvPosition{
SecId: ofxgo.SecurityId{
UniqueId: "04956010",
UniqueIdType: "CUSIP",
},
HeldInAcct: ofxgo.SubAcctTypeCash,
PosType: ofxgo.PosTypeLong,
Units: posunits1,
UnitPrice: posunitprice1,
MktVal: posmktval1,
DtPriceAsOf: *ofxgo.NewDateGMT(2017, 4, 3, 12, 0, 0, 0),
},
},
ofxgo.StockPosition{
InvPos: ofxgo.InvPosition{
SecId: ofxgo.SecurityId{
UniqueId: "36960410",
UniqueIdType: "CUSIP",
},
HeldInAcct: ofxgo.SubAcctTypeCash,
PosType: ofxgo.PosTypeLong,
Units: posunits2,
UnitPrice: posunitprice2,
MktVal: posmktval2,
DtPriceAsOf: *ofxgo.NewDateGMT(2017, 4, 3, 12, 0, 0, 0),
},
},
},
InvBal: &invbalance,
}
expected.InvStmt = append(expected.InvStmt, &statementResponse)
var strikeprice ofxgo.Amount
strikeprice.SetFrac64(226, 1)
seclist := ofxgo.SecurityList{
Securities: []ofxgo.Security{
ofxgo.StockInfo{
SecInfo: ofxgo.SecInfo{
SecId: ofxgo.SecurityId{
UniqueId: "78462F10",
UniqueIdType: "CUSIP",
},
SecName: "SPDR S&P 500 ETF TRUST",
Ticker: "SPY",
},
},
ofxgo.OptInfo{
SecInfo: ofxgo.SecInfo{
SecId: ofxgo.SecurityId{
UniqueId: "SPY161216C00226000",
UniqueIdType: "CUSIP",
},
SecName: "SPY Dec 16 2016 226.00 Call",
Ticker: "SPY 161216C00226000",
},
OptType: ofxgo.OptTypeCall,
StrikePrice: strikeprice,
DtExpire: *ofxgo.NewDateGMT(2016, 12, 16, 12, 0, 0, 0),
ShPerCtrct: 100,
},
},
}
expected.SecList = append(expected.SecList, &seclist)
response, err := ofxgo.ParseResponse(responseReader)
if err != nil {
t.Fatalf("Unexpected error unmarshalling response: %s\n", err)
}
checkResponsesEqual(t, &expected, response)
}