[
  {
    "path": ".github/workflows/test.yml",
    "content": "name: ofxgo CI Test\n\non: [push, pull_request]\n\njobs:\n  test:\n    strategy:\n      matrix:\n        go-version: [1.19.x, 1.22.x, 1.24.x]\n        os: [ubuntu-latest, macos-latest, windows-latest]\n        include:\n          - os: ubuntu-latest\n            go-version: 1.14.x\n          - os: windows-latest\n            go-version: 1.14.x\n    runs-on: ${{ matrix.os }}\n    steps:\n    - name: Install Go\n      uses: actions/setup-go@v5\n      with:\n        go-version: ${{ matrix.go-version }}\n    - name: Checkout code\n      uses: actions/checkout@v2\n    - name: Test\n      run: go test -v -covermode=count -coverprofile=\"profile.cov\" ./...\n    - name: Send Coverage\n      uses: shogo82148/actions-goveralls@v1\n      with:\n        path-to-profile: \"profile.cov\"\n        flag-name: ${{ matrix.os }}-go-${{ matrix.go-version }}\n        parallel: true\n  # notifies that all test jobs are finished.\n  finish:\n    needs: test\n    runs-on: ubuntu-latest\n    steps:\n      - uses: shogo82148/actions-goveralls@v1\n        with:\n          parallel-finished: true\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n                            NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n                     END OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "README.md",
    "content": "# OFXGo\n\n[![Go Report Card](https://goreportcard.com/badge/github.com/aclindsa/ofxgo)](https://goreportcard.com/report/github.com/aclindsa/ofxgo)\n[![Build Status](https://github.com/aclindsa/ofxgo/workflows/ofxgo%20CI%20Test/badge.svg?branch=master)](https://github.com/aclindsa/ofxgo/actions?query=workflow%3A%22ofxgo+CI+Test%22+branch%3Amaster)\n[![Coverage Status](https://coveralls.io/repos/github/aclindsa/ofxgo/badge.svg?branch=master)](https://coveralls.io/github/aclindsa/ofxgo?branch=master)\n[![PkgGoDev](https://pkg.go.dev/badge/github.com/aclindsa?ofxgo)](https://pkg.go.dev/github.com/aclindsa/ofxgo)\n\n**OFXGo** is a library for querying OFX servers and/or parsing the responses. It\nalso provides an example command-line client to demonstrate the use of the\nlibrary.\n\n## Goals\n\nThe main purpose of this project is to provide a library to make it easier to\nquery financial information with OFX from the comfort of Golang, without having\nto marshal/unmarshal to SGML or XML. The library does _not_ intend to abstract\naway all of the details of the OFX specification, which would be difficult to do\nwell. Instead, it exposes the OFX SGML/XML hierarchy as structs which mostly\nresemble it. Its primary goal is to enable the creation of other personal\nfinance software in Go (as it was created to allow me to fetch OFX transactions\nfor my own project, [MoneyGo](https://github.com/aclindsa/moneygo)).\n\nBecause the OFX specification is rather... 'comprehensive,' it can be difficult\nfor those unfamiliar with it to figure out where to start. To that end, I have\ncreated a sample command-line client which uses the library to do simple tasks\n(currently it does little more than list accounts and query for balances and\ntransactions). My hope is that by studying its code, new users will be able to\nfigure out how to use the library much faster than staring at the OFX\nspecification (or this library's [API\ndocumentation](https://pkg.go.dev/github.com/aclindsa/ofxgo)). The command-line client\nalso serves as an easy way for me to test/debug the library with actual\nfinancial institutions, which frequently have 'quirks' in their implementations.\nThe command-line client can be found in the [cmd/ofx\ndirectory](https://github.com/aclindsa/ofxgo/tree/master/cmd/ofx) of this\nrepository.\n\n## Library documentation\n\nDocumentation can be found with the `go doc` tool, or at\nhttps://pkg.go.dev/github.com/aclindsa/ofxgo\n\n## Example Usage\n\nThe following code snippet demonstrates how to use OFXGo to query and parse\nOFX code from a checking account, printing the balance and returned transactions:\n\n```go\nclient := ofxgo.BasicClient{} // Accept the default Client settings\n\n// These values are specific to your bank\nvar query ofxgo.Request\nquery.URL = \"https://secu.example.com/ofx\"\nquery.Signon.Org = ofxgo.String(\"SECU\")\nquery.Signon.Fid = ofxgo.String(\"1234\")\n\n// Set your username/password\nquery.Signon.UserID = ofxgo.String(\"username\")\nquery.Signon.UserPass = ofxgo.String(\"hunter2\")\n\nuid, _ := ofxgo.RandomUID() // Handle error in real code\nquery.Bank = append(query.Bank, &ofxgo.StatementRequest{\n\tTrnUID: *uid,\n\tBankAcctFrom: ofxgo.BankAcct{\n\t\tBankID:   ofxgo.String(\"123456789\"),   // Possibly your routing number\n\t\tAcctID:   ofxgo.String(\"00011122233\"), // Possibly your account number\n\t\tAcctType: ofxgo.AcctTypeChecking,\n\t},\n\tInclude: true, // Include transactions (instead of only balance information)\n})\n\nresponse, _ := client.Request(&query) // Handle error in real code\n\n// Was there an OFX error while processing our request?\nif response.Signon.Status.Code != 0 {\n\tmeaning, _ := response.Signon.Status.CodeMeaning()\n\tfmt.Printf(\"Nonzero signon status (%d: %s) with message: %s\\n\", response.Signon.Status.Code, meaning, response.Signon.Status.Message)\n\tos.Exit(1)\n}\n\nif len(response.Bank) < 1 {\n\tfmt.Println(\"No banking messages received\")\n\tos.Exit(1)\n}\n\nif stmt, ok := response.Bank[0].(*ofxgo.StatementResponse); ok {\n\tfmt.Printf(\"Balance: %s %s (as of %s)\\n\", stmt.BalAmt, stmt.CurDef, stmt.DtAsOf)\n\tfmt.Println(\"Transactions:\")\n\tfor _, tran := range stmt.BankTranList.Transactions {\n\t\tcurrency := stmt.CurDef\n\t\tif ok, _ := tran.Currency.Valid(); ok {\n\t\t\tcurrency = tran.Currency.CurSym\n\t\t}\n\t\tfmt.Printf(\"%s %-15s %-11s %s%s%s\\n\", tran.DtPosted, tran.TrnAmt.String()+\" \"+currency.String(), tran.TrnType, tran.Name, tran.Payee.Name, tran.Memo)\n\t}\n}\n```\n\nSimilarly, if you have an OFX file available locally, you can parse it directly:\n\n```go\nfunc main() {\n\tf, err := os.Open(\"./transactions.qfx\")\n\tif err != nil {\n\t\tfmt.Printf(\"can't open file: %v\\n\", err)\n\t\treturn\n\t}\n\tdefer f.Close()\n\n\tresp, err := ofxgo.ParseResponse(f)\n\tif err != nil {\n\t\tfmt.Printf(\"can't parse response: %v\\n\", err)\n\t\treturn\n\t}\n\n\t// do something with resp (*ofxgo.Response)\n}\n```\n\n## Requirements\n\nOFXGo requires go >= 1.12\n\n## Using the command-line client\n\nTo install the command-line client and test it out, you may do the following:\n\n$ go get -v github.com/aclindsa/ofxgo/cmd/ofx && go install -v github.com/aclindsa/ofxgo/cmd/ofx\n\nOnce installed (at ~/go/bin/ofx by default, if you haven't set $GOPATH), the\ncommand's usage should help you to use it (`./ofx --help` for a listing of the\navailable subcommands and their purposes, `./ofx subcommand --help` for\nindividual subcommand usage).\n"
  },
  {
    "path": "bank.go",
    "content": "package ofxgo\n\nimport (\n\t\"errors\"\n\t\"github.com/aclindsa/xml\"\n)\n\n// StatementRequest represents a request for a bank statement. It is used to\n// request balances and/or transactions for checking, savings, money market,\n// and line of credit accounts. See CCStatementRequest for the analog for\n// credit card accounts.\ntype StatementRequest struct {\n\tXMLName   xml.Name `xml:\"STMTTRNRQ\"`\n\tTrnUID    UID      `xml:\"TRNUID\"`\n\tCltCookie String   `xml:\"CLTCOOKIE,omitempty\"`\n\tTAN       String   `xml:\"TAN,omitempty\"` // Transaction authorization number\n\t// TODO `xml:\"OFXEXTENSION,omitempty\"`\n\tBankAcctFrom   BankAcct `xml:\"STMTRQ>BANKACCTFROM\"`\n\tDtStart        *Date    `xml:\"STMTRQ>INCTRAN>DTSTART,omitempty\"`\n\tDtEnd          *Date    `xml:\"STMTRQ>INCTRAN>DTEND,omitempty\"`\n\tInclude        Boolean  `xml:\"STMTRQ>INCTRAN>INCLUDE\"`          // Include transactions (instead of just balance)\n\tIncludePending Boolean  `xml:\"STMTRQ>INCLUDEPENDING,omitempty\"` // Include pending transactions\n\tIncTranImg     Boolean  `xml:\"STMTRQ>INCTRANIMG,omitempty\"`     // Include transaction images\n}\n\n// Name returns the name of the top-level transaction XML/SGML element\nfunc (r *StatementRequest) Name() string {\n\treturn \"STMTTRNRQ\"\n}\n\n// Valid returns (true, nil) if this struct would be valid OFX if marshalled\n// into XML/SGML\nfunc (r *StatementRequest) Valid(version ofxVersion) (bool, error) {\n\tif ok, err := r.TrnUID.Valid(); !ok {\n\t\treturn false, err\n\t}\n\tif r.IncludePending && version < OfxVersion220 {\n\t\treturn false, errors.New(\"StatementRequest.IncludePending invalid for OFX < 2.2\")\n\t}\n\tif r.IncTranImg && version < OfxVersion210 {\n\t\treturn false, errors.New(\"StatementRequest.IncTranImg invalid for OFX < 2.1\")\n\t}\n\treturn r.BankAcctFrom.Valid()\n}\n\n// Type returns which message set this message belongs to (which Request\n// element of type []Message it should appended to)\nfunc (r *StatementRequest) Type() messageType {\n\treturn BankRq\n}\n\n// Payee specifies a complete billing address for a payee\ntype Payee struct {\n\tXMLName    xml.Name `xml:\"PAYEE\"`\n\tName       String   `xml:\"NAME\"`\n\tAddr1      String   `xml:\"ADDR1\"`\n\tAddr2      String   `xml:\"ADDR2,omitempty\"`\n\tAddr3      String   `xml:\"ADDR3,omitempty\"`\n\tCity       String   `xml:\"CITY\"`\n\tState      String   `xml:\"STATE\"`\n\tPostalCode String   `xml:\"POSTALCODE\"`\n\tCountry    String   `xml:\"COUNTRY,omitempty\"`\n\tPhone      String   `xml:\"PHONE\"`\n}\n\n// Valid returns (true, nil) if this struct is valid OFX\nfunc (p Payee) Valid() (bool, error) {\n\tif len(p.Name) == 0 {\n\t\treturn false, errors.New(\"Payee.Name empty\")\n\t} else if len(p.Addr1) == 0 {\n\t\treturn false, errors.New(\"Payee.Addr1 empty\")\n\t} else if len(p.City) == 0 {\n\t\treturn false, errors.New(\"Payee.City empty\")\n\t} else if len(p.State) == 0 {\n\t\treturn false, errors.New(\"Payee.State empty\")\n\t} else if len(p.PostalCode) == 0 {\n\t\treturn false, errors.New(\"Payee.PostalCode empty\")\n\t} else if len(p.Country) != 0 && len(p.Country) != 3 {\n\t\treturn false, errors.New(\"Payee.Country invalid length\")\n\t} else if len(p.Phone) == 0 {\n\t\treturn false, errors.New(\"Payee.Phone empty\")\n\t}\n\treturn true, nil\n}\n\n// ImageData represents the metadata surrounding a check or other image file,\n// including how to retrieve the image\ntype ImageData struct {\n\tXMLName      xml.Name     `xml:\"IMAGEDATA\"`\n\tImageType    imageType    `xml:\"IMAGETYPE\"`    // One of STATEMENT, TRANSACTION, TAX\n\tImageRef     String       `xml:\"IMAGEREF\"`     // URL or identifier, depending on IMAGEREFTYPE\n\tImageRefType imageRefType `xml:\"IMAGEREFTYPE\"` // One of OPAQUE, URL, FORMURL (see spec for more details on how to access images of each of these types)\n\t// Only one of the next two should be valid at any given time\n\tImageDelay   Int      `xml:\"IMAGEDELAY,omitempty\"`   // Number of calendar days from DTSERVER (for statement images) or DTPOSTED (for transaction image) the image will become available\n\tDtImageAvail *Date    `xml:\"DTIMAGEAVAIL,omitempty\"` // Date image will become available\n\tImageTTL     Int      `xml:\"IMAGETTL,omitempty\"`     // Number of days after image becomes available that it will remain available\n\tCheckSup     checkSup `xml:\"CHECKSUP,omitempty\"`     // What is contained in check images. One of FRONTONLY, BACKONLY, FRONTANDBACK\n}\n\n// Transaction represents a single banking transaction. At a minimum, it\n// identifies the type of transaction (TrnType) and the date it was posted\n// (DtPosted). Ideally it also provides metadata to help the user recognize\n// this transaction (i.e. CheckNum, Name or Payee, Memo, etc.)\ntype Transaction struct {\n\tXMLName       xml.Name      `xml:\"STMTTRN\"`\n\tTrnType       trnType       `xml:\"TRNTYPE\"` // One of CREDIT, DEBIT, INT (interest earned or paid. Note: Depends on signage of amount), DIV, FEE, SRVCHG (service charge), DEP (deposit), ATM (Note: Depends on signage of amount), POS (Note: Depends on signage of amount), XFER, CHECK, PAYMENT, CASH, DIRECTDEP, DIRECTDEBIT, REPEATPMT, OTHER\n\tDtPosted      Date          `xml:\"DTPOSTED\"`\n\tDtUser        *Date         `xml:\"DTUSER,omitempty\"`\n\tDtAvail       *Date         `xml:\"DTAVAIL,omitempty\"`\n\tTrnAmt        Amount        `xml:\"TRNAMT\"`\n\tFiTID         String        `xml:\"FITID\"`                   // Client uses FITID to detect whether it has previously downloaded the transaction\n\tCorrectFiTID  String        `xml:\"CORRECTFITID,omitempty\"`  // Transaction ID that this transaction corrects, if present\n\tCorrectAction correctAction `xml:\"CORRECTACTION,omitempty\"` // One of DELETE, REPLACE\n\tSrvrTID       String        `xml:\"SRVRTID,omitempty\"`\n\tCheckNum      String        `xml:\"CHECKNUM,omitempty\"`\n\tRefNum        String        `xml:\"REFNUM,omitempty\"`\n\tSIC           Int           `xml:\"SIC,omitempty\"` // Standard Industrial Code\n\tPayeeID       String        `xml:\"PAYEEID,omitempty\"`\n\t// Note: Servers should provide NAME or PAYEE, but not both\n\tName       String      `xml:\"NAME,omitempty\"`\n\tPayee      *Payee      `xml:\"PAYEE,omitempty\"`\n\tExtdName   String      `xml:\"EXTDNAME,omitempty\"`   // Extended name of payee or transaction description\n\tBankAcctTo *BankAcct   `xml:\"BANKACCTTO,omitempty\"` // If the transfer was to a bank account we have the account information for\n\tCCAcctTo   *CCAcct     `xml:\"CCACCTTO,omitempty\"`   // If the transfer was to a credit card account we have the account information for\n\tMemo       String      `xml:\"MEMO,omitempty\"`       // Extra information (not in NAME)\n\tImageData  []ImageData `xml:\"IMAGEDATA,omitempty\"`\n\n\t// Only one of Currency and OrigCurrency can ever be Valid() for the same transaction\n\tCurrency      *Currency     `xml:\"CURRENCY,omitempty\"`      // Represents the currency of TrnAmt (instead of CURDEF in STMTRS) if Valid\n\tOrigCurrency  *Currency     `xml:\"ORIGCURRENCY,omitempty\"`  // Represents the currency TrnAmt was converted to STMTRS' CURDEF from if Valid\n\tInv401kSource inv401kSource `xml:\"INV401KSOURCE,omitempty\"` // One of PRETAX, AFTERTAX, MATCH, PROFITSHARING, ROLLOVER, OTHERVEST, OTHERNONVEST (Default if not present is OTHERNONVEST. The following cash source types are subject to vesting: MATCH, PROFITSHARING, and OTHERVEST.)\n}\n\n// Valid returns (true, nil) if this struct is valid OFX\nfunc (t Transaction) Valid(version ofxVersion) (bool, error) {\n\tvar emptyDate Date\n\tif !t.TrnType.Valid() || t.TrnType == TrnTypeHold {\n\t\treturn false, errors.New(\"Transaction.TrnType invalid\")\n\t} else if t.DtPosted.Equal(emptyDate) {\n\t\treturn false, errors.New(\"Transaction.DtPosted not filled\")\n\t} else if len(t.FiTID) == 0 {\n\t\treturn false, errors.New(\"Transaction.FiTID empty\")\n\t} else if len(t.CorrectFiTID) > 0 && t.CorrectAction.Valid() {\n\t\treturn false, errors.New(\"Transaction.CorrectFiTID nonempty but CorrectAction invalid\")\n\t} else if len(t.Name) > 0 && t.Payee != nil {\n\t\treturn false, errors.New(\"Only one of Transaction.Name and Payee may be specified\")\n\t}\n\tif t.Payee != nil {\n\t\tif ok, err := t.Payee.Valid(); !ok {\n\t\t\treturn false, err\n\t\t}\n\t}\n\tif t.BankAcctTo != nil && t.CCAcctTo != nil {\n\t\treturn false, errors.New(\"Only one of Transaction.BankAcctTo and CCAcctTo may be specified\")\n\t} else if t.BankAcctTo != nil {\n\t\tif ok, err := t.BankAcctTo.Valid(); !ok {\n\t\t\treturn false, err\n\t\t}\n\t} else if t.CCAcctTo != nil {\n\t\tif ok, err := t.CCAcctTo.Valid(); !ok {\n\t\t\treturn false, err\n\t\t}\n\t}\n\tif version < OfxVersion220 && len(t.ImageData) > 0 {\n\t\treturn false, errors.New(\"Transaction.ImageData only supportd for OFX > 220\")\n\t} else if len(t.ImageData) > 2 {\n\t\treturn false, errors.New(\"Only 2 of ImageData allowed in Transaction\")\n\t}\n\tvar ok1, ok2 bool\n\tif t.Currency != nil {\n\t\tok1, _ = t.Currency.Valid()\n\t}\n\tif t.OrigCurrency != nil {\n\t\tok2, _ = t.OrigCurrency.Valid()\n\t}\n\tif ok1 && ok2 {\n\t\treturn false, errors.New(\"Currency and OrigCurrency both supplied for Pending Transaction, only one allowed\")\n\t}\n\treturn true, nil\n}\n\n// TransactionList represents a list of bank transactions, and also includes\n// the date range its transactions cover.\ntype TransactionList struct {\n\tXMLName      xml.Name      `xml:\"BANKTRANLIST\"`\n\tDtStart      Date          `xml:\"DTSTART\"` // Start date for transaction data\n\tDtEnd        Date          `xml:\"DTEND\"`   // Value that client should send in next <DTSTART> request to ensure that it does not miss any transactions\n\tTransactions []Transaction `xml:\"STMTTRN,omitempty\"`\n}\n\n// Valid returns (true, nil) if this struct is valid OFX\nfunc (l TransactionList) Valid(version ofxVersion) (bool, error) {\n\tvar emptyDate Date\n\tif l.DtStart.Equal(emptyDate) {\n\t\treturn false, errors.New(\"TransactionList.DtStart not filled\")\n\t} else if l.DtEnd.Equal(emptyDate) {\n\t\treturn false, errors.New(\"TransactionList.DtEnd not filled\")\n\t}\n\tfor _, t := range l.Transactions {\n\t\tif ok, err := t.Valid(version); !ok {\n\t\t\treturn false, err\n\t\t}\n\t}\n\n\treturn true, nil\n}\n\n// PendingTransaction represents a single pending transaction. It is similar to\n// Transaction, but is not finalized (and may never be). For instance, it lacks\n// FiTID and DtPosted fields.\ntype PendingTransaction struct {\n\tXMLName   xml.Name    `xml:\"STMTTRNP\"`\n\tTrnType   trnType     `xml:\"TRNTYPE\"` // One of CREDIT, DEBIT, INT (interest earned or paid. Note: Depends on signage of amount), DIV, FEE, SRVCHG (service charge), DEP (deposit), ATM (Note: Depends on signage of amount), POS (Note: Depends on signage of amount), XFER, CHECK, PAYMENT, CASH, DIRECTDEP, DIRECTDEBIT, REPEATPMT, HOLD, OTHER\n\tDtTran    Date        `xml:\"DTTRAN\"`\n\tDtExpire  *Date       `xml:\"DTEXPIRE,omitempty\"` // only valid for TrnType==HOLD, the date the hold will expire\n\tTrnAmt    Amount      `xml:\"TRNAMT\"`\n\tRefNum    String      `xml:\"REFNUM,omitempty\"`\n\tName      String      `xml:\"NAME,omitempty\"`\n\tExtdName  String      `xml:\"EXTDNAME,omitempty\"` // Extended name of payee or transaction description\n\tMemo      String      `xml:\"MEMO,omitempty\"`     // Extra information (not in NAME)\n\tImageData []ImageData `xml:\"IMAGEDATA,omitempty\"`\n\n\t// Only one of Currency and OrigCurrency can ever be Valid() for the same transaction\n\tCurrency     Currency `xml:\"CURRENCY,omitempty\"`     // Represents the currency of TrnAmt (instead of CURDEF in STMTRS) if Valid\n\tOrigCurrency Currency `xml:\"ORIGCURRENCY,omitempty\"` // Represents the currency TrnAmt was converted to STMTRS' CURDEF from if Valid\n}\n\n// Valid returns (true, nil) if this struct is valid OFX\nfunc (t PendingTransaction) Valid() (bool, error) {\n\tvar emptyDate Date\n\tif !t.TrnType.Valid() {\n\t\treturn false, errors.New(\"PendingTransaction.TrnType invalid\")\n\t} else if t.DtTran.Equal(emptyDate) {\n\t\treturn false, errors.New(\"PendingTransaction.DtTran not filled\")\n\t} else if len(t.Name) == 0 {\n\t\treturn false, errors.New(\"PendingTransaction.Name empty\")\n\t}\n\tok1, _ := t.Currency.Valid()\n\tok2, _ := t.OrigCurrency.Valid()\n\tif ok1 && ok2 {\n\t\treturn false, errors.New(\"Currency and OrigCurrency both supplied for Pending Transaction, only one allowed\")\n\t}\n\treturn true, nil\n}\n\n// PendingTransactionList represents a list of pending transactions, along with\n// the date they were generated\ntype PendingTransactionList struct {\n\tXMLName      xml.Name             `xml:\"BANKTRANLISTP\"`\n\tDtAsOf       Date                 `xml:\"DTASOF\"` // Date and time this set of pending transactions was generated\n\tTransactions []PendingTransaction `xml:\"STMTTRNP,omitempty\"`\n}\n\n// Valid returns (true, nil) if this struct is valid OFX\nfunc (l PendingTransactionList) Valid() (bool, error) {\n\tvar emptyDate Date\n\tif l.DtAsOf.Equal(emptyDate) {\n\t\treturn false, errors.New(\"PendingTransactionList.DtAsOf not filled\")\n\t}\n\tfor _, t := range l.Transactions {\n\t\tif ok, err := t.Valid(); !ok {\n\t\t\treturn false, err\n\t\t}\n\t}\n\n\treturn true, nil\n}\n\n// Balance represents a generic (free-form) balance defined by an FI.\ntype Balance struct {\n\tXMLName xml.Name `xml:\"BAL\"`\n\tName    String   `xml:\"NAME\"`\n\tDesc    String   `xml:\"DESC\"`\n\n\t// Balance type:\n\t// DOLLAR = dollar (value formatted DDDD.cc)\n\t// PERCENT = percentage (value formatted XXXX.YYYY)\n\t// NUMBER = number (value formatted as is)\n\tBalType balType `xml:\"BALTYPE\"`\n\n\tValue    Amount    `xml:\"VALUE\"`\n\tDtAsOf   *Date     `xml:\"DTASOF,omitempty\"`\n\tCurrency *Currency `xml:\"CURRENCY,omitempty\"` // if BALTYPE is DOLLAR\n}\n\n// Valid returns (true, nil) if this struct is valid OFX\nfunc (b Balance) Valid() (bool, error) {\n\tif len(b.Name) == 0 || len(b.Desc) == 0 {\n\t\treturn false, errors.New(\"Balance Name and Desc not supplied\")\n\t}\n\tif !b.BalType.Valid() {\n\t\treturn false, errors.New(\"Balance BALTYPE not specified\")\n\t}\n\treturn true, nil\n}\n\n// StatementResponse represents a bank account statement, including its\n// balances and possibly transactions. It is a response to StatementRequest, or\n// sometimes provided as part of an OFX file downloaded manually from an FI.\ntype StatementResponse struct {\n\tXMLName   xml.Name `xml:\"STMTTRNRS\"`\n\tTrnUID    UID      `xml:\"TRNUID\"`\n\tStatus    Status   `xml:\"STATUS\"`\n\tCltCookie String   `xml:\"CLTCOOKIE,omitempty\"`\n\t// TODO `xml:\"OFXEXTENSION,omitempty\"`\n\tCurDef        CurrSymbol              `xml:\"STMTRS>CURDEF\"`\n\tBankAcctFrom  BankAcct                `xml:\"STMTRS>BANKACCTFROM\"`\n\tBankTranList  *TransactionList        `xml:\"STMTRS>BANKTRANLIST,omitempty\"`\n\tBankTranListP *PendingTransactionList `xml:\"STMTRS>BANKTRANLISTP,omitempty\"`\n\tBalAmt        Amount                  `xml:\"STMTRS>LEDGERBAL>BALAMT\"`\n\tDtAsOf        Date                    `xml:\"STMTRS>LEDGERBAL>DTASOF\"`\n\tAvailBalAmt   *Amount                 `xml:\"STMTRS>AVAILBAL>BALAMT,omitempty\"`\n\tAvailDtAsOf   *Date                   `xml:\"STMTRS>AVAILBAL>DTASOF,omitempty\"`\n\tCashAdvBalAmt *Amount                 `xml:\"STMTRS>CASHADVBALAMT,omitempty\"` // Only for CREDITLINE accounts, available balance for cash advances\n\tIntRate       *Amount                 `xml:\"STMTRS>INTRATE,omitempty\"`       // Current interest rate\n\tBalList       []Balance               `xml:\"STMTRS>BALLIST>BAL,omitempty\"`\n\tMktgInfo      String                  `xml:\"STMTRS>MKTGINFO,omitempty\"` // Marketing information\n}\n\n// Name returns the name of the top-level transaction XML/SGML element\nfunc (sr *StatementResponse) Name() string {\n\treturn \"STMTTRNRS\"\n}\n\n// Valid returns (true, nil) if this struct was valid OFX when unmarshalled\nfunc (sr *StatementResponse) Valid(version ofxVersion) (bool, error) {\n\tvar emptyDate Date\n\tif ok, err := sr.TrnUID.Valid(); !ok {\n\t\treturn false, err\n\t} else if ok, err := sr.Status.Valid(); !ok {\n\t\treturn false, err\n\t} else if ok, err := sr.CurDef.Valid(); !ok {\n\t\treturn false, err\n\t} else if ok, err := sr.BankAcctFrom.Valid(); !ok {\n\t\treturn false, err\n\t} else if sr.DtAsOf.Equal(emptyDate) {\n\t\treturn false, errors.New(\"StatementResponse.DtAsOf not filled\")\n\t} else if (sr.AvailBalAmt == nil) != (sr.AvailDtAsOf == nil) {\n\t\treturn false, errors.New(\"StatementResponse.Avail* must both either be present or absent\")\n\t}\n\tif sr.BankTranList != nil {\n\t\tif ok, err := sr.BankTranList.Valid(version); !ok {\n\t\t\treturn false, err\n\t\t}\n\t}\n\tif sr.BankTranListP != nil {\n\t\tif version < OfxVersion220 {\n\t\t\treturn false, errors.New(\"StatementResponse.BankTranListP invalid for OFX < 2.2\")\n\t\t}\n\t\tif ok, err := sr.BankTranListP.Valid(); !ok {\n\t\t\treturn false, err\n\t\t}\n\t}\n\tfor _, bal := range sr.BalList {\n\t\tif ok, err := bal.Valid(); !ok {\n\t\t\treturn false, err\n\t\t}\n\t}\n\treturn true, nil\n}\n\n// Type returns which message set this message belongs to (which Response\n// element of type []Message it belongs to)\nfunc (sr *StatementResponse) Type() messageType {\n\treturn BankRs\n}\n"
  },
  {
    "path": "bank_test.go",
    "content": "package ofxgo\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestMarshalBankStatementRequest(t *testing.T) {\n\tvar expectedString string = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<?OFX OFXHEADER=\"200\" VERSION=\"203\" SECURITY=\"NONE\" OLDFILEUID=\"NONE\" NEWFILEUID=\"NONE\"?>\n<OFX>\n\t<SIGNONMSGSRQV1>\n\t\t<SONRQ>\n\t\t\t<DTCLIENT>20060115112300.000[-5:EST]</DTCLIENT>\n\t\t\t<USERID>myusername</USERID>\n\t\t\t<USERPASS>Pa$$word</USERPASS>\n\t\t\t<LANGUAGE>ENG</LANGUAGE>\n\t\t\t<FI>\n\t\t\t\t<ORG>BNK</ORG>\n\t\t\t\t<FID>1987</FID>\n\t\t\t</FI>\n\t\t\t<APPID>OFXGO</APPID>\n\t\t\t<APPVER>0001</APPVER>\n\t\t</SONRQ>\n\t</SIGNONMSGSRQV1>\n\t<BANKMSGSRQV1>\n\t\t<STMTTRNRQ>\n\t\t\t<TRNUID>123</TRNUID>\n\t\t\t<STMTRQ>\n\t\t\t\t<BANKACCTFROM>\n\t\t\t\t\t<BANKID>318398732</BANKID>\n\t\t\t\t\t<ACCTID>78346129</ACCTID>\n\t\t\t\t\t<ACCTTYPE>CHECKING</ACCTTYPE>\n\t\t\t\t</BANKACCTFROM>\n\t\t\t\t<INCTRAN>\n\t\t\t\t\t<INCLUDE>Y</INCLUDE>\n\t\t\t\t</INCTRAN>\n\t\t\t</STMTRQ>\n\t\t</STMTTRNRQ>\n\t</BANKMSGSRQV1>\n</OFX>`\n\n\tvar client = BasicClient{\n\t\tAppID:       \"OFXGO\",\n\t\tAppVer:      \"0001\",\n\t\tSpecVersion: OfxVersion203,\n\t}\n\n\tvar request Request\n\trequest.Signon.UserID = \"myusername\"\n\trequest.Signon.UserPass = \"Pa$$word\"\n\trequest.Signon.Org = \"BNK\"\n\trequest.Signon.Fid = \"1987\"\n\n\tstatementRequest := StatementRequest{\n\t\tTrnUID: \"123\",\n\t\tBankAcctFrom: BankAcct{\n\t\t\tBankID:   \"318398732\",\n\t\t\tAcctID:   \"78346129\",\n\t\t\tAcctType: AcctTypeChecking,\n\t\t},\n\t\tInclude: true,\n\t}\n\trequest.Bank = append(request.Bank, &statementRequest)\n\n\trequest.SetClientFields(&client)\n\t// Overwrite the DtClient value set by SetClientFields to time.Now()\n\tEST := time.FixedZone(\"EST\", -5*60*60)\n\trequest.Signon.DtClient = *NewDate(2006, 1, 15, 11, 23, 0, 0, EST)\n\n\tmarshalCheckRequest(t, &request, expectedString)\n}\n\nfunc TestMarshalBankStatementRequest103(t *testing.T) {\n\tvar expectedString string = `OFXHEADER:100\nDATA:OFXSGML\nVERSION:103\nSECURITY:NONE\nENCODING:USASCII\nCHARSET:1252\nCOMPRESSION:NONE\nOLDFILEUID:NONE\nNEWFILEUID:NONE\n\n<OFX>\n\t<SIGNONMSGSRQV1>\n\t\t<SONRQ>\n\t\t\t<DTCLIENT>20060115112300.000[-5:EST]\n\t\t\t<USERID>myusername\n\t\t\t<USERPASS>Pa$$word\n\t\t\t<LANGUAGE>ENG\n\t\t\t<FI>\n\t\t\t\t<ORG>BNK\n\t\t\t\t<FID>1987\n\t\t\t</FI>\n\t\t\t<APPID>OFXGO\n\t\t\t<APPVER>0001\n\t\t</SONRQ>\n\t</SIGNONMSGSRQV1>\n\t<BANKMSGSRQV1>\n\t\t<STMTTRNRQ>\n\t\t\t<TRNUID>123\n\t\t\t<STMTRQ>\n\t\t\t\t<BANKACCTFROM>\n\t\t\t\t\t<BANKID>318398732\n\t\t\t\t\t<ACCTID>78346129\n\t\t\t\t\t<ACCTTYPE>CHECKING\n\t\t\t\t</BANKACCTFROM>\n\t\t\t\t<INCTRAN>\n\t\t\t\t\t<INCLUDE>Y\n\t\t\t\t</INCTRAN>\n\t\t\t</STMTRQ>\n\t\t</STMTTRNRQ>\n\t</BANKMSGSRQV1>\n</OFX>`\n\n\tvar client = BasicClient{\n\t\tAppID:       \"OFXGO\",\n\t\tAppVer:      \"0001\",\n\t\tSpecVersion: OfxVersion103,\n\t}\n\n\tvar request Request\n\trequest.Signon.UserID = \"myusername\"\n\trequest.Signon.UserPass = \"Pa$$word\"\n\trequest.Signon.Org = \"BNK\"\n\trequest.Signon.Fid = \"1987\"\n\n\tstatementRequest := StatementRequest{\n\t\tTrnUID: \"123\",\n\t\tBankAcctFrom: BankAcct{\n\t\t\tBankID:   \"318398732\",\n\t\t\tAcctID:   \"78346129\",\n\t\t\tAcctType: AcctTypeChecking,\n\t\t},\n\t\tInclude: true,\n\t}\n\trequest.Bank = append(request.Bank, &statementRequest)\n\n\trequest.SetClientFields(&client)\n\t// Overwrite the DtClient value set by SetClientFields to time.Now()\n\tEST := time.FixedZone(\"EST\", -5*60*60)\n\trequest.Signon.DtClient = *NewDate(2006, 1, 15, 11, 23, 0, 0, EST)\n\n\tmarshalCheckRequest(t, &request, expectedString)\n}\n\nfunc TestUnmarshalBankStatementResponse(t *testing.T) {\n\tresponseReader := strings.NewReader(`<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<?OFX OFXHEADER=\"200\" VERSION=\"203\" SECURITY=\"NONE\" OLDFILEUID=\"NONE\" NEWFILEUID=\"NONE\"?>\n<OFX>\n\t<SIGNONMSGSRSV1>\n\t\t<SONRS>\n\t\t\t<STATUS>\n\t\t\t\t<CODE>0</CODE>\n\t\t\t\t<SEVERITY>INFO</SEVERITY>\n\t\t\t</STATUS>\n\t\t\t<DTSERVER>20060115112303</DTSERVER>\n\t\t\t<LANGUAGE>ENG</LANGUAGE>\n\t\t\t<DTPROFUP>20050221091300</DTPROFUP>\n\t\t\t<DTACCTUP>20060102160000</DTACCTUP>\n\t\t\t<FI>\n\t\t\t\t<ORG>BNK</ORG>\n\t\t\t\t<FID>1987</FID>\n\t\t\t</FI>\n\t\t</SONRS>\n\t</SIGNONMSGSRSV1>\n\t<BANKMSGSRSV1>\n\t\t<STMTTRNRS>\n\t\t\t<TRNUID>1001</TRNUID>\n\t\t\t<STATUS>\n\t\t\t\t<CODE>0</CODE>\n\t\t\t\t<SEVERITY>INFO</SEVERITY>\n\t\t\t</STATUS>\n\t\t\t<STMTRS>\n\t\t\t\t<CURDEF>USD</CURDEF>\n\t\t\t\t<BANKACCTFROM>\n\t\t\t\t\t<BANKID>318398732</BANKID>\n\t\t\t\t\t<ACCTID>78346129</ACCTID>\n\t\t\t\t\t<ACCTTYPE>CHECKING</ACCTTYPE>\n\t\t\t\t</BANKACCTFROM>\n\t\t\t\t<BANKTRANLIST>\n\t\t\t\t\t<DTSTART>20060101</DTSTART>\n\t\t\t\t\t<DTEND>20060115</DTEND>\n\t\t\t\t\t<STMTTRN>\n\t\t\t\t\t\t<TRNTYPE>CHECK</TRNTYPE>\n\t\t\t\t\t\t<DTPOSTED>20060104</DTPOSTED>\n\t\t\t\t\t\t<TRNAMT>-200.00</TRNAMT>\n\t\t\t\t\t\t<FITID>00592</FITID>\n\t\t\t\t\t\t<CHECKNUM>2002</CHECKNUM>\n\t\t\t\t\t</STMTTRN>\n\t\t\t\t\t<STMTTRN>\n\t\t\t\t\t\t<TRNTYPE>ATM</TRNTYPE>\n\t\t\t\t\t\t<DTPOSTED>20060112</DTPOSTED>\n\t\t\t\t\t\t<DTUSER>20060112</DTUSER>\n\t\t\t\t\t\t<TRNAMT>-300.00</TRNAMT>\n\t\t\t\t\t\t<FITID>00679</FITID>\n\t\t\t\t\t</STMTTRN>\n\t\t\t\t</BANKTRANLIST>\n\t\t\t\t<LEDGERBAL>\n\t\t\t\t\t<BALAMT>200.29</BALAMT>\n\t\t\t\t\t<DTASOF>200601141600</DTASOF>\n\t\t\t\t</LEDGERBAL>\n\t\t\t\t<AVAILBAL>\n\t\t\t\t\t<BALAMT>200.29</BALAMT>\n\t\t\t\t\t<DTASOF>200601141600</DTASOF>\n\t\t\t\t</AVAILBAL>\n\t\t\t</STMTRS>\n\t\t</STMTTRNRS>\n\t</BANKMSGSRSV1>\n</OFX>`)\n\tvar expected Response\n\n\texpected.Version = OfxVersion203\n\texpected.Signon.Status.Code = 0\n\texpected.Signon.Status.Severity = \"INFO\"\n\texpected.Signon.DtServer = *NewDateGMT(2006, 1, 15, 11, 23, 03, 0)\n\texpected.Signon.Language = \"ENG\"\n\texpected.Signon.DtProfUp = NewDateGMT(2005, 2, 21, 9, 13, 0, 0)\n\texpected.Signon.DtAcctUp = NewDateGMT(2006, 1, 2, 16, 0, 0, 0)\n\texpected.Signon.Org = \"BNK\"\n\texpected.Signon.Fid = \"1987\"\n\n\tvar trnamt1, trnamt2 Amount\n\ttrnamt1.SetFrac64(-20000, 100)\n\ttrnamt2.SetFrac64(-30000, 100)\n\n\tbanktranlist := TransactionList{\n\t\tDtStart: *NewDateGMT(2006, 1, 1, 0, 0, 0, 0),\n\t\tDtEnd:   *NewDateGMT(2006, 1, 15, 0, 0, 0, 0),\n\t\tTransactions: []Transaction{\n\t\t\t{\n\t\t\t\tTrnType:  TrnTypeCheck,\n\t\t\t\tDtPosted: *NewDateGMT(2006, 1, 4, 0, 0, 0, 0),\n\t\t\t\tTrnAmt:   trnamt1,\n\t\t\t\tFiTID:    \"00592\",\n\t\t\t\tCheckNum: \"2002\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tTrnType:  TrnTypeATM,\n\t\t\t\tDtPosted: *NewDateGMT(2006, 1, 12, 0, 0, 0, 0),\n\t\t\t\tDtUser:   NewDateGMT(2006, 1, 12, 0, 0, 0, 0),\n\t\t\t\tTrnAmt:   trnamt2,\n\t\t\t\tFiTID:    \"00679\",\n\t\t\t},\n\t\t},\n\t}\n\n\tvar balamt, availbalamt Amount\n\tbalamt.SetFrac64(20029, 100)\n\tavailbalamt.SetFrac64(20029, 100)\n\n\tusd, err := NewCurrSymbol(\"USD\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating CurrSymbol for USD\\n\")\n\t}\n\n\tstatementResponse := StatementResponse{\n\t\tTrnUID: \"1001\",\n\t\tStatus: Status{\n\t\t\tCode:     0,\n\t\t\tSeverity: \"INFO\",\n\t\t},\n\t\tCurDef: *usd,\n\t\tBankAcctFrom: BankAcct{\n\t\t\tBankID:   \"318398732\",\n\t\t\tAcctID:   \"78346129\",\n\t\t\tAcctType: AcctTypeChecking,\n\t\t},\n\t\tBankTranList: &banktranlist,\n\t\tBalAmt:       balamt,\n\t\tDtAsOf:       *NewDateGMT(2006, 1, 14, 16, 0, 0, 0),\n\t\tAvailBalAmt:  &availbalamt,\n\t\tAvailDtAsOf:  NewDateGMT(2006, 1, 14, 16, 0, 0, 0),\n\t}\n\texpected.Bank = append(expected.Bank, &statementResponse)\n\n\tresponse, err := ParseResponse(responseReader)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error unmarshalling response: %s\\n\", err)\n\t}\n\n\tcheckResponsesEqual(t, &expected, response)\n\tcheckResponseRoundTrip(t, response)\n}\n\nfunc TestPayeeValid(t *testing.T) {\n\tp := Payee{\n\t\tName:       \"Jane\",\n\t\tAddr1:      \"Sesame Street\",\n\t\tCity:       \"Mytown\",\n\t\tState:      \"AA\",\n\t\tPostalCode: \"12345\",\n\t\tPhone:      \"12345678901\",\n\t}\n\tvalid, err := p.Valid()\n\tif !valid {\n\t\tt.Fatalf(\"Unexpected error from calling Valid: %s\\n\", err)\n\t}\n\n\t// Ensure some empty fields trigger invalid response\n\tbadp := p\n\tbadp.Name = \"\"\n\tvalid, err = badp.Valid()\n\tif valid || err == nil {\n\t\tt.Fatalf(\"Expected error from calling Valid with empty name\\n\")\n\t}\n\n\tbadp = p\n\tbadp.Addr1 = \"\"\n\tvalid, err = badp.Valid()\n\tif valid || err == nil {\n\t\tt.Fatalf(\"Expected error from calling Valid with empty address\\n\")\n\t}\n\n\tbadp = p\n\tbadp.City = \"\"\n\tvalid, err = badp.Valid()\n\tif valid || err == nil {\n\t\tt.Fatalf(\"Expected error from calling Valid with empty city\\n\")\n\t}\n\n\tbadp = p\n\tbadp.State = \"\"\n\tvalid, err = badp.Valid()\n\tif valid || err == nil {\n\t\tt.Fatalf(\"Expected error from calling Valid with empty state\\n\")\n\t}\n\n\tbadp = p\n\tbadp.PostalCode = \"\"\n\tvalid, err = badp.Valid()\n\tif valid || err == nil {\n\t\tt.Fatalf(\"Expected error from calling Valid with empty postal code\\n\")\n\t}\n\n\tbadp = p\n\tbadp.Phone = \"\"\n\tvalid, err = badp.Valid()\n\tif valid || err == nil {\n\t\tt.Fatalf(\"Expected error from calling Valid with empty phone\\n\")\n\t}\n}\n\nfunc TestBalanceValid(t *testing.T) {\n\tvar a Amount\n\ta.SetFrac64(8, 1)\n\tb := Balance{\n\t\tName:    \"Checking\",\n\t\tDesc:    \"Jane's Personal Checking\",\n\t\tBalType: BalTypeDollar,\n\t\tValue:   a,\n\t}\n\tvalid, err := b.Valid()\n\tif !valid {\n\t\tt.Fatalf(\"Unexpected error from calling Valid: %s\\n\", err)\n\t}\n\n\tbadb := b\n\tbadb.Name = \"\"\n\tvalid, err = badb.Valid()\n\tif valid || err == nil {\n\t\tt.Fatalf(\"Expected error from calling Valid with empty name\\n\")\n\t}\n\n\tbadb = b\n\tbadb.Desc = \"\"\n\tvalid, err = badb.Valid()\n\tif valid || err == nil {\n\t\tt.Fatalf(\"Expected error from calling Valid with empty description\\n\")\n\t}\n\n\tbadb = Balance{\n\t\tName:  \"Checking\",\n\t\tDesc:  \"Jane's Personal Checking\",\n\t\tValue: a,\n\t}\n\tvalid, err = badb.Valid()\n\tif valid || err == nil {\n\t\tt.Fatalf(\"Expected error from calling Valid with unspecified balance type\\n\")\n\t}\n}\n"
  },
  {
    "path": "basic_client.go",
    "content": "package ofxgo\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// BasicClient provides a standard Client implementation suitable for most\n// financial institutions. BasicClient uses default, non-zero settings, even if\n// its fields are not initialized.\ntype BasicClient struct {\n\t// Request fields to overwrite with the client's values. If nonempty,\n\t// defaults are used\n\tSpecVersion ofxVersion // VERSION in header\n\tAppID       string     // SONRQ>APPID\n\tAppVer      string     // SONRQ>APPVER\n\n\t// Don't insert newlines or indentation when marshalling to SGML/XML\n\tNoIndent bool\n\t// Use carriage returns on new lines\n\tCarriageReturn bool\n\t// Set User-Agent header to this string, if not empty\n\tUserAgent string\n\n\tHTTPClient *http.Client\n}\n\n// OfxVersion returns the OFX specification version this BasicClient will marshal\n// Requests as. Defaults to \"203\" if the client's SpecVersion field is empty.\nfunc (c *BasicClient) OfxVersion() ofxVersion {\n\tif c.SpecVersion.Valid() {\n\t\treturn c.SpecVersion\n\t}\n\treturn OfxVersion203\n}\n\n// ID returns this BasicClient's OFX AppID field, defaulting to \"OFXGO\" if\n// unspecified.\nfunc (c *BasicClient) ID() String {\n\tif len(c.AppID) > 0 {\n\t\treturn String(c.AppID)\n\t}\n\treturn String(\"OFXGO\")\n}\n\n// Version returns this BasicClient's version number as a string, defaulting to\n// \"0001\" if unspecified.\nfunc (c *BasicClient) Version() String {\n\tif len(c.AppVer) > 0 {\n\t\treturn String(c.AppVer)\n\t}\n\treturn String(\"0001\")\n}\n\n// IndentRequests returns true if the marshaled XML should be indented (and\n// contain newlines, since the two are linked in the current implementation)\nfunc (c *BasicClient) IndentRequests() bool {\n\treturn !c.NoIndent\n}\n\n// CarriageReturnNewLines returns true if carriage returns should be used on new lines, false otherwise\nfunc (c *BasicClient) CarriageReturnNewLines() bool {\n\treturn c.CarriageReturn\n}\n\n// RawRequest is a convenience wrapper around http.Post. It is exposed only for\n// when you need to read/inspect the raw HTTP response yourself.\nfunc (c *BasicClient) RawRequest(URL string, r io.Reader) (*http.Response, error) {\n\tif !strings.HasPrefix(URL, \"https://\") {\n\t\treturn nil, errors.New(\"Refusing to send OFX request with possible plain-text password over non-https protocol\")\n\t}\n\n\thttpClient := c.HTTPClient\n\tif httpClient == nil {\n\t\thttpClient = http.DefaultClient\n\t}\n\n\trequest, err := http.NewRequest(\"POST\", URL, r)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\trequest.Header.Set(\"Content-Type\", \"application/x-ofx\")\n\trequest.Header.Add(\"Accept\", \"*/*, application/x-ofx\")\n\tif c.UserAgent != \"\" {\n\t\trequest.Header.Set(\"User-Agent\", c.UserAgent)\n\t}\n\tresponse, err := httpClient.Do(request)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif response.StatusCode != 200 {\n\t\treturn response, errors.New(\"OFXQuery request status: \" + response.Status)\n\t}\n\n\treturn response, nil\n}\n\n// RequestNoParse marshals a Request to XML, makes an HTTP request, and returns\n// the raw HTTP response\nfunc (c *BasicClient) RequestNoParse(r *Request) (*http.Response, error) {\n\treturn clientRequestNoParse(c, r)\n}\n\n// Request marshals a Request to XML, makes an HTTP request, and then\n// unmarshals the response into a Response object.\nfunc (c *BasicClient) Request(r *Request) (*Response, error) {\n\treturn clientRequest(c, r)\n}\n"
  },
  {
    "path": "basic_client_test.go",
    "content": "package ofxgo\n\nimport (\n\t\"errors\"\n\t\"net\"\n\t\"net/http\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestBasicClient_HTTPClient(t *testing.T) {\n\tc := &BasicClient{\n\t\tHTTPClient: &http.Client{\n\t\t\tTransport: &http.Transport{\n\t\t\t\tDial: func(network, addr string) (net.Conn, error) {\n\t\t\t\t\treturn nil, errors.New(\"bad test client\")\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\t_, err := c.Request(&Request{\n\t\tURL: \"https://test\",\n\t\tSignon: SignonRequest{\n\t\t\tUserID:   \"test\",\n\t\t\tUserPass: \"test\",\n\t\t},\n\t})\n\tif err == nil || !strings.Contains(err.Error(), \"bad test client\") {\n\t\tt.Fatalf(\"expected error containing 'bad test client', got: %v\", err)\n\t}\n}\n"
  },
  {
    "path": "client.go",
    "content": "package ofxgo\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// Client serves to aggregate OFX client settings that may be necessary to talk\n// to a particular server due to quirks in that server's implementation.\n// Client also provides the Request and RequestNoParse helper methods to aid in\n// making and parsing requests.\ntype Client interface {\n\t// Used to fill out a Request object\n\tOfxVersion() ofxVersion\n\tID() String\n\tVersion() String\n\tIndentRequests() bool\n\tCarriageReturnNewLines() bool\n\n\t// Request marshals a Request object into XML, makes an HTTP request\n\t// against it's URL, and then unmarshals the response into a Response\n\t// object.\n\t//\n\t// Before being marshaled, some of the the Request object's values are\n\t// overwritten, namely those dictated by the BasicClient's configuration\n\t// (Version, AppID, AppVer fields), and the client's current time\n\t// (DtClient). These are updated in place in the supplied Request object so\n\t// they may later be inspected by the caller.\n\tRequest(r *Request) (*Response, error)\n\n\t// RequestNoParse marshals a Request object into XML, makes an HTTP\n\t// request, and returns the raw HTTP response. Unlike RawRequest(), it\n\t// takes client settings into account. Unlike Request(), it doesn't parse\n\t// the response into  an ofxgo.Request object.\n\t//\n\t// Caveat: The caller is responsible for closing the http Response.Body\n\t// (see the http module's documentation for more information)\n\tRequestNoParse(r *Request) (*http.Response, error)\n\n\t// RawRequest is little more than a thin wrapper around http.Post\n\t//\n\t// In most cases, you should probably be using Request() instead, but\n\t// RawRequest can be useful if you need to read the raw unparsed http\n\t// response yourself (perhaps for downloading an OFX file for use by an\n\t// external program, or debugging server behavior), or have a handcrafted\n\t// request you'd like to try.\n\t//\n\t// Caveats: RawRequest does *not* take client settings into account as\n\t// Client.Request() does, so your particular server may or may not like\n\t// whatever we read from 'r'. The caller is responsible for closing the\n\t// http Response.Body (see the http module's documentation for more\n\t// information)\n\tRawRequest(URL string, r io.Reader) (*http.Response, error)\n}\n\ntype clientCreationFunc func(*BasicClient) Client\n\n// GetClient returns a new Client for a given URL. It attempts to find a\n// specialized client for this URL, but simply returns the passed-in\n// BasicClient if no such match is found.\nfunc GetClient(URL string, bc *BasicClient) Client {\n\tclients := []struct {\n\t\tURL  string\n\t\tFunc clientCreationFunc\n\t}{\n\t\t{\"https://ofx.discovercard.com\", NewDiscoverCardClient},\n\t\t{\"https://vesnc.vanguard.com/us/OfxDirectConnectServlet\", NewVanguardClient},\n\t}\n\tfor _, client := range clients {\n\t\tif client.URL == strings.Trim(URL, \"/\") {\n\t\t\treturn client.Func(bc)\n\t\t}\n\t}\n\treturn bc\n}\n\n// clientRequestNoParse can be used for building clients' RequestNoParse\n// methods if they require fairly standard behavior\nfunc clientRequestNoParse(c Client, r *Request) (*http.Response, error) {\n\tr.SetClientFields(c)\n\n\tb, err := r.Marshal()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn c.RawRequest(r.URL, b)\n}\n\n// clientRequest can be used for building clients' Request methods if they\n// require fairly standard behavior\nfunc clientRequest(c Client, r *Request) (*Response, error) {\n\tresponse, err := c.RequestNoParse(r)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer response.Body.Close()\n\n\tofxresp, err := ParseResponse(response.Body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn ofxresp, nil\n}\n"
  },
  {
    "path": "cmd/ofx/bankdownload.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"github.com/aclindsa/ofxgo\"\n\t\"io\"\n\t\"os\"\n)\n\nvar downloadCommand = command{\n\tName:        \"download-bank\",\n\tDescription: \"Download a bank account statement to a file\",\n\tFlags:       flag.NewFlagSet(\"download-bank\", flag.ExitOnError),\n\tCheckFlags:  downloadCheckFlags,\n\tDo:          download,\n}\n\nvar filename, bankID, acctID, acctType string\n\nfunc init() {\n\tdefineServerFlags(downloadCommand.Flags)\n\tdownloadCommand.Flags.StringVar(&filename, \"filename\", \"./response.ofx\", \"The file to save to\")\n\tdownloadCommand.Flags.StringVar(&bankID, \"bankid\", \"\", \"BankID (from `get-accounts` subcommand)\")\n\tdownloadCommand.Flags.StringVar(&acctID, \"acctid\", \"\", \"AcctID (from `get-accounts` subcommand)\")\n\tdownloadCommand.Flags.StringVar(&acctType, \"accttype\", \"CHECKING\", \"AcctType (from `get-accounts` subcommand)\")\n}\n\nfunc downloadCheckFlags() bool {\n\tret := checkServerFlags()\n\n\tif len(filename) == 0 {\n\t\tfmt.Println(\"Error: Filename empty\")\n\t\treturn false\n\t}\n\n\treturn ret\n}\n\nfunc download() {\n\tclient, query := newRequest()\n\n\tacctTypeEnum, err := ofxgo.NewAcctType(acctType)\n\tif err != nil {\n\t\tfmt.Println(\"Error parsing accttype:\", err)\n\t\tos.Exit(1)\n\t}\n\n\tuid, err := ofxgo.RandomUID()\n\tif err != nil {\n\t\tfmt.Println(\"Error creating uid for transaction:\", err)\n\t\tos.Exit(1)\n\t}\n\n\tstatementRequest := ofxgo.StatementRequest{\n\t\tTrnUID: *uid,\n\t\tBankAcctFrom: ofxgo.BankAcct{\n\t\t\tBankID:   ofxgo.String(bankID),\n\t\t\tAcctID:   ofxgo.String(acctID),\n\t\t\tAcctType: acctTypeEnum,\n\t\t},\n\t\tInclude: true,\n\t}\n\n\tquery.Bank = append(query.Bank, &statementRequest)\n\n\tif dryrun {\n\t\tprintRequest(client, query)\n\t\treturn\n\t}\n\n\tresponse, err := client.RequestNoParse(query)\n\tif err != nil {\n\t\tfmt.Println(\"Error requesting account statement:\", err)\n\t\tos.Exit(1)\n\t}\n\tdefer response.Body.Close()\n\n\tfile, err := os.Create(filename)\n\tif err != nil {\n\t\tfmt.Println(\"Error creating file to write to:\", err)\n\t\tos.Exit(1)\n\t}\n\tdefer file.Close()\n\n\t_, err = io.Copy(file, response.Body)\n\tif err != nil {\n\t\tfmt.Println(\"Error writing response to file:\", err)\n\t\tos.Exit(1)\n\t}\n}\n"
  },
  {
    "path": "cmd/ofx/banktransactions.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"github.com/aclindsa/ofxgo\"\n\t\"os\"\n)\n\nvar bankTransactionsCommand = command{\n\tName:        \"transactions-bank\",\n\tDescription: \"Print bank transactions and balance\",\n\tFlags:       flag.NewFlagSet(\"transactions-bank\", flag.ExitOnError),\n\tCheckFlags:  checkServerFlags,\n\tDo:          bankTransactions,\n}\n\nfunc init() {\n\tdefineServerFlags(bankTransactionsCommand.Flags)\n\tbankTransactionsCommand.Flags.StringVar(&bankID, \"bankid\", \"\", \"BankID (from `get-accounts` subcommand)\")\n\tbankTransactionsCommand.Flags.StringVar(&acctID, \"acctid\", \"\", \"AcctID (from `get-accounts` subcommand)\")\n\tbankTransactionsCommand.Flags.StringVar(&acctType, \"accttype\", \"CHECKING\", \"AcctType (from `get-accounts` subcommand)\")\n}\n\nfunc bankTransactions() {\n\tclient, query := newRequest()\n\n\tacctTypeEnum, err := ofxgo.NewAcctType(acctType)\n\tif err != nil {\n\t\tfmt.Println(\"Error parsing accttype:\", err)\n\t\tos.Exit(1)\n\t}\n\n\tuid, err := ofxgo.RandomUID()\n\tif err != nil {\n\t\tfmt.Println(\"Error creating uid for transaction:\", err)\n\t\tos.Exit(1)\n\t}\n\n\tstatementRequest := ofxgo.StatementRequest{\n\t\tTrnUID: *uid,\n\t\tBankAcctFrom: ofxgo.BankAcct{\n\t\t\tBankID:   ofxgo.String(bankID),\n\t\t\tAcctID:   ofxgo.String(acctID),\n\t\t\tAcctType: acctTypeEnum,\n\t\t},\n\t\tInclude: true,\n\t}\n\n\tquery.Bank = append(query.Bank, &statementRequest)\n\n\tif dryrun {\n\t\tprintRequest(client, query)\n\t\treturn\n\t}\n\n\tresponse, err := client.Request(query)\n\tif err != nil {\n\t\tfmt.Println(\"Error requesting account statement:\", err)\n\t\tos.Exit(1)\n\t}\n\n\tif response.Signon.Status.Code != 0 {\n\t\tmeaning, _ := response.Signon.Status.CodeMeaning()\n\t\tfmt.Printf(\"Nonzero signon status (%d: %s) with message: %s\\n\", response.Signon.Status.Code, meaning, response.Signon.Status.Message)\n\t\tos.Exit(1)\n\t}\n\n\tif len(response.Bank) < 1 {\n\t\tfmt.Println(\"No banking messages received\")\n\t\treturn\n\t}\n\n\tif stmt, ok := response.Bank[0].(*ofxgo.StatementResponse); ok {\n\t\tfmt.Printf(\"Balance: %s %s (as of %s)\\n\", stmt.BalAmt, stmt.CurDef, stmt.DtAsOf)\n\t\tfmt.Println(\"Transactions:\")\n\t\tfor _, tran := range stmt.BankTranList.Transactions {\n\t\t\tprintTransaction(stmt.CurDef, &tran)\n\t\t}\n\t}\n}\n\nfunc printTransaction(defCurrency ofxgo.CurrSymbol, tran *ofxgo.Transaction) {\n\tcurrency := defCurrency\n\tif tran.Currency != nil {\n\t\tcurrency = tran.Currency.CurSym\n\t}\n\n\tvar name string\n\tif len(tran.Name) > 0 {\n\t\tname = string(tran.Name)\n\t} else if tran.Payee != nil {\n\t\tname = string(tran.Payee.Name)\n\t}\n\n\tif len(tran.Memo) > 0 {\n\t\tname = name + \" - \" + string(tran.Memo)\n\t}\n\n\tfmt.Printf(\"%s %-15s %-11s %s\\n\", tran.DtPosted, tran.TrnAmt.String()+\" \"+currency.String(), tran.TrnType, name)\n}\n"
  },
  {
    "path": "cmd/ofx/ccdownload.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"github.com/aclindsa/ofxgo\"\n\t\"io\"\n\t\"os\"\n)\n\nvar ccDownloadCommand = command{\n\tName:        \"download-cc\",\n\tDescription: \"Download a credit card account statement to a file\",\n\tFlags:       flag.NewFlagSet(\"download-cc\", flag.ExitOnError),\n\tCheckFlags:  ccDownloadCheckFlags,\n\tDo:          ccDownload,\n}\n\nfunc init() {\n\tdefineServerFlags(ccDownloadCommand.Flags)\n\tccDownloadCommand.Flags.StringVar(&filename, \"filename\", \"./response.ofx\", \"The file to save to\")\n\tccDownloadCommand.Flags.StringVar(&acctID, \"acctid\", \"\", \"AcctID (from `get-accounts` subcommand)\")\n}\n\nfunc ccDownloadCheckFlags() bool {\n\tret := checkServerFlags()\n\n\tif len(filename) == 0 {\n\t\tfmt.Println(\"Error: Filename empty\")\n\t\treturn false\n\t}\n\n\treturn ret\n}\n\nfunc ccDownload() {\n\tclient, query := newRequest()\n\n\tuid, err := ofxgo.RandomUID()\n\tif err != nil {\n\t\tfmt.Println(\"Error creating uid for transaction:\", err)\n\t\tos.Exit(1)\n\t}\n\n\tstatementRequest := ofxgo.CCStatementRequest{\n\t\tTrnUID: *uid,\n\t\tCCAcctFrom: ofxgo.CCAcct{\n\t\t\tAcctID: ofxgo.String(acctID),\n\t\t},\n\t\tInclude: true,\n\t}\n\tquery.CreditCard = append(query.CreditCard, &statementRequest)\n\n\tif dryrun {\n\t\tprintRequest(client, query)\n\t\treturn\n\t}\n\n\tresponse, err := client.RequestNoParse(query)\n\n\tif err != nil {\n\t\tfmt.Println(\"Error requesting account statement:\", err)\n\t\tos.Exit(1)\n\t}\n\tdefer response.Body.Close()\n\n\tfile, err := os.Create(filename)\n\tif err != nil {\n\t\tfmt.Println(\"Error creating file to write to:\", err)\n\t\tos.Exit(1)\n\t}\n\tdefer file.Close()\n\n\t_, err = io.Copy(file, response.Body)\n\tif err != nil {\n\t\tfmt.Println(\"Error writing response to file:\", err)\n\t\tos.Exit(1)\n\t}\n}\n"
  },
  {
    "path": "cmd/ofx/cctransactions.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"github.com/aclindsa/ofxgo\"\n\t\"os\"\n)\n\nvar ccTransactionsCommand = command{\n\tName:        \"transactions-cc\",\n\tDescription: \"Print credit card transactions and balance\",\n\tFlags:       flag.NewFlagSet(\"transactions-cc\", flag.ExitOnError),\n\tCheckFlags:  checkServerFlags,\n\tDo:          ccTransactions,\n}\n\nfunc init() {\n\tdefineServerFlags(ccTransactionsCommand.Flags)\n\tccTransactionsCommand.Flags.StringVar(&acctID, \"acctid\", \"\", \"AcctID (from `get-accounts` subcommand)\")\n}\n\nfunc ccTransactions() {\n\tclient, query := newRequest()\n\n\tuid, err := ofxgo.RandomUID()\n\tif err != nil {\n\t\tfmt.Println(\"Error creating uid for transaction:\", err)\n\t\tos.Exit(1)\n\t}\n\n\tstatementRequest := ofxgo.CCStatementRequest{\n\t\tTrnUID: *uid,\n\t\tCCAcctFrom: ofxgo.CCAcct{\n\t\t\tAcctID: ofxgo.String(acctID),\n\t\t},\n\t\tInclude: true,\n\t}\n\tquery.CreditCard = append(query.CreditCard, &statementRequest)\n\n\tif dryrun {\n\t\tprintRequest(client, query)\n\t\treturn\n\t}\n\n\tresponse, err := client.Request(query)\n\tif err != nil {\n\t\tfmt.Println(\"Error requesting account statement:\", err)\n\t\tos.Exit(1)\n\t}\n\n\tif response.Signon.Status.Code != 0 {\n\t\tmeaning, _ := response.Signon.Status.CodeMeaning()\n\t\tfmt.Printf(\"Nonzero signon status (%d: %s) with message: %s\\n\", response.Signon.Status.Code, meaning, response.Signon.Status.Message)\n\t\tos.Exit(1)\n\t}\n\n\tif len(response.CreditCard) < 1 {\n\t\tfmt.Println(\"No banking messages received\")\n\t\treturn\n\t}\n\n\tif stmt, ok := response.CreditCard[0].(*ofxgo.CCStatementResponse); ok {\n\t\tfmt.Printf(\"Balance: %s %s (as of %s)\\n\", stmt.BalAmt, stmt.CurDef, stmt.DtAsOf)\n\t\tfmt.Println(\"Transactions:\")\n\t\tfor _, tran := range stmt.BankTranList.Transactions {\n\t\t\tcurrency := stmt.CurDef\n\t\t\tif tran.Currency != nil {\n\t\t\t\tcurrency = tran.Currency.CurSym\n\t\t\t}\n\n\t\t\tvar name string\n\t\t\tif len(tran.Name) > 0 {\n\t\t\t\tname = string(tran.Name)\n\t\t\t} else {\n\t\t\t\tname = string(tran.Payee.Name)\n\t\t\t}\n\n\t\t\tif len(tran.Memo) > 0 {\n\t\t\t\tname = name + \" - \" + string(tran.Memo)\n\t\t\t}\n\n\t\t\tfmt.Printf(\"%s %-15s %-11s %s\\n\", tran.DtPosted, tran.TrnAmt.String()+\" \"+currency.String(), tran.TrnType, name)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "cmd/ofx/command.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"golang.org/x/term\"\n\t\"os\"\n)\n\ntype command struct {\n\tName        string\n\tDescription string\n\tFlags       *flag.FlagSet\n\tCheckFlags  func() bool // Check the flag values after they're parsed, printing errors and returning false if they're incorrect\n\tDo          func()      // Run the command (only called if CheckFlags returns true)\n}\n\nfunc (c *command) usage() {\n\tfmt.Printf(\"Usage of %s:\\n\", c.Name)\n\tc.Flags.PrintDefaults()\n}\n\n// flags common to all server transactions\nvar serverURL, username, password, org, fid, appID, appVer, ofxVersion, clientUID string\nvar noIndentRequests bool\nvar carriageReturn bool\nvar dryrun bool\nvar userAgent string\n\nfunc defineServerFlags(f *flag.FlagSet) {\n\tf.StringVar(&serverURL, \"url\", \"\", \"Financial institution's OFX Server URL (see ofxhome.com if you don't know it)\")\n\tf.StringVar(&username, \"username\", \"\", \"Your username at financial institution\")\n\tf.StringVar(&password, \"password\", \"\", \"Your password at financial institution\")\n\tf.StringVar(&org, \"org\", \"\", \"'ORG' for your financial institution\")\n\tf.StringVar(&fid, \"fid\", \"\", \"'FID' for your financial institution\")\n\tf.StringVar(&appID, \"appid\", \"QWIN\", \"'APPID' to pretend to be\")\n\tf.StringVar(&appVer, \"appver\", \"2400\", \"'APPVER' to pretend to be\")\n\tf.StringVar(&ofxVersion, \"ofxversion\", \"203\", \"OFX version to use\")\n\tf.StringVar(&clientUID, \"clientuid\", \"\", \"Client UID (only required by a few FIs, like Chase)\")\n\tf.BoolVar(&noIndentRequests, \"noindent\", false, \"Don't indent OFX requests\")\n\tf.BoolVar(&carriageReturn, \"carriagereturn\", false, \"Use carriage return as line separator\")\n\tf.StringVar(&userAgent, \"useragent\", \"\", \"Use string as User-Agent header when sending request\")\n\tf.BoolVar(&dryrun, \"dryrun\", false, \"Don't send request - print content of request instead\")\n}\n\nfunc checkServerFlags() bool {\n\tvar ret bool = true\n\tif len(serverURL) == 0 {\n\t\tfmt.Println(\"Error: Server URL empty\")\n\t\tret = false\n\t}\n\tif len(username) == 0 {\n\t\tfmt.Println(\"Error: Username empty\")\n\t\tret = false\n\t}\n\n\tif ret && len(password) == 0 {\n\t\tfmt.Printf(\"Password for %s: \", username)\n\t\tpass, err := term.ReadPassword(int(os.Stdin.Fd()))\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"Error reading password: %s\\n\", err)\n\t\t\tret = false\n\t\t} else {\n\t\t\tpassword = string(pass)\n\t\t}\n\t}\n\treturn ret\n}\n"
  },
  {
    "path": "cmd/ofx/detect_settings.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"github.com/aclindsa/ofxgo\"\n\t\"os\"\n\t\"time\"\n)\n\nvar detectSettingsCommand = command{\n\tName:        \"detect-settings\",\n\tDescription: \"Attempt to guess client settings needed for a particular financial institution\",\n\tFlags:       flag.NewFlagSet(\"detect-settings\", flag.ExitOnError),\n\tCheckFlags:  checkServerFlags,\n\tDo:          detectSettings,\n}\n\nvar delay uint64\n\nfunc init() {\n\tdetectSettingsCommand.Flags.StringVar(&serverURL, \"url\", \"\", \"Financial institution's OFX Server URL (see ofxhome.com if you don't know it)\")\n\tdetectSettingsCommand.Flags.StringVar(&username, \"username\", \"\", \"Your username at financial institution\")\n\tdetectSettingsCommand.Flags.StringVar(&password, \"password\", \"\", \"Your password at financial institution\")\n\tdetectSettingsCommand.Flags.StringVar(&org, \"org\", \"\", \"'ORG' for your financial institution\")\n\tdetectSettingsCommand.Flags.StringVar(&fid, \"fid\", \"\", \"'FID' for your financial institution\")\n\tdetectSettingsCommand.Flags.Uint64Var(&delay, \"delay\", 500, \"How long to delay between two subsequent requests, in milliseconds\")\n}\n\n// We keep a separate list of APPIDs to preserve the ordering (ordering isn't\n// guaranteed in maps). We want to try them in order from 'best' and most\n// likely to work to 'worse' and least likely to work\nvar appIDs = []string{\n\t\"OFXGO\", // ofxgo (this library)\n\t\"QWIN\",  // Intuit Quicken Windows\n\t\"QMOFX\", // Intuit Quicken Mac\n\t\"QB\",    // Intuit QuickBooks Windows\n\t\"Money\", // Microsoft Money 2007\n}\n\nvar appVersions = map[string][]string{\n\t\"OFXGO\": { // ofxgo (this library)\n\t\t\"0001\",\n\t},\n\t\"QWIN\": { // Intuit Quicken Windows\n\t\t\"2600\", // 2017\n\t\t\"2500\", // 2016\n\t\t\"2400\", // 2015\n\t\t\"2300\", // 2014\n\t\t\"2200\", // 2013\n\t\t\"2100\", // 2012\n\t\t\"2000\", // 2011\n\t\t\"1900\", // 2010\n\t\t\"1800\", // 2009\n\t\t\"1700\", // 2008\n\t\t\"1600\", // 2007\n\t\t\"1500\", // 2006\n\t\t\"1400\", // 2005\n\t},\n\t\"QMOFX\": { // Intuit Quicken Mac\n\t\t\"1700\", // 2008\n\t\t\"1600\", // 2007\n\t\t\"1500\", // 2006\n\t\t\"1400\", // 2005\n\t},\n\t\"QB\": { // Intuit QuickBooks Windows\n\t\t\"1800\", // 2008\n\t\t\"1700\", // 2007\n\t\t\"1600\", // 2006\n\t\t\"1500\", // 2005\n\t},\n\t\"Money\": { // Microsoft Money 2007\n\t\t\"1600\", // 2007\n\t\t\"1500\", // 2006\n\t\t\"1400\", // 2005\n\t\t\"1200\", // 2004\n\t\t\"1100\", // 2003\n\t},\n}\n\nvar versions = []string{\n\t\"203\",\n\t\"103\",\n\t\"200\",\n\t\"201\",\n\t\"202\",\n\t\"210\",\n\t\"211\",\n\t\"102\",\n\t\"151\",\n\t\"160\",\n\t\"220\",\n}\n\nfunc detectSettings() {\n\tvar attempts uint\n\tfor _, appID := range appIDs {\n\t\tfor _, appVer := range appVersions[appID] {\n\t\t\tfor _, version := range versions {\n\t\t\t\tfor _, noIndent := range []bool{false, true} {\n\t\t\t\t\tif tryProfile(appID, appVer, version, noIndent) {\n\t\t\t\t\t\tfmt.Println(\"The following settings were found to work:\")\n\t\t\t\t\t\tfmt.Printf(\"AppID: %s\\n\", appID)\n\t\t\t\t\t\tfmt.Printf(\"AppVer: %s\\n\", appVer)\n\t\t\t\t\t\tfmt.Printf(\"OFX Version: %s\\n\", version)\n\t\t\t\t\t\tfmt.Printf(\"noindent: %t\\n\", noIndent)\n\t\t\t\t\t\tos.Exit(0)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tattempts++\n\t\t\t\t\t\tvar noIndentString string\n\t\t\t\t\t\tif noIndent {\n\t\t\t\t\t\t\tnoIndentString = \" noindent\"\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfmt.Printf(\"Attempt %d failed (%s %s %s%s), trying again after %dms...\\n\", attempts, appID, appVer, version, noIndentString, delay)\n\t\t\t\t\t\ttime.Sleep(time.Duration(delay) * time.Millisecond)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nconst anonymous = \"anonymous00000000000000000000000\"\n\nfunc tryProfile(appID, appVer, version string, noindent bool) bool {\n\tver, err := ofxgo.NewOfxVersion(version)\n\tif err != nil {\n\t\tfmt.Println(\"Error creating new OfxVersion enum:\", err)\n\t\tos.Exit(1)\n\t}\n\tvar client = ofxgo.GetClient(serverURL,\n\t\t&ofxgo.BasicClient{\n\t\t\tAppID:       appID,\n\t\t\tAppVer:      appVer,\n\t\t\tSpecVersion: ver,\n\t\t\tNoIndent:    noindent,\n\t\t})\n\n\tvar query ofxgo.Request\n\tquery.URL = serverURL\n\tquery.Signon.ClientUID = ofxgo.UID(clientUID)\n\tquery.Signon.UserID = ofxgo.String(username)\n\tquery.Signon.UserPass = ofxgo.String(password)\n\tquery.Signon.Org = ofxgo.String(org)\n\tquery.Signon.Fid = ofxgo.String(fid)\n\n\tuid, err := ofxgo.RandomUID()\n\tif err != nil {\n\t\tfmt.Println(\"Error creating uid for transaction:\", err)\n\t\tos.Exit(1)\n\t}\n\n\tprofileRequest := ofxgo.ProfileRequest{\n\t\tTrnUID:   *uid,\n\t\tDtProfUp: ofxgo.Date{Time: time.Unix(0, 0)},\n\t}\n\tquery.Prof = append(query.Prof, &profileRequest)\n\n\t_, err = client.Request(&query)\n\tif err == nil {\n\t\treturn true\n\t}\n\n\t// try again with anonymous logins\n\tquery.Signon.UserID = ofxgo.String(anonymous)\n\tquery.Signon.UserPass = ofxgo.String(anonymous)\n\n\t_, err = client.Request(&query)\n\treturn err == nil\n}\n"
  },
  {
    "path": "cmd/ofx/get_accounts.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"github.com/aclindsa/ofxgo\"\n\t\"os\"\n\t\"time\"\n)\n\nvar getAccountsCommand = command{\n\tName:        \"get-accounts\",\n\tDescription: \"List accounts at your financial institution\",\n\tFlags:       flag.NewFlagSet(\"get-accounts\", flag.ExitOnError),\n\tCheckFlags:  checkServerFlags,\n\tDo:          getAccounts,\n}\n\nfunc init() {\n\tdefineServerFlags(getAccountsCommand.Flags)\n}\n\nfunc getAccounts() {\n\tclient, query := newRequest()\n\n\tuid, err := ofxgo.RandomUID()\n\tif err != nil {\n\t\tfmt.Println(\"Error creating uid for transaction:\", err)\n\t\tos.Exit(1)\n\t}\n\n\tacctInfo := ofxgo.AcctInfoRequest{\n\t\tTrnUID:   *uid,\n\t\tDtAcctUp: ofxgo.Date{Time: time.Unix(0, 0)},\n\t}\n\tquery.Signup = append(query.Signup, &acctInfo)\n\n\tif dryrun {\n\t\tprintRequest(client, query)\n\t\treturn\n\t}\n\n\tresponse, err := client.Request(query)\n\tif err != nil {\n\t\tfmt.Println(\"Error requesting account information:\", err)\n\t\tos.Exit(1)\n\t}\n\n\tif response.Signon.Status.Code != 0 {\n\t\tmeaning, _ := response.Signon.Status.CodeMeaning()\n\t\tfmt.Printf(\"Nonzero signon status (%d: %s) with message: %s\\n\", response.Signon.Status.Code, meaning, response.Signon.Status.Message)\n\t\tos.Exit(1)\n\t}\n\n\tif len(response.Signup) < 1 {\n\t\tfmt.Println(\"No signup messages received\")\n\t\treturn\n\t}\n\n\tfmt.Printf(\"\\nFound the following accounts:\\n\\n\")\n\n\tif acctinfo, ok := response.Signup[0].(*ofxgo.AcctInfoResponse); ok {\n\t\tfor _, acct := range acctinfo.AcctInfo {\n\t\t\tif acct.BankAcctInfo != nil {\n\t\t\t\tfmt.Printf(\"Bank Account:\\n\\tBankID: \\\"%s\\\"\\n\\tAcctID: \\\"%s\\\"\\n\\tAcctType: %s\\n\", acct.BankAcctInfo.BankAcctFrom.BankID, acct.BankAcctInfo.BankAcctFrom.AcctID, acct.BankAcctInfo.BankAcctFrom.AcctType)\n\t\t\t} else if acct.CCAcctInfo != nil {\n\t\t\t\tfmt.Printf(\"Credit card:\\n\\tAcctID: \\\"%s\\\"\\n\", acct.CCAcctInfo.CCAcctFrom.AcctID)\n\t\t\t} else if acct.InvAcctInfo != nil {\n\t\t\t\tfmt.Printf(\"Investment account:\\n\\tBrokerID: \\\"%s\\\"\\n\\tAcctID: \\\"%s\\\"\\n\", acct.InvAcctInfo.InvAcctFrom.BrokerID, acct.InvAcctInfo.InvAcctFrom.AcctID)\n\t\t\t} else {\n\t\t\t\tfmt.Printf(\"Unknown type: %s %s\\n\", acct.Name, acct.Desc)\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "cmd/ofx/invdownload.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"github.com/aclindsa/ofxgo\"\n\t\"io\"\n\t\"os\"\n)\n\nvar invDownloadCommand = command{\n\tName:        \"download-inv\",\n\tDescription: \"Download a investment account statement to a file\",\n\tFlags:       flag.NewFlagSet(\"download-inv\", flag.ExitOnError),\n\tCheckFlags:  invDownloadCheckFlags,\n\tDo:          invDownload,\n}\n\nvar brokerID string\n\nfunc init() {\n\tdefineServerFlags(invDownloadCommand.Flags)\n\tinvDownloadCommand.Flags.StringVar(&filename, \"filename\", \"./response.ofx\", \"The file to save to\")\n\tinvDownloadCommand.Flags.StringVar(&acctID, \"acctid\", \"\", \"AcctID (from `get-accounts` subcommand)\")\n\tinvDownloadCommand.Flags.StringVar(&brokerID, \"brokerid\", \"\", \"BrokerID (from `get-accounts` subcommand)\")\n}\n\nfunc invDownloadCheckFlags() bool {\n\tret := checkServerFlags()\n\n\tif len(filename) == 0 {\n\t\tfmt.Println(\"Error: Filename empty\")\n\t\treturn false\n\t}\n\n\treturn ret\n}\n\nfunc invDownload() {\n\tclient, query := newRequest()\n\n\tuid, err := ofxgo.RandomUID()\n\tif err != nil {\n\t\tfmt.Println(\"Error creating uid for transaction:\", err)\n\t\tos.Exit(1)\n\t}\n\n\tstatementRequest := ofxgo.InvStatementRequest{\n\t\tTrnUID: *uid,\n\t\tInvAcctFrom: ofxgo.InvAcct{\n\t\t\tBrokerID: ofxgo.String(brokerID),\n\t\t\tAcctID:   ofxgo.String(acctID),\n\t\t},\n\t\tInclude:        true,\n\t\tIncludeOO:      true,\n\t\tIncludePos:     true,\n\t\tIncludeBalance: true,\n\t\tInclude401K:    true,\n\t\tInclude401KBal: true,\n\t}\n\tquery.InvStmt = append(query.InvStmt, &statementRequest)\n\n\tif dryrun {\n\t\tprintRequest(client, query)\n\t\treturn\n\t}\n\n\tresponse, err := client.RequestNoParse(query)\n\n\tif err != nil {\n\t\tfmt.Println(\"Error requesting account statement:\", err)\n\n\t\tos.Exit(1)\n\t}\n\tdefer response.Body.Close()\n\n\tfile, err := os.Create(filename)\n\tif err != nil {\n\t\tfmt.Println(\"Error creating file to write to:\", err)\n\t\tos.Exit(1)\n\t}\n\tdefer file.Close()\n\n\t_, err = io.Copy(file, response.Body)\n\tif err != nil {\n\t\tfmt.Println(\"Error writing response to file:\", err)\n\t\tos.Exit(1)\n\t}\n}\n"
  },
  {
    "path": "cmd/ofx/invtransactions.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"github.com/aclindsa/ofxgo\"\n\t\"os\"\n)\n\nvar invTransactionsCommand = command{\n\tName:        \"transactions-inv\",\n\tDescription: \"Print investment transactions\",\n\tFlags:       flag.NewFlagSet(\"transactions-inv\", flag.ExitOnError),\n\tCheckFlags:  checkServerFlags,\n\tDo:          invTransactions,\n}\n\nfunc init() {\n\tdefineServerFlags(invTransactionsCommand.Flags)\n\tinvTransactionsCommand.Flags.StringVar(&acctID, \"acctid\", \"\", \"AcctID (from `get-accounts` subcommand)\")\n\tinvTransactionsCommand.Flags.StringVar(&brokerID, \"brokerid\", \"\", \"BrokerID (from `get-accounts` subcommand)\")\n}\n\nfunc invTransactions() {\n\tclient, query := newRequest()\n\n\tuid, err := ofxgo.RandomUID()\n\tif err != nil {\n\t\tfmt.Println(\"Error creating uid for transaction:\", err)\n\t\tos.Exit(1)\n\t}\n\n\tstatementRequest := ofxgo.InvStatementRequest{\n\t\tTrnUID: *uid,\n\t\tInvAcctFrom: ofxgo.InvAcct{\n\t\t\tBrokerID: ofxgo.String(brokerID),\n\t\t\tAcctID:   ofxgo.String(acctID),\n\t\t},\n\t\tInclude:        true,\n\t\tIncludeOO:      true,\n\t\tIncludePos:     true,\n\t\tIncludeBalance: true,\n\t\tInclude401K:    true,\n\t\tInclude401KBal: true,\n\t}\n\tquery.InvStmt = append(query.InvStmt, &statementRequest)\n\n\tif dryrun {\n\t\tprintRequest(client, query)\n\t\treturn\n\t}\n\n\tresponse, err := client.Request(query)\n\tif err != nil {\n\t\tos.Exit(1)\n\t}\n\n\tif response.Signon.Status.Code != 0 {\n\t\tmeaning, _ := response.Signon.Status.CodeMeaning()\n\t\tfmt.Printf(\"Nonzero signon status (%d: %s) with message: %s\\n\", response.Signon.Status.Code, meaning, response.Signon.Status.Message)\n\t\tos.Exit(1)\n\t}\n\n\tif len(response.InvStmt) < 1 {\n\t\tfmt.Println(\"No investment messages received\")\n\t\treturn\n\t}\n\n\tif stmt, ok := response.InvStmt[0].(*ofxgo.InvStatementResponse); ok {\n\t\tavailCash := stmt.InvBal.AvailCash\n\t\tif availCash.IsInt() && availCash.Num().Int64() != 0 {\n\t\t\tfmt.Printf(\"Balance: %s %s (as of %s)\\n\", stmt.InvBal.AvailCash, stmt.CurDef, stmt.DtAsOf)\n\t\t}\n\t\tfor _, banktrans := range stmt.InvTranList.BankTransactions {\n\t\t\tfmt.Printf(\"\\nBank Transactions for %s subaccount:\\n\", banktrans.SubAcctFund)\n\t\t\tfor _, tran := range banktrans.Transactions {\n\t\t\t\tprintTransaction(stmt.CurDef, &tran)\n\t\t\t}\n\t\t}\n\t\tfmt.Printf(\"\\nInvestment Transactions:\\n\")\n\t\tfor _, t := range stmt.InvTranList.InvTransactions {\n\t\t\tfmt.Printf(\"%-14s\", t.TransactionType())\n\t\t\tswitch tran := t.(type) {\n\t\t\tcase ofxgo.BuyDebt:\n\t\t\t\tprintInvBuy(stmt.CurDef, &tran.InvBuy)\n\t\t\tcase ofxgo.BuyMF:\n\t\t\t\tprintInvBuy(stmt.CurDef, &tran.InvBuy)\n\t\t\tcase ofxgo.BuyOpt:\n\t\t\t\tprintInvBuy(stmt.CurDef, &tran.InvBuy)\n\t\t\tcase ofxgo.BuyOther:\n\t\t\t\tprintInvBuy(stmt.CurDef, &tran.InvBuy)\n\t\t\tcase ofxgo.BuyStock:\n\t\t\t\tprintInvBuy(stmt.CurDef, &tran.InvBuy)\n\t\t\tcase ofxgo.ClosureOpt:\n\t\t\t\tprintInvTran(&tran.InvTran)\n\t\t\t\tfmt.Printf(\"%s %s contracts (%d shares each)\\n\", tran.OptAction, tran.Units, tran.ShPerCtrct)\n\t\t\tcase ofxgo.Income:\n\t\t\t\tprintInvTran(&tran.InvTran)\n\t\t\t\tcurrency := stmt.CurDef\n\t\t\t\tif ok, _ := tran.Currency.Valid(); ok {\n\t\t\t\t\tcurrency = tran.Currency.CurSym\n\t\t\t\t}\n\t\t\t\tfmt.Printf(\" %s %s %s (%s %s)\\n\", tran.IncomeType, tran.Total, currency, tran.SecID.UniqueIDType, tran.SecID.UniqueID)\n\t\t\t\t// TODO print ticker instead of CUSIP\n\t\t\tcase ofxgo.InvExpense:\n\t\t\t\tprintInvTran(&tran.InvTran)\n\t\t\t\tcurrency := stmt.CurDef\n\t\t\t\tif ok, _ := tran.Currency.Valid(); ok {\n\t\t\t\t\tcurrency = tran.Currency.CurSym\n\t\t\t\t}\n\t\t\t\tfmt.Printf(\" %s %s (%s %s)\\n\", tran.Total, currency, tran.SecID.UniqueIDType, tran.SecID.UniqueID)\n\t\t\t\t// TODO print ticker instead of CUSIP\n\t\t\tcase ofxgo.JrnlFund:\n\t\t\t\tprintInvTran(&tran.InvTran)\n\t\t\t\tfmt.Printf(\" %s %s (%s -> %s)\\n\", tran.Total, stmt.CurDef, tran.SubAcctFrom, tran.SubAcctTo)\n\t\t\tcase ofxgo.JrnlSec:\n\t\t\t\tprintInvTran(&tran.InvTran)\n\t\t\t\tfmt.Printf(\" %s %s %s (%s -> %s)\\n\", tran.Units, tran.SecID.UniqueIDType, tran.SecID.UniqueID, tran.SubAcctFrom, tran.SubAcctTo)\n\t\t\t\t// TODO print ticker instead of CUSIP\n\t\t\tcase ofxgo.MarginInterest:\n\t\t\t\tprintInvTran(&tran.InvTran)\n\t\t\t\tcurrency := stmt.CurDef\n\t\t\t\tif ok, _ := tran.Currency.Valid(); ok {\n\t\t\t\t\tcurrency = tran.Currency.CurSym\n\t\t\t\t}\n\t\t\t\tfmt.Printf(\" %s %s\\n\", tran.Total, currency)\n\t\t\tcase ofxgo.Reinvest:\n\t\t\t\tprintInvTran(&tran.InvTran)\n\t\t\t\tcurrency := stmt.CurDef\n\t\t\t\tif ok, _ := tran.Currency.Valid(); ok {\n\t\t\t\t\tcurrency = tran.Currency.CurSym\n\t\t\t\t}\n\t\t\t\tfmt.Printf(\" %s (%s %s)@%s %s (Total: %s)\\n\", tran.Units, tran.SecID.UniqueIDType, tran.SecID.UniqueID, tran.UnitPrice, currency, tran.Total)\n\t\t\t\t// TODO print ticker instead of CUSIP\n\t\t\tcase ofxgo.RetOfCap:\n\t\t\t\tprintInvTran(&tran.InvTran)\n\t\t\t\tcurrency := stmt.CurDef\n\t\t\t\tif ok, _ := tran.Currency.Valid(); ok {\n\t\t\t\t\tcurrency = tran.Currency.CurSym\n\t\t\t\t}\n\t\t\t\tfmt.Printf(\" %s %s (%s %s)\\n\", tran.Total, currency, tran.SecID.UniqueIDType, tran.SecID.UniqueID)\n\t\t\t\t// TODO print ticker instead of CUSIP\n\t\t\tcase ofxgo.SellDebt:\n\t\t\t\tprintInvSell(stmt.CurDef, &tran.InvSell)\n\t\t\tcase ofxgo.SellMF:\n\t\t\t\tprintInvSell(stmt.CurDef, &tran.InvSell)\n\t\t\tcase ofxgo.SellOpt:\n\t\t\t\tprintInvSell(stmt.CurDef, &tran.InvSell)\n\t\t\tcase ofxgo.SellOther:\n\t\t\t\tprintInvSell(stmt.CurDef, &tran.InvSell)\n\t\t\tcase ofxgo.SellStock:\n\t\t\t\tprintInvSell(stmt.CurDef, &tran.InvSell)\n\t\t\tcase ofxgo.Split:\n\t\t\t\tprintInvTran(&tran.InvTran)\n\t\t\t\tcurrency := stmt.CurDef\n\t\t\t\tif ok, _ := tran.Currency.Valid(); ok {\n\t\t\t\t\tcurrency = tran.Currency.CurSym\n\t\t\t\t}\n\t\t\t\tfmt.Printf(\" %d/%d %s -> %s shares of %s %s (%s %s for fractional shares)\\n\", tran.Numerator, tran.Denominator, tran.OldUnits, tran.NewUnits, tran.SecID.UniqueIDType, tran.SecID.UniqueID, tran.FracCash, currency)\n\t\t\t\t// TODO print ticker instead of CUSIP\n\t\t\tcase ofxgo.Transfer:\n\t\t\t\tprintInvTran(&tran.InvTran)\n\t\t\t\tfmt.Printf(\" %s (%s %s) %s\\n\", tran.Units, tran.SecID.UniqueIDType, tran.SecID.UniqueID, tran.TferAction)\n\t\t\t\t// TODO print ticker instead of CUSIP\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc printInvTran(it *ofxgo.InvTran) {\n\tfmt.Printf(\"%s\", it.DtTrade)\n}\n\nfunc printInvBuy(defCurrency ofxgo.CurrSymbol, ib *ofxgo.InvBuy) {\n\tprintInvTran(&ib.InvTran)\n\tcurrency := defCurrency\n\tif ok, _ := ib.Currency.Valid(); ok {\n\t\tcurrency = ib.Currency.CurSym\n\t}\n\n\tfmt.Printf(\"%s (%s %s)@%s %s (Total: %s)\\n\", ib.Units, ib.SecID.UniqueIDType, ib.SecID.UniqueID, ib.UnitPrice, currency, ib.Total)\n\t// TODO print ticker instead of CUSIP\n}\n\nfunc printInvSell(defCurrency ofxgo.CurrSymbol, is *ofxgo.InvSell) {\n\tprintInvTran(&is.InvTran)\n\tcurrency := defCurrency\n\tif ok, _ := is.Currency.Valid(); ok {\n\t\tcurrency = is.Currency.CurSym\n\t}\n\n\tfmt.Printf(\" %s (%s %s)@%s %s (Total: %s)\\n\", is.Units, is.SecID.UniqueIDType, is.SecID.UniqueID, is.UnitPrice, currency.String(), is.Total)\n\t// TODO print ticker instead of CUSIP\n}\n"
  },
  {
    "path": "cmd/ofx/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n)\n\nvar commands = []command{\n\tprofileDownloadCommand,\n\tgetAccountsCommand,\n\tdownloadCommand,\n\tccDownloadCommand,\n\tinvDownloadCommand,\n\tbankTransactionsCommand,\n\tccTransactionsCommand,\n\tinvTransactionsCommand,\n\tdetectSettingsCommand,\n}\n\nfunc usage() {\n\tfmt.Println(`The ofxgo command-line client provides a simple interface to\nquery, parse, and display financial data via the OFX specification.\n\nUsage:\n\tofx command [arguments]\n\nThe commands are:`)\n\n\tmaxlen := 0\n\tfor _, cmd := range commands {\n\t\tif len(cmd.Name) > maxlen {\n\t\t\tmaxlen = len(cmd.Name)\n\t\t}\n\t}\n\tformatString := \"    %-\" + strconv.Itoa(maxlen) + \"s    %s\\n\"\n\n\tfor _, cmd := range commands {\n\t\tfmt.Printf(formatString, cmd.Name, cmd.Description)\n\t}\n}\n\nfunc runCmd(c *command) {\n\terr := c.Flags.Parse(os.Args[2:])\n\tif err != nil {\n\t\tfmt.Printf(\"Error parsing flags: %s\\n\", err)\n\t\tc.usage()\n\t\tos.Exit(1)\n\t}\n\n\tif !c.CheckFlags() {\n\t\tfmt.Println()\n\t\tc.usage()\n\t\tos.Exit(1)\n\t}\n\n\tc.Do()\n}\n\nfunc main() {\n\tif len(os.Args) < 2 {\n\t\tfmt.Printf(\"Error: Please supply a sub-command. Usage:\\n\\n\")\n\t\tusage()\n\t\tos.Exit(1)\n\t}\n\tcmdName := os.Args[1]\n\tfor _, cmd := range commands {\n\t\tif cmd.Name == cmdName {\n\t\t\trunCmd(&cmd)\n\t\t\tos.Exit(0)\n\t\t}\n\t}\n\n\tswitch cmdName {\n\tcase \"-h\", \"-help\", \"--help\", \"help\":\n\t\tusage()\n\tdefault:\n\t\tfmt.Println(\"Error: Invalid sub-command. Usage:\")\n\t\tusage()\n\t\tos.Exit(1)\n\t}\n}\n"
  },
  {
    "path": "cmd/ofx/profiledownload.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"github.com/aclindsa/ofxgo\"\n\t\"io\"\n\t\"os\"\n)\n\nvar profileDownloadCommand = command{\n\tName:        \"download-profile\",\n\tDescription: \"Download a FI profile to a file\",\n\tFlags:       flag.NewFlagSet(\"download-profile\", flag.ExitOnError),\n\tCheckFlags:  downloadProfileCheckFlags,\n\tDo:          downloadProfile,\n}\n\nfunc init() {\n\tdefineServerFlags(profileDownloadCommand.Flags)\n\tprofileDownloadCommand.Flags.StringVar(&filename, \"filename\", \"./response.ofx\", \"The file to save to\")\n}\n\nfunc downloadProfileCheckFlags() bool {\n\t// Assume if the user didn't specify username that we should use anonymous\n\t// values for it and password\n\tif len(username) == 0 {\n\t\tusername = \"anonymous00000000000000000000000\"\n\t\tpassword = \"anonymous00000000000000000000000\"\n\t}\n\n\tret := checkServerFlags()\n\n\tif len(filename) == 0 {\n\t\tfmt.Println(\"Error: Filename empty\")\n\t\treturn false\n\t}\n\n\treturn ret\n}\n\nfunc downloadProfile() {\n\tclient, query := newRequest()\n\n\tuid, err := ofxgo.RandomUID()\n\tif err != nil {\n\t\tfmt.Println(\"Error creating uid for transaction:\", err)\n\t\tos.Exit(1)\n\t}\n\n\tprofileRequest := ofxgo.ProfileRequest{\n\t\tTrnUID: *uid,\n\t}\n\n\tquery.Prof = append(query.Prof, &profileRequest)\n\n\tif dryrun {\n\t\tprintRequest(client, query)\n\t\treturn\n\t}\n\n\tresponse, err := client.RequestNoParse(query)\n\tif err != nil {\n\t\tfmt.Println(\"Error requesting FI profile:\", err)\n\t\tos.Exit(1)\n\t}\n\tdefer response.Body.Close()\n\n\tfile, err := os.Create(filename)\n\tif err != nil {\n\t\tfmt.Println(\"Error creating file to write to:\", err)\n\t\tos.Exit(1)\n\t}\n\tdefer file.Close()\n\n\t_, err = io.Copy(file, response.Body)\n\tif err != nil {\n\t\tfmt.Println(\"Error writing response to file:\", err)\n\t\tos.Exit(1)\n\t}\n}\n"
  },
  {
    "path": "cmd/ofx/util.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/aclindsa/ofxgo\"\n\t\"os\"\n)\n\nfunc newRequest() (ofxgo.Client, *ofxgo.Request) {\n\tver, err := ofxgo.NewOfxVersion(ofxVersion)\n\tif err != nil {\n\t\tfmt.Println(\"Error creating new OfxVersion enum:\", err)\n\t\tos.Exit(1)\n\t}\n\tvar client = ofxgo.GetClient(serverURL,\n\t\t&ofxgo.BasicClient{\n\t\t\tAppID:          appID,\n\t\t\tAppVer:         appVer,\n\t\t\tSpecVersion:    ver,\n\t\t\tNoIndent:       noIndentRequests,\n\t\t\tCarriageReturn: carriageReturn,\n\t\t\tUserAgent:      userAgent,\n\t\t})\n\n\tvar query ofxgo.Request\n\tquery.URL = serverURL\n\tquery.Signon.ClientUID = ofxgo.UID(clientUID)\n\tquery.Signon.UserID = ofxgo.String(username)\n\tquery.Signon.UserPass = ofxgo.String(password)\n\tquery.Signon.Org = ofxgo.String(org)\n\tquery.Signon.Fid = ofxgo.String(fid)\n\n\treturn client, &query\n}\n\nfunc printRequest(c ofxgo.Client, r *ofxgo.Request) {\n\tr.SetClientFields(c)\n\n\tb, err := r.Marshal()\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\tfmt.Println(b)\n}\n"
  },
  {
    "path": "common.go",
    "content": "package ofxgo\n\n//go:generate ./generate_constants.py\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/aclindsa/xml\"\n)\n\nfunc writeHeader(b *bytes.Buffer, v ofxVersion, carriageReturn bool) error {\n\t// Write the header appropriate to our version\n\tswitch v {\n\tcase OfxVersion102, OfxVersion103, OfxVersion151, OfxVersion160:\n\t\theader := `OFXHEADER:100\nDATA:OFXSGML\nVERSION:` + v.String() + `\nSECURITY:NONE\nENCODING:USASCII\nCHARSET:1252\nCOMPRESSION:NONE\nOLDFILEUID:NONE\nNEWFILEUID:NONE\n\n`\n\t\tif carriageReturn {\n\t\t\theader = strings.Replace(header, \"\\n\", \"\\r\\n\", -1)\n\t\t}\n\t\tb.WriteString(header)\n\tcase OfxVersion200, OfxVersion201, OfxVersion202, OfxVersion203, OfxVersion210, OfxVersion211, OfxVersion220:\n\t\tb.WriteString(`<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>`)\n\t\tif carriageReturn {\n\t\t\tb.WriteByte('\\r')\n\t\t}\n\t\tb.WriteByte('\\n')\n\t\tb.WriteString(`<?OFX OFXHEADER=\"200\" VERSION=\"` + v.String() + `\" SECURITY=\"NONE\" OLDFILEUID=\"NONE\" NEWFILEUID=\"NONE\"?>`)\n\t\tif carriageReturn {\n\t\t\tb.WriteByte('\\r')\n\t\t}\n\t\tb.WriteByte('\\n')\n\tdefault:\n\t\treturn fmt.Errorf(\"%d is not a valid OFX version string\", v)\n\t}\n\treturn nil\n}\n\n// Message represents an OFX message in a message set. it is used to ease\n// marshalling and unmarshalling.\ntype Message interface {\n\tName() string                           // The name of the OFX transaction wrapper element this represents\n\tValid(version ofxVersion) (bool, error) // Called before a Message is marshaled and after it's unmarshaled to ensure the request or response is valid\n\tType() messageType                      // The message set this message belongs to\n}\n\ntype messageType uint\n\n// These constants are returned by Messages' Type() functions to determine\n// which message set they belong to\nconst (\n\t// Requests\n\tSignonRq messageType = iota\n\tSignupRq\n\tBankRq\n\tCreditCardRq\n\tLoanRq\n\tInvStmtRq\n\tInterXferRq\n\tWireXferRq\n\tBillpayRq\n\tEmailRq\n\tSecListRq\n\tPresDirRq\n\tPresDlvRq\n\tProfRq\n\tImageRq\n\n\t// Responses\n\tSignonRs\n\tSignupRs\n\tBankRs\n\tCreditCardRs\n\tLoanRs\n\tInvStmtRs\n\tInterXferRs\n\tWireXferRs\n\tBillpayRs\n\tEmailRs\n\tSecListRs\n\tPresDirRs\n\tPresDlvRs\n\tProfRs\n\tImageRs\n)\n\nfunc (t messageType) String() string {\n\tswitch t {\n\tcase SignonRq:\n\t\treturn \"SIGNONMSGSRQV1\"\n\tcase SignupRq:\n\t\treturn \"SIGNUPMSGSRQV1\"\n\tcase BankRq:\n\t\treturn \"BANKMSGSRQV1\"\n\tcase CreditCardRq:\n\t\treturn \"CREDITCARDMSGSRQV1\"\n\tcase LoanRq:\n\t\treturn \"LOANMSGSRQV1\"\n\tcase InvStmtRq:\n\t\treturn \"INVSTMTMSGSRQV1\"\n\tcase InterXferRq:\n\t\treturn \"INTERXFERMSGSRQV1\"\n\tcase WireXferRq:\n\t\treturn \"WIREXFERMSGSRQV1\"\n\tcase BillpayRq:\n\t\treturn \"BILLPAYMSGSRQV1\"\n\tcase EmailRq:\n\t\treturn \"EMAILMSGSRQV1\"\n\tcase SecListRq:\n\t\treturn \"SECLISTMSGSRQV1\"\n\tcase PresDirRq:\n\t\treturn \"PRESDIRMSGSRQV1\"\n\tcase PresDlvRq:\n\t\treturn \"PRESDLVMSGSRQV1\"\n\tcase ProfRq:\n\t\treturn \"PROFMSGSRQV1\"\n\tcase ImageRq:\n\t\treturn \"IMAGEMSGSRQV1\"\n\tcase SignonRs:\n\t\treturn \"SIGNONMSGSRSV1\"\n\tcase SignupRs:\n\t\treturn \"SIGNUPMSGSRSV1\"\n\tcase BankRs:\n\t\treturn \"BANKMSGSRSV1\"\n\tcase CreditCardRs:\n\t\treturn \"CREDITCARDMSGSRSV1\"\n\tcase LoanRs:\n\t\treturn \"LOANMSGSRSV1\"\n\tcase InvStmtRs:\n\t\treturn \"INVSTMTMSGSRSV1\"\n\tcase InterXferRs:\n\t\treturn \"INTERXFERMSGSRSV1\"\n\tcase WireXferRs:\n\t\treturn \"WIREXFERMSGSRSV1\"\n\tcase BillpayRs:\n\t\treturn \"BILLPAYMSGSRSV1\"\n\tcase EmailRs:\n\t\treturn \"EMAILMSGSRSV1\"\n\tcase SecListRs:\n\t\treturn \"SECLISTMSGSRSV1\"\n\tcase PresDirRs:\n\t\treturn \"PRESDIRMSGSRSV1\"\n\tcase PresDlvRs:\n\t\treturn \"PRESDLVMSGSRSV1\"\n\tcase ProfRs:\n\t\treturn \"PROFMSGSRSV1\"\n\tcase ImageRs:\n\t\treturn \"IMAGEMSGSRSV1\"\n\t}\n\tpanic(\"Invalid messageType\")\n}\n\n// Map of error codes to their meanings, SEVERITY, and conditions under which\n// OFX servers are expected to return them\nvar statusMeanings = map[Int][3]string{\n\t0:     {\"Success\", \"INFO\", \"The server successfully processed the request.\"},\n\t1:     {\"Client is up-to-date\", \"INFO\", \"Based on the client timestamp, the client has the latest information. The response does not supply any additional information.\"},\n\t2000:  {\"General error\", \"ERROR\", \"Error other than those specified by the remaining error codes. Note: Servers should provide a more specific error whenever possible. Error code 2000 should be reserved for cases in which a more specific code is not available.\"},\n\t2001:  {\"Invalid account\", \"ERROR\", \"\"},\n\t2002:  {\"General account error\", \"ERROR\", \"Account error not specified by the remaining error codes.\"},\n\t2003:  {\"Account not found\", \"ERROR\", \"The specified account number does not correspond to one of the user’s accounts.\"},\n\t2004:  {\"Account closed\", \"ERROR\", \"The specified account number corresponds to an account that has been closed.\"},\n\t2005:  {\"Account not authorized\", \"ERROR\", \"The user is not authorized to perform this action on the account, or the server does not allow this type of action to be performed on the account.\"},\n\t2006:  {\"Source account not found\", \"ERROR\", \"The specified account number does not correspond to one of the user’s accounts.\"},\n\t2007:  {\"Source account closed\", \"ERROR\", \"The specified account number corresponds to an account that has been closed.\"},\n\t2008:  {\"Source account not authorized\", \"ERROR\", \"The user is not authorized to perform this action on the account, or the server does not allow this type of action to be performed on the account.\"},\n\t2009:  {\"Destination account not found\", \"ERROR\", \"The specified account number does not correspond to one of the user’s accounts.\"},\n\t2010:  {\"Destination account closed\", \"ERROR\", \"The specified account number corresponds to an account that has been closed.\"},\n\t2011:  {\"Destination account not authorized\", \"ERROR\", \"The user is not authorized to perform this action on the account, or the server does not allow this type of action to be performed on the account.\"},\n\t2012:  {\"Invalid amount\", \"ERROR\", \"The specified amount is not valid for this action; for example, the user specified a negative payment amount.\"},\n\t2014:  {\"Date too soon\", \"ERROR\", \"The server cannot process the requested action by the date specified by the user.\"},\n\t2015:  {\"Date too far in future\", \"ERROR\", \"The server cannot accept requests for an action that far in the future.\"},\n\t2016:  {\"Transaction already committed\", \"ERROR\", \"Transaction has entered the processing loop and cannot be modified/cancelled using OFX.  The transaction may still be cancelled or modified using other means (for example, a phone call to Customer Service).\"},\n\t2017:  {\"Already canceled\", \"ERROR\", \"The transaction cannot be canceled or modified because it has already been canceled.\"},\n\t2018:  {\"Unknown server ID\", \"ERROR\", \"The specified server ID does not exist or no longer exists.\"},\n\t2019:  {\"Duplicate request\", \"ERROR\", \"A request with this <TRNUID> has already been received and processed.\"},\n\t2020:  {\"Invalid date\", \"ERROR\", \"The specified datetime stamp cannot be parsed; for instance, the datetime stamp specifies 25:00 hours.\"},\n\t2021:  {\"Unsupported version\", \"ERROR\", \"The server does not support the requested version. The version of the message set specified by the client is not supported by this server.\"},\n\t2022:  {\"Invalid TAN\", \"ERROR\", \"The server was unable to validate the TAN sent in the request.\"},\n\t2023:  {\"Unknown FITID\", \"ERROR\", \"The specified FITID/BILLID does not exist or no longer exists.  [BILLID not found (ERROR) in the billing message sets]\"},\n\t2025:  {\"Branch ID missing\", \"ERROR\", \"A <BRANCHID> value must be provided in the <BANKACCTFROM> aggregate for this country system, but this field is missing.\"},\n\t2026:  {\"Bank name doesn’t match bank ID\", \"ERROR\", \"The value of <BANKNAME> in the <EXTBANKACCTTO> aggregate is inconsistent with the value of <BANKID> in the <BANKACCTTO> aggregate.\"},\n\t2027:  {\"Invalid date range\", \"ERROR\", \"Response for non-overlapping dates, date ranges in the future, et cetera.\"},\n\t2028:  {\"Requested element unknown\", \"WARN\", \"One or more elements of the request were not recognized by the server or the server (as noted in the FI Profile) does not support the elements. The server executed the element transactions it understood and supported. For example, the request file included private tags in a <PMTRQ> but the server was able to execute the rest of the request.\"},\n\t3000:  {\"MFA Challenge authentication required\", \"ERROR\", \"User credentials are correct, but further authentication required. Client should send <MFACHALLENGERQ> in next request.\"},\n\t3001:  {\"MFA Challenge information is invalid\", \"ERROR\", \"User or client information sent in MFACHALLENGEA contains invalid information\"},\n\t6500:  {\"<REJECTIFMISSING>Y invalid without <TOKEN>\", \"ERROR\", \"This error code may appear in the <SYNCERROR> element of an <xxxSYNCRS> wrapper (in <PRESDLVMSGSRSV1> and V2 message set responses) or the <CODE> contained in any embedded transaction wrappers within a sync response. The corresponding sync request wrapper included <REJECTIFMISSING>Y with <REFRESH>Y or <TOKENONLY>Y, which is illegal.\"},\n\t6501:  {\"Embedded transactions in request failed to process: Out of date\", \"WARN\", \"<REJECTIFMISSING>Y and embedded transactions appeared in the request sync wrapper and the provided <TOKEN> was out of date. This code should be used in the <SYNCERROR> of the response sync wrapper.\"},\n\t6502:  {\"Unable to process embedded transaction due to out-of-date <TOKEN>\", \"ERROR\", \"Used in response transaction wrapper for embedded transactions when <SYNCERROR>6501 appears in the surrounding sync wrapper.\"},\n\t10000: {\"Stop check in process\", \"INFO\", \"Stop check is already in process.\"},\n\t10500: {\"Too many checks to process\", \"ERROR\", \"The stop-payment request <STPCHKRQ> specifies too many checks.\"},\n\t10501: {\"Invalid payee\", \"ERROR\", \"Payee error not specified by the remaining error codes.\"},\n\t10502: {\"Invalid payee address\", \"ERROR\", \"Some portion of the payee’s address is incorrect or unknown.\"},\n\t10503: {\"Invalid payee account number\", \"ERROR\", \"The account number <PAYACCT> of the requested payee is invalid.\"},\n\t10504: {\"Insufficient funds\", \"ERROR\", \"The server cannot process the request because the specified account does not have enough funds.\"},\n\t10505: {\"Cannot modify element\", \"ERROR\", \"The server does not allow modifications to one or more values in a modification request.\"},\n\t10506: {\"Cannot modify source account\", \"ERROR\", \"Reserved for future use.\"},\n\t10507: {\"Cannot modify destination account\", \"ERROR\", \"Reserved for future use.\"},\n\t10508: {\"Invalid frequency\", \"ERROR\", \"The specified frequency <FREQ> does not match one of the accepted frequencies for recurring transactions.\"},\n\t10509: {\"Model already canceled\", \"ERROR\", \"The server has already canceled the specified recurring model.\"},\n\t10510: {\"Invalid payee ID\", \"ERROR\", \"The specified payee ID does not exist or no longer exists.\"},\n\t10511: {\"Invalid payee city\", \"ERROR\", \"The specified city is incorrect or unknown.\"},\n\t10512: {\"Invalid payee state\", \"ERROR\", \"The specified state is incorrect or unknown.\"},\n\t10513: {\"Invalid payee postal code\", \"ERROR\", \"The specified postal code is incorrect or unknown.\"},\n\t10514: {\"Transaction already processed\", \"ERROR\", \"Transaction has already been sent or date due is past\"},\n\t10515: {\"Payee not modifiable by client\", \"ERROR\", \"The server does not allow clients to change payee information.\"},\n\t10516: {\"Wire beneficiary invalid\", \"ERROR\", \"The specified wire beneficiary does not exist or no longer exists.\"},\n\t10517: {\"Invalid payee name\", \"ERROR\", \"The server does not recognize the specified payee name.\"},\n\t10518: {\"Unknown model ID\", \"ERROR\", \"The specified model ID does not exist or no longer exists.\"},\n\t10519: {\"Invalid payee list ID\", \"ERROR\", \"The specified payee list ID does not exist or no longer exists.\"},\n\t10600: {\"Table type not found\", \"ERROR\", \"The specified table type is not recognized or does not exist.\"},\n\t12250: {\"Investment transaction download not supported\", \"WARN\", \"The server does not support investment transaction download.\"},\n\t12251: {\"Investment position download not supported\", \"WARN\", \"The server does not support investment position download.\"},\n\t12252: {\"Investment positions for specified date not available\", \"WARN\", \"The server does not support investment positions for the specified date.\"},\n\t12253: {\"Investment open order download not supported\", \"WARN\", \"The server does not support open order download.\"},\n\t12254: {\"Investment balances download not supported\", \"WARN\", \"The server does not support investment balances download.\"},\n\t12255: {\"401(k) not available for this account\", \"ERROR\", \"401(k) information requested from a non- 401(k) account.\"},\n\t12500: {\"One or more securities not found\", \"ERROR\", \"The server could not find the requested securities.\"},\n\t13000: {\"User ID & password will be sent out-of-band\", \"INFO\", \"The server will send the user ID and password via postal mail, e-mail, or another means. The accompanying message will provide details.\"},\n\t13500: {\"Unable to enroll user\", \"ERROR\", \"The server could not enroll the user.\"},\n\t13501: {\"User already enrolled\", \"ERROR\", \"The server has already enrolled the user.\"},\n\t13502: {\"Invalid service\", \"ERROR\", \"The server does not support the service <SVC> specified in the service-activation request.\"},\n\t13503: {\"Cannot change user information\", \"ERROR\", \"The server does not support the <CHGUSERINFORQ> request.\"},\n\t13504: {\"<FI> Missing or Invalid in <SONRQ>\", \"ERROR\", \"The FI requires the client to provide the <FI> aggregate in the <SONRQ> request, but either none was provided, or the one provided was invalid.\"},\n\t14500: {\"1099 forms not available\", \"ERROR\", \"1099 forms are not yet available for the tax year requested.\"},\n\t14501: {\"1099 forms not available for user ID\", \"ERROR\", \"This user does not have any 1099 forms available.\"},\n\t14600: {\"W2 forms not available\", \"ERROR\", \"W2 forms are not yet available for the tax year requested.\"},\n\t14601: {\"W2 forms not available for user ID\", \"ERROR\", \"The user does not have any W2 forms available.\"},\n\t14700: {\"1098 forms not available\", \"ERROR\", \"1098 forms are not yet available for the tax year requested.\"},\n\t14701: {\"1098 forms not available for user ID\", \"ERROR\", \"The user does not have any 1098 forms available.\"},\n\t15000: {\"Must change USERPASS\", \"INFO\", \"The user must change his or her <USERPASS> number as part of the next OFX request.\"},\n\t15500: {\"Signon invalid\", \"ERROR\", \"The user cannot signon because he or she entered an invalid user ID or password.\"},\n\t15501: {\"Customer account already in use\", \"ERROR\", \"The server allows only one connection at a time, and another user is already signed on.  Please try again later.\"},\n\t15502: {\"USERPASS lockout\", \"ERROR\", \"The server has received too many failed signon attempts for this user. Please call the FI’s technical support number.\"},\n\t15503: {\"Could not change USERPASS\", \"ERROR\", \"The server does not support the <PINCHRQ> request.\"},\n\t15504: {\"Could not provide random data\", \"ERROR\", \"The server could not generate random data as requested by the <CHALLENGERQ>.\"},\n\t15505: {\"Country system not supported\", \"ERROR\", \"The server does not support the country specified in the <COUNTRY> field of the <SONRQ> aggregate.\"},\n\t15506: {\"Empty signon not supported\", \"ERROR\", \"The server does not support signons not accompanied by some other transaction.\"},\n\t15507: {\"Signon invalid without supporting pin change request\", \"ERROR\", \"The OFX block associated with the signon does not contain a pin change request and should.\"},\n\t15508: {\"Transaction not authorized. \", \"ERROR\", \"Current user is not authorized to perform this action on behalf of the <USERID>.\"},\n\t15510: {\"CLIENTUID error\", \"ERROR\", \"The CLIENTUID sent by the client was incorrect. User must register the Client UID.\"},\n\t15511: {\"MFA error\", \"ERROR\", \"User should contact financial institution.\"},\n\t15512: {\"AUTHTOKEN required\", \"ERROR\", \"User needs to contact financial institution to obtain AUTHTOKEN. Client should send it in the next request.\"},\n\t15513: {\"AUTHTOKEN invalid\", \"ERROR\", \"The AUTHTOKEN sent by the client was invalid.\"},\n\t16500: {\"HTML not allowed\", \"ERROR\", \"The server does not accept HTML formatting in the request.\"},\n\t16501: {\"Unknown mail To:\", \"ERROR\", \"The server was unable to send mail to the specified Internet address.\"},\n\t16502: {\"Invalid URL\", \"ERROR\", \"The server could not parse the URL.\"},\n\t16503: {\"Unable to get URL\", \"ERROR\", \"The server was unable to retrieve the information at this URL (e.g., an HTTP 400 or 500 series error).\"},\n}\n\n// Status represents the status of a Response (both top-level Request objects,\n// and *Response objects)\ntype Status struct {\n\tXMLName  xml.Name `xml:\"STATUS\"`\n\tCode     Int      `xml:\"CODE\"`\n\tSeverity String   `xml:\"SEVERITY\"`\n\tMessage  String   `xml:\"MESSAGE,omitempty\"`\n}\n\n// Valid returns whether the Status is valid according to the OFX spec\nfunc (s *Status) Valid() (bool, error) {\n\tswitch s.Severity {\n\tcase \"INFO\", \"WARN\", \"ERROR\":\n\tdefault:\n\t\treturn false, errors.New(\"Invalid STATUS>SEVERITY\")\n\t}\n\n\tif arr, ok := statusMeanings[s.Code]; ok {\n\t\tif arr[1] != string(s.Severity) {\n\t\t\treturn false, errors.New(\"Unexpected SEVERITY for STATUS>CODE\")\n\t\t}\n\t} else {\n\t\treturn false, errors.New(\"Unknown OFX status code\")\n\t}\n\n\treturn true, nil\n}\n\n// CodeMeaning returns the meaning of the current status Code\nfunc (s *Status) CodeMeaning() (string, error) {\n\tif arr, ok := statusMeanings[s.Code]; ok {\n\t\treturn arr[0], nil\n\t}\n\treturn \"\", errors.New(\"Unknown OFX status code\")\n}\n\n// CodeConditions returns the conditions under which an OFX server is expected\n// to return the current status Code\nfunc (s *Status) CodeConditions() (string, error) {\n\tif arr, ok := statusMeanings[s.Code]; ok {\n\t\treturn arr[2], nil\n\t}\n\treturn \"\", errors.New(\"Unknown OFX status code\")\n}\n\n// BankAcct represents the identifying information for one bank account\ntype BankAcct struct {\n\tXMLName  xml.Name // BANKACCTTO or BANKACCTFROM\n\tBankID   String   `xml:\"BANKID\"`\n\tBranchID String   `xml:\"BRANCHID,omitempty\"` // Unused in USA\n\tAcctID   String   `xml:\"ACCTID\"`\n\tAcctType acctType `xml:\"ACCTTYPE\"`          // One of CHECKING, SAVINGS, MONEYMRKT, CREDITLINE, CD\n\tAcctKey  String   `xml:\"ACCTKEY,omitempty\"` // Unused in USA\n}\n\n// Valid returns whether the BankAcct is valid according to the OFX spec\nfunc (b BankAcct) Valid() (bool, error) {\n\tif len(b.BankID) == 0 {\n\t\treturn false, errors.New(\"BankAcct.BankID empty\")\n\t}\n\tif len(b.AcctID) == 0 {\n\t\treturn false, errors.New(\"BankAcct.AcctID empty\")\n\t}\n\tif !b.AcctType.Valid() {\n\t\treturn false, errors.New(\"Invalid or unspecified BankAcct.AcctType\")\n\t}\n\treturn true, nil\n}\n\n// CCAcct represents the identifying information for one checking account\ntype CCAcct struct {\n\tXMLName xml.Name // CCACCTTO or CCACCTFROM\n\tAcctID  String   `xml:\"ACCTID\"`\n\tAcctKey String   `xml:\"ACCTKEY,omitempty\"` // Unused in USA\n}\n\n// Valid returns whether the CCAcct is valid according to the OFX spec\nfunc (c CCAcct) Valid() (bool, error) {\n\tif len(c.AcctID) == 0 {\n\t\treturn false, errors.New(\"CCAcct.AcctID empty\")\n\t}\n\treturn true, nil\n}\n\n// InvAcct represents the identifying information for one investment account\ntype InvAcct struct {\n\tXMLName  xml.Name // INVACCTTO or INVACCTFROM\n\tBrokerID String   `xml:\"BROKERID\"`\n\tAcctID   String   `xml:\"ACCTID\"`\n}\n\n// Currency represents one ISO-4217 currency. CURRENCY elements signify that\n// the transaction containing this Currency struct is in this currency instead\n// of being converted to the statement's default. ORIGCURRENCY elements signify\n// that the transaction containing this Currency struct was converted to the\n// statement's default from the specified currency.\ntype Currency struct {\n\tXMLName xml.Name   // CURRENCY or ORIGCURRENCY\n\tCurRate Amount     `xml:\"CURRATE\"` // Ratio of statement's currency (CURDEF) to transaction currency (CURSYM)\n\tCurSym  CurrSymbol `xml:\"CURSYM\"`  // ISO-4217 3-character currency identifier\n}\n\n// Valid returns whether the Currency is valid according to the OFX spec\nfunc (c Currency) Valid() (bool, error) {\n\tif c.CurRate.IsInt() && c.CurRate.Num().Int64() == 0 {\n\t\treturn false, errors.New(\"CurRate may not be zero\")\n\t} else if ok, err := c.CurSym.Valid(); !ok {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n"
  },
  {
    "path": "common_test.go",
    "content": "package ofxgo\n\nimport (\n\t\"testing\"\n)\n\nfunc TestStatusValid(t *testing.T) {\n\ts := Status{\n\t\tCode:     0,\n\t\tSeverity: \"INFO\",\n\t\tMessage:  \"Success\",\n\t}\n\tif ok, err := s.Valid(); !ok {\n\t\tt.Fatalf(\"Status unexpectedly invalid: %s\\n\", err)\n\t}\n\n\ts.Severity = \"INVALID\"\n\tif ok, err := s.Valid(); ok || err == nil {\n\t\tt.Fatalf(\"Status unexpectedly valid invalid Severity\\n\")\n\t}\n\n\ts.Severity = \"WARN\"\n\tif ok, err := s.Valid(); ok || err == nil {\n\t\tt.Fatalf(\"Status unexpectedly valid with wrong Severity for Code 0\\n\")\n\t}\n\n\ts.Code = 9\n\tif ok, err := s.Valid(); ok || err == nil {\n\t\tt.Fatalf(\"Status unexpectedly valid with invalid Code\\n\")\n\t}\n}\n\nfunc TestStatusCodeMeaning(t *testing.T) {\n\ts := Status{\n\t\tCode:     15500,\n\t\tSeverity: \"ERROR\",\n\t}\n\tmeaning, err := s.CodeMeaning()\n\tif err != nil {\n\t\tt.Fatalf(\"Status.CodeMeaning unexpectedly failed: %s\\n\", err)\n\t}\n\tif meaning != \"Signon invalid\" {\n\t\tt.Fatalf(\"Unexpected meaning for Code 15500: \\\"%s\\\"\\n\", meaning)\n\t}\n\n\ts.Code = 999\n\tif meaning, err := s.CodeMeaning(); len(meaning) != 0 || err == nil {\n\t\tt.Fatalf(\"Status.CodeMeaning unexpectedly succeeded with invalid Code\\n\")\n\t}\n}\n\nfunc TestStatusCodeConditions(t *testing.T) {\n\ts := Status{\n\t\tCode:     2006,\n\t\tSeverity: \"ERROR\",\n\t}\n\tif conditions, err := s.CodeConditions(); len(conditions) == 0 || err != nil {\n\t\tt.Fatalf(\"Status.CodeConditions unexpectedly failed: %s\\n\", err)\n\t}\n\n\ts.Code = 999\n\tif conditions, err := s.CodeConditions(); len(conditions) != 0 || err == nil {\n\t\tt.Fatalf(\"Status.CodeConditions unexpectedly succeeded with invalid Code\\n\")\n\t}\n}\n"
  },
  {
    "path": "constants.go",
    "content": "package ofxgo\n\n/*\n * Do not edit this file by hand. It is auto-generated by calling `go generate`.\n * To make changes, edit generate_constants.py, re-run `go generate`, and check\n * in the result.\n */\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/aclindsa/xml\"\n)\n\ntype ofxVersion uint\n\n// OfxVersion* constants represent the OFX specification version in use\nconst (\n\tOfxVersion102 ofxVersion = 1 + iota\n\tOfxVersion103\n\tOfxVersion151\n\tOfxVersion160\n\tOfxVersion200\n\tOfxVersion201\n\tOfxVersion202\n\tOfxVersion203\n\tOfxVersion210\n\tOfxVersion211\n\tOfxVersion220\n)\n\nvar ofxVersions = [...]string{\"102\", \"103\", \"151\", \"160\", \"200\", \"201\", \"202\", \"203\", \"210\", \"211\", \"220\"}\n\nfunc (e ofxVersion) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= OfxVersion102 && e <= OfxVersion220\n}\n\nfunc (e ofxVersion) String() string {\n\tif e.Valid() {\n\t\treturn ofxVersions[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid ofxVersion (%d)\", e)\n}\n\nfunc (e *ofxVersion) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range ofxVersions {\n\t\tif s == value {\n\t\t\t*e = ofxVersion(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid OfxVersion: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *ofxVersion) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e ofxVersion) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(ofxVersions[e-1], start)\n\treturn nil\n}\n\n// NewOfxVersion returns returns an 'enum' value of type ofxVersion given its\n// string representation\nfunc NewOfxVersion(s string) (ofxVersion, error) {\n\tvar e ofxVersion\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype acctType uint\n\n// AcctType* constants represent types of bank accounts\nconst (\n\tAcctTypeChecking acctType = 1 + iota\n\tAcctTypeSavings\n\tAcctTypeMoneyMrkt\n\tAcctTypeCreditLine\n\tAcctTypeCD\n)\n\nvar acctTypes = [...]string{\"CHECKING\", \"SAVINGS\", \"MONEYMRKT\", \"CREDITLINE\", \"CD\"}\n\nfunc (e acctType) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= AcctTypeChecking && e <= AcctTypeCD\n}\n\nfunc (e acctType) String() string {\n\tif e.Valid() {\n\t\treturn acctTypes[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid acctType (%d)\", e)\n}\n\nfunc (e *acctType) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range acctTypes {\n\t\tif s == value {\n\t\t\t*e = acctType(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid AcctType: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *acctType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e acctType) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(acctTypes[e-1], start)\n\treturn nil\n}\n\n// NewAcctType returns returns an 'enum' value of type acctType given its\n// string representation\nfunc NewAcctType(s string) (acctType, error) {\n\tvar e acctType\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype trnType uint\n\n// TrnType* constants represent types of transactions. INT, ATM, and POS depend on the signage of the account.\nconst (\n\tTrnTypeCredit trnType = 1 + iota\n\tTrnTypeDebit\n\tTrnTypeInt\n\tTrnTypeDiv\n\tTrnTypeFee\n\tTrnTypeSrvChg\n\tTrnTypeDep\n\tTrnTypeATM\n\tTrnTypePOS\n\tTrnTypeXfer\n\tTrnTypeCheck\n\tTrnTypePayment\n\tTrnTypeCash\n\tTrnTypeDirectDep\n\tTrnTypeDirectDebit\n\tTrnTypeRepeatPmt\n\tTrnTypeHold\n\tTrnTypeOther\n)\n\nvar trnTypes = [...]string{\"CREDIT\", \"DEBIT\", \"INT\", \"DIV\", \"FEE\", \"SRVCHG\", \"DEP\", \"ATM\", \"POS\", \"XFER\", \"CHECK\", \"PAYMENT\", \"CASH\", \"DIRECTDEP\", \"DIRECTDEBIT\", \"REPEATPMT\", \"HOLD\", \"OTHER\"}\n\nfunc (e trnType) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= TrnTypeCredit && e <= TrnTypeOther\n}\n\nfunc (e trnType) String() string {\n\tif e.Valid() {\n\t\treturn trnTypes[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid trnType (%d)\", e)\n}\n\nfunc (e *trnType) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range trnTypes {\n\t\tif s == value {\n\t\t\t*e = trnType(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid TrnType: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *trnType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e trnType) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(trnTypes[e-1], start)\n\treturn nil\n}\n\n// NewTrnType returns returns an 'enum' value of type trnType given its\n// string representation\nfunc NewTrnType(s string) (trnType, error) {\n\tvar e trnType\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype imageType uint\n\n// ImageType* constants represent what this image contains\nconst (\n\tImageTypeStatement imageType = 1 + iota\n\tImageTypeTransaction\n\tImageTypeTax\n)\n\nvar imageTypes = [...]string{\"STATEMENT\", \"TRANSACTION\", \"TAX\"}\n\nfunc (e imageType) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= ImageTypeStatement && e <= ImageTypeTax\n}\n\nfunc (e imageType) String() string {\n\tif e.Valid() {\n\t\treturn imageTypes[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid imageType (%d)\", e)\n}\n\nfunc (e *imageType) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range imageTypes {\n\t\tif s == value {\n\t\t\t*e = imageType(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid ImageType: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *imageType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e imageType) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(imageTypes[e-1], start)\n\treturn nil\n}\n\n// NewImageType returns returns an 'enum' value of type imageType given its\n// string representation\nfunc NewImageType(s string) (imageType, error) {\n\tvar e imageType\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype imageRefType uint\n\n// ImageRefType* constants represent the type of reference to the image\nconst (\n\tImageRefTypeOpaque imageRefType = 1 + iota\n\tImageRefTypeURL\n\tImageRefTypeFormURL\n)\n\nvar imageRefTypes = [...]string{\"OPAQUE\", \"URL\", \"FORMURL\"}\n\nfunc (e imageRefType) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= ImageRefTypeOpaque && e <= ImageRefTypeFormURL\n}\n\nfunc (e imageRefType) String() string {\n\tif e.Valid() {\n\t\treturn imageRefTypes[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid imageRefType (%d)\", e)\n}\n\nfunc (e *imageRefType) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range imageRefTypes {\n\t\tif s == value {\n\t\t\t*e = imageRefType(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid ImageRefType: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *imageRefType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e imageRefType) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(imageRefTypes[e-1], start)\n\treturn nil\n}\n\n// NewImageRefType returns returns an 'enum' value of type imageRefType given its\n// string representation\nfunc NewImageRefType(s string) (imageRefType, error) {\n\tvar e imageRefType\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype checkSup uint\n\n// CheckSup* constants represent what portions of the check this image contains\nconst (\n\tCheckSupFrontOnly checkSup = 1 + iota\n\tCheckSupBackOnly\n\tCheckSupFrontAndBack\n)\n\nvar checkSups = [...]string{\"FRONTONLY\", \"BACKONLY\", \"FRONTANDBACK\"}\n\nfunc (e checkSup) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= CheckSupFrontOnly && e <= CheckSupFrontAndBack\n}\n\nfunc (e checkSup) String() string {\n\tif e.Valid() {\n\t\treturn checkSups[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid checkSup (%d)\", e)\n}\n\nfunc (e *checkSup) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range checkSups {\n\t\tif s == value {\n\t\t\t*e = checkSup(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid CheckSup: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *checkSup) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e checkSup) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(checkSups[e-1], start)\n\treturn nil\n}\n\n// NewCheckSup returns returns an 'enum' value of type checkSup given its\n// string representation\nfunc NewCheckSup(s string) (checkSup, error) {\n\tvar e checkSup\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype correctAction uint\n\n// CorrectAction* constants represent whether this transaction correction replaces or deletes the transaction matching its CORRECTFITID\nconst (\n\tCorrectActionDelete correctAction = 1 + iota\n\tCorrectActionReplace\n)\n\nvar correctActions = [...]string{\"DELETE\", \"REPLACE\"}\n\nfunc (e correctAction) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= CorrectActionDelete && e <= CorrectActionReplace\n}\n\nfunc (e correctAction) String() string {\n\tif e.Valid() {\n\t\treturn correctActions[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid correctAction (%d)\", e)\n}\n\nfunc (e *correctAction) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range correctActions {\n\t\tif s == value {\n\t\t\t*e = correctAction(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid CorrectAction: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *correctAction) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e correctAction) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(correctActions[e-1], start)\n\treturn nil\n}\n\n// NewCorrectAction returns returns an 'enum' value of type correctAction given its\n// string representation\nfunc NewCorrectAction(s string) (correctAction, error) {\n\tvar e correctAction\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype balType uint\n\n// BalType* constants represent how this BAL's VALUE field should be interpreted\nconst (\n\tBalTypeDollar balType = 1 + iota\n\tBalTypePercent\n\tBalTypeNumber\n)\n\nvar balTypes = [...]string{\"DOLLAR\", \"PERCENT\", \"NUMBER\"}\n\nfunc (e balType) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= BalTypeDollar && e <= BalTypeNumber\n}\n\nfunc (e balType) String() string {\n\tif e.Valid() {\n\t\treturn balTypes[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid balType (%d)\", e)\n}\n\nfunc (e *balType) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range balTypes {\n\t\tif s == value {\n\t\t\t*e = balType(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid BalType: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *balType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e balType) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(balTypes[e-1], start)\n\treturn nil\n}\n\n// NewBalType returns returns an 'enum' value of type balType given its\n// string representation\nfunc NewBalType(s string) (balType, error) {\n\tvar e balType\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype inv401kSource uint\n\n// Inv401kSource* constants represent the source of money used for this security in a 401(k) account. Default if not present is OTHERNONVEST. The following cash source types are subject to vesting: MATCH, PROFITSHARING, and OTHERVEST.\nconst (\n\tInv401kSourcePreTax inv401kSource = 1 + iota\n\tInv401kSourceAfterTax\n\tInv401kSourceMatch\n\tInv401kSourceProfitSharing\n\tInv401kSourceRollover\n\tInv401kSourceOtherVest\n\tInv401kSourceOtherNonVest\n)\n\nvar inv401kSources = [...]string{\"PRETAX\", \"AFTERTAX\", \"MATCH\", \"PROFITSHARING\", \"ROLLOVER\", \"OTHERVEST\", \"OTHERNONVEST\"}\n\nfunc (e inv401kSource) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= Inv401kSourcePreTax && e <= Inv401kSourceOtherNonVest\n}\n\nfunc (e inv401kSource) String() string {\n\tif e.Valid() {\n\t\treturn inv401kSources[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid inv401kSource (%d)\", e)\n}\n\nfunc (e *inv401kSource) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range inv401kSources {\n\t\tif s == value {\n\t\t\t*e = inv401kSource(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid Inv401kSource: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *inv401kSource) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e inv401kSource) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(inv401kSources[e-1], start)\n\treturn nil\n}\n\n// NewInv401kSource returns returns an 'enum' value of type inv401kSource given its\n// string representation\nfunc NewInv401kSource(s string) (inv401kSource, error) {\n\tvar e inv401kSource\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype subAcctType uint\n\n// SubAcctType* constants represent the sub-account type for a source and/or destination of a transaction. Used in fields named SubAcctFrom, SubAcctTo, SubAcctSec, SubAcctFund, HeldInAcct.\nconst (\n\tSubAcctTypeCash subAcctType = 1 + iota\n\tSubAcctTypeMargin\n\tSubAcctTypeShort\n\tSubAcctTypeOther\n)\n\nvar subAcctTypes = [...]string{\"CASH\", \"MARGIN\", \"SHORT\", \"OTHER\"}\n\nfunc (e subAcctType) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= SubAcctTypeCash && e <= SubAcctTypeOther\n}\n\nfunc (e subAcctType) String() string {\n\tif e.Valid() {\n\t\treturn subAcctTypes[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid subAcctType (%d)\", e)\n}\n\nfunc (e *subAcctType) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range subAcctTypes {\n\t\tif s == value {\n\t\t\t*e = subAcctType(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid SubAcctType: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *subAcctType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e subAcctType) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(subAcctTypes[e-1], start)\n\treturn nil\n}\n\n// NewSubAcctType returns returns an 'enum' value of type subAcctType given its\n// string representation\nfunc NewSubAcctType(s string) (subAcctType, error) {\n\tvar e subAcctType\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype buyType uint\n\n// BuyType* constants represent types of purchases\nconst (\n\tBuyTypeBuy buyType = 1 + iota\n\tBuyTypeBuyToCover\n)\n\nvar buyTypes = [...]string{\"BUY\", \"BUYTOCOVER\"}\n\nfunc (e buyType) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= BuyTypeBuy && e <= BuyTypeBuyToCover\n}\n\nfunc (e buyType) String() string {\n\tif e.Valid() {\n\t\treturn buyTypes[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid buyType (%d)\", e)\n}\n\nfunc (e *buyType) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range buyTypes {\n\t\tif s == value {\n\t\t\t*e = buyType(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid BuyType: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *buyType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e buyType) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(buyTypes[e-1], start)\n\treturn nil\n}\n\n// NewBuyType returns returns an 'enum' value of type buyType given its\n// string representation\nfunc NewBuyType(s string) (buyType, error) {\n\tvar e buyType\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype optAction uint\n\n// OptAction* constants represent types of actions for options\nconst (\n\tOptActionExercise optAction = 1 + iota\n\tOptActionAssign\n\tOptActionExpire\n)\n\nvar optActions = [...]string{\"EXERCISE\", \"ASSIGN\", \"EXPIRE\"}\n\nfunc (e optAction) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= OptActionExercise && e <= OptActionExpire\n}\n\nfunc (e optAction) String() string {\n\tif e.Valid() {\n\t\treturn optActions[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid optAction (%d)\", e)\n}\n\nfunc (e *optAction) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range optActions {\n\t\tif s == value {\n\t\t\t*e = optAction(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid OptAction: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *optAction) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e optAction) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(optActions[e-1], start)\n\treturn nil\n}\n\n// NewOptAction returns returns an 'enum' value of type optAction given its\n// string representation\nfunc NewOptAction(s string) (optAction, error) {\n\tvar e optAction\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype tferAction uint\n\n// TferAction* constants represent whether the transfer is into or out of this account\nconst (\n\tTferActionIn tferAction = 1 + iota\n\tTferActionOut\n)\n\nvar tferActions = [...]string{\"IN\", \"OUT\"}\n\nfunc (e tferAction) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= TferActionIn && e <= TferActionOut\n}\n\nfunc (e tferAction) String() string {\n\tif e.Valid() {\n\t\treturn tferActions[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid tferAction (%d)\", e)\n}\n\nfunc (e *tferAction) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range tferActions {\n\t\tif s == value {\n\t\t\t*e = tferAction(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid TferAction: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *tferAction) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e tferAction) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(tferActions[e-1], start)\n\treturn nil\n}\n\n// NewTferAction returns returns an 'enum' value of type tferAction given its\n// string representation\nfunc NewTferAction(s string) (tferAction, error) {\n\tvar e tferAction\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype posType uint\n\n// PosType* constants represent position type\nconst (\n\tPosTypeLong posType = 1 + iota\n\tPosTypeShort\n)\n\nvar posTypes = [...]string{\"LONG\", \"SHORT\"}\n\nfunc (e posType) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= PosTypeLong && e <= PosTypeShort\n}\n\nfunc (e posType) String() string {\n\tif e.Valid() {\n\t\treturn posTypes[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid posType (%d)\", e)\n}\n\nfunc (e *posType) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range posTypes {\n\t\tif s == value {\n\t\t\t*e = posType(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid PosType: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *posType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e posType) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(posTypes[e-1], start)\n\treturn nil\n}\n\n// NewPosType returns returns an 'enum' value of type posType given its\n// string representation\nfunc NewPosType(s string) (posType, error) {\n\tvar e posType\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype secured uint\n\n// Secured* constants represent how an option is secured\nconst (\n\tSecuredNaked secured = 1 + iota\n\tSecuredCovered\n)\n\nvar secureds = [...]string{\"NAKED\", \"COVERED\"}\n\nfunc (e secured) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= SecuredNaked && e <= SecuredCovered\n}\n\nfunc (e secured) String() string {\n\tif e.Valid() {\n\t\treturn secureds[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid secured (%d)\", e)\n}\n\nfunc (e *secured) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range secureds {\n\t\tif s == value {\n\t\t\t*e = secured(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid Secured: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *secured) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e secured) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(secureds[e-1], start)\n\treturn nil\n}\n\n// NewSecured returns returns an 'enum' value of type secured given its\n// string representation\nfunc NewSecured(s string) (secured, error) {\n\tvar e secured\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype duration uint\n\n// Duration* constants represent how long the investment order is good for\nconst (\n\tDurationDay duration = 1 + iota\n\tDurationGoodTilCancel\n\tDurationImmediate\n)\n\nvar durations = [...]string{\"DAY\", \"GOODTILCANCEL\", \"IMMEDIATE\"}\n\nfunc (e duration) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= DurationDay && e <= DurationImmediate\n}\n\nfunc (e duration) String() string {\n\tif e.Valid() {\n\t\treturn durations[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid duration (%d)\", e)\n}\n\nfunc (e *duration) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range durations {\n\t\tif s == value {\n\t\t\t*e = duration(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid Duration: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *duration) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e duration) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(durations[e-1], start)\n\treturn nil\n}\n\n// NewDuration returns returns an 'enum' value of type duration given its\n// string representation\nfunc NewDuration(s string) (duration, error) {\n\tvar e duration\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype restriction uint\n\n// Restriction* constants represent a special restriction on an investment order\nconst (\n\tRestrictionAllOrNone restriction = 1 + iota\n\tRestrictionMinUnits\n\tRestrictionNone\n)\n\nvar restrictions = [...]string{\"ALLORNONE\", \"MINUNITS\", \"NONE\"}\n\nfunc (e restriction) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= RestrictionAllOrNone && e <= RestrictionNone\n}\n\nfunc (e restriction) String() string {\n\tif e.Valid() {\n\t\treturn restrictions[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid restriction (%d)\", e)\n}\n\nfunc (e *restriction) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range restrictions {\n\t\tif s == value {\n\t\t\t*e = restriction(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid Restriction: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *restriction) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e restriction) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(restrictions[e-1], start)\n\treturn nil\n}\n\n// NewRestriction returns returns an 'enum' value of type restriction given its\n// string representation\nfunc NewRestriction(s string) (restriction, error) {\n\tvar e restriction\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype unitType uint\n\n// UnitType* constants represent type of the UNITS value\nconst (\n\tUnitTypeShares unitType = 1 + iota\n\tUnitTypeCurrency\n)\n\nvar unitTypes = [...]string{\"SHARES\", \"CURRENCY\"}\n\nfunc (e unitType) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= UnitTypeShares && e <= UnitTypeCurrency\n}\n\nfunc (e unitType) String() string {\n\tif e.Valid() {\n\t\treturn unitTypes[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid unitType (%d)\", e)\n}\n\nfunc (e *unitType) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range unitTypes {\n\t\tif s == value {\n\t\t\t*e = unitType(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid UnitType: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *unitType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e unitType) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(unitTypes[e-1], start)\n\treturn nil\n}\n\n// NewUnitType returns returns an 'enum' value of type unitType given its\n// string representation\nfunc NewUnitType(s string) (unitType, error) {\n\tvar e unitType\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype optBuyType uint\n\n// OptBuyType* constants represent types of purchases for options\nconst (\n\tOptBuyTypeBuyToOpen optBuyType = 1 + iota\n\tOptBuyTypeBuyToClose\n)\n\nvar optBuyTypes = [...]string{\"BUYTOOPEN\", \"BUYTOCLOSE\"}\n\nfunc (e optBuyType) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= OptBuyTypeBuyToOpen && e <= OptBuyTypeBuyToClose\n}\n\nfunc (e optBuyType) String() string {\n\tif e.Valid() {\n\t\treturn optBuyTypes[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid optBuyType (%d)\", e)\n}\n\nfunc (e *optBuyType) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range optBuyTypes {\n\t\tif s == value {\n\t\t\t*e = optBuyType(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid OptBuyType: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *optBuyType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e optBuyType) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(optBuyTypes[e-1], start)\n\treturn nil\n}\n\n// NewOptBuyType returns returns an 'enum' value of type optBuyType given its\n// string representation\nfunc NewOptBuyType(s string) (optBuyType, error) {\n\tvar e optBuyType\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype sellType uint\n\n// SellType* constants represent types of sales\nconst (\n\tSellTypeSell sellType = 1 + iota\n\tSellTypeSellShort\n)\n\nvar sellTypes = [...]string{\"SELL\", \"SELLSHORT\"}\n\nfunc (e sellType) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= SellTypeSell && e <= SellTypeSellShort\n}\n\nfunc (e sellType) String() string {\n\tif e.Valid() {\n\t\treturn sellTypes[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid sellType (%d)\", e)\n}\n\nfunc (e *sellType) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range sellTypes {\n\t\tif s == value {\n\t\t\t*e = sellType(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid SellType: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *sellType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e sellType) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(sellTypes[e-1], start)\n\treturn nil\n}\n\n// NewSellType returns returns an 'enum' value of type sellType given its\n// string representation\nfunc NewSellType(s string) (sellType, error) {\n\tvar e sellType\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype loanPmtFreq uint\n\n// LoanPmtFreq* constants represent the frequency of loan payments\nconst (\n\tLoanPmtFreqWeekly loanPmtFreq = 1 + iota\n\tLoanPmtFreqBiweekly\n\tLoanPmtFreqTwiceMonthly\n\tLoanPmtFreqMonthly\n\tLoanPmtFreqFourWeeks\n\tLoanPmtFreqBiMonthly\n\tLoanPmtFreqQuarterly\n\tLoanPmtFreqSemiannually\n\tLoanPmtFreqAnnually\n\tLoanPmtFreqOther\n)\n\nvar loanPmtFreqs = [...]string{\"WEEKLY\", \"BIWEEKLY\", \"TWICEMONTHLY\", \"MONTHLY\", \"FOURWEEKS\", \"BIMONTHLY\", \"QUARTERLY\", \"SEMIANNUALLY\", \"ANNUALLY\", \"OTHER\"}\n\nfunc (e loanPmtFreq) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= LoanPmtFreqWeekly && e <= LoanPmtFreqOther\n}\n\nfunc (e loanPmtFreq) String() string {\n\tif e.Valid() {\n\t\treturn loanPmtFreqs[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid loanPmtFreq (%d)\", e)\n}\n\nfunc (e *loanPmtFreq) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range loanPmtFreqs {\n\t\tif s == value {\n\t\t\t*e = loanPmtFreq(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid LoanPmtFreq: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *loanPmtFreq) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e loanPmtFreq) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(loanPmtFreqs[e-1], start)\n\treturn nil\n}\n\n// NewLoanPmtFreq returns returns an 'enum' value of type loanPmtFreq given its\n// string representation\nfunc NewLoanPmtFreq(s string) (loanPmtFreq, error) {\n\tvar e loanPmtFreq\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype incomeType uint\n\n// IncomeType* constants represent types of investment income\nconst (\n\tIncomeTypeCGLong incomeType = 1 + iota\n\tIncomeTypeCGShort\n\tIncomeTypeDiv\n\tIncomeTypeInterest\n\tIncomeTypeMisc\n)\n\nvar incomeTypes = [...]string{\"CGLONG\", \"CGSHORT\", \"DIV\", \"INTEREST\", \"MISC\"}\n\nfunc (e incomeType) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= IncomeTypeCGLong && e <= IncomeTypeMisc\n}\n\nfunc (e incomeType) String() string {\n\tif e.Valid() {\n\t\treturn incomeTypes[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid incomeType (%d)\", e)\n}\n\nfunc (e *incomeType) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range incomeTypes {\n\t\tif s == value {\n\t\t\t*e = incomeType(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid IncomeType: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *incomeType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e incomeType) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(incomeTypes[e-1], start)\n\treturn nil\n}\n\n// NewIncomeType returns returns an 'enum' value of type incomeType given its\n// string representation\nfunc NewIncomeType(s string) (incomeType, error) {\n\tvar e incomeType\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype sellReason uint\n\n// SellReason* constants represent the reason the sell of a debt security was generated: CALL (the debt was called), SELL (the debt was sold), MATURITY (the debt reached maturity)\nconst (\n\tSellReasonCall sellReason = 1 + iota\n\tSellReasonSell\n\tSellReasonMaturity\n)\n\nvar sellReasons = [...]string{\"CALL\", \"SELL\", \"MATURITY\"}\n\nfunc (e sellReason) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= SellReasonCall && e <= SellReasonMaturity\n}\n\nfunc (e sellReason) String() string {\n\tif e.Valid() {\n\t\treturn sellReasons[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid sellReason (%d)\", e)\n}\n\nfunc (e *sellReason) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range sellReasons {\n\t\tif s == value {\n\t\t\t*e = sellReason(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid SellReason: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *sellReason) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e sellReason) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(sellReasons[e-1], start)\n\treturn nil\n}\n\n// NewSellReason returns returns an 'enum' value of type sellReason given its\n// string representation\nfunc NewSellReason(s string) (sellReason, error) {\n\tvar e sellReason\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype optSellType uint\n\n// OptSellType* constants represent types of sales for options\nconst (\n\tOptSellTypeSellToClose optSellType = 1 + iota\n\tOptSellTypeSellToOpen\n)\n\nvar optSellTypes = [...]string{\"SELLTOCLOSE\", \"SELLTOOPEN\"}\n\nfunc (e optSellType) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= OptSellTypeSellToClose && e <= OptSellTypeSellToOpen\n}\n\nfunc (e optSellType) String() string {\n\tif e.Valid() {\n\t\treturn optSellTypes[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid optSellType (%d)\", e)\n}\n\nfunc (e *optSellType) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range optSellTypes {\n\t\tif s == value {\n\t\t\t*e = optSellType(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid OptSellType: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *optSellType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e optSellType) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(optSellTypes[e-1], start)\n\treturn nil\n}\n\n// NewOptSellType returns returns an 'enum' value of type optSellType given its\n// string representation\nfunc NewOptSellType(s string) (optSellType, error) {\n\tvar e optSellType\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype relType uint\n\n// RelType* constants represent related option transaction types\nconst (\n\tRelTypeSpread relType = 1 + iota\n\tRelTypeStraddle\n\tRelTypeNone\n\tRelTypeOther\n)\n\nvar relTypes = [...]string{\"SPREAD\", \"STRADDLE\", \"NONE\", \"OTHER\"}\n\nfunc (e relType) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= RelTypeSpread && e <= RelTypeOther\n}\n\nfunc (e relType) String() string {\n\tif e.Valid() {\n\t\treturn relTypes[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid relType (%d)\", e)\n}\n\nfunc (e *relType) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range relTypes {\n\t\tif s == value {\n\t\t\t*e = relType(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid RelType: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *relType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e relType) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(relTypes[e-1], start)\n\treturn nil\n}\n\n// NewRelType returns returns an 'enum' value of type relType given its\n// string representation\nfunc NewRelType(s string) (relType, error) {\n\tvar e relType\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype charType uint\n\n// CharType* constants represent types of characters allowed in password\nconst (\n\tCharTypeAlphaOnly charType = 1 + iota\n\tCharTypeNumericOnly\n\tCharTypeAlphaOrNumeric\n\tCharTypeAlphaAndNumeric\n)\n\nvar charTypes = [...]string{\"ALPHAONLY\", \"NUMERICONLY\", \"ALPHAORNUMERIC\", \"ALPHAANDNUMERIC\"}\n\nfunc (e charType) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= CharTypeAlphaOnly && e <= CharTypeAlphaAndNumeric\n}\n\nfunc (e charType) String() string {\n\tif e.Valid() {\n\t\treturn charTypes[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid charType (%d)\", e)\n}\n\nfunc (e *charType) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range charTypes {\n\t\tif s == value {\n\t\t\t*e = charType(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid CharType: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *charType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e charType) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(charTypes[e-1], start)\n\treturn nil\n}\n\n// NewCharType returns returns an 'enum' value of type charType given its\n// string representation\nfunc NewCharType(s string) (charType, error) {\n\tvar e charType\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype syncMode uint\n\n// SyncMode* constants represent data synchronization mode supported (see OFX spec for more details)\nconst (\n\tSyncModeFull syncMode = 1 + iota\n\tSyncModeLite\n)\n\nvar syncModes = [...]string{\"FULL\", \"LITE\"}\n\nfunc (e syncMode) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= SyncModeFull && e <= SyncModeLite\n}\n\nfunc (e syncMode) String() string {\n\tif e.Valid() {\n\t\treturn syncModes[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid syncMode (%d)\", e)\n}\n\nfunc (e *syncMode) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range syncModes {\n\t\tif s == value {\n\t\t\t*e = syncMode(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid SyncMode: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *syncMode) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e syncMode) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(syncModes[e-1], start)\n\treturn nil\n}\n\n// NewSyncMode returns returns an 'enum' value of type syncMode given its\n// string representation\nfunc NewSyncMode(s string) (syncMode, error) {\n\tvar e syncMode\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype ofxSec uint\n\n// OfxSec* constants represent the type of application-level security required for the message set\nconst (\n\tOfxSecNone ofxSec = 1 + iota\n\tOfxSecType1\n)\n\nvar ofxSecs = [...]string{\"NONE\", \"TYPE 1\"}\n\nfunc (e ofxSec) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= OfxSecNone && e <= OfxSecType1\n}\n\nfunc (e ofxSec) String() string {\n\tif e.Valid() {\n\t\treturn ofxSecs[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid ofxSec (%d)\", e)\n}\n\nfunc (e *ofxSec) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range ofxSecs {\n\t\tif s == value {\n\t\t\t*e = ofxSec(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid OfxSec: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *ofxSec) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e ofxSec) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(ofxSecs[e-1], start)\n\treturn nil\n}\n\n// NewOfxSec returns returns an 'enum' value of type ofxSec given its\n// string representation\nfunc NewOfxSec(s string) (ofxSec, error) {\n\tvar e ofxSec\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype debtType uint\n\n// DebtType* constants represent debt type\nconst (\n\tDebtTypeCoupon debtType = 1 + iota\n\tDebtTypeZero\n)\n\nvar debtTypes = [...]string{\"COUPON\", \"ZERO\"}\n\nfunc (e debtType) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= DebtTypeCoupon && e <= DebtTypeZero\n}\n\nfunc (e debtType) String() string {\n\tif e.Valid() {\n\t\treturn debtTypes[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid debtType (%d)\", e)\n}\n\nfunc (e *debtType) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range debtTypes {\n\t\tif s == value {\n\t\t\t*e = debtType(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid DebtType: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *debtType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e debtType) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(debtTypes[e-1], start)\n\treturn nil\n}\n\n// NewDebtType returns returns an 'enum' value of type debtType given its\n// string representation\nfunc NewDebtType(s string) (debtType, error) {\n\tvar e debtType\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype debtClass uint\n\n// DebtClass* constants represent the class of debt\nconst (\n\tDebtClassTreasury debtClass = 1 + iota\n\tDebtClassMunicipal\n\tDebtClassCorporate\n\tDebtClassOther\n)\n\nvar debtClasss = [...]string{\"TREASURY\", \"MUNICIPAL\", \"CORPORATE\", \"OTHER\"}\n\nfunc (e debtClass) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= DebtClassTreasury && e <= DebtClassOther\n}\n\nfunc (e debtClass) String() string {\n\tif e.Valid() {\n\t\treturn debtClasss[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid debtClass (%d)\", e)\n}\n\nfunc (e *debtClass) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range debtClasss {\n\t\tif s == value {\n\t\t\t*e = debtClass(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid DebtClass: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *debtClass) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e debtClass) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(debtClasss[e-1], start)\n\treturn nil\n}\n\n// NewDebtClass returns returns an 'enum' value of type debtClass given its\n// string representation\nfunc NewDebtClass(s string) (debtClass, error) {\n\tvar e debtClass\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype couponFreq uint\n\n// CouponFreq* constants represent when debt coupons mature\nconst (\n\tCouponFreqMonthly couponFreq = 1 + iota\n\tCouponFreqQuarterly\n\tCouponFreqSemiannual\n\tCouponFreqAnnual\n\tCouponFreqOther\n)\n\nvar couponFreqs = [...]string{\"MONTHLY\", \"QUARTERLY\", \"SEMIANNUAL\", \"ANNUAL\", \"OTHER\"}\n\nfunc (e couponFreq) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= CouponFreqMonthly && e <= CouponFreqOther\n}\n\nfunc (e couponFreq) String() string {\n\tif e.Valid() {\n\t\treturn couponFreqs[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid couponFreq (%d)\", e)\n}\n\nfunc (e *couponFreq) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range couponFreqs {\n\t\tif s == value {\n\t\t\t*e = couponFreq(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid CouponFreq: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *couponFreq) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e couponFreq) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(couponFreqs[e-1], start)\n\treturn nil\n}\n\n// NewCouponFreq returns returns an 'enum' value of type couponFreq given its\n// string representation\nfunc NewCouponFreq(s string) (couponFreq, error) {\n\tvar e couponFreq\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype callType uint\n\n// CallType* constants represent type of next call (for a debt)\nconst (\n\tCallTypeCall callType = 1 + iota\n\tCallTypePut\n\tCallTypePrefund\n\tCallTypeMaturity\n)\n\nvar callTypes = [...]string{\"CALL\", \"PUT\", \"PREFUND\", \"MATURITY\"}\n\nfunc (e callType) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= CallTypeCall && e <= CallTypeMaturity\n}\n\nfunc (e callType) String() string {\n\tif e.Valid() {\n\t\treturn callTypes[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid callType (%d)\", e)\n}\n\nfunc (e *callType) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range callTypes {\n\t\tif s == value {\n\t\t\t*e = callType(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid CallType: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *callType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e callType) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(callTypes[e-1], start)\n\treturn nil\n}\n\n// NewCallType returns returns an 'enum' value of type callType given its\n// string representation\nfunc NewCallType(s string) (callType, error) {\n\tvar e callType\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype assetClass uint\n\n// AssetClass* constants represent type of asset classes\nconst (\n\tAssetClassDomesticBond assetClass = 1 + iota\n\tAssetClassIntlBond\n\tAssetClassLargeStock\n\tAssetClassSmallStock\n\tAssetClassIntlStock\n\tAssetClassMoneyMrkt\n\tAssetClassOther\n)\n\nvar assetClasss = [...]string{\"DOMESTICBOND\", \"INTLBOND\", \"LARGESTOCK\", \"SMALLSTOCK\", \"INTLSTOCK\", \"MONEYMRKT\", \"OTHER\"}\n\nfunc (e assetClass) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= AssetClassDomesticBond && e <= AssetClassOther\n}\n\nfunc (e assetClass) String() string {\n\tif e.Valid() {\n\t\treturn assetClasss[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid assetClass (%d)\", e)\n}\n\nfunc (e *assetClass) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range assetClasss {\n\t\tif s == value {\n\t\t\t*e = assetClass(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid AssetClass: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *assetClass) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e assetClass) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(assetClasss[e-1], start)\n\treturn nil\n}\n\n// NewAssetClass returns returns an 'enum' value of type assetClass given its\n// string representation\nfunc NewAssetClass(s string) (assetClass, error) {\n\tvar e assetClass\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype mfType uint\n\n// MfType* constants represent types of mutual funds\nconst (\n\tMfTypeOpenEnd mfType = 1 + iota\n\tMfTypeCloseEnd\n\tMfTypeOther\n)\n\nvar mfTypes = [...]string{\"OPENEND\", \"CLOSEEND\", \"OTHER\"}\n\nfunc (e mfType) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= MfTypeOpenEnd && e <= MfTypeOther\n}\n\nfunc (e mfType) String() string {\n\tif e.Valid() {\n\t\treturn mfTypes[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid mfType (%d)\", e)\n}\n\nfunc (e *mfType) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range mfTypes {\n\t\tif s == value {\n\t\t\t*e = mfType(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid MfType: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *mfType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e mfType) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(mfTypes[e-1], start)\n\treturn nil\n}\n\n// NewMfType returns returns an 'enum' value of type mfType given its\n// string representation\nfunc NewMfType(s string) (mfType, error) {\n\tvar e mfType\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype optType uint\n\n// OptType* constants represent whether the option is a PUT or a CALL\nconst (\n\tOptTypePut optType = 1 + iota\n\tOptTypeCall\n)\n\nvar optTypes = [...]string{\"PUT\", \"CALL\"}\n\nfunc (e optType) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= OptTypePut && e <= OptTypeCall\n}\n\nfunc (e optType) String() string {\n\tif e.Valid() {\n\t\treturn optTypes[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid optType (%d)\", e)\n}\n\nfunc (e *optType) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range optTypes {\n\t\tif s == value {\n\t\t\t*e = optType(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid OptType: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *optType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e optType) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(optTypes[e-1], start)\n\treturn nil\n}\n\n// NewOptType returns returns an 'enum' value of type optType given its\n// string representation\nfunc NewOptType(s string) (optType, error) {\n\tvar e optType\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype stockType uint\n\n// StockType* constants represent types of stock\nconst (\n\tStockTypeCommon stockType = 1 + iota\n\tStockTypePreferred\n\tStockTypeConvertible\n\tStockTypeOther\n)\n\nvar stockTypes = [...]string{\"COMMON\", \"PREFERRED\", \"CONVERTIBLE\", \"OTHER\"}\n\nfunc (e stockType) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= StockTypeCommon && e <= StockTypeOther\n}\n\nfunc (e stockType) String() string {\n\tif e.Valid() {\n\t\treturn stockTypes[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid stockType (%d)\", e)\n}\n\nfunc (e *stockType) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range stockTypes {\n\t\tif s == value {\n\t\t\t*e = stockType(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid StockType: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *stockType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e stockType) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(stockTypes[e-1], start)\n\treturn nil\n}\n\n// NewStockType returns returns an 'enum' value of type stockType given its\n// string representation\nfunc NewStockType(s string) (stockType, error) {\n\tvar e stockType\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype holderType uint\n\n// HolderType* constants represent how the account is held\nconst (\n\tHolderTypeIndividual holderType = 1 + iota\n\tHolderTypeJoint\n\tHolderTypeCustodial\n\tHolderTypeTrust\n\tHolderTypeOther\n)\n\nvar holderTypes = [...]string{\"INDIVIDUAL\", \"JOINT\", \"CUSTODIAL\", \"TRUST\", \"OTHER\"}\n\nfunc (e holderType) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= HolderTypeIndividual && e <= HolderTypeOther\n}\n\nfunc (e holderType) String() string {\n\tif e.Valid() {\n\t\treturn holderTypes[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid holderType (%d)\", e)\n}\n\nfunc (e *holderType) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range holderTypes {\n\t\tif s == value {\n\t\t\t*e = holderType(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid HolderType: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *holderType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e holderType) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(holderTypes[e-1], start)\n\treturn nil\n}\n\n// NewHolderType returns returns an 'enum' value of type holderType given its\n// string representation\nfunc NewHolderType(s string) (holderType, error) {\n\tvar e holderType\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype acctClassification uint\n\n// AcctClassification* constants represent the type of an account\nconst (\n\tAcctClassificationPersonal acctClassification = 1 + iota\n\tAcctClassificationBusiness\n\tAcctClassificationCorporate\n\tAcctClassificationOther\n)\n\nvar acctClassifications = [...]string{\"PERSONAL\", \"BUSINESS\", \"CORPORATE\", \"OTHER\"}\n\nfunc (e acctClassification) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= AcctClassificationPersonal && e <= AcctClassificationOther\n}\n\nfunc (e acctClassification) String() string {\n\tif e.Valid() {\n\t\treturn acctClassifications[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid acctClassification (%d)\", e)\n}\n\nfunc (e *acctClassification) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range acctClassifications {\n\t\tif s == value {\n\t\t\t*e = acctClassification(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid AcctClassification: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *acctClassification) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e acctClassification) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(acctClassifications[e-1], start)\n\treturn nil\n}\n\n// NewAcctClassification returns returns an 'enum' value of type acctClassification given its\n// string representation\nfunc NewAcctClassification(s string) (acctClassification, error) {\n\tvar e acctClassification\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype svcStatus uint\n\n// SvcStatus* constants represent the status of the account: AVAIL = Available, but not yet requested, PEND = Requested, but not yet available, ACTIVE = In use\nconst (\n\tSvcStatusAvail svcStatus = 1 + iota\n\tSvcStatusPend\n\tSvcStatusActive\n)\n\nvar svcStatuss = [...]string{\"AVAIL\", \"PEND\", \"ACTIVE\"}\n\nfunc (e svcStatus) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= SvcStatusAvail && e <= SvcStatusActive\n}\n\nfunc (e svcStatus) String() string {\n\tif e.Valid() {\n\t\treturn svcStatuss[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid svcStatus (%d)\", e)\n}\n\nfunc (e *svcStatus) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range svcStatuss {\n\t\tif s == value {\n\t\t\t*e = svcStatus(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid SvcStatus: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *svcStatus) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e svcStatus) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(svcStatuss[e-1], start)\n\treturn nil\n}\n\n// NewSvcStatus returns returns an 'enum' value of type svcStatus given its\n// string representation\nfunc NewSvcStatus(s string) (svcStatus, error) {\n\tvar e svcStatus\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n\ntype usProductType uint\n\n// UsProductType* constants represent type of investment account (in the US)\nconst (\n\tUsProductType401K usProductType = 1 + iota\n\tUsProductType403B\n\tUsProductTypeIRA\n\tUsProductTypeKEOGH\n\tUsProductTypeOther\n\tUsProductTypeSARSEP\n\tUsProductTypeSimple\n\tUsProductTypeNormal\n\tUsProductTypeTDA\n\tUsProductTypeTrust\n\tUsProductTypeUGMA\n)\n\nvar usProductTypes = [...]string{\"401K\", \"403B\", \"IRA\", \"KEOGH\", \"OTHER\", \"SARSEP\", \"SIMPLE\", \"NORMAL\", \"TDA\", \"TRUST\", \"UGMA\"}\n\nfunc (e usProductType) Valid() bool {\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= UsProductType401K && e <= UsProductTypeUGMA\n}\n\nfunc (e usProductType) String() string {\n\tif e.Valid() {\n\t\treturn usProductTypes[e-1]\n\t}\n\treturn fmt.Sprintf(\"invalid usProductType (%d)\", e)\n}\n\nfunc (e *usProductType) FromString(in string) error {\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range usProductTypes {\n\t\tif s == value {\n\t\t\t*e = usProductType(i + 1)\n\t\t\treturn nil\n\t\t}\n\t}\n\t*e = 0\n\treturn errors.New(\"Invalid UsProductType: \\\"\" + in + \"\\\"\")\n}\n\nfunc (e *usProductType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn e.FromString(value)\n}\n\nfunc (e usProductType) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n\tif !e.Valid() {\n\t\treturn nil\n\t}\n\tenc.EncodeElement(usProductTypes[e-1], start)\n\treturn nil\n}\n\n// NewUsProductType returns returns an 'enum' value of type usProductType given its\n// string representation\nfunc NewUsProductType(s string) (usProductType, error) {\n\tvar e usProductType\n\terr := e.FromString(s)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn e, nil\n}\n"
  },
  {
    "path": "constants_test.go",
    "content": "package ofxgo\n\n/*\n * Do not edit this file by hand. It is auto-generated by calling `go generate`.\n * To make changes, edit generate_constants.py, re-run `go generate`, and check\n * in the result.\n */\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/aclindsa/xml\"\n)\n\nfunc TestOfxVersion(t *testing.T) {\n\te, err := NewOfxVersion(\"102\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new OfxVersion from string \\\"102\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"OfxVersion unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"220\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on OfxVersion.FromString(\\\"220\\\")\\n\")\n\t}\n\tif e.String() != \"220\" {\n\t\tt.Fatalf(\"OfxVersion.String() expected to be \\\"220\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"220\", &e)\n\n\toverwritten, err := NewOfxVersion(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new OfxVersion from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"OfxVersion created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"OfxVersion created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(OfxVersion): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"220\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE ofxVersion\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct OfxVersion): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>220</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>220</E></SC>\", string(b))\n\t}\n}\n\nfunc TestAcctType(t *testing.T) {\n\te, err := NewAcctType(\"CHECKING\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new AcctType from string \\\"CHECKING\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"AcctType unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"CD\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on AcctType.FromString(\\\"CD\\\")\\n\")\n\t}\n\tif e.String() != \"CD\" {\n\t\tt.Fatalf(\"AcctType.String() expected to be \\\"CD\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"CD\", &e)\n\n\toverwritten, err := NewAcctType(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new AcctType from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"AcctType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"AcctType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(AcctType): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"CD\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE acctType\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct AcctType): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>CD</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>CD</E></SC>\", string(b))\n\t}\n}\n\nfunc TestTrnType(t *testing.T) {\n\te, err := NewTrnType(\"CREDIT\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new TrnType from string \\\"CREDIT\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"TrnType unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"OTHER\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on TrnType.FromString(\\\"OTHER\\\")\\n\")\n\t}\n\tif e.String() != \"OTHER\" {\n\t\tt.Fatalf(\"TrnType.String() expected to be \\\"OTHER\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"OTHER\", &e)\n\n\toverwritten, err := NewTrnType(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new TrnType from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"TrnType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"TrnType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(TrnType): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"OTHER\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE trnType\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct TrnType): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>OTHER</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>OTHER</E></SC>\", string(b))\n\t}\n}\n\nfunc TestImageType(t *testing.T) {\n\te, err := NewImageType(\"STATEMENT\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new ImageType from string \\\"STATEMENT\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"ImageType unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"TAX\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on ImageType.FromString(\\\"TAX\\\")\\n\")\n\t}\n\tif e.String() != \"TAX\" {\n\t\tt.Fatalf(\"ImageType.String() expected to be \\\"TAX\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"TAX\", &e)\n\n\toverwritten, err := NewImageType(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new ImageType from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"ImageType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"ImageType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(ImageType): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"TAX\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE imageType\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct ImageType): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>TAX</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>TAX</E></SC>\", string(b))\n\t}\n}\n\nfunc TestImageRefType(t *testing.T) {\n\te, err := NewImageRefType(\"OPAQUE\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new ImageRefType from string \\\"OPAQUE\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"ImageRefType unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"FORMURL\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on ImageRefType.FromString(\\\"FORMURL\\\")\\n\")\n\t}\n\tif e.String() != \"FORMURL\" {\n\t\tt.Fatalf(\"ImageRefType.String() expected to be \\\"FORMURL\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"FORMURL\", &e)\n\n\toverwritten, err := NewImageRefType(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new ImageRefType from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"ImageRefType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"ImageRefType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(ImageRefType): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"FORMURL\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE imageRefType\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct ImageRefType): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>FORMURL</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>FORMURL</E></SC>\", string(b))\n\t}\n}\n\nfunc TestCheckSup(t *testing.T) {\n\te, err := NewCheckSup(\"FRONTONLY\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new CheckSup from string \\\"FRONTONLY\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"CheckSup unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"FRONTANDBACK\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on CheckSup.FromString(\\\"FRONTANDBACK\\\")\\n\")\n\t}\n\tif e.String() != \"FRONTANDBACK\" {\n\t\tt.Fatalf(\"CheckSup.String() expected to be \\\"FRONTANDBACK\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"FRONTANDBACK\", &e)\n\n\toverwritten, err := NewCheckSup(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new CheckSup from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"CheckSup created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"CheckSup created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(CheckSup): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"FRONTANDBACK\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE checkSup\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct CheckSup): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>FRONTANDBACK</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>FRONTANDBACK</E></SC>\", string(b))\n\t}\n}\n\nfunc TestCorrectAction(t *testing.T) {\n\te, err := NewCorrectAction(\"DELETE\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new CorrectAction from string \\\"DELETE\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"CorrectAction unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"REPLACE\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on CorrectAction.FromString(\\\"REPLACE\\\")\\n\")\n\t}\n\tif e.String() != \"REPLACE\" {\n\t\tt.Fatalf(\"CorrectAction.String() expected to be \\\"REPLACE\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"REPLACE\", &e)\n\n\toverwritten, err := NewCorrectAction(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new CorrectAction from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"CorrectAction created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"CorrectAction created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(CorrectAction): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"REPLACE\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE correctAction\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct CorrectAction): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>REPLACE</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>REPLACE</E></SC>\", string(b))\n\t}\n}\n\nfunc TestBalType(t *testing.T) {\n\te, err := NewBalType(\"DOLLAR\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new BalType from string \\\"DOLLAR\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"BalType unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"NUMBER\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on BalType.FromString(\\\"NUMBER\\\")\\n\")\n\t}\n\tif e.String() != \"NUMBER\" {\n\t\tt.Fatalf(\"BalType.String() expected to be \\\"NUMBER\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"NUMBER\", &e)\n\n\toverwritten, err := NewBalType(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new BalType from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"BalType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"BalType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(BalType): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"NUMBER\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE balType\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct BalType): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>NUMBER</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>NUMBER</E></SC>\", string(b))\n\t}\n}\n\nfunc TestInv401kSource(t *testing.T) {\n\te, err := NewInv401kSource(\"PRETAX\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new Inv401kSource from string \\\"PRETAX\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"Inv401kSource unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"OTHERNONVEST\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on Inv401kSource.FromString(\\\"OTHERNONVEST\\\")\\n\")\n\t}\n\tif e.String() != \"OTHERNONVEST\" {\n\t\tt.Fatalf(\"Inv401kSource.String() expected to be \\\"OTHERNONVEST\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"OTHERNONVEST\", &e)\n\n\toverwritten, err := NewInv401kSource(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new Inv401kSource from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"Inv401kSource created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"Inv401kSource created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(Inv401kSource): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"OTHERNONVEST\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE inv401kSource\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct Inv401kSource): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>OTHERNONVEST</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>OTHERNONVEST</E></SC>\", string(b))\n\t}\n}\n\nfunc TestSubAcctType(t *testing.T) {\n\te, err := NewSubAcctType(\"CASH\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new SubAcctType from string \\\"CASH\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"SubAcctType unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"OTHER\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on SubAcctType.FromString(\\\"OTHER\\\")\\n\")\n\t}\n\tif e.String() != \"OTHER\" {\n\t\tt.Fatalf(\"SubAcctType.String() expected to be \\\"OTHER\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"OTHER\", &e)\n\n\toverwritten, err := NewSubAcctType(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new SubAcctType from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"SubAcctType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"SubAcctType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(SubAcctType): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"OTHER\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE subAcctType\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct SubAcctType): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>OTHER</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>OTHER</E></SC>\", string(b))\n\t}\n}\n\nfunc TestBuyType(t *testing.T) {\n\te, err := NewBuyType(\"BUY\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new BuyType from string \\\"BUY\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"BuyType unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"BUYTOCOVER\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on BuyType.FromString(\\\"BUYTOCOVER\\\")\\n\")\n\t}\n\tif e.String() != \"BUYTOCOVER\" {\n\t\tt.Fatalf(\"BuyType.String() expected to be \\\"BUYTOCOVER\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"BUYTOCOVER\", &e)\n\n\toverwritten, err := NewBuyType(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new BuyType from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"BuyType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"BuyType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(BuyType): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"BUYTOCOVER\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE buyType\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct BuyType): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>BUYTOCOVER</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>BUYTOCOVER</E></SC>\", string(b))\n\t}\n}\n\nfunc TestOptAction(t *testing.T) {\n\te, err := NewOptAction(\"EXERCISE\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new OptAction from string \\\"EXERCISE\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"OptAction unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"EXPIRE\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on OptAction.FromString(\\\"EXPIRE\\\")\\n\")\n\t}\n\tif e.String() != \"EXPIRE\" {\n\t\tt.Fatalf(\"OptAction.String() expected to be \\\"EXPIRE\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"EXPIRE\", &e)\n\n\toverwritten, err := NewOptAction(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new OptAction from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"OptAction created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"OptAction created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(OptAction): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"EXPIRE\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE optAction\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct OptAction): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>EXPIRE</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>EXPIRE</E></SC>\", string(b))\n\t}\n}\n\nfunc TestTferAction(t *testing.T) {\n\te, err := NewTferAction(\"IN\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new TferAction from string \\\"IN\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"TferAction unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"OUT\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on TferAction.FromString(\\\"OUT\\\")\\n\")\n\t}\n\tif e.String() != \"OUT\" {\n\t\tt.Fatalf(\"TferAction.String() expected to be \\\"OUT\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"OUT\", &e)\n\n\toverwritten, err := NewTferAction(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new TferAction from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"TferAction created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"TferAction created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(TferAction): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"OUT\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE tferAction\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct TferAction): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>OUT</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>OUT</E></SC>\", string(b))\n\t}\n}\n\nfunc TestPosType(t *testing.T) {\n\te, err := NewPosType(\"LONG\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new PosType from string \\\"LONG\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"PosType unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"SHORT\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on PosType.FromString(\\\"SHORT\\\")\\n\")\n\t}\n\tif e.String() != \"SHORT\" {\n\t\tt.Fatalf(\"PosType.String() expected to be \\\"SHORT\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"SHORT\", &e)\n\n\toverwritten, err := NewPosType(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new PosType from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"PosType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"PosType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(PosType): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"SHORT\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE posType\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct PosType): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>SHORT</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>SHORT</E></SC>\", string(b))\n\t}\n}\n\nfunc TestSecured(t *testing.T) {\n\te, err := NewSecured(\"NAKED\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new Secured from string \\\"NAKED\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"Secured unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"COVERED\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on Secured.FromString(\\\"COVERED\\\")\\n\")\n\t}\n\tif e.String() != \"COVERED\" {\n\t\tt.Fatalf(\"Secured.String() expected to be \\\"COVERED\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"COVERED\", &e)\n\n\toverwritten, err := NewSecured(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new Secured from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"Secured created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"Secured created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(Secured): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"COVERED\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE secured\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct Secured): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>COVERED</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>COVERED</E></SC>\", string(b))\n\t}\n}\n\nfunc TestDuration(t *testing.T) {\n\te, err := NewDuration(\"DAY\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new Duration from string \\\"DAY\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"Duration unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"IMMEDIATE\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on Duration.FromString(\\\"IMMEDIATE\\\")\\n\")\n\t}\n\tif e.String() != \"IMMEDIATE\" {\n\t\tt.Fatalf(\"Duration.String() expected to be \\\"IMMEDIATE\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"IMMEDIATE\", &e)\n\n\toverwritten, err := NewDuration(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new Duration from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"Duration created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"Duration created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(Duration): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"IMMEDIATE\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE duration\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct Duration): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>IMMEDIATE</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>IMMEDIATE</E></SC>\", string(b))\n\t}\n}\n\nfunc TestRestriction(t *testing.T) {\n\te, err := NewRestriction(\"ALLORNONE\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new Restriction from string \\\"ALLORNONE\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"Restriction unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"NONE\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on Restriction.FromString(\\\"NONE\\\")\\n\")\n\t}\n\tif e.String() != \"NONE\" {\n\t\tt.Fatalf(\"Restriction.String() expected to be \\\"NONE\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"NONE\", &e)\n\n\toverwritten, err := NewRestriction(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new Restriction from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"Restriction created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"Restriction created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(Restriction): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"NONE\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE restriction\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct Restriction): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>NONE</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>NONE</E></SC>\", string(b))\n\t}\n}\n\nfunc TestUnitType(t *testing.T) {\n\te, err := NewUnitType(\"SHARES\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new UnitType from string \\\"SHARES\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"UnitType unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"CURRENCY\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on UnitType.FromString(\\\"CURRENCY\\\")\\n\")\n\t}\n\tif e.String() != \"CURRENCY\" {\n\t\tt.Fatalf(\"UnitType.String() expected to be \\\"CURRENCY\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"CURRENCY\", &e)\n\n\toverwritten, err := NewUnitType(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new UnitType from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"UnitType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"UnitType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(UnitType): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"CURRENCY\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE unitType\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct UnitType): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>CURRENCY</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>CURRENCY</E></SC>\", string(b))\n\t}\n}\n\nfunc TestOptBuyType(t *testing.T) {\n\te, err := NewOptBuyType(\"BUYTOOPEN\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new OptBuyType from string \\\"BUYTOOPEN\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"OptBuyType unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"BUYTOCLOSE\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on OptBuyType.FromString(\\\"BUYTOCLOSE\\\")\\n\")\n\t}\n\tif e.String() != \"BUYTOCLOSE\" {\n\t\tt.Fatalf(\"OptBuyType.String() expected to be \\\"BUYTOCLOSE\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"BUYTOCLOSE\", &e)\n\n\toverwritten, err := NewOptBuyType(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new OptBuyType from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"OptBuyType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"OptBuyType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(OptBuyType): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"BUYTOCLOSE\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE optBuyType\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct OptBuyType): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>BUYTOCLOSE</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>BUYTOCLOSE</E></SC>\", string(b))\n\t}\n}\n\nfunc TestSellType(t *testing.T) {\n\te, err := NewSellType(\"SELL\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new SellType from string \\\"SELL\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"SellType unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"SELLSHORT\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on SellType.FromString(\\\"SELLSHORT\\\")\\n\")\n\t}\n\tif e.String() != \"SELLSHORT\" {\n\t\tt.Fatalf(\"SellType.String() expected to be \\\"SELLSHORT\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"SELLSHORT\", &e)\n\n\toverwritten, err := NewSellType(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new SellType from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"SellType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"SellType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(SellType): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"SELLSHORT\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE sellType\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct SellType): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>SELLSHORT</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>SELLSHORT</E></SC>\", string(b))\n\t}\n}\n\nfunc TestLoanPmtFreq(t *testing.T) {\n\te, err := NewLoanPmtFreq(\"WEEKLY\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new LoanPmtFreq from string \\\"WEEKLY\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"LoanPmtFreq unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"OTHER\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on LoanPmtFreq.FromString(\\\"OTHER\\\")\\n\")\n\t}\n\tif e.String() != \"OTHER\" {\n\t\tt.Fatalf(\"LoanPmtFreq.String() expected to be \\\"OTHER\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"OTHER\", &e)\n\n\toverwritten, err := NewLoanPmtFreq(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new LoanPmtFreq from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"LoanPmtFreq created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"LoanPmtFreq created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(LoanPmtFreq): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"OTHER\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE loanPmtFreq\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct LoanPmtFreq): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>OTHER</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>OTHER</E></SC>\", string(b))\n\t}\n}\n\nfunc TestIncomeType(t *testing.T) {\n\te, err := NewIncomeType(\"CGLONG\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new IncomeType from string \\\"CGLONG\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"IncomeType unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"MISC\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on IncomeType.FromString(\\\"MISC\\\")\\n\")\n\t}\n\tif e.String() != \"MISC\" {\n\t\tt.Fatalf(\"IncomeType.String() expected to be \\\"MISC\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"MISC\", &e)\n\n\toverwritten, err := NewIncomeType(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new IncomeType from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"IncomeType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"IncomeType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(IncomeType): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"MISC\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE incomeType\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct IncomeType): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>MISC</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>MISC</E></SC>\", string(b))\n\t}\n}\n\nfunc TestSellReason(t *testing.T) {\n\te, err := NewSellReason(\"CALL\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new SellReason from string \\\"CALL\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"SellReason unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"MATURITY\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on SellReason.FromString(\\\"MATURITY\\\")\\n\")\n\t}\n\tif e.String() != \"MATURITY\" {\n\t\tt.Fatalf(\"SellReason.String() expected to be \\\"MATURITY\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"MATURITY\", &e)\n\n\toverwritten, err := NewSellReason(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new SellReason from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"SellReason created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"SellReason created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(SellReason): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"MATURITY\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE sellReason\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct SellReason): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>MATURITY</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>MATURITY</E></SC>\", string(b))\n\t}\n}\n\nfunc TestOptSellType(t *testing.T) {\n\te, err := NewOptSellType(\"SELLTOCLOSE\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new OptSellType from string \\\"SELLTOCLOSE\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"OptSellType unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"SELLTOOPEN\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on OptSellType.FromString(\\\"SELLTOOPEN\\\")\\n\")\n\t}\n\tif e.String() != \"SELLTOOPEN\" {\n\t\tt.Fatalf(\"OptSellType.String() expected to be \\\"SELLTOOPEN\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"SELLTOOPEN\", &e)\n\n\toverwritten, err := NewOptSellType(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new OptSellType from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"OptSellType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"OptSellType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(OptSellType): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"SELLTOOPEN\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE optSellType\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct OptSellType): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>SELLTOOPEN</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>SELLTOOPEN</E></SC>\", string(b))\n\t}\n}\n\nfunc TestRelType(t *testing.T) {\n\te, err := NewRelType(\"SPREAD\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new RelType from string \\\"SPREAD\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"RelType unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"OTHER\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on RelType.FromString(\\\"OTHER\\\")\\n\")\n\t}\n\tif e.String() != \"OTHER\" {\n\t\tt.Fatalf(\"RelType.String() expected to be \\\"OTHER\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"OTHER\", &e)\n\n\toverwritten, err := NewRelType(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new RelType from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"RelType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"RelType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(RelType): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"OTHER\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE relType\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct RelType): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>OTHER</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>OTHER</E></SC>\", string(b))\n\t}\n}\n\nfunc TestCharType(t *testing.T) {\n\te, err := NewCharType(\"ALPHAONLY\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new CharType from string \\\"ALPHAONLY\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"CharType unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"ALPHAANDNUMERIC\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on CharType.FromString(\\\"ALPHAANDNUMERIC\\\")\\n\")\n\t}\n\tif e.String() != \"ALPHAANDNUMERIC\" {\n\t\tt.Fatalf(\"CharType.String() expected to be \\\"ALPHAANDNUMERIC\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"ALPHAANDNUMERIC\", &e)\n\n\toverwritten, err := NewCharType(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new CharType from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"CharType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"CharType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(CharType): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"ALPHAANDNUMERIC\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE charType\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct CharType): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>ALPHAANDNUMERIC</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>ALPHAANDNUMERIC</E></SC>\", string(b))\n\t}\n}\n\nfunc TestSyncMode(t *testing.T) {\n\te, err := NewSyncMode(\"FULL\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new SyncMode from string \\\"FULL\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"SyncMode unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"LITE\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on SyncMode.FromString(\\\"LITE\\\")\\n\")\n\t}\n\tif e.String() != \"LITE\" {\n\t\tt.Fatalf(\"SyncMode.String() expected to be \\\"LITE\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"LITE\", &e)\n\n\toverwritten, err := NewSyncMode(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new SyncMode from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"SyncMode created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"SyncMode created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(SyncMode): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"LITE\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE syncMode\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct SyncMode): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>LITE</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>LITE</E></SC>\", string(b))\n\t}\n}\n\nfunc TestOfxSec(t *testing.T) {\n\te, err := NewOfxSec(\"NONE\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new OfxSec from string \\\"NONE\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"OfxSec unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"TYPE 1\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on OfxSec.FromString(\\\"TYPE 1\\\")\\n\")\n\t}\n\tif e.String() != \"TYPE 1\" {\n\t\tt.Fatalf(\"OfxSec.String() expected to be \\\"TYPE 1\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"TYPE 1\", &e)\n\n\toverwritten, err := NewOfxSec(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new OfxSec from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"OfxSec created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"OfxSec created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(OfxSec): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"TYPE 1\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE ofxSec\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct OfxSec): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>TYPE 1</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>TYPE 1</E></SC>\", string(b))\n\t}\n}\n\nfunc TestDebtType(t *testing.T) {\n\te, err := NewDebtType(\"COUPON\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new DebtType from string \\\"COUPON\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"DebtType unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"ZERO\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on DebtType.FromString(\\\"ZERO\\\")\\n\")\n\t}\n\tif e.String() != \"ZERO\" {\n\t\tt.Fatalf(\"DebtType.String() expected to be \\\"ZERO\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"ZERO\", &e)\n\n\toverwritten, err := NewDebtType(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new DebtType from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"DebtType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"DebtType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(DebtType): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"ZERO\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE debtType\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct DebtType): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>ZERO</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>ZERO</E></SC>\", string(b))\n\t}\n}\n\nfunc TestDebtClass(t *testing.T) {\n\te, err := NewDebtClass(\"TREASURY\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new DebtClass from string \\\"TREASURY\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"DebtClass unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"OTHER\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on DebtClass.FromString(\\\"OTHER\\\")\\n\")\n\t}\n\tif e.String() != \"OTHER\" {\n\t\tt.Fatalf(\"DebtClass.String() expected to be \\\"OTHER\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"OTHER\", &e)\n\n\toverwritten, err := NewDebtClass(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new DebtClass from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"DebtClass created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"DebtClass created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(DebtClass): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"OTHER\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE debtClass\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct DebtClass): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>OTHER</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>OTHER</E></SC>\", string(b))\n\t}\n}\n\nfunc TestCouponFreq(t *testing.T) {\n\te, err := NewCouponFreq(\"MONTHLY\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new CouponFreq from string \\\"MONTHLY\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"CouponFreq unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"OTHER\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on CouponFreq.FromString(\\\"OTHER\\\")\\n\")\n\t}\n\tif e.String() != \"OTHER\" {\n\t\tt.Fatalf(\"CouponFreq.String() expected to be \\\"OTHER\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"OTHER\", &e)\n\n\toverwritten, err := NewCouponFreq(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new CouponFreq from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"CouponFreq created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"CouponFreq created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(CouponFreq): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"OTHER\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE couponFreq\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct CouponFreq): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>OTHER</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>OTHER</E></SC>\", string(b))\n\t}\n}\n\nfunc TestCallType(t *testing.T) {\n\te, err := NewCallType(\"CALL\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new CallType from string \\\"CALL\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"CallType unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"MATURITY\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on CallType.FromString(\\\"MATURITY\\\")\\n\")\n\t}\n\tif e.String() != \"MATURITY\" {\n\t\tt.Fatalf(\"CallType.String() expected to be \\\"MATURITY\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"MATURITY\", &e)\n\n\toverwritten, err := NewCallType(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new CallType from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"CallType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"CallType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(CallType): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"MATURITY\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE callType\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct CallType): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>MATURITY</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>MATURITY</E></SC>\", string(b))\n\t}\n}\n\nfunc TestAssetClass(t *testing.T) {\n\te, err := NewAssetClass(\"DOMESTICBOND\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new AssetClass from string \\\"DOMESTICBOND\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"AssetClass unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"OTHER\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on AssetClass.FromString(\\\"OTHER\\\")\\n\")\n\t}\n\tif e.String() != \"OTHER\" {\n\t\tt.Fatalf(\"AssetClass.String() expected to be \\\"OTHER\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"OTHER\", &e)\n\n\toverwritten, err := NewAssetClass(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new AssetClass from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"AssetClass created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"AssetClass created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(AssetClass): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"OTHER\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE assetClass\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct AssetClass): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>OTHER</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>OTHER</E></SC>\", string(b))\n\t}\n}\n\nfunc TestMfType(t *testing.T) {\n\te, err := NewMfType(\"OPENEND\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new MfType from string \\\"OPENEND\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"MfType unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"OTHER\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on MfType.FromString(\\\"OTHER\\\")\\n\")\n\t}\n\tif e.String() != \"OTHER\" {\n\t\tt.Fatalf(\"MfType.String() expected to be \\\"OTHER\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"OTHER\", &e)\n\n\toverwritten, err := NewMfType(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new MfType from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"MfType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"MfType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(MfType): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"OTHER\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE mfType\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct MfType): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>OTHER</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>OTHER</E></SC>\", string(b))\n\t}\n}\n\nfunc TestOptType(t *testing.T) {\n\te, err := NewOptType(\"PUT\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new OptType from string \\\"PUT\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"OptType unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"CALL\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on OptType.FromString(\\\"CALL\\\")\\n\")\n\t}\n\tif e.String() != \"CALL\" {\n\t\tt.Fatalf(\"OptType.String() expected to be \\\"CALL\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"CALL\", &e)\n\n\toverwritten, err := NewOptType(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new OptType from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"OptType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"OptType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(OptType): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"CALL\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE optType\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct OptType): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>CALL</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>CALL</E></SC>\", string(b))\n\t}\n}\n\nfunc TestStockType(t *testing.T) {\n\te, err := NewStockType(\"COMMON\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new StockType from string \\\"COMMON\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"StockType unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"OTHER\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on StockType.FromString(\\\"OTHER\\\")\\n\")\n\t}\n\tif e.String() != \"OTHER\" {\n\t\tt.Fatalf(\"StockType.String() expected to be \\\"OTHER\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"OTHER\", &e)\n\n\toverwritten, err := NewStockType(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new StockType from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"StockType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"StockType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(StockType): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"OTHER\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE stockType\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct StockType): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>OTHER</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>OTHER</E></SC>\", string(b))\n\t}\n}\n\nfunc TestHolderType(t *testing.T) {\n\te, err := NewHolderType(\"INDIVIDUAL\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new HolderType from string \\\"INDIVIDUAL\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"HolderType unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"OTHER\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on HolderType.FromString(\\\"OTHER\\\")\\n\")\n\t}\n\tif e.String() != \"OTHER\" {\n\t\tt.Fatalf(\"HolderType.String() expected to be \\\"OTHER\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"OTHER\", &e)\n\n\toverwritten, err := NewHolderType(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new HolderType from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"HolderType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"HolderType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(HolderType): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"OTHER\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE holderType\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct HolderType): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>OTHER</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>OTHER</E></SC>\", string(b))\n\t}\n}\n\nfunc TestAcctClassification(t *testing.T) {\n\te, err := NewAcctClassification(\"PERSONAL\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new AcctClassification from string \\\"PERSONAL\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"AcctClassification unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"OTHER\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on AcctClassification.FromString(\\\"OTHER\\\")\\n\")\n\t}\n\tif e.String() != \"OTHER\" {\n\t\tt.Fatalf(\"AcctClassification.String() expected to be \\\"OTHER\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"OTHER\", &e)\n\n\toverwritten, err := NewAcctClassification(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new AcctClassification from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"AcctClassification created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"AcctClassification created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(AcctClassification): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"OTHER\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE acctClassification\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct AcctClassification): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>OTHER</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>OTHER</E></SC>\", string(b))\n\t}\n}\n\nfunc TestSvcStatus(t *testing.T) {\n\te, err := NewSvcStatus(\"AVAIL\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new SvcStatus from string \\\"AVAIL\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"SvcStatus unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"ACTIVE\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on SvcStatus.FromString(\\\"ACTIVE\\\")\\n\")\n\t}\n\tif e.String() != \"ACTIVE\" {\n\t\tt.Fatalf(\"SvcStatus.String() expected to be \\\"ACTIVE\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"ACTIVE\", &e)\n\n\toverwritten, err := NewSvcStatus(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new SvcStatus from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"SvcStatus created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"SvcStatus created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(SvcStatus): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"ACTIVE\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE svcStatus\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct SvcStatus): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>ACTIVE</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>ACTIVE</E></SC>\", string(b))\n\t}\n}\n\nfunc TestUsProductType(t *testing.T) {\n\te, err := NewUsProductType(\"401K\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating new UsProductType from string \\\"401K\\\"\\n\")\n\t}\n\tif !e.Valid() {\n\t\tt.Fatalf(\"UsProductType unexpectedly invalid\\n\")\n\t}\n\terr = e.FromString(\"UGMA\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on UsProductType.FromString(\\\"UGMA\\\")\\n\")\n\t}\n\tif e.String() != \"UGMA\" {\n\t\tt.Fatalf(\"UsProductType.String() expected to be \\\"UGMA\\\"\\n\")\n\t}\n\n\tmarshalHelper(t, \"UGMA\", &e)\n\n\toverwritten, err := NewUsProductType(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error creating new UsProductType from string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\"\\n\")\n\t}\n\tif overwritten.Valid() {\n\t\tt.Fatalf(\"UsProductType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not be valid\\n\")\n\t}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {\n\t\tt.Fatalf(\"UsProductType created with string \\\"THISWILLNEVERBEAVALIDENUMSTRING\\\" should not return valid string from String()\\n\")\n\t}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(UsProductType): %s\\n\", err)\n\t}\n\tif string(b) != \"\" {\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\n\", string(b))\n\t}\n\n\tunmarshalHelper(t, \"UGMA\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\n\")\n\t}\n\n\ttype SC struct {\n\t\tE usProductType\n\t}\n\tsc := SC{E: e}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct UsProductType): %s\\n\", err)\n\t}\n\tif string(b) != \"<SC><E>UGMA</E></SC>\" {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", \"<SC><E>UGMA</E></SC>\", string(b))\n\t}\n}\n"
  },
  {
    "path": "creditcard.go",
    "content": "package ofxgo\n\nimport (\n\t\"github.com/aclindsa/xml\"\n)\n\n// CCStatementRequest represents a request for a credit card statement. It is\n// used to request balances and/or transactions. See StatementRequest for the\n// analog for all other bank accounts.\ntype CCStatementRequest struct {\n\tXMLName   xml.Name `xml:\"CCSTMTTRNRQ\"`\n\tTrnUID    UID      `xml:\"TRNUID\"`\n\tCltCookie String   `xml:\"CLTCOOKIE,omitempty\"`\n\tTAN       String   `xml:\"TAN,omitempty\"`\n\t// TODO OFXEXTENSION\n\tCCAcctFrom     CCAcct  `xml:\"CCSTMTRQ>CCACCTFROM\"`\n\tDtStart        *Date   `xml:\"CCSTMTRQ>INCTRAN>DTSTART,omitempty\"`\n\tDtEnd          *Date   `xml:\"CCSTMTRQ>INCTRAN>DTEND,omitempty\"`\n\tInclude        Boolean `xml:\"CCSTMTRQ>INCTRAN>INCLUDE\"`          // Include transactions (instead of just balance)\n\tIncludePending Boolean `xml:\"CCSTMTRQ>INCLUDEPENDING,omitempty\"` // Include pending transactions\n\tIncTranImg     Boolean `xml:\"CCSTMTRQ>INCTRANIMG,omitempty\"`     // Include transaction images\n}\n\n// Name returns the name of the top-level transaction XML/SGML element\nfunc (r *CCStatementRequest) Name() string {\n\treturn \"CCSTMTTRNRQ\"\n}\n\n// Valid returns (true, nil) if this struct would be valid OFX if marshalled\n// into XML/SGML\nfunc (r *CCStatementRequest) Valid(version ofxVersion) (bool, error) {\n\tif ok, err := r.TrnUID.Valid(); !ok {\n\t\treturn false, err\n\t}\n\t// TODO implement\n\treturn true, nil\n}\n\n// Type returns which message set this message belongs to (which Request\n// element of type []Message it should appended to)\nfunc (r *CCStatementRequest) Type() messageType {\n\treturn CreditCardRq\n}\n\n// CCStatementResponse represents a credit card statement, including its\n// balances and possibly transactions. It is a response to CCStatementRequest,\n// or sometimes provided as part of an OFX file downloaded manually from an FI.\ntype CCStatementResponse struct {\n\tXMLName   xml.Name `xml:\"CCSTMTTRNRS\"`\n\tTrnUID    UID      `xml:\"TRNUID\"`\n\tStatus    Status   `xml:\"STATUS\"`\n\tCltCookie String   `xml:\"CLTCOOKIE,omitempty\"`\n\t// TODO `xml:\"OFXEXTENSION,omitempty\"`\n\tCurDef       CurrSymbol       `xml:\"CCSTMTRS>CURDEF\"`\n\tCCAcctFrom   CCAcct           `xml:\"CCSTMTRS>CCACCTFROM\"`\n\tBankTranList *TransactionList `xml:\"CCSTMTRS>BANKTRANLIST,omitempty\"`\n\t//BANKTRANLISTP\n\tBalAmt        Amount    `xml:\"CCSTMTRS>LEDGERBAL>BALAMT\"`\n\tDtAsOf        Date      `xml:\"CCSTMTRS>LEDGERBAL>DTASOF\"`\n\tAvailBalAmt   *Amount   `xml:\"CCSTMTRS>AVAILBAL>BALAMT,omitempty\"`\n\tAvailDtAsOf   *Date     `xml:\"CCSTMTRS>AVAILBAL>DTASOF,omitempty\"`\n\tCashAdvBalAmt Amount    `xml:\"CCSTMTRS>CASHADVBALAMT,omitempty\"`           // Only for CREDITLINE accounts, available balance for cash advances\n\tIntRatePurch  Amount    `xml:\"CCSTMTRS>INTRATEPURCH,omitempty\"`            // Current interest rate for purchases\n\tIntRateCash   Amount    `xml:\"CCSTMTRS>INTRATECASH,omitempty\"`             // Current interest rate for cash advances\n\tIntRateXfer   Amount    `xml:\"CCSTMTRS>INTRATEXFER,omitempty\"`             // Current interest rate for cash advances\n\tRewardName    String    `xml:\"CCSTMTRS>REWARDINFO>NAME,omitempty\"`         // Name of the reward program referred to by the next two elements\n\tRewardBal     Amount    `xml:\"CCSTMTRS>REWARDINFO>REWARDBAL,omitempty\"`    // Current balance of the reward program\n\tRewardEarned  Amount    `xml:\"CCSTMTRS>REWARDINFO>REWARDEARNED,omitempty\"` // Reward amount earned YTD\n\tBalList       []Balance `xml:\"CCSTMTRS>BALLIST>BAL,omitempty\"`\n\tMktgInfo      String    `xml:\"CCSTMTRS>MKTGINFO,omitempty\"` // Marketing information\n}\n\n// Name returns the name of the top-level transaction XML/SGML element\nfunc (sr *CCStatementResponse) Name() string {\n\treturn \"CCSTMTTRNRS\"\n}\n\n// Valid returns (true, nil) if this struct was valid OFX when unmarshalled\nfunc (sr *CCStatementResponse) Valid(version ofxVersion) (bool, error) {\n\tif ok, err := sr.TrnUID.Valid(); !ok {\n\t\treturn false, err\n\t}\n\t//TODO implement\n\treturn true, nil\n}\n\n// Type returns which message set this message belongs to (which Response\n// element of type []Message it belongs to)\nfunc (sr *CCStatementResponse) Type() messageType {\n\treturn CreditCardRs\n}\n"
  },
  {
    "path": "creditcard_test.go",
    "content": "package ofxgo\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestMarshalCCStatementRequest(t *testing.T) {\n\tvar expectedString string = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<?OFX OFXHEADER=\"200\" VERSION=\"203\" SECURITY=\"NONE\" OLDFILEUID=\"NONE\" NEWFILEUID=\"NONE\"?>\n<OFX>\n\t<SIGNONMSGSRQV1>\n\t\t<SONRQ>\n\t\t\t<DTCLIENT>20170331153848.000[0:GMT]</DTCLIENT>\n\t\t\t<USERID>myusername</USERID>\n\t\t\t<USERPASS>Pa$$word</USERPASS>\n\t\t\t<LANGUAGE>ENG</LANGUAGE>\n\t\t\t<FI>\n\t\t\t\t<ORG>BNK</ORG>\n\t\t\t\t<FID>1987</FID>\n\t\t\t</FI>\n\t\t\t<APPID>OFXGO</APPID>\n\t\t\t<APPVER>0001</APPVER>\n\t\t</SONRQ>\n\t</SIGNONMSGSRQV1>\n\t<CREDITCARDMSGSRQV1>\n\t\t<CCSTMTTRNRQ>\n\t\t\t<TRNUID>913846</TRNUID>\n\t\t\t<CCSTMTRQ>\n\t\t\t\t<CCACCTFROM>\n\t\t\t\t\t<ACCTID>XXXXXXXXXXXX1234</ACCTID>\n\t\t\t\t</CCACCTFROM>\n\t\t\t\t<INCTRAN>\n\t\t\t\t\t<DTSTART>20170101000000.000[0:GMT]</DTSTART>\n\t\t\t\t\t<INCLUDE>Y</INCLUDE>\n\t\t\t\t</INCTRAN>\n\t\t\t</CCSTMTRQ>\n\t\t</CCSTMTTRNRQ>\n\t</CREDITCARDMSGSRQV1>\n</OFX>`\n\n\tvar client = BasicClient{\n\t\tAppID:       \"OFXGO\",\n\t\tAppVer:      \"0001\",\n\t\tSpecVersion: OfxVersion203,\n\t}\n\n\tvar request Request\n\trequest.Signon.UserID = \"myusername\"\n\trequest.Signon.UserPass = \"Pa$$word\"\n\trequest.Signon.Org = \"BNK\"\n\trequest.Signon.Fid = \"1987\"\n\n\tstatementRequest := CCStatementRequest{\n\t\tTrnUID: \"913846\",\n\t\tCCAcctFrom: CCAcct{\n\t\t\tAcctID: \"XXXXXXXXXXXX1234\",\n\t\t},\n\t\tDtStart: NewDateGMT(2017, 1, 1, 0, 0, 0, 0),\n\t\tInclude: true,\n\t}\n\trequest.CreditCard = append(request.CreditCard, &statementRequest)\n\n\trequest.SetClientFields(&client)\n\t// Overwrite the DtClient value set by SetClientFields to time.Now()\n\trequest.Signon.DtClient = *NewDateGMT(2017, 3, 31, 15, 38, 48, 0)\n\n\tmarshalCheckRequest(t, &request, expectedString)\n}\n\nfunc TestUnmarshalCCStatementResponse102(t *testing.T) {\n\tresponseReader := strings.NewReader(`OFXHEADER:100\nDATA:OFXSGML\nVERSION:102\nSECURITY:NONE\nENCODING:USASCII\nCHARSET:1252\nCOMPRESSION:NONE\nOLDFILEUID:NONE\nNEWFILEUID:NONE\n\n<OFX><SIGNONMSGSRSV1><SONRS><STATUS><CODE>0<SEVERITY>INFO<MESSAGE>SUCCESS</STATUS><DTSERVER>20170331154648.331[-4:EDT]<LANGUAGE>ENG<FI><ORG>01<FID>81729</FI></SONRS></SIGNONMSGSRSV1><CREDITCARDMSGSRSV1><CCSTMTTRNRS><TRNUID>59e850ad-7448-b4ce-4b71-29057763b306<STATUS><CODE>0<SEVERITY>INFO</STATUS><CCSTMTRS><CURDEF>USD<CCACCTFROM><ACCTID>9283744488463775</CCACCTFROM><BANKTRANLIST><DTSTART>20161201154648.688[-5:EST]<DTEND>20170331154648.688[-4:EDT]<STMTTRN><TRNTYPE>DEBIT<DTPOSTED>20170209120000[0:GMT]<TRNAMT>-7.96<FITID>2017020924435657040207171600195<NAME>SLICE OF NY</STMTTRN><STMTTRN><TRNTYPE>CREDIT<DTPOSTED>20161228120000[0:GMT]<TRNAMT>3830.46<FITID>2016122823633637200000258482730<NAME>Payment Thank You Electro</STMTTRN><STMTTRN><TRNTYPE>DEBIT<DTPOSTED>20170327120000[0:GMT]<TRNAMT>-17.7<FITID>2017032724445727085300442885680<NAME>KROGER FUEL #9999</STMTTRN></BANKTRANLIST><LEDGERBAL><BALAMT>-9334<DTASOF>20170331080000.000[-4:EDT]</LEDGERBAL><AVAILBAL><BALAMT>7630.17<DTASOF>20170331080000.000[-4:EDT]</AVAILBAL></CCSTMTRS></CCSTMTTRNRS></CREDITCARDMSGSRSV1></OFX>`)\n\tvar expected Response\n\tEDT := time.FixedZone(\"EDT\", -4*60*60)\n\tEST := time.FixedZone(\"EST\", -5*60*60)\n\n\texpected.Version = OfxVersion102\n\texpected.Signon.Status.Code = 0\n\texpected.Signon.Status.Severity = \"INFO\"\n\texpected.Signon.Status.Message = \"SUCCESS\"\n\texpected.Signon.DtServer = *NewDate(2017, 3, 31, 15, 46, 48, 331000000, EDT)\n\texpected.Signon.Language = \"ENG\"\n\texpected.Signon.Org = \"01\"\n\texpected.Signon.Fid = \"81729\"\n\n\tvar trnamt1, trnamt2, trnamt3 Amount\n\ttrnamt1.SetFrac64(-796, 100)\n\ttrnamt2.SetFrac64(383046, 100)\n\ttrnamt3.SetFrac64(-1770, 100)\n\n\tbanktranlist := TransactionList{\n\t\tDtStart: *NewDate(2016, 12, 1, 15, 46, 48, 688000000, EST),\n\t\tDtEnd:   *NewDate(2017, 3, 31, 15, 46, 48, 688000000, EDT),\n\t\tTransactions: []Transaction{\n\t\t\t{\n\t\t\t\tTrnType:  TrnTypeDebit,\n\t\t\t\tDtPosted: *NewDateGMT(2017, 2, 9, 12, 0, 0, 0),\n\t\t\t\tTrnAmt:   trnamt1,\n\t\t\t\tFiTID:    \"2017020924435657040207171600195\",\n\t\t\t\tName:     \"SLICE OF NY\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tTrnType:  TrnTypeCredit,\n\t\t\t\tDtPosted: *NewDateGMT(2016, 12, 28, 12, 0, 0, 0),\n\t\t\t\tTrnAmt:   trnamt2,\n\t\t\t\tFiTID:    \"2016122823633637200000258482730\",\n\t\t\t\tName:     \"Payment Thank You Electro\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tTrnType:  TrnTypeDebit,\n\t\t\t\tDtPosted: *NewDateGMT(2017, 3, 27, 12, 0, 0, 0),\n\t\t\t\tTrnAmt:   trnamt3,\n\t\t\t\tFiTID:    \"2017032724445727085300442885680\",\n\t\t\t\tName:     \"KROGER FUEL #9999\",\n\t\t\t},\n\t\t},\n\t}\n\n\tvar balamt, availbalamt Amount\n\tbalamt.SetFrac64(-933400, 100)\n\tavailbalamt.SetFrac64(763017, 100)\n\n\tusd, err := NewCurrSymbol(\"USD\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating CurrSymbol for USD\\n\")\n\t}\n\n\tstatementResponse := CCStatementResponse{\n\t\tTrnUID: \"59e850ad-7448-b4ce-4b71-29057763b306\",\n\t\tStatus: Status{\n\t\t\tCode:     0,\n\t\t\tSeverity: \"INFO\",\n\t\t},\n\t\tCurDef: *usd,\n\t\tCCAcctFrom: CCAcct{\n\t\t\tAcctID: \"9283744488463775\",\n\t\t},\n\t\tBankTranList: &banktranlist,\n\t\tBalAmt:       balamt,\n\t\tDtAsOf:       *NewDate(2017, 3, 31, 8, 0, 0, 0, EDT),\n\t\tAvailBalAmt:  &availbalamt,\n\t\tAvailDtAsOf:  NewDate(2017, 3, 31, 8, 0, 0, 0, EDT),\n\t}\n\texpected.CreditCard = append(expected.CreditCard, &statementResponse)\n\n\tresponse, err := ParseResponse(responseReader)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error unmarshalling response: %s\\n\", err)\n\t}\n\n\tcheckResponsesEqual(t, &expected, response)\n\tcheckResponseRoundTrip(t, response)\n}\n"
  },
  {
    "path": "discovercard_client.go",
    "content": "package ofxgo\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n)\n\n// DiscoverCardClient provides a Client implementation which handles\n// DiscoverCard's broken HTTP header behavior. DiscoverCardClient uses default,\n// non-zero settings, if its fields are not initialized.\ntype DiscoverCardClient struct {\n\t*BasicClient\n}\n\n// NewDiscoverCardClient returns a Client interface configured to handle\n// Discover Card's brand of idiosyncrasy\nfunc NewDiscoverCardClient(bc *BasicClient) Client {\n\treturn &DiscoverCardClient{bc}\n}\n\nfunc discoverCardHTTPPost(URL string, r io.Reader) (*http.Response, error) {\n\t// Either convert or copy to a bytes.Buffer to be able to determine the\n\t// request length for the Content-Length header\n\tbuf, ok := r.(*bytes.Buffer)\n\tif !ok {\n\t\tbuf = &bytes.Buffer{}\n\t\t_, err := io.Copy(buf, r)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\turl, err := url.Parse(URL)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tpath := url.Path\n\tif path == \"\" {\n\t\tpath = \"/\"\n\t}\n\n\t// Discover requires only these headers and in this exact order, or it\n\t// returns HTTP 403\n\theaders := fmt.Sprintf(\"POST %s HTTP/1.1\\r\\n\"+\n\t\t\"Content-Type: application/x-ofx\\r\\n\"+\n\t\t\"Host: %s\\r\\n\"+\n\t\t\"Content-Length: %d\\r\\n\"+\n\t\t\"Connection: Keep-Alive\\r\\n\"+\n\t\t\"\\r\\n\", path, url.Hostname(), buf.Len())\n\n\thost := url.Host\n\tif url.Port() == \"\" {\n\t\thost += \":443\"\n\t}\n\n\t// BUGBUG: cannot do defer conn.Close() until body is read,\n\t// we are \"leaking\" a socket here, but it will be finalized\n\tconn, err := tls.Dial(\"tcp\", host, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfmt.Fprint(conn, headers)\n\t_, err = io.Copy(conn, buf)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn http.ReadResponse(bufio.NewReader(conn), nil)\n}\n\n// RawRequest is a convenience wrapper around http.Post. It is exposed only for\n// when you need to read/inspect the raw HTTP response yourself.\nfunc (c *DiscoverCardClient) RawRequest(URL string, r io.Reader) (*http.Response, error) {\n\tif !strings.HasPrefix(URL, \"https://\") {\n\t\treturn nil, errors.New(\"Refusing to send OFX request with possible plain-text password over non-https protocol\")\n\t}\n\n\tresponse, err := discoverCardHTTPPost(URL, r)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif response.StatusCode != 200 {\n\t\treturn nil, errors.New(\"OFXQuery request status: \" + response.Status)\n\t}\n\n\treturn response, nil\n}\n\n// RequestNoParse marshals a Request to XML, makes an HTTP request, and returns\n// the raw HTTP response\nfunc (c *DiscoverCardClient) RequestNoParse(r *Request) (*http.Response, error) {\n\treturn clientRequestNoParse(c, r)\n}\n\n// Request marshals a Request to XML, makes an HTTP request, and then\n// unmarshals the response into a Response object.\nfunc (c *DiscoverCardClient) Request(r *Request) (*Response, error) {\n\treturn clientRequest(c, r)\n}\n"
  },
  {
    "path": "doc.go",
    "content": "/*\nPackage ofxgo seeks to provide a library to make it easier to query and/or\nparse financial information with OFX from the comfort of Golang, without having\nto deal with marshalling/unmarshalling the SGML or XML. The library does *not*\nintend to abstract away all of the details of the OFX specification, which\nwould be difficult to do well. Instead, it exposes the OFX SGML/XML hierarchy\nas structs which mostly resemble it. For more information on OFX and to read\nthe specification, see http://ofx.net.\n\nThere are three main top-level objects defined in ofxgo. These are Client,\nRequest, and Response. The Request and Response objects represent OFX requests\nand responses as Golang structs. Client contains settings which control how\nrequests and responses are marshalled and unmarshalled (the OFX version used,\nclient id and version, whether to indent SGML/XML tags, etc.), and provides\nhelper methods for making requests and optionally parsing the response using\nthose settings.\n\nEvery Request object contains a SignonRequest element, called Signon. This\nelement contains the username, password (or key), and the ORG and FID fields\nparticular to the financial institution being queried, and an optional ClientUID\nfield (required by some FIs). Likewise, each Response contains a SignonResponse\nobject which contains, among other things, the Status of the request. Any status\nwith a nonzero Code should be inspected for a possible error (using the Severity\nand Message fields populated by the server, or the CodeMeaning() and\nCodeConditions() functions which return information about a particular code as\nspecified by the OFX specification).\n\nEach top-level Request or Response object may contain zero or more messages,\nsorted into named slices by message set, just as the OFX specification groups\nthem. Here are the supported types of Request/Response objects (along with the\nname of the slice of Messages they belong to in parentheses):\n\nRequests:\n  var r AcctInfoRequest     // (Signup) Request a list of the valid accounts\n                            //   for this user\n  var r CCStatementRequest  // (CreditCard) Request the balance (and optionally\n                            //   list of transactions) for a credit card\n  var r StatementRequest    // (Bank) Request the balance (and optionally list\n                            //   of transactions) for a bank account\n  var r InvStatementRequest // (InvStmt) Request balance, transactions,\n                            //   existing positions, and/or open orders for an\n                            //   investment account\n  var r SecListRequest      // (SecList) Request securities details and prices\n  var r ProfileRequest      // (Prof) Request the server's capabilities (which\n                            //   messages sets it supports, along with features)\n\nResponses:\n  var r AcctInfoResponse     // (Signup) List of the valid accounts for this\n                             //   user\n  var r CCStatementResponse  // (CreditCard) The balance (and optionally list of\n                             //   transactions) for a credit card\n  var r StatementResponse    // (Bank) The balance (and optionally list of\n                             //   transactions) for a bank account\n  var r InvStatementResponse // (InvStmt) The balance, transactions, existing\n                             //   positions, and/or open orders for an\n                             //   investment account\n  var r SecListResponse      // (SecList) Returned as a result of\n                             //   SecListRequest, but only contains request\n                             //   status\n  var r SecurityList         // (SecList) The actual list of securities, prices,\n                             //   etc. (sent as a result of SecListRequest or\n                             //   InvStatementRequest)\n  var r ProfileResponse      // (Prof) Describes the server's capabilities\n\nWhen constructing a Request, simply append the desired message to the message\nset it belongs to. For Responses, it is the user's responsibility to make type\nassertions on objects found inside one of these message sets before using them.\n\nFor example, the following code would request a bank statement for a checking\naccount and print the balance:\n\n  import (\n    \"fmt\"\n    \"os\"\n  )\n\n  var client Client // By not initializing them, we accept all default\n                          // client values\n  var request Request\n\n  // These are all specific to you and your financial institution\n  request.URL = \"https://ofx.example.com\"\n  request.Signon.UserID = String(\"john\")\n  request.Signon.UserPass = String(\"hunter2\")\n  request.Signon.Org = String(\"MyBank\")\n  request.Signon.Fid = String(\"0001\")\n\n  uid, err := RandomUID()\n  if err != nil {\n    fmt.Println(\"Error creating uid for transaction:\", err)\n    os.Exit(1)\n  }\n\n  statementRequest := StatementRequest{\n    TrnUID: *uid,\n    BankAcctFrom: BankAcct{\n      BankID:   String(\"123456789\"),\n      AcctID:   String(\"11111111111\"),\n      AcctType: AcctTypeChecking,\n    },\n  }\n\n  request.Bank = append(request.Bank, &statementRequest)\n\n  response, err := client.Request(request)\n  if err != nil {\n    fmt.Println(\"Error requesting account statement:\", err)\n    os.Exit(1)\n  }\n\n  if response.Signon.Status.Code != 0 {\n    meaning, _ := response.Signon.Status.CodeMeaning()\n    fmt.Printf(\"Nonzero signon status (%d: %s) with message: %s\\n\", response.Signon.Status.Code, meaning, response.Signon.Status.Message)\n    os.Exit(1)\n  }\n\n  if len(response.Bank) < 1 {\n    fmt.Println(\"No banking messages received\")\n  } else if stmt, ok := response.Bank[0].(*StatementResponse); ok {\n    fmt.Printf(\"Balance: %s %s (as of %s)\\n\", stmt.BalAmt, stmt.CurDef, stmt.DtAsOf)\n  }\n\nMore usage examples may be found in the example command-line client provided\nwith this library, in the cmd/ofx directory of the source.\n\n*/\npackage ofxgo\n"
  },
  {
    "path": "generate_constants.py",
    "content": "#!/usr/bin/env python\n\nenums = {\n    # OFX spec version\n    \"OfxVersion\": ([\"102\", \"103\", \"151\", \"160\", \"200\", \"201\", \"202\", \"203\", \"210\", \"211\", \"220\"], \"the OFX specification version in use\"),\n\n    # Bank/general\n    \"AcctType\": ([\"Checking\", \"Savings\", \"MoneyMrkt\", \"CreditLine\", \"CD\"], \"types of bank accounts\"),\n    \"TrnType\": ([\"Credit\", \"Debit\", \"Int\", \"Div\", \"Fee\", \"SrvChg\", \"Dep\", \"ATM\", \"POS\", \"Xfer\", \"Check\", \"Payment\", \"Cash\", \"DirectDep\", \"DirectDebit\", \"RepeatPmt\", \"Hold\", \"Other\"], \"types of transactions. INT, ATM, and POS depend on the signage of the account.\"),\n    \"ImageType\": ([\"Statement\", \"Transaction\", \"Tax\"], \"what this image contains\"),\n    \"ImageRefType\": ([\"Opaque\", \"URL\", \"FormURL\"], \"the type of reference to the image\"),\n    \"CheckSup\": ([\"FrontOnly\", \"BackOnly\", \"FrontAndBack\"], \"what portions of the check this image contains\"),\n    \"CorrectAction\": ([\"Delete\", \"Replace\"], \"whether this transaction correction replaces or deletes the transaction matching its CORRECTFITID\"),\n    \"BalType\": ([\"Dollar\", \"Percent\", \"Number\"], \"how this BAL's VALUE field should be interpreted\"),\n\n    # InvStmt\n    \"Inv401kSource\": ([\"PreTax\", \"AfterTax\", \"Match\", \"ProfitSharing\", \"Rollover\", \"OtherVest\", \"OtherNonVest\"], \"the source of money used for this security in a 401(k) account. Default if not present is OTHERNONVEST. The following cash source types are subject to vesting: MATCH, PROFITSHARING, and OTHERVEST.\"),\n    \"SubAcctType\": ([\"Cash\", \"Margin\", \"Short\", \"Other\"], \"the sub-account type for a source and/or destination of a transaction. Used in fields named SubAcctFrom, SubAcctTo, SubAcctSec, SubAcctFund, HeldInAcct.\"),\n    \"BuyType\": ([\"Buy\", \"BuyToCover\"], \"types of purchases\"),\n    \"OptAction\": ([\"Exercise\", \"Assign\", \"Expire\"], \"types of actions for options\"),\n    \"TferAction\": ([\"In\", \"Out\"], \"whether the transfer is into or out of this account\"),\n    \"PosType\": ([\"Long\", \"Short\"], \"position type\"),\n    \"Secured\": ([\"Naked\", \"Covered\"], \"how an option is secured\"),\n    \"Duration\": ([\"Day\", \"GoodTilCancel\", \"Immediate\"], \"how long the investment order is good for\"),\n    \"Restriction\": ([\"AllOrNone\", \"MinUnits\", \"None\"], \"a special restriction on an investment order\"),\n    \"UnitType\": ([\"Shares\", \"Currency\"], \"type of the UNITS value\"),\n    \"OptBuyType\": ([\"BuyToOpen\", \"BuyToClose\"], \"types of purchases for options\"),\n    \"SellType\": ([\"Sell\", \"SellShort\"], \"types of sales\"),\n    \"LoanPmtFreq\": ([\"Weekly\", \"Biweekly\", \"TwiceMonthly\", \"Monthly\", \"FourWeeks\", \"BiMonthly\", \"Quarterly\", \"Semiannually\", \"Annually\", \"Other\"], \"the frequency of loan payments\"),\n    \"IncomeType\": ([\"CGLong\", \"CGShort\", \"Div\", \"Interest\", \"Misc\"], \"types of investment income\"),\n \"SellReason\": ([\"Call\", \"Sell\", \"Maturity\"], \"the reason the sell of a debt security was generated: CALL (the debt was called), SELL (the debt was sold), MATURITY (the debt reached maturity)\"),\n    \"OptSellType\": ([\"SellToClose\", \"SellToOpen\"], \"types of sales for options\"),\n    \"RelType\": ([\"Spread\", \"Straddle\", \"None\", \"Other\"], \"related option transaction types\"),\n\n    # Prof\n    \"CharType\": ([\"AlphaOnly\", \"NumericOnly\", \"AlphaOrNumeric\", \"AlphaAndNumeric\"], \"types of characters allowed in password\"),\n    \"SyncMode\": ([\"Full\", \"Lite\"], \"data synchronization mode supported (see OFX spec for more details)\"),\n    \"OfxSec\": ([\"None\", \"Type 1\"], \"the type of application-level security required for the message set\"),\n\n    # SecList\n    \"DebtType\": ([\"Coupon\", \"Zero\"], \"debt type\"),\n    \"DebtClass\": ([\"Treasury\", \"Municipal\", \"Corporate\", \"Other\"], \"the class of debt\"),\n    \"CouponFreq\": ([\"Monthly\", \"Quarterly\", \"Semiannual\", \"Annual\", \"Other\"], \"when debt coupons mature\"),\n    \"CallType\": ([\"Call\", \"Put\", \"Prefund\", \"Maturity\"], \"type of next call (for a debt)\"),\n    \"AssetClass\": ([\"DomesticBond\", \"IntlBond\", \"LargeStock\", \"SmallStock\", \"IntlStock\", \"MoneyMrkt\", \"Other\"], \"type of asset classes\"),\n    \"MfType\": ([\"OpenEnd\", \"CloseEnd\", \"Other\"], \"types of mutual funds\"),\n    \"OptType\": ([\"Put\", \"Call\"], \"whether the option is a PUT or a CALL\"),\n    \"StockType\": ([\"Common\", \"Preferred\", \"Convertible\", \"Other\"], \"types of stock\"),\n\n    # Signup\n    \"HolderType\": ([\"Individual\", \"Joint\", \"Custodial\", \"Trust\", \"Other\"], \"how the account is held\"),\n    \"AcctClassification\": ([\"Personal\", \"Business\", \"Corporate\", \"Other\"], \"the type of an account\"),\n    \"SvcStatus\": ([\"Avail\", \"Pend\", \"Active\"], \"the status of the account: AVAIL = Available, but not yet requested, PEND = Requested, but not yet available, ACTIVE = In use\"),\n    \"UsProductType\": ([\"401K\", \"403B\", \"IRA\", \"KEOGH\", \"Other\", \"SARSEP\", \"Simple\", \"Normal\", \"TDA\", \"Trust\", \"UGMA\"], \"type of investment account (in the US)\"),\n}\n\nheader = \"\"\"package ofxgo\n\n/*\n * Do not edit this file by hand. It is auto-generated by calling `go generate`.\n * To make changes, edit generate_constants.py, re-run `go generate`, and check\n * in the result.\n */\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/aclindsa/xml\"\n)\n\"\"\"\n\ntemplate = \"\"\"\ntype {enumLower} uint\n\n// {enum}* constants represent {comment}\nconst (\n{constNames})\n\nvar {enumLower}s = [...]string{{\"{upperValueString}\"}}\n\nfunc (e {enumLower}) Valid() bool {{\n\t// This check is mostly out of paranoia, ensuring e != 0 should be\n\t// sufficient\n\treturn e >= {firstValue} && e <= {lastValue}\n}}\n\nfunc (e {enumLower}) String() string {{\n\tif e.Valid() {{\n\t\treturn {enumLower}s[e-1]\n\t}}\n\treturn fmt.Sprintf(\"invalid {enumLower} (%d)\", e)\n}}\n\nfunc (e *{enumLower}) FromString(in string) error {{\n\tvalue := strings.TrimSpace(in)\n\n\tfor i, s := range {enumLower}s {{\n\t\tif s == value {{\n\t\t\t*e = {enumLower}(i + 1)\n\t\t\treturn nil\n\t\t}}\n\t}}\n\t*e = 0\n\treturn errors.New(\"Invalid {enum}: \\\\\\\"\" + in + \"\\\\\\\"\")\n}}\n\nfunc (e *{enumLower}) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {{\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {{\n\t\treturn err\n\t}}\n\n\treturn e.FromString(value)\n}}\n\nfunc (e {enumLower}) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {{\n\tif !e.Valid() {{\n\t\treturn nil\n\t}}\n\tenc.EncodeElement({enumLower}s[e-1], start)\n\treturn nil\n}}\n\n// New{enum} returns returns an 'enum' value of type {enumLower} given its\n// string representation\nfunc New{enum}(s string) ({enumLower}, error) {{\n\tvar e {enumLower}\n\terr := e.FromString(s)\n\tif err != nil {{\n\t\treturn 0, err\n\t}}\n\treturn e, nil\n}}\n\"\"\"\n\nwith open(\"constants.go\", 'w') as f:\n    f.write(header)\n\n    for enum in enums:\n        enumLower = enum[:1].lower() + enum[1:].replace(\" \", \"\")\n        firstValue = enum+enums[enum][0][0].replace(\" \", \"\")\n        lastValue = enum+enums[enum][0][-1].replace(\" \", \"\")\n\n        comment = enums[enum][1]\n\n        constNames = \"\\t{firstValue} {enumLower} = 1 + iota\\n\".format(\n            enum=enum,\n            firstValue=firstValue,\n            enumLower=enumLower)\n        for value in enums[enum][0][1:]:\n            constNames += \"\\t{enum}{value}\\n\".format(\n                enum=enum,\n                value=value.replace(\" \", \"\"))\n\n        upperValueString = \"\\\", \\\"\".join([s.upper() for s in enums[enum][0]])\n\n        f.write(template.format(enum=enum,\n                              enumLower=enumLower,\n                              comment=comment,\n                              firstValue=firstValue,\n                              lastValue=lastValue,\n                              constNames=constNames,\n                              upperValueString=upperValueString))\n\ntest_header = \"\"\"package ofxgo\n\n/*\n * Do not edit this file by hand. It is auto-generated by calling `go generate`.\n * To make changes, edit generate_constants.py, re-run `go generate`, and check\n * in the result.\n */\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/aclindsa/xml\"\n)\n\"\"\"\n\ntest_template = \"\"\"\nfunc Test{enum}(t *testing.T) {{\n\te, err := New{enum}(\"{firstValueUpper}\")\n\tif err != nil {{\n\t\tt.Fatalf(\"Unexpected error creating new {enum} from string \\\\\\\"{firstValueUpper}\\\\\\\"\\\\n\")\n\t}}\n\tif !e.Valid() {{\n\t\tt.Fatalf(\"{enum} unexpectedly invalid\\\\n\")\n\t}}\n\terr = e.FromString(\"{lastValueUpper}\")\n\tif err != nil {{\n\t\tt.Fatalf(\"Unexpected error on {enum}.FromString(\\\\\\\"{lastValueUpper}\\\\\\\")\\\\n\")\n\t}}\n\tif e.String() != \"{lastValueUpper}\" {{\n\t\tt.Fatalf(\"{enum}.String() expected to be \\\\\\\"{lastValueUpper}\\\\\\\"\\\\n\")\n\t}}\n\n\tmarshalHelper(t, \"{lastValueUpper}\", &e)\n\n\toverwritten, err := New{enum}(\"THISWILLNEVERBEAVALIDENUMSTRING\")\n\tif err == nil {{\n\t\tt.Fatalf(\"Expected error creating new {enum} from string \\\\\\\"THISWILLNEVERBEAVALIDENUMSTRING\\\\\\\"\\\\n\")\n\t}}\n\tif overwritten.Valid() {{\n\t\tt.Fatalf(\"{enum} created with string \\\\\\\"THISWILLNEVERBEAVALIDENUMSTRING\\\\\\\" should not be valid\\\\n\")\n\t}}\n\tif !strings.Contains(strings.ToLower(overwritten.String()), \"invalid\") {{\n\t\tt.Fatalf(\"{enum} created with string \\\\\\\"THISWILLNEVERBEAVALIDENUMSTRING\\\\\\\" should not return valid string from String()\\\\n\")\n\t}}\n\n\tb, err := xml.Marshal(&overwritten)\n\tif err != nil {{\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal({enum}): %s\\\\n\", err)\n\t}}\n\tif string(b) != \"\" {{\n\t\tt.Fatalf(\"Expected empty string, got '%s'\\\\n\", string(b))\n\t}}\n\n\tunmarshalHelper(t, \"{lastValueUpper}\", &e, &overwritten)\n\n\terr = xml.Unmarshal([]byte(\"<GARBAGE><!LALDK>\"), &overwritten)\n\tif err == nil {{\n\t\tt.Fatalf(\"Expected error unmarshalling garbage value\\\\n\")\n\t}}\n\n\ttype SC struct {{\n\t\tE {enumLower}\n\t}}\n\tsc := SC{{E: e}}\n\tb, err = xml.Marshal(sc)\n\tif err != nil {{\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(struct {enum}): %s\\\\n\", err)\n\t}}\n\tif string(b) != \"<SC><E>{lastValueUpper}</E></SC>\" {{\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\\\n\", \"<SC><E>{lastValueUpper}</E></SC>\", string(b))\n\t}}\n}}\n\"\"\"\n\nwith open(\"constants_test.go\", 'w') as f:\n    f.write(test_header)\n\n    for enum in enums:\n        enumLower = enum[:1].lower() + enum[1:].replace(\" \", \"\")\n        firstValueUpper = enums[enum][0][0].upper()\n        lastValueUpper = enums[enum][0][-1].upper()\n        f.write(test_template.format(enum=enum,\n                              enumLower=enumLower,\n                              firstValueUpper=firstValueUpper,\n                              lastValueUpper=lastValueUpper))\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/aclindsa/ofxgo\n\nrequire (\n\tgithub.com/aclindsa/xml v0.0.0-20201125035057-bbd5c9ec99ac\n\tgolang.org/x/term v0.0.0-20210927222741-03fcf44c2211\n\tgolang.org/x/text v0.3.8\n)\n\ngo 1.9\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/aclindsa/xml v0.0.0-20201125035057-bbd5c9ec99ac h1:xCNSfPWpcx3Sdz/+aB/Re4L8oA6Y4kRRRuTh1CHCDEw=\ngithub.com/aclindsa/xml v0.0.0-20201125035057-bbd5c9ec99ac/go.mod h1:GjqOUT8xlg5+T19lFv6yAGNrtMKkZ839Gt4e16mBXlY=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=\ngolang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\n"
  },
  {
    "path": "invstmt.go",
    "content": "package ofxgo\n\nimport (\n\t\"errors\"\n\t\"github.com/aclindsa/xml\"\n)\n\n// InvStatementRequest allows a customer to request transactions, positions,\n// open orders, and balances. It specifies what types of information to include\n// in hte InvStatementResponse and which account to include it for.\ntype InvStatementRequest struct {\n\tXMLName   xml.Name `xml:\"INVSTMTTRNRQ\"`\n\tTrnUID    UID      `xml:\"TRNUID\"`\n\tCltCookie String   `xml:\"CLTCOOKIE,omitempty\"`\n\tTAN       String   `xml:\"TAN,omitempty\"` // Transaction authorization number\n\t// TODO `xml:\"OFXEXTENSION,omitempty\"`\n\tInvAcctFrom      InvAcct `xml:\"INVSTMTRQ>INVACCTFROM\"`\n\tDtStart          *Date   `xml:\"INVSTMTRQ>INCTRAN>DTSTART,omitempty\"`\n\tDtEnd            *Date   `xml:\"INVSTMTRQ>INCTRAN>DTEND,omitempty\"`\n\tInclude          Boolean `xml:\"INVSTMTRQ>INCTRAN>INCLUDE\"`         // Include transactions (instead of just balance)\n\tIncludeOO        Boolean `xml:\"INVSTMTRQ>INCOO\"`                   // Include open orders\n\tPosDtAsOf        *Date   `xml:\"INVSTMTRQ>INCPOS>DTASOF,omitempty\"` // Date that positions should be sent down for, if present\n\tIncludePos       Boolean `xml:\"INVSTMTRQ>INCPOS>INCLUDE\"`          // Include position data in response\n\tIncludeBalance   Boolean `xml:\"INVSTMTRQ>INCBAL\"`                  // Include investment balance in response\n\tInclude401K      Boolean `xml:\"INVSTMTRQ>INC401K,omitempty\"`       // Include 401k information\n\tInclude401KBal   Boolean `xml:\"INVSTMTRQ>INC401KBAL,omitempty\"`    // Include 401k balance information\n\tIncludeTranImage Boolean `xml:\"INVSTMTRQ>INCTRANIMAGE,omitempty\"`  // Include transaction images\n}\n\n// Name returns the name of the top-level transaction XML/SGML element\nfunc (r *InvStatementRequest) Name() string {\n\treturn \"INVSTMTTRNRQ\"\n}\n\n// Valid returns (true, nil) if this struct would be valid OFX if marshalled\n// into XML/SGML\nfunc (r *InvStatementRequest) Valid(version ofxVersion) (bool, error) {\n\tif ok, err := r.TrnUID.Valid(); !ok {\n\t\treturn false, err\n\t}\n\t// TODO implement\n\treturn true, nil\n}\n\n// Type returns which message set this message belongs to (which Request\n// element of type []Message it should appended to)\nfunc (r *InvStatementRequest) Type() messageType {\n\treturn InvStmtRq\n}\n\n// InvTran represents generic investment transaction. It is included in both\n// InvBuy and InvSell as well as many of the more specific transaction\n// aggregates.\ntype InvTran struct {\n\tXMLName       xml.Name `xml:\"INVTRAN\"`\n\tFiTID         String   `xml:\"FITID\"`                   // Unique FI-assigned transaction ID. This ID is used to detect duplicate downloads\n\tSrvrTID       String   `xml:\"SRVRTID,omitempty\"`       // Server assigned transaction ID\n\tDtTrade       Date     `xml:\"DTTRADE\"`                 // trade date; for stock splits, day of record\n\tDtSettle      *Date    `xml:\"DTSETTLE,omitempty\"`      // settlement date; for stock splits, execution date\n\tReversalFiTID String   `xml:\"REVERSALFITID,omitempty\"` // For a reversal transaction, the FITID of the transaction that is being reversed.\n\tMemo          String   `xml:\"MEMO,omitempty\"`\n}\n\n// InvBuy represents generic investment purchase transaction. It is included\n// in many of the more specific transaction Buy* aggregates below.\ntype InvBuy struct {\n\tXMLName      xml.Name    `xml:\"INVBUY\"`\n\tInvTran      InvTran     `xml:\"INVTRAN\"`\n\tSecID        SecurityID  `xml:\"SECID\"`\n\tUnits        Amount      `xml:\"UNITS\"`            // For stocks, MFs, other, number of shares held. Bonds = face value. Options = number of contracts\n\tUnitPrice    Amount      `xml:\"UNITPRICE\"`        // For stocks, MFs, other, price per share. Bonds = percentage of par. Option = premium per share of underlying security\n\tMarkup       Amount      `xml:\"MARKUP,omitempty\"` // Portion of UNITPRICE that is attributed to the dealer markup\n\tCommission   Amount      `xml:\"COMMISSION,omitempty\"`\n\tTaxes        Amount      `xml:\"TAXES,omitempty\"`\n\tFees         Amount      `xml:\"FEES,omitempty\"`\n\tLoad         Amount      `xml:\"LOAD,omitempty\"`\n\tTotal        Amount      `xml:\"TOTAL\"`                  // Transaction total. Buys, sells, etc.:((quan. * (price +/- markup/markdown)) +/-(commission + fees + load + taxes + penalty + withholding + statewithholding)). Distributions, interest, margin interest, misc. expense, etc.: amount. Return of cap: cost basis\n\tCurrency     Currency    `xml:\"CURRENCY,omitempty\"`     // Represents the currency this transaction is in (instead of CURDEF in INVSTMTRS) if Valid()\n\tOrigCurrency Currency    `xml:\"ORIGCURRENCY,omitempty\"` // Represents the currency this transaction was converted to INVSTMTRS' CURDEF from if Valid\n\tSubAcctSec   subAcctType `xml:\"SUBACCTSEC\"`             // Sub-account type for this security. One of CASH, MARGIN, SHORT, OTHER\n\tSubAcctFund  subAcctType `xml:\"SUBACCTFUND\"`            // Where did the money for the transaction come from or go to? CASH, MARGIN, SHORT, OTHER\n\n\t// The next three elements must either all be provided, or none of them\n\tLoanID        String `xml:\"LOANID,omitempty\"`        // For 401(k) accounts only. Indicates that the transaction was due to a loan or a loan repayment, and which loan it was\n\tLoanPrincipal Amount `xml:\"LOANPRINCIPAL,omitempty\"` // For 401(k) accounts only. Indicates how much of the loan repayment was principal\n\tLoanInterest  Amount `xml:\"LOANINTEREST,omitempty\"`  // For 401(k) accounts only. Indicates how much of the loan repayment was interest\n\n\tInv401kSource    inv401kSource `xml:\"INV401KSOURCE,omitempty\"`    // Source of money for this transaction. 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\n\tDtPayroll        *Date         `xml:\"DTPAYROLL,omitempty\"`        // For 401(k)accounts, date the funds for this transaction was obtained via payroll deduction\n\tPriorYearContrib Boolean       `xml:\"PRIORYEARCONTRIB,omitempty\"` // For 401(k) accounts, indicates that this Buy was made with a prior year contribution\n}\n\n// InvSell represents generic investment sale transaction. It is included in\n// many of the more specific transaction Sell* aggregates below.\ntype InvSell struct {\n\tXMLName      xml.Name    `xml:\"INVSELL\"`\n\tInvTran      InvTran     `xml:\"INVTRAN\"`\n\tSecID        SecurityID  `xml:\"SECID\"`\n\tUnits        Amount      `xml:\"UNITS\"`              // For stocks, MFs, other, number of shares held. Bonds = face value. Options = number of contracts\n\tUnitPrice    Amount      `xml:\"UNITPRICE\"`          // For stocks, MFs, other, price per share. Bonds = percentage of par. Option = premium per share of underlying security\n\tMarkdown     Amount      `xml:\"MARKDOWN,omitempty\"` // Portion of UNITPRICE that is attributed to the dealer markdown\n\tCommission   Amount      `xml:\"COMMISSION,omitempty\"`\n\tTaxes        Amount      `xml:\"TAXES,omitempty\"`\n\tFees         Amount      `xml:\"FEES,omitempty\"`\n\tLoad         Amount      `xml:\"LOAD,omitempty\"`\n\tWithholding  Amount      `xml:\"WITHHOLDING,omitempty\"`  // Federal tax withholdings\n\tTaxExempt    Boolean     `xml:\"TAXEXEMPT,omitempty\"`    // Tax-exempt transaction\n\tTotal        Amount      `xml:\"TOTAL\"`                  // Transaction total. Buys, sells, etc.:((quan. * (price +/- markup/markdown)) +/-(commission + fees + load + taxes + penalty + withholding + statewithholding)). Distributions, interest, margin interest, misc. expense, etc.: amount. Return of cap: cost basis\n\tGain         Amount      `xml:\"GAIN,omitempty\"`         // Total gain\n\tCurrency     Currency    `xml:\"CURRENCY,omitempty\"`     // Represents the currency this transaction is in (instead of CURDEF in INVSTMTRS) if Valid()\n\tOrigCurrency Currency    `xml:\"ORIGCURRENCY,omitempty\"` // Represents the currency this transaction was converted to INVSTMTRS' CURDEF from if Valid\n\tSubAcctSec   subAcctType `xml:\"SUBACCTSEC\"`             // Sub-account type for this security. One of CASH, MARGIN, SHORT, OTHER\n\tSubAcctFund  subAcctType `xml:\"SUBACCTFUND\"`            // Where did the money for the transaction come from or go to? CASH, MARGIN, SHORT, OTHER\n\n\tLoanID           String `xml:\"LOANID,omitempty\"`           // For 401(k) accounts only. Indicates that the transaction was due to a loan or a loan repayment, and which loan it was\n\tStateWithholding Amount `xml:\"STATEWITHHOLDING,omitempty\"` // State tax withholdings\n\tPenalty          Amount `xml:\"PENALTY,omitempty\"`          // Amount withheld due to penalty\n\n\tInv401kSource inv401kSource `xml:\"INV401KSOURCE,omitempty\"` // Source of money for this transaction. 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\n}\n\n// BuyDebt represents a transaction purchasing a debt security\ntype BuyDebt struct {\n\tXMLName  xml.Name `xml:\"BUYDEBT\"`\n\tInvBuy   InvBuy   `xml:\"INVBUY\"`\n\tAccrdInt Amount   `xml:\"ACCRDINT,omitempty\"` // Accrued interest. This amount is not reflected in the <TOTAL> field of a containing aggregate.\n}\n\n// TransactionType returns a string representation of this transaction's type\nfunc (t BuyDebt) TransactionType() string {\n\treturn \"BUYDEBT\"\n}\n\nfunc (t BuyDebt) InvTransaction() InvTran {\n\treturn t.InvBuy.InvTran\n}\n\n// BuyMF represents a transaction purchasing a mutual fund\ntype BuyMF struct {\n\tXMLName  xml.Name `xml:\"BUYMF\"`\n\tInvBuy   InvBuy   `xml:\"INVBUY\"`\n\tBuyType  buyType  `xml:\"BUYTYPE\"`            // One of BUY, BUYTOCOVER (BUYTOCOVER used to close short sales.)\n\tRelFiTID String   `xml:\"RELFITID,omitempty\"` // used to relate transactions associated with mutual fund exchanges\n}\n\n// TransactionType returns a string representation of this transaction's type\nfunc (t BuyMF) TransactionType() string {\n\treturn \"BUYMF\"\n}\n\nfunc (t BuyMF) InvTransaction() InvTran {\n\treturn t.InvBuy.InvTran\n}\n\n// BuyOpt represents a transaction purchasing an option\ntype BuyOpt struct {\n\tXMLName    xml.Name   `xml:\"BUYOPT\"`\n\tInvBuy     InvBuy     `xml:\"INVBUY\"`\n\tOptBuyType optBuyType `xml:\"OPTBUYTYPE\"` // type of purchase: BUYTOOPEN, BUYTOCLOSE (The BUYTOOPEN buy type is like “ordinary” buying of option and works like stocks.)\n\tShPerCtrct Int        `xml:\"SHPERCTRCT\"` // Shares per contract\n}\n\n// TransactionType returns a string representation of this transaction's type\nfunc (t BuyOpt) TransactionType() string {\n\treturn \"BUYOPT\"\n}\n\nfunc (t BuyOpt) InvTransaction() InvTran {\n\treturn t.InvBuy.InvTran\n}\n\n// BuyOther represents a transaction purchasing a type of security not covered\n// by the other Buy* structs\ntype BuyOther struct {\n\tXMLName xml.Name `xml:\"BUYOTHER\"`\n\tInvBuy  InvBuy   `xml:\"INVBUY\"`\n}\n\n// TransactionType returns a string representation of this transaction's type\nfunc (t BuyOther) TransactionType() string {\n\treturn \"BUYOTHER\"\n}\n\nfunc (t BuyOther) InvTransaction() InvTran {\n\treturn t.InvBuy.InvTran\n}\n\n// BuyStock represents a transaction purchasing stock\ntype BuyStock struct {\n\tXMLName xml.Name `xml:\"BUYSTOCK\"`\n\tInvBuy  InvBuy   `xml:\"INVBUY\"`\n\tBuyType buyType  `xml:\"BUYTYPE\"` // One of BUY, BUYTOCOVER (BUYTOCOVER used to close short sales.)\n}\n\n// TransactionType returns a string representation of this transaction's type\nfunc (t BuyStock) TransactionType() string {\n\treturn \"BUYSTOCK\"\n}\n\nfunc (t BuyStock) InvTransaction() InvTran {\n\treturn t.InvBuy.InvTran\n}\n\n// ClosureOpt represents a transaction closing a position for an option\ntype ClosureOpt struct {\n\tXMLName    xml.Name    `xml:\"CLOSUREOPT\"`\n\tInvTran    InvTran     `xml:\"INVTRAN\"`\n\tSecID      SecurityID  `xml:\"SECID\"`\n\tOptAction  optAction   `xml:\"OPTACTION\"`          // One of EXERCISE, ASSIGN, EXPIRE. The EXERCISE action is used to close out an option that is exercised. The ASSIGN action is used when an option writer is assigned. The EXPIRE action is used when the option’s expired date is reached\n\tUnits      Amount      `xml:\"UNITS\"`              // For stocks, MFs, other, number of shares held. Bonds = face value. Options = number of contracts\n\tShPerCtrct Int         `xml:\"SHPERCTRCT\"`         // Shares per contract\n\tSubAcctSec subAcctType `xml:\"SUBACCTSEC\"`         // Sub-account type for this security. One of CASH, MARGIN, SHORT, OTHER\n\tRelFiTID   String      `xml:\"RELFITID,omitempty\"` // used to relate transactions associated with mutual fund exchanges\n\tGain       Amount      `xml:\"GAIN,omitempty\"`     // Total gain\n}\n\n// TransactionType returns a string representation of this transaction's type\nfunc (t ClosureOpt) TransactionType() string {\n\treturn \"CLOSUREOPT\"\n}\n\nfunc (t ClosureOpt) InvTransaction() InvTran {\n\treturn t.InvTran\n}\n\n// Income represents a transaction where investment income is being realized as\n// cash into the investment account\ntype Income struct {\n\tXMLName       xml.Name      `xml:\"INCOME\"`\n\tInvTran       InvTran       `xml:\"INVTRAN\"`\n\tSecID         SecurityID    `xml:\"SECID\"`\n\tIncomeType    incomeType    `xml:\"INCOMETYPE\"` // Type of investment income: CGLONG (capital gains-long term), CGSHORT (capital gains-short term), DIV (dividend), INTEREST, MISC\n\tTotal         Amount        `xml:\"TOTAL\"`\n\tSubAcctSec    subAcctType   `xml:\"SUBACCTSEC\"`              // Sub-account type for this security. One of CASH, MARGIN, SHORT, OTHER\n\tSubAcctFund   subAcctType   `xml:\"SUBACCTFUND\"`             // Where did the money for the transaction come from or go to? CASH, MARGIN, SHORT, OTHER\n\tTaxExempt     Boolean       `xml:\"TAXEXEMPT,omitempty\"`     // Tax-exempt transaction\n\tWithholding   Amount        `xml:\"WITHHOLDING,omitempty\"`   // Federal tax withholdings\n\tCurrency      Currency      `xml:\"CURRENCY,omitempty\"`      // Represents the currency this transaction is in (instead of CURDEF in INVSTMTRS) if Valid()\n\tOrigCurrency  Currency      `xml:\"ORIGCURRENCY,omitempty\"`  // Represents the currency this transaction was converted to INVSTMTRS' CURDEF from if Valid\n\tInv401kSource inv401kSource `xml:\"INV401KSOURCE,omitempty\"` // Source of money for this transaction. 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\n}\n\n// TransactionType returns a string representation of this transaction's type\nfunc (t Income) TransactionType() string {\n\treturn \"INCOME\"\n}\n\nfunc (t Income) InvTransaction() InvTran {\n\treturn t.InvTran\n}\n\n// InvExpense represents a transaction realizing an expense associated with an\n// investment\ntype InvExpense struct {\n\tXMLName       xml.Name      `xml:\"INVEXPENSE\"`\n\tInvTran       InvTran       `xml:\"INVTRAN\"`\n\tSecID         SecurityID    `xml:\"SECID\"`\n\tTotal         Amount        `xml:\"TOTAL\"`\n\tSubAcctSec    subAcctType   `xml:\"SUBACCTSEC\"`              // Sub-account type for this security. One of CASH, MARGIN, SHORT, OTHER\n\tSubAcctFund   subAcctType   `xml:\"SUBACCTFUND\"`             // Where did the money for the transaction come from or go to? CASH, MARGIN, SHORT, OTHER\n\tCurrency      Currency      `xml:\"CURRENCY,omitempty\"`      // Represents the currency this transaction is in (instead of CURDEF in INVSTMTRS) if Valid()\n\tOrigCurrency  Currency      `xml:\"ORIGCURRENCY,omitempty\"`  // Represents the currency this transaction was converted to INVSTMTRS' CURDEF from if Valid\n\tInv401kSource inv401kSource `xml:\"INV401KSOURCE,omitempty\"` // Source of money for this transaction. 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\n}\n\n// TransactionType returns a string representation of this transaction's type\nfunc (t InvExpense) TransactionType() string {\n\treturn \"INVEXPENSE\"\n}\n\nfunc (t InvExpense) InvTransaction() InvTran {\n\treturn t.InvTran\n}\n\n// JrnlFund represents a transaction journaling cash holdings between\n// sub-accounts within the same investment account\ntype JrnlFund struct {\n\tXMLName     xml.Name    `xml:\"JRNLFUND\"`\n\tInvTran     InvTran     `xml:\"INVTRAN\"`\n\tTotal       Amount      `xml:\"TOTAL\"`\n\tSubAcctFrom subAcctType `xml:\"SUBACCTFROM\"` // Sub-account cash is being transferred from: CASH, MARGIN, SHORT, OTHER\n\tSubAcctTo   subAcctType `xml:\"SUBACCTTO\"`   // Sub-account cash is being transferred to: CASH, MARGIN, SHORT, OTHER\n}\n\n// TransactionType returns a string representation of this transaction's type\nfunc (t JrnlFund) TransactionType() string {\n\treturn \"JRNLFUND\"\n}\n\nfunc (t JrnlFund) InvTransaction() InvTran {\n\treturn t.InvTran\n}\n\n// JrnlSec represents a transaction journaling security holdings between\n// sub-accounts within the same investment account\ntype JrnlSec struct {\n\tXMLName     xml.Name    `xml:\"JRNLSEC\"`\n\tInvTran     InvTran     `xml:\"INVTRAN\"`\n\tSecID       SecurityID  `xml:\"SECID\"`\n\tSubAcctFrom subAcctType `xml:\"SUBACCTFROM\"` // Sub-account cash is being transferred from: CASH, MARGIN, SHORT, OTHER\n\tSubAcctTo   subAcctType `xml:\"SUBACCTTO\"`   // Sub-account cash is being transferred to: CASH, MARGIN, SHORT, OTHER\n\tUnits       Amount      `xml:\"UNITS\"`       // For stocks, MFs, other, number of shares held. Bonds = face value. Options = number of contracts\n}\n\n// TransactionType returns a string representation of this transaction's type\nfunc (t JrnlSec) TransactionType() string {\n\treturn \"JRNLSEC\"\n}\n\nfunc (t JrnlSec) InvTransaction() InvTran {\n\treturn t.InvTran\n}\n\n// MarginInterest represents a transaction realizing a margin interest expense\ntype MarginInterest struct {\n\tXMLName      xml.Name    `xml:\"MARGININTEREST\"`\n\tInvTran      InvTran     `xml:\"INVTRAN\"`\n\tTotal        Amount      `xml:\"TOTAL\"`\n\tSubAcctFund  subAcctType `xml:\"SUBACCTFUND\"`            // Where did the money for the transaction come from or go to? CASH, MARGIN, SHORT, OTHER\n\tCurrency     Currency    `xml:\"CURRENCY,omitempty\"`     // Represents the currency this transaction is in (instead of CURDEF in INVSTMTRS) if Valid()\n\tOrigCurrency Currency    `xml:\"ORIGCURRENCY,omitempty\"` // Represents the currency this transaction was converted to INVSTMTRS' CURDEF from if Valid\n}\n\n// TransactionType returns a string representation of this transaction's type\nfunc (t MarginInterest) TransactionType() string {\n\treturn \"MARGININTEREST\"\n}\n\nfunc (t MarginInterest) InvTransaction() InvTran {\n\treturn t.InvTran\n}\n\n// Reinvest is a single transaction that contains both income and an investment\n// transaction. If servers can’t track this as a single transaction they should\n// return an Income transaction and an InvTran.\ntype Reinvest struct {\n\tXMLName       xml.Name      `xml:\"REINVEST\"`\n\tInvTran       InvTran       `xml:\"INVTRAN\"`\n\tSecID         SecurityID    `xml:\"SECID\"`\n\tIncomeType    incomeType    `xml:\"INCOMETYPE\"` // Type of investment income: CGLONG (capital gains-long term), CGSHORT (capital gains-short term), DIV (dividend), INTEREST, MISC\n\tTotal         Amount        `xml:\"TOTAL\"`      // Transaction total. Buys, sells, etc.:((quan. * (price +/- markup/markdown)) +/-(commission + fees + load + taxes + penalty + withholding + statewithholding)). Distributions, interest, margin interest, misc. expense, etc.: amount. Return of cap: cost basis\n\tSubAcctSec    subAcctType   `xml:\"SUBACCTSEC\"` // Sub-account type for this security. One of CASH, MARGIN, SHORT, OTHER\n\tUnits         Amount        `xml:\"UNITS\"`      // For stocks, MFs, other, number of shares held. Bonds = face value. Options = number of contracts\n\tUnitPrice     Amount        `xml:\"UNITPRICE\"`  // For stocks, MFs, other, price per share. Bonds = percentage of par. Option = premium per share of underlying security\n\tCommission    Amount        `xml:\"COMMISSION,omitempty\"`\n\tTaxes         Amount        `xml:\"TAXES,omitempty\"`\n\tFees          Amount        `xml:\"FEES,omitempty\"`\n\tLoad          Amount        `xml:\"LOAD,omitempty\"`\n\tTaxExempt     Boolean       `xml:\"TAXEXEMPT,omitempty\"`     // Tax-exempt transaction\n\tCurrency      Currency      `xml:\"CURRENCY,omitempty\"`      // Represents the currency this transaction is in (instead of CURDEF in INVSTMTRS) if Valid()\n\tOrigCurrency  Currency      `xml:\"ORIGCURRENCY,omitempty\"`  // Represents the currency this transaction was converted to INVSTMTRS' CURDEF from if Valid\n\tInv401kSource inv401kSource `xml:\"INV401KSOURCE,omitempty\"` // Source of money for this transaction. 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\n}\n\n// TransactionType returns a string representation of this transaction's type\nfunc (t Reinvest) TransactionType() string {\n\treturn \"REINVEST\"\n}\n\nfunc (t Reinvest) InvTransaction() InvTran {\n\treturn t.InvTran\n}\n\n// RetOfCap represents a transaction where capital is being returned to the\n// account holder\ntype RetOfCap struct {\n\tXMLName       xml.Name      `xml:\"RETOFCAP\"`\n\tInvTran       InvTran       `xml:\"INVTRAN\"`\n\tSecID         SecurityID    `xml:\"SECID\"`\n\tTotal         Amount        `xml:\"TOTAL\"`\n\tSubAcctSec    subAcctType   `xml:\"SUBACCTSEC\"`              // Sub-account type for this security. One of CASH, MARGIN, SHORT, OTHER\n\tSubAcctFund   subAcctType   `xml:\"SUBACCTFUND\"`             // Where did the money for the transaction come from or go to? CASH, MARGIN, SHORT, OTHER\n\tCurrency      Currency      `xml:\"CURRENCY,omitempty\"`      // Represents the currency this transaction is in (instead of CURDEF in INVSTMTRS) if Valid()\n\tOrigCurrency  Currency      `xml:\"ORIGCURRENCY,omitempty\"`  // Represents the currency this transaction was converted to INVSTMTRS' CURDEF from if Valid\n\tInv401kSource inv401kSource `xml:\"INV401KSOURCE,omitempty\"` // Source of money for this transaction. 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\n}\n\n// TransactionType returns a string representation of this transaction's type\nfunc (t RetOfCap) TransactionType() string {\n\treturn \"RETOFCAP\"\n}\n\nfunc (t RetOfCap) InvTransaction() InvTran {\n\treturn t.InvTran\n}\n\n// SellDebt represents the sale of a debt security. Used when debt is sold,\n// called, or reaches maturity.\ntype SellDebt struct {\n\tXMLName    xml.Name   `xml:\"SELLDEBT\"`\n\tInvSell    InvSell    `xml:\"INVSELL\"`\n\tSellReason sellReason `xml:\"SELLREASON\"`         // CALL (the debt was called), SELL (the debt was sold), MATURITY (the debt reached maturity)\n\tAccrdInt   Amount     `xml:\"ACCRDINT,omitempty\"` // Accrued interest\n}\n\n// TransactionType returns a string representation of this transaction's type\nfunc (t SellDebt) TransactionType() string {\n\treturn \"SELLDEBT\"\n}\n\nfunc (t SellDebt) InvTransaction() InvTran {\n\treturn t.InvSell.InvTran\n}\n\n// SellMF represents a transaction selling a mutual fund\ntype SellMF struct {\n\tXMLName      xml.Name `xml:\"SELLMF\"`\n\tInvSell      InvSell  `xml:\"INVSELL\"`\n\tSellType     sellType `xml:\"SELLTYPE\"` // Type of sell. SELL, SELLSHORT\n\tAvgCostBasis Amount   `xml:\"AVGCOSTBASIS\"`\n\tRelFiTID     String   `xml:\"RELFITID,omitempty\"` // used to relate transactions associated with mutual fund exchanges\n}\n\n// TransactionType returns a string representation of this transaction's type\nfunc (t SellMF) TransactionType() string {\n\treturn \"SELLMF\"\n}\n\nfunc (t SellMF) InvTransaction() InvTran {\n\treturn t.InvSell.InvTran\n}\n\n// SellOpt represents a transaction selling an option. Depending on the value\n// of OptSellType, can be used to sell a previously bought option or write a\n// new option.\ntype SellOpt struct {\n\tXMLName     xml.Name    `xml:\"SELLOPT\"`\n\tInvSell     InvSell     `xml:\"INVSELL\"`\n\tOptSellType optSellType `xml:\"OPTSELLTYPE\"`        // For options, type of sell: SELLTOCLOSE, SELLTOOPEN. The SELLTOCLOSE action is selling a previously bought option. The SELLTOOPEN action is writing an option\n\tShPerCtrct  Int         `xml:\"SHPERCTRCT\"`         // Shares per contract\n\tRelFiTID    String      `xml:\"RELFITID,omitempty\"` // used to relate transactions associated with mutual fund exchanges\n\tRelType     relType     `xml:\"RELTYPE,omitempty\"`  // Related option transaction type: SPREAD, STRADDLE, NONE, OTHER\n\tSecured     secured     `xml:\"SECURED,omitempty\"`  // NAKED, COVERED\n}\n\n// TransactionType returns a string representation of this transaction's type\nfunc (t SellOpt) TransactionType() string {\n\treturn \"SELLOPT\"\n}\n\nfunc (t SellOpt) InvTransaction() InvTran {\n\treturn t.InvSell.InvTran\n}\n\n// SellOther represents a transaction selling a security type not covered by\n// the other Sell* structs\ntype SellOther struct {\n\tXMLName xml.Name `xml:\"SELLOTHER\"`\n\tInvSell InvSell  `xml:\"INVSELL\"`\n}\n\n// TransactionType returns a string representation of this transaction's type\nfunc (t SellOther) TransactionType() string {\n\treturn \"SELLOTHER\"\n}\n\nfunc (t SellOther) InvTransaction() InvTran {\n\treturn t.InvSell.InvTran\n}\n\n// SellStock represents a transaction selling stock\ntype SellStock struct {\n\tXMLName  xml.Name `xml:\"SELLSTOCK\"`\n\tInvSell  InvSell  `xml:\"INVSELL\"`\n\tSellType sellType `xml:\"SELLTYPE\"` // Type of sell. SELL, SELLSHORT\n}\n\n// TransactionType returns a string representation of this transaction's type\nfunc (t SellStock) TransactionType() string {\n\treturn \"SELLSTOCK\"\n}\n\nfunc (t SellStock) InvTransaction() InvTran {\n\treturn t.InvSell.InvTran\n}\n\n// Split represents a stock or mutual fund split\ntype Split struct {\n\tXMLName       xml.Name      `xml:\"SPLIT\"`\n\tInvTran       InvTran       `xml:\"INVTRAN\"`\n\tSecID         SecurityID    `xml:\"SECID\"`\n\tSubAcctSec    subAcctType   `xml:\"SUBACCTSEC\"`              // Sub-account type for this security. One of CASH, MARGIN, SHORT, OTHER\n\tOldUnits      Amount        `xml:\"OLDUNITS\"`                // number of shares before the split\n\tNewUnits      Amount        `xml:\"NEWUNITS\"`                // number of shares after the split\n\tNumerator     Int           `xml:\"NUMERATOR\"`               // split ratio numerator\n\tDenominator   Int           `xml:\"DENOMINATOR\"`             // split ratio denominator\n\tCurrency      Currency      `xml:\"CURRENCY,omitempty\"`      // Represents the currency this transaction is in (instead of CURDEF in INVSTMTRS) if Valid()\n\tOrigCurrency  Currency      `xml:\"ORIGCURRENCY,omitempty\"`  // Represents the currency this transaction was converted to INVSTMTRS' CURDEF from if Valid\n\tFracCash      Amount        `xml:\"FRACCASH,omitempty\"`      // cash for fractional units\n\tSubAcctFund   subAcctType   `xml:\"SUBACCTFUND,omitempty\"`   // Where did the money for the transaction come from or go to? CASH, MARGIN, SHORT, OTHER\n\tInv401kSource inv401kSource `xml:\"INV401KSOURCE,omitempty\"` // Source of money for this transaction. 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\n}\n\n// TransactionType returns a string representation of this transaction's type\nfunc (t Split) TransactionType() string {\n\treturn \"SPLIT\"\n}\n\nfunc (t Split) InvTransaction() InvTran {\n\treturn t.InvTran\n}\n\n// Transfer represents the transfer of securities into or out of an account\ntype Transfer struct {\n\tXMLName       xml.Name      `xml:\"TRANSFER\"`\n\tInvTran       InvTran       `xml:\"INVTRAN\"`\n\tSecID         SecurityID    `xml:\"SECID\"`\n\tSubAcctSec    subAcctType   `xml:\"SUBACCTSEC\"` // Sub-account type for this security. One of CASH, MARGIN, SHORT, OTHER\n\tUnits         Amount        `xml:\"UNITS\"`      // For stocks, MFs, other, number of shares held. Bonds = face value. Options = number of contracts\n\tTferAction    tferAction    `xml:\"TFERACTION\"` // One of IN, OUT\n\tPosType       posType       `xml:\"POSTYPE\"`    // Position type. One of LONG, SHORT\n\tInvAcctFrom   InvAcct       `xml:\"INVACCTFROM,omitempty\"`\n\tAvgCostBasis  Amount        `xml:\"AVGCOSTBASIS,omitempty\"`\n\tUnitPrice     Amount        `xml:\"UNITPRICE,omitempty\"` // For stocks, MFs, other, price per share. Bonds = percentage of par. Option = premium per share of underlying security\n\tDtPurchase    *Date         `xml:\"DTPURCHASE,omitempty\"`\n\tInv401kSource inv401kSource `xml:\"INV401KSOURCE,omitempty\"` // Source of money for this transaction. 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\n}\n\n// TransactionType returns a string representation of this transaction's type\nfunc (t Transfer) TransactionType() string {\n\treturn \"TRANSFER\"\n}\n\nfunc (t Transfer) InvTransaction() InvTran {\n\treturn t.InvTran\n}\n\n// InvTransaction is a generic interface met by all investment transactions\n// (Buy*, Sell*, & co.)\ntype InvTransaction interface {\n\tTransactionType() string\n\tInvTransaction() InvTran\n}\n\n// InvBankTransaction is a banking transaction performed in an investment\n// account. This represents all transactions not related to securities - for\n// instance, funding the account using cash from another bank.\ntype InvBankTransaction struct {\n\tXMLName      xml.Name      `xml:\"INVBANKTRAN\"`\n\tTransactions []Transaction `xml:\"STMTTRN,omitempty\"`\n\tSubAcctFund  subAcctType   `xml:\"SUBACCTFUND\"` // Where did the money for the transaction come from or go to? CASH, MARGIN, SHORT, OTHER\n}\n\n// InvTranList represents a list of investment account transactions. It\n// includes the date range its transactions cover, as well as the bank- and\n// security-related transactions themselves. It must be unmarshalled manually\n// due to the structure (don't know what kind of InvTransaction is coming next)\ntype InvTranList struct {\n\tXMLName          xml.Name `xml:\"INVTRANLIST\"`\n\tDtStart          Date\n\tDtEnd            Date // This is the value that should be sent as <DTSTART> in the next InvStatementRequest to ensure that no transactions are missed\n\tInvTransactions  []InvTransaction\n\tBankTransactions []InvBankTransaction\n}\n\n// UnmarshalXML handles unmarshalling an InvTranList element from an SGML/XML\n// string\nfunc (l *InvTranList) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tfor {\n\t\ttok, err := nextNonWhitespaceToken(d)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t} else if end, ok := tok.(xml.EndElement); ok && end.Name.Local == start.Name.Local {\n\t\t\t// If we found the end of our starting element, we're done parsing\n\t\t\treturn nil\n\t\t} else if startElement, ok := tok.(xml.StartElement); ok {\n\t\t\tswitch startElement.Name.Local {\n\t\t\tcase \"DTSTART\":\n\t\t\t\tvar dtstart Date\n\t\t\t\tif err := d.DecodeElement(&dtstart, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tl.DtStart = dtstart\n\t\t\tcase \"DTEND\":\n\t\t\t\tvar dtend Date\n\t\t\t\tif err := d.DecodeElement(&dtend, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tl.DtEnd = dtend\n\t\t\tcase \"BUYDEBT\":\n\t\t\t\tvar tran BuyDebt\n\t\t\t\tif err := d.DecodeElement(&tran, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tl.InvTransactions = append(l.InvTransactions, InvTransaction(tran))\n\t\t\tcase \"BUYMF\":\n\t\t\t\tvar tran BuyMF\n\t\t\t\tif err := d.DecodeElement(&tran, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tl.InvTransactions = append(l.InvTransactions, InvTransaction(tran))\n\t\t\tcase \"BUYOPT\":\n\t\t\t\tvar tran BuyOpt\n\t\t\t\tif err := d.DecodeElement(&tran, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tl.InvTransactions = append(l.InvTransactions, InvTransaction(tran))\n\t\t\tcase \"BUYOTHER\":\n\t\t\t\tvar tran BuyOther\n\t\t\t\tif err := d.DecodeElement(&tran, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tl.InvTransactions = append(l.InvTransactions, InvTransaction(tran))\n\t\t\tcase \"BUYSTOCK\":\n\t\t\t\tvar tran BuyStock\n\t\t\t\tif err := d.DecodeElement(&tran, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tl.InvTransactions = append(l.InvTransactions, InvTransaction(tran))\n\t\t\tcase \"CLOSUREOPT\":\n\t\t\t\tvar tran ClosureOpt\n\t\t\t\tif err := d.DecodeElement(&tran, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tl.InvTransactions = append(l.InvTransactions, InvTransaction(tran))\n\t\t\tcase \"INCOME\":\n\t\t\t\tvar tran Income\n\t\t\t\tif err := d.DecodeElement(&tran, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tl.InvTransactions = append(l.InvTransactions, InvTransaction(tran))\n\t\t\tcase \"INVEXPENSE\":\n\t\t\t\tvar tran InvExpense\n\t\t\t\tif err := d.DecodeElement(&tran, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tl.InvTransactions = append(l.InvTransactions, InvTransaction(tran))\n\t\t\tcase \"JRNLFUND\":\n\t\t\t\tvar tran JrnlFund\n\t\t\t\tif err := d.DecodeElement(&tran, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tl.InvTransactions = append(l.InvTransactions, InvTransaction(tran))\n\t\t\tcase \"JRNLSEC\":\n\t\t\t\tvar tran JrnlSec\n\t\t\t\tif err := d.DecodeElement(&tran, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tl.InvTransactions = append(l.InvTransactions, InvTransaction(tran))\n\t\t\tcase \"MARGININTEREST\":\n\t\t\t\tvar tran MarginInterest\n\t\t\t\tif err := d.DecodeElement(&tran, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tl.InvTransactions = append(l.InvTransactions, InvTransaction(tran))\n\t\t\tcase \"REINVEST\":\n\t\t\t\tvar tran Reinvest\n\t\t\t\tif err := d.DecodeElement(&tran, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tl.InvTransactions = append(l.InvTransactions, InvTransaction(tran))\n\t\t\tcase \"RETOFCAP\":\n\t\t\t\tvar tran RetOfCap\n\t\t\t\tif err := d.DecodeElement(&tran, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tl.InvTransactions = append(l.InvTransactions, InvTransaction(tran))\n\t\t\tcase \"SELLDEBT\":\n\t\t\t\tvar tran SellDebt\n\t\t\t\tif err := d.DecodeElement(&tran, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tl.InvTransactions = append(l.InvTransactions, InvTransaction(tran))\n\t\t\tcase \"SELLMF\":\n\t\t\t\tvar tran SellMF\n\t\t\t\tif err := d.DecodeElement(&tran, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tl.InvTransactions = append(l.InvTransactions, InvTransaction(tran))\n\t\t\tcase \"SELLOPT\":\n\t\t\t\tvar tran SellOpt\n\t\t\t\tif err := d.DecodeElement(&tran, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tl.InvTransactions = append(l.InvTransactions, InvTransaction(tran))\n\t\t\tcase \"SELLOTHER\":\n\t\t\t\tvar tran SellOther\n\t\t\t\tif err := d.DecodeElement(&tran, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tl.InvTransactions = append(l.InvTransactions, InvTransaction(tran))\n\t\t\tcase \"SELLSTOCK\":\n\t\t\t\tvar tran SellStock\n\t\t\t\tif err := d.DecodeElement(&tran, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tl.InvTransactions = append(l.InvTransactions, InvTransaction(tran))\n\t\t\tcase \"SPLIT\":\n\t\t\t\tvar tran Split\n\t\t\t\tif err := d.DecodeElement(&tran, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tl.InvTransactions = append(l.InvTransactions, InvTransaction(tran))\n\t\t\tcase \"TRANSFER\":\n\t\t\t\tvar tran Transfer\n\t\t\t\tif err := d.DecodeElement(&tran, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tl.InvTransactions = append(l.InvTransactions, InvTransaction(tran))\n\t\t\tcase \"INVBANKTRAN\":\n\t\t\t\tvar tran InvBankTransaction\n\t\t\t\tif err := d.DecodeElement(&tran, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tl.BankTransactions = append(l.BankTransactions, tran)\n\t\t\tdefault:\n\t\t\t\treturn errors.New(\"Invalid INVTRANLIST child tag: \" + startElement.Name.Local)\n\t\t\t}\n\t\t} else {\n\t\t\treturn errors.New(\"Didn't find an opening element\")\n\t\t}\n\t}\n}\n\n// MarshalXML handles marshalling an InvTranList element to an SGML/XML string\nfunc (l *InvTranList) MarshalXML(e *xml.Encoder, start xml.StartElement) error {\n\tinvTranListElement := xml.StartElement{Name: xml.Name{Local: \"INVTRANLIST\"}}\n\tif err := e.EncodeToken(invTranListElement); err != nil {\n\t\treturn err\n\t}\n\terr := e.EncodeElement(&l.DtStart, xml.StartElement{Name: xml.Name{Local: \"DTSTART\"}})\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = e.EncodeElement(&l.DtEnd, xml.StartElement{Name: xml.Name{Local: \"DTEND\"}})\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, t := range l.InvTransactions {\n\t\tstart := xml.StartElement{Name: xml.Name{Local: t.TransactionType()}}\n\t\tswitch tran := t.(type) {\n\t\tcase BuyDebt:\n\t\t\tif err := e.EncodeElement(&tran, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase BuyMF:\n\t\t\tif err := e.EncodeElement(&tran, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase BuyOpt:\n\t\t\tif err := e.EncodeElement(&tran, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase BuyOther:\n\t\t\tif err := e.EncodeElement(&tran, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase BuyStock:\n\t\t\tif err := e.EncodeElement(&tran, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase ClosureOpt:\n\t\t\tif err := e.EncodeElement(&tran, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase Income:\n\t\t\tif err := e.EncodeElement(&tran, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase InvExpense:\n\t\t\tif err := e.EncodeElement(&tran, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase JrnlFund:\n\t\t\tif err := e.EncodeElement(&tran, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase JrnlSec:\n\t\t\tif err := e.EncodeElement(&tran, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase MarginInterest:\n\t\t\tif err := e.EncodeElement(&tran, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase Reinvest:\n\t\t\tif err := e.EncodeElement(&tran, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase RetOfCap:\n\t\t\tif err := e.EncodeElement(&tran, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase SellDebt:\n\t\t\tif err := e.EncodeElement(&tran, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase SellMF:\n\t\t\tif err := e.EncodeElement(&tran, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase SellOpt:\n\t\t\tif err := e.EncodeElement(&tran, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase SellOther:\n\t\t\tif err := e.EncodeElement(&tran, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase SellStock:\n\t\t\tif err := e.EncodeElement(&tran, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase Split:\n\t\t\tif err := e.EncodeElement(&tran, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase Transfer:\n\t\t\tif err := e.EncodeElement(&tran, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn errors.New(\"Invalid INVTRANLIST child type: \" + tran.TransactionType())\n\t\t}\n\t}\n\tfor _, tran := range l.BankTransactions {\n\t\terr = e.EncodeElement(&tran, xml.StartElement{Name: xml.Name{Local: \"INVBANKTRAN\"}})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := e.EncodeToken(invTranListElement.End()); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// InvPosition contains generic position information included in each of the\n// other *Position types\ntype InvPosition struct {\n\tXMLName       xml.Name      `xml:\"INVPOS\"`\n\tSecID         SecurityID    `xml:\"SECID\"`\n\tHeldInAcct    subAcctType   `xml:\"HELDINACCT\"`             // Sub-account type, one of CASH, MARGIN, SHORT, OTHER\n\tPosType       posType       `xml:\"POSTYPE\"`                // SHORT = Writer for options, Short for all others; LONG = Holder for options, Long for all others.\n\tUnits         Amount        `xml:\"UNITS\"`                  // For stocks, MFs, other, number of shares held. Bonds = face value. Options = number of contracts\n\tUnitPrice     Amount        `xml:\"UNITPRICE\"`              // For stocks, MFs, other, price per share. Bonds = percentage of par. Option = premium per share of underlying security\n\tMktVal        Amount        `xml:\"MKTVAL\"`                 // Market value of this position\n\tAvgCostBasis  Amount        `xml:\"AVGCOSTBASIS,omitempty\"` //\n\tDtPriceAsOf   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,\n\tCurrency      *Currency     `xml:\"CURRENCY,omitempty\"`     // Overriding currency for UNITPRICE\n\tMemo          String        `xml:\"MEMO,omitempty\"`\n\tInv401kSource inv401kSource `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\n}\n\n// Position is an interface satisfied by all the other *Position types\ntype Position interface {\n\tPositionType() string\n\tInvPosition() InvPosition\n}\n\n// DebtPosition represents a position held in a debt security\ntype DebtPosition struct {\n\tXMLName xml.Name    `xml:\"POSDEBT\"`\n\tInvPos  InvPosition `xml:\"INVPOS\"`\n}\n\n// PositionType returns a string representation of this position's type\nfunc (p DebtPosition) PositionType() string {\n\treturn \"POSDEBT\"\n}\n\n// InvPosition returns InvPos\nfunc (p DebtPosition) InvPosition() InvPosition {\n\treturn p.InvPos\n}\n\n// MFPosition represents a position held in a mutual fund\ntype MFPosition struct {\n\tXMLName     xml.Name    `xml:\"POSMF\"`\n\tInvPos      InvPosition `xml:\"INVPOS\"`\n\tUnitsStreet Amount      `xml:\"UNITSSTREET,omitempty\"` // Units in the FI’s street name\n\tUnitsUser   Amount      `xml:\"UNITSUSER,omitempty\"`   // Units in the user's name directly\n\tReinvDiv    Boolean     `xml:\"REINVDIV,omitempty\"`    // Reinvest dividends\n\tReinvCG     Boolean     `xml:\"REINVCG,omitempty\"`     // Reinvest capital gains\n}\n\n// PositionType returns a string representation of this position's type\nfunc (p MFPosition) PositionType() string {\n\treturn \"POSMF\"\n}\n\n// InvPosition returns InvPos\nfunc (p MFPosition) InvPosition() InvPosition {\n\treturn p.InvPos\n}\n\n// OptPosition represents a position held in an option\ntype OptPosition struct {\n\tXMLName xml.Name    `xml:\"POSOPT\"`\n\tInvPos  InvPosition `xml:\"INVPOS\"`\n\tSecured secured     `xml:\"SECURED,omitempty\"` // One of NAKED, COVERED\n}\n\n// PositionType returns a string representation of this position's type\nfunc (p OptPosition) PositionType() string {\n\treturn \"POSOPT\"\n}\n\n// InvPosition returns InvPos\nfunc (p OptPosition) InvPosition() InvPosition {\n\treturn p.InvPos\n}\n\n// OtherPosition represents a position held in a security type not covered by\n// the other *Position elements\ntype OtherPosition struct {\n\tXMLName xml.Name    `xml:\"POSOTHER\"`\n\tInvPos  InvPosition `xml:\"INVPOS\"`\n}\n\n// PositionType returns a string representation of this position's type\nfunc (p OtherPosition) PositionType() string {\n\treturn \"POSOTHER\"\n}\n\n// InvPosition returns InvPos\nfunc (p OtherPosition) InvPosition() InvPosition {\n\treturn p.InvPos\n}\n\n// StockPosition represents a position held in a stock\ntype StockPosition struct {\n\tXMLName     xml.Name    `xml:\"POSSTOCK\"`\n\tInvPos      InvPosition `xml:\"INVPOS\"`\n\tUnitsStreet Amount      `xml:\"UNITSSTREET,omitempty\"` // Units in the FI’s street name\n\tUnitsUser   Amount      `xml:\"UNITSUSER,omitempty\"`   // Units in the user's name directly\n\tReinvDiv    Boolean     `xml:\"REINVDIV,omitempty\"`    // Reinvest dividends\n}\n\n// PositionType returns a string representation of this position's type\nfunc (p StockPosition) PositionType() string {\n\treturn \"POSSTOCK\"\n}\n\n// InvPosition returns InvPos\nfunc (p StockPosition) InvPosition() InvPosition {\n\treturn p.InvPos\n}\n\n// PositionList represents a list of positions held in securities in an\n// investment account\ntype PositionList []Position\n\n// UnmarshalXML handles unmarshalling a PositionList from an XML string\nfunc (p *PositionList) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tfor {\n\t\ttok, err := nextNonWhitespaceToken(d)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t} else if end, ok := tok.(xml.EndElement); ok && end.Name.Local == start.Name.Local {\n\t\t\t// If we found the end of our starting element, we're done parsing\n\t\t\treturn nil\n\t\t} else if startElement, ok := tok.(xml.StartElement); ok {\n\t\t\tswitch startElement.Name.Local {\n\t\t\tcase \"POSDEBT\":\n\t\t\t\tvar position DebtPosition\n\t\t\t\tif err := d.DecodeElement(&position, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\t*p = append(*p, Position(position))\n\t\t\tcase \"POSMF\":\n\t\t\t\tvar position MFPosition\n\t\t\t\tif err := d.DecodeElement(&position, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\t*p = append(*p, Position(position))\n\t\t\tcase \"POSOPT\":\n\t\t\t\tvar position OptPosition\n\t\t\t\tif err := d.DecodeElement(&position, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\t*p = append(*p, Position(position))\n\t\t\tcase \"POSOTHER\":\n\t\t\t\tvar position OtherPosition\n\t\t\t\tif err := d.DecodeElement(&position, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\t*p = append(*p, Position(position))\n\t\t\tcase \"POSSTOCK\":\n\t\t\t\tvar position StockPosition\n\t\t\t\tif err := d.DecodeElement(&position, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\t*p = append(*p, Position(position))\n\t\t\tdefault:\n\t\t\t\treturn errors.New(\"Invalid INVPOSLIST child tag: \" + startElement.Name.Local)\n\t\t\t}\n\t\t} else {\n\t\t\treturn errors.New(\"Didn't find an opening element\")\n\t\t}\n\t}\n}\n\n// MarshalXML handles marshalling a PositionList to an XML string\nfunc (p PositionList) MarshalXML(e *xml.Encoder, start xml.StartElement) error {\n\tinvPosListElement := xml.StartElement{Name: xml.Name{Local: \"INVPOSLIST\"}}\n\tif err := e.EncodeToken(invPosListElement); err != nil {\n\t\treturn err\n\t}\n\tfor _, position := range p {\n\t\tstart := xml.StartElement{Name: xml.Name{Local: position.PositionType()}}\n\t\tswitch pos := position.(type) {\n\t\tcase DebtPosition:\n\t\t\tif err := e.EncodeElement(&pos, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase MFPosition:\n\t\t\tif err := e.EncodeElement(&pos, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase OptPosition:\n\t\t\tif err := e.EncodeElement(&pos, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase OtherPosition:\n\t\t\tif err := e.EncodeElement(&pos, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase StockPosition:\n\t\t\tif err := e.EncodeElement(&pos, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn errors.New(\"Invalid INVPOSLIST child type: \" + pos.PositionType())\n\t\t}\n\t}\n\tif err := e.EncodeToken(invPosListElement.End()); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// InvBalance contains three (or optionally four) specified balances as well as\n// a free-form list of generic balance information which may be provided by an\n// FI.\ntype InvBalance struct {\n\tXMLName       xml.Name  `xml:\"INVBAL\"`\n\tAvailCash     Amount    `xml:\"AVAILCASH\"`     // Available cash across all sub-accounts, including sweep funds\n\tMarginBalance Amount    `xml:\"MARGINBALANCE\"` // Negative means customer has borrowed funds\n\tShortBalance  Amount    `xml:\"SHORTBALANCE\"`  // Always positive, market value of all short positions\n\tBuyPower      Amount    `xml:\"BUYPOWER,omitempty\"`\n\tBalList       []Balance `xml:\"BALLIST>BAL,omitempty\"`\n}\n\n// OO represents a generic open investment order. It is included in the other\n// OO* elements.\ntype OO struct {\n\tXMLName       xml.Name      `xml:\"OO\"`\n\tFiTID         String        `xml:\"FITID\"`\n\tSrvrTID       String        `xml:\"SRVRTID,omitempty\"`\n\tSecID         SecurityID    `xml:\"SECID\"`\n\tDtPlaced      Date          `xml:\"DTPLACED\"`           // Date the order was placed\n\tUnits         Amount        `xml:\"UNITS\"`              // Quantity of the security the open order is for\n\tSubAcct       subAcctType   `xml:\"SUBACCT\"`            // One of CASH, MARGIN, SHORT, OTHER\n\tDuration      duration      `xml:\"DURATION\"`           // How long the order is good for. One of DAY, GOODTILCANCEL, IMMEDIATE\n\tRestriction   restriction   `xml:\"RESTRICTION\"`        // Special restriction on the order: One of ALLORNONE, MINUNITS, NONE\n\tMinUnits      Amount        `xml:\"MINUNITS,omitempty\"` // Minimum number of units that must be filled for the order\n\tLimitPrice    Amount        `xml:\"LIMITPRICE,omitempty\"`\n\tStopPrice     Amount        `xml:\"STOPPRICE,omitempty\"`\n\tMemo          String        `xml:\"MEMO,omitempty\"`\n\tCurrency      *Currency     `xml:\"CURRENCY,omitempty\"`      // Overriding currency for UNITPRICE\n\tInv401kSource inv401kSource `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\n}\n\n// OpenOrder is an interface satisfied by all the OO* elements.\ntype OpenOrder interface {\n\tOrderType() string\n}\n\n// OOBuyDebt represents an open order to purchase a debt security\ntype OOBuyDebt struct {\n\tXMLName   xml.Name `xml:\"OOBUYDEBT\"`\n\tOO        OO       `xml:\"OO\"`\n\tAuction   Boolean  `xml:\"AUCTION\"` // whether the debt should be purchased at the auction\n\tDtAuction *Date    `xml:\"DTAUCTION,omitempty\"`\n}\n\n// OrderType returns a string representation of this order's type\nfunc (o OOBuyDebt) OrderType() string {\n\treturn \"OOBUYDEBT\"\n}\n\n// OOBuyMF represents an open order to purchase a mutual fund\ntype OOBuyMF struct {\n\tXMLName  xml.Name `xml:\"OOBUYMF\"`\n\tOO       OO       `xml:\"OO\"`\n\tBuyType  buyType  `xml:\"BUYTYPE\"`  // One of BUY, BUYTOCOVER\n\tUnitType unitType `xml:\"UNITTYPE\"` // What the units represent: one of SHARES, CURRENCY\n}\n\n// OrderType returns a string representation of this order's type\nfunc (o OOBuyMF) OrderType() string {\n\treturn \"OOBUYMF\"\n}\n\n// OOBuyOpt represents an open order to purchase an option\ntype OOBuyOpt struct {\n\tXMLName    xml.Name   `xml:\"OOBUYOPT\"`\n\tOO         OO         `xml:\"OO\"`\n\tOptBuyType optBuyType `xml:\"OPTBUYTYPE\"` // One of BUYTOOPEN, BUYTOCLOSE\n}\n\n// OrderType returns a string representation of this order's type\nfunc (o OOBuyOpt) OrderType() string {\n\treturn \"OOBUYOPT\"\n}\n\n// OOBuyOther represents an open order to purchase a security type not covered\n// by the other OOBuy* elements\ntype OOBuyOther struct {\n\tXMLName  xml.Name `xml:\"OOBUYOTHER\"`\n\tOO       OO       `xml:\"OO\"`\n\tUnitType unitType `xml:\"UNITTYPE\"` // What the units represent: one of SHARES, CURRENCY\n}\n\n// OrderType returns a string representation of this order's type\nfunc (o OOBuyOther) OrderType() string {\n\treturn \"OOBUYOTHER\"\n}\n\n// OOBuyStock represents an open order to purchase stock\ntype OOBuyStock struct {\n\tXMLName xml.Name `xml:\"OOBUYSTOCK\"`\n\tOO      OO       `xml:\"OO\"`\n\tBuyType buyType  `xml:\"BUYTYPE\"` // One of BUY, BUYTOCOVER\n}\n\n// OrderType returns a string representation of this order's type\nfunc (o OOBuyStock) OrderType() string {\n\treturn \"OOBUYSTOCK\"\n}\n\n// OOSellDebt represents an open order to sell a debt security\ntype OOSellDebt struct {\n\tXMLName xml.Name `xml:\"OOSELLDEBT\"`\n\tOO      OO       `xml:\"OO\"`\n}\n\n// OrderType returns a string representation of this order's type\nfunc (o OOSellDebt) OrderType() string {\n\treturn \"OOSELLDEBT\"\n}\n\n// OOSellMF represents an open order to sell a mutual fund\ntype OOSellMF struct {\n\tXMLName  xml.Name `xml:\"OOSELLMF\"`\n\tOO       OO       `xml:\"OO\"`\n\tSellType sellType `xml:\"SELLTYPE\"` // One of SELL, SELLSHORT\n\tUnitType unitType `xml:\"UNITTYPE\"` // What the units represent: one of SHARES, CURRENCY\n\tSellAll  Boolean  `xml:\"SELLALL\"`  // Sell entire holding\n}\n\n// OrderType returns a string representation of this order's type\nfunc (o OOSellMF) OrderType() string {\n\treturn \"OOSELLMF\"\n}\n\n// OOSellOpt represents an open order to sell an option\ntype OOSellOpt struct {\n\tXMLName     xml.Name    `xml:\"OOSELLOPT\"`\n\tOO          OO          `xml:\"OO\"`\n\tOptSellType optSellType `xml:\"OPTSELLTYPE\"` // One of SELLTOOPEN, SELLTOCLOSE\n}\n\n// OrderType returns a string representation of this order's type\nfunc (o OOSellOpt) OrderType() string {\n\treturn \"OOSELLOPT\"\n}\n\n// OOSellOther represents an open order to sell a security type not covered by\n// the other OOSell* elements\ntype OOSellOther struct {\n\tXMLName  xml.Name `xml:\"OOSELLOTHER\"`\n\tOO       OO       `xml:\"OO\"`\n\tUnitType unitType `xml:\"UNITTYPE\"` // What the units represent: one of SHARES, CURRENCY\n}\n\n// OrderType returns a string representation of this order's type\nfunc (o OOSellOther) OrderType() string {\n\treturn \"OOSELLOTHER\"\n}\n\n// OOSellStock represents an open order to sell stock\ntype OOSellStock struct {\n\tXMLName  xml.Name `xml:\"OOSELLSTOCK\"`\n\tOO       OO       `xml:\"OO\"`\n\tSellType sellType `xml:\"SELLTYPE\"` // One of SELL, SELLSHORT\n}\n\n// OrderType returns a string representation of this order's type\nfunc (o OOSellStock) OrderType() string {\n\treturn \"OOSELLSTOCK\"\n}\n\n// OOSwitchMF represents an open order to switch to or purchase a different\n// mutual fund\ntype OOSwitchMF struct {\n\tXMLName   xml.Name   `xml:\"SWITCHMF\"`\n\tOO        OO         `xml:\"OO\"`\n\tSecID     SecurityID `xml:\"SECID\"`     // Security ID of the fund to switch to or purchase\n\tUnitType  unitType   `xml:\"UNITTYPE\"`  // What the units represent: one of SHARES, CURRENCY\n\tSwitchAll Boolean    `xml:\"SWITCHALL\"` // Switch entire holding\n}\n\n// OrderType returns a string representation of this order's type\nfunc (o OOSwitchMF) OrderType() string {\n\treturn \"SWITCHMF\"\n}\n\n// OOList represents a list of open orders (OO* elements)\ntype OOList []OpenOrder\n\n// UnmarshalXML handles unmarshalling an OOList element from an XML string\nfunc (o *OOList) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tfor {\n\t\ttok, err := nextNonWhitespaceToken(d)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t} else if end, ok := tok.(xml.EndElement); ok && end.Name.Local == start.Name.Local {\n\t\t\t// If we found the end of our starting element, we're done parsing\n\t\t\treturn nil\n\t\t} else if startElement, ok := tok.(xml.StartElement); ok {\n\t\t\tswitch startElement.Name.Local {\n\t\t\tcase \"OOBUYDEBT\":\n\t\t\t\tvar oo OOBuyDebt\n\t\t\t\tif err := d.DecodeElement(&oo, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\t*o = append(*o, OpenOrder(oo))\n\t\t\tcase \"OOBUYMF\":\n\t\t\t\tvar oo OOBuyMF\n\t\t\t\tif err := d.DecodeElement(&oo, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\t*o = append(*o, OpenOrder(oo))\n\t\t\tcase \"OOBUYOPT\":\n\t\t\t\tvar oo OOBuyOpt\n\t\t\t\tif err := d.DecodeElement(&oo, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\t*o = append(*o, OpenOrder(oo))\n\t\t\tcase \"OOBUYOTHER\":\n\t\t\t\tvar oo OOBuyOther\n\t\t\t\tif err := d.DecodeElement(&oo, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\t*o = append(*o, OpenOrder(oo))\n\t\t\tcase \"OOBUYSTOCK\":\n\t\t\t\tvar oo OOBuyStock\n\t\t\t\tif err := d.DecodeElement(&oo, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\t*o = append(*o, OpenOrder(oo))\n\t\t\tcase \"OOSELLDEBT\":\n\t\t\t\tvar oo OOSellDebt\n\t\t\t\tif err := d.DecodeElement(&oo, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\t*o = append(*o, OpenOrder(oo))\n\t\t\tcase \"OOSELLMF\":\n\t\t\t\tvar oo OOSellMF\n\t\t\t\tif err := d.DecodeElement(&oo, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\t*o = append(*o, OpenOrder(oo))\n\t\t\tcase \"OOSELLOPT\":\n\t\t\t\tvar oo OOSellOpt\n\t\t\t\tif err := d.DecodeElement(&oo, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\t*o = append(*o, OpenOrder(oo))\n\t\t\tcase \"OOSELLOTHER\":\n\t\t\t\tvar oo OOSellOther\n\t\t\t\tif err := d.DecodeElement(&oo, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\t*o = append(*o, OpenOrder(oo))\n\t\t\tcase \"OOSELLSTOCK\":\n\t\t\t\tvar oo OOSellStock\n\t\t\t\tif err := d.DecodeElement(&oo, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\t*o = append(*o, OpenOrder(oo))\n\t\t\tcase \"SWITCHMF\":\n\t\t\t\tvar oo OOSwitchMF\n\t\t\t\tif err := d.DecodeElement(&oo, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\t*o = append(*o, OpenOrder(oo))\n\t\t\tdefault:\n\t\t\t\treturn errors.New(\"Invalid OOList child tag: \" + startElement.Name.Local)\n\t\t\t}\n\t\t} else {\n\t\t\treturn errors.New(\"Didn't find an opening element\")\n\t\t}\n\t}\n}\n\n// MarshalXML handles marshalling an OOList to an XML string\nfunc (o OOList) MarshalXML(e *xml.Encoder, start xml.StartElement) error {\n\tooListElement := xml.StartElement{Name: xml.Name{Local: \"INVOOLIST\"}}\n\tif err := e.EncodeToken(ooListElement); err != nil {\n\t\treturn err\n\t}\n\tfor _, openorder := range o {\n\t\tstart := xml.StartElement{Name: xml.Name{Local: openorder.OrderType()}}\n\t\tswitch oo := openorder.(type) {\n\t\tcase OOBuyDebt:\n\t\t\tif err := e.EncodeElement(&oo, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase OOBuyMF:\n\t\t\tif err := e.EncodeElement(&oo, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase OOBuyOpt:\n\t\t\tif err := e.EncodeElement(&oo, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase OOBuyOther:\n\t\t\tif err := e.EncodeElement(&oo, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase OOBuyStock:\n\t\t\tif err := e.EncodeElement(&oo, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase OOSellDebt:\n\t\t\tif err := e.EncodeElement(&oo, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase OOSellMF:\n\t\t\tif err := e.EncodeElement(&oo, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase OOSellOpt:\n\t\t\tif err := e.EncodeElement(&oo, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase OOSellOther:\n\t\t\tif err := e.EncodeElement(&oo, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase OOSellStock:\n\t\t\tif err := e.EncodeElement(&oo, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase OOSwitchMF:\n\t\t\tif err := e.EncodeElement(&oo, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn errors.New(\"Invalid OOLIST child type: \" + oo.OrderType())\n\t\t}\n\t}\n\tif err := e.EncodeToken(ooListElement.End()); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// ContribSecurity identifies current contribution allocation for a security in\n// a 401(k) account\ntype ContribSecurity struct {\n\tXMLName                 xml.Name   `xml:\"CONTRIBSECURITY\"`\n\tSecID                   SecurityID `xml:\"SECID\"`\n\tPreTaxContribPct        Amount     `xml:\"PRETAXCONTRIBPCT,omitempty\"`        // Percentage of each new employee pretax contribution allocated to this security, rate.\n\tPreTaxContribAmt        Amount     `xml:\"PRETAXCONTRIBAMT,omitempty\"`        // Fixed amount of each new employee pretax contribution allocated to this security, amount\n\tAfterTaxContribPct      Amount     `xml:\"AFTERTAXCONTRIBPCT,omitempty\"`      // Percentage of each new employee after tax contribution allocated to this security, rate.\n\tAfterTaxContribAmt      Amount     `xml:\"AFTERTAXCONTRIBAMT,omitempty\"`      // Fixed amount of each new employee pretax contribution allocated to this security, amount.\n\tMatchContribPct         Amount     `xml:\"MATCHCONTRIBPCT,omitempty\"`         // Percentage of each new employer match contribution allocated to this security, rate.\n\tMatchContribAmt         Amount     `xml:\"MATCHCONTRIBAMT,omitempty\"`         // Fixed amount of each new employer match contribution allocated to this security, amount.\n\tProfitSharingContribPct Amount     `xml:\"PROFITSHARINGCONTRIBPCT,omitempty\"` // Percentage of each new employer profit sharing contribution allocated to this security, rate.\n\tProfitSharingContribAmt Amount     `xml:\"PROFITSHARINGCONTRIBAMT,omitempty\"` // Fixed amount of each new employer profit sharing contribution allocated to this security, amount.\n\tRolloverContribPct      Amount     `xml:\"ROLLOVERCONTRIBPCT,omitempty\"`      // Percentage of new rollover contributions allocated to this security, rate.\n\tRolloverContribAmt      Amount     `xml:\"ROLLOVERCONTRIBAMT,omitempty\"`      // Fixed amount of new rollover contributions allocated to this security, amount.\n\tOtherVestPct            Amount     `xml:\"OTHERVESTPCT,omitempty\"`            // Percentage of each new other employer contribution allocated to this security, rate.\n\tOtherVestAmt            Amount     `xml:\"OTHERVESTAMT,omitempty\"`            // Fixed amount of each new other employer contribution allocated to this security, amount.\n\tOtherNonVestPct         Amount     `xml:\"OTHERNONVESTPCT,omitempty\"`         // Percentage of each new other employee contribution allocated to this security, rate.\n\tOtherNonVestAmt         Amount     `xml:\"OTHERNONVESTAMT,omitempty\"`         // Fixed amount of each new other employee contribution allocated to this security, amount\n}\n\n// VestInfo provides the vesting percentage of a 401(k) account as of a\n// particular date (past, present, or future)\ntype VestInfo struct {\n\tXMLName  xml.Name `xml:\"VESTINFO\"`\n\tVestDate *Date    `xml:\"VESTDATE,omitempty\"` // Date at which vesting percentage changes. Default (if empty) is that the vesting percentage below applies to the current date\n\tVestPct  Amount   `xml:\"VESTPCT\"`\n}\n\n// LoanInfo represents a loan outstanding against this 401(k) account\ntype LoanInfo struct {\n\tXMLName               xml.Name    `xml:\"VESTINFO\"`\n\tLoanID                String      `xml:\"LOANID\"`                          // Identifier of this loan\n\tLoanDesc              String      `xml:\"LOANDESC,omitempty\"`              // Loan description\n\tInitialLoanBal        Amount      `xml:\"INITIALLOANBAL,omitempty\"`        // Initial loan balance\n\tLoanStartDate         *Date       `xml:\"LOANSTARTDATE,omitempty\"`         // Start date of loan\n\tCurrentLoanBal        Amount      `xml:\"CURRENTLOANBAL\"`                  // Current loan principal balance\n\tDtAsOf                *Date       `xml:\"DTASOF\"`                          // Date and time of the current loan balance\n\tLoanRate              Amount      `xml:\"LOANRATE,omitempty\"`              // Loan annual interest rate\n\tLoanPmtAmt            Amount      `xml:\"LOANPMTAMT,omitempty\"`            // Loan payment amount\n\tLoanPmtFreq           loanPmtFreq `xml:\"LOANPMTFREQ,omitempty\"`           // Frequency of loan repayments: WEEKLY, BIWEEKLY, TWICEMONTHLY, MONTHLY, FOURWEEKS, BIMONTHLY, QUARTERLY, SEMIANNUALLY, ANNUALLY, OTHER. See section 10.2.1 for calculation rules.\n\tLoanPmtsInitial       Int         `xml:\"LOANPMTSINITIAL,omitempty\"`       // Initial number of loan payments.\n\tLoanPmtsRemaining     Int         `xml:\"LOANPMTSREMAINING,omitempty\"`     // Remaining number of loan payments\n\tLoanMaturityDate      *Date       `xml:\"LOANMATURITYDATE,omitempty\"`      // Expected loan end date\n\tLoanTotalProjInterest Amount      `xml:\"LOANTOTALPROJINTEREST,omitempty\"` // Total projected interest to be paid on this loan\n\tLoanInterestToDate    Amount      `xml:\"LOANINTERESTTODATE,omitempty\"`    // Total interested paid to date on this loan\n\tLoanExtPmtDate        *Date       `xml:\"LOANNEXTPMTDATE,omitempty\"`       // Next payment due date\n}\n\n// Inv401KSummaryAggregate represents the total of either contributions,\n// withdrawals, or earnings made in each contribution type in a given period\n// (dates specified in a containing Inv401KSummaryPeriod)\ntype Inv401KSummaryAggregate struct {\n\tXMLName       xml.Name // One of CONTRIBUTIONS, WITHDRAWALS, EARNINGS\n\tPreTax        Amount   `xml:\"PRETAX,omitempty\"`        // Pretax contributions, withdrawals, or earlings.\n\tAfterTax      Amount   `xml:\"AFTERTAX,omitempty\"`      // After tax contributions, withdrawals, or earlings.\n\tMatch         Amount   `xml:\"MATCH,omitempty\"`         // Employer matching contributions, withdrawals, or earlings.\n\tProfitSharing Amount   `xml:\"PROFITSHARING,omitempty\"` // Profit sharing contributions, withdrawals, or earlings.\n\tRollover      Amount   `xml:\"ROLLOVER,omitempty\"`      // Rollover contributions, withdrawals, or earlings.\n\tOtherVest     Amount   `xml:\"OTHERVEST,omitempty\"`     // Other vesting contributions, withdrawals, or earlings.\n\tOtherNonVest  Amount   `xml:\"OTHERNONVEST,omitempty\"`  // Other non-vesting contributions, withdrawals, or earlings.\n\tTotal         Amount   `xml:\"TOTAL\"`                   // Sum of contributions, withdrawals, or earlings from all fund sources.\n}\n\n// Inv401KSummaryPeriod contains the total contributions, withdrawals, and\n// earnings made in the given date range\ntype Inv401KSummaryPeriod struct {\n\tXMLName       xml.Name                 // One of YEARTODATE, INCEPTODATE, or PERIODTODATE\n\tDtStart       Date                     `xml:\"DTSTART\"`\n\tDtEnd         Date                     `xml:\"DTEND\"`\n\tContributions *Inv401KSummaryAggregate `xml:\"CONTRIBUTIONS,omitempty\"` // 401(k) contribution aggregate. Note: this includes loan payments.\n\tWithdrawls    *Inv401KSummaryAggregate `xml:\"WITHDRAWLS,omitempty\"`    // 401(k) withdrawals aggregate. Note: this includes loan withdrawals.\n\tEarnings      *Inv401KSummaryAggregate `xml:\"EARNINGS,omitempty\"`      // 401(k) earnings aggregate. This is the market value change. It includes dividends/interest, and capital gains - realized and unrealized.\n}\n\n// Inv401K is included in InvStatementResponse for 401(k) accounts and provides\n// a summary of the 401(k) specific information about the user's account.\ntype Inv401K struct {\n\tXMLName             xml.Name `xml:\"INV401K\"`\n\tEmployerName        String   `xml:\"EMPLOYERNAME\"`\n\tPlanID              String   `xml:\"PLANID,omitempty\"`              // Plan number\n\tPlanJoinDate        *Date    `xml:\"PLANJOINDATE,omitempty\"`        // Date the employee joined the plan\n\tEmployerContactInfo String   `xml:\"EMPLOYERCONTACTINFO,omitempty\"` // Name of contact person at employer, plus any available contact information, such as phone number\n\tBrokerContactInfo   String   `xml:\"BROKERCONTACTINFO,omitempty\"`   // Name of contact person at broker, plus any available contact information, such as phone number\n\tDeferPctPreTax      Amount   `xml:\"DEFERPCTPRETAX,omitempty\"`      // Percent of employee salary deferred before tax\n\tDeferPctAfterTax    Amount   `xml:\"DEFERPCTAFTERTAX,omitempty\"`    // Percent of employee salary deferred after tax\n\n\t//<MATCHINFO> Aggregate containing employer match information. Absent if employer does not contribute matching funds.\n\tMatchPct            Amount                `xml:\"MATCHINFO>MATCHPCT,omitempty\"`          // Percent of employee contribution matched, e.g., 75% if contribution rate is $0.75/$1.00\n\tMaxMatchAmt         Amount                `xml:\"MATCHINFO>MAXMATCHAMT,omitempty\"`       // Maximum employer contribution amount in any year\n\tMaxMatchPct         Amount                `xml:\"MATCHINFO>MAXMATCHPCT,omitempty\"`       // Current maximum employer contribution percentage. Maximum match in a year is MAXMATCHPCT up to the MAXMATCHAMT, if provided\n\tStartOfYear         *Date                 `xml:\"MATCHINFO>STARTOFYEAR,omitempty\"`       // Specifies when the employer contribution max is reset. Some plans have a maximum based on the company fiscal year rather than calendar year. Assume calendar year if omitted. Only the month and day (MMDD) are used; year (YYYY) and time are ignored\n\tBaseMatchAmt        Amount                `xml:\"MATCHINFO>BASEMATCHAMT\"`                // Specifies a fixed dollar amount contributed by the employer if the employee participates in the plan at all. This may be present in addition to the <MATCHPCT>. $0 if omitted\n\tBaseMatchPct        Amount                `xml:\"MATCHINFO>BASEMATCHPCT\"`                // Specifies a fixed percent of employee salary matched if the employee participates in the plan at all. This may be present in addition to the MATCHPCT>. 0% if omitted. Base match in a year is BASEMATCHPCT up to the BASEMATCHAMT,if provided\n\tContribInfo         []ContribSecurity     `xml:\"CONTRIBINTO>CONTRIBSECURITY\"`           // Aggregate to describe how new contributions are distributed among the available securities.\n\tCurrentVestPct      Amount                `xml:\"CURRENTVESTPCT,omitempty\"`              // Estimated percentage of employer contributions vested as of the current date. If omitted, assume 100%\n\tVestInfo            []VestInfo            `xml:\"VESTINFO,omitempty\"`                    // Vest change dates. Provides the vesting percentage as of any particular past, current, or future date. 0 or more.\n\tLoanInfo            []LoanInfo            `xml:\"LOANINFO,omitempty\"`                    // List of any loans outstanding against this account\n\tYearToDateSummary   Inv401KSummaryPeriod  `xml:\"INV401KSUMMARY>YEARTODATE\"`             // Contributions to date for this calendar year.\n\tInceptToDateSummary *Inv401KSummaryPeriod `xml:\"INV401KSUMMARY>INCEPTODATE,omitempty\"`  // Total contributions to date (since inception)\n\tPeriodToDate        *Inv401KSummaryPeriod `xml:\"INV401KSUMMARY>PERIODTODATE,omitempty\"` // Total contributions this contribution period\n}\n\n// Inv401KBal provides the balances for different 401(k) subaccount types, as\n// well as the total cash value of the securities held\ntype Inv401KBal struct {\n\tXMLName       xml.Name  `xml:\"INV401KBAL\"`\n\tCashBal       Amount    `xml:\"CASHBAL,omitempty\"`       // Available cash balance\n\tPreTax        Amount    `xml:\"PRETAX,omitempty\"`        // Current value of all securities purchased with Before Tax Employee contributions\n\tAfterTax      Amount    `xml:\"AFTERTAX,omitempty\"`      // Current value of all securities purchased with After Tax Employee contributions\n\tMatch         Amount    `xml:\"MATCH,omitempty\"`         // Current value of all securities purchased with Employer Match contributions\n\tProfitSharing Amount    `xml:\"PROFITSHARING,omitempty\"` // Current value of all securities purchased with Employer Profit Sharing contributions\n\tRollover      Amount    `xml:\"ROLLOVER,omitempty\"`      // Current value of all securities purchased with Rollover contributions\n\tOtherVest     Amount    `xml:\"OTHERVEST,omitempty\"`     // Current value of all securities purchased with Other (vesting) Employer contributions\n\tOtherNonVest  Amount    `xml:\"OTHERNONVEST,omitempty\"`  // Current value of all securities purchased with Other (non-vesting) Employer contributions\n\tTotal         Amount    `xml:\"TOTAL\"`                   // Current value of all securities purchased with all contributions\n\tBalList       []Balance `xml:\"BALLIST>BAL,omitempty\"`\n}\n\n// InvStatementResponse includes requested transaction, position, open order,\n// and balance information for an investment account. It is in response to an\n// InvStatementRequest or sometimes provided as part of an OFX file downloaded\n// manually from an FI.\ntype InvStatementResponse struct {\n\tXMLName   xml.Name `xml:\"INVSTMTTRNRS\"`\n\tTrnUID    UID      `xml:\"TRNUID\"`\n\tStatus    Status   `xml:\"STATUS\"`\n\tCltCookie String   `xml:\"CLTCOOKIE,omitempty\"`\n\t// TODO `xml:\"OFXEXTENSION,omitempty\"`\n\tDtAsOf      Date         `xml:\"INVSTMTRS>DTASOF\"`\n\tCurDef      CurrSymbol   `xml:\"INVSTMTRS>CURDEF\"`\n\tInvAcctFrom InvAcct      `xml:\"INVSTMTRS>INVACCTFROM\"`\n\tInvTranList *InvTranList `xml:\"INVSTMTRS>INVTRANLIST,omitempty\"`\n\tInvPosList  PositionList `xml:\"INVSTMTRS>INVPOSLIST,omitempty\"`\n\tInvBal      *InvBalance  `xml:\"INVSTMTRS>INVBAL,omitempty\"`\n\tInvOOList   OOList       `xml:\"INVSTMTRS>INVOOLIST,omitempty\"`\n\tMktgInfo    String       `xml:\"INVSTMTRS>MKTGINFO,omitempty\"` // Marketing information\n\tInv401K     *Inv401K     `xml:\"INVSTMTRS>INV401K,omitempty\"`\n\tInv401KBal  *Inv401KBal  `xml:\"INVSTMTRS>INV401KBAL,omitempty\"`\n}\n\n// Name returns the name of the top-level transaction XML/SGML element\nfunc (sr *InvStatementResponse) Name() string {\n\treturn \"INVSTMTTRNRS\"\n}\n\n// Valid returns (true, nil) if this struct was valid OFX when unmarshalled\nfunc (sr *InvStatementResponse) Valid(version ofxVersion) (bool, error) {\n\tif ok, err := sr.TrnUID.Valid(); !ok {\n\t\treturn false, err\n\t}\n\t//TODO implement\n\treturn true, nil\n}\n\n// Type returns which message set this message belongs to (which Response\n// element of type []Message it belongs to)\nfunc (sr *InvStatementResponse) Type() messageType {\n\treturn InvStmtRs\n}\n"
  },
  {
    "path": "invstmt_test.go",
    "content": "package ofxgo\n\nimport (\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/aclindsa/xml\"\n)\n\nfunc TestMarshalInvStatementRequest(t *testing.T) {\n\tvar expectedString string = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<?OFX OFXHEADER=\"200\" VERSION=\"203\" SECURITY=\"NONE\" OLDFILEUID=\"NONE\" NEWFILEUID=\"NONE\"?>\n<OFX>\n\t<SIGNONMSGSRQV1>\n\t\t<SONRQ>\n\t\t\t<DTCLIENT>20160224131905.000[-5:EST]</DTCLIENT>\n\t\t\t<USERID>1998124</USERID>\n\t\t\t<USERPASS>Sup3eSekrit</USERPASS>\n\t\t\t<LANGUAGE>ENG</LANGUAGE>\n\t\t\t<FI>\n\t\t\t\t<ORG>First Bank</ORG>\n\t\t\t\t<FID>01</FID>\n\t\t\t</FI>\n\t\t\t<APPID>MYAPP</APPID>\n\t\t\t<APPVER>1234</APPVER>\n\t\t</SONRQ>\n\t</SIGNONMSGSRQV1>\n\t<INVSTMTMSGSRQV1>\n\t\t<INVSTMTTRNRQ>\n\t\t\t<TRNUID>382827d6-e2d0-4396-bf3b-665979285420</TRNUID>\n\t\t\t<INVSTMTRQ>\n\t\t\t\t<INVACCTFROM>\n\t\t\t\t\t<BROKERID>fi.example.com</BROKERID>\n\t\t\t\t\t<ACCTID>82736664</ACCTID>\n\t\t\t\t</INVACCTFROM>\n\t\t\t\t<INCTRAN>\n\t\t\t\t\t<DTSTART>20160101000000.000[-5:EST]</DTSTART>\n\t\t\t\t\t<INCLUDE>Y</INCLUDE>\n\t\t\t\t</INCTRAN>\n\t\t\t\t<INCOO>Y</INCOO>\n\t\t\t\t<INCPOS>\n\t\t\t\t\t<INCLUDE>Y</INCLUDE>\n\t\t\t\t</INCPOS>\n\t\t\t\t<INCBAL>Y</INCBAL>\n\t\t\t</INVSTMTRQ>\n\t\t</INVSTMTTRNRQ>\n\t</INVSTMTMSGSRQV1>\n</OFX>`\n\n\tvar client = BasicClient{\n\t\tAppID:       \"MYAPP\",\n\t\tAppVer:      \"1234\",\n\t\tSpecVersion: OfxVersion203,\n\t}\n\n\tvar request Request\n\trequest.Signon.UserID = \"1998124\"\n\trequest.Signon.UserPass = \"Sup3eSekrit\"\n\trequest.Signon.Org = \"First Bank\"\n\trequest.Signon.Fid = \"01\"\n\n\tEST := time.FixedZone(\"EST\", -5*60*60)\n\n\tstatementRequest := InvStatementRequest{\n\t\tTrnUID: \"382827d6-e2d0-4396-bf3b-665979285420\",\n\t\tInvAcctFrom: InvAcct{\n\t\t\tBrokerID: \"fi.example.com\",\n\t\t\tAcctID:   \"82736664\",\n\t\t},\n\t\tDtStart:        NewDate(2016, 1, 1, 0, 0, 0, 0, EST),\n\t\tInclude:        true,\n\t\tIncludeOO:      true,\n\t\tIncludePos:     true,\n\t\tIncludeBalance: true,\n\t}\n\trequest.InvStmt = append(request.InvStmt, &statementRequest)\n\n\trequest.SetClientFields(&client)\n\t// Overwrite the DtClient value set by SetClientFields to time.Now()\n\trequest.Signon.DtClient = *NewDate(2016, 2, 24, 13, 19, 5, 0, EST)\n\n\tmarshalCheckRequest(t, &request, expectedString)\n}\n\nfunc TestUnmarshalInvStatementResponse(t *testing.T) {\n\tresponseReader := strings.NewReader(`<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<?OFX OFXHEADER=\"200\" VERSION=\"203\" SECURITY=\"NONE\" OLDFILEUID=\"NONE\" NEWFILEUID=\"NONE\"?>\n<OFX>\n\t<SIGNONMSGSRSV1>\n\t\t<SONRS>\n\t\t\t<STATUS>\n\t\t\t\t<CODE>0</CODE>\n\t\t\t\t<SEVERITY>INFO</SEVERITY>\n\t\t\t</STATUS>\n\t\t\t<DTSERVER>20170401201244</DTSERVER>\n\t\t\t<LANGUAGE>ENG</LANGUAGE>\n\t\t\t<FI>\n\t\t\t\t<ORG>INVSTRUS</ORG>\n\t\t\t\t<FID>9999</FID>\n\t\t\t</FI>\n\t\t</SONRS>\n\t</SIGNONMSGSRSV1>\n\t<INVSTMTMSGSRSV1>\n\t\t<INVSTMTTRNRS>\n\t\t\t<TRNUID>1a0117ad-692b-4c6a-a21b-020d37d34d49</TRNUID>\n\t\t\t<STATUS>\n\t\t\t\t<CODE>0</CODE>\n\t\t\t\t<SEVERITY>INFO</SEVERITY>\n\t\t\t</STATUS>\n\t\t\t<INVSTMTRS>\n\t\t\t\t<DTASOF>20170331000000</DTASOF>\n\t\t\t\t<CURDEF>USD</CURDEF>\n\t\t\t\t<INVACCTFROM>\n\t\t\t\t\t<BROKERID>invstrus.com</BROKERID>\n\t\t\t\t\t<ACCTID>91827364</ACCTID>\n\t\t\t\t</INVACCTFROM>\n\t\t\t\t<INVTRANLIST>\n\t\t\t\t\t<DTSTART>20170101000000</DTSTART>\n\t\t\t\t\t<DTEND>20170331000000</DTEND>\n\t\t\t\t\t<BUYSTOCK>\n\t\t\t\t\t\t<INVBUY>\n\t\t\t\t\t\t\t<INVTRAN>\n\t\t\t\t\t\t\t\t<FITID>729483191</FITID>\n\t\t\t\t\t\t\t\t<DTTRADE>20170203</DTTRADE>\n\t\t\t\t\t\t\t\t<DTSETTLE>20170207</DTSETTLE>\n\t\t\t\t\t\t\t</INVTRAN>\n\t\t\t\t\t\t\t<SECID>\n\t\t\t\t\t\t\t\t<UNIQUEID>78462F103</UNIQUEID>\n\t\t\t\t\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t\t\t\t\t</SECID>\n\t\t\t\t\t\t\t<UNITS>100</UNITS>\n\t\t\t\t\t\t\t<UNITPRICE>229.00</UNITPRICE>\n\t\t\t\t\t\t\t<COMMISSION>9.00</COMMISSION>\n\t\t\t\t\t\t\t<TOTAL>-22909.00</TOTAL>\n\t\t\t\t\t\t\t<SUBACCTSEC>CASH</SUBACCTSEC>\n\t\t\t\t\t\t\t<SUBACCTFUND>CASH</SUBACCTFUND>\n\t\t\t\t\t\t</INVBUY>\n\t\t\t\t\t\t<BUYTYPE>BUY</BUYTYPE>\n\t\t\t\t\t</BUYSTOCK>\n\t\t\t\t\t<INVBANKTRAN>\n\t\t\t\t\t\t<STMTTRN>\n\t\t\t\t\t\t\t<TRNTYPE>CREDIT</TRNTYPE>\n\t\t\t\t\t\t\t<DTPOSTED>20170120</DTPOSTED>\n\t\t\t\t\t\t\t<DTUSER>20170118</DTUSER>\n\t\t\t\t\t\t\t<DTAVAIL>20170123</DTAVAIL>\n\t\t\t\t\t\t\t<TRNAMT>22000.00</TRNAMT>\n\t\t\t\t\t\t\t<FITID>993838</FITID>\n\t\t\t\t\t\t\t<NAME>DEPOSIT</NAME>\n\t\t\t\t\t\t\t<MEMO>CHECK 19980</MEMO>\n\t\t\t\t\t\t</STMTTRN>\n\t\t\t\t\t\t<SUBACCTFUND>CASH</SUBACCTFUND>\n\t\t\t\t\t</INVBANKTRAN>\n\t\t\t\t</INVTRANLIST>\n\t\t\t\t<INVPOSLIST>\n\t\t\t\t\t<POSSTOCK>\n\t\t\t\t\t\t<INVPOS>\n\t\t\t\t\t\t\t<SECID>\n\t\t\t\t\t\t\t\t<UNIQUEID>78462F103</UNIQUEID>\n\t\t\t\t\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t\t\t\t\t</SECID>\n\t\t\t\t\t\t\t<HELDINACCT>CASH</HELDINACCT>\n\t\t\t\t\t\t\t<POSTYPE>LONG</POSTYPE>\n\t\t\t\t\t\t\t<UNITS>200</UNITS>\n\t\t\t\t\t\t\t<UNITPRICE>235.74</UNITPRICE>\n\t\t\t\t\t\t\t<MKTVAL>47148.00</MKTVAL>\n\t\t\t\t\t\t\t<DTPRICEASOF>20170331160000</DTPRICEASOF>\n\t\t\t\t\t\t\t<MEMO>Price as of previous close</MEMO>\n\t\t\t\t\t\t</INVPOS>\n\t\t\t\t\t</POSSTOCK>\n\t\t\t\t\t<POSOPT>\n\t\t\t\t\t\t<INVPOS>\n\t\t\t\t\t\t\t<SECID>\n\t\t\t\t\t\t\t\t<UNIQUEID>129887339</UNIQUEID>\n\t\t\t\t\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t\t\t\t\t</SECID>\n\t\t\t\t\t\t\t<HELDINACCT>CASH</HELDINACCT>\n\t\t\t\t\t\t\t<POSTYPE>LONG</POSTYPE>\n\t\t\t\t\t\t\t<UNITS>1</UNITS>\n\t\t\t\t\t\t\t<UNITPRICE>3</UNITPRICE>\n\t\t\t\t\t\t\t<MKTVAL>300</MKTVAL>\n\t\t\t\t\t\t\t<DTPRICEASOF>20170331160000</DTPRICEASOF>\n\t\t\t\t\t\t</INVPOS>\n\t\t\t\t\t</POSOPT>\n\t\t\t\t</INVPOSLIST>\n\t\t\t\t<INVBAL>\n\t\t\t\t\t<AVAILCASH>16.73</AVAILCASH>\n\t\t\t\t\t<MARGINBALANCE>-819.20</MARGINBALANCE>\n\t\t\t\t\t<SHORTBALANCE>0</SHORTBALANCE>\n\t\t\t\t\t<BALLIST>\n\t\t\t\t\t\t<BAL>\n\t\t\t\t\t\t\t<NAME>Sweep Int Rate</NAME>\n\t\t\t\t\t\t\t<DESC>Current interest rate for sweep account balances</DESC>\n\t\t\t\t\t\t\t<BALTYPE>PERCENT</BALTYPE>\n\t\t\t\t\t\t\t<VALUE>0.25</VALUE>\n\t\t\t\t\t\t\t<DTASOF>20170401</DTASOF>\n\t\t\t\t\t\t</BAL>\n\t\t\t\t\t</BALLIST>\n\t\t\t\t</INVBAL>\n\t\t\t\t<INVOOLIST>\n\t\t\t\t\t<OOBUYMF>\n\t\t\t\t\t\t<OO>\n\t\t\t\t\t\t\t<FITID>76464632</FITID>\n\t\t\t\t\t\t\t<SECID>\n\t\t\t\t\t\t\t\t<UNIQUEID>922908645</UNIQUEID>\n\t\t\t\t\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t\t\t\t\t</SECID>\n\t\t\t\t\t\t\t<DTPLACED>20170310124445</DTPLACED>\n\t\t\t\t\t\t\t<UNITS>10</UNITS>\n\t\t\t\t\t\t\t<SUBACCT>CASH</SUBACCT>\n\t\t\t\t\t\t\t<DURATION>GOODTILCANCEL</DURATION>\n\t\t\t\t\t\t\t<RESTRICTION>NONE</RESTRICTION>\n\t\t\t\t\t\t\t<LIMITPRICE>168.50</LIMITPRICE>\n\t\t\t\t\t\t</OO>\n\t\t\t\t\t\t<BUYTYPE>BUY</BUYTYPE>\n\t\t\t\t\t\t<UNITTYPE>SHARES</UNITTYPE>\n\t\t\t\t\t</OOBUYMF>\n\t\t\t\t\t<OOBUYSTOCK>\n\t\t\t\t\t\t<OO>\n\t\t\t\t\t\t\t<FITID>999387423</FITID>\n\t\t\t\t\t\t\t<SECID>\n\t\t\t\t\t\t\t\t<UNIQUEID>899422348</UNIQUEID>\n\t\t\t\t\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t\t\t\t\t</SECID>\n\t\t\t\t\t\t\t<DTPLACED>20170324031900</DTPLACED>\n\t\t\t\t\t\t\t<UNITS>25</UNITS>\n\t\t\t\t\t\t\t<SUBACCT>CASH</SUBACCT>\n\t\t\t\t\t\t\t<DURATION>GOODTILCANCEL</DURATION>\n\t\t\t\t\t\t\t<RESTRICTION>ALLORNONE</RESTRICTION>\n\t\t\t\t\t\t\t<LIMITPRICE>19.75</LIMITPRICE>\n\t\t\t\t\t\t</OO>\n\t\t\t\t\t\t<BUYTYPE>BUY</BUYTYPE>\n\t\t\t\t\t</OOBUYSTOCK>\n\t\t\t\t</INVOOLIST>\n\t\t\t</INVSTMTRS>\n\t\t</INVSTMTTRNRS>\n\t</INVSTMTMSGSRSV1>\n\t<SECLISTMSGSRSV1>\n\t\t<SECLIST>\n\t\t\t<STOCKINFO>\n\t\t\t\t<SECINFO>\n\t\t\t\t\t<SECID>\n\t\t\t\t\t\t<UNIQUEID>78462F103</UNIQUEID>\n\t\t\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t\t\t</SECID>\n\t\t\t\t\t<SECNAME>S&amp;P 500 ETF</SECNAME>\n\t\t\t\t\t<TICKER>SPY</TICKER>\n\t\t\t\t\t<FIID>99184</FIID>\n\t\t\t\t</SECINFO>\n\t\t\t\t<YIELD>1.92</YIELD>\n\t\t\t\t<ASSETCLASS>OTHER</ASSETCLASS>\n\t\t\t</STOCKINFO>\n\t\t\t<OPTINFO>\n\t\t\t\t<SECINFO>\n\t\t\t\t\t<SECID>\n\t\t\t\t\t\t<UNIQUEID>129887339</UNIQUEID>\n\t\t\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t\t\t</SECID>\n\t\t\t\t\t<SECNAME>John's Fertilizer Puts</SECNAME>\n\t\t\t\t\t<TICKER>FERTP</TICKER>\n\t\t\t\t\t<FIID>882919</FIID>\n\t\t\t\t</SECINFO>\n\t\t\t\t<OPTTYPE>PUT</OPTTYPE>\n\t\t\t\t<STRIKEPRICE>79.00</STRIKEPRICE>\n\t\t\t\t<DTEXPIRE>20170901</DTEXPIRE>\n\t\t\t\t<SHPERCTRCT>100</SHPERCTRCT>\n\t\t\t\t<SECID>\n\t\t\t\t\t<UNIQUEID>983322180</UNIQUEID>\n\t\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t\t</SECID>\n\t\t\t\t<ASSETCLASS>LARGESTOCK</ASSETCLASS>\n\t\t\t</OPTINFO>\n\t\t\t<STOCKINFO>\n\t\t\t\t<SECINFO>\n\t\t\t\t\t<SECID>\n\t\t\t\t\t\t<UNIQUEID>899422348</UNIQUEID>\n\t\t\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t\t\t</SECID>\n\t\t\t\t\t<SECNAME>Whatchamacallit, Inc.</SECNAME>\n\t\t\t\t\t<TICKER>WHAT</TICKER>\n\t\t\t\t\t<FIID>883897</FIID>\n\t\t\t\t</SECINFO>\n\t\t\t\t<YIELD>17</YIELD>\n\t\t\t\t<ASSETCLASS>SMALLSTOCK</ASSETCLASS>\n\t\t\t</STOCKINFO>\n\t\t\t<MFINFO>\n\t\t\t\t<SECINFO>\n\t\t\t\t\t<SECID>\n\t\t\t\t\t\t<UNIQUEID>922908645</UNIQUEID>\n\t\t\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t\t\t</SECID>\n\t\t\t\t\t<SECNAME>Mid-Cap Index Fund Admiral Shares</SECNAME>\n\t\t\t\t\t<TICKER>VIMAX</TICKER>\n\t\t\t\t</SECINFO>\n\t\t\t</MFINFO>\n\t\t\t<DEBTINFO>\n\t\t\t\t<SECINFO>\n\t\t\t\t\t<SECID>\n\t\t\t\t\t\t<UNIQUEID>99182828</UNIQUEID>\n\t\t\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t\t\t</SECID>\n\t\t\t\t\t<SECNAME>Someone's Class B Debt</SECNAME>\n\t\t\t\t</SECINFO>\n\t\t\t\t<PARVALUE>100.29</PARVALUE>\n\t\t\t\t<DEBTTYPE>COUPON</DEBTTYPE>\n\t\t\t\t<DTCOUPON>20170901</DTCOUPON>\n\t\t\t\t<COUPONFREQ>QUARTERLY</COUPONFREQ>\n\t\t\t</DEBTINFO>\n\t\t\t<OTHERINFO>\n\t\t\t\t<SECINFO>\n\t\t\t\t\t<SECID>\n\t\t\t\t\t\t<UNIQUEID>88181818</UNIQUEID>\n\t\t\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t\t\t</SECID>\n\t\t\t\t\t<SECNAME>Foo Bar</SECNAME>\n\t\t\t\t</SECINFO>\n\t\t\t\t<TYPEDESC>Don't know what this is</TYPEDESC>\n\t\t\t</OTHERINFO>\n\t\t</SECLIST>\n\t</SECLISTMSGSRSV1>\n</OFX>`)\n\tvar expected Response\n\n\texpected.Version = OfxVersion203\n\texpected.Signon.Status.Code = 0\n\texpected.Signon.Status.Severity = \"INFO\"\n\texpected.Signon.DtServer = *NewDateGMT(2017, 4, 1, 20, 12, 44, 0)\n\texpected.Signon.Language = \"ENG\"\n\texpected.Signon.Org = \"INVSTRUS\"\n\texpected.Signon.Fid = \"9999\"\n\n\tvar units1, unitprice1, commission1, total1, amount2 Amount\n\tunits1.SetFrac64(100, 1)\n\tunitprice1.SetFrac64(229, 1)\n\tcommission1.SetFrac64(9, 1)\n\ttotal1.SetFrac64(-22909, 1)\n\tamount2.SetFrac64(22000, 1)\n\n\tinvtranlist := InvTranList{\n\t\tDtStart: *NewDateGMT(2017, 1, 1, 0, 0, 0, 0),\n\t\tDtEnd:   *NewDateGMT(2017, 3, 31, 0, 0, 0, 0),\n\t\tInvTransactions: []InvTransaction{\n\t\t\tBuyStock{\n\t\t\t\tInvBuy: InvBuy{\n\t\t\t\t\tInvTran: InvTran{\n\t\t\t\t\t\tFiTID:    \"729483191\",\n\t\t\t\t\t\tDtTrade:  *NewDateGMT(2017, 2, 3, 0, 0, 0, 0),\n\t\t\t\t\t\tDtSettle: NewDateGMT(2017, 2, 7, 0, 0, 0, 0),\n\t\t\t\t\t},\n\t\t\t\t\tSecID: SecurityID{\n\t\t\t\t\t\tUniqueID:     \"78462F103\",\n\t\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t\t},\n\t\t\t\t\tUnits:       units1,\n\t\t\t\t\tUnitPrice:   unitprice1,\n\t\t\t\t\tCommission:  commission1,\n\t\t\t\t\tTotal:       total1,\n\t\t\t\t\tSubAcctSec:  SubAcctTypeCash,\n\t\t\t\t\tSubAcctFund: SubAcctTypeCash,\n\t\t\t\t},\n\t\t\t\tBuyType: BuyTypeBuy,\n\t\t\t},\n\t\t},\n\t\tBankTransactions: []InvBankTransaction{\n\t\t\t{\n\t\t\t\tTransactions: []Transaction{\n\t\t\t\t\t{\n\t\t\t\t\t\tTrnType:  TrnTypeCredit,\n\t\t\t\t\t\tDtPosted: *NewDateGMT(2017, 1, 20, 0, 0, 0, 0),\n\t\t\t\t\t\tDtUser:   NewDateGMT(2017, 1, 18, 0, 0, 0, 0),\n\t\t\t\t\t\tDtAvail:  NewDateGMT(2017, 1, 23, 0, 0, 0, 0),\n\n\t\t\t\t\t\tTrnAmt: amount2,\n\t\t\t\t\t\tFiTID:  \"993838\",\n\t\t\t\t\t\tName:   \"DEPOSIT\",\n\t\t\t\t\t\tMemo:   \"CHECK 19980\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tSubAcctFund: SubAcctTypeCash,\n\t\t\t},\n\t\t},\n\t}\n\n\tvar availcash, marginbalance, shortbalance, balvalue Amount\n\tavailcash.SetFrac64(1673, 100)\n\tmarginbalance.SetFrac64(-8192, 10)\n\tshortbalance.SetFrac64(0, 1)\n\tbalvalue.SetFrac64(25, 100)\n\n\tinvbalance := InvBalance{\n\t\tAvailCash:     availcash,\n\t\tMarginBalance: marginbalance,\n\t\tShortBalance:  shortbalance,\n\t\tBalList: []Balance{\n\t\t\t{\n\t\t\t\tName:    \"Sweep Int Rate\",\n\t\t\t\tDesc:    \"Current interest rate for sweep account balances\",\n\t\t\t\tBalType: BalTypePercent,\n\t\t\t\tValue:   balvalue,\n\t\t\t\tDtAsOf:  NewDateGMT(2017, 4, 1, 0, 0, 0, 0),\n\t\t\t},\n\t\t},\n\t}\n\n\tvar balamt, availbalamt, posunits1, posunitprice1, posmktval1, posunits2, posunitprice2, posmktval2, oounits1, oolimitprice1, oounits2, oolimitprice2 Amount\n\tbalamt.SetFrac64(20029, 100)\n\tavailbalamt.SetFrac64(20029, 100)\n\tposunits1.SetFrac64(200, 1)\n\tposunitprice1.SetFrac64(23574, 100)\n\tposmktval1.SetFrac64(47148, 1)\n\tposunits2.SetFrac64(1, 1)\n\tposunitprice2.SetFrac64(3, 1)\n\tposmktval2.SetFrac64(300, 1)\n\toounits1.SetFrac64(10, 1)\n\toolimitprice1.SetFrac64(16850, 100)\n\toounits2.SetFrac64(25, 1)\n\toolimitprice2.SetFrac64(1975, 100)\n\n\tusd, err := NewCurrSymbol(\"USD\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating CurrSymbol for USD\\n\")\n\t}\n\n\tstatementResponse := InvStatementResponse{\n\t\tTrnUID: \"1a0117ad-692b-4c6a-a21b-020d37d34d49\",\n\t\tStatus: Status{\n\t\t\tCode:     0,\n\t\t\tSeverity: \"INFO\",\n\t\t},\n\t\tDtAsOf: *NewDateGMT(2017, 3, 31, 0, 0, 0, 0),\n\t\tCurDef: *usd,\n\t\tInvAcctFrom: InvAcct{\n\t\t\tBrokerID: \"invstrus.com\",\n\t\t\tAcctID:   \"91827364\",\n\t\t},\n\t\tInvTranList: &invtranlist,\n\t\tInvPosList: PositionList{\n\t\t\tStockPosition{\n\t\t\t\tInvPos: InvPosition{\n\t\t\t\t\tSecID: SecurityID{\n\t\t\t\t\t\tUniqueID:     \"78462F103\",\n\t\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t\t},\n\t\t\t\t\tHeldInAcct:  SubAcctTypeCash,\n\t\t\t\t\tPosType:     PosTypeLong,\n\t\t\t\t\tUnits:       posunits1,\n\t\t\t\t\tUnitPrice:   posunitprice1,\n\t\t\t\t\tMktVal:      posmktval1,\n\t\t\t\t\tDtPriceAsOf: *NewDateGMT(2017, 3, 31, 16, 0, 0, 0),\n\t\t\t\t\tMemo:        \"Price as of previous close\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tOptPosition{\n\t\t\t\tInvPos: InvPosition{\n\t\t\t\t\tSecID: SecurityID{\n\t\t\t\t\t\tUniqueID:     \"129887339\",\n\t\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t\t},\n\t\t\t\t\tHeldInAcct:  SubAcctTypeCash,\n\t\t\t\t\tPosType:     PosTypeLong,\n\t\t\t\t\tUnits:       posunits2,\n\t\t\t\t\tUnitPrice:   posunitprice2,\n\t\t\t\t\tMktVal:      posmktval2,\n\t\t\t\t\tDtPriceAsOf: *NewDateGMT(2017, 3, 31, 16, 0, 0, 0),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tInvBal: &invbalance,\n\t\tInvOOList: OOList{\n\t\t\tOOBuyMF{\n\t\t\t\tOO: OO{\n\t\t\t\t\tFiTID: \"76464632\",\n\t\t\t\t\tSecID: SecurityID{\n\t\t\t\t\t\tUniqueID:     \"922908645\",\n\t\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t\t},\n\t\t\t\t\tDtPlaced:    *NewDateGMT(2017, 3, 10, 12, 44, 45, 0),\n\t\t\t\t\tUnits:       oounits1,\n\t\t\t\t\tSubAcct:     SubAcctTypeCash,\n\t\t\t\t\tDuration:    DurationGoodTilCancel,\n\t\t\t\t\tRestriction: RestrictionNone,\n\t\t\t\t\tLimitPrice:  oolimitprice1,\n\t\t\t\t},\n\t\t\t\tBuyType:  BuyTypeBuy,\n\t\t\t\tUnitType: UnitTypeShares,\n\t\t\t},\n\t\t\tOOBuyStock{\n\t\t\t\tOO: OO{\n\t\t\t\t\tFiTID: \"999387423\",\n\t\t\t\t\tSecID: SecurityID{\n\t\t\t\t\t\tUniqueID:     \"899422348\",\n\t\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t\t},\n\t\t\t\t\tDtPlaced:    *NewDateGMT(2017, 3, 24, 3, 19, 0, 0),\n\t\t\t\t\tUnits:       oounits2,\n\t\t\t\t\tSubAcct:     SubAcctTypeCash,\n\t\t\t\t\tDuration:    DurationGoodTilCancel,\n\t\t\t\t\tRestriction: RestrictionAllOrNone,\n\t\t\t\t\tLimitPrice:  oolimitprice2,\n\t\t\t\t},\n\t\t\t\tBuyType: BuyTypeBuy,\n\t\t\t},\n\t\t},\n\t}\n\texpected.InvStmt = append(expected.InvStmt, &statementResponse)\n\n\tvar yield1, yield2, strikeprice, parvalue Amount\n\tyield1.SetFrac64(192, 100)\n\tyield2.SetFrac64(17, 1)\n\tstrikeprice.SetFrac64(79, 1)\n\tparvalue.SetFrac64(10029, 100)\n\n\tseclist := SecurityList{\n\t\tSecurities: []Security{\n\t\t\tStockInfo{\n\t\t\t\tSecInfo: SecInfo{\n\t\t\t\t\tSecID: SecurityID{\n\t\t\t\t\t\tUniqueID:     \"78462F103\",\n\t\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t\t},\n\t\t\t\t\tSecName: \"S&P 500 ETF\",\n\t\t\t\t\tTicker:  \"SPY\",\n\t\t\t\t\tFiID:    \"99184\",\n\t\t\t\t},\n\t\t\t\tYield:      yield1,\n\t\t\t\tAssetClass: AssetClassOther,\n\t\t\t},\n\t\t\tOptInfo{\n\t\t\t\tSecInfo: SecInfo{\n\t\t\t\t\tSecID: SecurityID{\n\t\t\t\t\t\tUniqueID:     \"129887339\",\n\t\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t\t},\n\t\t\t\t\tSecName: \"John's Fertilizer Puts\",\n\t\t\t\t\tTicker:  \"FERTP\",\n\t\t\t\t\tFiID:    \"882919\",\n\t\t\t\t},\n\t\t\t\tOptType:     OptTypePut,\n\t\t\t\tStrikePrice: strikeprice,\n\t\t\t\tDtExpire:    *NewDateGMT(2017, 9, 1, 0, 0, 0, 0),\n\t\t\t\tShPerCtrct:  100,\n\t\t\t\tSecID: &SecurityID{\n\t\t\t\t\tUniqueID:     \"983322180\",\n\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t},\n\t\t\t\tAssetClass: AssetClassLargeStock,\n\t\t\t},\n\t\t\tStockInfo{\n\t\t\t\tSecInfo: SecInfo{\n\t\t\t\t\tSecID: SecurityID{\n\t\t\t\t\t\tUniqueID:     \"899422348\",\n\t\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t\t},\n\t\t\t\t\tSecName: \"Whatchamacallit, Inc.\",\n\t\t\t\t\tTicker:  \"WHAT\",\n\t\t\t\t\tFiID:    \"883897\",\n\t\t\t\t},\n\t\t\t\tYield:      yield2,\n\t\t\t\tAssetClass: AssetClassSmallStock,\n\t\t\t},\n\t\t\tMFInfo{\n\t\t\t\tSecInfo: SecInfo{\n\t\t\t\t\tSecID: SecurityID{\n\t\t\t\t\t\tUniqueID:     \"922908645\",\n\t\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t\t},\n\t\t\t\t\tSecName: \"Mid-Cap Index Fund Admiral Shares\",\n\t\t\t\t\tTicker:  \"VIMAX\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tDebtInfo{\n\t\t\t\tSecInfo: SecInfo{\n\t\t\t\t\tSecID: SecurityID{\n\t\t\t\t\t\tUniqueID:     \"99182828\",\n\t\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t\t},\n\t\t\t\t\tSecName: \"Someone's Class B Debt\",\n\t\t\t\t},\n\t\t\t\tParValue:   parvalue,\n\t\t\t\tDebtType:   DebtTypeCoupon,\n\t\t\t\tDtCoupon:   NewDateGMT(2017, 9, 1, 0, 0, 0, 0),\n\t\t\t\tCouponFreq: CouponFreqQuarterly,\n\t\t\t},\n\t\t\tOtherInfo{\n\t\t\t\tSecInfo: SecInfo{\n\t\t\t\t\tSecID: SecurityID{\n\t\t\t\t\t\tUniqueID:     \"88181818\",\n\t\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t\t},\n\t\t\t\t\tSecName: \"Foo Bar\",\n\t\t\t\t},\n\t\t\t\tTypeDesc: \"Don't know what this is\",\n\t\t\t},\n\t\t},\n\t}\n\texpected.SecList = append(expected.SecList, &seclist)\n\n\tresponse, err := ParseResponse(responseReader)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error unmarshalling response: %s\\n\", err)\n\t}\n\n\tcheckResponsesEqual(t, &expected, response)\n\tcheckResponseRoundTrip(t, response)\n}\n\nfunc TestUnmarshalInvStatementResponse102(t *testing.T) {\n\tresponseReader := strings.NewReader(`OFXHEADER: 100\nDATA: OFXSGML\nVERSION: 102\nSECURITY: NONE\nENCODING: USASCII\nCHARSET: 1252\nCOMPRESSION: NONE\nOLDFILEUID: NONE\nNEWFILEUID: NONE\n\n<OFX>\n <SIGNONMSGSRSV1>\n  <SONRS>\n   <STATUS>\n    <CODE>0\n    <SEVERITY>INFO\n   </STATUS>\n   <DTSERVER>20170403120000\n   <LANGUAGE>ENG\n   <FI>\n    <ORG>VV\n    <FID>1000\n   </FI>\n   <INTU.BID>1000\n  </SONRS>\n </SIGNONMSGSRSV1>\n <INVSTMTMSGSRSV1>\n  <INVSTMTTRNRS>\n   <TRNUID>1283719872\n   <STATUS>\n    <CODE>0\n    <SEVERITY>INFO\n   </STATUS>\n   <INVSTMTRS>\n    <DTASOF>20170403120000\n    <CURDEF>USD\n    <INVACCTFROM>\n     <BROKERID>www.exampletrader.com\n     <ACCTID>12341234\n    </INVACCTFROM>\n    <INVTRANLIST>\n     <DTSTART>20161206120000\n     <DTEND>20170403120000\n     <SELLOPT>\n      <INVSELL>\n       <INVTRAN>\n        <FITID>12341234-20161207-1\n        <DTTRADE>20161207120000\n        <DTSETTLE>20161208120000\n       </INVTRAN>\n       <SECID>\n        <UNIQUEID>SPY161216C00226000\n        <UNIQUEIDTYPE>CUSIP\n       </SECID>\n       <UNITS>-1.0000\n       <UNITPRICE>0.3500\n       <COMMISSION>8.8500\n       <FEES>0.2600\n       <TOTAL>200.8900\n       <SUBACCTSEC>CASH\n       <SUBACCTFUND>CASH\n      </INVSELL>\n      <OPTSELLTYPE>SELLTOOPEN\n      <SHPERCTRCT>100\n     </SELLOPT>\n     <CLOSUREOPT>\n      <INVTRAN>\n       <FITID>12341234-20161215-1\n       <DTTRADE>20161215120000\n       <DTSETTLE>20161220120000\n      </INVTRAN>\n      <SECID>\n       <UNIQUEID>78462F10\n       <UNIQUEIDTYPE>CUSIP\n      </SECID>\n      <OPTACTION>ASSIGN\n      <UNITS>-100.0000\n      <SHPERCTRCT>100\n      <SUBACCTSEC>CASH\n     </CLOSUREOPT>\n     <CLOSUREOPT>\n      <INVTRAN>\n       <FITID>12341234-20161215-2\n       <DTTRADE>20161215120000\n       <DTSETTLE>20161215120000\n      </INVTRAN>\n      <SECID>\n       <UNIQUEID>SPY161216C00226000\n       <UNIQUEIDTYPE>CUSIP\n      </SECID>\n      <OPTACTION>ASSIGN\n      <UNITS>1.0000\n      <SHPERCTRCT>100\n      <SUBACCTSEC>CASH\n     </CLOSUREOPT>\n    </INVTRANLIST>\n    <INVPOSLIST>\n     <POSSTOCK>\n      <INVPOS>\n       <SECID>\n        <UNIQUEID>04956010\n        <UNIQUEIDTYPE>CUSIP\n       </SECID>\n       <HELDINACCT>CASH\n       <POSTYPE>LONG\n       <UNITS>100\n       <UNITPRICE>79.0000\n       <MKTVAL>79000\n       <DTPRICEASOF>20170403120000\n      </INVPOS>\n     </POSSTOCK>\n     <POSSTOCK>\n      <INVPOS>\n       <SECID>\n        <UNIQUEID>36960410\n        <UNIQUEIDTYPE>CUSIP\n       </SECID>\n       <HELDINACCT>CASH\n       <POSTYPE>LONG\n       <UNITS>100.00\n       <UNITPRICE>29.8700\n       <MKTVAL>2987.00\n       <DTPRICEASOF>20170403120000\n      </INVPOS>\n     </POSSTOCK>\n    </INVPOSLIST>\n    <INVBAL>\n     <AVAILCASH>0.0\n     <MARGINBALANCE>-0.00\n     <SHORTBALANCE>0.00\n    </INVBAL>\n   </INVSTMTRS>\n  </INVSTMTTRNRS>\n </INVSTMTMSGSRSV1>\n <SECLISTMSGSRSV1>\n  <SECLIST>\n   <STOCKINFO>\n    <SECINFO>\n     <SECID>\n      <UNIQUEID>78462F10\n      <UNIQUEIDTYPE>CUSIP\n     </SECID>\n     <SECNAME>SPDR S&amp;P 500 ETF TRUST\n     <TICKER>SPY\n    </SECINFO>\n   </STOCKINFO>\n   <OPTINFO>\n    <SECINFO>\n     <SECID>\n      <UNIQUEID>SPY161216C00226000\n      <UNIQUEIDTYPE>CUSIP\n     </SECID>\n     <SECNAME>SPY Dec 16 2016 226.00 Call\n     <TICKER>SPY   161216C00226000\n    </SECINFO>\n    <OPTTYPE>CALL\n    <STRIKEPRICE>226.00\n    <DTEXPIRE>20161216120000\n    <SHPERCTRCT>100\n   </OPTINFO>\n  </SECLIST>\n </SECLISTMSGSRSV1>\n</OFX>`)\n\tvar expected Response\n\n\texpected.Version = OfxVersion102\n\texpected.Signon.Status.Code = 0\n\texpected.Signon.Status.Severity = \"INFO\"\n\texpected.Signon.DtServer = *NewDateGMT(2017, 4, 3, 12, 0, 0, 0)\n\texpected.Signon.Language = \"ENG\"\n\texpected.Signon.Org = \"VV\"\n\texpected.Signon.Fid = \"1000\"\n\t// Ignored <INTU.BID>1000\n\n\tvar units1, unitprice1, commission1, fees1, total1, units2, units3 Amount\n\tunits1.SetFrac64(-1, 1)\n\tunitprice1.SetFrac64(35, 100)\n\tcommission1.SetFrac64(885, 100)\n\tfees1.SetFrac64(26, 100)\n\ttotal1.SetFrac64(20089, 100)\n\tunits2.SetFrac64(-100, 1)\n\tunits3.SetFrac64(1, 1)\n\n\tinvtranlist := InvTranList{\n\t\tDtStart: *NewDateGMT(2016, 12, 6, 12, 0, 0, 0),\n\t\tDtEnd:   *NewDateGMT(2017, 4, 3, 12, 0, 0, 0),\n\t\tInvTransactions: []InvTransaction{\n\t\t\tSellOpt{\n\t\t\t\tInvSell: InvSell{\n\t\t\t\t\tInvTran: InvTran{\n\t\t\t\t\t\tFiTID:    \"12341234-20161207-1\",\n\t\t\t\t\t\tDtTrade:  *NewDateGMT(2016, 12, 7, 12, 0, 0, 0),\n\t\t\t\t\t\tDtSettle: NewDateGMT(2016, 12, 8, 12, 0, 0, 0),\n\t\t\t\t\t},\n\t\t\t\t\tSecID: SecurityID{\n\t\t\t\t\t\tUniqueID:     \"SPY161216C00226000\",\n\t\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t\t},\n\t\t\t\t\tUnits:       units1,\n\t\t\t\t\tUnitPrice:   unitprice1,\n\t\t\t\t\tCommission:  commission1,\n\t\t\t\t\tFees:        fees1,\n\t\t\t\t\tTotal:       total1,\n\t\t\t\t\tSubAcctSec:  SubAcctTypeCash,\n\t\t\t\t\tSubAcctFund: SubAcctTypeCash,\n\t\t\t\t},\n\t\t\t\tOptSellType: OptSellTypeSellToOpen,\n\t\t\t\tShPerCtrct:  100,\n\t\t\t},\n\t\t\tClosureOpt{\n\t\t\t\tInvTran: InvTran{\n\t\t\t\t\tFiTID:    \"12341234-20161215-1\",\n\t\t\t\t\tDtTrade:  *NewDateGMT(2016, 12, 15, 12, 0, 0, 0),\n\t\t\t\t\tDtSettle: NewDateGMT(2016, 12, 20, 12, 0, 0, 0),\n\t\t\t\t},\n\t\t\t\tSecID: SecurityID{\n\t\t\t\t\tUniqueID:     \"78462F10\",\n\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t},\n\t\t\t\tOptAction:  OptActionAssign,\n\t\t\t\tUnits:      units2,\n\t\t\t\tShPerCtrct: 100,\n\t\t\t\tSubAcctSec: SubAcctTypeCash,\n\t\t\t},\n\t\t\tClosureOpt{\n\t\t\t\tInvTran: InvTran{\n\t\t\t\t\tFiTID:    \"12341234-20161215-2\",\n\t\t\t\t\tDtTrade:  *NewDateGMT(2016, 12, 15, 12, 0, 0, 0),\n\t\t\t\t\tDtSettle: NewDateGMT(2016, 12, 15, 12, 0, 0, 0),\n\t\t\t\t},\n\t\t\t\tSecID: SecurityID{\n\t\t\t\t\tUniqueID:     \"SPY161216C00226000\",\n\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t},\n\t\t\t\tOptAction:  OptActionAssign,\n\t\t\t\tUnits:      units3,\n\t\t\t\tShPerCtrct: 100,\n\t\t\t\tSubAcctSec: SubAcctTypeCash,\n\t\t\t},\n\t\t},\n\t}\n\n\tvar availcash, marginbalance, shortbalance Amount\n\tavailcash.SetFrac64(0, 1)\n\tmarginbalance.SetFrac64(-0, 1)\n\tshortbalance.SetFrac64(0, 1)\n\n\tinvbalance := InvBalance{\n\t\tAvailCash:     availcash,\n\t\tMarginBalance: marginbalance,\n\t\tShortBalance:  shortbalance,\n\t}\n\n\tvar posunits1, posunitprice1, posmktval1, posunits2, posunitprice2, posmktval2 Amount\n\tposunits1.SetFrac64(100, 1)\n\tposunitprice1.SetFrac64(79, 1)\n\tposmktval1.SetFrac64(79000, 1)\n\tposunits2.SetFrac64(100, 1)\n\tposunitprice2.SetFrac64(2987, 100)\n\tposmktval2.SetFrac64(2987, 1)\n\n\tusd, err := NewCurrSymbol(\"USD\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error creating CurrSymbol for USD\\n\")\n\t}\n\n\tstatementResponse := InvStatementResponse{\n\t\tTrnUID: \"1283719872\",\n\t\tStatus: Status{\n\t\t\tCode:     0,\n\t\t\tSeverity: \"INFO\",\n\t\t},\n\t\tDtAsOf: *NewDateGMT(2017, 4, 3, 12, 0, 0, 0),\n\t\tCurDef: *usd,\n\t\tInvAcctFrom: InvAcct{\n\t\t\tBrokerID: \"www.exampletrader.com\",\n\t\t\tAcctID:   \"12341234\",\n\t\t},\n\t\tInvTranList: &invtranlist,\n\t\tInvPosList: PositionList{\n\t\t\tStockPosition{\n\t\t\t\tInvPos: InvPosition{\n\t\t\t\t\tSecID: SecurityID{\n\t\t\t\t\t\tUniqueID:     \"04956010\",\n\t\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t\t},\n\t\t\t\t\tHeldInAcct:  SubAcctTypeCash,\n\t\t\t\t\tPosType:     PosTypeLong,\n\t\t\t\t\tUnits:       posunits1,\n\t\t\t\t\tUnitPrice:   posunitprice1,\n\t\t\t\t\tMktVal:      posmktval1,\n\t\t\t\t\tDtPriceAsOf: *NewDateGMT(2017, 4, 3, 12, 0, 0, 0),\n\t\t\t\t},\n\t\t\t},\n\t\t\tStockPosition{\n\t\t\t\tInvPos: InvPosition{\n\t\t\t\t\tSecID: SecurityID{\n\t\t\t\t\t\tUniqueID:     \"36960410\",\n\t\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t\t},\n\t\t\t\t\tHeldInAcct:  SubAcctTypeCash,\n\t\t\t\t\tPosType:     PosTypeLong,\n\t\t\t\t\tUnits:       posunits2,\n\t\t\t\t\tUnitPrice:   posunitprice2,\n\t\t\t\t\tMktVal:      posmktval2,\n\t\t\t\t\tDtPriceAsOf: *NewDateGMT(2017, 4, 3, 12, 0, 0, 0),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tInvBal: &invbalance,\n\t}\n\texpected.InvStmt = append(expected.InvStmt, &statementResponse)\n\n\tvar strikeprice Amount\n\tstrikeprice.SetFrac64(226, 1)\n\n\tseclist := SecurityList{\n\t\tSecurities: []Security{\n\t\t\tStockInfo{\n\t\t\t\tSecInfo: SecInfo{\n\t\t\t\t\tSecID: SecurityID{\n\t\t\t\t\t\tUniqueID:     \"78462F10\",\n\t\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t\t},\n\t\t\t\t\tSecName: \"SPDR S&P 500 ETF TRUST\",\n\t\t\t\t\tTicker:  \"SPY\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tOptInfo{\n\t\t\t\tSecInfo: SecInfo{\n\t\t\t\t\tSecID: SecurityID{\n\t\t\t\t\t\tUniqueID:     \"SPY161216C00226000\",\n\t\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t\t},\n\t\t\t\t\tSecName: \"SPY Dec 16 2016 226.00 Call\",\n\t\t\t\t\tTicker:  \"SPY   161216C00226000\",\n\t\t\t\t},\n\t\t\t\tOptType:     OptTypeCall,\n\t\t\t\tStrikePrice: strikeprice,\n\t\t\t\tDtExpire:    *NewDateGMT(2016, 12, 16, 12, 0, 0, 0),\n\t\t\t\tShPerCtrct:  100,\n\t\t\t},\n\t\t},\n\t}\n\texpected.SecList = append(expected.SecList, &seclist)\n\n\tresponse, err := ParseResponse(responseReader)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error unmarshalling response: %s\\n\", err)\n\t}\n\n\tcheckResponsesEqual(t, &expected, response)\n\tcheckResponseRoundTrip(t, response)\n}\n\nfunc TestUnmarshalInvTranList(t *testing.T) {\n\tinput := `<INVTRANLIST>\n\t<DTSTART>20170101000000</DTSTART>\n\t<DTEND>20170331000000</DTEND>\n\t<BUYDEBT>\n\t\t<INVBUY>\n\t\t\t<INVTRAN>\n\t\t\t\t<FITID>81818</FITID>\n\t\t\t\t<DTTRADE>20170203</DTTRADE>\n\t\t\t\t<DTSETTLE>20170207</DTSETTLE>\n\t\t\t</INVTRAN>\n\t\t\t<SECID>\n\t\t\t\t<UNIQUEID>78462F103</UNIQUEID>\n\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t</SECID>\n\t\t\t<UNITS>100</UNITS>\n\t\t\t<UNITPRICE>229.00</UNITPRICE>\n\t\t\t<COMMISSION>9.00</COMMISSION>\n\t\t\t<FEES>.26</FEES>\n\t\t\t<TOTAL>-22090.26</TOTAL>\n\t\t\t<SUBACCTSEC>CASH</SUBACCTSEC>\n\t\t\t<SUBACCTFUND>CASH</SUBACCTFUND>\n\t\t</INVBUY>\n\t\t<ACCRDINT>101.2</ACCRDINT>\n\t</BUYDEBT>\n\t<BUYOPT>\n\t\t<INVBUY>\n\t\t\t<INVTRAN>\n\t\t\t\t<FITID>81818</FITID>\n\t\t\t\t<DTTRADE>20170203</DTTRADE>\n\t\t\t\t<MEMO>Something to make a memo about</MEMO>\n\t\t\t</INVTRAN>\n\t\t\t<SECID>\n\t\t\t\t<UNIQUEID>78462F103</UNIQUEID>\n\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t</SECID>\n\t\t\t<UNITS>100</UNITS>\n\t\t\t<UNITPRICE>229.00</UNITPRICE>\n\t\t\t<TOTAL>-22090.26</TOTAL>\n\t\t\t<SUBACCTSEC>CASH</SUBACCTSEC>\n\t\t\t<SUBACCTFUND>CASH</SUBACCTFUND>\n\t\t</INVBUY>\n\t\t<OPTBUYTYPE>BUYTOOPEN</OPTBUYTYPE>\n\t\t<SHPERCTRCT>100</SHPERCTRCT>\n\t</BUYOPT>\n\t<INVEXPENSE>\n\t\t<INVTRAN>\n\t\t\t<FITID>129837-1111</FITID>\n\t\t\t<DTTRADE>20170203</DTTRADE>\n\t\t</INVTRAN>\n\t\t<SECID>\n\t\t\t<UNIQUEID>78462F103</UNIQUEID>\n\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t</SECID>\n\t\t<TOTAL>0.26</TOTAL>\n\t\t<SUBACCTSEC>CASH</SUBACCTSEC>\n\t\t<SUBACCTFUND>CASH</SUBACCTFUND>\n\t</INVEXPENSE>\n\t<JRNLSEC>\n\t\t<INVTRAN>\n\t\t\t<FITID>129837-1112</FITID>\n\t\t\t<DTTRADE>20170203</DTTRADE>\n\t\t</INVTRAN>\n\t\t<SECID>\n\t\t\t<UNIQUEID>78462F103</UNIQUEID>\n\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t</SECID>\n\t\t<UNITS>2300</UNITS>\n\t\t<SUBACCTTO>CASH</SUBACCTTO>\n\t\t<SUBACCTFROM>CASH</SUBACCTFROM>\n\t</JRNLSEC>\n\t<JRNLFUND>\n\t\t<INVTRAN>\n\t\t\t<FITID>129837-1112</FITID>\n\t\t\t<DTTRADE>20170203</DTTRADE>\n\t\t</INVTRAN>\n\t\t<TOTAL>2300</TOTAL>\n\t\t<SUBACCTTO>CASH</SUBACCTTO>\n\t\t<SUBACCTFROM>CASH</SUBACCTFROM>\n\t</JRNLFUND>\n\t<BUYOTHER>\n\t\t<INVBUY>\n\t\t\t<INVTRAN>\n\t\t\t\t<FITID>81818</FITID>\n\t\t\t\t<DTTRADE>20170203</DTTRADE>\n\t\t\t</INVTRAN>\n\t\t\t<SECID>\n\t\t\t\t<UNIQUEID>78462F103</UNIQUEID>\n\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t</SECID>\n\t\t\t<UNITS>100</UNITS>\n\t\t\t<UNITPRICE>229.00</UNITPRICE>\n\t\t\t<TOTAL>-22090.26</TOTAL>\n\t\t\t<SUBACCTSEC>CASH</SUBACCTSEC>\n\t\t\t<SUBACCTFUND>CASH</SUBACCTFUND>\n\t\t</INVBUY>\n\t</BUYOTHER>\n\t<MARGININTEREST>\n\t\t<INVTRAN>\n\t\t\t<FITID>129837-1112</FITID>\n\t\t\t<DTTRADE>20170203</DTTRADE>\n\t\t</INVTRAN>\n\t\t<TOTAL>2300</TOTAL>\n\t\t<SUBACCTFUND>CASH</SUBACCTFUND>\n\t</MARGININTEREST>\n\t<SELLDEBT>\n\t\t<INVSELL>\n\t\t\t<INVTRAN>\n\t\t\t\t<FITID>129837-1111</FITID>\n\t\t\t\t<DTTRADE>20170203</DTTRADE>\n\t\t\t</INVTRAN>\n\t\t\t<SECID>\n\t\t\t\t<UNIQUEID>78462F103</UNIQUEID>\n\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t</SECID>\n\t\t\t<UNITS>100</UNITS>\n\t\t\t<UNITPRICE>229.00</UNITPRICE>\n\t\t\t<TOTAL>-22090.26</TOTAL>\n\t\t\t<SUBACCTSEC>CASH</SUBACCTSEC>\n\t\t\t<SUBACCTFUND>CASH</SUBACCTFUND>\n\t\t</INVSELL>\n\t\t<SELLREASON>SELL</SELLREASON>\n\t</SELLDEBT>\n\t<RETOFCAP>\n\t\t<INVTRAN>\n\t\t\t<FITID>129837-1111</FITID>\n\t\t\t<DTTRADE>20170203</DTTRADE>\n\t\t</INVTRAN>\n\t\t<SECID>\n\t\t\t<UNIQUEID>78462F103</UNIQUEID>\n\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t</SECID>\n\t\t<TOTAL>2300.00</TOTAL>\n\t\t<SUBACCTSEC>CASH</SUBACCTSEC>\n\t\t<SUBACCTFUND>CASH</SUBACCTFUND>\n\t</RETOFCAP>\n\t<SPLIT>\n\t\t<INVTRAN>\n\t\t\t<FITID>129837-1111</FITID>\n\t\t\t<DTTRADE>20170203</DTTRADE>\n\t\t</INVTRAN>\n\t\t<SECID>\n\t\t\t<UNIQUEID>78462F103</UNIQUEID>\n\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t</SECID>\n\t\t<SUBACCTSEC>CASH</SUBACCTSEC>\n\t\t<OLDUNITS>100</OLDUNITS>\n\t\t<NEWUNITS>200</NEWUNITS>\n\t\t<NUMERATOR>2</NUMERATOR>\n\t\t<DENOMINATOR>1</DENOMINATOR>\n\t</SPLIT>\n\t<SELLOTHER>\n\t\t<INVSELL>\n\t\t\t<INVTRAN>\n\t\t\t\t<FITID>129837-1111</FITID>\n\t\t\t\t<DTTRADE>20170203</DTTRADE>\n\t\t\t</INVTRAN>\n\t\t\t<SECID>\n\t\t\t\t<UNIQUEID>78462F103</UNIQUEID>\n\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t</SECID>\n\t\t\t<UNITS>100</UNITS>\n\t\t\t<UNITPRICE>229.00</UNITPRICE>\n\t\t\t<TOTAL>-22090.26</TOTAL>\n\t\t\t<SUBACCTSEC>CASH</SUBACCTSEC>\n\t\t\t<SUBACCTFUND>CASH</SUBACCTFUND>\n\t\t</INVSELL>\n\t</SELLOTHER>\n</INVTRANLIST>`\n\n\tvar units1, unitprice1, commission1, fees1, total1, accrdint, total2, oldunits1, newunits1 Amount\n\tunits1.SetFrac64(100, 1)\n\tunitprice1.SetFrac64(229, 1)\n\tcommission1.SetFrac64(9, 1)\n\tfees1.SetFrac64(26, 100)\n\ttotal1.SetFrac64(-2209026, 100)\n\taccrdint.SetFrac64(1012, 10)\n\ttotal2.SetFrac64(2300, 1)\n\toldunits1.SetFrac64(100, 1)\n\tnewunits1.SetFrac64(200, 1)\n\n\texpected := InvTranList{\n\t\tDtStart: *NewDateGMT(2017, 1, 1, 0, 0, 0, 0),\n\t\tDtEnd:   *NewDateGMT(2017, 3, 31, 0, 0, 0, 0),\n\t\tInvTransactions: []InvTransaction{\n\t\t\tBuyDebt{\n\t\t\t\tInvBuy: InvBuy{\n\t\t\t\t\tInvTran: InvTran{\n\t\t\t\t\t\tFiTID:    \"81818\",\n\t\t\t\t\t\tDtTrade:  *NewDateGMT(2017, 2, 3, 0, 0, 0, 0),\n\t\t\t\t\t\tDtSettle: NewDateGMT(2017, 2, 7, 0, 0, 0, 0),\n\t\t\t\t\t},\n\t\t\t\t\tSecID: SecurityID{\n\t\t\t\t\t\tUniqueID:     \"78462F103\",\n\t\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t\t},\n\t\t\t\t\tUnits:       units1,\n\t\t\t\t\tUnitPrice:   unitprice1,\n\t\t\t\t\tCommission:  commission1,\n\t\t\t\t\tFees:        fees1,\n\t\t\t\t\tTotal:       total1,\n\t\t\t\t\tSubAcctSec:  SubAcctTypeCash,\n\t\t\t\t\tSubAcctFund: SubAcctTypeCash,\n\t\t\t\t},\n\t\t\t\tAccrdInt: accrdint,\n\t\t\t},\n\t\t\tBuyOpt{\n\t\t\t\tInvBuy: InvBuy{\n\t\t\t\t\tInvTran: InvTran{\n\t\t\t\t\t\tFiTID:   \"81818\",\n\t\t\t\t\t\tDtTrade: *NewDateGMT(2017, 2, 3, 0, 0, 0, 0),\n\t\t\t\t\t\tMemo:    \"Something to make a memo about\",\n\t\t\t\t\t},\n\t\t\t\t\tSecID: SecurityID{\n\t\t\t\t\t\tUniqueID:     \"78462F103\",\n\t\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t\t},\n\t\t\t\t\tUnits:       units1,\n\t\t\t\t\tUnitPrice:   unitprice1,\n\t\t\t\t\tTotal:       total1,\n\t\t\t\t\tSubAcctSec:  SubAcctTypeCash,\n\t\t\t\t\tSubAcctFund: SubAcctTypeCash,\n\t\t\t\t},\n\t\t\t\tOptBuyType: OptBuyTypeBuyToOpen,\n\t\t\t\tShPerCtrct: 100,\n\t\t\t},\n\t\t\tInvExpense{\n\t\t\t\tInvTran: InvTran{\n\t\t\t\t\tFiTID:   \"129837-1111\",\n\t\t\t\t\tDtTrade: *NewDateGMT(2017, 2, 3, 0, 0, 0, 0),\n\t\t\t\t},\n\t\t\t\tSecID: SecurityID{\n\t\t\t\t\tUniqueID:     \"78462F103\",\n\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t},\n\t\t\t\tTotal:       fees1,\n\t\t\t\tSubAcctSec:  SubAcctTypeCash,\n\t\t\t\tSubAcctFund: SubAcctTypeCash,\n\t\t\t},\n\t\t\tJrnlSec{\n\t\t\t\tInvTran: InvTran{\n\t\t\t\t\tFiTID:   \"129837-1112\",\n\t\t\t\t\tDtTrade: *NewDateGMT(2017, 2, 3, 0, 0, 0, 0),\n\t\t\t\t},\n\t\t\t\tSecID: SecurityID{\n\t\t\t\t\tUniqueID:     \"78462F103\",\n\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t},\n\t\t\t\tUnits:       total2,\n\t\t\t\tSubAcctTo:   SubAcctTypeCash,\n\t\t\t\tSubAcctFrom: SubAcctTypeCash,\n\t\t\t},\n\t\t\tJrnlFund{\n\t\t\t\tInvTran: InvTran{\n\t\t\t\t\tFiTID:   \"129837-1112\",\n\t\t\t\t\tDtTrade: *NewDateGMT(2017, 2, 3, 0, 0, 0, 0),\n\t\t\t\t},\n\t\t\t\tTotal:       total2,\n\t\t\t\tSubAcctTo:   SubAcctTypeCash,\n\t\t\t\tSubAcctFrom: SubAcctTypeCash,\n\t\t\t},\n\t\t\tBuyOther{\n\t\t\t\tInvBuy: InvBuy{\n\t\t\t\t\tInvTran: InvTran{\n\t\t\t\t\t\tFiTID:   \"81818\",\n\t\t\t\t\t\tDtTrade: *NewDateGMT(2017, 2, 3, 0, 0, 0, 0),\n\t\t\t\t\t},\n\t\t\t\t\tSecID: SecurityID{\n\t\t\t\t\t\tUniqueID:     \"78462F103\",\n\t\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t\t},\n\t\t\t\t\tUnits:       units1,\n\t\t\t\t\tUnitPrice:   unitprice1,\n\t\t\t\t\tTotal:       total1,\n\t\t\t\t\tSubAcctSec:  SubAcctTypeCash,\n\t\t\t\t\tSubAcctFund: SubAcctTypeCash,\n\t\t\t\t},\n\t\t\t},\n\t\t\tMarginInterest{\n\t\t\t\tInvTran: InvTran{\n\t\t\t\t\tFiTID:   \"129837-1112\",\n\t\t\t\t\tDtTrade: *NewDateGMT(2017, 2, 3, 0, 0, 0, 0),\n\t\t\t\t},\n\t\t\t\tTotal:       total2,\n\t\t\t\tSubAcctFund: SubAcctTypeCash,\n\t\t\t},\n\t\t\tSellDebt{\n\t\t\t\tInvSell: InvSell{\n\t\t\t\t\tInvTran: InvTran{\n\t\t\t\t\t\tFiTID:   \"129837-1111\",\n\t\t\t\t\t\tDtTrade: *NewDateGMT(2017, 2, 3, 0, 0, 0, 0),\n\t\t\t\t\t},\n\t\t\t\t\tSecID: SecurityID{\n\t\t\t\t\t\tUniqueID:     \"78462F103\",\n\t\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t\t},\n\t\t\t\t\tUnits:       units1,\n\t\t\t\t\tUnitPrice:   unitprice1,\n\t\t\t\t\tTotal:       total1,\n\t\t\t\t\tSubAcctSec:  SubAcctTypeCash,\n\t\t\t\t\tSubAcctFund: SubAcctTypeCash,\n\t\t\t\t},\n\t\t\t\tSellReason: SellReasonSell,\n\t\t\t},\n\t\t\tRetOfCap{\n\t\t\t\tInvTran: InvTran{\n\t\t\t\t\tFiTID:   \"129837-1111\",\n\t\t\t\t\tDtTrade: *NewDateGMT(2017, 2, 3, 0, 0, 0, 0),\n\t\t\t\t},\n\t\t\t\tSecID: SecurityID{\n\t\t\t\t\tUniqueID:     \"78462F103\",\n\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t},\n\t\t\t\tTotal:       total2,\n\t\t\t\tSubAcctSec:  SubAcctTypeCash,\n\t\t\t\tSubAcctFund: SubAcctTypeCash,\n\t\t\t},\n\t\t\tSplit{\n\t\t\t\tInvTran: InvTran{\n\t\t\t\t\tFiTID:   \"129837-1111\",\n\t\t\t\t\tDtTrade: *NewDateGMT(2017, 2, 3, 0, 0, 0, 0),\n\t\t\t\t},\n\t\t\t\tSecID: SecurityID{\n\t\t\t\t\tUniqueID:     \"78462F103\",\n\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t},\n\t\t\t\tSubAcctSec:  SubAcctTypeCash,\n\t\t\t\tOldUnits:    oldunits1,\n\t\t\t\tNewUnits:    newunits1,\n\t\t\t\tNumerator:   2,\n\t\t\t\tDenominator: 1,\n\t\t\t},\n\t\t\tSellOther{\n\t\t\t\tInvSell: InvSell{\n\t\t\t\t\tInvTran: InvTran{\n\t\t\t\t\t\tFiTID:   \"129837-1111\",\n\t\t\t\t\t\tDtTrade: *NewDateGMT(2017, 2, 3, 0, 0, 0, 0),\n\t\t\t\t\t},\n\t\t\t\t\tSecID: SecurityID{\n\t\t\t\t\t\tUniqueID:     \"78462F103\",\n\t\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t\t},\n\t\t\t\t\tUnits:       units1,\n\t\t\t\t\tUnitPrice:   unitprice1,\n\t\t\t\t\tTotal:       total1,\n\t\t\t\t\tSubAcctSec:  SubAcctTypeCash,\n\t\t\t\t\tSubAcctFund: SubAcctTypeCash,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tvar actual InvTranList\n\terr := xml.Unmarshal([]byte(input), &actual)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error unmarshalling InvTranList: %s\\n\", err)\n\t}\n\tcheckEqual(t, \"InvTranList\", reflect.ValueOf(&expected), reflect.ValueOf(&actual))\n}\n\nfunc TestUnmarshalPositionList(t *testing.T) {\n\tinput := `<INVPOSLIST>\n\t<POSOTHER>\n\t\t<INVPOS>\n\t\t\t<SECID>\n\t\t\t\t<UNIQUEID>78462F103</UNIQUEID>\n\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t</SECID>\n\t\t\t<HELDINACCT>CASH</HELDINACCT>\n\t\t\t<POSTYPE>LONG</POSTYPE>\n\t\t\t<UNITS>1</UNITS>\n\t\t\t<UNITPRICE>3</UNITPRICE>\n\t\t\t<MKTVAL>300</MKTVAL>\n\t\t\t<DTPRICEASOF>20170331160000</DTPRICEASOF>\n\t\t</INVPOS>\n\t</POSOTHER>\n\t<POSSTOCK>\n\t\t<INVPOS>\n\t\t\t<SECID>\n\t\t\t\t<UNIQUEID>78462F103</UNIQUEID>\n\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t</SECID>\n\t\t\t<HELDINACCT>CASH</HELDINACCT>\n\t\t\t<POSTYPE>SHORT</POSTYPE>\n\t\t\t<UNITS>200</UNITS>\n\t\t\t<UNITPRICE>235.74</UNITPRICE>\n\t\t\t<MKTVAL>47148.00</MKTVAL>\n\t\t\t<DTPRICEASOF>20170331160000</DTPRICEASOF>\n\t\t\t<MEMO>Price as of previous close</MEMO>\n\t\t</INVPOS>\n\t\t<REINVDIV>Y</REINVDIV>\n\t</POSSTOCK>\n\t<POSDEBT>\n\t\t<INVPOS>\n\t\t\t<SECID>\n\t\t\t\t<UNIQUEID>129887339</UNIQUEID>\n\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t</SECID>\n\t\t\t<HELDINACCT>CASH</HELDINACCT>\n\t\t\t<POSTYPE>LONG</POSTYPE>\n\t\t\t<UNITS>1</UNITS>\n\t\t\t<UNITPRICE>3</UNITPRICE>\n\t\t\t<MKTVAL>300</MKTVAL>\n\t\t\t<DTPRICEASOF>20170331160000</DTPRICEASOF>\n\t\t</INVPOS>\n\t</POSDEBT>\n\t<POSOPT>\n\t\t<INVPOS>\n\t\t\t<SECID>\n\t\t\t\t<UNIQUEID>129887339</UNIQUEID>\n\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t</SECID>\n\t\t\t<HELDINACCT>CASH</HELDINACCT>\n\t\t\t<POSTYPE>LONG</POSTYPE>\n\t\t\t<UNITS>1</UNITS>\n\t\t\t<UNITPRICE>3</UNITPRICE>\n\t\t\t<MKTVAL>300</MKTVAL>\n\t\t\t<DTPRICEASOF>20170331160000</DTPRICEASOF>\n\t\t</INVPOS>\n\t</POSOPT>\n\t<POSMF>\n\t\t<INVPOS>\n\t\t\t<SECID>\n\t\t\t\t<UNIQUEID>78462F103</UNIQUEID>\n\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t</SECID>\n\t\t\t<HELDINACCT>CASH</HELDINACCT>\n\t\t\t<POSTYPE>LONG</POSTYPE>\n\t\t\t<UNITS>200</UNITS>\n\t\t\t<UNITPRICE>235.74</UNITPRICE>\n\t\t\t<MKTVAL>47148.00</MKTVAL>\n\t\t\t<DTPRICEASOF>20170331160000</DTPRICEASOF>\n\t\t\t<MEMO>Price as of previous close</MEMO>\n\t\t</INVPOS>\n\t\t<REINVDIV>Y</REINVDIV>\n\t\t<REINVCG>N</REINVCG>\n\t</POSMF>\n</INVPOSLIST>`\n\n\tvar posunits1, posunitprice1, posmktval1, posunits2, posunitprice2, posmktval2 Amount\n\tposunits1.SetFrac64(200, 1)\n\tposunitprice1.SetFrac64(23574, 100)\n\tposmktval1.SetFrac64(47148, 1)\n\tposunits2.SetFrac64(1, 1)\n\tposunitprice2.SetFrac64(3, 1)\n\tposmktval2.SetFrac64(300, 1)\n\n\texpected := PositionList{\n\t\tOtherPosition{\n\t\t\tInvPos: InvPosition{\n\t\t\t\tSecID: SecurityID{\n\t\t\t\t\tUniqueID:     \"78462F103\",\n\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t},\n\t\t\t\tHeldInAcct:  SubAcctTypeCash,\n\t\t\t\tPosType:     PosTypeLong,\n\t\t\t\tUnits:       posunits2,\n\t\t\t\tUnitPrice:   posunitprice2,\n\t\t\t\tMktVal:      posmktval2,\n\t\t\t\tDtPriceAsOf: *NewDateGMT(2017, 3, 31, 16, 0, 0, 0),\n\t\t\t},\n\t\t},\n\t\tStockPosition{\n\t\t\tInvPos: InvPosition{\n\t\t\t\tSecID: SecurityID{\n\t\t\t\t\tUniqueID:     \"78462F103\",\n\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t},\n\t\t\t\tHeldInAcct:  SubAcctTypeCash,\n\t\t\t\tPosType:     PosTypeShort,\n\t\t\t\tUnits:       posunits1,\n\t\t\t\tUnitPrice:   posunitprice1,\n\t\t\t\tMktVal:      posmktval1,\n\t\t\t\tDtPriceAsOf: *NewDateGMT(2017, 3, 31, 16, 0, 0, 0),\n\t\t\t\tMemo:        \"Price as of previous close\",\n\t\t\t},\n\t\t\tReinvDiv: true,\n\t\t},\n\t\tDebtPosition{\n\t\t\tInvPos: InvPosition{\n\t\t\t\tSecID: SecurityID{\n\t\t\t\t\tUniqueID:     \"129887339\",\n\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t},\n\t\t\t\tHeldInAcct:  SubAcctTypeCash,\n\t\t\t\tPosType:     PosTypeLong,\n\t\t\t\tUnits:       posunits2,\n\t\t\t\tUnitPrice:   posunitprice2,\n\t\t\t\tMktVal:      posmktval2,\n\t\t\t\tDtPriceAsOf: *NewDateGMT(2017, 3, 31, 16, 0, 0, 0),\n\t\t\t},\n\t\t},\n\t\tOptPosition{\n\t\t\tInvPos: InvPosition{\n\t\t\t\tSecID: SecurityID{\n\t\t\t\t\tUniqueID:     \"129887339\",\n\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t},\n\t\t\t\tHeldInAcct:  SubAcctTypeCash,\n\t\t\t\tPosType:     PosTypeLong,\n\t\t\t\tUnits:       posunits2,\n\t\t\t\tUnitPrice:   posunitprice2,\n\t\t\t\tMktVal:      posmktval2,\n\t\t\t\tDtPriceAsOf: *NewDateGMT(2017, 3, 31, 16, 0, 0, 0),\n\t\t\t},\n\t\t},\n\t\tMFPosition{\n\t\t\tInvPos: InvPosition{\n\t\t\t\tSecID: SecurityID{\n\t\t\t\t\tUniqueID:     \"78462F103\",\n\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t},\n\t\t\t\tHeldInAcct:  SubAcctTypeCash,\n\t\t\t\tPosType:     PosTypeLong,\n\t\t\t\tUnits:       posunits1,\n\t\t\t\tUnitPrice:   posunitprice1,\n\t\t\t\tMktVal:      posmktval1,\n\t\t\t\tDtPriceAsOf: *NewDateGMT(2017, 3, 31, 16, 0, 0, 0),\n\t\t\t\tMemo:        \"Price as of previous close\",\n\t\t\t},\n\t\t\tReinvDiv: true,\n\t\t\tReinvCG:  false,\n\t\t},\n\t}\n\n\tvar actual PositionList\n\terr := xml.Unmarshal([]byte(input), &actual)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error unmarshalling PositionList: %s\\n\", err)\n\t}\n\tcheckEqual(t, \"PositionList\", reflect.ValueOf(&expected), reflect.ValueOf(&actual))\n}\n\nfunc TestUnmarshalOOList(t *testing.T) {\n\tinput := `<INVOOLIST>\n\t<OOBUYDEBT>\n\t\t<OO>\n\t\t\t<FITID>76464632</FITID>\n\t\t\t<SECID>\n\t\t\t\t<UNIQUEID>922908645</UNIQUEID>\n\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t</SECID>\n\t\t\t<DTPLACED>20170310124445</DTPLACED>\n\t\t\t<UNITS>10</UNITS>\n\t\t\t<SUBACCT>CASH</SUBACCT>\n\t\t\t<DURATION>DAY</DURATION>\n\t\t\t<RESTRICTION>NONE</RESTRICTION>\n\t\t</OO>\n\t\t<AUCTION>Y</AUCTION>\n\t</OOBUYDEBT>\n\t<OOBUYMF>\n\t\t<OO>\n\t\t\t<FITID>76464632</FITID>\n\t\t\t<SECID>\n\t\t\t\t<UNIQUEID>922908645</UNIQUEID>\n\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t</SECID>\n\t\t\t<DTPLACED>20170310124445</DTPLACED>\n\t\t\t<UNITS>10</UNITS>\n\t\t\t<SUBACCT>CASH</SUBACCT>\n\t\t\t<DURATION>GOODTILCANCEL</DURATION>\n\t\t\t<RESTRICTION>NONE</RESTRICTION>\n\t\t\t<LIMITPRICE>168.50</LIMITPRICE>\n\t\t</OO>\n\t\t<BUYTYPE>BUY</BUYTYPE>\n\t\t<UNITTYPE>SHARES</UNITTYPE>\n\t</OOBUYMF>\n\t<OOBUYOPT>\n\t\t<OO>\n\t\t\t<FITID>999387423</FITID>\n\t\t\t<SECID>\n\t\t\t\t<UNIQUEID>899422348</UNIQUEID>\n\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t</SECID>\n\t\t\t<DTPLACED>20170324031900</DTPLACED>\n\t\t\t<UNITS>25</UNITS>\n\t\t\t<SUBACCT>CASH</SUBACCT>\n\t\t\t<DURATION>GOODTILCANCEL</DURATION>\n\t\t\t<RESTRICTION>ALLORNONE</RESTRICTION>\n\t\t\t<LIMITPRICE>19.75</LIMITPRICE>\n\t\t</OO>\n\t\t<OPTBUYTYPE>BUYTOCLOSE</OPTBUYTYPE>\n\t</OOBUYOPT>\n\t<OOBUYSTOCK>\n\t\t<OO>\n\t\t\t<FITID>999387423</FITID>\n\t\t\t<SECID>\n\t\t\t\t<UNIQUEID>899422348</UNIQUEID>\n\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t</SECID>\n\t\t\t<DTPLACED>20170324031900</DTPLACED>\n\t\t\t<UNITS>25</UNITS>\n\t\t\t<SUBACCT>CASH</SUBACCT>\n\t\t\t<DURATION>GOODTILCANCEL</DURATION>\n\t\t\t<RESTRICTION>ALLORNONE</RESTRICTION>\n\t\t\t<LIMITPRICE>19.75</LIMITPRICE>\n\t\t</OO>\n\t\t<BUYTYPE>BUY</BUYTYPE>\n\t</OOBUYSTOCK>\n\t<OOBUYOTHER>\n\t\t<OO>\n\t\t\t<FITID>999387423</FITID>\n\t\t\t<SECID>\n\t\t\t\t<UNIQUEID>899422348</UNIQUEID>\n\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t</SECID>\n\t\t\t<DTPLACED>20170324031900</DTPLACED>\n\t\t\t<UNITS>25</UNITS>\n\t\t\t<SUBACCT>CASH</SUBACCT>\n\t\t\t<DURATION>GOODTILCANCEL</DURATION>\n\t\t\t<RESTRICTION>ALLORNONE</RESTRICTION>\n\t\t\t<LIMITPRICE>19.75</LIMITPRICE>\n\t\t</OO>\n\t\t<UNITTYPE>CURRENCY</UNITTYPE>\n\t</OOBUYOTHER>\n\t<OOSELLDEBT>\n\t\t<OO>\n\t\t\t<FITID>999387423</FITID>\n\t\t\t<SECID>\n\t\t\t\t<UNIQUEID>899422348</UNIQUEID>\n\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t</SECID>\n\t\t\t<DTPLACED>20170324031900</DTPLACED>\n\t\t\t<UNITS>25</UNITS>\n\t\t\t<SUBACCT>CASH</SUBACCT>\n\t\t\t<DURATION>GOODTILCANCEL</DURATION>\n\t\t\t<RESTRICTION>ALLORNONE</RESTRICTION>\n\t\t</OO>\n\t</OOSELLDEBT>\n\t<OOSELLMF>\n\t\t<OO>\n\t\t\t<FITID>999387423</FITID>\n\t\t\t<SECID>\n\t\t\t\t<UNIQUEID>899422348</UNIQUEID>\n\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t</SECID>\n\t\t\t<DTPLACED>20170324031900</DTPLACED>\n\t\t\t<UNITS>25</UNITS>\n\t\t\t<SUBACCT>CASH</SUBACCT>\n\t\t\t<DURATION>GOODTILCANCEL</DURATION>\n\t\t\t<RESTRICTION>ALLORNONE</RESTRICTION>\n\t\t</OO>\n\t\t<SELLTYPE>SELLSHORT</SELLTYPE>\n\t\t<UNITTYPE>SHARES\n\t\t</UNITTYPE>\n\t\t<SELLALL>Y</SELLALL>\n\t</OOSELLMF>\n\t<OOSELLOPT>\n\t\t<OO>\n\t\t\t<FITID>999387423</FITID>\n\t\t\t<SECID>\n\t\t\t\t<UNIQUEID>899422348</UNIQUEID>\n\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t</SECID>\n\t\t\t<DTPLACED>20170324031900</DTPLACED>\n\t\t\t<UNITS>25</UNITS>\n\t\t\t<SUBACCT>CASH</SUBACCT>\n\t\t\t<DURATION>GOODTILCANCEL</DURATION>\n\t\t\t<RESTRICTION>ALLORNONE</RESTRICTION>\n\t\t</OO>\n\t\t<OPTSELLTYPE>SELLTOOPEN</OPTSELLTYPE>\n\t</OOSELLOPT>\n\t<OOSELLOTHER>\n\t\t<OO>\n\t\t\t<FITID>999387423</FITID>\n\t\t\t<SECID>\n\t\t\t\t<UNIQUEID>899422348</UNIQUEID>\n\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t</SECID>\n\t\t\t<DTPLACED>20170324031900</DTPLACED>\n\t\t\t<UNITS>25</UNITS>\n\t\t\t<SUBACCT>CASH</SUBACCT>\n\t\t\t<DURATION>GOODTILCANCEL</DURATION>\n\t\t\t<RESTRICTION>ALLORNONE</RESTRICTION>\n\t\t</OO>\n\t\t<UNITTYPE>SHARES</UNITTYPE>\n\t</OOSELLOTHER>\n\t<OOSELLSTOCK>\n\t\t<OO>\n\t\t\t<FITID>999387423</FITID>\n\t\t\t<SECID>\n\t\t\t\t<UNIQUEID>899422348</UNIQUEID>\n\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t</SECID>\n\t\t\t<DTPLACED>20170324031900</DTPLACED>\n\t\t\t<UNITS>25</UNITS>\n\t\t\t<SUBACCT>CASH</SUBACCT>\n\t\t\t<DURATION>GOODTILCANCEL</DURATION>\n\t\t\t<RESTRICTION>ALLORNONE</RESTRICTION>\n\t\t</OO>\n\t\t<SELLTYPE>SELL</SELLTYPE>\n\t</OOSELLSTOCK>\n\t<SWITCHMF>\n\t\t<OO>\n\t\t\t<FITID>999387423</FITID>\n\t\t\t<SECID>\n\t\t\t\t<UNIQUEID>899422348</UNIQUEID>\n\t\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t\t</SECID>\n\t\t\t<DTPLACED>20170324031900</DTPLACED>\n\t\t\t<UNITS>25</UNITS>\n\t\t\t<SUBACCT>CASH</SUBACCT>\n\t\t\t<DURATION>GOODTILCANCEL</DURATION>\n\t\t\t<RESTRICTION>ALLORNONE</RESTRICTION>\n\t\t</OO>\n\t\t<SECID>\n\t\t\t<UNIQUEID>899422389</UNIQUEID>\n\t\t\t<UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n\t\t</SECID>\n\t\t<UNITTYPE>CURRENCY</UNITTYPE>\n\t\t<SWITCHALL>N</SWITCHALL>\n\t</SWITCHMF>\n</INVOOLIST>`\n\n\tvar oounits1, oolimitprice1, oounits2, oolimitprice2 Amount\n\toounits1.SetFrac64(10, 1)\n\toolimitprice1.SetFrac64(16850, 100)\n\toounits2.SetFrac64(25, 1)\n\toolimitprice2.SetFrac64(1975, 100)\n\n\texpected := OOList{\n\t\tOOBuyDebt{\n\t\t\tOO: OO{\n\t\t\t\tFiTID: \"76464632\",\n\t\t\t\tSecID: SecurityID{\n\t\t\t\t\tUniqueID:     \"922908645\",\n\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t},\n\t\t\t\tDtPlaced:    *NewDateGMT(2017, 3, 10, 12, 44, 45, 0),\n\t\t\t\tUnits:       oounits1,\n\t\t\t\tSubAcct:     SubAcctTypeCash,\n\t\t\t\tDuration:    DurationDay,\n\t\t\t\tRestriction: RestrictionNone,\n\t\t\t},\n\t\t\tAuction: true,\n\t\t},\n\t\tOOBuyMF{\n\t\t\tOO: OO{\n\t\t\t\tFiTID: \"76464632\",\n\t\t\t\tSecID: SecurityID{\n\t\t\t\t\tUniqueID:     \"922908645\",\n\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t},\n\t\t\t\tDtPlaced:    *NewDateGMT(2017, 3, 10, 12, 44, 45, 0),\n\t\t\t\tUnits:       oounits1,\n\t\t\t\tSubAcct:     SubAcctTypeCash,\n\t\t\t\tDuration:    DurationGoodTilCancel,\n\t\t\t\tRestriction: RestrictionNone,\n\t\t\t\tLimitPrice:  oolimitprice1,\n\t\t\t},\n\t\t\tBuyType:  BuyTypeBuy,\n\t\t\tUnitType: UnitTypeShares,\n\t\t},\n\t\tOOBuyOpt{\n\t\t\tOO: OO{\n\t\t\t\tFiTID: \"999387423\",\n\t\t\t\tSecID: SecurityID{\n\t\t\t\t\tUniqueID:     \"899422348\",\n\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t},\n\t\t\t\tDtPlaced:    *NewDateGMT(2017, 3, 24, 3, 19, 0, 0),\n\t\t\t\tUnits:       oounits2,\n\t\t\t\tSubAcct:     SubAcctTypeCash,\n\t\t\t\tDuration:    DurationGoodTilCancel,\n\t\t\t\tRestriction: RestrictionAllOrNone,\n\t\t\t\tLimitPrice:  oolimitprice2,\n\t\t\t},\n\t\t\tOptBuyType: OptBuyTypeBuyToClose,\n\t\t},\n\t\tOOBuyStock{\n\t\t\tOO: OO{\n\t\t\t\tFiTID: \"999387423\",\n\t\t\t\tSecID: SecurityID{\n\t\t\t\t\tUniqueID:     \"899422348\",\n\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t},\n\t\t\t\tDtPlaced:    *NewDateGMT(2017, 3, 24, 3, 19, 0, 0),\n\t\t\t\tUnits:       oounits2,\n\t\t\t\tSubAcct:     SubAcctTypeCash,\n\t\t\t\tDuration:    DurationGoodTilCancel,\n\t\t\t\tRestriction: RestrictionAllOrNone,\n\t\t\t\tLimitPrice:  oolimitprice2,\n\t\t\t},\n\t\t\tBuyType: BuyTypeBuy,\n\t\t},\n\t\tOOBuyOther{\n\t\t\tOO: OO{\n\t\t\t\tFiTID: \"999387423\",\n\t\t\t\tSecID: SecurityID{\n\t\t\t\t\tUniqueID:     \"899422348\",\n\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t},\n\t\t\t\tDtPlaced:    *NewDateGMT(2017, 3, 24, 3, 19, 0, 0),\n\t\t\t\tUnits:       oounits2,\n\t\t\t\tSubAcct:     SubAcctTypeCash,\n\t\t\t\tDuration:    DurationGoodTilCancel,\n\t\t\t\tRestriction: RestrictionAllOrNone,\n\t\t\t\tLimitPrice:  oolimitprice2,\n\t\t\t},\n\t\t\tUnitType: UnitTypeCurrency,\n\t\t},\n\t\tOOSellDebt{\n\t\t\tOO: OO{\n\t\t\t\tFiTID: \"999387423\",\n\t\t\t\tSecID: SecurityID{\n\t\t\t\t\tUniqueID:     \"899422348\",\n\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t},\n\t\t\t\tDtPlaced:    *NewDateGMT(2017, 3, 24, 3, 19, 0, 0),\n\t\t\t\tUnits:       oounits2,\n\t\t\t\tSubAcct:     SubAcctTypeCash,\n\t\t\t\tDuration:    DurationGoodTilCancel,\n\t\t\t\tRestriction: RestrictionAllOrNone,\n\t\t\t},\n\t\t},\n\t\tOOSellMF{\n\t\t\tOO: OO{\n\t\t\t\tFiTID: \"999387423\",\n\t\t\t\tSecID: SecurityID{\n\t\t\t\t\tUniqueID:     \"899422348\",\n\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t},\n\t\t\t\tDtPlaced:    *NewDateGMT(2017, 3, 24, 3, 19, 0, 0),\n\t\t\t\tUnits:       oounits2,\n\t\t\t\tSubAcct:     SubAcctTypeCash,\n\t\t\t\tDuration:    DurationGoodTilCancel,\n\t\t\t\tRestriction: RestrictionAllOrNone,\n\t\t\t},\n\t\t\tSellType: SellTypeSellShort,\n\t\t\tUnitType: UnitTypeShares,\n\t\t\tSellAll:  true,\n\t\t},\n\t\tOOSellOpt{\n\t\t\tOO: OO{\n\t\t\t\tFiTID: \"999387423\",\n\t\t\t\tSecID: SecurityID{\n\t\t\t\t\tUniqueID:     \"899422348\",\n\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t},\n\t\t\t\tDtPlaced:    *NewDateGMT(2017, 3, 24, 3, 19, 0, 0),\n\t\t\t\tUnits:       oounits2,\n\t\t\t\tSubAcct:     SubAcctTypeCash,\n\t\t\t\tDuration:    DurationGoodTilCancel,\n\t\t\t\tRestriction: RestrictionAllOrNone,\n\t\t\t},\n\t\t\tOptSellType: OptSellTypeSellToOpen,\n\t\t},\n\t\tOOSellOther{\n\t\t\tOO: OO{\n\t\t\t\tFiTID: \"999387423\",\n\t\t\t\tSecID: SecurityID{\n\t\t\t\t\tUniqueID:     \"899422348\",\n\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t},\n\t\t\t\tDtPlaced:    *NewDateGMT(2017, 3, 24, 3, 19, 0, 0),\n\t\t\t\tUnits:       oounits2,\n\t\t\t\tSubAcct:     SubAcctTypeCash,\n\t\t\t\tDuration:    DurationGoodTilCancel,\n\t\t\t\tRestriction: RestrictionAllOrNone,\n\t\t\t},\n\t\t\tUnitType: UnitTypeShares,\n\t\t},\n\t\tOOSellStock{\n\t\t\tOO: OO{\n\t\t\t\tFiTID: \"999387423\",\n\t\t\t\tSecID: SecurityID{\n\t\t\t\t\tUniqueID:     \"899422348\",\n\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t},\n\t\t\t\tDtPlaced:    *NewDateGMT(2017, 3, 24, 3, 19, 0, 0),\n\t\t\t\tUnits:       oounits2,\n\t\t\t\tSubAcct:     SubAcctTypeCash,\n\t\t\t\tDuration:    DurationGoodTilCancel,\n\t\t\t\tRestriction: RestrictionAllOrNone,\n\t\t\t},\n\t\t\tSellType: SellTypeSell,\n\t\t},\n\t\tOOSwitchMF{\n\t\t\tOO: OO{\n\t\t\t\tFiTID: \"999387423\",\n\t\t\t\tSecID: SecurityID{\n\t\t\t\t\tUniqueID:     \"899422348\",\n\t\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t\t},\n\t\t\t\tDtPlaced:    *NewDateGMT(2017, 3, 24, 3, 19, 0, 0),\n\t\t\t\tUnits:       oounits2,\n\t\t\t\tSubAcct:     SubAcctTypeCash,\n\t\t\t\tDuration:    DurationGoodTilCancel,\n\t\t\t\tRestriction: RestrictionAllOrNone,\n\t\t\t},\n\t\t\tSecID: SecurityID{\n\t\t\t\tUniqueID:     \"899422389\",\n\t\t\t\tUniqueIDType: \"CUSIP\",\n\t\t\t},\n\t\t\tUnitType:  UnitTypeCurrency,\n\t\t\tSwitchAll: false,\n\t\t},\n\t}\n\n\tvar actual OOList\n\terr := xml.Unmarshal([]byte(input), &actual)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error unmarshalling OOList: %s\\n\", err)\n\t}\n\tcheckEqual(t, \"OOList\", reflect.ValueOf(&expected), reflect.ValueOf(&actual))\n}\n\nfunc TestSecurityInfo(t *testing.T) {\n\tsecInfo := SecInfo{\n\t\tTicker: \"ABC\",\n\t}\n\ttests := []Security{\n\t\tDebtInfo{SecInfo: secInfo},\n\t\tMFInfo{SecInfo: secInfo},\n\t\tOptInfo{SecInfo: secInfo},\n\t\tOtherInfo{SecInfo: secInfo},\n\t\tStockInfo{SecInfo: secInfo},\n\t}\n\n\tfor _, tc := range tests {\n\t\tt.Run(tc.SecurityType(), func(t *testing.T) {\n\t\t\tinfo := tc.SecurityInfo()\n\t\t\tif info.Ticker != secInfo.Ticker {\n\t\t\t\tt.Errorf(\"got %v, want %v\", info, secInfo)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestInvPosition(t *testing.T) {\n\tinvPos := InvPosition{\n\t\tMemo: \"stuff\",\n\t}\n\ttests := []Position{\n\t\tDebtPosition{InvPos: invPos},\n\t\tMFPosition{InvPos: invPos},\n\t\tOptPosition{InvPos: invPos},\n\t\tOtherPosition{InvPos: invPos},\n\t\tStockPosition{InvPos: invPos},\n\t}\n\n\tfor _, tc := range tests {\n\t\tt.Run(tc.PositionType(), func(t *testing.T) {\n\t\t\tpos := tc.InvPosition()\n\t\t\tif pos.Memo != invPos.Memo {\n\t\t\t\tt.Errorf(\"got %v, want %v\", pos, invPos)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestInvTransaction(t *testing.T) {\n\tinvTran := InvTran{\n\t\tMemo: \"stuff\",\n\t}\n\ttests := []InvTransaction{\n\t\tBuyDebt{InvBuy: InvBuy{InvTran: invTran}},\n\t\tBuyMF{InvBuy: InvBuy{InvTran: invTran}},\n\t\tBuyOpt{InvBuy: InvBuy{InvTran: invTran}},\n\t\tBuyOther{InvBuy: InvBuy{InvTran: invTran}},\n\t\tBuyStock{InvBuy: InvBuy{InvTran: invTran}},\n\t\tClosureOpt{InvTran: invTran},\n\t\tIncome{InvTran: invTran},\n\t\tInvExpense{InvTran: invTran},\n\t\tJrnlFund{InvTran: invTran},\n\t\tJrnlSec{InvTran: invTran},\n\t\tMarginInterest{InvTran: invTran},\n\t\tReinvest{InvTran: invTran},\n\t\tRetOfCap{InvTran: invTran},\n\t\tSellDebt{InvSell: InvSell{InvTran: invTran}},\n\t\tSellMF{InvSell: InvSell{InvTran: invTran}},\n\t\tSellOpt{InvSell: InvSell{InvTran: invTran}},\n\t\tSellOther{InvSell: InvSell{InvTran: invTran}},\n\t\tSellStock{InvSell: InvSell{InvTran: invTran}},\n\t\tSplit{InvTran: invTran},\n\t\tTransfer{InvTran: invTran},\n\t}\n\n\tfor _, tc := range tests {\n\t\tt.Run(tc.TransactionType(), func(t *testing.T) {\n\t\t\ttran := tc.InvTransaction()\n\t\t\tif tran.Memo != invTran.Memo {\n\t\t\t\tt.Errorf(\"got %v, want %v\", tran, invTran)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "leaf_elements.go",
    "content": "package ofxgo\n\n// A list of all the leaf elements in OFX 1.0.3 (the last SGML version of the\n// spec). These are all the elements that are possibly left unclosed, and which\n// can have no children of their own. Fortunately these two sets of elements\n// are the same. We use this list when parsing to remove ambiguities about\n// element nesting.\n//\n// Generated using the following command with the 1.0.3 SPEC .dtd file:\n//   # sed -rn 's/^<!ELEMENT\\s+([A-Z0-9]+)\\s+-\\s+[oO]\\s+%.*TYPE\\s*>.*$/\\t\"\\1\",/p' *.dtd | sort\nvar ofxLeafElements = []string{\n\t\"ACCESSKEY\",\n\t\"ACCRDINT\",\n\t\"ACCTID\",\n\t\"ACCTKEY\",\n\t\"ACCTREQUIRED\",\n\t\"ACCTTYPE\",\n\t\"ADDR1\",\n\t\"ADDR2\",\n\t\"ADDR3\",\n\t\"ADJAMT\",\n\t\"ADJDATE\",\n\t\"ADJDESC\",\n\t\"ADJNO\",\n\t\"APPID\",\n\t\"APPVER\",\n\t\"ASSETCLASS\",\n\t\"AUCTION\",\n\t\"AUTHTOKEN\",\n\t\"AUTHTOKENFIRST\",\n\t\"AUTHTOKENINFOURL\",\n\t\"AUTHTOKENLABEL\",\n\t\"AVAILACCTS\",\n\t\"AVAILCASH\",\n\t\"AVGCOSTBASIS\",\n\t\"BALAMT\",\n\t\"BALCLOSE\",\n\t\"BALDNLD\",\n\t\"BALMIN\",\n\t\"BALOPEN\",\n\t\"BALTYPE\",\n\t\"BANKID\",\n\t\"BILLREFINFO\",\n\t\"BRANCHID\",\n\t\"BROKERID\",\n\t\"BUYPOWER\",\n\t\"BUYTYPE\",\n\t\"CALLPRICE\",\n\t\"CALLTYPE\",\n\t\"CANADDPAYEE\",\n\t\"CANBILLPAY\",\n\t\"CANCELWND\",\n\t\"CANEMAIL\",\n\t\"CANMODMDLS\",\n\t\"CANMODPMTS\",\n\t\"CANMODXFERS\",\n\t\"CANNOTIFY\",\n\t\"CANPENDING\",\n\t\"CANRECUR\",\n\t\"CANSCHED\",\n\t\"CANUSEDESC\",\n\t\"CANUSERANGE\",\n\t\"CASESEN\",\n\t\"CHARTYPE\",\n\t\"CHECKING\",\n\t\"CHECKNUM\",\n\t\"CHGPINFIRST\",\n\t\"CHGUSERINFO\",\n\t\"CHKANDDEB\",\n\t\"CHKERROR\",\n\t\"CHKNUMEND\",\n\t\"CHKNUMSTART\",\n\t\"CHKSTATUS\",\n\t\"CITY\",\n\t\"CLIENTACTREQ\",\n\t\"CLIENTROUTING\",\n\t\"CLIENTUID\",\n\t\"CLIENTUIDREQ\",\n\t\"CLOSINGAVAIL\",\n\t\"CLTCOOKIE\",\n\t\"CODE\",\n\t\"COMMISSION\",\n\t\"CONFMSG\",\n\t\"CORRECTACTION\",\n\t\"CORRECTFITID\",\n\t\"COUNTRY\",\n\t\"COUPONFREQ\",\n\t\"COUPONRT\",\n\t\"CREDITLIMIT\",\n\t\"CSPHONE\",\n\t\"CURDEF\",\n\t\"CURRATE\",\n\t\"CURSYM\",\n\t\"DATEBIRTH\",\n\t\"DAYPHONE\",\n\t\"DAYSTOPAY\",\n\t\"DAYSWITH\",\n\t\"DEBADJ\",\n\t\"DEBTCLASS\",\n\t\"DEBTTYPE\",\n\t\"DENOMINATOR\",\n\t\"DEPANDCREDIT\",\n\t\"DESC\",\n\t\"DFLTDAYSTOPAY\",\n\t\"DIFFFIRSTPMT\",\n\t\"DIFFLASTPMT\",\n\t\"DOMXFERFEE\",\n\t\"DSCAMT\",\n\t\"DSCDATE\",\n\t\"DSCDESC\",\n\t\"DSCRATE\",\n\t\"DTACCTUP\",\n\t\"DTASOF\",\n\t\"DTAUCTION\",\n\t\"DTAVAIL\",\n\t\"DTCALL\",\n\t\"DTCHANGED\",\n\t\"DTCLIENT\",\n\t\"DTCLOSE\",\n\t\"DTCOUPON\",\n\t\"DTCREATED\",\n\t\"DTDUE\",\n\t\"DTEND\",\n\t\"DTEXPIRE\",\n\t\"DTINFOCHG\",\n\t\"DTMAT\",\n\t\"DTNEXT\",\n\t\"DTOPEN\",\n\t\"DTPLACED\",\n\t\"DTPMTDUE\",\n\t\"DTPMTPRC\",\n\t\"DTPOSTED\",\n\t\"DTPOSTEND\",\n\t\"DTPOSTSTART\",\n\t\"DTPRICEASOF\",\n\t\"DTPROFUP\",\n\t\"DTPURCHASE\",\n\t\"DTSERVER\",\n\t\"DTSETTLE\",\n\t\"DTSTART\",\n\t\"DTTRADE\",\n\t\"DTUSER\",\n\t\"DTXFERPRC\",\n\t\"DTXFERPRJ\",\n\t\"DTYIELDASOF\",\n\t\"DURATION\",\n\t\"EMAIL\",\n\t\"EVEPHONE\",\n\t\"EXTDPMTCHK\",\n\t\"EXTDPMTFOR\",\n\t\"FAXPHONE\",\n\t\"FEE\",\n\t\"FEEMSG\",\n\t\"FEES\",\n\t\"FIASSETCLASS\",\n\t\"FICERTID\",\n\t\"FID\",\n\t\"FIID\",\n\t\"FINALAMT\",\n\t\"FINAME\",\n\t\"FINCHG\",\n\t\"FIRSTNAME\",\n\t\"FITID\",\n\t\"FRACCASH\",\n\t\"FREQ\",\n\t\"FROM\",\n\t\"GAIN\",\n\t\"GENUSERKEY\",\n\t\"GETMIMESUP\",\n\t\"HASEXTDPMT\",\n\t\"HELDINACCT\",\n\t\"IDSCOPE\",\n\t\"INCBAL\",\n\t\"INCIMAGES\",\n\t\"INCLUDE\",\n\t\"INCOMETYPE\",\n\t\"INCOO\",\n\t\"INITIALAMT\",\n\t\"INTLXFERFEE\",\n\t\"INVACCTTYPE\",\n\t\"INVALIDACCTTYPE\",\n\t\"INVDATE\",\n\t\"INVDESC\",\n\t\"INVNO\",\n\t\"INVPAIDAMT\",\n\t\"INVTOTALAMT\",\n\t\"LANGUAGE\",\n\t\"LASTNAME\",\n\t\"LIMITPRICE\",\n\t\"LITMAMT\",\n\t\"LITMDESC\",\n\t\"LOAD\",\n\t\"LOSTSYNC\",\n\t\"MAILSUP\",\n\t\"MARGINBALANCE\",\n\t\"MARKDOWN\",\n\t\"MARKUP\",\n\t\"MAX\",\n\t\"MEMO\",\n\t\"MESSAGE\",\n\t\"MFACHALLENGEFIRST\",\n\t\"MFACHALLENGESUPT\",\n\t\"MFAPHRASEA\",\n\t\"MFAPHRASEID\",\n\t\"MFAPHRASELABEL\",\n\t\"MFTYPE\",\n\t\"MIDDLENAME\",\n\t\"MIN\",\n\t\"MINPMTDUE\",\n\t\"MINUNITS\",\n\t\"MKTGINFO\",\n\t\"MKTVAL\",\n\t\"MODELWND\",\n\t\"MODPENDING\",\n\t\"NAME\",\n\t\"NEWUNITS\",\n\t\"NEWUSERPASS\",\n\t\"NINSTS\",\n\t\"NONCE\",\n\t\"NUMERATOR\",\n\t\"OFXSEC\",\n\t\"OLDUNITS\",\n\t\"OODNLD\",\n\t\"OPTACTION\",\n\t\"OPTBUYTYPE\",\n\t\"OPTIONLEVEL\",\n\t\"OPTSELLTYPE\",\n\t\"OPTTYPE\",\n\t\"ORG\",\n\t\"PARVALUE\",\n\t\"PAYACCT\",\n\t\"PAYANDCREDIT\",\n\t\"PAYEEID\",\n\t\"PAYEELSTID\",\n\t\"PAYINSTRUCT\",\n\t\"PERCENT\",\n\t\"PHONE\",\n\t\"PINCH\",\n\t\"PMTBYADDR\",\n\t\"PMTBYPAYEEID\",\n\t\"PMTBYXFER\",\n\t\"PMTPRCCODE\",\n\t\"POSDNLD\",\n\t\"POSTALCODE\",\n\t\"POSTPROCWND\",\n\t\"POSTYPE\",\n\t\"PROCDAYSOFF\",\n\t\"PROCENDTM\",\n\t\"PURANDADV\",\n\t\"RATING\",\n\t\"RECSRVRTID\",\n\t\"REFNUM\",\n\t\"REFRESH\",\n\t\"REFRESHSUPT\",\n\t\"REINVCG\",\n\t\"REINVDIV\",\n\t\"REJECTIFMISSING\",\n\t\"RELFITID\",\n\t\"RELTYPE\",\n\t\"RESPFILEER\",\n\t\"RESTRICTION\",\n\t\"SECLISTRQDNLD\",\n\t\"SECNAME\",\n\t\"SECURED\",\n\t\"SECURITYNAME\",\n\t\"SELLALL\",\n\t\"SELLREASON\",\n\t\"SELLTYPE\",\n\t\"SESSCOOKIE\",\n\t\"SEVERITY\",\n\t\"SHORTBALANCE\",\n\t\"SHPERCTRCT\",\n\t\"SIC\",\n\t\"SIGNONREALM\",\n\t\"SPACES\",\n\t\"SPECIAL\",\n\t\"SPNAME\",\n\t\"SRVRTID\",\n\t\"STATE\",\n\t\"STOCKTYPE\",\n\t\"STOPPRICE\",\n\t\"STPCHKFEE\",\n\t\"STRIKEPRICE\",\n\t\"STSVIAMODS\",\n\t\"SUBACCT\",\n\t\"SUBACCTFROM\",\n\t\"SUBACCTSEC\",\n\t\"SUBACCTTO\",\n\t\"SUBJECT\",\n\t\"SUPTXDL\",\n\t\"SVC\",\n\t\"SVCSTATUS\",\n\t\"SWITCHALL\",\n\t\"SYNCMODE\",\n\t\"TAN\",\n\t\"TAXES\",\n\t\"TAXEXEMPT\",\n\t\"TAXID\",\n\t\"TEMPPASS\",\n\t\"TFERACTION\",\n\t\"TICKER\",\n\t\"TO\",\n\t\"TOKEN\",\n\t\"TOKENONLY\",\n\t\"TOTAL\",\n\t\"TOTALFEES\",\n\t\"TOTALINT\",\n\t\"TRANDNLD\",\n\t\"TRANSPSEC\",\n\t\"TRNAMT\",\n\t\"TRNTYPE\",\n\t\"TRNUID\",\n\t\"TSKEYEXPIRE\",\n\t\"TSPHONE\",\n\t\"TYPEDESC\",\n\t\"UNIQUEID\",\n\t\"UNIQUEIDTYPE\",\n\t\"UNITPRICE\",\n\t\"UNITS\",\n\t\"UNITSSTREET\",\n\t\"UNITSUSER\",\n\t\"UNITTYPE\",\n\t\"URL\",\n\t\"USEHTML\",\n\t\"USERCRED1\",\n\t\"USERCRED1LABEL\",\n\t\"USERCRED2\",\n\t\"USERCRED2LABEL\",\n\t\"USERID\",\n\t\"USERKEY\",\n\t\"USERPASS\",\n\t\"USPRODUCTTYPE\",\n\t\"VALUE\",\n\t\"VER\",\n\t\"WITHHOLDING\",\n\t\"XFERDAYSWITH\",\n\t\"XFERDEST\",\n\t\"XFERDFLTDAYSTOPAY\",\n\t\"XFERPRCCODE\",\n\t\"XFERSRC\",\n\t\"YIELD\",\n\t\"YIELDTOCALL\",\n\t\"YIELDTOMAT\",\n}\n"
  },
  {
    "path": "profile.go",
    "content": "package ofxgo\n\nimport (\n\t\"errors\"\n\t\"github.com/aclindsa/xml\"\n\t\"strings\"\n)\n\n// ProfileRequest represents a request for a server to provide a profile of its\n// capabilities (which message sets and versions it supports, how to access\n// them, which languages and which types of synchronization they support, etc.)\ntype ProfileRequest struct {\n\tXMLName   xml.Name `xml:\"PROFTRNRQ\"`\n\tTrnUID    UID      `xml:\"TRNUID\"`\n\tCltCookie String   `xml:\"CLTCOOKIE,omitempty\"`\n\tTAN       String   `xml:\"TAN,omitempty\"` // Transaction authorization number\n\t// TODO `xml:\"OFXEXTENSION,omitempty\"`\n\tClientRouting String `xml:\"PROFRQ>CLIENTROUTING\"` // Forced to NONE\n\tDtProfUp      Date   `xml:\"PROFRQ>DTPROFUP\"`      // Date and time client last received a profile update\n}\n\n// Name returns the name of the top-level transaction XML/SGML element\nfunc (r *ProfileRequest) Name() string {\n\treturn \"PROFTRNRQ\"\n}\n\n// Valid returns (true, nil) if this struct would be valid OFX if marshalled\n// into XML/SGML\nfunc (r *ProfileRequest) Valid(version ofxVersion) (bool, error) {\n\tif ok, err := r.TrnUID.Valid(); !ok {\n\t\treturn false, err\n\t}\n\t// TODO implement\n\tr.ClientRouting = \"NONE\"\n\treturn true, nil\n}\n\n// Type returns which message set this message belongs to (which Request\n// element of type []Message it should appended to)\nfunc (r *ProfileRequest) Type() messageType {\n\treturn ProfRq\n}\n\n// SignonInfo provides the requirements to login to a single signon realm. A\n// signon realm consists of all MessageSets which can be accessed using one set\n// of login credentials. Most FI's only use one signon realm to make it easier\n// and less confusing for the user.\ntype SignonInfo struct {\n\tXMLName           xml.Name `xml:\"SIGNONINFO\"`\n\tSignonRealm       String   `xml:\"SIGNONREALM\"`              // The SignonRealm for which this SignonInfo provides information. This SignonInfo is valid for all MessageSets with SignonRealm fields matching this one\n\tMin               Int      `xml:\"MIN\"`                      // Minimum number of password characters\n\tMax               Int      `xml:\"MAX\"`                      // Maximum number of password characters\n\tCharType          charType `xml:\"CHARTYPE\"`                 // One of ALPHAONLY, NUMERICONLY, ALPHAORNUMERIC, ALPHAANDNUMERIC\n\tCaseSen           Boolean  `xml:\"CASESEN\"`                  // Password is case-sensitive?\n\tSpecial           Boolean  `xml:\"SPECIAL\"`                  // Special characters allowed?\n\tSpaces            Boolean  `xml:\"SPACES\"`                   // Spaces allowed?\n\tPinCh             Boolean  `xml:\"PINCH\"`                    // Pin change <PINCHRQ> requests allowed\n\tChgPinFirst       Boolean  `xml:\"CHGPINFIRST\"`              // Server requires user to change password at first signon\n\tUserCred1Label    String   `xml:\"USERCRED1LABEL,omitempty\"` // Prompt for USERCRED1 (if this field is present, USERCRED1 is required)\n\tUserCred2Label    String   `xml:\"USERCRED2LABEL,omitempty\"` // Prompt for USERCRED2 (if this field is present, USERCRED2 is required)\n\tClientUIDReq      Boolean  `xml:\"CLIENTUIDREQ,omitempty\"`   // CLIENTUID required?\n\tAuthTokenFirst    Boolean  `xml:\"AUTHTOKENFIRST,omitempty\"` // Server requires AUTHTOKEN as part of first signon\n\tAuthTokenLabel    String   `xml:\"AUTHTOKENLABEL,omitempty\"`\n\tAuthTokenInfoURL  String   `xml:\"AUTHTOKENINFOURL,omitempty\"`\n\tMFAChallengeSupt  Boolean  `xml:\"MFACHALLENGESUPT,omitempty\"`  // Server supports MFACHALLENGE\n\tMFAChallengeFIRST Boolean  `xml:\"MFACHALLENGEFIRST,omitempty\"` // Server requires MFACHALLENGE to be sent with first signon\n\tAccessTokenReq    Boolean  `xml:\"ACCESSTOKENREQ,omitempty\"`    // Server requires ACCESSTOKEN to be sent with all requests except profile\n}\n\n// MessageSet represents one message set supported by an FI and its\n// capabilities\ntype MessageSet struct {\n\tXMLName     xml.Name // <xxxMSGSETVn>\n\tName        string   // <xxxMSGSETVn> (copy of XMLName.Local)\n\tVer         Int      `xml:\"MSGSETCORE>VER\"`                   // Message set version - should always match 'n' in <xxxMSGSETVn> of Name\n\tURL         String   `xml:\"MSGSETCORE>URL\"`                   // URL where messages in this set are to be set\n\tOfxSec      ofxSec   `xml:\"MSGSETCORE>OFXSEC\"`                // NONE or 'TYPE 1'\n\tTranspSec   Boolean  `xml:\"MSGSETCORE>TRANSPSEC\"`             // Transport-level security must be used\n\tSignonRealm String   `xml:\"MSGSETCORE>SIGNONREALM\"`           // Used to identify which SignonInfo to use for to this MessageSet\n\tLanguage    []String `xml:\"MSGSETCORE>LANGUAGE\"`              // List of supported languages\n\tSyncMode    syncMode `xml:\"MSGSETCORE>SYNCMODE\"`              // One of FULL, LITE\n\tRefreshSupt Boolean  `xml:\"MSGSETCORE>REFRESHSUPT,omitempty\"` // Y if server supports <REFRESH>Y within synchronizations. This option is irrelevant for full synchronization servers. Clients must ignore <REFRESHSUPT> (or its absence) if the profile also specifies <SYNCMODE>FULL. For lite synchronization, the default is N. Without <REFRESHSUPT>Y, lite synchronization servers are not required to support <REFRESH>Y requests\n\tRespFileER  Boolean  `xml:\"MSGSETCORE>RESPFILEER\"`            // server supports file-based error recovery\n\tSpName      String   `xml:\"MSGSETCORE>SPNAME\"`                // Name of service provider\n\t// TODO MessageSet-specific stuff?\n}\n\n// MessageSetList is a list of MessageSets (necessary because they must be\n// manually parsed)\ntype MessageSetList []MessageSet\n\n// UnmarshalXML handles unmarshalling a MessageSetList element from an XML string\nfunc (msl *MessageSetList) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tfor {\n\t\tvar msgset MessageSet\n\t\ttok, err := nextNonWhitespaceToken(d)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t} else if end, ok := tok.(xml.EndElement); ok && end.Name.Local == start.Name.Local {\n\t\t\t// If we found the end of our starting element, we're done parsing\n\t\t\treturn nil\n\t\t} else if _, ok := tok.(xml.StartElement); ok {\n\t\t\t// Found starting tag for <xxxMSGSET>. Get the next one (xxxMSGSETVn) and decode that struct\n\t\t\ttok, err := nextNonWhitespaceToken(d)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t} else if versionStart, ok := tok.(xml.StartElement); ok {\n\t\t\t\tif err := d.DecodeElement(&msgset, &versionStart); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn errors.New(\"Invalid MSGSETLIST formatting\")\n\t\t\t}\n\t\t\tmsgset.Name = msgset.XMLName.Local\n\n\t\t\t// Eat ending tags for <xxxMSGSET>\n\t\t\ttok, err = nextNonWhitespaceToken(d)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t} else if _, ok := tok.(xml.EndElement); !ok {\n\t\t\t\treturn errors.New(\"Invalid MSGSETLIST formatting\")\n\t\t\t}\n\t\t} else {\n\t\t\treturn errors.New(\"MSGSETLIST didn't find an opening xxxMSGSETVn element\")\n\t\t}\n\t\t*msl = MessageSetList(append(*(*[]MessageSet)(msl), msgset))\n\t}\n}\n\n// MarshalXML handles marshalling a MessageSetList element to an XML string\nfunc (msl *MessageSetList) MarshalXML(e *xml.Encoder, start xml.StartElement) error {\n\tmessageSetListElement := xml.StartElement{Name: xml.Name{Local: \"MSGSETLIST\"}}\n\tif err := e.EncodeToken(messageSetListElement); err != nil {\n\t\treturn err\n\t}\n\tfor _, messageset := range *msl {\n\t\tif !strings.HasSuffix(messageset.Name, \"V1\") {\n\t\t\treturn errors.New(\"Expected MessageSet.Name to end with \\\"V1\\\"\")\n\t\t}\n\t\tmessageSetName := strings.TrimSuffix(messageset.Name, \"V1\")\n\t\tmessageSetElement := xml.StartElement{Name: xml.Name{Local: messageSetName}}\n\t\tif err := e.EncodeToken(messageSetElement); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tstart := xml.StartElement{Name: xml.Name{Local: messageset.Name}}\n\t\tif err := e.EncodeElement(&messageset, start); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := e.EncodeToken(messageSetElement.End()); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := e.EncodeToken(messageSetListElement.End()); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// ProfileResponse contains a requested profile of the server's capabilities\n// (which message sets and versions it supports, how to access them, which\n// languages and which types of synchronization they support, etc.). Note that\n// if the server does not support ClientRouting=NONE (as we always send with\n// ProfileRequest), this may be an error)\ntype ProfileResponse struct {\n\tXMLName   xml.Name `xml:\"PROFTRNRS\"`\n\tTrnUID    UID      `xml:\"TRNUID\"`\n\tStatus    Status   `xml:\"STATUS\"`\n\tCltCookie String   `xml:\"CLTCOOKIE,omitempty\"`\n\t// TODO `xml:\"OFXEXTENSION,omitempty\"`\n\tMessageSetList MessageSetList `xml:\"PROFRS>MSGSETLIST\"`\n\tSignonInfoList []SignonInfo   `xml:\"PROFRS>SIGNONINFOLIST>SIGNONINFO\"`\n\tDtProfUp       Date           `xml:\"PROFRS>DTPROFUP\"`\n\tFiName         String         `xml:\"PROFRS>FINAME\"`\n\tAddr1          String         `xml:\"PROFRS>ADDR1\"`\n\tAddr2          String         `xml:\"PROFRS>ADDR2,omitempty\"`\n\tAddr3          String         `xml:\"PROFRS>ADDR3,omitempty\"`\n\tCity           String         `xml:\"PROFRS>CITY\"`\n\tState          String         `xml:\"PROFRS>STATE\"`\n\tPostalCode     String         `xml:\"PROFRS>POSTALCODE\"`\n\tCountry        String         `xml:\"PROFRS>COUNTRY\"`\n\tCsPhone        String         `xml:\"PROFRS>CSPHONE,omitempty\"`\n\tTsPhone        String         `xml:\"PROFRS>TSPHONE,omitempty\"`\n\tFaxPhone       String         `xml:\"PROFRS>FAXPHONE,omitempty\"`\n\tURL            String         `xml:\"PROFRS>URL,omitempty\"`\n\tEmail          String         `xml:\"PROFRS>EMAIL,omitempty\"`\n}\n\n// Name returns the name of the top-level transaction XML/SGML element\nfunc (pr *ProfileResponse) Name() string {\n\treturn \"PROFTRNRS\"\n}\n\n// Valid returns (true, nil) if this struct was valid OFX when unmarshalled\nfunc (pr *ProfileResponse) Valid(version ofxVersion) (bool, error) {\n\tif ok, err := pr.TrnUID.Valid(); !ok {\n\t\treturn false, err\n\t}\n\t//TODO implement\n\treturn true, nil\n}\n\n// Type returns which message set this message belongs to (which Response\n// element of type []Message it belongs to)\nfunc (pr *ProfileResponse) Type() messageType {\n\treturn ProfRs\n}\n"
  },
  {
    "path": "profile_test.go",
    "content": "package ofxgo\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestMarshalProfileRequest(t *testing.T) {\n\tvar expectedString string = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<?OFX OFXHEADER=\"200\" VERSION=\"203\" SECURITY=\"NONE\" OLDFILEUID=\"NONE\" NEWFILEUID=\"NONE\"?>\n<OFX>\n\t<SIGNONMSGSRQV1>\n\t\t<SONRQ>\n\t\t\t<DTCLIENT>20160614073400.000[-5:EST]</DTCLIENT>\n\t\t\t<USERID>anonymous00000000000000000000000</USERID>\n\t\t\t<USERPASS>anonymous00000000000000000000000</USERPASS>\n\t\t\t<LANGUAGE>ENG</LANGUAGE>\n\t\t\t<FI>\n\t\t\t\t<ORG>BNK</ORG>\n\t\t\t\t<FID>1987</FID>\n\t\t\t</FI>\n\t\t\t<APPID>OFXGO</APPID>\n\t\t\t<APPVER>0001</APPVER>\n\t\t</SONRQ>\n\t</SIGNONMSGSRQV1>\n\t<PROFMSGSRQV1>\n\t\t<PROFTRNRQ>\n\t\t\t<TRNUID>983373</TRNUID>\n\t\t\t<PROFRQ>\n\t\t\t\t<CLIENTROUTING>NONE</CLIENTROUTING>\n\t\t\t\t<DTPROFUP>20160101000000.000[-5:EST]</DTPROFUP>\n\t\t\t</PROFRQ>\n\t\t</PROFTRNRQ>\n\t</PROFMSGSRQV1>\n</OFX>`\n\n\tvar client = BasicClient{\n\t\tAppID:       \"OFXGO\",\n\t\tAppVer:      \"0001\",\n\t\tSpecVersion: OfxVersion203,\n\t}\n\n\tvar request Request\n\trequest.Signon.UserID = \"anonymous00000000000000000000000\"\n\trequest.Signon.UserPass = \"anonymous00000000000000000000000\"\n\trequest.Signon.Org = \"BNK\"\n\trequest.Signon.Fid = \"1987\"\n\n\tEST := time.FixedZone(\"EST\", -5*60*60)\n\n\tprofileRequest := ProfileRequest{\n\t\tTrnUID:   \"983373\",\n\t\tDtProfUp: *NewDate(2016, 1, 1, 0, 0, 0, 0, EST),\n\t}\n\trequest.Prof = append(request.Prof, &profileRequest)\n\n\trequest.SetClientFields(&client)\n\t// Overwrite the DtClient value set by SetClientFields to time.Now()\n\trequest.Signon.DtClient = *NewDate(2016, 6, 14, 7, 34, 0, 0, EST)\n\n\tmarshalCheckRequest(t, &request, expectedString)\n}\n\nfunc TestUnmarshalProfileResponse102(t *testing.T) {\n\tresponseReader := strings.NewReader(`OFXHEADER:100\nDATA:OFXSGML\nVERSION:102\nSECURITY:NONE\nENCODING:USASCII\nCHARSET:1252\nCOMPRESSION:NONE\nOLDFILEUID:NONE\nNEWFILEUID:NONE\n\n<OFX>\n<SIGNONMSGSRSV1>\n<SONRS>\n<STATUS>\n<CODE>0\n<SEVERITY>INFO\n</STATUS>\n<DTSERVER>20170403093458.000\n<LANGUAGE>ENG\n<DTPROFUP>20021119140000\n</SONRS>\n</SIGNONMSGSRSV1>\n<PROFMSGSRSV1>\n<PROFTRNRS>\n<TRNUID>0f94ce83-13b7-7568-e4fc-c02c7b47e7ab\n<STATUS>\n<CODE>0\n<SEVERITY>INFO\n</STATUS>\n<PROFRS>\n<MSGSETLIST>\n<SIGNONMSGSET>\n<SIGNONMSGSETV1>\n<MSGSETCORE>\n<VER>1\n<URL>https://ofx.example.com/cgi-ofx/exampleofx\n<OFXSEC>NONE\n<TRANSPSEC>Y\n<SIGNONREALM>Example Trade\n<LANGUAGE>ENG\n<SYNCMODE>LITE\n<RESPFILEER>N\n<INTU.TIMEOUT>300\n</MSGSETCORE>\n</SIGNONMSGSETV1>\n</SIGNONMSGSET>\n<SIGNUPMSGSET>\n<SIGNUPMSGSETV1>\n<MSGSETCORE>\n<VER>1\n<URL>https://ofx.example.com/cgi-ofx/exampleofx\n<OFXSEC>NONE\n<TRANSPSEC>Y\n<SIGNONREALM>Example Trade\n<LANGUAGE>ENG\n<SYNCMODE>LITE\n<RESPFILEER>N\n<INTU.TIMEOUT>300\n</MSGSETCORE>\n<CLIENTENROLL>\n<ACCTREQUIRED>Y\n</CLIENTENROLL>\n<CHGUSERINFO>N\n<AVAILACCTS>Y\n<CLIENTACTREQ>Y\n</SIGNUPMSGSETV1>\n</SIGNUPMSGSET>\n<INVSTMTMSGSET>\n<INVSTMTMSGSETV1>\n<MSGSETCORE>\n<VER>1\n<URL>https://ofx.example.com/cgi-ofx/exampleofx\n<OFXSEC>NONE\n<TRANSPSEC>Y\n<SIGNONREALM>Example Trade\n<LANGUAGE>ENG\n<SYNCMODE>LITE\n<RESPFILEER>N\n<INTU.TIMEOUT>300\n</MSGSETCORE>\n<TRANDNLD>Y\n<OODNLD>N\n<POSDNLD>Y\n<BALDNLD>Y\n<CANEMAIL>N\n</INVSTMTMSGSETV1>\n</INVSTMTMSGSET>\n<SECLISTMSGSET>\n<SECLISTMSGSETV1>\n<MSGSETCORE>\n<VER>1\n<URL>https://ofx.example.com/cgi-ofx/exampleofx\n<OFXSEC>NONE\n<TRANSPSEC>Y\n<SIGNONREALM>Example Trade\n<LANGUAGE>ENG\n<SYNCMODE>LITE\n<RESPFILEER>N\n<INTU.TIMEOUT>300\n</MSGSETCORE>\n<SECLISTRQDNLD>Y\n</SECLISTMSGSETV1>\n</SECLISTMSGSET>\n<PROFMSGSET>\n<PROFMSGSETV1>\n<MSGSETCORE>\n<VER>1\n<URL>https://ofx.example.com/cgi-ofx/exampleofx\n<OFXSEC>NONE\n<TRANSPSEC>Y\n<SIGNONREALM>Example Trade\n<LANGUAGE>ENG\n<SYNCMODE>LITE\n<RESPFILEER>N\n<INTU.TIMEOUT>300\n</MSGSETCORE>\n</PROFMSGSETV1>\n</PROFMSGSET>\n</MSGSETLIST>\n<SIGNONINFOLIST>\n<SIGNONINFO>\n<SIGNONREALM>Example Trade\n<MIN>1\n<MAX>32\n<CHARTYPE>ALPHAORNUMERIC\n<CASESEN>N\n<SPECIAL>Y\n<SPACES>N\n<PINCH>N\n<CHGPINFIRST>N\n</SIGNONINFO>\n</SIGNONINFOLIST>\n<DTPROFUP>20021119140000\n<FINAME>Example Trade Financial\n<ADDR1>5555 Buhunkus Drive\n<CITY>Someville\n<STATE>NC\n<POSTALCODE>28801\n<COUNTRY>USA\n<CSPHONE>1-800-234-5678\n<TSPHONE>1-800-234-5678\n<FAXPHONE>1-888-234-5678\n<URL>http://www.example.com\n<EMAIL>service@example.com\n<INTU.BROKERID>example.com\n</PROFRS>\n</PROFTRNRS>\n</PROFMSGSRSV1>\n</OFX>`)\n\tvar expected Response\n\n\texpected.Version = OfxVersion102\n\texpected.Signon.Status.Code = 0\n\texpected.Signon.Status.Severity = \"INFO\"\n\texpected.Signon.DtServer = *NewDateGMT(2017, 4, 3, 9, 34, 58, 0)\n\texpected.Signon.Language = \"ENG\"\n\texpected.Signon.DtProfUp = NewDateGMT(2002, 11, 19, 14, 0, 0, 0)\n\n\tprofileResponse := ProfileResponse{\n\t\tTrnUID: \"0f94ce83-13b7-7568-e4fc-c02c7b47e7ab\",\n\t\tStatus: Status{\n\t\t\tCode:     0,\n\t\t\tSeverity: \"INFO\",\n\t\t},\n\t\tMessageSetList: MessageSetList{\n\t\t\tMessageSet{\n\t\t\t\tName:        \"SIGNONMSGSETV1\",\n\t\t\t\tVer:         1,\n\t\t\t\tURL:         \"https://ofx.example.com/cgi-ofx/exampleofx\",\n\t\t\t\tOfxSec:      OfxSecNone,\n\t\t\t\tTranspSec:   true,\n\t\t\t\tSignonRealm: \"Example Trade\",\n\t\t\t\tLanguage:    []String{\"ENG\"},\n\t\t\t\tSyncMode:    SyncModeLite,\n\t\t\t\tRespFileER:  false,\n\t\t\t\t// Ignored: <INTU.TIMEOUT>300\n\t\t\t},\n\t\t\tMessageSet{\n\t\t\t\tName:        \"SIGNUPMSGSETV1\",\n\t\t\t\tVer:         1,\n\t\t\t\tURL:         \"https://ofx.example.com/cgi-ofx/exampleofx\",\n\t\t\t\tOfxSec:      OfxSecNone,\n\t\t\t\tTranspSec:   true,\n\t\t\t\tSignonRealm: \"Example Trade\",\n\t\t\t\tLanguage:    []String{\"ENG\"},\n\t\t\t\tSyncMode:    SyncModeLite,\n\t\t\t\tRespFileER:  false,\n\t\t\t\t// Ignored: <INTU.TIMEOUT>300\n\t\t\t},\n\t\t\tMessageSet{\n\t\t\t\tName:        \"INVSTMTMSGSETV1\",\n\t\t\t\tVer:         1,\n\t\t\t\tURL:         \"https://ofx.example.com/cgi-ofx/exampleofx\",\n\t\t\t\tOfxSec:      OfxSecNone,\n\t\t\t\tTranspSec:   true,\n\t\t\t\tSignonRealm: \"Example Trade\",\n\t\t\t\tLanguage:    []String{\"ENG\"},\n\t\t\t\tSyncMode:    SyncModeLite,\n\t\t\t\tRespFileER:  false,\n\t\t\t\t// Ignored: <INTU.TIMEOUT>300\n\t\t\t},\n\t\t\tMessageSet{\n\t\t\t\tName:        \"SECLISTMSGSETV1\",\n\t\t\t\tVer:         1,\n\t\t\t\tURL:         \"https://ofx.example.com/cgi-ofx/exampleofx\",\n\t\t\t\tOfxSec:      OfxSecNone,\n\t\t\t\tTranspSec:   true,\n\t\t\t\tSignonRealm: \"Example Trade\",\n\t\t\t\tLanguage:    []String{\"ENG\"},\n\t\t\t\tSyncMode:    SyncModeLite,\n\t\t\t\tRespFileER:  false,\n\t\t\t\t// Ignored: <INTU.TIMEOUT>300\n\t\t\t},\n\t\t\tMessageSet{\n\t\t\t\tName:        \"PROFMSGSETV1\",\n\t\t\t\tVer:         1,\n\t\t\t\tURL:         \"https://ofx.example.com/cgi-ofx/exampleofx\",\n\t\t\t\tOfxSec:      OfxSecNone,\n\t\t\t\tTranspSec:   true,\n\t\t\t\tSignonRealm: \"Example Trade\",\n\t\t\t\tLanguage:    []String{\"ENG\"},\n\t\t\t\tSyncMode:    SyncModeLite,\n\t\t\t\tRespFileER:  false,\n\t\t\t\t// Ignored: <INTU.TIMEOUT>300\n\t\t\t},\n\t\t},\n\t\tSignonInfoList: []SignonInfo{\n\t\t\t{\n\t\t\t\tSignonRealm: \"Example Trade\",\n\t\t\t\tMin:         1,\n\t\t\t\tMax:         32,\n\t\t\t\tCharType:    CharTypeAlphaOrNumeric,\n\t\t\t\tCaseSen:     false,\n\t\t\t\tSpecial:     true,\n\t\t\t\tSpaces:      false,\n\t\t\t\tPinCh:       false,\n\t\t\t\tChgPinFirst: false,\n\t\t\t},\n\t\t},\n\t\tDtProfUp:   *NewDateGMT(2002, 11, 19, 14, 0, 0, 0),\n\t\tFiName:     \"Example Trade Financial\",\n\t\tAddr1:      \"5555 Buhunkus Drive\",\n\t\tCity:       \"Someville\",\n\t\tState:      \"NC\",\n\t\tPostalCode: \"28801\",\n\t\tCountry:    \"USA\",\n\t\tCsPhone:    \"1-800-234-5678\",\n\t\tTsPhone:    \"1-800-234-5678\",\n\t\tFaxPhone:   \"1-888-234-5678\",\n\t\tURL:        \"http://www.example.com\",\n\t\tEmail:      \"service@example.com\",\n\t\t// Ignored: <INTU.BROKERID>example.com\n\t}\n\texpected.Prof = append(expected.Prof, &profileResponse)\n\n\tresponse, err := ParseResponse(responseReader)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error unmarshalling response: %s\\n\", err)\n\t}\n\n\tcheckResponsesEqual(t, &expected, response)\n\tcheckResponseRoundTrip(t, response)\n}\n"
  },
  {
    "path": "request.go",
    "content": "package ofxgo\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"github.com/aclindsa/xml\"\n\t\"time\"\n)\n\n// Request is the top-level object marshalled and sent to OFX servers. It is\n// constructed by appending one or more request objects to the message set they\n// correspond to (i.e. appending StatementRequest to Request.Bank to get a bank\n// statemement). If a *Request object is appended to the wrong message set, an\n// error will be returned when Marshal() is called on this Request.\ntype Request struct {\n\tURL        string\n\tVersion    ofxVersion    // OFX version, overwritten in Client.Request()\n\tSignon     SignonRequest //<SIGNONMSGSETV1>\n\tSignup     []Message     //<SIGNUPMSGSETV1>\n\tBank       []Message     //<BANKMSGSETV1>\n\tCreditCard []Message     //<CREDITCARDMSGSETV1>\n\tLoan       []Message     //<LOANMSGSETV1>\n\tInvStmt    []Message     //<INVSTMTMSGSETV1>\n\tInterXfer  []Message     //<INTERXFERMSGSETV1>\n\tWireXfer   []Message     //<WIREXFERMSGSETV1>\n\tBillpay    []Message     //<BILLPAYMSGSETV1>\n\tEmail      []Message     //<EMAILMSGSETV1>\n\tSecList    []Message     //<SECLISTMSGSETV1>\n\tPresDir    []Message     //<PRESDIRMSGSETV1>\n\tPresDlv    []Message     //<PRESDLVMSGSETV1>\n\tProf       []Message     //<PROFMSGSETV1>\n\tImage      []Message     //<IMAGEMSGSETV1>\n\n\tindent         bool // Whether to indent the marshaled XML\n\tcarriageReturn bool // Whether to user carriage returns in new lines for marshaled XML\n}\n\nfunc encodeMessageSet(e *xml.Encoder, requests []Message, set messageType, version ofxVersion) error {\n\tif len(requests) > 0 {\n\t\tmessageSetElement := xml.StartElement{Name: xml.Name{Local: set.String()}}\n\t\tif err := e.EncodeToken(messageSetElement); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tfor _, request := range requests {\n\t\t\tif request.Type() != set {\n\t\t\t\treturn errors.New(\"Expected \" + set.String() + \" message , found \" + request.Type().String())\n\t\t\t}\n\t\t\tif ok, err := request.Valid(version); !ok {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif err := e.Encode(request); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tif err := e.EncodeToken(messageSetElement.End()); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// SetClientFields overwrites the fields in this Request object controlled by\n// the Client\nfunc (oq *Request) SetClientFields(c Client) {\n\toq.Signon.DtClient.Time = time.Now()\n\n\t// Overwrite fields that the client controls\n\toq.Version = c.OfxVersion()\n\toq.Signon.AppID = c.ID()\n\toq.Signon.AppVer = c.Version()\n\toq.indent = c.IndentRequests()\n\toq.carriageReturn = c.CarriageReturnNewLines()\n}\n\n// Marshal this Request into its SGML/XML representation held in a bytes.Buffer\n//\n// If error is non-nil, this bytes.Buffer is ready to be sent to an OFX server\nfunc (oq *Request) Marshal() (*bytes.Buffer, error) {\n\tvar b bytes.Buffer\n\n\t// Write the header appropriate to our version\n\twriteHeader(&b, oq.Version, oq.carriageReturn)\n\n\tencoder := xml.NewEncoder(&b)\n\tif oq.indent {\n\t\tencoder.Indent(\"\", \"    \")\n\t}\n\tif oq.carriageReturn {\n\t\tencoder.CarriageReturn(true)\n\t}\n\tif oq.Version < OfxVersion200 {\n\t\t// OFX 100 series versions should avoid element close tags for compatibility\n\t\tencoder.SetDisableAutoClose(ofxLeafElements...)\n\t}\n\n\tofxElement := xml.StartElement{Name: xml.Name{Local: \"OFX\"}}\n\n\tif err := encoder.EncodeToken(ofxElement); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif ok, err := oq.Signon.Valid(oq.Version); !ok {\n\t\treturn nil, err\n\t}\n\tsignonMsgSet := xml.StartElement{Name: xml.Name{Local: SignonRq.String()}}\n\tif err := encoder.EncodeToken(signonMsgSet); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := encoder.Encode(&oq.Signon); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := encoder.EncodeToken(signonMsgSet.End()); err != nil {\n\t\treturn nil, err\n\t}\n\n\tmessageSets := []struct {\n\t\tMessages []Message\n\t\tType     messageType\n\t}{\n\t\t{oq.Signup, SignupRq},\n\t\t{oq.Bank, BankRq},\n\t\t{oq.CreditCard, CreditCardRq},\n\t\t{oq.Loan, LoanRq},\n\t\t{oq.InvStmt, InvStmtRq},\n\t\t{oq.InterXfer, InterXferRq},\n\t\t{oq.WireXfer, WireXferRq},\n\t\t{oq.Billpay, BillpayRq},\n\t\t{oq.Email, EmailRq},\n\t\t{oq.SecList, SecListRq},\n\t\t{oq.PresDir, PresDirRq},\n\t\t{oq.PresDlv, PresDlvRq},\n\t\t{oq.Prof, ProfRq},\n\t\t{oq.Image, ImageRq},\n\t}\n\tfor _, set := range messageSets {\n\t\tif err := encodeMessageSet(encoder, set.Messages, set.Type, oq.Version); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tif err := encoder.EncodeToken(ofxElement.End()); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := encoder.Flush(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &b, nil\n}\n"
  },
  {
    "path": "request_test.go",
    "content": "package ofxgo\n\nimport (\n\t\"regexp\"\n\t\"strings\"\n\t\"testing\"\n)\n\n// match leading and trailing whitespace on each line\nvar ignoreSpacesRe = regexp.MustCompile(\"(?m)^[ \\t]+|[ \\t]*$[\\r\\n]+\")\n\nfunc marshalCheckRequest(t *testing.T, request *Request, expected string) {\n\tt.Helper()\n\tbuf, err := request.Marshal()\n\tif err != nil {\n\t\tt.Fatalf(\"%s: Unexpected error marshalling request: %s\\n\", t.Name(), err)\n\t}\n\tactualString := buf.String()\n\n\t// Ignore spaces between XML elements\n\texpectedString := ignoreSpacesRe.ReplaceAllString(expected, \"\")\n\tactualString = ignoreSpacesRe.ReplaceAllString(actualString, \"\")\n\n\tif expectedString != actualString {\n\t\tcompareLength := len(expectedString)\n\t\tif len(actualString) < compareLength {\n\t\t\tcompareLength = len(actualString)\n\t\t}\n\n\t\tfor i := 0; i < compareLength; i++ {\n\t\t\tif expectedString[i] != actualString[i] {\n\t\t\t\tfirstDifferencePosition := 13\n\t\t\t\tdisplayStart := i - 10\n\t\t\t\tprefix := \"...\"\n\t\t\t\tsuffix := \"...\"\n\t\t\t\tif displayStart < 0 {\n\t\t\t\t\tprefix = \"\"\n\t\t\t\t\tfirstDifferencePosition = i\n\t\t\t\t\tdisplayStart = 0\n\t\t\t\t}\n\t\t\t\tdisplayEnd := displayStart + 40\n\t\t\t\tif displayEnd > compareLength {\n\t\t\t\t\tsuffix = \"\"\n\t\t\t\t\tdisplayEnd = compareLength\n\t\t\t\t}\n\t\t\t\tt.Fatalf(\"%s expected '%s%s%s',\\ngot '%s%s%s'\\n     %s^ first difference\\n\", t.Name(), prefix, expectedString[displayStart:displayEnd], suffix, prefix, actualString[displayStart:displayEnd], suffix, strings.Repeat(\" \", firstDifferencePosition))\n\t\t\t}\n\t\t}\n\n\t\tif len(actualString) > compareLength {\n\t\t\tt.Fatalf(\"%s: Actual string longer than expected string\\n\", t.Name())\n\t\t} else {\n\t\t\tt.Fatalf(\"%s: Actual string shorter than expected string\\n\", t.Name())\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "response.go",
    "content": "package ofxgo\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/aclindsa/xml\"\n)\n\n// Response is the top-level object returned from a parsed OFX response file.\n// It can be inspected by using type assertions or switches on the message set\n// you're interested in.\ntype Response struct {\n\tVersion    ofxVersion     // OFX header version\n\tSignon     SignonResponse //<SIGNONMSGSETV1>\n\tSignup     []Message      //<SIGNUPMSGSETV1>\n\tBank       []Message      //<BANKMSGSETV1>\n\tCreditCard []Message      //<CREDITCARDMSGSETV1>\n\tLoan       []Message      //<LOANMSGSETV1>\n\tInvStmt    []Message      //<INVSTMTMSGSETV1>\n\tInterXfer  []Message      //<INTERXFERMSGSETV1>\n\tWireXfer   []Message      //<WIREXFERMSGSETV1>\n\tBillpay    []Message      //<BILLPAYMSGSETV1>\n\tEmail      []Message      //<EMAILMSGSETV1>\n\tSecList    []Message      //<SECLISTMSGSETV1>\n\tPresDir    []Message      //<PRESDIRMSGSETV1>\n\tPresDlv    []Message      //<PRESDLVMSGSETV1>\n\tProf       []Message      //<PROFMSGSETV1>\n\tImage      []Message      //<IMAGEMSGSETV1>\n}\n\nfunc (or *Response) readSGMLHeaders(r *bufio.Reader) error {\n\tb, err := r.ReadSlice('<')\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ts := string(b)\n\terr = r.UnreadByte()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// According to the latest OFX SGML spec (1.6), headers should be CRLF-separated\n\t// and written as KEY:VALUE. However, some banks include a whitespace after the\n\t// colon (KEY: VALUE), while others include no line breaks at all. The spec doesn't\n\t// require a line break after the OFX headers, but it is allowed, and will be\n\t// optionally captured & discarded by the trailing `\\s*`. Valid SGML headers must\n\t// always be present in exactly this order, so a regular expression is acceptable.\n\theaderExp := regexp.MustCompile(\n\t\t`^OFXHEADER:\\s*(?P<OFXHEADER>\\d+)\\s*` +\n\t\t\t`DATA:\\s*(?P<DATA>[A-Z]+)\\s*` +\n\t\t\t`VERSION:\\s*(?P<VERSION>\\d+)\\s*` +\n\t\t\t`SECURITY:\\s*(?P<SECURITY>[\\w]+)\\s*` +\n\t\t\t`ENCODING:\\s*(?P<ENCODING>[A-Z0-9-]+)\\s*` +\n\t\t\t`CHARSET:\\s*(?P<CHARSET>[\\w-]+)\\s*` +\n\t\t\t`COMPRESSION:\\s*(?P<COMPRESSION>[A-Z]+)\\s*` +\n\t\t\t`OLDFILEUID:\\s*(?P<OLDFILEUID>[\\w-]+)\\s*` +\n\t\t\t`NEWFILEUID:\\s*(?P<NEWFILEUID>[\\w-]+)\\s*<$`)\n\n\tmatches := headerExp.FindStringSubmatch(s)\n\tif len(matches) == 0 {\n\t\treturn errors.New(\"OFX headers malformed\")\n\t}\n\n\tfor i, name := range headerExp.SubexpNames() {\n\t\tif i == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\theaderValue := matches[i]\n\t\tswitch name {\n\t\tcase \"OFXHEADER\":\n\t\t\tif headerValue != \"100\" {\n\t\t\t\treturn errors.New(\"OFXHEADER is not 100\")\n\t\t\t}\n\t\tcase \"DATA\":\n\t\t\tif headerValue != \"OFXSGML\" {\n\t\t\t\treturn errors.New(\"OFX DATA header does not contain OFXSGML\")\n\t\t\t}\n\t\tcase \"VERSION\":\n\t\t\terr := or.Version.FromString(headerValue)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif or.Version > OfxVersion160 {\n\t\t\t\treturn errors.New(\"OFX VERSION > 160 in SGML header\")\n\t\t\t}\n\t\tcase \"SECURITY\":\n\t\t\tif !(headerValue == \"NONE\" || headerValue == \"TYPE1\") {\n\t\t\t\treturn errors.New(\"OFX SECURITY header must be NONE or TYPE1\")\n\t\t\t}\n\t\tcase \"COMPRESSION\":\n\t\t\tif headerValue != \"NONE\" {\n\t\t\t\treturn errors.New(\"OFX COMPRESSION header not NONE\")\n\t\t\t}\n\t\tcase \"ENCODING\", \"CHARSET\", \"OLDFILEUID\", \"NEWFILEUID\":\n\t\t\t// TODO: check/handle these headers?\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (or *Response) readXMLHeaders(decoder *xml.Decoder) error {\n\tvar tok xml.Token\n\ttok, err := nextNonWhitespaceToken(decoder)\n\tif err != nil {\n\t\treturn err\n\t} else if xmlElem, ok := tok.(xml.ProcInst); !ok || xmlElem.Target != \"xml\" {\n\t\treturn errors.New(\"Missing xml processing instruction\")\n\t}\n\n\t// parse the OFX header\n\ttok, err = nextNonWhitespaceToken(decoder)\n\tif err != nil {\n\t\treturn err\n\t} else if ofxElem, ok := tok.(xml.ProcInst); ok && ofxElem.Target == \"OFX\" {\n\t\tvar seenHeader, seenVersion bool = false, false\n\n\t\theaders := bytes.TrimSpace(ofxElem.Inst)\n\t\tfor len(headers) > 0 {\n\t\t\ttmp := bytes.SplitN(headers, []byte(\"=\\\"\"), 2)\n\t\t\tif len(tmp) != 2 {\n\t\t\t\treturn errors.New(\"Malformed OFX header\")\n\t\t\t}\n\t\t\theader := string(tmp[0])\n\t\t\theaders = tmp[1]\n\t\t\ttmp = bytes.SplitN(headers, []byte(\"\\\"\"), 2)\n\t\t\tif len(tmp) != 2 {\n\t\t\t\treturn errors.New(\"Malformed OFX header\")\n\t\t\t}\n\t\t\tvalue := string(tmp[0])\n\t\t\theaders = bytes.TrimSpace(tmp[1])\n\n\t\t\tswitch header {\n\t\t\tcase \"OFXHEADER\":\n\t\t\t\tif value != \"200\" {\n\t\t\t\t\treturn errors.New(\"OFXHEADER is not 200\")\n\t\t\t\t}\n\t\t\t\tseenHeader = true\n\t\t\tcase \"VERSION\":\n\t\t\t\terr := or.Version.FromString(value)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tseenVersion = true\n\n\t\t\t\tif or.Version < OfxVersion200 {\n\t\t\t\t\treturn errors.New(\"OFX VERSION < 200 in XML header\")\n\t\t\t\t}\n\t\t\tcase \"SECURITY\":\n\t\t\t\tif value != \"NONE\" {\n\t\t\t\t\treturn errors.New(\"OFX SECURITY header not NONE\")\n\t\t\t\t}\n\t\t\tcase \"OLDFILEUID\", \"NEWFILEUID\":\n\t\t\t\t// TODO check/handle these headers?\n\t\t\tdefault:\n\t\t\t\treturn errors.New(\"Invalid OFX header: \" + header)\n\t\t\t}\n\t\t}\n\n\t\tif !seenHeader {\n\t\t\treturn errors.New(\"OFXHEADER version missing\")\n\t\t}\n\t\tif !seenVersion {\n\t\t\treturn errors.New(\"OFX VERSION header missing\")\n\t\t}\n\n\t} else {\n\t\treturn errors.New(\"Missing xml 'OFX' processing instruction\")\n\t}\n\treturn nil\n}\n\n// Number of bytes of response to read when attempting to figure out whether\n// we're using OFX or SGML\nconst guessVersionCheckBytes = 1024\n\n// Defaults to XML if it can't determine the version or if there is any\n// ambiguity\n// Returns false for SGML, true (for XML) otherwise.\nfunc guessVersion(r *bufio.Reader) (bool, error) {\n\tb, _ := r.Peek(guessVersionCheckBytes)\n\tif b == nil {\n\t\treturn false, errors.New(\"Failed to read OFX header\")\n\t}\n\tsgmlIndex := bytes.Index(b, []byte(\"OFXHEADER:\"))\n\txmlIndex := bytes.Index(b, []byte(\"OFXHEADER=\"))\n\tif sgmlIndex < 0 {\n\t\treturn true, nil\n\t} else if xmlIndex < 0 {\n\t\treturn false, nil\n\t} else {\n\t\treturn xmlIndex <= sgmlIndex, nil\n\t}\n}\n\n// A map of message set tags to a map of transaction wrapper tags to the\n// reflect.Type of the struct for that transaction type. Used when decoding\n// Responses. Newly-implemented response transaction types *must* be added to\n// this map in order to be unmarshalled.\nvar responseTypes = map[string]map[string]reflect.Type{\n\tSignupRs.String(): {\n\t\t(&AcctInfoResponse{}).Name(): reflect.TypeOf(AcctInfoResponse{})},\n\tBankRs.String(): {\n\t\t(&StatementResponse{}).Name(): reflect.TypeOf(StatementResponse{})},\n\tCreditCardRs.String(): {\n\t\t(&CCStatementResponse{}).Name(): reflect.TypeOf(CCStatementResponse{})},\n\tLoanRs.String(): {},\n\tInvStmtRs.String(): {\n\t\t(&InvStatementResponse{}).Name(): reflect.TypeOf(InvStatementResponse{})},\n\tInterXferRs.String(): {},\n\tWireXferRs.String():  {},\n\tBillpayRs.String():   {},\n\tEmailRs.String():     {},\n\tSecListRs.String(): {\n\t\t(&SecListResponse{}).Name(): reflect.TypeOf(SecListResponse{}),\n\t\t(&SecurityList{}).Name():    reflect.TypeOf(SecurityList{})},\n\tPresDirRs.String(): {},\n\tPresDlvRs.String(): {},\n\tProfRs.String(): {\n\t\t(&ProfileResponse{}).Name(): reflect.TypeOf(ProfileResponse{})},\n\tImageRs.String(): {},\n}\n\nfunc decodeMessageSet(d *xml.Decoder, start xml.StartElement, msgs *[]Message, version ofxVersion) error {\n\tsetTypes, ok := responseTypes[start.Name.Local]\n\tif !ok {\n\t\treturn errors.New(\"Invalid message set: \" + start.Name.Local)\n\t}\n\tfor {\n\t\ttok, err := nextNonWhitespaceToken(d)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t} else if end, ok := tok.(xml.EndElement); ok && end.Name.Local == start.Name.Local {\n\t\t\t// If we found the end of our starting element, we're done parsing\n\t\t\treturn nil\n\t\t} else if startElement, ok := tok.(xml.StartElement); ok {\n\t\t\tresponseType, ok := setTypes[startElement.Name.Local]\n\t\t\tif !ok {\n\t\t\t\t// If you are a developer and received this message after you\n\t\t\t\t// thought you added a new transaction type, make sure you\n\t\t\t\t// added it to the responseTypes map above\n\t\t\t\treturn errors.New(\"Unsupported response transaction for \" +\n\t\t\t\t\tstart.Name.Local + \": \" + startElement.Name.Local)\n\t\t\t}\n\t\t\tresponse := reflect.New(responseType).Interface()\n\t\t\tresponseMessage := response.(Message)\n\t\t\tif err := d.DecodeElement(responseMessage, &startElement); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\t*msgs = append(*msgs, responseMessage)\n\t\t} else {\n\t\t\treturn errors.New(\"Didn't find an opening element\")\n\t\t}\n\t}\n}\n\n// ParseResponse parses and validates an OFX response in SGML or XML into a\n// Response object from the given io.Reader\n//\n// It is commonly used as part of Client.Request(), but may be used on its own\n// to parse already-downloaded OFX files (such as those from 'Web Connect'). It\n// performs version autodetection if it can and attempts to be as forgiving as\n// possible about the input format.\nfunc ParseResponse(reader io.Reader) (*Response, error) {\n\tresp, err := DecodeResponse(reader)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t_, err = resp.Valid()\n\treturn resp, err\n}\n\n// DecodeResponse parses an OFX response in SGML or XML into a Response object\n// from the given io.Reader\nfunc DecodeResponse(reader io.Reader) (*Response, error) {\n\tvar or Response\n\n\tr := bufio.NewReaderSize(reader, guessVersionCheckBytes)\n\txmlVersion, err := guessVersion(r)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// parse SGML headers before creating XML decoder\n\tif !xmlVersion {\n\t\tif err := or.readSGMLHeaders(r); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tdecoder := xml.NewDecoder(r)\n\tif !xmlVersion {\n\t\tdecoder.Strict = false\n\t\tdecoder.AutoCloseAfterCharData = ofxLeafElements\n\t}\n\tdecoder.CharsetReader = func(charset string, input io.Reader) (io.Reader, error) {\n\t\treturn input, nil\n\t}\n\n\tif xmlVersion {\n\t\t// parse the xml header\n\t\tif err := or.readXMLHeaders(decoder); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\ttok, err := nextNonWhitespaceToken(decoder)\n\tif err != nil {\n\t\treturn nil, err\n\t} else if ofxStart, ok := tok.(xml.StartElement); !ok || ofxStart.Name.Local != \"OFX\" {\n\t\treturn nil, errors.New(\"Missing opening OFX xml element\")\n\t}\n\n\t// Unmarshal the signon message\n\ttok, err = nextNonWhitespaceToken(decoder)\n\tif err != nil {\n\t\treturn nil, err\n\t} else if signonStart, ok := tok.(xml.StartElement); ok && signonStart.Name.Local == SignonRs.String() {\n\t\tif err := decoder.Decode(&or.Signon); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t} else {\n\t\treturn nil, errors.New(\"Missing opening SIGNONMSGSRSV1 xml element\")\n\t}\n\n\ttok, err = nextNonWhitespaceToken(decoder)\n\tif err != nil {\n\t\treturn nil, err\n\t} else if signonEnd, ok := tok.(xml.EndElement); !ok || signonEnd.Name.Local != SignonRs.String() {\n\t\treturn nil, errors.New(\"Missing closing SIGNONMSGSRSV1 xml element\")\n\t}\n\n\tvar messageSlices = map[string]*[]Message{\n\t\tSignupRs.String():     &or.Signup,\n\t\tBankRs.String():       &or.Bank,\n\t\tCreditCardRs.String(): &or.CreditCard,\n\t\tLoanRs.String():       &or.Loan,\n\t\tInvStmtRs.String():    &or.InvStmt,\n\t\tInterXferRs.String():  &or.InterXfer,\n\t\tWireXferRs.String():   &or.WireXfer,\n\t\tBillpayRs.String():    &or.Billpay,\n\t\tEmailRs.String():      &or.Email,\n\t\tSecListRs.String():    &or.SecList,\n\t\tPresDirRs.String():    &or.PresDir,\n\t\tPresDlvRs.String():    &or.PresDlv,\n\t\tProfRs.String():       &or.Prof,\n\t\tImageRs.String():      &or.Image,\n\t}\n\n\tfor {\n\t\ttok, err = nextNonWhitespaceToken(decoder)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t} else if ofxEnd, ok := tok.(xml.EndElement); ok && ofxEnd.Name.Local == \"OFX\" {\n\t\t\treturn &or, nil // found closing XML element, so we're done\n\t\t} else if start, ok := tok.(xml.StartElement); ok {\n\t\t\tslice, ok := messageSlices[start.Name.Local]\n\t\t\tif !ok {\n\t\t\t\treturn nil, errors.New(\"Invalid message set: \" + start.Name.Local)\n\t\t\t}\n\t\t\tif err := decodeMessageSet(decoder, start, slice, or.Version); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t} else {\n\t\t\treturn nil, errors.New(\"Found unexpected token\")\n\t\t}\n\t}\n}\n\n// Valid returns whether the Response is valid according to the OFX spec\nfunc (or *Response) Valid() (bool, error) {\n\tvar errs errInvalid\n\tif ok, err := or.Signon.Valid(or.Version); !ok {\n\t\terrs.AddErr(err)\n\t}\n\tfor _, messageSet := range [][]Message{\n\t\tor.Signup,\n\t\tor.Bank,\n\t\tor.CreditCard,\n\t\tor.Loan,\n\t\tor.InvStmt,\n\t\tor.InterXfer,\n\t\tor.WireXfer,\n\t\tor.Billpay,\n\t\tor.Email,\n\t\tor.SecList,\n\t\tor.PresDir,\n\t\tor.PresDlv,\n\t\tor.Prof,\n\t\tor.Image,\n\t} {\n\t\tfor _, message := range messageSet {\n\t\t\tif ok, err := message.Valid(or.Version); !ok {\n\t\t\t\terrs.AddErr(err)\n\t\t\t}\n\t\t}\n\t}\n\terr := errs.ErrOrNil()\n\treturn err == nil, err\n}\n\n// Marshal this Response into its SGML/XML representation held in a bytes.Buffer\n//\n// If error is non-nil, this bytes.Buffer is ready to be sent to an OFX client\nfunc (or *Response) Marshal() (*bytes.Buffer, error) {\n\tvar b bytes.Buffer\n\n\t// Write the header appropriate to our version\n\twriteHeader(&b, or.Version, false)\n\n\tencoder := xml.NewEncoder(&b)\n\tencoder.Indent(\"\", \"    \")\n\n\tofxElement := xml.StartElement{Name: xml.Name{Local: \"OFX\"}}\n\n\tif err := encoder.EncodeToken(ofxElement); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif ok, err := or.Signon.Valid(or.Version); !ok {\n\t\treturn nil, err\n\t}\n\tsignonMsgSet := xml.StartElement{Name: xml.Name{Local: SignonRs.String()}}\n\tif err := encoder.EncodeToken(signonMsgSet); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := encoder.Encode(&or.Signon); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := encoder.EncodeToken(signonMsgSet.End()); err != nil {\n\t\treturn nil, err\n\t}\n\n\tmessageSets := []struct {\n\t\tMessages []Message\n\t\tType     messageType\n\t}{\n\t\t{or.Signup, SignupRs},\n\t\t{or.Bank, BankRs},\n\t\t{or.CreditCard, CreditCardRs},\n\t\t{or.Loan, LoanRs},\n\t\t{or.InvStmt, InvStmtRs},\n\t\t{or.InterXfer, InterXferRs},\n\t\t{or.WireXfer, WireXferRs},\n\t\t{or.Billpay, BillpayRs},\n\t\t{or.Email, EmailRs},\n\t\t{or.SecList, SecListRs},\n\t\t{or.PresDir, PresDirRs},\n\t\t{or.PresDlv, PresDlvRs},\n\t\t{or.Prof, ProfRs},\n\t\t{or.Image, ImageRs},\n\t}\n\tfor _, set := range messageSets {\n\t\tif err := encodeMessageSet(encoder, set.Messages, set.Type, or.Version); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tif err := encoder.EncodeToken(ofxElement.End()); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := encoder.Flush(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &b, nil\n}\n\n// errInvalid represents validation failures while parsing an OFX response\n// If an institution returns slightly malformed data, ParseResponse will return a best-effort parsed response and a validation error.\ntype errInvalid []error\n\nfunc (e errInvalid) Error() string {\n\tvar errStrings []string\n\tfor _, err := range e {\n\t\terrStrings = append(errStrings, err.Error())\n\t}\n\treturn fmt.Sprintf(\"Validation failed: %s\", strings.Join(errStrings, \"; \"))\n}\n\nfunc (e *errInvalid) AddErr(err error) {\n\tif err != nil {\n\t\tif errs, ok := err.(errInvalid); ok {\n\t\t\t*e = append(*e, errs...)\n\t\t} else {\n\t\t\t*e = append(*e, err)\n\t\t}\n\t}\n}\n\nfunc (e errInvalid) ErrOrNil() error {\n\tif len(e) > 0 {\n\t\treturn e\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "response_test.go",
    "content": "package ofxgo\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/aclindsa/xml\"\n)\n\n// Attempt to find a method on the provided Value called 'Equal' which is a\n// receiver for the Value, takes one argument of the same type, and returns\n// one bool. equalMethodOf() returns the nil value if the method couldn't be\n// found.\nfunc equalMethodOf(v reflect.Value) reflect.Value {\n\tif equalMethod, ok := v.Type().MethodByName(\"Equal\"); ok {\n\t\tif !equalMethod.Func.IsNil() &&\n\t\t\tequalMethod.Type.NumIn() == 2 &&\n\t\t\tequalMethod.Type.In(0) == v.Type() &&\n\t\t\tequalMethod.Type.In(1) == v.Type() &&\n\t\t\tequalMethod.Type.NumOut() == 1 &&\n\t\t\tequalMethod.Type.Out(0).Kind() == reflect.Bool {\n\t\t\treturn v.MethodByName(\"Equal\")\n\t\t}\n\t}\n\treturn reflect.ValueOf(nil)\n}\n\n// Attempt to return a string representation of the value appropriate for its\n// type by finding a method on the provided Value called 'String' which is a\n// receiver for the Value, and returns one string. stringMethodOf() returns\n// fmt.Sprintf(\"%s\", v) if it can't find a String method.\nfunc valueToString(v reflect.Value) string {\n\tif equalMethod, ok := v.Type().MethodByName(\"String\"); ok {\n\t\tif !equalMethod.Func.IsNil() &&\n\t\t\tequalMethod.Type.NumIn() == 1 &&\n\t\t\tequalMethod.Type.In(0) == v.Type() &&\n\t\t\tequalMethod.Type.NumOut() == 1 &&\n\t\t\tequalMethod.Type.Out(0).Kind() == reflect.String {\n\t\t\tout := v.MethodByName(\"String\").Call([]reflect.Value{})\n\t\t\treturn out[0].String()\n\t\t}\n\t}\n\treturn fmt.Sprintf(\"%s\", v)\n}\n\n// Recursively check that the expected and actual Values are equal in value.\n// If the two Values are equal in type and contain an appropriate Equal()\n// method (see equalMethodOf()), that method is used for comparison. The\n// provided testing.T is failed with a message if any inequality is found.\nfunc checkEqual(t *testing.T, fieldName string, expected, actual reflect.Value) {\n\tif expected.IsValid() && !actual.IsValid() {\n\t\tt.Fatalf(\"%s: %s was unexpectedly nil\\n\", t.Name(), fieldName)\n\t} else if !expected.IsValid() && actual.IsValid() {\n\t\tt.Fatalf(\"%s: Expected %s to be nil (it wasn't)\\n\", t.Name(), fieldName)\n\t} else if !expected.IsValid() && !actual.IsValid() {\n\t\treturn\n\t}\n\n\tif expected.Type() != actual.Type() {\n\t\tt.Fatalf(\"%s: Expected %s type for %s, found %s\\n\", t.Name(), expected.Type(), fieldName, actual.Type())\n\t}\n\n\tequalMethod := equalMethodOf(expected)\n\tif equalMethod.IsValid() {\n\t\tin := []reflect.Value{actual}\n\t\tout := equalMethod.Call(in)\n\t\tif !out[0].Bool() {\n\t\t\tt.Fatalf(\"%s: %s !Equal(): expected '%s', got '%s'\\n\", t.Name(), fieldName, valueToString(expected), valueToString(actual))\n\t\t}\n\t\treturn\n\t}\n\n\tswitch expected.Kind() {\n\tcase reflect.Array:\n\t\tfor i := 0; i < expected.Len(); i++ {\n\t\t\tcheckEqual(t, fmt.Sprintf(\"%s[%d]\", fieldName, i), expected.Index(i), actual.Index(i))\n\t\t}\n\tcase reflect.Slice:\n\t\tif !expected.IsNil() && actual.IsNil() {\n\t\t\tt.Fatalf(\"%s: %s was unexpectedly nil\\n\", t.Name(), fieldName)\n\t\t} else if expected.IsNil() && !actual.IsNil() {\n\t\t\tt.Fatalf(\"%s: Expected %s to be nil (it wasn't)\\n\", t.Name(), fieldName)\n\t\t}\n\t\tif expected.Len() != actual.Len() {\n\t\t\tt.Fatalf(\"%s: Expected len(%s) to to be %d, was %d\\n\", t.Name(), fieldName, expected.Len(), actual.Len())\n\t\t}\n\t\tfor i := 0; i < expected.Len(); i++ {\n\t\t\tcheckEqual(t, fmt.Sprintf(\"%s[%d]\", fieldName, i), expected.Index(i), actual.Index(i))\n\t\t}\n\tcase reflect.Interface:\n\t\tif !expected.IsNil() && actual.IsNil() {\n\t\t\tt.Fatalf(\"%s: %s was unexpectedly nil\\n\", t.Name(), fieldName)\n\t\t} else if expected.IsNil() && !actual.IsNil() {\n\t\t\tt.Fatalf(\"%s: Expected %s to be nil (it wasn't)\\n\", t.Name(), fieldName)\n\t\t}\n\t\tcheckEqual(t, fieldName, expected.Elem(), actual.Elem())\n\tcase reflect.Ptr:\n\t\tcheckEqual(t, fieldName, expected.Elem(), actual.Elem())\n\tcase reflect.Struct:\n\t\tstructType := expected.Type()\n\t\tfor i, n := 0, expected.NumField(); i < n; i++ {\n\t\t\tfield := structType.Field(i)\n\t\t\t// skip XMLName fields so we can be lazy and not fill them out in\n\t\t\t// testing code\n\t\t\tvar xmlname xml.Name\n\t\t\tif field.Name == \"XMLName\" && field.Type == reflect.TypeOf(xmlname) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Construct a new field name for this field, containing the parent\n\t\t\t// fieldName\n\t\t\tnewFieldName := fieldName\n\t\t\tif fieldName != \"\" {\n\t\t\t\tnewFieldName = fieldName + \".\"\n\t\t\t}\n\t\t\tnewFieldName = newFieldName + field.Name\n\t\t\tcheckEqual(t, newFieldName, expected.Field(i), actual.Field(i))\n\t\t}\n\tcase reflect.String:\n\t\tif expected.String() != actual.String() {\n\t\t\tt.Fatalf(\"%s: %s expected to be '%s', found '%s'\\n\", t.Name(), fieldName, expected.String(), actual.String())\n\t\t}\n\tcase reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:\n\t\tif expected.Uint() != actual.Uint() {\n\t\t\tt.Fatalf(\"%s: %s expected to be '%s', found '%s'\\n\", t.Name(), fieldName, valueToString(expected), valueToString(actual))\n\t\t}\n\tdefault:\n\t\tt.Fatalf(\"%s: %s has unexpected type that didn't provide an Equal() method: %s\\n\", t.Name(), fieldName, expected.Type().Name())\n\t}\n}\n\nfunc checkResponsesEqual(t *testing.T, expected, actual *Response) {\n\tcheckEqual(t, \"\", reflect.ValueOf(expected), reflect.ValueOf(actual))\n}\n\nfunc checkResponseRoundTrip(t *testing.T, response *Response) {\n\tb, err := response.Marshal()\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error re-marshaling OFX response: %s\\n\", err)\n\t}\n\troundtripped, err := ParseResponse(b)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error re-parsing OFX response: %s\\n\", err)\n\t}\n\tcheckResponsesEqual(t, response, roundtripped)\n}\n\n// Ensure that these samples both parse without errors, and can be converted\n// back and forth without changing.\nfunc TestValidSamples(t *testing.T) {\n\tfn := func(path string, info os.FileInfo, err error) error {\n\t\tif info.IsDir() {\n\t\t\treturn nil\n\t\t} else if ext := filepath.Ext(path); ext != \".ofx\" && ext != \".qfx\" {\n\t\t\treturn nil\n\t\t}\n\t\tfile, err := os.Open(path)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Unexpected error opening %s: %s\\n\", path, err)\n\t\t}\n\t\tresponse, err := ParseResponse(file)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Unexpected error parsing OFX response in %s: %s\\n\", path, err)\n\t\t}\n\t\tcheckResponseRoundTrip(t, response)\n\t\treturn nil\n\t}\n\tfilepath.Walk(\"samples/valid_responses\", fn)\n\tfilepath.Walk(\"samples/busted_responses\", fn)\n}\n\nfunc TestInvalidResponse(t *testing.T) {\n\t// in this example, the severity is invalid due to mixed upper and lower case letters\n\tconst invalidResponse = `OFXHEADER:100\nDATA:OFXSGML\nVERSION:102\nSECURITY:NONE\nENCODING:USASCII\nCHARSET:1252\nCOMPRESSION:NONE\nOLDFILEUID:NONE\nNEWFILEUID:NONE\n\n<OFX>\n\t<SIGNONMSGSRSV1>\n\t\t<SONRS>\n\t\t\t<STATUS>\n\t\t\t\t<CODE>0</CODE>\n\t\t\t\t<SEVERITY>Info</SEVERITY>\n\t\t\t</STATUS>\n\t\t\t<LANGUAGE>ENG</LANGUAGE>\n\t\t</SONRS>\n\t</SIGNONMSGSRSV1>\n\t<BANKMSGSRSV1>\n\t\t<STMTTRNRS>\n\t\t\t<TRNUID>0</TRNUID>\n\t\t\t<STATUS>\n\t\t\t\t<CODE>0</CODE>\n\t\t\t\t<SEVERITY>Info</SEVERITY>\n\t\t\t</STATUS>\n\t\t</STMTTRNRS>\n\t</BANKMSGSRSV1>\n</OFX>\n`\n\tconst expectedErr = \"Validation failed: Invalid STATUS>SEVERITY; Invalid STATUS>SEVERITY\"\n\n\tt.Run(\"parse response\", func(t *testing.T) {\n\t\tresp, err := ParseResponse(bytes.NewReader([]byte(invalidResponse)))\n\t\texpectedErr := \"Validation failed: Invalid STATUS>SEVERITY; Invalid STATUS>SEVERITY\"\n\t\tif err == nil {\n\t\t\tt.Fatalf(\"ParseResponse should fail with %q, found nil\", expectedErr)\n\t\t}\n\t\tif _, ok := err.(errInvalid); !ok {\n\t\t\tt.Errorf(\"ParseResponse should return an error with type ErrInvalid, found %T\", err)\n\t\t}\n\t\tif err.Error() != expectedErr {\n\t\t\tt.Errorf(\"ParseResponse should fail with %q, found %v\", expectedErr, err)\n\t\t}\n\t\tif resp == nil {\n\t\t\tt.Errorf(\"Response must not be nil if only validation errors are present\")\n\t\t}\n\t})\n\n\tt.Run(\"parse failed\", func(t *testing.T) {\n\t\tresp, err := ParseResponse(bytes.NewReader(nil))\n\t\tif err == nil {\n\t\t\tt.Error(\"ParseResponse should fail to decode\")\n\t\t}\n\t\tif resp != nil {\n\t\t\tt.Errorf(\"ParseResponse should return a nil response, found: %v\", resp)\n\t\t}\n\t})\n\n\tt.Run(\"decode, then validate response\", func(t *testing.T) {\n\t\tresp, err := DecodeResponse(bytes.NewReader([]byte(invalidResponse)))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error: %s\", err.Error())\n\t\t}\n\t\tif resp == nil {\n\t\t\tt.Fatal(\"Response should not be nil from successful decode\")\n\t\t}\n\t\tvalid, err := resp.Valid()\n\t\tif valid {\n\t\t\tt.Error(\"Response should not be valid\")\n\t\t}\n\t\tif err == nil {\n\t\t\tt.Fatalf(\"response.Valid() should fail with %q, found nil\", expectedErr)\n\t\t}\n\t\tif _, ok := err.(errInvalid); !ok {\n\t\t\tt.Errorf(\"response.Valid() should return an error of type ErrInvalid, found: %T\", err)\n\t\t}\n\t\tif err.Error() != expectedErr {\n\t\t\tt.Errorf(\"response.Valid() should return an error with message %q, but found %q\", expectedErr, err.Error())\n\t\t}\n\t})\n}\n\nfunc TestErrInvalidError(t *testing.T) {\n\texpectedErr := `Validation failed: A; B; C`\n\tactualErr := errInvalid{\n\t\terrors.New(\"A\"),\n\t\terrors.New(\"B\"),\n\t\terrors.New(\"C\"),\n\t}.Error()\n\tif expectedErr != actualErr {\n\t\tt.Errorf(\"Unexpected invalid error message to be %q, but was: %s\", expectedErr, actualErr)\n\t}\n}\n\nfunc TestErrInvalidAddErr(t *testing.T) {\n\tt.Run(\"nil error should be a no-op\", func(t *testing.T) {\n\t\tvar errs errInvalid\n\t\terrs.AddErr(nil)\n\t\tif len(errs) != 0 {\n\t\t\tt.Errorf(\"Nil err should not be added\")\n\t\t}\n\t})\n\n\tt.Run(\"adds an error normally\", func(t *testing.T) {\n\t\tvar errs errInvalid\n\t\terrs.AddErr(errors.New(\"some error\"))\n\n\t})\n\n\tt.Run(\"adding the same type should flatten the errors\", func(t *testing.T) {\n\t\tvar errs errInvalid\n\t\terrs.AddErr(errInvalid{\n\t\t\terrors.New(\"A\"),\n\t\t\terrors.New(\"B\"),\n\t\t})\n\t\terrs.AddErr(errInvalid{\n\t\t\terrors.New(\"C\"),\n\t\t})\n\t\tif len(errs) != 3 {\n\t\t\tt.Errorf(\"Errors should be flattened like [A, B, C], but found: %+v\", errs)\n\t\t}\n\t})\n}\n\nfunc TestErrInvalidErrOrNil(t *testing.T) {\n\tvar errs errInvalid\n\tif err := errs.ErrOrNil(); err != nil {\n\t\tt.Errorf(\"No added errors should return nil, found: %v\", err)\n\t}\n\tsomeError := errors.New(\"some error\")\n\terrs.AddErr(someError)\n\terr := errs.ErrOrNil()\n\tif err == nil {\n\t\tt.Fatal(\"Expected an error, found nil.\")\n\t}\n\tif _, ok := err.(errInvalid); !ok {\n\t\tt.Fatalf(\"Expected err to be of type errInvalid, found: %T\", err)\n\t}\n\terrInv := err.(errInvalid)\n\tif len(errInv) != 1 || errInv[0] != someError {\n\t\tt.Errorf(\"Expected ErrOrNil to return itself, found: %v\", err)\n\t}\n}\n"
  },
  {
    "path": "samples/busted_responses/bmo_v102__no_header_newline.qfx",
    "content": "OFXHEADER:100\nDATA:OFXSGML\nVERSION:102\nSECURITY:NONE\nENCODING:USASCII\nCHARSET:1252\nCOMPRESSION:NONE\nOLDFILEUID:NONE\nNEWFILEUID:NONE\n<OFX>\n<SIGNONMSGSRSV1>\n<SONRS>\n<STATUS>\n<CODE>0\n<SEVERITY>INFO\n<MESSAGE>OK\n</STATUS>\n<DTSERVER>20181202184906.217[-5:EDT]\n<USERKEY>SJLDF802DV09DF80\n<LANGUAGE>ENG\n<INTU.BID>00017\n</SONRS>\n</SIGNONMSGSRSV1>\n<CREDITCARDMSGSRSV1>\n<CCSTMTTRNRS>\n<TRNUID>1\n<STATUS>\n<CODE>0\n<SEVERITY>INFO\n<MESSAGE>OK\n</STATUS>\n<CCSTMTRS>\n<CURDEF>CAD\n<CCACCTFROM>\n<ACCTID>2380370270281083\n</CCACCTFROM>\n<BANKTRANLIST>\n<DTSTART>20181202184905.909[-5:EDT]\n<DTEND>20181202184905.909[-5:EDT]\n<STMTTRN>\n<TRNTYPE>CREDIT\n<DTPOSTED>20181030000000.000[-5:EDT]\n<TRNAMT>2042.24\n<FITID>2380370270281083201810302054456\n<NAME>PAYMENT RECEIVED - THANK YOU\n</STMTTRN>\n</BANKTRANLIST>\n<LEDGERBAL>\n<BALAMT>-552.63\n<DTASOF>20181202184906.217[-5:EDT]\n</LEDGERBAL>\n<AVAILBAL>\n<BALAMT>-552.63\n<DTASOF>20181202184906.217[-5:EDT]\n</AVAILBAL>\n</CCSTMTRS>\n</CCSTMTTRNRS>\n</CREDITCARDMSGSRSV1>\n</OFX>\n"
  },
  {
    "path": "samples/busted_responses/wellsfargo.qfx",
    "content": "OFXHEADER:100DATA:OFXSGMLVERSION:102SECURITY:NONEENCODING:USASCIICHARSET:1252COMPRESSION:NONEOLDFILEUID:NONENEWFILEUID:NONE<OFX><SIGNONMSGSRSV1><SONRS><STATUS><CODE>0<SEVERITY>INFO<MESSAGE>SUCCESS</STATUS><DTSERVER>20210102211014.201[-8:PST]<LANGUAGE>ENG<FI><ORG>WF<FID>1000</FI><SESSCOOKIE>abc-123<INTU.BID>1000<INTU.USERID>jane_doe</SONRS></SIGNONMSGSRSV1><BANKMSGSRSV1><STMTTRNRS><TRNUID>0<STATUS><CODE>0<SEVERITY>INFO<MESSAGE>SUCCESS</STATUS><STMTRS><CURDEF>USD<BANKACCTFROM><BANKID>123456789<ACCTID>9876543210<ACCTTYPE>CHECKING</BANKACCTFROM><BANKTRANLIST><DTSTART>20201201120000.000[-8:PST]<DTEND>20201231120000.000[-8:PST]<STMTTRN><TRNTYPE>DIRECTDEBIT<DTPOSTED>20201201120000.000[-8:PST]<TRNAMT>-12.34<FITID>202012011<NAME>AE Visa Card AE EPAY<MEMO> XXXXX1234</STMTTRN></BANKTRANLIST><LEDGERBAL><BALAMT>123.45<DTASOF>20201231120000.000[-8:PST]</LEDGERBAL><AVAILBAL><BALAMT>123.45<DTASOF>20201231120000.000[-8:PST]</AVAILBAL></STMTRS></STMTTRNRS></BANKMSGSRSV1></OFX>"
  },
  {
    "path": "samples/valid_responses/401k_v203.ofx",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><?OFX OFXHEADER=\"200\" VERSION=\"203\" SECURITY=\"NONE\" OLDFILEUID=\"NONE\" NEWFILEUID=\"NONE\"?><OFX><SIGNONMSGSRSV1> <SONRS> <STATUS><CODE>0</CODE><SEVERITY>INFO</SEVERITY><MESSAGE>SUCCESS</MESSAGE></STATUS><DTSERVER>20170406202132.828[-4:EDT]</DTSERVER><LANGUAGE>ENG</LANGUAGE><FI><ORG>SAHWI</ORG><FID>8133</FID></FI></SONRS></SIGNONMSGSRSV1><INVSTMTMSGSRSV1> <INVSTMTTRNRS><TRNUID>c8e9b92c-bf70-4145-9c24-c85284b20d3c</TRNUID> <STATUS><CODE>0</CODE><SEVERITY>INFO</SEVERITY><MESSAGE>SUCCESS</MESSAGE></STATUS><INVSTMTRS><DTASOF>20170406192133.020[-4:EDT]</DTASOF><CURDEF>USD</CURDEF><INVACCTFROM><BROKERID>fi.example.com</BROKERID><ACCTID>79451753939</ACCTID></INVACCTFROM> <INVTRANLIST><DTSTART>20170105192132.950[-5:EST]</DTSTART><DTEND>20170405202132.950[-4:EDT]</DTEND><BUYMF><INVBUY><INVTRAN><FITID>4efe0ca7-0f02-40fa-a064-aba0613bd6bb</FITID><DTTRADE>20170106070000.000[-5:EST]</DTTRADE><MEMO>CONTRIBUTION;VANGUARD TARGET 2045 OAEL;as of 01/06/2017</MEMO></INVTRAN><SECID><UNIQUEID>OAEL</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>1451.8174914388237</UNITS><UNITPRICE>-1511.6547253613091</UNITPRICE><TOTAL>3909.809828287014</TOTAL><SUBACCTSEC>OTHER</SUBACCTSEC><SUBACCTFUND>OTHER</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE>    </BUYMF><BUYMF><INVBUY><INVTRAN><FITID>c91ded6a-7587-4e12-9981-b41ad8a10b11</FITID><DTTRADE>20170120070000.000[-5:EST]</DTTRADE><MEMO>CONTRIBUTION;VANGUARD TARGET 2045 OAEL;as of 01/20/2017</MEMO></INVTRAN><SECID><UNIQUEID>OAEL</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>1698.133578625033</UNITS><UNITPRICE>1727.6765303967318</UNITPRICE><TOTAL>3577.2910257455405</TOTAL><SUBACCTSEC>OTHER</SUBACCTSEC><SUBACCTFUND>OTHER</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE>    </BUYMF><BUYMF><INVBUY><INVTRAN><FITID>d4e8b94d-0601-450c-b862-e3eb4ab9bcf7</FITID><DTTRADE>20170203070000.000[-5:EST]</DTTRADE><MEMO>CONTRIBUTION;VANGUARD TARGET 2045 OAEL;as of 02/03/2017</MEMO></INVTRAN><SECID><UNIQUEID>OAEL</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>-861.0104210832337</UNITS><UNITPRICE>4091.2289289557075</UNITPRICE><TOTAL>258.62764651238285</TOTAL><SUBACCTSEC>OTHER</SUBACCTSEC><SUBACCTFUND>OTHER</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE>    </BUYMF><BUYMF><INVBUY><INVTRAN><FITID>ce27d094-83fb-47c4-9cc7-942b8fa90f2a</FITID><DTTRADE>20170217070000.000[-5:EST]</DTTRADE><MEMO>CONTRIBUTION;VANGUARD TARGET 2045 OAEL;as of 02/17/2017</MEMO></INVTRAN><SECID><UNIQUEID>OAEL</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>1483.633960187689</UNITS><UNITPRICE>4962.56590437185</UNITPRICE><TOTAL>4894.689514323327</TOTAL><SUBACCTSEC>OTHER</SUBACCTSEC><SUBACCTFUND>OTHER</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE>    </BUYMF><BUYMF><INVBUY><INVTRAN><FITID>2c678c32-bdb2-411c-95be-ce4b0b22476c</FITID><DTTRADE>20170303070000.000[-5:EST]</DTTRADE><MEMO>CONTRIBUTION;VANGUARD TARGET 2045 OAEL;as of 03/03/2017</MEMO></INVTRAN><SECID><UNIQUEID>OAEL</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>4820.263907020751</UNITS><UNITPRICE>4533.508051253512</UNITPRICE><TOTAL>4586.446085462384</TOTAL><SUBACCTSEC>OTHER</SUBACCTSEC><SUBACCTFUND>OTHER</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE>    </BUYMF><BUYMF><INVBUY><INVTRAN><FITID>87f95061-6eb8-4417-872d-b6b50d59b828</FITID><DTTRADE>20170317070000.000[-4:EDT]</DTTRADE><MEMO>CONTRIBUTION;VANGUARD TARGET 2045 OAEL;as of 03/17/2017</MEMO></INVTRAN><SECID><UNIQUEID>OAEL</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>398.12130359284674</UNITS><UNITPRICE>4462.870483725029</UNITPRICE><TOTAL>-980.7454839221953</TOTAL><SUBACCTSEC>OTHER</SUBACCTSEC><SUBACCTFUND>OTHER</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE>    </BUYMF><BUYMF><INVBUY><INVTRAN><FITID>8d82af65-1ec2-4617-a58e-66f9f1f17a1b</FITID><DTTRADE>20170331070000.000[-4:EDT]</DTTRADE><MEMO>CONTRIBUTION;VANGUARD TARGET 2045 OAEL;as of 03/31/2017</MEMO></INVTRAN><SECID><UNIQUEID>OAEL</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>-1675.047591144809</UNITS><UNITPRICE>3398.755275302541</UNITPRICE><TOTAL>4137.105758433049</TOTAL><SUBACCTSEC>OTHER</SUBACCTSEC><SUBACCTFUND>OTHER</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE>    </BUYMF></INVTRANLIST> <INVPOSLIST><POSMF><INVPOS><SECID><UNIQUEID>OAEL</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><HELDINACCT>OTHER</HELDINACCT><POSTYPE>LONG</POSTYPE><UNITS>3508.7072431017987</UNITS><UNITPRICE>4265.298363227409</UNITPRICE><MKTVAL>-1285.6893734826635</MKTVAL>    <DTPRICEASOF>20170405160000.000[-4:EDT]</DTPRICEASOF>   <MEMO>Market close as of 04/05/2017;VANGUARD TARGET 2045</MEMO></INVPOS></POSMF></INVPOSLIST> <INVBAL><AVAILCASH>3118</AVAILCASH><MARGINBALANCE>2360</MARGINBALANCE><SHORTBALANCE>2408</SHORTBALANCE><BALLIST><BAL><NAME>MarketValue</NAME><DESC>MarketValue</DESC><BALTYPE>DOLLAR</BALTYPE><VALUE>238.6182750915259</VALUE><DTASOF>20170406192133.020[-4:EDT]</DTASOF></BAL><BAL><NAME>VestedValue</NAME><DESC>VestedValue</DESC><BALTYPE>DOLLAR</BALTYPE><VALUE>4769.862117574797</VALUE><DTASOF>20170406192133.020[-4:EDT]</DTASOF></BAL><BAL><NAME>TotalAssetsValue</NAME><DESC>TotalAssetsValue</DESC><BALTYPE>DOLLAR</BALTYPE><VALUE>3159.3943094763545</VALUE><DTASOF>20170406192133.020[-4:EDT]</DTASOF></BAL></BALLIST></INVBAL><INV401K><EMPLOYERNAME>QC 401(K) PLAN</EMPLOYERNAME></INV401K><INV401KBAL><TOTAL>2951.191737953516</TOTAL><BALLIST><BAL><NAME>MarketValue</NAME><DESC>MarketValue</DESC><BALTYPE>DOLLAR</BALTYPE><VALUE>1855.56557652199</VALUE><DTASOF>20170406192133.020[-4:EDT]</DTASOF></BAL><BAL><NAME>VestedValue</NAME><DESC>VestedValue</DESC><BALTYPE>DOLLAR</BALTYPE><VALUE>1535.8298944440207</VALUE><DTASOF>20170406192133.020[-4:EDT]</DTASOF></BAL><BAL><NAME>TotalAssetsValue</NAME><DESC>TotalAssetsValue</DESC><BALTYPE>DOLLAR</BALTYPE><VALUE>62.4904063375684</VALUE><DTASOF>20170406192133.020[-4:EDT]</DTASOF></BAL></BALLIST></INV401KBAL></INVSTMTRS></INVSTMTTRNRS></INVSTMTMSGSRSV1> <SECLISTMSGSRSV1><SECLIST><MFINFO><SECINFO><SECID><UNIQUEID>OAEL</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SECNAME>VANGUARD TARGET 2045</SECNAME><FIID>OAEL</FIID><UNITPRICE>-281.3371623586104</UNITPRICE><DTASOF>20170405160000.000[-4:EDT]</DTASOF><MEMO>Market close as of 04/05/2017;VANGUARD TARGET 2045</MEMO></SECINFO><MFTYPE>OTHER</MFTYPE></MFINFO></SECLIST></SECLISTMSGSRSV1></OFX>"
  },
  {
    "path": "samples/valid_responses/inv_v202.ofx",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\" ?><?OFX OFXHEADER=\"200\" VERSION=\"202\" SECURITY=\"NONE\" OLDFILEUID=\"NONE\" NEWFILEUID=\"NONE\"?><OFX><SIGNONMSGSRSV1><SONRS><STATUS><CODE>0</CODE><SEVERITY>INFO</SEVERITY><MESSAGE>Successful Sign On</MESSAGE></STATUS><DTSERVER>20170406192303[-5:EST]</DTSERVER><LANGUAGE>ENG</LANGUAGE><DTPROFUP>20140605083000</DTPROFUP><FI><ORG>TLILW</ORG><FID>1055</FID></FI><SESSCOOKIE>06NCJ.wUGTRH0oti9WlMhf3pe_rqXxd1ukz2</SESSCOOKIE></SONRS></SIGNONMSGSRSV1><INVSTMTMSGSRSV1><INVSTMTTRNRS><TRNUID>dd4ac034-448a-4613-b91d-984928345d6c</TRNUID><STATUS><CODE>0</CODE><SEVERITY>INFO</SEVERITY></STATUS><INVSTMTRS><DTASOF>20170406160000.000[-5:EST]</DTASOF><CURDEF>USD</CURDEF><INVACCTFROM><BROKERID>fi.example.com</BROKERID><ACCTID>13971288918</ACCTID></INVACCTFROM><INVTRANLIST><DTSTART>20151006160000.000[-5:EST]160000.000[-5:EST]</DTSTART><DTEND>20170406192303.000[-5:EST]</DTEND><BUYMF><INVBUY><INVTRAN><FITID>de647ad6-38fb-431e-aac4-34c5f13c420e</FITID><DTTRADE>20170315160000.000[-5:EST]</DTTRADE><DTSETTLE>20170316160000.000[-5:EST]</DTSETTLE><MEMO>BUY</MEMO></INVTRAN><SECID><UNIQUEID>921937108</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>3824.9561497776986</UNITS><UNITPRICE>2859.475796083826</UNITPRICE><TOTAL>-943.5043319408105</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE></BUYMF><BUYMF><INVBUY><INVTRAN><FITID>714ccb86-c5a7-4c9d-9cf9-8f32ef1e679f</FITID><DTTRADE>20160606160000.000[-5:EST]</DTTRADE><DTSETTLE>20160607160000.000[-5:EST]</DTSETTLE><MEMO>BUY</MEMO></INVTRAN><SECID><UNIQUEID>922908728</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>-936.6718270646302</UNITS><UNITPRICE>-1111.9306674701193</UNITPRICE><TOTAL>1881.084138894339</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE></BUYMF><BUYMF><INVBUY><INVTRAN><FITID>4cb6dcae-cb86-4344-8570-a9c5ec9708d7</FITID><DTTRADE>20160708160000.000[-5:EST]</DTTRADE><DTSETTLE>20160711160000.000[-5:EST]</DTSETTLE><MEMO>BUY</MEMO></INVTRAN><SECID><UNIQUEID>922908728</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>699.4447046891851</UNITS><UNITPRICE>363.8027792569965</UNITPRICE><TOTAL>3795.512144054378</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE></BUYMF><BUYMF><INVBUY><INVTRAN><FITID>efaddcc4-cede-4bef-b9ef-7da02d544a3a</FITID><DTTRADE>20160624160000.000[-5:EST]</DTTRADE><DTSETTLE>20160627160000.000[-5:EST]</DTSETTLE><MEMO>BUY</MEMO></INVTRAN><SECID><UNIQUEID>921909602</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>-494.01115906217296</UNITS><UNITPRICE>49.39274242653801</UNITPRICE><TOTAL>261.55338688051506</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE></BUYMF><BUYMF><INVBUY><INVTRAN><FITID>a21af8fb-6054-439c-9a26-2e14ba934160</FITID><DTTRADE>20160708160000.000[-5:EST]</DTTRADE><DTSETTLE>20160711160000.000[-5:EST]</DTSETTLE><MEMO>BUY</MEMO></INVTRAN><SECID><UNIQUEID>921909602</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>3736.3895069700466</UNITS><UNITPRICE>3698.7829383158605</UNITPRICE><TOTAL>-1897.0973450115864</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE></BUYMF><BUYMF><INVBUY><INVTRAN><FITID>8de3b4f5-ad22-4805-8fd1-f72344e86300</FITID><DTTRADE>20160322160000.000[-5:EST]</DTTRADE><DTSETTLE>20160322160000.000[-5:EST]</DTSETTLE><MEMO>MONEY FUND PURCHASE</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>3221.5665716980166</UNITS><UNITPRICE>705.5007864208351</UNITPRICE><TOTAL>-1322.9060478923284</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE></BUYMF><BUYMF><INVBUY><INVTRAN><FITID>ad28b781-8372-416a-9755-61e2d44489be</FITID><DTTRADE>20160331160000.000[-5:EST]</DTTRADE><DTSETTLE>20160331160000.000[-5:EST]</DTSETTLE><MEMO>MONEY FUND PURCHASE</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>3674.003918319091</UNITS><UNITPRICE>-1435.673365926304</UNITPRICE><TOTAL>-362.7565458501381</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE></BUYMF><BUYMF><INVBUY><INVTRAN><FITID>32993fde-e2db-4f79-8d4c-dcc49cdcb72c</FITID><DTTRADE>20160412160000.000[-5:EST]</DTTRADE><DTSETTLE>20160412160000.000[-5:EST]</DTSETTLE><MEMO>MONEY FUND PURCHASE</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>-495.00661745345815</UNITS><UNITPRICE>-1274.7417304464868</UNITPRICE><TOTAL>1551.8392618390644</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE></BUYMF><BUYMF><INVBUY><INVTRAN><FITID>8e5edf9c-8c21-4501-ab59-4415cf4170f6</FITID><DTTRADE>20160623160000.000[-5:EST]</DTTRADE><DTSETTLE>20160623160000.000[-5:EST]</DTSETTLE><MEMO>MONEY FUND PURCHASE</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>3515.9628441137</UNITS><UNITPRICE>-1787.7474642873335</UNITPRICE><TOTAL>-1818.8440342775998</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE></BUYMF><BUYMF><INVBUY><INVTRAN><FITID>b4d01c4c-e1ef-409d-9f48-2b644d9d848f</FITID><DTTRADE>20160629160000.000[-5:EST]</DTTRADE><DTSETTLE>20160629160000.000[-5:EST]</DTSETTLE><MEMO>MONEY FUND PURCHASE</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>1225.4461429600337</UNITS><UNITPRICE>366.5165936332555</UNITPRICE><TOTAL>1153.2510138507491</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE></BUYMF><BUYMF><INVBUY><INVTRAN><FITID>eec03d0c-6453-4a53-b0a8-51f702e949aa</FITID><DTTRADE>20160713160000.000[-5:EST]</DTTRADE><DTSETTLE>20160713160000.000[-5:EST]</DTSETTLE><MEMO>MONEY FUND PURCHASE</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>2498.406083177002</UNITS><UNITPRICE>-1208.3010162822115</UNITPRICE><TOTAL>1600.132544574224</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE></BUYMF><BUYMF><INVBUY><INVTRAN><FITID>502d83b5-ffa8-48bb-934c-56325c48a81f</FITID><DTTRADE>20160729160000.000[-5:EST]</DTTRADE><DTSETTLE>20160729160000.000[-5:EST]</DTSETTLE><MEMO>MONEY FUND PURCHASE</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>4993.923954128822</UNITS><UNITPRICE>1707.5331796630198</UNITPRICE><TOTAL>3649.7280516953333</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE></BUYMF><BUYSTOCK><INVBUY><INVTRAN><FITID>5cafa134-09d3-4b3f-a1f1-025cd8adbae5</FITID><DTTRADE>20160620160000.000[-5:EST]</DTTRADE><DTSETTLE>20160623160000.000[-5:EST]</DTSETTLE><MEMO>BUY</MEMO></INVTRAN><SECID><UNIQUEID>921909768</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>797.5765771818847</UNITS><UNITPRICE>2662.72040200787</UNITPRICE><TOTAL>-275.77051479619445</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE></BUYSTOCK><BUYSTOCK><INVBUY><INVTRAN><FITID>e81bfeb2-499e-4f99-bbf6-79d53f20147d</FITID><DTTRADE>20160407160000.000[-5:EST]</DTTRADE><DTSETTLE>20160412160000.000[-5:EST]</DTSETTLE><MEMO>BUY</MEMO></INVTRAN><SECID><UNIQUEID>922908769</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>970.9179101188292</UNITS><UNITPRICE>-1998.7249049199902</UNITPRICE><TOTAL>-164.63123293080207</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE></BUYSTOCK><BUYSTOCK><INVBUY><INVTRAN><FITID>e8be8c30-ebe5-4569-b28e-fb70879c1ccd</FITID><DTTRADE>20160620160000.000[-5:EST]</DTTRADE><DTSETTLE>20160623160000.000[-5:EST]</DTSETTLE><MEMO>BUY</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>3873.497618552873</UNITS><UNITPRICE>3512.5093763919913</UNITPRICE><TOTAL>3108.422217648994</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE></BUYSTOCK><INCOME><INVTRAN><FITID>b9d4de3a-4adc-4a8f-a105-65309d891aec</FITID><DTTRADE>20160729160000.000[-5:EST]</DTTRADE><DTSETTLE>20160729160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND PAYMENTDIVIDEND PAYMENT</MEMO></INVTRAN><SECID><UNIQUEID>78462F103</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>-1477.6134874063105</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INCOME><INCOME><INVTRAN><FITID>b9bb3cd8-def2-4d56-802e-ec568e59d528</FITID><DTTRADE>20160627160000.000[-5:EST]</DTTRADE><DTSETTLE>20160627160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND PAYMENTDIVIDEND PAYMENT</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>1751.5484207314612</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INCOME><REINVEST><INVTRAN><FITID>68dd7706-2643-4536-a393-559358d41f3b</FITID><DTTRADE>20160606160000.000[-5:EST]</DTTRADE><DTSETTLE>20160606160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>049560105</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>4665.123209260046</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>687.4062074118997</UNITS><UNITPRICE>2608.9777289608946</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>767d3192-8adb-432d-a497-d8a8194976bd</FITID><DTTRADE>20160906160000.000[-5:EST]</DTTRADE><DTSETTLE>20160906160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>049560105</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>3437.8395682876217</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>3896.0034382169033</UNITS><UNITPRICE>281.58504788855</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>0cad8534-c48a-4163-919f-30a4d3387bc7</FITID><DTTRADE>20161212160000.000[-5:EST]</DTTRADE><DTSETTLE>20161212160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>049560105</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>559.222484741992</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>1000.910441584736</UNITS><UNITPRICE>4967.567166366519</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>04ea0cd9-d24d-4c4f-bf83-d292e0cc7e70</FITID><DTTRADE>20170313160000.000[-5:EST]</DTTRADE><DTSETTLE>20170313160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>049560105</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>-804.5795400324632</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>3460.530468110246</UNITS><UNITPRICE>4531.655627986529</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>1715c17f-4196-4869-ae4c-74bfda77cdf6</FITID><DTTRADE>20170331160000.000[-5:EST]</DTTRADE><DTSETTLE>20170331160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921937108</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>-199.0248089881802</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>-896.9373178685921</UNITS><UNITPRICE>3769.556536805486</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>510c52d3-5a36-4a49-bb89-d27a88c38896</FITID><DTTRADE>20160429160000.000[-5:EST]</DTTRADE><DTSETTLE>20160429160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>78462F103</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>3599.617072348834</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>4217.247725936561</UNITS><UNITPRICE>4443.307423188817</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>1bbda50a-4241-4574-8eea-6d701915eaa6</FITID><DTTRADE>20160620160000.000[-5:EST]</DTTRADE><DTSETTLE>20160620160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>922908769</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>4801.656470737234</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>4850.350814371729</UNITS><UNITPRICE>197.02138351123904</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>15f48a59-fa18-4d79-be7b-aa895dace10e</FITID><DTTRADE>20160613160000.000[-5:EST]</DTTRADE><DTSETTLE>20160613160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>922908728</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>4283.192496197073</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>50.384908410886055</UNITS><UNITPRICE>4191.032455604349</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>c0cbe4af-a9cc-414f-a2f3-1c16c7e8ce09</FITID><DTTRADE>20160912160000.000[-5:EST]</DTTRADE><DTSETTLE>20160912160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>922908728</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>2592.639549298905</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>3013.4291202195436</UNITS><UNITPRICE>-1532.8386386523748</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>96c8ba63-b67d-4c01-97dc-4f0197da4e55</FITID><DTTRADE>20161219160000.000[-5:EST]</DTTRADE><DTSETTLE>20161219160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>922908728</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>-840.5784945499015</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>1266.312419760618</UNITS><UNITPRICE>3726.484685708977</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>bb0c2a67-2997-401d-9fec-75ac7da0e25a</FITID><DTTRADE>20170323160000.000[-5:EST]</DTTRADE><DTSETTLE>20170323160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>922908728</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>194.4819761372787</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>569.9490360903501</UNITS><UNITPRICE>4927.735196476322</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>6633eb88-d7be-4852-a253-950b2195a8be</FITID><DTTRADE>20160708160000.000[-5:EST]</DTTRADE><DTSETTLE>20160708160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>-1888.2662493974499</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>1478.387428924409</UNITS><UNITPRICE>-99.46010783475981</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>a5e85d6a-cbe9-4eb1-a8ca-2f2963610f9c</FITID><DTTRADE>20160805160000.000[-5:EST]</DTTRADE><DTSETTLE>20160805160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>-1386.5211858727876</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>1508.1581641848293</UNITS><UNITPRICE>-1378.7609375925254</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>878c9aba-7661-4b62-a6d3-cf87971845f2</FITID><DTTRADE>20160908160000.000[-5:EST]</DTTRADE><DTSETTLE>20160908160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>-1414.0447828528177</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>445.5239214525827</UNITS><UNITPRICE>-172.45635459467644</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>8e41a7e1-d66a-428f-9cda-88faa50f4c11</FITID><DTTRADE>20161007160000.000[-5:EST]</DTTRADE><DTSETTLE>20161007160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>4836.862627205668</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>-1848.7398241240553</UNITS><UNITPRICE>3484.70494148761</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>fd5193a1-50ff-4c77-992b-f121fe18ac9d</FITID><DTTRADE>20161107160000.000[-5:EST]</DTTRADE><DTSETTLE>20161107160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>1674.3790257730475</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>4068.3057004416214</UNITS><UNITPRICE>-458.0712334291586</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>246eefac-86a5-48d9-ac62-88665d4f57cb</FITID><DTTRADE>20161207160000.000[-5:EST]</DTTRADE><DTSETTLE>20161207160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>-697.60213411001</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>1223.1913821397843</UNITS><UNITPRICE>-821.7273826433402</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>ff06f5a6-4806-403b-8091-743dbbe3d663</FITID><DTTRADE>20161229160000.000[-5:EST]</DTTRADE><DTSETTLE>20161229160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>3770.210174013371</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>1489.7888313470862</UNITS><UNITPRICE>815.1762795992504</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>df123aa5-b2c1-48b7-a41e-d475005fc9ca</FITID><DTTRADE>20161229160000.000[-5:EST]</DTTRADE><DTSETTLE>20161229160000.000[-5:EST]</DTSETTLE><MEMO>DIV REINVEST LT CAP GAIN</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>1781.9847395203892</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>-1864.7526784443658</UNITS><UNITPRICE>860.6009700187328</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>db167683-8917-4f21-862a-b2170aebd0ac</FITID><DTTRADE>20161229160000.000[-5:EST]</DTTRADE><DTSETTLE>20161229160000.000[-5:EST]</DTSETTLE><MEMO>DIV REINVEST ST CAP GAIN</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>2851.6793885008674</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>429.0849160317348</UNITS><UNITPRICE>-1222.3552596775967</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>158986ca-ab01-4edf-9312-094c2d1e4025</FITID><DTTRADE>20170207160000.000[-5:EST]</DTTRADE><DTSETTLE>20170207160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>663.9823123641668</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>4899.138916697189</UNITS><UNITPRICE>-1563.802650539355</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>b0925442-08c3-4c67-ad18-ea6d1dd04f2f</FITID><DTTRADE>20170307160000.000[-5:EST]</DTTRADE><DTSETTLE>20170307160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>2744.502124012006</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>1958.1477299349744</UNITS><UNITPRICE>4132.228202840814</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>7df019fd-2285-4b05-88b6-f8ca2a343594</FITID><DTTRADE>20160330160000.000[-5:EST]</DTTRADE><DTSETTLE>20160330160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>464287655</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>-1876.9501097583257</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>1112.6152006675607</UNITS><UNITPRICE>-1613.8151036363959</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>ccc1740a-51c8-4a5c-8a5c-46e4068c92d3</FITID><DTTRADE>20160912160000.000[-5:EST]</DTTRADE><DTSETTLE>20160912160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921909602</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>-747.5029507231845</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>-427.03654986838114</UNITS><UNITPRICE>-833.4083610380633</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>ced017e7-18d8-4fa1-acb4-a0d19f346f6e</FITID><DTTRADE>20161219160000.000[-5:EST]</DTTRADE><DTSETTLE>20161219160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921909602</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>4261.009665361342</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>-388.7065731312666</UNITS><UNITPRICE>2042.101658644944</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>aebcf801-43cf-45bd-91ed-e6400c30fa83</FITID><DTTRADE>20170323160000.000[-5:EST]</DTTRADE><DTSETTLE>20170323160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921909602</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>2465.891796916825</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>4356.4145624090015</UNITS><UNITPRICE>-970.8551166422644</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>8062028a-05cb-4b0e-992a-a5bea0cfaddd</FITID><DTTRADE>20160331160000.000[-5:EST]</DTTRADE><DTSETTLE>20160331160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>258.71226421087067</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>1994.4503222858289</UNITS><UNITPRICE>3550.717727066314</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>db1db72d-9694-42bf-a03c-22e7fed8e13a</FITID><DTTRADE>20160429160000.000[-5:EST]</DTTRADE><DTSETTLE>20160429160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>3688.672913300633</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>3651.4810695887454</UNITS><UNITPRICE>933.9259775180922</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>09955123-3065-4076-880f-601ccc95f58a</FITID><DTTRADE>20160531160000.000[-5:EST]</DTTRADE><DTSETTLE>20160531160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>2666.517573929893</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>-347.17198994326554</UNITS><UNITPRICE>-1311.963332976477</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>a5767318-b4eb-4ffd-a7a3-2cbed4858ea8</FITID><DTTRADE>20160831160000.000[-5:EST]</DTTRADE><DTSETTLE>20160831160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>4951.177346049531</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>4175.989618842068</UNITS><UNITPRICE>-1358.8888703376006</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>73678cfe-03c9-4cdd-ba68-0faca0d0e254</FITID><DTTRADE>20160930160000.000[-5:EST]</DTTRADE><DTSETTLE>20160930160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>-443.4906622325843</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>-1435.7484320772205</UNITS><UNITPRICE>275.57230926506645</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>4c5cbd18-46c0-4202-9396-e61cbca47b8d</FITID><DTTRADE>20161031160000.000[-5:EST]</DTTRADE><DTSETTLE>20161031160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>1605.7657503064056</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>3971.830099850472</UNITS><UNITPRICE>-895.7166526891983</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>b9c97d11-473b-4ebd-bf8d-2acfd697cdba</FITID><DTTRADE>20161130160000.000[-5:EST]</DTTRADE><DTSETTLE>20161130160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>2324.900790886096</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>-458.5953248656965</UNITS><UNITPRICE>-917.8604050931776</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>17ccd8b3-53a2-4263-bfcb-362f65a0cd24</FITID><DTTRADE>20161230160000.000[-5:EST]</DTTRADE><DTSETTLE>20161230160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>-788.0354705690729</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>3433.538315146986</UNITS><UNITPRICE>721.2715150817912</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>f8990096-bb99-4436-872f-b201748a658e</FITID><DTTRADE>20170131160000.000[-5:EST]</DTTRADE><DTSETTLE>20170131160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>3694.493368489051</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>2123.633147710315</UNITS><UNITPRICE>-1842.162661124652</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>c723a4c9-38a5-4177-bb54-27f6d6497148</FITID><DTTRADE>20170228160000.000[-5:EST]</DTTRADE><DTSETTLE>20170228160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>-1686.0597755682554</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>-426.95493832616444</UNITS><UNITPRICE>-1490.5751476938383</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>7c196cd5-b15c-4c28-97d6-662c73de39f1</FITID><DTTRADE>20170331160000.000[-5:EST]</DTTRADE><DTSETTLE>20170331160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>1608.9423884884068</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>2806.670702862508</UNITS><UNITPRICE>-1985.0186597915401</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>83d5c032-b138-4e04-81d3-cd28ae0fb248</FITID><DTTRADE>20160329160000.000[-5:EST]</DTTRADE><DTSETTLE>20160329160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>78464A763</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>4822.357184455312</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>3187.176159151365</UNITS><UNITPRICE>229.30250007374025</UNITPRICE></REINVEST><SELLMF><INVSELL><INVTRAN><FITID>c7c27965-0509-4a2e-80ee-d4fe829f6e03</FITID><DTTRADE>20160607160000.000[-5:EST]</DTTRADE><DTSETTLE>20160607160000.000[-5:EST]</DTSETTLE><MEMO>MONEY FUND REDEMPTION</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>3020.4980179811837</UNITS><UNITPRICE>4057.2553797251203</UNITPRICE><TOTAL>-602.8925974222395</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVSELL><SELLTYPE>SELL</SELLTYPE></SELLMF><SELLMF><INVSELL><INVTRAN><FITID>d4c6df49-3d9b-43b8-aedd-3caab2aff6ec</FITID><DTTRADE>20160627160000.000[-5:EST]</DTTRADE><DTSETTLE>20160627160000.000[-5:EST]</DTSETTLE><MEMO>MONEY FUND REDEMPTION</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>3130.1288139552935</UNITS><UNITPRICE>-1223.5051072925905</UNITPRICE><TOTAL>4764.349342218469</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVSELL><SELLTYPE>SELL</SELLTYPE></SELLMF><SELLMF><INVSELL><INVTRAN><FITID>ddd1cb7d-0404-4355-a62d-3c8176216955</FITID><DTTRADE>20160711160000.000[-5:EST]</DTTRADE><DTSETTLE>20160711160000.000[-5:EST]</DTSETTLE><MEMO>MONEY FUND REDEMPTION</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>3009.691763341514</UNITS><UNITPRICE>1428.7464682968198</UNITPRICE><TOTAL>3738.2011235349337</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVSELL><SELLTYPE>SELL</SELLTYPE></SELLMF><SELLSTOCK><INVSELL><INVTRAN><FITID>c68a0eac-e703-438a-b47c-42f70382dbeb</FITID><DTTRADE>20160708160000.000[-5:EST]</DTTRADE><DTSETTLE>20160713160000.000[-5:EST]</DTSETTLE><MEMO>SELL</MEMO></INVTRAN><SECID><UNIQUEID>921909768</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>651.0422700680319</UNITS><UNITPRICE>1494.6765600163667</UNITPRICE><FEES>2026.1394193465926</FEES><TOTAL>1945.5385333219733</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVSELL><SELLTYPE>SELL</SELLTYPE></SELLSTOCK><SELLSTOCK><INVSELL><INVTRAN><FITID>baa02c1e-6301-46ff-ba04-e1151bea8c2e</FITID><DTTRADE>20160620160000.000[-5:EST]</DTTRADE><DTSETTLE>20160623160000.000[-5:EST]</DTSETTLE><MEMO>SELL</MEMO></INVTRAN><SECID><UNIQUEID>084670702</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>-1263.5789517595033</UNITS><UNITPRICE>-1328.9207615255718</UNITPRICE><COMMISSION>-599.6197666896526</COMMISSION><FEES>-870.6754395021421</FEES><TOTAL>-464.9649670580118</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVSELL><SELLTYPE>SELL</SELLTYPE></SELLSTOCK><SELLSTOCK><INVSELL><INVTRAN><FITID>410f4e07-cf3e-46cf-a006-3cb0e0fa6f8a</FITID><DTTRADE>20160624160000.000[-5:EST]</DTTRADE><DTSETTLE>20160629160000.000[-5:EST]</DTSETTLE><MEMO>SELL</MEMO></INVTRAN><SECID><UNIQUEID>049560105</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>-1347.1262691325437</UNITS><UNITPRICE>3782.7001510165483</UNITPRICE><COMMISSION>2306.016528419612</COMMISSION><FEES>1615.2202825935715</FEES><TOTAL>3318.618515196583</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVSELL><SELLTYPE>SELL</SELLTYPE></SELLSTOCK><SELLSTOCK><INVSELL><INVTRAN><FITID>b4824f9e-0bbc-4d51-b685-e06677998a90</FITID><DTTRADE>20160620160000.000[-5:EST]</DTTRADE><DTSETTLE>20160623160000.000[-5:EST]</DTSETTLE><MEMO>SELL</MEMO></INVTRAN><SECID><UNIQUEID>78462F103</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>3140.745854038385</UNITS><UNITPRICE>3531.648404397405</UNITPRICE><COMMISSION>1906.5936543597913</COMMISSION><FEES>2449.2303779168496</FEES><TOTAL>687.3186255123292</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVSELL><SELLTYPE>SELL</SELLTYPE></SELLSTOCK><SELLSTOCK><INVSELL><INVTRAN><FITID>96b78949-4287-45e7-9fa4-5ab928f1708d</FITID><DTTRADE>20160708160000.000[-5:EST]</DTTRADE><DTSETTLE>20160713160000.000[-5:EST]</DTSETTLE><MEMO>SELL</MEMO></INVTRAN><SECID><UNIQUEID>922908769</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>-572.4558678467724</UNITS><UNITPRICE>860.7465602131565</UNITPRICE><FEES>-1428.278894600494</FEES><TOTAL>4027.049420348547</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVSELL><SELLTYPE>SELL</SELLTYPE></SELLSTOCK><SELLSTOCK><INVSELL><INVTRAN><FITID>19d3b807-2857-4805-9465-d74140a92c49</FITID><DTTRADE>20160620160000.000[-5:EST]</DTTRADE><DTSETTLE>20160623160000.000[-5:EST]</DTSETTLE><MEMO>SELL</MEMO></INVTRAN><SECID><UNIQUEID>464287655</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>3812.8685527163107</UNITS><UNITPRICE>-1887.2736512639467</UNITPRICE><COMMISSION>-1941.1600324868327</COMMISSION><FEES>3342.565983781469</FEES><TOTAL>-152.64856008477113</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVSELL><SELLTYPE>SELL</SELLTYPE></SELLSTOCK><SELLSTOCK><INVSELL><INVTRAN><FITID>8cb56b3e-cb37-4c1d-9389-45568e4ddc5d</FITID><DTTRADE>20160407160000.000[-5:EST]</DTTRADE><DTSETTLE>20160412160000.000[-5:EST]</DTSETTLE><MEMO>SELL</MEMO></INVTRAN><SECID><UNIQUEID>78464A763</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>-767.0641414622553</UNITS><UNITPRICE>1903.954918823295</UNITPRICE><COMMISSION>2900.987838172772</COMMISSION><FEES>-19.953350790284958</FEES><TOTAL>-1724.3355702286553</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVSELL><SELLTYPE>SELL</SELLTYPE></SELLSTOCK><TRANSFER><INVTRAN><FITID>272deeb3-b738-43f4-b767-d98b9b95332b</FITID><DTTRADE>20160321160000.000[-5:EST]</DTTRADE><DTSETTLE>20160321160000.000[-5:EST]</DTSETTLE><MEMO>TRANSFER OF ACCOUNTTRANSFER OF ACCOUNT</MEMO></INVTRAN><SECID><UNIQUEID>084670702</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>1810.3258411454908</UNITS><TFERACTION>IN</TFERACTION><POSTYPE>LONG</POSTYPE></TRANSFER><TRANSFER><INVTRAN><FITID>5ad31721-4669-4e28-99f5-8c0c709847d3</FITID><DTTRADE>20160321160000.000[-5:EST]</DTTRADE><DTSETTLE>20160321160000.000[-5:EST]</DTSETTLE><MEMO>TRANSFER OF ACCOUNTTRANSFER OF ACCOUNT</MEMO></INVTRAN><SECID><UNIQUEID>049560105</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>1792.0252683912558</UNITS><TFERACTION>IN</TFERACTION><POSTYPE>LONG</POSTYPE></TRANSFER><TRANSFER><INVTRAN><FITID>14b2b7cb-52d5-4326-8bae-fa39ef94c1f3</FITID><DTTRADE>20160321160000.000[-5:EST]</DTTRADE><DTSETTLE>20160321160000.000[-5:EST]</DTSETTLE><MEMO>TRANSFER OF ACCOUNTTRANSFER OF ACCOUNT</MEMO></INVTRAN><SECID><UNIQUEID>78462F103</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>696.0835085032941</UNITS><TFERACTION>IN</TFERACTION><POSTYPE>LONG</POSTYPE></TRANSFER><TRANSFER><INVTRAN><FITID>ed56c305-3a72-4402-943f-3bed81cf21a7</FITID><DTTRADE>20160321160000.000[-5:EST]</DTTRADE><DTSETTLE>20160321160000.000[-5:EST]</DTSETTLE><MEMO>TRANSFER OF ACCOUNTTRANSFER OF ACCOUNT</MEMO></INVTRAN><SECID><UNIQUEID>464287655</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>-1602.927207913195</UNITS><TFERACTION>IN</TFERACTION><POSTYPE>LONG</POSTYPE></TRANSFER><TRANSFER><INVTRAN><FITID>057cf934-14e7-41d5-bbe2-27f980d9f7fa</FITID><DTTRADE>20160321160000.000[-5:EST]</DTTRADE><DTSETTLE>20160321160000.000[-5:EST]</DTSETTLE><MEMO>TRANSFER OF ACCOUNTTRANSFER OF ACCOUNT</MEMO></INVTRAN><SECID><UNIQUEID>78464A763</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>2905.7461268824436</UNITS><TFERACTION>IN</TFERACTION><POSTYPE>LONG</POSTYPE></TRANSFER><INVBANKTRAN><STMTTRN><TRNTYPE>OTHER</TRNTYPE><DTPOSTED>20160321160000.000[-5:EST]</DTPOSTED><TRNAMT>-64.40574934902884</TRNAMT><FITID>248704e4-687d-4062-aa54-f0dd8bb8def4</FITID><NAME>CASH</NAME><MEMO>TRANSFER OF ACCOUNTTRANSFER OF ACCOUNT</MEMO></STMTTRN><SUBACCTFUND>CASH</SUBACCTFUND></INVBANKTRAN><INVBANKTRAN><STMTTRN><TRNTYPE>OTHER</TRNTYPE><DTPOSTED>20160330160000.000[-5:EST]</DTPOSTED><TRNAMT>1712.0554730109434</TRNAMT><FITID>67e92ad1-eb71-4c20-945d-34fb05c7b57f</FITID><NAME>CASH</NAME><MEMO>TRANSFER OF ACCOUNTTRANSFER OF ACCOUNT</MEMO></STMTTRN><SUBACCTFUND>CASH</SUBACCTFUND></INVBANKTRAN><INVBANKTRAN><STMTTRN><TRNTYPE>OTHER</TRNTYPE><DTPOSTED>20160607160000.000[-5:EST]</DTPOSTED><TRNAMT>-1991.1515760947818</TRNAMT><FITID>7bcae2a6-28c4-46fa-a2df-45d70951de40</FITID></STMTTRN><SUBACCTFUND>CASH</SUBACCTFUND></INVBANKTRAN><INVBANKTRAN><STMTTRN><TRNTYPE>OTHER</TRNTYPE><DTPOSTED>20170316160000.000[-5:EST]</DTPOSTED><TRNAMT>1610.0655820336997</TRNAMT><FITID>8748c137-21b1-4300-9fd7-4ab435ca0e15</FITID></STMTTRN><SUBACCTFUND>CASH</SUBACCTFUND></INVBANKTRAN></INVTRANLIST><INVPOSLIST><POSSTOCK><INVPOS><SECID><UNIQUEID>049560105</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><HELDINACCT>CASH</HELDINACCT><POSTYPE>LONG</POSTYPE><UNITS>3095.206230511463</UNITS><UNITPRICE>1645.5817152750637</UNITPRICE><MKTVAL>-376.5943957036134</MKTVAL><DTPRICEASOF>20170405160000.000[-5:EST]</DTPRICEASOF><MEMO>Price as of date based on closing price</MEMO></INVPOS><REINVDIV>Y</REINVDIV></POSSTOCK><POSMF><INVPOS><SECID><UNIQUEID>921909602</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><HELDINACCT>CASH</HELDINACCT><POSTYPE>LONG</POSTYPE><UNITS>642.9018339627378</UNITS><UNITPRICE>1109.3060432427337</UNITPRICE><MKTVAL>1904.5190171996674</MKTVAL><DTPRICEASOF>20170405160000.000[-5:EST]</DTPRICEASOF><MEMO>Price as of date based on closing price</MEMO></INVPOS><REINVDIV>Y</REINVDIV><REINVCG>Y</REINVCG></POSMF><POSMF><INVPOS><SECID><UNIQUEID>921937108</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><HELDINACCT>CASH</HELDINACCT><POSTYPE>LONG</POSTYPE><UNITS>195.51686193193655</UNITS><UNITPRICE>4982.684391844139</UNITPRICE><MKTVAL>-1296.7031927802377</MKTVAL><DTPRICEASOF>20170405160000.000[-5:EST]</DTPRICEASOF><MEMO>Price as of date based on closing price</MEMO></INVPOS><REINVDIV>Y</REINVDIV><REINVCG>Y</REINVCG></POSMF><POSMF><INVPOS><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><HELDINACCT>CASH</HELDINACCT><POSTYPE>LONG</POSTYPE><UNITS>544.8705218820041</UNITS><UNITPRICE>-1203.8947910486945</UNITPRICE><MKTVAL>-623.0571816887875</MKTVAL><DTPRICEASOF>20170405160000.000[-5:EST]</DTPRICEASOF><MEMO>Price as of date based on closing price</MEMO></INVPOS><REINVDIV>Y</REINVDIV><REINVCG>N</REINVCG></POSMF><POSMF><INVPOS><SECID><UNIQUEID>922908728</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><HELDINACCT>CASH</HELDINACCT><POSTYPE>LONG</POSTYPE><UNITS>1586.192165445339</UNITS><UNITPRICE>4143.434139828162</UNITPRICE><MKTVAL>2460.9685534384507</MKTVAL><DTPRICEASOF>20170405160000.000[-5:EST]</DTPRICEASOF><MEMO>Price as of date based on closing price</MEMO></INVPOS><REINVDIV>Y</REINVDIV><REINVCG>Y</REINVCG></POSMF><POSMF><INVPOS><SECID><UNIQUEID>922906300</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><HELDINACCT>CASH</HELDINACCT><POSTYPE>LONG</POSTYPE><UNITS>80.78210736327719</UNITS><UNITPRICE>-1355.2464236252567</UNITPRICE><MKTVAL>3770.7899180836966</MKTVAL><DTPRICEASOF>20170405160000.000[-5:EST]</DTPRICEASOF><MEMO>Price as of date based on closing price</MEMO></INVPOS><REINVDIV>N</REINVDIV><REINVCG>N</REINVCG></POSMF></INVPOSLIST><INVBAL><AVAILCASH>-1525.5718103575014</AVAILCASH><MARGINBALANCE>-136.37998298712773</MARGINBALANCE><SHORTBALANCE>4487.050086486819</SHORTBALANCE></INVBAL></INVSTMTRS></INVSTMTTRNRS></INVSTMTMSGSRSV1><SECLISTMSGSRSV1><SECLIST><STOCKINFO><SECINFO><SECID><UNIQUEID>049560105</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SECNAME>ATMOS ENERGY CORP</SECNAME><TICKER>ATO</TICKER><UNITPRICE>44.124154010998836</UNITPRICE><MEMO>Price as of date based on closing price</MEMO></SECINFO><STOCKTYPE>COMMON</STOCKTYPE><YIELD>-1942.3433104810056</YIELD></STOCKINFO><STOCKINFO><SECINFO><SECID><UNIQUEID>921909768</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SECNAME>VANGUARD TOTAL INTL STOCK INDE</SECNAME><TICKER>921909768</TICKER><MEMO>BUY</MEMO></SECINFO></STOCKINFO><STOCKINFO><SECINFO><SECID><UNIQUEID>084670702</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SECNAME>BERKSHIRE HATHAWAY INC DE CL B</SECNAME><TICKER>084670702</TICKER><MEMO>TRANSFER OF ACCOUNT</MEMO></SECINFO></STOCKINFO><STOCKINFO><SECINFO><SECID><UNIQUEID>78462F103</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SECNAME>20SPDR SP 500 ETF</SECNAME><TICKER>78462F103</TICKER><MEMO>TRANSFER OF ACCOUNT</MEMO></SECINFO></STOCKINFO><STOCKINFO><SECINFO><SECID><UNIQUEID>922908769</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SECNAME>VANGUARD TOTAL STOCK MARKET ETF</SECNAME><TICKER>922908769</TICKER><MEMO>BUY</MEMO></SECINFO></STOCKINFO><STOCKINFO><SECINFO><SECID><UNIQUEID>464287655</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SECNAME>ISHARES RUSSELL 2000 ETF</SECNAME><TICKER>464287655</TICKER><MEMO>TRANSFER OF ACCOUNT</MEMO></SECINFO></STOCKINFO><STOCKINFO><SECINFO><SECID><UNIQUEID>78464A763</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SECNAME>20SPDR SERIES TRUST SP DIVIDEND</SECNAME><TICKER>78464A763</TICKER><MEMO>TRANSFER OF ACCOUNT</MEMO></SECINFO></STOCKINFO><MFINFO><SECINFO><SECID><UNIQUEID>921909602</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SECNAME>Vanguard Total International Stock Index Fund Investor Shares</SECNAME><TICKER>VGTSX</TICKER><UNITPRICE>-970.1670162560076</UNITPRICE><MEMO>Price as of date based on closing price</MEMO></SECINFO><MFTYPE>OPENEND</MFTYPE></MFINFO><MFINFO><SECINFO><SECID><UNIQUEID>921937108</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SECNAME>Vanguard Total Bond Market Index Fund Investor Shares</SECNAME><TICKER>VBMFX</TICKER><UNITPRICE>-687.4978500583932</UNITPRICE><MEMO>Price as of date based on closing price</MEMO></SECINFO><MFTYPE>OPENEND</MFTYPE></MFINFO><MFINFO><SECINFO><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SECNAME>VANGUARD TOTAL BOND MARKET ETF</SECNAME><TICKER>BND</TICKER><UNITPRICE>4236.284682348263</UNITPRICE><MEMO>Price as of date based on closing price</MEMO></SECINFO><MFTYPE>OPENEND</MFTYPE></MFINFO><MFINFO><SECINFO><SECID><UNIQUEID>922908728</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SECNAME>Vanguard Total Stock Market Index Fund Admiral Shares</SECNAME><TICKER>VTSAX</TICKER><UNITPRICE>-1555.4850628829035</UNITPRICE><MEMO>Price as of date based on closing price</MEMO></SECINFO><MFTYPE>OPENEND</MFTYPE></MFINFO><MFINFO><SECINFO><SECID><UNIQUEID>922906300</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SECNAME>Vanguard Federal Money Market Fund</SECNAME><TICKER>VMFXX</TICKER><UNITPRICE>2587.989630805755</UNITPRICE><MEMO>Price as of date based on closing price</MEMO></SECINFO><MFTYPE>OPENEND</MFTYPE></MFINFO><MFINFO><SECINFO><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SECNAME>VANGUARD FEDERAL MONEY MARKET</SECNAME><TICKER>FED12Q45Q</TICKER><MEMO>MONEY FUND PURCHASE</MEMO></SECINFO><MFTYPE>OPENEND</MFTYPE></MFINFO></SECLIST></SECLISTMSGSRSV1></OFX>"
  },
  {
    "path": "samples/valid_responses/ira_v202.ofx",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\" ?><?OFX OFXHEADER=\"200\" VERSION=\"202\" SECURITY=\"NONE\" OLDFILEUID=\"NONE\" NEWFILEUID=\"NONE\"?><OFX><SIGNONMSGSRSV1><SONRS><STATUS><CODE>0</CODE><SEVERITY>INFO</SEVERITY><MESSAGE>Successful Sign On</MESSAGE></STATUS><DTSERVER>20170406192636[-5:EST]</DTSERVER><LANGUAGE>ENG</LANGUAGE><DTPROFUP>20140605083000</DTPROFUP><FI><ORG>OAHGK</ORG><FID>3418</FID></FI><SESSCOOKIE>J3stupv8Hc1_S5omeZDXUhGx4P7QiAqdIlEg</SESSCOOKIE></SONRS></SIGNONMSGSRSV1><INVSTMTMSGSRSV1><INVSTMTTRNRS><TRNUID>eaa98f87-e8f9-43e8-bacf-34d171f25fc9</TRNUID><STATUS><CODE>0</CODE><SEVERITY>INFO</SEVERITY></STATUS><INVSTMTRS><DTASOF>20170406160000.000[-5:EST]</DTASOF><CURDEF>USD</CURDEF><INVACCTFROM><BROKERID>fi.example.com</BROKERID><ACCTID>38140787919</ACCTID></INVACCTFROM><INVTRANLIST><DTSTART>20151006160000.000[-5:EST]160000.000[-5:EST]</DTSTART><DTEND>20170406192636.000[-5:EST]</DTEND><BUYMF><INVBUY><INVTRAN><FITID>73ef3a78-8826-4255-ad94-5fd3d02d0ee5</FITID><DTTRADE>20160216160000.000[-5:EST]</DTTRADE><DTSETTLE>20160216160000.000[-5:EST]</DTSETTLE><MEMO>MONEY FUND PURCHASE</MEMO></INVTRAN><SECID><UNIQUEID>922906201</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>19.278217566280546</UNITS><UNITPRICE>-1525.4663711937778</UNITPRICE><TOTAL>4363.22682198809</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE></BUYMF><BUYMF><INVBUY><INVTRAN><FITID>59febec8-0659-4485-ae49-ba7c96700a7a</FITID><DTTRADE>20160623160000.000[-5:EST]</DTTRADE><DTSETTLE>20160623160000.000[-5:EST]</DTSETTLE><MEMO>MONEY FUND PURCHASE</MEMO></INVTRAN><SECID><UNIQUEID>922906201</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>4647.861405961569</UNITS><UNITPRICE>2278.367694616166</UNITPRICE><TOTAL>2469.6389083029935</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE></BUYMF><BUYMF><INVBUY><INVTRAN><FITID>22bf0c32-1b0e-40d4-9ca6-ad09e1334f8c</FITID><DTTRADE>20160919160000.000[-5:EST]</DTTRADE><DTSETTLE>20160919160000.000[-5:EST]</DTSETTLE><MEMO>MONEY FUND PURCHASE</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>1631.6699159955547</UNITS><UNITPRICE>1634.4064644381651</UNITPRICE><TOTAL>1449.4689262297838</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE></BUYMF><BUYMF><INVBUY><INVTRAN><FITID>85cccd7d-a1d2-42c5-9a56-303d32bfb4c0</FITID><DTTRADE>20160920160000.000[-5:EST]</DTTRADE><DTSETTLE>20160920160000.000[-5:EST]</DTSETTLE><MEMO>MONEY FUND PURCHASE</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>-1170.5911481582543</UNITS><UNITPRICE>4914.434618559914</UNITPRICE><TOTAL>623.239143201663</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE></BUYMF><BUYMF><INVBUY><INVTRAN><FITID>1266686a-3499-479f-840b-c83046d1cbb4</FITID><DTTRADE>20160915160000.000[-5:EST]</DTTRADE><DTSETTLE>20160916160000.000[-5:EST]</DTSETTLE><MEMO>BUY</MEMO></INVTRAN><SECID><UNIQUEID>922906201</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>1879.6402178171297</UNITS><UNITPRICE>-1749.2269524431063</UNITPRICE><TOTAL>1673.3291030640453</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE></BUYMF><BUYMF><INVBUY><INVTRAN><FITID>2246810d-61ef-48eb-92da-9daab8a67304</FITID><DTTRADE>20160211160000.000[-5:EST]</DTTRADE><DTSETTLE>20160212160000.000[-5:EST]</DTSETTLE><MEMO>BUY</MEMO></INVTRAN><SECID><UNIQUEID>922908728</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>3671.138197686205</UNITS><UNITPRICE>-845.2352332259197</UNITPRICE><TOTAL>271.334148396772</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE></BUYMF><BUYSTOCK><INVBUY><INVTRAN><FITID>c337e361-1814-4f5f-adb6-cfb7c4fa9726</FITID><DTTRADE>20160304160000.000[-5:EST]</DTTRADE><DTSETTLE>20160309160000.000[-5:EST]</DTSETTLE><MEMO>BUY</MEMO></INVTRAN><SECID><UNIQUEID>921909768</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>429.2321735536443</UNITS><UNITPRICE>-877.5569867259219</UNITPRICE><TOTAL>-114.01187925332829</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE></BUYSTOCK><BUYSTOCK><INVBUY><INVTRAN><FITID>36e24251-84ac-4417-af61-d5d744e7abf0</FITID><DTTRADE>20160620160000.000[-5:EST]</DTTRADE><DTSETTLE>20160623160000.000[-5:EST]</DTSETTLE><MEMO>BUY</MEMO></INVTRAN><SECID><UNIQUEID>921909768</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>1826.5988919707065</UNITS><UNITPRICE>4168.070526154769</UNITPRICE><TOTAL>215.5740907298882</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVBUY><BUYTYPE>BUY</BUYTYPE></BUYSTOCK><INCOME><INVTRAN><FITID>7af54cfd-9c78-4a76-b22d-2033224da06f</FITID><DTTRADE>20160916160000.000[-5:EST]</DTTRADE><DTSETTLE>20160916160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND PAYMENTDIVIDEND PAYMENT</MEMO></INVTRAN><SECID><UNIQUEID>922906201</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>4761.745483958354</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INCOME><REINVEST><INVTRAN><FITID>d453519b-1c71-4e15-9809-89f42f5cb628</FITID><DTTRADE>20160229160000.000[-5:EST]</DTTRADE><DTSETTLE>20160229160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>922906201</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>2231.878532889339</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>4449.730443820237</UNITS><UNITPRICE>3969.3791558741023</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>5f08d9e2-d001-4921-9449-4132e8e13ec0</FITID><DTTRADE>20160331160000.000[-5:EST]</DTTRADE><DTSETTLE>20160331160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>922906201</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>-410.30842159812414</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>-1959.5556604318092</UNITS><UNITPRICE>-219.51461198200673</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>ce0d9325-fbb4-44bf-bff7-dcacc53b3e99</FITID><DTTRADE>20160429160000.000[-5:EST]</DTTRADE><DTSETTLE>20160429160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>922906201</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>-1486.5386646073728</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>3773.739320660916</UNITS><UNITPRICE>-1746.9027368524678</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>3a727ea0-122b-4cb4-97ab-2576d23e700c</FITID><DTTRADE>20160531160000.000[-5:EST]</DTTRADE><DTSETTLE>20160531160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>922906201</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>2534.453837971075</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>-393.44253321583165</UNITS><UNITPRICE>252.97625493777514</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>ddba8b2d-ddde-48c8-ba29-a235a93057fc</FITID><DTTRADE>20160630160000.000[-5:EST]</DTTRADE><DTSETTLE>20160630160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>922906201</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>376.163078287369</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>1556.854252531913</UNITS><UNITPRICE>4125.271895070654</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>0700e49f-7ba5-4b3a-a691-7c6d13a092eb</FITID><DTTRADE>20160729160000.000[-5:EST]</DTTRADE><DTSETTLE>20160729160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>922906201</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>3989.128366075679</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>4865.942955331687</UNITS><UNITPRICE>2917.8660022095837</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>c59073eb-b79a-4e5f-9431-ecc8a55f22c6</FITID><DTTRADE>20160831160000.000[-5:EST]</DTTRADE><DTSETTLE>20160831160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>922906201</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>4078.3093503085493</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>-496.18434078439077</UNITS><UNITPRICE>2753.140482911571</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>17c5fe14-84a3-4a73-af59-bf30b548cb6a</FITID><DTTRADE>20160321160000.000[-5:EST]</DTTRADE><DTSETTLE>20160321160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921909768</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>-1221.9538935107803</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>4746.528489740064</UNITS><UNITPRICE>959.2180222934749</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>ad51c9bd-8f75-43e5-840f-a70cd26a24dd</FITID><DTTRADE>20160620160000.000[-5:EST]</DTTRADE><DTSETTLE>20160620160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921909768</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>-1857.2140850765047</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>3806.262793072783</UNITS><UNITPRICE>3235.6283373172528</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>136b035b-f86b-4675-9c6b-4ae0d94a0e0e</FITID><DTTRADE>20160919160000.000[-5:EST]</DTTRADE><DTSETTLE>20160919160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921909768</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>3312.468204221862</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>2275.7272987234383</UNITS><UNITPRICE>2929.3284555223727</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>688dc78f-fd4d-4e32-b2b8-0f617c586dd5</FITID><DTTRADE>20161227160000.000[-5:EST]</DTTRADE><DTSETTLE>20161227160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921909768</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>3035.100981165604</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>2419.275917573039</UNITS><UNITPRICE>-1467.6568132937696</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>a3741898-1c78-4ef0-af4a-6b8257d621d9</FITID><DTTRADE>20170330160000.000[-5:EST]</DTTRADE><DTSETTLE>20170330160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921909768</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>2551.8931819614763</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>302.28999602868134</UNITS><UNITPRICE>-879.0269579585956</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>cb9d79b9-95bd-48fc-96f8-994a53629a2b</FITID><DTTRADE>20161031160000.000[-5:EST]</DTTRADE><DTSETTLE>20161031160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>1148.6433443100404</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>4252.104493615425</UNITS><UNITPRICE>3234.07741416937</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>e051e4ec-8270-4ca6-93c3-04c27b922a24</FITID><DTTRADE>20161130160000.000[-5:EST]</DTTRADE><DTSETTLE>20161130160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>2508.700799816961</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>2766.5990846524974</UNITS><UNITPRICE>3605.4590902283144</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>015a3fd7-e0e7-435d-b855-34ab8a227e1e</FITID><DTTRADE>20161230160000.000[-5:EST]</DTTRADE><DTSETTLE>20161230160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>483.7378756418202</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>3472.482451765719</UNITS><UNITPRICE>590.1028901047425</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>38a4ec6a-38e1-4212-b8e6-3bb8c8178e9e</FITID><DTTRADE>20170131160000.000[-5:EST]</DTTRADE><DTSETTLE>20170131160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>4427.3291160400495</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>14.790104918561838</UNITS><UNITPRICE>-442.8826055075442</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>b49f4795-5d30-4e1c-af65-6cfe40ae5493</FITID><DTTRADE>20170228160000.000[-5:EST]</DTTRADE><DTSETTLE>20170228160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>3029.2980480118968</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>2856.74575019062</UNITS><UNITPRICE>3809.4489376295987</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>270a9dc3-48e2-4a9f-a930-cd29e32b0d00</FITID><DTTRADE>20170331160000.000[-5:EST]</DTTRADE><DTSETTLE>20170331160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>2645.102484563339</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>-1673.3475119684217</UNITS><UNITPRICE>4523.535003881921</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>d79e8743-7954-4cab-a528-a8e06d3a0dd5</FITID><DTTRADE>20160314160000.000[-5:EST]</DTTRADE><DTSETTLE>20160314160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>922908728</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>282.4616376483614</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>4587.877478132082</UNITS><UNITPRICE>98.54334236670365</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>863cac2d-7fd2-4315-8a39-2f1254f796a3</FITID><DTTRADE>20160613160000.000[-5:EST]</DTTRADE><DTSETTLE>20160613160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>922908728</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>916.914663767484</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>-14.35876384698281</UNITS><UNITPRICE>1571.9396824755122</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>1fe41d06-0f89-40f0-b12d-311ded76d45f</FITID><DTTRADE>20160912160000.000[-5:EST]</DTTRADE><DTSETTLE>20160912160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>922908728</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>3431.2319557327646</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>37.20842508401711</UNITS><UNITPRICE>3414.1136928125725</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>ea033881-5ac6-4f95-8fe7-2c6c1a7d41f6</FITID><DTTRADE>20161219160000.000[-5:EST]</DTTRADE><DTSETTLE>20161219160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>922908728</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>-431.16553302783905</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>-1160.4641462740697</UNITS><UNITPRICE>-1577.1193468728723</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>1b435119-4e87-4d97-bd5f-e44340d9389f</FITID><DTTRADE>20170323160000.000[-5:EST]</DTTRADE><DTSETTLE>20170323160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>922908728</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>-1161.8607928529427</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>3671.400688821197</UNITS><UNITPRICE>1422.2445464358248</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>4f4ba849-7194-402f-8c92-8b7618159954</FITID><DTTRADE>20160205160000.000[-5:EST]</DTTRADE><DTSETTLE>20160205160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>-347.64977589301</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>2374.894731116042</UNITS><UNITPRICE>-1230.0935445193008</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>c30bcf33-8edd-4dd7-91bf-d54b6eca97b7</FITID><DTTRADE>20160307160000.000[-5:EST]</DTTRADE><DTSETTLE>20160307160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>2417.051313778199</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>461.9607296369909</UNITS><UNITPRICE>-1025.0001320219317</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>324261d5-494c-49c4-bcfb-512f27d3702c</FITID><DTTRADE>20160407160000.000[-5:EST]</DTTRADE><DTSETTLE>20160407160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>3500.224708480051</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>2702.8310894059196</UNITS><UNITPRICE>-1792.261661945493</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>28ac3202-1f40-4d26-9673-96261abf06a4</FITID><DTTRADE>20160506160000.000[-5:EST]</DTTRADE><DTSETTLE>20160506160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>-152.25279799932332</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>-501.217439303663</UNITS><UNITPRICE>-1264.293386130012</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>fbd84e50-79ab-4093-8520-56c437c5989b</FITID><DTTRADE>20160607160000.000[-5:EST]</DTTRADE><DTSETTLE>20160607160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>2208.543518179934</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>-530.6029451553413</UNITS><UNITPRICE>1272.8801552972213</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>99186239-b9c7-41de-a60b-f90ee4b261d8</FITID><DTTRADE>20160708160000.000[-5:EST]</DTTRADE><DTSETTLE>20160708160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>-277.9538080741984</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>-528.367954885613</UNITS><UNITPRICE>3868.180990916442</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>6ca39969-5ba1-448d-9d73-d051e4163337</FITID><DTTRADE>20160805160000.000[-5:EST]</DTTRADE><DTSETTLE>20160805160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>1390.1352177192844</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>-1282.7634111873078</UNITS><UNITPRICE>-1029.886744521014</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>981191b4-40b0-41f8-8ba8-9c2063fe0ec9</FITID><DTTRADE>20160908160000.000[-5:EST]</DTTRADE><DTSETTLE>20160908160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>782.6003016009354</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>1456.8047497333328</UNITS><UNITPRICE>1249.8290763247796</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>9b7a3810-a11b-4b6f-911a-49e445b0018f</FITID><DTTRADE>20161007160000.000[-5:EST]</DTTRADE><DTSETTLE>20161007160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>70.3205044010142</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>3796.940584853094</UNITS><UNITPRICE>-1062.457078472081</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>0766a122-24dc-4399-8123-c71115f66229</FITID><DTTRADE>20161107160000.000[-5:EST]</DTTRADE><DTSETTLE>20161107160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>2299.071174809411</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>4735.8314513901605</UNITS><UNITPRICE>4788.540796418702</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>32571a95-af17-4606-a711-a6dcf029b10a</FITID><DTTRADE>20161207160000.000[-5:EST]</DTTRADE><DTSETTLE>20161207160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>-1533.0306472468815</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>-390.4765137990337</UNITS><UNITPRICE>459.80849257522095</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>22e8ade5-d07a-448b-b406-78b45f6326cf</FITID><DTTRADE>20161229160000.000[-5:EST]</DTTRADE><DTSETTLE>20161229160000.000[-5:EST]</DTSETTLE><MEMO>DIV REINVEST LT CAP GAIN</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>1988.2260617644365</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>3758.6280421581005</UNITS><UNITPRICE>4972.58289556606</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>7918a1c7-9ed0-4d04-a94c-065bf8b977f3</FITID><DTTRADE>20161229160000.000[-5:EST]</DTTRADE><DTSETTLE>20161229160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>-77.40659410599346</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>411.7060971423184</UNITS><UNITPRICE>3148.7787880383175</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>ebd51218-a3f3-47b9-9537-f63bd46bd303</FITID><DTTRADE>20161229160000.000[-5:EST]</DTTRADE><DTSETTLE>20161229160000.000[-5:EST]</DTSETTLE><MEMO>DIV REINVEST ST CAP GAIN</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>4773.43800249682</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>-1370.5195304265344</UNITS><UNITPRICE>-1062.9678794567449</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>1d921e57-baa4-4062-921a-fdbb9ab1f47a</FITID><DTTRADE>20170207160000.000[-5:EST]</DTTRADE><DTSETTLE>20170207160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>-1372.6835513390852</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>4065.6167094485336</UNITS><UNITPRICE>174.34112807874317</UNITPRICE></REINVEST><REINVEST><INVTRAN><FITID>c40d8462-85c1-4f77-b777-8f2744c34f92</FITID><DTTRADE>20170307160000.000[-5:EST]</DTTRADE><DTSETTLE>20170307160000.000[-5:EST]</DTSETTLE><MEMO>DIVIDEND REINVESTMENTDIVIDEND REINVESTMENT</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><INCOMETYPE>DIV</INCOMETYPE><TOTAL>2594.475521583572</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>-1651.7027587758605</UNITS><UNITPRICE>4149.456664678262</UNITPRICE></REINVEST><SELLMF><INVSELL><INVTRAN><FITID>36d00a6f-6602-4593-a200-b723856ce623</FITID><DTTRADE>20160309160000.000[-5:EST]</DTTRADE><DTSETTLE>20160309160000.000[-5:EST]</DTSETTLE><MEMO>MONEY FUND REDEMPTION</MEMO></INVTRAN><SECID><UNIQUEID>922906201</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>2334.814219650776</UNITS><UNITPRICE>-1694.5681771062114</UNITPRICE><TOTAL>4735.917005707134</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVSELL><SELLTYPE>SELL</SELLTYPE></SELLMF><SELLMF><INVSELL><INVTRAN><FITID>925feb14-77d3-420a-827a-db93b4465af5</FITID><DTTRADE>20160916160000.000[-5:EST]</DTTRADE><DTSETTLE>20160916160000.000[-5:EST]</DTSETTLE><MEMO>MONEY FUND REDEMPTION</MEMO></INVTRAN><SECID><UNIQUEID>922906201</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>4295.372364405455</UNITS><UNITPRICE>3246.8949011086625</UNITPRICE><TOTAL>4834.973735025837</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVSELL><SELLTYPE>SELL</SELLTYPE></SELLMF><SELLMF><INVSELL><INVTRAN><FITID>e5e66514-a870-4297-873a-6574d8a8b0aa</FITID><DTTRADE>20160210160000.000[-5:EST]</DTTRADE><DTSETTLE>20160216160000.000[-5:EST]</DTSETTLE><MEMO>SELL</MEMO></INVTRAN><SECID><UNIQUEID>261978811</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>385.79378753745823</UNITS><UNITPRICE>1754.8233843951994</UNITPRICE><COMMISSION>-1157.1992982444128</COMMISSION><TOTAL>527.6610260161524</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVSELL><SELLTYPE>SELL</SELLTYPE></SELLMF><SELLMF><INVSELL><INVTRAN><FITID>9767f231-8dae-49fe-a005-d83f39fb571c</FITID><DTTRADE>20160919160000.000[-5:EST]</DTTRADE><DTSETTLE>20160920160000.000[-5:EST]</DTSETTLE><MEMO>SELL</MEMO></INVTRAN><SECID><UNIQUEID>922906201</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>4378.111421483186</UNITS><UNITPRICE>2772.217534654248</UNITPRICE><TOTAL>-1794.720488184712</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVSELL><SELLTYPE>SELL</SELLTYPE></SELLMF><SELLSTOCK><INVSELL><INVTRAN><FITID>ba99872b-0a43-4e98-ad43-83676f13ccc8</FITID><DTTRADE>20160620160000.000[-5:EST]</DTTRADE><DTSETTLE>20160623160000.000[-5:EST]</DTSETTLE><MEMO>SELL</MEMO></INVTRAN><SECID><UNIQUEID>084670702</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>-714.1261345131281</UNITS><UNITPRICE>-342.0917087218054</UNITPRICE><COMMISSION>3078.2330073727353</COMMISSION><FEES>-1464.4908557319884</FEES><TOTAL>4425.271509313134</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVSELL><SELLTYPE>SELL</SELLTYPE></SELLSTOCK><SELLSTOCK><INVSELL><INVTRAN><FITID>05da01b5-a0c0-4bc7-8ebc-75045f0ddcb0</FITID><DTTRADE>20160304160000.000[-5:EST]</DTTRADE><DTSETTLE>20160309160000.000[-5:EST]</DTSETTLE><MEMO>SELL</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>34.99839888799761</UNITS><UNITPRICE>2061.706825519594</UNITPRICE><FEES>886.1447439244439</FEES><TOTAL>-1552.353494167612</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVSELL><SELLTYPE>SELL</SELLTYPE></SELLSTOCK><SELLSTOCK><INVSELL><INVTRAN><FITID>4183e7d7-a876-4435-9c4b-c87bbbf45269</FITID><DTTRADE>20160210160000.000[-5:EST]</DTTRADE><DTSETTLE>20160216160000.000[-5:EST]</DTSETTLE><MEMO>SELL</MEMO></INVTRAN><SECID><UNIQUEID>464287465</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><UNITS>3156.4568246974277</UNITS><UNITPRICE>-360.26578375855047</UNITPRICE><COMMISSION>4017.0101411304004</COMMISSION><FEES>4741.67980576631</FEES><TOTAL>2757.176333763726</TOTAL><SUBACCTSEC>CASH</SUBACCTSEC><SUBACCTFUND>CASH</SUBACCTFUND></INVSELL><SELLTYPE>SELL</SELLTYPE></SELLSTOCK><TRANSFER><INVTRAN><FITID>e3579f9a-c7d0-4a6e-abcd-8ab6f049a1d5</FITID><DTTRADE>20160202160000.000[-5:EST]</DTTRADE><DTSETTLE>20160202160000.000[-5:EST]</DTSETTLE><MEMO>FOR IRA ACCOUNTSFOR IRA ACCOUNTS</MEMO></INVTRAN><SECID><UNIQUEID>261978811</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>-944.6652942505041</UNITS><TFERACTION>IN</TFERACTION><POSTYPE>LONG</POSTYPE></TRANSFER><TRANSFER><INVTRAN><FITID>f7bed88a-9d81-405f-9c4b-6de2f6e47382</FITID><DTTRADE>20160202160000.000[-5:EST]</DTTRADE><DTSETTLE>20160202160000.000[-5:EST]</DTSETTLE><MEMO>FOR IRA ACCOUNTSFOR IRA ACCOUNTS</MEMO></INVTRAN><SECID><UNIQUEID>084670702</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>4492.551705542272</UNITS><TFERACTION>IN</TFERACTION><POSTYPE>LONG</POSTYPE></TRANSFER><TRANSFER><INVTRAN><FITID>64250efe-6a33-416d-a3ef-88eeb7a4d3f7</FITID><DTTRADE>20160202160000.000[-5:EST]</DTTRADE><DTSETTLE>20160202160000.000[-5:EST]</DTSETTLE><MEMO>FOR IRA ACCOUNTSFOR IRA ACCOUNTS</MEMO></INVTRAN><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>-536.6639659620153</UNITS><TFERACTION>IN</TFERACTION><POSTYPE>LONG</POSTYPE></TRANSFER><TRANSFER><INVTRAN><FITID>6b2b601b-0c2c-4cfb-91e2-a268ecca7032</FITID><DTTRADE>20160202160000.000[-5:EST]</DTTRADE><DTSETTLE>20160202160000.000[-5:EST]</DTSETTLE><MEMO>FOR IRA ACCOUNTSFOR IRA ACCOUNTS</MEMO></INVTRAN><SECID><UNIQUEID>464287465</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SUBACCTSEC>CASH</SUBACCTSEC><UNITS>2017.7913572287862</UNITS><TFERACTION>IN</TFERACTION><POSTYPE>LONG</POSTYPE></TRANSFER><INVBANKTRAN><STMTTRN><TRNTYPE>OTHER</TRNTYPE><DTPOSTED>20160202160000.000[-5:EST]</DTPOSTED><TRNAMT>3730.7633832701704</TRNAMT><FITID>98ab64d2-3624-4e5f-abc5-b231993d3873</FITID><NAME>CASH</NAME><MEMO>FOR IRA ACCOUNTSFOR IRA ACCOUNTS</MEMO></STMTTRN><SUBACCTFUND>CASH</SUBACCTFUND></INVBANKTRAN></INVTRANLIST><INVPOSLIST><POSMF><INVPOS><SECID><UNIQUEID>921909768</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><HELDINACCT>CASH</HELDINACCT><POSTYPE>LONG</POSTYPE><UNITS>1127.4611089185805</UNITS><UNITPRICE>3421.969333823841</UNITPRICE><MKTVAL>4064.057559925758</MKTVAL><DTPRICEASOF>20170405160000.000[-5:EST]</DTPRICEASOF><MEMO>Price as of date based on closing price</MEMO></INVPOS><REINVDIV>Y</REINVDIV><REINVCG>N</REINVCG></POSMF><POSMF><INVPOS><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><HELDINACCT>CASH</HELDINACCT><POSTYPE>LONG</POSTYPE><UNITS>3428.985258170972</UNITS><UNITPRICE>4183.637265410978</UNITPRICE><MKTVAL>245.4779875808499</MKTVAL><DTPRICEASOF>20170405160000.000[-5:EST]</DTPRICEASOF><MEMO>Price as of date based on closing price</MEMO></INVPOS><REINVDIV>Y</REINVDIV><REINVCG>N</REINVCG></POSMF><POSMF><INVPOS><SECID><UNIQUEID>922908728</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><HELDINACCT>CASH</HELDINACCT><POSTYPE>LONG</POSTYPE><UNITS>-743.019460940511</UNITS><UNITPRICE>1379.1607413853667</UNITPRICE><MKTVAL>4216.260823009951</MKTVAL><DTPRICEASOF>20170405160000.000[-5:EST]</DTPRICEASOF><MEMO>Price as of date based on closing price</MEMO></INVPOS><REINVDIV>Y</REINVDIV><REINVCG>Y</REINVCG></POSMF><POSMF><INVPOS><SECID><UNIQUEID>922906300</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><HELDINACCT>CASH</HELDINACCT><POSTYPE>LONG</POSTYPE><UNITS>-1776.1307845701342</UNITS><UNITPRICE>3758.0299003430027</UNITPRICE><MKTVAL>4201.087474487898</MKTVAL><DTPRICEASOF>20170405160000.000[-5:EST]</DTPRICEASOF><MEMO>Price as of date based on closing price</MEMO></INVPOS><REINVDIV>N</REINVDIV><REINVCG>N</REINVCG></POSMF></INVPOSLIST><INVBAL><AVAILCASH>3250.107711696731</AVAILCASH><MARGINBALANCE>414.96545358474805</MARGINBALANCE><SHORTBALANCE>4842.585013190769</SHORTBALANCE></INVBAL></INVSTMTRS></INVSTMTTRNRS></INVSTMTMSGSRSV1><SECLISTMSGSRSV1><SECLIST><STOCKINFO><SECINFO><SECID><UNIQUEID>084670702</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SECNAME>BERKSHIRE HATHAWAY INC DE CL B</SECNAME><TICKER>084670702</TICKER><MEMO>FOR IRA ACCOUNTS</MEMO></SECINFO></STOCKINFO><STOCKINFO><SECINFO><SECID><UNIQUEID>464287465</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SECNAME>ISHARES MSCI EAFE  ETF</SECNAME><TICKER>464287465</TICKER><MEMO>FOR IRA ACCOUNTS</MEMO></SECINFO></STOCKINFO><MFINFO><SECINFO><SECID><UNIQUEID>921909768</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SECNAME>VANGUARD TOTAL INTL STOCK INDEX FUND ETF</SECNAME><TICKER>VXUS</TICKER><UNITPRICE>4231.006710834493</UNITPRICE><MEMO>Price as of date based on closing price</MEMO></SECINFO><MFTYPE>OPENEND</MFTYPE></MFINFO><MFINFO><SECINFO><SECID><UNIQUEID>921937835</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SECNAME>VANGUARD TOTAL BOND MARKET ETF</SECNAME><TICKER>BND</TICKER><UNITPRICE>-1905.470808585281</UNITPRICE><MEMO>Price as of date based on closing price</MEMO></SECINFO><MFTYPE>OPENEND</MFTYPE></MFINFO><MFINFO><SECINFO><SECID><UNIQUEID>922908728</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SECNAME>Vanguard Total Stock Market Index Fund Admiral Shares</SECNAME><TICKER>VTSAX</TICKER><UNITPRICE>2130.136485094742</UNITPRICE><MEMO>Price as of date based on closing price</MEMO></SECINFO><MFTYPE>OPENEND</MFTYPE></MFINFO><MFINFO><SECINFO><SECID><UNIQUEID>922906300</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SECNAME>Vanguard Federal Money Market Fund</SECNAME><TICKER>VMFXX</TICKER><UNITPRICE>1290.0386960392157</UNITPRICE><MEMO>Price as of date based on closing price</MEMO></SECINFO><MFTYPE>OPENEND</MFTYPE></MFINFO><MFINFO><SECINFO><SECID><UNIQUEID>922906201</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SECNAME>VANGUARD PRIME MONEY MARKET FU</SECNAME><TICKER>922906201</TICKER><MEMO>MONEY FUND PURCHASE</MEMO></SECINFO><MFTYPE>OPENEND</MFTYPE></MFINFO><MFINFO><SECINFO><SECID><UNIQUEID>261978811</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SECNAME>20DREYFUS / LAUREL INTL SP 500</SECNAME><TICKER>261978811</TICKER><MEMO>FOR IRA ACCOUNTS</MEMO></SECINFO><MFTYPE>OPENEND</MFTYPE></MFINFO><MFINFO><SECINFO><SECID><UNIQUEID>FED12Q45Q</UNIQUEID><UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE></SECID><SECNAME>VANGUARD FEDERAL MONEY MARKET</SECNAME><TICKER>FED12Q45Q</TICKER><MEMO>MONEY FUND PURCHASE</MEMO></SECINFO><MFTYPE>OPENEND</MFTYPE></MFINFO></SECLIST></SECLISTMSGSRSV1></OFX>"
  },
  {
    "path": "samples/valid_responses/moneymrkt1_v103.ofx",
    "content": "OFXHEADER:100\nDATA:OFXSGML\nVERSION:103\nSECURITY:NONE\nENCODING:USASCII\nCHARSET:1252\nCOMPRESSION:NONE\nOLDFILEUID:NONE\nNEWFILEUID:NONE\n\n<OFX>\n<SIGNONMSGSRSV1><SONRS>\n<STATUS>\n<CODE>0\n<SEVERITY>INFO\n</STATUS>\n<DTSERVER>20170407001840.607[0:GMT]\n<LANGUAGE>ENG\n<FI>\n<ORG>UJKDO\n<FID>3534\n</FI>\n</SONRS>\n</SIGNONMSGSRSV1>\n<BANKMSGSRSV1>\n<STMTTRNRS>\n<TRNUID>e1707dfd-695d-4451-8d9c-0e142fdc456a\n<STATUS>\n<CODE>0\n<SEVERITY>INFO\n</STATUS>\n<STMTRS>\n<CURDEF>USD\n<BANKACCTFROM>\n<BANKID>598813374\n<ACCTID>35342483513\n<ACCTTYPE>MONEYMRKT\n</BANKACCTFROM>\n<BANKTRANLIST>\n<DTSTART>20170107011841.262[0:GMT]\n<DTEND>20170407001841.262[0:GMT]\n<STMTTRN>\n<TRNTYPE>CREDIT\n<DTPOSTED>20170117120000.000[0:GMT]\n<TRNAMT>-995.4190396554627\n<FITID>2fb2640c-cee3-4643-8ba3-ea21a4d18954\n<NAME>Dividend Earned\n</STMTTRN>\n<STMTTRN>\n<TRNTYPE>CREDIT\n<DTPOSTED>20170215120000.000[0:GMT]\n<TRNAMT>788.5385340523635\n<FITID>c9d856df-339c-47c6-9f6a-8c2e2910f62e\n<NAME>Dividend Earned\n</STMTTRN>\n<STMTTRN>\n<TRNTYPE>CREDIT\n<DTPOSTED>20170315120000.000[0:GMT]\n<TRNAMT>3070.1328011762807\n<FITID>1107ace0-048b-4c0c-b5f3-45b6be4cd71d\n<NAME>Dividend Earned\n</STMTTRN>\n</BANKTRANLIST>\n<LEDGERBAL>\n<BALAMT>2607.1664944585727\n<DTASOF>20170407001841.262[0:GMT]\n</LEDGERBAL>\n<AVAILBAL>\n<BALAMT>4503.683156768119\n<DTASOF>20170407001841.262[0:GMT]\n</AVAILBAL>\n</STMTRS>\n</STMTTRNRS>\n</BANKMSGSRSV1>\n</OFX>"
  },
  {
    "path": "samples/valid_responses/moneymrkt1_v103_TYPE1.ofx",
    "content": "OFXHEADER:100\nDATA:OFXSGML\nVERSION:103\nSECURITY:TYPE1\nENCODING:USASCII\nCHARSET:1252\nCOMPRESSION:NONE\nOLDFILEUID:NONE\nNEWFILEUID:NONE\n\n<OFX>\n<SIGNONMSGSRSV1><SONRS>\n<STATUS>\n<CODE>0\n<SEVERITY>INFO\n</STATUS>\n<DTSERVER>20170407001840.607[0:GMT]\n<LANGUAGE>ENG\n<FI>\n<ORG>UJKDO\n<FID>3534\n</FI>\n</SONRS>\n</SIGNONMSGSRSV1>\n<BANKMSGSRSV1>\n<STMTTRNRS>\n<TRNUID>e1707dfd-695d-4451-8d9c-0e142fdc456a\n<STATUS>\n<CODE>0\n<SEVERITY>INFO\n</STATUS>\n<STMTRS>\n<CURDEF>USD\n<BANKACCTFROM>\n<BANKID>598813374\n<ACCTID>35342483513\n<ACCTTYPE>MONEYMRKT\n</BANKACCTFROM>\n<BANKTRANLIST>\n<DTSTART>20170107011841.262[0:GMT]\n<DTEND>20170407001841.262[0:GMT]\n<STMTTRN>\n<TRNTYPE>CREDIT\n<DTPOSTED>20170117120000.000[0:GMT]\n<TRNAMT>-995.4190396554627\n<FITID>2fb2640c-cee3-4643-8ba3-ea21a4d18954\n<NAME>Dividend Earned\n</STMTTRN>\n<STMTTRN>\n<TRNTYPE>CREDIT\n<DTPOSTED>20170215120000.000[0:GMT]\n<TRNAMT>788.5385340523635\n<FITID>c9d856df-339c-47c6-9f6a-8c2e2910f62e\n<NAME>Dividend Earned\n</STMTTRN>\n<STMTTRN>\n<TRNTYPE>CREDIT\n<DTPOSTED>20170315120000.000[0:GMT]\n<TRNAMT>3070.1328011762807\n<FITID>1107ace0-048b-4c0c-b5f3-45b6be4cd71d\n<NAME>Dividend Earned\n</STMTTRN>\n</BANKTRANLIST>\n<LEDGERBAL>\n<BALAMT>2607.1664944585727\n<DTASOF>20170407001841.262[0:GMT]\n</LEDGERBAL>\n<AVAILBAL>\n<BALAMT>4503.683156768119\n<DTASOF>20170407001841.262[0:GMT]\n</AVAILBAL>\n</STMTRS>\n</STMTTRNRS>\n</BANKMSGSRSV1>\n</OFX>"
  },
  {
    "path": "samples/valid_responses/moneymrkt1_v203.ofx",
    "content": "<?xml version=\"1.0\" encoding=\"utf-16\"?>\n<?OFX OFXHEADER=\"200\" VERSION=\"203\" SECURITY=\"NONE\" OLDFILEUID=\"NONE\" NEWFILEUID=\"NONE\"?>\n<OFX>\n  <SIGNONMSGSRSV1><SONRS>\n  <STATUS>\n    <CODE>0</CODE>\n    <SEVERITY>INFO</SEVERITY>\n  </STATUS>\n  <DTSERVER>20170407001504.765[0:GMT]</DTSERVER>\n  <LANGUAGE>ENG</LANGUAGE>\n  <FI>\n    <ORG>EHPDK</ORG>\n    <FID>4881</FID>\n  </FI>\n</SONRS></SIGNONMSGSRSV1>\n  <BANKMSGSRSV1>\n    <STMTTRNRS>\n      <TRNUID>262e39f1-e698-48cb-b2a2-b2f8ac2478fa</TRNUID>\n<STATUS>\n  <CODE>0</CODE>\n  <SEVERITY>INFO</SEVERITY>\n</STATUS>\n<STMTRS>\n  <CURDEF>USD</CURDEF>\n  <BANKACCTFROM>\n    <BANKID>188545178</BANKID>\n    <ACCTID>83483499583</ACCTID>\n    <ACCTTYPE>MONEYMRKT</ACCTTYPE>\n  </BANKACCTFROM>\n  <BANKTRANLIST>\n    <DTSTART>20170107011505.296[0:GMT]</DTSTART>\n    <DTEND>20170407001505.296[0:GMT]</DTEND>\n    <STMTTRN>\n      <TRNTYPE>CREDIT</TRNTYPE>\n      <DTPOSTED>20170117120000.000[0:GMT]</DTPOSTED>\n      <TRNAMT>1303.7355652284177</TRNAMT>\n      <FITID>fa603343-dd05-434e-9ffc-933f51f8cedf</FITID>\n      <NAME>Dividend Earned</NAME>\n    </STMTTRN>\n    <STMTTRN>\n      <TRNTYPE>CREDIT</TRNTYPE>\n      <DTPOSTED>20170215120000.000[0:GMT]</DTPOSTED>\n      <TRNAMT>4321.245210194117</TRNAMT>\n      <FITID>061f8fb0-8b56-4938-a45e-5db0e42d74f7</FITID>\n      <NAME>Dividend Earned</NAME>\n    </STMTTRN>\n    <STMTTRN>\n      <TRNTYPE>CREDIT</TRNTYPE>\n      <DTPOSTED>20170315120000.000[0:GMT]</DTPOSTED>\n      <TRNAMT>-850.1196618322863</TRNAMT>\n      <FITID>a43dfadf-350b-4bb1-85d7-8be57ad0ef82</FITID>\n      <NAME>Dividend Earned</NAME>\n    </STMTTRN>\n  </BANKTRANLIST>\n  <LEDGERBAL>\n    <BALAMT>3317.738651126686</BALAMT>\n    <DTASOF>20170407001505.296[0:GMT]</DTASOF>\n  </LEDGERBAL>\n  <AVAILBAL>\n    <BALAMT>658.9377225082694</BALAMT>\n    <DTASOF>20170407001505.296[0:GMT]</DTASOF>\n  </AVAILBAL>\n</STMTRS></STMTTRNRS>\n  </BANKMSGSRSV1>\n</OFX>"
  },
  {
    "path": "seclist.go",
    "content": "package ofxgo\n\nimport (\n\t\"errors\"\n\t\"github.com/aclindsa/xml\"\n)\n\n// SecurityID identifies a security by its CUSIP (for US-based FI's, others may\n// use UniqueID types other than CUSIP)\ntype SecurityID struct {\n\tXMLName      xml.Name `xml:\"SECID\"`\n\tUniqueID     String   `xml:\"UNIQUEID\"`     // CUSIP for US FI's\n\tUniqueIDType String   `xml:\"UNIQUEIDTYPE\"` // Should always be \"CUSIP\" for US FI's\n}\n\n// SecurityRequest represents a request for one security. It is specified with\n// a SECID aggregate, a ticker symbol, or an FI assigned identifier (but no\n// more than one of them at a time)\ntype SecurityRequest struct {\n\tXMLName xml.Name `xml:\"SECRQ\"`\n\t// Only one of the next three should be present\n\tSecID  *SecurityID `xml:\"SECID,omitempty\"`\n\tTicker String      `xml:\"TICKER,omitempty\"`\n\tFiID   String      `xml:\"FIID,omitempty\"`\n}\n\n// SecListRequest represents a request for information (namely price) about one\n// or more securities\ntype SecListRequest struct {\n\tXMLName   xml.Name `xml:\"SECLISTTRNRQ\"`\n\tTrnUID    UID      `xml:\"TRNUID\"`\n\tCltCookie String   `xml:\"CLTCOOKIE,omitempty\"`\n\tTAN       String   `xml:\"TAN,omitempty\"` // Transaction authorization number\n\t// TODO `xml:\"OFXEXTENSION,omitempty\"`\n\tSecurities []SecurityRequest `xml:\"SECLISTRQ>SECRQ,omitempty\"`\n}\n\n// Name returns the name of the top-level transaction XML/SGML element\nfunc (r *SecListRequest) Name() string {\n\treturn \"SECLISTTRNRQ\"\n}\n\n// Valid returns (true, nil) if this struct would be valid OFX if marshalled\n// into XML/SGML\nfunc (r *SecListRequest) Valid(version ofxVersion) (bool, error) {\n\tif ok, err := r.TrnUID.Valid(); !ok {\n\t\treturn false, err\n\t}\n\t// TODO implement\n\treturn true, nil\n}\n\n// Type returns which message set this message belongs to (which Request\n// element of type []Message it should appended to)\nfunc (r *SecListRequest) Type() messageType {\n\treturn SecListRq\n}\n\n// SecListResponse is always empty (except for the transaction UID, status, and\n// optional client cookie). Its presence signifies that the SecurityList (a\n// different element from this one) immediately after this element in\n// Response.SecList was been generated in response to the same SecListRequest\n// this is a response to.\ntype SecListResponse struct {\n\tXMLName   xml.Name `xml:\"SECLISTTRNRS\"`\n\tTrnUID    UID      `xml:\"TRNUID\"`\n\tStatus    Status   `xml:\"STATUS\"`\n\tCltCookie String   `xml:\"CLTCOOKIE,omitempty\"`\n\t// TODO `xml:\"OFXEXTENSION,omitempty\"`\n\t// SECLISTRS is always empty, so we don't parse it here. The actual securities list will be in a top-level element parallel to SECLISTTRNRS\n}\n\n// Name returns the name of the top-level transaction XML/SGML element\nfunc (r *SecListResponse) Name() string {\n\treturn \"SECLISTTRNRS\"\n}\n\n// Valid returns (true, nil) if this struct was valid OFX when unmarshalled\nfunc (r *SecListResponse) Valid(version ofxVersion) (bool, error) {\n\tif ok, err := r.TrnUID.Valid(); !ok {\n\t\treturn false, err\n\t}\n\t// TODO implement\n\treturn true, nil\n}\n\n// Type returns which message set this message belongs to (which Response\n// element of type []Message it belongs to)\nfunc (r *SecListResponse) Type() messageType {\n\treturn SecListRs\n}\n\n// Security is satisfied by all *Info elements providing information about\n// securities for SecurityList\ntype Security interface {\n\tSecurityType() string\n\tSecurityInfo() SecInfo\n}\n\n// SecInfo represents the generic information about a security. It is included\n// in most other *Info elements.\ntype SecInfo struct {\n\tXMLName   xml.Name   `xml:\"SECINFO\"`\n\tSecID     SecurityID `xml:\"SECID\"`\n\tSecName   String     `xml:\"SECNAME\"`          // Full name of security\n\tTicker    String     `xml:\"TICKER,omitempty\"` // Ticker symbol\n\tFiID      String     `xml:\"FIID,omitempty\"`\n\tRating    String     `xml:\"RATING,omitempty\"`\n\tUnitPrice Amount     `xml:\"UNITPRICE,omitempty\"` // Current price, as of DTASOF\n\tDtAsOf    *Date      `xml:\"DTASOF,omitempty\"`    // Date UNITPRICE was for\n\tCurrency  *Currency  `xml:\"CURRENCY,omitempty\"`  // Overriding currency for UNITPRICE\n\tMemo      String     `xml:\"MEMO,omitempty\"`\n}\n\n// DebtInfo provides information about a debt security\ntype DebtInfo struct {\n\tXMLName      xml.Name   `xml:\"DEBTINFO\"`\n\tSecInfo      SecInfo    `xml:\"SECINFO\"`\n\tParValue     Amount     `xml:\"PARVALUE\"`\n\tDebtType     debtType   `xml:\"DEBTTYPE\"`               // One of COUPON, ZERO (zero coupon)\n\tDebtClass    debtClass  `xml:\"DEBTCLASS,omitempty\"`    // One of TREASURY, MUNICIPAL, CORPORATE, OTHER\n\tCouponRate   Amount     `xml:\"COUPONRT,omitempty\"`     // Bond coupon rate for next closest call date\n\tDtCoupon     *Date      `xml:\"DTCOUPON,omitempty\"`     // Maturity date for next coupon\n\tCouponFreq   couponFreq `xml:\"COUPONFREQ,omitempty\"`   // When coupons mature - one of MONTHLY, QUARTERLY, SEMIANNUAL, ANNUAL, or OTHER\n\tCallPrice    Amount     `xml:\"CALLPRICE,omitempty\"`    // Bond call price\n\tYieldToCall  Amount     `xml:\"YIELDTOCALL,omitempty\"`  // Yield to next call\n\tDtCall       *Date      `xml:\"DTCALL,omitempty\"`       // Next call date\n\tCallType     callType   `xml:\"CALLTYPE,omitempt\"`      // Type of next call. One of CALL, PUT, PREFUND, MATURITY\n\tYieldToMat   Amount     `xml:\"YIELDTOMAT,omitempty\"`   // Yield to maturity\n\tDtMat        *Date      `xml:\"DTMAT,omitempty\"`        // Debt maturity date\n\tAssetClass   assetClass `xml:\"ASSETCLASS,omitempty\"`   // One of DOMESTICBOND, INTLBOND, LARGESTOCK, SMALLSTOCK, INTLSTOCK, MONEYMRKT, OTHER\n\tFiAssetClass String     `xml:\"FIASSETCLASS,omitempty\"` // FI-defined asset class\n}\n\n// SecurityType returns a string representation of this security's type\nfunc (i DebtInfo) SecurityType() string {\n\treturn \"DEBTINFO\"\n}\n\n// SecurityInfo returns SecInfo\nfunc (i DebtInfo) SecurityInfo() SecInfo {\n\treturn i.SecInfo\n}\n\n// AssetPortion represents the percentage of a mutual fund with the given asset\n// classification\ntype AssetPortion struct {\n\tXMLName    xml.Name   `xml:\"PORTION\"`\n\tAssetClass assetClass `xml:\"ASSETCLASS\"` // One of DOMESTICBOND, INTLBOND, LARGESTOCK, SMALLSTOCK, INTLSTOCK, MONEYMRKT, OTHER\n\tPercent    Amount     `xml:\"PERCENT\"`    // Percentage of the fund that falls under this asset class\n}\n\n// FiAssetPortion represents the percentage of a mutual fund with the given\n// FI-defined asset classification (AssetPortion should be used for all asset\n// classifications defined by the assetClass enum)\ntype FiAssetPortion struct {\n\tXMLName      xml.Name `xml:\"FIPORTION\"`\n\tFiAssetClass String   `xml:\"FIASSETCLASS,omitempty\"` // FI-defined asset class\n\tPercent      Amount   `xml:\"PERCENT\"`                // Percentage of the fund that falls under this asset class\n}\n\n// MFInfo provides information about a mutual fund\ntype MFInfo struct {\n\tXMLName        xml.Name         `xml:\"MFINFO\"`\n\tSecInfo        SecInfo          `xml:\"SECINFO\"`\n\tMfType         mfType           `xml:\"MFTYPE\"`                // One of OPEN, END, CLOSEEND, OTHER\n\tYield          Amount           `xml:\"YIELD,omitempty\"`       // Current yield reported as the dividend expressed as a portion of the current stock price\n\tDtYieldAsOf    *Date            `xml:\"DTYIELDASOF,omitempty\"` // Date YIELD is valid for\n\tAssetClasses   []AssetPortion   `xml:\"MFASSETCLASS>PORTION\"`\n\tFiAssetClasses []FiAssetPortion `xml:\"FIMFASSETCLASS>FIPORTION\"`\n}\n\n// SecurityType returns a string representation of this security's type\nfunc (i MFInfo) SecurityType() string {\n\treturn \"MFINFO\"\n}\n\n// SecurityInfo returns SecInfo\nfunc (i MFInfo) SecurityInfo() SecInfo {\n\treturn i.SecInfo\n}\n\n// OptInfo provides information about an option\ntype OptInfo struct {\n\tXMLName      xml.Name    `xml:\"OPTINFO\"`\n\tSecInfo      SecInfo     `xml:\"SECINFO\"`\n\tOptType      optType     `xml:\"OPTTYPE\"` // One of PUT, CALL\n\tStrikePrice  Amount      `xml:\"STRIKEPRICE\"`\n\tDtExpire     Date        `xml:\"DTEXPIRE\"`               // Expiration date\n\tShPerCtrct   Int         `xml:\"SHPERCTRCT\"`             // Shares per contract\n\tSecID        *SecurityID `xml:\"SECID,omitempty\"`        // Security ID of the underlying security\n\tAssetClass   assetClass  `xml:\"ASSETCLASS,omitempty\"`   // One of DOMESTICBOND, INTLBOND, LARGESTOCK, SMALLSTOCK, INTLSTOCK, MONEYMRKT, OTHER\n\tFiAssetClass String      `xml:\"FIASSETCLASS,omitempty\"` // FI-defined asset class\n}\n\n// SecurityType returns a string representation of this security's type\nfunc (i OptInfo) SecurityType() string {\n\treturn \"OPTINFO\"\n}\n\n// SecurityInfo returns SecInfo\nfunc (i OptInfo) SecurityInfo() SecInfo {\n\treturn i.SecInfo\n}\n\n// OtherInfo provides information about a security type not covered by the\n// other *Info elements\ntype OtherInfo struct {\n\tXMLName      xml.Name   `xml:\"OTHERINFO\"`\n\tSecInfo      SecInfo    `xml:\"SECINFO\"`\n\tTypeDesc     String     `xml:\"TYPEDESC,omitempty\"`     // Description of security type\n\tAssetClass   assetClass `xml:\"ASSETCLASS,omitempty\"`   // One of DOMESTICBOND, INTLBOND, LARGESTOCK, SMALLSTOCK, INTLSTOCK, MONEYMRKT, OTHER\n\tFiAssetClass String     `xml:\"FIASSETCLASS,omitempty\"` // FI-defined asset class\n}\n\n// SecurityType returns a string representation of this security's type\nfunc (i OtherInfo) SecurityType() string {\n\treturn \"OTHERINFO\"\n}\n\n// SecurityInfo returns SecInfo\nfunc (i OtherInfo) SecurityInfo() SecInfo {\n\treturn i.SecInfo\n}\n\n// StockInfo provides information about a security type\ntype StockInfo struct {\n\tXMLName      xml.Name   `xml:\"STOCKINFO\"`\n\tSecInfo      SecInfo    `xml:\"SECINFO\"`\n\tStockType    stockType  `xml:\"STOCKTYPE,omitempty\"`    // One of COMMON, PREFERRED, CONVERTIBLE, OTHER\n\tYield        Amount     `xml:\"YIELD,omitempty\"`        // Current yield reported as the dividend expressed as a portion of the current stock price\n\tDtYieldAsOf  *Date      `xml:\"DTYIELDASOF,omitempty\"`  // Date YIELD is valid for\n\tAssetClass   assetClass `xml:\"ASSETCLASS,omitempty\"`   // One of DOMESTICBOND, INTLBOND, LARGESTOCK, SMALLSTOCK, INTLSTOCK, MONEYMRKT, OTHER\n\tFiAssetClass String     `xml:\"FIASSETCLASS,omitempty\"` // FI-defined asset class\n}\n\n// SecurityType returns a string representation of this security's type\nfunc (i StockInfo) SecurityType() string {\n\treturn \"STOCKINFO\"\n}\n\n// SecurityInfo returns SecInfo\nfunc (i StockInfo) SecurityInfo() SecInfo {\n\treturn i.SecInfo\n}\n\n// SecurityList is a container for Security objects containaing information\n// about securities\ntype SecurityList struct {\n\tXMLName    xml.Name `xml:\"SECLIST\"`\n\tSecurities []Security\n}\n\n// Name returns the name of the top-level transaction XML/SGML element\nfunc (r *SecurityList) Name() string {\n\treturn \"SECLIST\"\n}\n\n// Valid returns (true, nil) if this struct was valid OFX when unmarshalled\nfunc (r *SecurityList) Valid(version ofxVersion) (bool, error) {\n\t// TODO implement\n\treturn true, nil\n}\n\n// Type returns which message set this message belongs to (which Response\n// element of type []Message it belongs to)\nfunc (r *SecurityList) Type() messageType {\n\treturn SecListRs\n}\n\n// UnmarshalXML handles unmarshalling a SecurityList from an SGML/XML string\nfunc (r *SecurityList) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tfor {\n\t\ttok, err := nextNonWhitespaceToken(d)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t} else if end, ok := tok.(xml.EndElement); ok && end.Name.Local == start.Name.Local {\n\t\t\t// If we found the end of our starting element, we're done parsing\n\t\t\treturn nil\n\t\t} else if startElement, ok := tok.(xml.StartElement); ok {\n\t\t\tswitch startElement.Name.Local {\n\t\t\tcase \"DEBTINFO\":\n\t\t\t\tvar info DebtInfo\n\t\t\t\tif err := d.DecodeElement(&info, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tr.Securities = append(r.Securities, Security(info))\n\t\t\tcase \"MFINFO\":\n\t\t\t\tvar info MFInfo\n\t\t\t\tif err := d.DecodeElement(&info, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tr.Securities = append(r.Securities, Security(info))\n\t\t\tcase \"OPTINFO\":\n\t\t\t\tvar info OptInfo\n\t\t\t\tif err := d.DecodeElement(&info, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tr.Securities = append(r.Securities, Security(info))\n\t\t\tcase \"OTHERINFO\":\n\t\t\t\tvar info OtherInfo\n\t\t\t\tif err := d.DecodeElement(&info, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tr.Securities = append(r.Securities, Security(info))\n\t\t\tcase \"STOCKINFO\":\n\t\t\t\tvar info StockInfo\n\t\t\t\tif err := d.DecodeElement(&info, &startElement); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tr.Securities = append(r.Securities, Security(info))\n\t\t\tdefault:\n\t\t\t\treturn errors.New(\"Invalid SECLIST child tag: \" + startElement.Name.Local)\n\t\t\t}\n\t\t} else {\n\t\t\treturn errors.New(\"Didn't find an opening element\")\n\t\t}\n\t}\n}\n\n// MarshalXML handles marshalling a SecurityList to an SGML/XML string\nfunc (r *SecurityList) MarshalXML(e *xml.Encoder, start xml.StartElement) error {\n\tsecListElement := xml.StartElement{Name: xml.Name{Local: \"SECLIST\"}}\n\tif err := e.EncodeToken(secListElement); err != nil {\n\t\treturn err\n\t}\n\tfor _, s := range r.Securities {\n\t\tstart := xml.StartElement{Name: xml.Name{Local: s.SecurityType()}}\n\t\tswitch sec := s.(type) {\n\t\tcase DebtInfo:\n\t\t\tif err := e.EncodeElement(&sec, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase MFInfo:\n\t\t\tif err := e.EncodeElement(&sec, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase OptInfo:\n\t\t\tif err := e.EncodeElement(&sec, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase OtherInfo:\n\t\t\tif err := e.EncodeElement(&sec, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase StockInfo:\n\t\t\tif err := e.EncodeElement(&sec, start); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn errors.New(\"Invalid SECLIST child type: \" + sec.SecurityType())\n\t\t}\n\t}\n\tif err := e.EncodeToken(secListElement.End()); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "signon.go",
    "content": "package ofxgo\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/aclindsa/xml\"\n)\n\n// SignonRequest identifies and authenticates a user to their FI and is\n// provided with every Request\ntype SignonRequest struct {\n\tXMLName    xml.Name `xml:\"SONRQ\"`\n\tDtClient   Date     `xml:\"DTCLIENT\"` // Current time on client, overwritten in Client.Request()\n\tUserID     String   `xml:\"USERID\"`\n\tUserPass   String   `xml:\"USERPASS,omitempty\"`\n\tUserKey    String   `xml:\"USERKEY,omitempty\"`\n\tGenUserKey Boolean  `xml:\"GENUSERKEY,omitempty\"`\n\tLanguage   String   `xml:\"LANGUAGE\"` // Defaults to ENG\n\tOrg        String   `xml:\"FI>ORG\"`\n\tFid        String   `xml:\"FI>FID\"`\n\tAppID      String   `xml:\"APPID\"`  // Overwritten in Client.Request()\n\tAppVer     String   `xml:\"APPVER\"` // Overwritten in Client.Request()\n\tClientUID  UID      `xml:\"CLIENTUID,omitempty\"`\n}\n\n// Name returns the name of the top-level transaction XML/SGML element\nfunc (r *SignonRequest) Name() string {\n\treturn \"SONRQ\"\n}\n\n// Valid returns (true, nil) if this struct would be valid OFX if marshalled\n// into XML/SGML\nfunc (r *SignonRequest) Valid(version ofxVersion) (bool, error) {\n\tif len(r.UserID) < 1 || len(r.UserID) > 32 {\n\t\treturn false, errors.New(\"SONRQ>USERID invalid length\")\n\t}\n\tif (len(r.UserPass) == 0) == (len(r.UserKey) == 0) {\n\t\treturn false, errors.New(\"One and only one of SONRQ>USERPASS and USERKEY must be supplied\")\n\t}\n\tif len(r.UserPass) > 32 {\n\t\treturn false, errors.New(\"SONRQ>USERPASS invalid length\")\n\t}\n\tif len(r.UserKey) > 64 {\n\t\treturn false, errors.New(\"SONRQ>USERKEY invalid length\")\n\t}\n\tif len(r.Language) == 0 {\n\t\tr.Language = \"ENG\"\n\t} else if len(r.Language) != 3 {\n\t\treturn false, fmt.Errorf(\"SONRQ>LANGUAGE invalid length: \\\"%s\\\"\", r.Language)\n\t}\n\tif len(r.AppID) < 1 || len(r.AppID) > 5 {\n\t\treturn false, errors.New(\"SONRQ>APPID invalid length\")\n\t}\n\tif len(r.AppVer) < 1 || len(r.AppVer) > 4 {\n\t\treturn false, errors.New(\"SONRQ>APPVER invalid length\")\n\t}\n\treturn true, nil\n}\n\n// SignonResponse is provided with every Response and indicates the success or\n// failure of the SignonRequest in the corresponding Request\ntype SignonResponse struct {\n\tXMLName     xml.Name `xml:\"SONRS\"`\n\tStatus      Status   `xml:\"STATUS\"`\n\tDtServer    Date     `xml:\"DTSERVER\"`\n\tUserKey     String   `xml:\"USERKEY,omitempty\"`\n\tTsKeyExpire *Date    `xml:\"TSKEYEXPIRE,omitempty\"`\n\tLanguage    String   `xml:\"LANGUAGE\"`\n\tDtProfUp    *Date    `xml:\"DTPROFUP,omitempty\"`\n\tDtAcctUp    *Date    `xml:\"DTACCTUP,omitempty\"`\n\tOrg         String   `xml:\"FI>ORG\"`\n\tFid         String   `xml:\"FI>FID\"`\n\tSessCookie  String   `xml:\"SESSCOOKIE,omitempty\"`\n\tAccessKey   String   `xml:\"ACCESSKEY,omitempty\"`\n}\n\n// Name returns the name of the top-level transaction XML/SGML element\nfunc (r *SignonResponse) Name() string {\n\treturn \"SONRS\"\n}\n\n// Valid returns (true, nil) if this struct was valid OFX when unmarshalled\nfunc (r *SignonResponse) Valid(version ofxVersion) (bool, error) {\n\tif len(r.Language) != 3 {\n\t\treturn false, fmt.Errorf(\"SONRS>LANGUAGE invalid length: \\\"%s\\\"\", r.Language)\n\t}\n\treturn r.Status.Valid()\n}\n"
  },
  {
    "path": "signon_test.go",
    "content": "package ofxgo\n\nimport (\n\t\"testing\"\n)\n\nfunc TestMarshalInvalidSignons(t *testing.T) {\n\tvar client = BasicClient{\n\t\tAppID:       \"OFXGO\",\n\t\tAppVer:      \"0001\",\n\t\tSpecVersion: OfxVersion203,\n\t}\n\n\tvar request Request\n\trequest.Signon.UserID = \"myusername\"\n\trequest.Signon.UserPass = \"Pa$$word\"\n\trequest.Signon.Org = \"BNK\"\n\trequest.Signon.Fid = \"1987\"\n\n\trequest.SetClientFields(&client)\n\t_, err := request.Marshal()\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error marshalling signon: %s\\n\", err)\n\t}\n\n\trequest.Signon.UserKey = \"mykey\"\n\t_, err = request.Marshal()\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error due to key and password both being specified\\n\")\n\t}\n\n\trequest.Signon.UserPass = \"\"\n\t_, err = request.Marshal()\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error marshalling signon: %s\\n\", err)\n\t}\n\n\trequest.Signon.UserID = \"\"\n\t_, err = request.Marshal()\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error due to unspecified UserID\\n\")\n\t}\n\trequest.Signon.UserID = \"lakhgdlsakhgdlkahdglkhsadlkghaslkdghsalkdghalsdhg\"\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error due to UserID too long\\n\")\n\t}\n\trequest.Signon.UserID = \"myusername\"\n\n\trequest.Signon.UserKey = \"adlfahdslkgahdweoihadf98agrha87rghasdf9hawhra2hrkwahhaguhwaoefajkei23hff\"\n\t_, err = request.Marshal()\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error due to UserKey too long\\n\")\n\t}\n\trequest.Signon.UserKey = \"\"\n\n\trequest.Signon.UserPass = \"adlfahdslkgahdweoihadf98agrha87rghasdf9hawhra2hrkwahhaguhwaoefajkei23hff\"\n\t_, err = request.Marshal()\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error due to UserPass too long\\n\")\n\t}\n\trequest.Signon.UserPass = \"lakhgdlkahd\"\n\n\trequest.Signon.Language = \"English\"\n\t_, err = request.Marshal()\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error due to Language too long\\n\")\n\t}\n\trequest.Signon.Language = \"EN\"\n\t_, err = request.Marshal()\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error due to Language too short\\n\")\n\t}\n\trequest.Signon.Language = \"\"\n\t_, err = request.Marshal()\n\tif err != nil || request.Signon.Language != \"ENG\" {\n\t\tt.Fatalf(\"Empty Language expected to default to ENG: %s\\n\", err)\n\t}\n\trequest.Signon.Language = \"ENG\"\n\n\trequest.Signon.AppID = \"\"\n\t_, err = request.Marshal()\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error due to missing AppID\\n\")\n\t}\n\trequest.SetClientFields(&client)\n\t_, err = request.Marshal()\n\tif err != nil {\n\t\tt.Fatalf(\"Client expected to set empty AppID: %s\\n\", err)\n\t}\n\tclient.AppID = \"ALKHGDH\"\n\trequest.SetClientFields(&client)\n\t_, err = request.Marshal()\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error due to AppID too long\\n\")\n\t}\n\tclient.AppID = \"OFXGO\"\n\n\trequest.Signon.AppVer = \"\"\n\t_, err = request.Marshal()\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error due to missing AppVer\\n\")\n\t}\n\trequest.SetClientFields(&client)\n\t_, err = request.Marshal()\n\tif err != nil {\n\t\tt.Fatalf(\"Client expected to set empty AppVer: %s\\n\", err)\n\t}\n\tclient.AppVer = \"00002\"\n\trequest.SetClientFields(&client)\n\t_, err = request.Marshal()\n\tif err == nil {\n\t\tt.Fatalf(\"Expected error due to AppVer too long\\n\")\n\t}\n\tclient.AppVer = \"0001\"\n\n\trequest.SetClientFields(&client)\n\t_, err = request.Marshal()\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error after resetting all fields to reasonable values: %s\\n\", err)\n\t}\n}\n"
  },
  {
    "path": "signup.go",
    "content": "package ofxgo\n\nimport (\n\t\"fmt\"\n\t\"github.com/aclindsa/xml\"\n)\n\n// AcctInfoRequest represents a request for the server to provide information\n// for all of the user's available accounts at this FI\ntype AcctInfoRequest struct {\n\tXMLName   xml.Name `xml:\"ACCTINFOTRNRQ\"`\n\tTrnUID    UID      `xml:\"TRNUID\"`\n\tCltCookie String   `xml:\"CLTCOOKIE,omitempty\"`\n\tTAN       String   `xml:\"TAN,omitempty\"` // Transaction authorization number\n\t// TODO `xml:\"OFXEXTENSION,omitempty\"`\n\tDtAcctUp Date `xml:\"ACCTINFORQ>DTACCTUP\"`\n}\n\n// Name returns the name of the top-level transaction XML/SGML element\nfunc (r *AcctInfoRequest) Name() string {\n\treturn \"ACCTINFOTRNRQ\"\n}\n\n// Valid returns (true, nil) if this struct would be valid OFX if marshalled\n// into XML/SGML\nfunc (r *AcctInfoRequest) Valid(version ofxVersion) (bool, error) {\n\tif ok, err := r.TrnUID.Valid(); !ok {\n\t\treturn false, err\n\t}\n\t// TODO implement\n\treturn true, nil\n}\n\n// Type returns which message set this message belongs to (which Request\n// element of type []Message it should appended to)\nfunc (r *AcctInfoRequest) Type() messageType {\n\treturn SignupRq\n}\n\n// HolderInfo contains the information a FI has about an account-holder\ntype HolderInfo struct {\n\tXMLName    xml.Name\n\tFirstName  String     `xml:\"FIRSTNAME\"`\n\tMiddleName String     `xml:\"MIDDLENAME,omitempty\"`\n\tLastName   String     `xml:\"LASTNAME\"`\n\tAddr1      String     `xml:\"ADDR1\"`\n\tAddr2      String     `xml:\"ADDR2,omitempty\"`\n\tAddr3      String     `xml:\"ADDR3,omitempty\"`\n\tCity       String     `xml:\"CITY\"`\n\tState      String     `xml:\"STATE\"`\n\tPostalCode String     `xml:\"POSTALCODE\"`\n\tCountry    String     `xml:\"COUNTRY,omitempty\"`\n\tDayPhone   String     `xml:\"DAYPHONE,omitempty\"`\n\tEvePhone   String     `xml:\"EVEPHONE,omitempty\"`\n\tEmail      String     `xml:\"EMAIL,omitempty\"`\n\tHolderType holderType `xml:\"HOLDERTYPE,omitempty\"` // One of INDIVIDUAL, JOINT, CUSTODIAL, TRUST, OTHER\n}\n\n// BankAcctInfo contains information about a bank account, including how to\n// access it (BankAcct), and whether it supports downloading transactions\n// (SupTxDl).\ntype BankAcctInfo struct {\n\tXMLName            xml.Name           `xml:\"BANKACCTINFO\"`\n\tBankAcctFrom       BankAcct           `xml:\"BANKACCTFROM\"`\n\tSupTxDl            Boolean            `xml:\"SUPTXDL\"`                      // Supports downloading transactions (as opposed to balance only)\n\tXferSrc            Boolean            `xml:\"XFERSRC\"`                      // Enabled as source for intra/interbank transfer\n\tXferDest           Boolean            `xml:\"XFERDEST\"`                     // Enabled as destination for intra/interbank transfer\n\tMaturityDate       Date               `xml:\"MATURITYDATE,omitempty\"`       // Maturity date for CD, if CD\n\tMaturityAmt        Amount             `xml:\"MATURITYAMOUNT,omitempty\"`     // Maturity amount for CD, if CD\n\tMinBalReq          Amount             `xml:\"MINBALREQ,omitempty\"`          // Minimum balance required to avoid service fees\n\tAcctClassification acctClassification `xml:\"ACCTCLASSIFICATION,omitempty\"` // One of PERSONAL, BUSINESS, CORPORATE, OTHER\n\tOverdraftLimit     Amount             `xml:\"OVERDRAFTLIMIT,omitempty\"`\n\tSvcStatus          svcStatus          `xml:\"SVCSTATUS\"` // One of AVAIL (available, but not yet requested), PEND (requested, but not yet available), ACTIVE\n}\n\n// String makes pointers to BankAcctInfo structs print nicely\nfunc (bai *BankAcctInfo) String() string {\n\treturn fmt.Sprintf(\"%+v\", *bai)\n}\n\n// CCAcctInfo contains information about a credit card account, including how\n// to access it (CCAcct), and whether it supports downloading transactions\n// (SupTxDl).\ntype CCAcctInfo struct {\n\tXMLName            xml.Name           `xml:\"CCACCTINFO\"`\n\tCCAcctFrom         CCAcct             `xml:\"CCACCTFROM\"`\n\tSupTxDl            Boolean            `xml:\"SUPTXDL\"`                      // Supports downloading transactions (as opposed to balance only)\n\tXferSrc            Boolean            `xml:\"XFERSRC\"`                      // Enabled as source for intra/interbank transfer\n\tXferDest           Boolean            `xml:\"XFERDEST\"`                     // Enabled as destination for intra/interbank transfer\n\tAcctClassification acctClassification `xml:\"ACCTCLASSIFICATION,omitempty\"` // One of PERSONAL, BUSINESS, CORPORATE, OTHER\n\tSvcStatus          svcStatus          `xml:\"SVCSTATUS\"`                    // One of AVAIL (available, but not yet requested), PEND (requested, but not yet available), ACTIVE\n}\n\n// String makes pointers to CCAcctInfo structs print nicely\nfunc (ci *CCAcctInfo) String() string {\n\treturn fmt.Sprintf(\"%+v\", *ci)\n}\n\n// InvAcctInfo contains information about an investment account, including how\n// to access it (InvAcct), and whether it supports downloading transactions\n// (SupTxDl).\ntype InvAcctInfo struct {\n\tXMLName       xml.Name      `xml:\"INVACCTINFO\"`\n\tInvAcctFrom   InvAcct       `xml:\"INVACCTFROM\"`\n\tUsProductType usProductType `xml:\"USPRODUCTTYPE\"`         // One of 401K, 403B, IRA, KEOGH, OTHER, SARSEP, SIMPLE, NORMAL, TDA, TRUST, UGMA\n\tChecking      Boolean       `xml:\"CHECKING\"`              // Has check-writing privileges\n\tSvcStatus     svcStatus     `xml:\"SVCSTATUS\"`             // One of AVAIL (available, but not yet requested), PEND (requested, but not yet available), ACTIVE\n\tInvAcctType   holderType    `xml:\"INVACCTTYPE,omitempty\"` // One of INDIVIDUAL, JOINT, TRUST, CORPORATE\n\tOptionLevel   String        `xml:\"OPTIONLEVEL,omitempty\"` // Text desribing option trading privileges\n}\n\n// String makes pointers to InvAcctInfo structs print nicely\nfunc (iai *InvAcctInfo) String() string {\n\treturn fmt.Sprintf(\"%+v\", *iai)\n}\n\n// AcctInfo represents generic account information. It should contain one (and\n// only one) *AcctInfo element corresponding to the tyep of account it\n// represents.\ntype AcctInfo struct {\n\tXMLName         xml.Name   `xml:\"ACCTINFO\"`\n\tName            String     `xml:\"NAME,omitempty\"`\n\tDesc            String     `xml:\"DESC,omitempty\"`\n\tPhone           String     `xml:\"PHONE,omitempty\"`\n\tPrimaryHolder   HolderInfo `xml:\"HOLDERINFO>PRIMARYHOLDER,omitempty\"`\n\tSecondaryHolder HolderInfo `xml:\"HOLDERINFO>SECONDARYHOLDER,omitempty\"`\n\n\t// Only one of the rest of the fields will be valid for any given AcctInfo\n\tBankAcctInfo *BankAcctInfo `xml:\"BANKACCTINFO,omitempty\"`\n\tCCAcctInfo   *CCAcctInfo   `xml:\"CCACCTINFO,omitempty\"`\n\tInvAcctInfo  *InvAcctInfo  `xml:\"INVACCTINFO,omitempty\"`\n\t// TODO LOANACCTINFO\n\t// TODO BPACCTINFO?\n}\n\n// AcctInfoResponse contains the information about all a user's accounts\n// accessible from this FI\ntype AcctInfoResponse struct {\n\tXMLName   xml.Name `xml:\"ACCTINFOTRNRS\"`\n\tTrnUID    UID      `xml:\"TRNUID\"`\n\tStatus    Status   `xml:\"STATUS\"`\n\tCltCookie String   `xml:\"CLTCOOKIE,omitempty\"`\n\t// TODO `xml:\"OFXEXTENSION,omitempty\"`\n\tDtAcctUp Date       `xml:\"ACCTINFORS>DTACCTUP\"`\n\tAcctInfo []AcctInfo `xml:\"ACCTINFORS>ACCTINFO,omitempty\"`\n}\n\n// Name returns the name of the top-level transaction XML/SGML element\nfunc (air *AcctInfoResponse) Name() string {\n\treturn \"ACCTINFOTRNRS\"\n}\n\n// Valid returns (true, nil) if this struct was valid OFX when unmarshalled\nfunc (air *AcctInfoResponse) Valid(version ofxVersion) (bool, error) {\n\tif ok, err := air.TrnUID.Valid(); !ok {\n\t\treturn false, err\n\t}\n\t//TODO implement\n\treturn true, nil\n}\n\n// Type returns which message set this message belongs to (which Response\n// element of type []Message it belongs to)\nfunc (air *AcctInfoResponse) Type() messageType {\n\treturn SignupRs\n}\n"
  },
  {
    "path": "signup_test.go",
    "content": "package ofxgo\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestMarshalAcctInfoRequest(t *testing.T) {\n\tvar expectedString string = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<?OFX OFXHEADER=\"200\" VERSION=\"203\" SECURITY=\"NONE\" OLDFILEUID=\"NONE\" NEWFILEUID=\"NONE\"?>\n<OFX>\n\t<SIGNONMSGSRQV1>\n\t\t<SONRQ>\n\t\t\t<DTCLIENT>20160115112300.000[-5:EST]</DTCLIENT>\n\t\t\t<USERID>myusername</USERID>\n\t\t\t<USERPASS>Pa$$word</USERPASS>\n\t\t\t<LANGUAGE>ENG</LANGUAGE>\n\t\t\t<FI>\n\t\t\t\t<ORG>BNK</ORG>\n\t\t\t\t<FID>1987</FID>\n\t\t\t</FI>\n\t\t\t<APPID>OFXGO</APPID>\n\t\t\t<APPVER>0001</APPVER>\n\t\t</SONRQ>\n\t</SIGNONMSGSRQV1>\n\t<SIGNUPMSGSRQV1>\n\t\t<ACCTINFOTRNRQ>\n\t\t\t<TRNUID>e3ad9bda-38fa-4e5b-8099-1bd567ddef7a</TRNUID>\n\t\t\t<ACCTINFORQ>\n\t\t\t\t<DTACCTUP>20151221182945.000[-5:EST]</DTACCTUP>\n\t\t\t</ACCTINFORQ>\n\t\t</ACCTINFOTRNRQ>\n\t</SIGNUPMSGSRQV1>\n</OFX>`\n\n\tEST := time.FixedZone(\"EST\", -5*60*60)\n\n\tvar client = BasicClient{\n\t\tAppID:       \"OFXGO\",\n\t\tAppVer:      \"0001\",\n\t\tSpecVersion: OfxVersion203,\n\t}\n\n\tvar request Request\n\trequest.Signon.UserID = \"myusername\"\n\trequest.Signon.UserPass = \"Pa$$word\"\n\trequest.Signon.Org = \"BNK\"\n\trequest.Signon.Fid = \"1987\"\n\n\tacctInfoRequest := AcctInfoRequest{\n\t\tTrnUID:   \"e3ad9bda-38fa-4e5b-8099-1bd567ddef7a\",\n\t\tDtAcctUp: *NewDate(2015, 12, 21, 18, 29, 45, 0, EST),\n\t}\n\trequest.Signup = append(request.Signup, &acctInfoRequest)\n\n\trequest.SetClientFields(&client)\n\t// Overwrite the DtClient value set by SetClientFields to time.Now()\n\trequest.Signon.DtClient = *NewDate(2016, 1, 15, 11, 23, 0, 0, EST)\n\n\tmarshalCheckRequest(t, &request, expectedString)\n}\n\nfunc TestUnmarshalAcctInfoResponse(t *testing.T) {\n\tresponseReader := strings.NewReader(`<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<?OFX OFXHEADER=\"200\" VERSION=\"203\" SECURITY=\"NONE\" OLDFILEUID=\"NONE\" NEWFILEUID=\"NONE\"?>\n<OFX>\n\t<SIGNONMSGSRSV1>\n\t\t<SONRS>\n\t\t\t<STATUS>\n\t\t\t\t<CODE>0</CODE>\n\t\t\t\t<SEVERITY>INFO</SEVERITY>\n\t\t\t</STATUS>\n\t\t\t<DTSERVER>20060115112303</DTSERVER>\n\t\t\t<LANGUAGE>ENG</LANGUAGE>\n\t\t\t<DTPROFUP>20050221091300</DTPROFUP>\n\t\t\t<DTACCTUP>20060102160000</DTACCTUP>\n\t\t\t<FI>\n\t\t\t\t<ORG>BNK</ORG>\n\t\t\t\t<FID>1987</FID>\n\t\t\t</FI>\n\t\t</SONRS>\n\t</SIGNONMSGSRSV1>\n\t<SIGNUPMSGSRSV1>\n\t\t<ACCTINFOTRNRS>\n\t\t\t<TRNUID>10938754</TRNUID>\n\t\t\t<STATUS>\n\t\t\t\t<CODE>0</CODE>\n\t\t\t\t<SEVERITY>INFO</SEVERITY>\n\t\t\t</STATUS>\n\t\t\t<ACCTINFORS>\n\t\t\t\t<DTACCTUP>20050228</DTACCTUP>\n\t\t\t\t<ACCTINFO>\n\t\t\t\t\t<DESC>Personal Checking</DESC>\n\t\t\t\t\t<PHONE>888-222-5827</PHONE>\n\t\t\t\t\t<BANKACCTINFO>\n\t\t\t\t\t\t<BANKACCTFROM>\n\t\t\t\t\t\t\t<BANKID>8367556009</BANKID>\n\t\t\t\t\t\t\t<ACCTID>000999847</ACCTID>\n\t\t\t\t\t\t\t<ACCTTYPE>MONEYMRKT</ACCTTYPE>\n\t\t\t\t\t\t</BANKACCTFROM>\n\t\t\t\t\t\t<SUPTXDL>Y</SUPTXDL>\n\t\t\t\t\t\t<XFERSRC>Y</XFERSRC>\n\t\t\t\t\t\t<XFERDEST>Y</XFERDEST>\n\t\t\t\t\t\t<SVCSTATUS>ACTIVE</SVCSTATUS>\n\t\t\t\t\t</BANKACCTINFO>\n\t\t\t\t</ACCTINFO>\n\t\t\t</ACCTINFORS>\n\t\t</ACCTINFOTRNRS>\n\t</SIGNUPMSGSRSV1>\n</OFX>`)\n\tvar expected Response\n\n\texpected.Version = OfxVersion203\n\texpected.Signon.Status.Code = 0\n\texpected.Signon.Status.Severity = \"INFO\"\n\texpected.Signon.DtServer = *NewDateGMT(2006, 1, 15, 11, 23, 03, 0)\n\texpected.Signon.Language = \"ENG\"\n\texpected.Signon.DtProfUp = NewDateGMT(2005, 2, 21, 9, 13, 0, 0)\n\texpected.Signon.DtAcctUp = NewDateGMT(2006, 1, 2, 16, 0, 0, 0)\n\texpected.Signon.Org = \"BNK\"\n\texpected.Signon.Fid = \"1987\"\n\n\tbankacctinfo := BankAcctInfo{\n\t\tBankAcctFrom: BankAcct{\n\t\t\tBankID:   \"8367556009\",\n\t\t\tAcctID:   \"000999847\",\n\t\t\tAcctType: AcctTypeMoneyMrkt,\n\t\t},\n\t\tSupTxDl:   true,\n\t\tXferSrc:   true,\n\t\tXferDest:  true,\n\t\tSvcStatus: SvcStatusActive,\n\t}\n\n\tacctInfoResponse := AcctInfoResponse{\n\t\tTrnUID: \"10938754\",\n\t\tStatus: Status{\n\t\t\tCode:     0,\n\t\t\tSeverity: \"INFO\",\n\t\t},\n\t\tDtAcctUp: *NewDateGMT(2005, 2, 28, 0, 0, 0, 0),\n\t\tAcctInfo: []AcctInfo{{\n\t\t\tDesc:         \"Personal Checking\",\n\t\t\tPhone:        \"888-222-5827\",\n\t\t\tBankAcctInfo: &bankacctinfo,\n\t\t}},\n\t}\n\texpected.Signup = append(expected.Signup, &acctInfoResponse)\n\n\tresponse, err := ParseResponse(responseReader)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error unmarshalling response: %s\\n\", err)\n\t}\n\n\tcheckResponsesEqual(t, &expected, response)\n\tcheckResponseRoundTrip(t, response)\n}\n"
  },
  {
    "path": "types.go",
    "content": "package ofxgo\n\nimport (\n\t\"crypto/rand\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/aclindsa/xml\"\n\t\"golang.org/x/text/currency\"\n\t\"math/big\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\n// Int provides helper methods to unmarshal int64 values from SGML/XML\ntype Int int64\n\n// UnmarshalXML handles unmarshalling an Int from an SGML/XML string. Leading\n// and trailing whitespace is ignored.\nfunc (i *Int) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvalue = strings.TrimSpace(value)\n\n\ti2, err := strconv.ParseInt(value, 10, 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*i = Int(i2)\n\treturn nil\n}\n\n// Equal returns true if the two Ints are equal in value\nfunc (i Int) Equal(o Int) bool {\n\treturn i == o\n}\n\n// Amount represents non-integer values (or at least values for fields that may\n// not necessarily be integers)\ntype Amount struct {\n\tbig.Rat\n}\n\n// UnmarshalXML handles unmarshalling an Amount from an SGML/XML string.\n// Leading and trailing whitespace is ignored.\nfunc (a *Amount) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvalue = strings.TrimSpace(value)\n\n\t// The OFX spec allows the start of the fractional amount to be delineated\n\t// by a comma, so fix that up before attempting to parse it into big.Rat\n\tvalue = strings.Replace(value, \",\", \".\", 1)\n\n\tif _, ok := a.SetString(value); !ok {\n\t\treturn errors.New(\"Failed to parse OFX amount\")\n\t}\n\treturn nil\n}\n\n// String prints a string representation of an Amount\nfunc (a Amount) String() string {\n\treturn strings.TrimRight(strings.TrimRight(a.FloatString(100), \"0\"), \".\")\n}\n\n// MarshalXML marshals an Amount to SGML/XML\nfunc (a Amount) MarshalXML(e *xml.Encoder, start xml.StartElement) error {\n\treturn e.EncodeElement(a.String(), start)\n}\n\n// Equal returns true if two Amounts are equal in value\nfunc (a Amount) Equal(o Amount) bool {\n\treturn (&a).Cmp(&o.Rat) == 0\n}\n\n// Date represents OFX date/time values\ntype Date struct {\n\ttime.Time\n}\n\nvar ofxDateFormats = []string{\n\t\"20060102150405.000\",\n\t\"20060102150405\",\n\t\"200601021504\",\n\t\"2006010215\",\n\t\"20060102\",\n}\nvar ofxDateZoneRegex = regexp.MustCompile(`^([+-]?[0-9]+)(\\.([0-9]{2}))?(:([A-Z]+))?$`)\n\n// UnmarshalXML handles unmarshalling a Date from an SGML/XML string. It\n// attempts to unmarshal the valid date formats in order of decreasing length\n// and defaults to GMT if a time zone is not provided, as per the OFX spec.\n// Leading and trailing whitespace is ignored.\nfunc (od *Date) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value, zone, zoneFormat string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvalue = strings.SplitN(value, \"]\", 2)[0]\n\tvalue = strings.TrimSpace(value)\n\n\t// Split the time zone off, if any\n\tsplit := strings.SplitN(value, \"[\", 2)\n\tif len(split) == 2 {\n\t\tvalue = split[0]\n\t\tzoneFormat = \" -0700\"\n\t\tzone = strings.TrimRight(split[1], \"]\")\n\n\t\tmatches := ofxDateZoneRegex.FindStringSubmatch(zone)\n\t\tif matches == nil {\n\t\t\treturn errors.New(\"Invalid OFX Date timezone format: \" + zone)\n\t\t}\n\t\tvar err error\n\t\tvar zonehours, zoneminutes int\n\t\tzonehours, err = strconv.Atoi(matches[1])\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif len(matches[3]) > 0 {\n\t\t\tzoneminutes, err = strconv.Atoi(matches[3])\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tzoneminutes = zoneminutes * 60 / 100\n\t\t}\n\t\tzone = fmt.Sprintf(\" %+03d%02d\", zonehours, zoneminutes)\n\n\t\t// Get the time zone name if it's there, default to GMT if the offset\n\t\t// is 0 and a name isn't supplied\n\t\tif len(matches[5]) > 0 {\n\t\t\tzone = zone + \" \" + matches[5]\n\t\t\tzoneFormat = zoneFormat + \" MST\"\n\t\t} else if zonehours == 0 && zoneminutes == 0 {\n\t\t\tzone = zone + \" GMT\"\n\t\t\tzoneFormat = zoneFormat + \" MST\"\n\t\t}\n\t} else {\n\t\t// Default to GMT if no time zone was specified\n\t\tzone = \" +0000 GMT\"\n\t\tzoneFormat = \" -0700 MST\"\n\t}\n\n\t// Try all the date formats, from longest to shortest\n\tfor _, format := range ofxDateFormats {\n\t\tt, err := time.Parse(format+zoneFormat, value+zone)\n\t\tif err == nil {\n\t\t\tod.Time = t\n\t\t\treturn nil\n\t\t}\n\t}\n\treturn errors.New(\"OFX: Couldn't parse date:\" + value)\n}\n\n// String returns a string representation of the Date abiding by the OFX spec\nfunc (od Date) String() string {\n\tformat := od.Format(ofxDateFormats[0])\n\tzonename, zoneoffset := od.Zone()\n\tif zoneoffset < 0 {\n\t\tformat += \"[\" + fmt.Sprintf(\"%+d\", zoneoffset/3600)\n\t} else {\n\t\tformat += \"[\" + fmt.Sprintf(\"%d\", zoneoffset/3600)\n\t}\n\tfractionaloffset := (zoneoffset % 3600) / 36\n\tif fractionaloffset > 0 {\n\t\tformat += \".\" + fmt.Sprintf(\"%02d\", fractionaloffset)\n\t} else if fractionaloffset < 0 {\n\t\tformat += \".\" + fmt.Sprintf(\"%02d\", -fractionaloffset)\n\t}\n\n\tif len(zonename) > 0 {\n\t\treturn format + \":\" + zonename + \"]\"\n\t}\n\treturn format + \"]\"\n}\n\n// MarshalXML marshals a Date to XML\nfunc (od Date) MarshalXML(e *xml.Encoder, start xml.StartElement) error {\n\treturn e.EncodeElement(od.String(), start)\n}\n\n// Equal returns true if the two Dates represent the same time (time zones are\n// accounted for when comparing, but are not required to match)\nfunc (od Date) Equal(o Date) bool {\n\treturn od.Time.Equal(o.Time)\n}\n\n// NewDate returns a new Date object with the provided date, time, and timezone\nfunc NewDate(year int, month time.Month, day, hour, min, sec, nsec int, loc *time.Location) *Date {\n\treturn &Date{Time: time.Date(year, month, day, hour, min, sec, nsec, loc)}\n}\n\nvar gmt = time.FixedZone(\"GMT\", 0)\n\n// NewDateGMT returns a new Date object with the provided date and time in the\n// GMT timezone\nfunc NewDateGMT(year int, month time.Month, day, hour, min, sec, nsec int) *Date {\n\treturn &Date{Time: time.Date(year, month, day, hour, min, sec, nsec, gmt)}\n}\n\n// String provides helper methods to unmarshal OFX string values from SGML/XML\ntype String string\n\n// UnmarshalXML handles unmarshalling a String from an SGML/XML string. Leading\n// and trailing whitespace is ignored.\nfunc (os *String) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\t*os = String(strings.TrimSpace(value))\n\treturn nil\n}\n\n// String returns the string\nfunc (os *String) String() string {\n\treturn string(*os)\n}\n\n// Equal returns true if the two Strings are equal in value\nfunc (os String) Equal(o String) bool {\n\treturn os == o\n}\n\n// Boolean provides helper methods to unmarshal bool values from OFX SGML/XML\ntype Boolean bool\n\n// UnmarshalXML handles unmarshalling a Boolean from an SGML/XML string.\n// Leading and trailing whitespace is ignored.\nfunc (ob *Boolean) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\ttmpob := strings.TrimSpace(value)\n\tswitch tmpob {\n\tcase \"Y\":\n\t\t*ob = Boolean(true)\n\tcase \"N\":\n\t\t*ob = Boolean(false)\n\tdefault:\n\t\treturn errors.New(\"Invalid OFX Boolean\")\n\t}\n\treturn nil\n}\n\n// MarshalXML marshals a Boolean to XML\nfunc (ob Boolean) MarshalXML(e *xml.Encoder, start xml.StartElement) error {\n\tif ob {\n\t\treturn e.EncodeElement(\"Y\", start)\n\t}\n\treturn e.EncodeElement(\"N\", start)\n}\n\n// String returns a string representation of a Boolean value\nfunc (ob *Boolean) String() string {\n\treturn fmt.Sprintf(\"%v\", *ob)\n}\n\n// Equal returns true if the two Booleans are the same\nfunc (ob Boolean) Equal(o Boolean) bool {\n\treturn ob == o\n}\n\n// UID represents an UID according to the OFX spec\ntype UID string\n\n// UnmarshalXML handles unmarshalling an UID from an SGML/XML string. Leading\n// and trailing whitespace is ignored.\nfunc (ou *UID) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\t*ou = UID(strings.TrimSpace(value))\n\treturn nil\n}\n\n// RecommendedFormat returns true iff this UID meets the OFX specification's\n// recommendation that UIDs follow the standard UUID 36-character format\nfunc (ou UID) RecommendedFormat() (bool, error) {\n\tif len(ou) != 36 {\n\t\treturn false, errors.New(\"UID not 36 characters long\")\n\t}\n\tif ou[8] != '-' || ou[13] != '-' || ou[18] != '-' || ou[23] != '-' {\n\t\treturn false, errors.New(\"UID missing hyphens at the appropriate places\")\n\t}\n\treturn true, nil\n}\n\n// Valid returns true, nil if the UID is valid. This is less strict than\n// RecommendedFormat, and will always return true, nil if it does.\nfunc (ou UID) Valid() (bool, error) {\n\tif len(ou) == 0 || len(ou) > 36 {\n\t\treturn false, errors.New(\"UID invalid length\")\n\t}\n\treturn true, nil\n}\n\n// Equal returns true if the two UIDs are the same\nfunc (ou UID) Equal(o UID) bool {\n\treturn ou == o\n}\n\n// RandomUID creates a new randomly-generated UID\nfunc RandomUID() (*UID, error) {\n\tuidbytes := make([]byte, 16)\n\tn, err := rand.Read(uidbytes[:])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif n != 16 {\n\t\treturn nil, errors.New(\"RandomUID failed to read 16 random bytes\")\n\t}\n\tuid := UID(fmt.Sprintf(\"%08x-%04x-%04x-%04x-%012x\", uidbytes[:4], uidbytes[4:6], uidbytes[6:8], uidbytes[8:10], uidbytes[10:]))\n\treturn &uid, nil\n}\n\n// CurrSymbol represents an ISO-4217 currency\ntype CurrSymbol struct {\n\tcurrency.Unit\n}\n\n// UnmarshalXML handles unmarshalling a CurrSymbol from an SGML/XML string.\n// Leading and trailing whitespace is ignored.\nfunc (c *CurrSymbol) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {\n\tvar value string\n\n\terr := d.DecodeElement(&value, &start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvalue = strings.TrimSpace(value)\n\n\tunit, err := currency.ParseISO(value)\n\tif err != nil {\n\t\treturn errors.New(\"Error parsing CurrSymbol:\" + err.Error())\n\t}\n\tc.Unit = unit\n\treturn nil\n}\n\n// MarshalXML marshals a CurrSymbol to SGML/XML\nfunc (c CurrSymbol) MarshalXML(e *xml.Encoder, start xml.StartElement) error {\n\treturn e.EncodeElement(c.String(), start)\n}\n\n// Equal returns true if the two Currencies are the same\nfunc (c CurrSymbol) Equal(o CurrSymbol) bool {\n\treturn c.String() == o.String()\n}\n\n// Valid returns true, nil if the CurrSymbol is valid.\nfunc (c CurrSymbol) Valid() (bool, error) {\n\tif c.String() == \"XXX\" {\n\t\treturn false, fmt.Errorf(\"Invalid CurrSymbol: %s\", c.Unit)\n\t}\n\treturn true, nil\n}\n\n// NewCurrSymbol returns a new CurrSymbol given a three-letter ISO-4217\n// currency symbol as a string\nfunc NewCurrSymbol(s string) (*CurrSymbol, error) {\n\tunit, err := currency.ParseISO(s)\n\tif err != nil {\n\t\treturn nil, errors.New(\"Error parsing string to create new CurrSymbol:\" + err.Error())\n\t}\n\treturn &CurrSymbol{unit}, nil\n}\n"
  },
  {
    "path": "types_test.go",
    "content": "package ofxgo\n\nimport (\n\t\"fmt\"\n\t\"github.com/aclindsa/xml\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc getTypeName(i interface{}) string {\n\tval := reflect.ValueOf(i)\n\n\t// Do the same thing that encoding/xml does to get the name\n\tfor val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr {\n\t\tif val.IsNil() {\n\t\t\treturn \"\"\n\t\t}\n\t\tval = val.Elem()\n\t}\n\treturn val.Type().Name()\n}\n\nfunc marshalHelper(t *testing.T, expected string, i interface{}) {\n\tt.Helper()\n\ttypename := getTypeName(i)\n\texpectedstring := fmt.Sprintf(\"<%s>%s</%s>\", typename, expected, typename)\n\tb, err := xml.Marshal(i)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Marshal(%T): %s\\n\", i, err)\n\t}\n\tif string(b) != expectedstring {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", expectedstring, string(b))\n\t}\n}\n\nfunc unmarshalHelper2(t *testing.T, input string, expected interface{}, overwritten interface{}, eq func(a, b interface{}) bool) {\n\tt.Helper()\n\ttypename := getTypeName(expected)\n\tinputstring := fmt.Sprintf(\"<%s>%s</%s>\", typename, input, typename)\n\terr := xml.Unmarshal([]byte(inputstring), &overwritten)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error on xml.Unmarshal(%T): %s\\n\", expected, err)\n\t}\n\tif !eq(overwritten, expected) {\n\t\tt.Fatalf(\"Expected '%s', got '%s'\\n\", expected, overwritten)\n\t}\n}\n\nfunc unmarshalHelper(t *testing.T, input string, expected interface{}, overwritten interface{}) {\n\tt.Helper()\n\teq := func(a, b interface{}) bool {\n\t\treturn reflect.DeepEqual(a, b)\n\t}\n\tunmarshalHelper2(t, input, expected, overwritten, eq)\n}\n\nfunc TestMarshalInt(t *testing.T) {\n\tvar i Int = 927\n\tmarshalHelper(t, \"927\", &i)\n\ti = 0\n\tmarshalHelper(t, \"0\", &i)\n\ti = -768276587425\n\tmarshalHelper(t, \"-768276587425\", &i)\n}\n\nfunc TestUnmarshalInt(t *testing.T) {\n\tvar i, overwritten Int = -48394, 0\n\tunmarshalHelper(t, \"-48394\", &i, &overwritten)\n\ti = 0\n\tunmarshalHelper(t, \"0\", &i, &overwritten)\n\ti = 198237198\n\tunmarshalHelper(t, \"198237198\", &i, &overwritten)\n\t// Make sure stray newlines are handled properly\n\tunmarshalHelper(t, \"198237198\\n\", &i, &overwritten)\n\tunmarshalHelper(t, \"198237198\\n\\t\", &i, &overwritten)\n}\n\nfunc TestMarshalAmount(t *testing.T) {\n\tvar a Amount\n\n\ta.SetFrac64(8, 1)\n\tmarshalHelper(t, \"8\", &a)\n\ta.SetFrac64(1, 8)\n\tmarshalHelper(t, \"0.125\", &a)\n\ta.SetFrac64(-1, 200)\n\tmarshalHelper(t, \"-0.005\", &a)\n\ta.SetInt64(0)\n\tmarshalHelper(t, \"0\", &a)\n\ta.SetInt64(-768276587425)\n\tmarshalHelper(t, \"-768276587425\", &a)\n\ta.SetFrac64(1, 12)\n\tmarshalHelper(t, \"0.0833333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333\", &a)\n\n\ttype AmountStruct struct {\n\t\tA Amount\n\t}\n\tvar as AmountStruct\n\tas.A.SetFrac64(1, 8)\n\tmarshalHelper(t, \"<A>0.125</A>\", as)\n}\n\nfunc TestUnmarshalAmount(t *testing.T) {\n\tvar a, overwritten Amount\n\n\t// Amount/big.Rat needs a special equality test because reflect.DeepEqual\n\t// doesn't always return equal for two values that big.Rat.Cmp() does\n\teq := func(a, b interface{}) bool {\n\t\tif amountA, ok := a.(*Amount); ok {\n\t\t\tif amountB, ok2 := b.(*Amount); ok2 {\n\t\t\t\treturn amountA.Cmp(&amountB.Rat) == 0\n\t\t\t}\n\t\t}\n\t\treturn false\n\t}\n\n\ta.SetFrac64(12, 1)\n\tunmarshalHelper2(t, \"12\", &a, &overwritten, eq)\n\ta.SetFrac64(-21309, 100)\n\tunmarshalHelper2(t, \"-213.09\", &a, &overwritten, eq)\n\ta.SetFrac64(8192, 1000)\n\tunmarshalHelper2(t, \"8.192\", &a, &overwritten, eq)\n\tunmarshalHelper2(t, \"+8.192\", &a, &overwritten, eq)\n\ta.SetInt64(0)\n\tunmarshalHelper2(t, \"0\", &a, &overwritten, eq)\n\tunmarshalHelper2(t, \"+0\", &a, &overwritten, eq)\n\tunmarshalHelper2(t, \"-0\", &a, &overwritten, eq)\n\ta.SetInt64(-19487135)\n\tunmarshalHelper2(t, \"-19487135\", &a, &overwritten, eq)\n\t// Make sure stray newlines are handled properly\n\tunmarshalHelper2(t, \"-19487135\\n\", &a, &overwritten, eq)\n\tunmarshalHelper2(t, \"-19487135\\n \\t \", &a, &overwritten, eq)\n}\n\nfunc TestAmountEqual(t *testing.T) {\n\tassertEq := func(a, b Amount) {\n\t\tif !a.Equal(b) {\n\t\t\tt.Fatalf(\"Amounts should be equal but Equal returned false: %s and %s\\n\", a, b)\n\t\t}\n\t}\n\tassertNEq := func(a, b Amount) {\n\t\tif a.Equal(b) {\n\t\t\tt.Fatalf(\"Amounts should not be equal but Equal returned true: %s and %s\\n\", a, b)\n\t\t}\n\t}\n\n\tvar a, b Amount\n\ta.SetInt64(-19487135)\n\tb.SetInt64(-19487135)\n\tassertEq(a, b)\n\tb.SetInt64(19487135)\n\tassertNEq(a, b)\n\tb.SetInt64(0)\n\tassertNEq(a, b)\n\ta.SetInt64(-0)\n\tassertEq(a, b)\n\ta.SetFrac64(1, 1000000000000000000)\n\tb.SetFrac64(1, 1000000000000000001)\n\tassertNEq(a, b)\n}\n\nfunc TestMarshalDate(t *testing.T) {\n\tvar d *Date\n\tUTC := time.FixedZone(\"UTC\", 0)\n\tGMTNodesc := time.FixedZone(\"\", 0)\n\tEST := time.FixedZone(\"EST\", -5*60*60)\n\tNPT := time.FixedZone(\"NPT\", (5*60+45)*60)\n\tIST := time.FixedZone(\"IST\", (5*60+30)*60)\n\tNST := time.FixedZone(\"NST\", -(3*60+30)*60)\n\n\td = NewDateGMT(2017, 3, 14, 15, 9, 26, 53*1000*1000)\n\tmarshalHelper(t, \"20170314150926.053[0:GMT]\", d)\n\td = NewDate(2017, 3, 14, 15, 9, 26, 53*1000*1000, NPT)\n\tmarshalHelper(t, \"20170314150926.053[5.75:NPT]\", d)\n\td = NewDate(2017, 3, 14, 15, 9, 26, 53*1000*1000, EST)\n\tmarshalHelper(t, \"20170314150926.053[-5:EST]\", d)\n\td = NewDate(2017, 3, 14, 15, 9, 26, 53*1000*1000, UTC)\n\tmarshalHelper(t, \"20170314150926.053[0:UTC]\", d)\n\td = NewDate(2017, 3, 14, 15, 9, 26, 53*1000*1000, IST)\n\tmarshalHelper(t, \"20170314150926.053[5.50:IST]\", d)\n\td = NewDate(9999, 11, 1, 23, 59, 59, 1000, EST)\n\tmarshalHelper(t, \"99991101235959.000[-5:EST]\", d)\n\td = NewDate(0, 1, 1, 0, 0, 0, 0, IST)\n\tmarshalHelper(t, \"00000101000000.000[5.50:IST]\", d)\n\td = &Date{Time: time.Unix(0, 0).In(UTC)}\n\tmarshalHelper(t, \"19700101000000.000[0:UTC]\", d)\n\td = NewDate(2017, 3, 14, 0, 0, 26, 53*1000*1000, EST)\n\tmarshalHelper(t, \"20170314000026.053[-5:EST]\", d)\n\td = NewDate(2017, 3, 14, 0, 0, 26, 53*1000*1000, NST)\n\tmarshalHelper(t, \"20170314000026.053[-3.50:NST]\", d)\n\n\t// Time zone without textual description\n\td = NewDate(2017, 3, 14, 15, 9, 26, 53*1000*1000, GMTNodesc)\n\tmarshalHelper(t, \"20170314150926.053[0]\", d)\n\n\ttype DateStruct struct {\n\t\tD Date\n\t}\n\td = NewDateGMT(2017, 3, 14, 15, 9, 26, 53*1000*1000)\n\tds := DateStruct{D: *d}\n\tmarshalHelper(t, \"<D>20170314150926.053[0:GMT]</D>\", ds)\n}\n\nfunc TestUnmarshalDate(t *testing.T) {\n\tvar d *Date\n\tvar overwritten Date\n\tGMT := time.FixedZone(\"GMT\", 0)\n\tEST := time.FixedZone(\"EST\", -5*60*60)\n\tNPT := time.FixedZone(\"NPT\", (5*60+45)*60)\n\tIST := time.FixedZone(\"IST\", (5*60+30)*60)\n\tNST := time.FixedZone(\"NST\", -(3*60+30)*60)\n\tNSTNodesc := time.FixedZone(\"\", -(3*60+30)*60)\n\n\teq := func(a, b interface{}) bool {\n\t\tif dateA, ok := a.(*Date); ok {\n\t\t\tif dateB, ok2 := b.(*Date); ok2 {\n\t\t\t\treturn dateA.Equal(*dateB)\n\t\t\t}\n\t\t}\n\t\treturn false\n\t}\n\n\t// Ensure omitted fields default to the correct values\n\td = NewDateGMT(2017, 3, 14, 15, 9, 26, 53*1000*1000)\n\tunmarshalHelper2(t, \"20170314150926.053[0]\", d, &overwritten, eq)\n\tunmarshalHelper2(t, \"20170314150926.053\", d, &overwritten, eq)\n\td = NewDate(2017, 3, 14, 0, 0, 0, 0, GMT)\n\tunmarshalHelper2(t, \"20170314\", d, &overwritten, eq)\n\n\t// Ensure all signs on time zone offsets are properly handled\n\td = NewDateGMT(2017, 3, 14, 15, 9, 26, 53*1000*1000)\n\tunmarshalHelper2(t, \"20170314150926.053[0:GMT]\", d, &overwritten, eq)\n\tunmarshalHelper2(t, \"20170314150926.053[+0:GMT]\", d, &overwritten, eq)\n\tunmarshalHelper2(t, \"20170314150926.053[-0:GMT]\", d, &overwritten, eq)\n\tunmarshalHelper2(t, \"20170314150926.053[0]\", d, &overwritten, eq)\n\tunmarshalHelper2(t, \"20170314150926.053[+0]\", d, &overwritten, eq)\n\tunmarshalHelper2(t, \"20170314150926.053[-0]\", d, &overwritten, eq)\n\n\td = NewDate(2017, 3, 14, 15, 9, 26, 53*1000*1000, NPT)\n\tunmarshalHelper2(t, \"20170314150926.053[5.75:NPT]\", d, &overwritten, eq)\n\td = NewDate(2017, 3, 14, 15, 9, 26, 53*1000*1000, EST)\n\tunmarshalHelper2(t, \"20170314150926.053[-5:EST]\", d, &overwritten, eq)\n\td = NewDate(2017, 3, 14, 15, 9, 26, 53*1000*1000, GMT)\n\tunmarshalHelper2(t, \"20170314150926.053[0:GMT]\", d, &overwritten, eq)\n\td = NewDate(2017, 3, 14, 15, 9, 26, 53*1000*1000, IST)\n\tunmarshalHelper2(t, \"20170314150926.053[5.50:IST]\", d, &overwritten, eq)\n\td = NewDate(2018, 11, 1, 23, 59, 58, 0, EST)\n\tunmarshalHelper2(t, \"20181101235958.000[-5:EST]\", d, &overwritten, eq)\n\td = NewDate(0, 1, 1, 0, 0, 0, 0, IST)\n\tunmarshalHelper2(t, \"00000101000000.000[5.50:IST]\", d, &overwritten, eq)\n\td = &Date{Time: time.Unix(0, 0).In(GMT)}\n\tunmarshalHelper2(t, \"19700101000000.000[0:GMT]\", d, &overwritten, eq)\n\td = NewDate(2017, 3, 14, 0, 0, 26, 53*1000*1000, EST)\n\tunmarshalHelper2(t, \"20170314000026.053[-5:EST]\", d, &overwritten, eq)\n\td = NewDate(2017, 3, 14, 0, 0, 26, 53*1000*1000, NST)\n\tunmarshalHelper2(t, \"20170314000026.053[-3.50:NST]\", d, &overwritten, eq)\n\n\t// Autopopulate zone without textual description for GMT\n\td = NewDate(2017, 3, 14, 15, 9, 26, 53*1000*1000, GMT)\n\tunmarshalHelper2(t, \"20170314150926.053[0]\", d, &overwritten, eq)\n\t// but not for others:\n\td = NewDate(2017, 3, 14, 0, 0, 26, 53*1000*1000, NSTNodesc)\n\tunmarshalHelper2(t, \"20170314000026.053[-3.50]\", d, &overwritten, eq)\n\n\t// Make sure we handle poorly-formatted dates (from Vanguard)\n\td = NewDate(2016, 12, 7, 16, 0, 0, 0, EST)\n\tunmarshalHelper2(t, \"20161207160000.000[-5:EST]610900.500[-9:BST]\", d, &overwritten, eq) // extra part intentionally different to ensure the first timezone is parsed\n\n\t// Make sure we properly handle ending newlines\n\td = NewDate(2018, 11, 1, 23, 59, 58, 0, EST)\n\tunmarshalHelper2(t, \"20181101235958.000[-5:EST]\\n\", d, &overwritten, eq)\n\tunmarshalHelper2(t, \"20181101235958.000[-5:EST]\\n\\t\", d, &overwritten, eq)\n}\n\nfunc TestDateEqual(t *testing.T) {\n\tGMT := time.FixedZone(\"GMT\", 0)\n\tEST := time.FixedZone(\"EST\", -5*60*60)\n\n\tassertEq := func(a, b *Date) {\n\t\tif !a.Equal(*b) {\n\t\t\tt.Fatalf(\"Dates should be equal but Equal returned false: %s and %s\\n\", *a, *b)\n\t\t}\n\t}\n\tassertNEq := func(a, b *Date) {\n\t\tif a.Equal(*b) {\n\t\t\tt.Fatalf(\"Dates should not be equal but Equal returned true: %s and %s\\n\", *a, *b)\n\t\t}\n\t}\n\n\t// Ensure omitted fields default to the correct values\n\tgmt1 := NewDateGMT(2017, 3, 14, 15, 9, 26, 53*1000*1000)\n\tgmt2 := NewDate(2017, 3, 14, 15, 9, 26, 53*1000*1000, GMT)\n\test1 := NewDate(2017, 3, 14, 10, 9, 26, 53*1000*1000, EST)\n\test2 := NewDate(2017, 3, 14, 10, 9, 26, 53*1000*1000+1, EST)\n\test3 := NewDate(2017, 3, 14, 15, 9, 26, 53*1000*1000, EST)\n\n\tassertEq(gmt1, gmt2)\n\tassertEq(gmt2, gmt1)\n\tassertEq(gmt1, est1)\n\n\tassertNEq(gmt1, est2)\n\tassertNEq(est1, est2)\n\tassertNEq(gmt1, est3)\n}\n\nfunc TestMarshalString(t *testing.T) {\n\tvar s String = \"\"\n\tmarshalHelper(t, \"\", &s)\n\ts = \"foo&bar\"\n\tmarshalHelper(t, \"foo&amp;bar\", &s)\n\ts = \"\\n\"\n\tmarshalHelper(t, \"&#xA;\", &s)\n\ts = \"Some Name\"\n\tmarshalHelper(t, \"Some Name\", &s)\n}\n\nfunc TestUnmarshalString(t *testing.T) {\n\tvar s, overwritten String = \"\", \"\"\n\tunmarshalHelper(t, \"\", &s, &overwritten)\n\ts = \"foo&bar\"\n\tunmarshalHelper(t, \"foo&amp;bar\", &s, &overwritten)\n\t// whitespace intentionally stripped because some OFX servers add newlines\n\t// inside tags\n\ts = \"new\\nline\"\n\tunmarshalHelper(t, \" new&#xA;line&#xA;\", &s, &overwritten)\n\ts = \"Some Name\"\n\tunmarshalHelper(t, \"Some Name\", &s, &overwritten)\n\t// Make sure stray newlines are handled properly\n\tunmarshalHelper(t, \"Some Name\\n\", &s, &overwritten)\n\tunmarshalHelper(t, \"Some Name\\n  \", &s, &overwritten)\n}\n\nfunc TestStringString(t *testing.T) {\n\tvar s String = \"foobar\"\n\tif s.String() != \"foobar\" {\n\t\tt.Fatalf(\"Unexpected result when returning String.String(): %s\\n\", s.String())\n\t}\n}\n\nfunc TestMarshalBoolean(t *testing.T) {\n\tvar b Boolean = true\n\tmarshalHelper(t, \"Y\", &b)\n\tb = false\n\tmarshalHelper(t, \"N\", &b)\n\n\ttype BooleanStruct struct {\n\t\tB Boolean\n\t}\n\tbs := BooleanStruct{B: true}\n\tmarshalHelper(t, \"<B>Y</B>\", bs)\n}\n\nfunc TestUnmarshalBoolean(t *testing.T) {\n\tvar b, overwritten Boolean = true, false\n\tunmarshalHelper(t, \"Y\", &b, &overwritten)\n\tb = false\n\tunmarshalHelper(t, \"N\", &b, &overwritten)\n\t// Make sure stray newlines are handled properly\n\tunmarshalHelper(t, \"N\\n\", &b, &overwritten)\n\tunmarshalHelper(t, \"N\\n \\t\", &b, &overwritten)\n}\n\nfunc TestStringBoolean(t *testing.T) {\n\tvar b Boolean = true\n\tif b.String() != \"true\" {\n\t\tt.Fatalf(\"Unexpected string for Boolean.String() for true: %s\\n\", b.String())\n\t}\n\tb = false\n\tif b.String() != \"false\" {\n\t\tt.Fatalf(\"Unexpected string for Boolean.String() for false: %s\\n\", b.String())\n\t}\n}\n\nfunc TestMarshalUID(t *testing.T) {\n\tvar u UID = \"d1cf3d3d-9ef9-4a97-b180-81706829cb04\"\n\tmarshalHelper(t, \"d1cf3d3d-9ef9-4a97-b180-81706829cb04\", &u)\n}\n\nfunc TestUnmarshalUID(t *testing.T) {\n\tvar u, overwritten UID = \"d1cf3d3d-9ef9-4a97-b180-81706829cb04\", \"\"\n\tunmarshalHelper(t, \"d1cf3d3d-9ef9-4a97-b180-81706829cb04\", &u, &overwritten)\n\t// Make sure stray newlines are handled properly\n\tu = \"0f94ce83-13b7-7568-e4fc-c02c7b47e7ab\"\n\tunmarshalHelper(t, \"0f94ce83-13b7-7568-e4fc-c02c7b47e7ab\\n\", &u, &overwritten)\n\tunmarshalHelper(t, \"0f94ce83-13b7-7568-e4fc-c02c7b47e7ab\\n\\t\", &u, &overwritten)\n}\n\nfunc TestUIDRecommendedFormat(t *testing.T) {\n\tvar u UID = \"d1cf3d3d-9ef9-4a97-b180-81706829cb04\"\n\tif ok, err := u.RecommendedFormat(); !ok || err != nil {\n\t\tt.Fatalf(\"UID unexpectedly failed validation\\n\")\n\t}\n\tu = \"d1cf3d3d-9ef9-4a97-b180-81706829cb0\"\n\tif ok, err := u.RecommendedFormat(); ok || err == nil {\n\t\tt.Fatalf(\"UID should have failed validation because it's too short\\n\")\n\t}\n\tu = \"d1cf3d3d-9ef94a97-b180-81706829cb04\"\n\tif ok, err := u.RecommendedFormat(); ok || err == nil {\n\t\tt.Fatalf(\"UID should have failed validation because it's missing hyphens\\n\")\n\t}\n\tu = \"d1cf3d3d-9ef9-4a97-b180981706829cb04\"\n\tif ok, err := u.RecommendedFormat(); ok || err == nil {\n\t\tt.Fatalf(\"UID should have failed validation because its hyphens have been replaced\\n\")\n\t}\n}\n\nfunc TestUIDValid(t *testing.T) {\n\tvar u UID = \"\"\n\tif ok, err := u.Valid(); ok || err == nil {\n\t\tt.Fatalf(\"Empty UID unexpectedly valid\\n\")\n\t}\n\tu = \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\tif ok, err := u.Valid(); ok || err == nil {\n\t\tt.Fatalf(\"Too-long UID unexpectedly valid\\n\")\n\t}\n\tu = \"7be37076-623a-425f-ae6b-a5465b7e93b0\"\n\tif ok, err := u.Valid(); !ok || err != nil {\n\t\tt.Fatalf(\"Good UID unexpectedly invalid: %s\\n\", err.Error())\n\t}\n}\n\nfunc TestRandomUID(t *testing.T) {\n\tuid, err := RandomUID()\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error when calling RandomUID: %s\\n\", err)\n\t}\n\tif ok, err := uid.RecommendedFormat(); !ok || err != nil {\n\t\tt.Fatalf(\"UID generated with RandomUID() doesn't match recommended format: %s\\n\", err)\n\t}\n}\n\nfunc TestMarshalCurrSymbol(t *testing.T) {\n\tc, _ := NewCurrSymbol(\"USD\")\n\tmarshalHelper(t, \"USD\", &c)\n\n\ttype CurrSymbolStruct struct {\n\t\tCS CurrSymbol\n\t}\n\tcss := CurrSymbolStruct{CS: *c}\n\tmarshalHelper(t, \"<CS>USD</CS>\", css)\n}\n\nfunc TestUnmarshalCurrSymbol(t *testing.T) {\n\tvar overwritten CurrSymbol\n\tc, _ := NewCurrSymbol(\"USD\")\n\tunmarshalHelper(t, \"USD\", c, &overwritten)\n\t// Make sure stray newlines are handled properly\n\tc, _ = NewCurrSymbol(\"EUR\")\n\tunmarshalHelper(t, \"EUR\\n\", c, &overwritten)\n\tunmarshalHelper(t, \"EUR\\n\\t\", c, &overwritten)\n}\n\nfunc TestCurrSymbolEqual(t *testing.T) {\n\tusd1, _ := NewCurrSymbol(\"USD\")\n\tusd2, _ := NewCurrSymbol(\"USD\")\n\tif !usd1.Equal(*usd2) {\n\t\tt.Fatalf(\"Two \\\"USD\\\" CurrSymbols returned !Equal()\\n\")\n\t}\n\txxx, _ := NewCurrSymbol(\"XXX\")\n\tif usd1.Equal(*xxx) {\n\t\tt.Fatalf(\"\\\"USD\\\" and \\\"XXX\\\" CurrSymbols returned Equal()\\n\")\n\t}\n}\n\nfunc TestCurrSymbolValid(t *testing.T) {\n\tvar initial CurrSymbol\n\tok, err := initial.Valid()\n\tif ok || err == nil {\n\t\tt.Fatalf(\"CurrSymbol unexpectedly returned Valid() for initial value\\n\")\n\t}\n\n\tars, _ := NewCurrSymbol(\"ARS\")\n\tok, err = ars.Valid()\n\tif !ok || err != nil {\n\t\tt.Fatalf(\"CurrSymbol unexpectedly returned !Valid() for \\\"ARS\\\": %s\\n\", err.Error())\n\t}\n\n\txxx, _ := NewCurrSymbol(\"XXX\")\n\tok, err = xxx.Valid()\n\tif ok || err == nil {\n\t\tt.Fatalf(\"CurrSymbol unexpectedly returned Valid() for \\\"XXX\\\"\\n\")\n\t}\n}\n\nfunc TestNewCurrSymbol(t *testing.T) {\n\tcurr, err := NewCurrSymbol(\"GBP\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error calling NewCurrSymbol: %s\\n\", err)\n\t}\n\tif curr.String() != \"GBP\" {\n\t\tt.Fatalf(\"Created CurrSymbol doesn't print \\\"GBP\\\" as string representation\\n\")\n\t}\n\tcurr, err = NewCurrSymbol(\"AFN\")\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error calling NewCurrSymbol: %s\\n\", err)\n\t}\n\tif curr.String() != \"AFN\" {\n\t\tt.Fatalf(\"Created CurrSymbol doesn't print \\\"AFN\\\" as string representation\\n\")\n\t}\n\tcurr, err = NewCurrSymbol(\"BLAH\")\n\tif err == nil {\n\t\tt.Fatalf(\"NewCurrSymbol didn't error on invalid currency identifier\\n\")\n\t}\n}\n"
  },
  {
    "path": "util.go",
    "content": "package ofxgo\n\nimport (\n\t\"bytes\"\n\t\"github.com/aclindsa/xml\"\n)\n\n// Returns the next available Token from the xml.Decoder that is not CharData\n// made up entirely of whitespace. This is useful to skip whitespace when\n// manually unmarshaling XML.\nfunc nextNonWhitespaceToken(decoder *xml.Decoder) (xml.Token, error) {\n\tfor {\n\t\ttok, err := decoder.Token()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t} else if chars, ok := tok.(xml.CharData); ok {\n\t\t\tstrippedBytes := bytes.TrimSpace(chars)\n\t\t\tif len(strippedBytes) != 0 {\n\t\t\t\treturn tok, nil\n\t\t\t}\n\t\t} else {\n\t\t\treturn tok, nil\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "vanguard_client.go",
    "content": "package ofxgo\n\nimport (\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// VanguardClient provides a Client implementation which handles Vanguard's\n// cookie-passing requirements and also enables older, disabled-by-default\n// cipher suites. VanguardClient uses default, non-zero settings, if its fields\n// are not initialized.\ntype VanguardClient struct {\n\t*BasicClient\n}\n\n// NewVanguardClient returns a Client interface configured to handle Vanguard's\n// brand of idiosyncrasy\nfunc NewVanguardClient(bc *BasicClient) Client {\n\treturn &VanguardClient{bc}\n}\n\n// vanguardHttpClient returns an http.Client with the default supported\n// ciphers plus the insecure ciphers Vanguard still uses.\nfunc vanguardHttpClient() *http.Client {\n\tvar clientCiphers []uint16\n\n\tvanguardCiphers := []uint16{\n\t\ttls.TLS_RSA_WITH_AES_128_CBC_SHA256,\n\t\ttls.TLS_RSA_WITH_AES_128_GCM_SHA256,\n\t\ttls.TLS_RSA_WITH_AES_256_GCM_SHA384,\n\t}\n\tdefaultCiphers := tls.CipherSuites()\n\tfor _, cipher := range defaultCiphers {\n\t\tclientCiphers = append(clientCiphers, cipher.ID)\n\t}\n\tclientCiphers = append(clientCiphers, vanguardCiphers...)\n\n\tclient := &http.Client{\n\t\tTransport: &http.Transport{\n\t\t\tTLSClientConfig: &tls.Config{\n\t\t\t\tCipherSuites: clientCiphers,\n\t\t\t},\n\t\t},\n\t}\n\treturn client\n}\n\n// RawRequest is a copy of BasicClient RawRequest with a custom http.Client\n// that enables older cipher suites.\nfunc (c *VanguardClient) RawRequest(URL string, r io.Reader) (*http.Response, error) {\n\tif !strings.HasPrefix(URL, \"https://\") {\n\t\treturn nil, errors.New(\"Refusing to send OFX request with possible plain-text password over non-https protocol\")\n\t}\n\n\trequest, err := http.NewRequest(\"POST\", URL, r)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\trequest.Header.Set(\"Content-Type\", \"application/x-ofx\")\n\tif c.UserAgent != \"\" {\n\t\trequest.Header.Set(\"User-Agent\", c.UserAgent)\n\t}\n\tclient := vanguardHttpClient()\n\tresponse, err := client.Do(request)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif response.StatusCode != 200 {\n\t\treturn response, errors.New(\"OFXQuery request status: \" + response.Status)\n\t}\n\n\treturn response, nil\n}\n\n// rawRequestCookiesInsecureCiphers is RawRequest with the added features of\n// sending cookies and using a custom http.Client that enables older cipher\n// suites which are disabled by default\nfunc rawRequestCookiesInsecureCiphers(URL string, r io.Reader, cookies []*http.Cookie) (*http.Response, error) {\n\tif !strings.HasPrefix(URL, \"https://\") {\n\t\treturn nil, errors.New(\"Refusing to send OFX request with possible plain-text password over non-https protocol\")\n\t}\n\n\trequest, err := http.NewRequest(\"POST\", URL, r)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\trequest.Header.Set(\"Content-Type\", \"application/x-ofx\")\n\tfor _, cookie := range cookies {\n\t\trequest.AddCookie(cookie)\n\t}\n\n\tclient := vanguardHttpClient()\n\tresponse, err := client.Do(request)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif response.StatusCode != 200 {\n\t\treturn nil, errors.New(\"OFXQuery request status: \" + response.Status)\n\t}\n\n\treturn response, nil\n}\n\n// RequestNoParse marshals a Request to XML, makes an HTTP request, and returns\n// the raw HTTP response\nfunc (c *VanguardClient) RequestNoParse(r *Request) (*http.Response, error) {\n\tr.SetClientFields(c)\n\n\tb, err := r.Marshal()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tresponse, err := c.RawRequest(r.URL, b)\n\n\t// Some financial institutions (cough, Vanguard, cough), require a cookie\n\t// to be set on the http request, or they return empty responses.\n\t// Fortunately, the initial response contains the cookie we need, so if we\n\t// detect an empty response with cookies set that didn't have any errors,\n\t// re-try the request while sending their cookies back to them.\n\tif response != nil && response.ContentLength <= 0 && len(response.Cookies()) > 0 {\n\t\tb, err = r.Marshal()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\treturn rawRequestCookiesInsecureCiphers(r.URL, b, response.Cookies())\n\t}\n\n\treturn response, err\n}\n\n// Request marshals a Request to XML, makes an HTTP request, and then\n// unmarshals the response into a Response object.\nfunc (c *VanguardClient) Request(r *Request) (*Response, error) {\n\treturn clientRequest(c, r)\n}\n"
  }
]