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) }