Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ofx support for treasury T-Bill #187

Merged
merged 18 commits into from
Jul 15, 2023
Merged

Conversation

zhou13
Copy link
Contributor

@zhou13 zhou13 commented Feb 6, 2023

  • Add ofx support for treasury T-Bill
  • Workaround the issues that Fidelity OFX shows the units of treasuries 100x larger.

@zhou13 zhou13 changed the title Add ofx support for treasure T-Bill Add ofx support for treasury T-Bill Feb 6, 2023
Copy link
Collaborator

@Zburatorul Zburatorul left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zhou13, thank you for identifying a real gap in ofx.py and closing it.

@@ -785,6 +786,10 @@ def get_security(unique_id: str) -> Optional[str]:
return None
sec = securities_map[unique_id]
ticker = sec.ticker
# Treasury bill and bond start with 912
if ticker.startswith("912"):
# Append "T" to make it a valid ticker
Copy link
Collaborator

@Zburatorul Zburatorul Feb 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this meant to be prepend rather than "append"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, fixed.

@@ -785,6 +786,10 @@ def get_security(unique_id: str) -> Optional[str]:
return None
sec = securities_map[unique_id]
ticker = sec.ticker
# Treasury bill and bond start with 912
if ticker.startswith("912"):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the ticker of a bond always the same as its CUSIP?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is at least for Fidelity's Ofx. I don't have other OFX so I am not sure about it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can verify this is also true for vanguard treasury.

@zhou13
Copy link
Contributor Author

zhou13 commented Feb 7, 2023

Pytest runs fine locally, not sure what happens. Also the balance change make a lot of changes to the existing tests.

@Zburatorul
Copy link
Collaborator

Pytest runs fine locally, not sure what happens. Also the balance change make a lot of changes to the existing tests.

That's exactly what happens. We need to figure out if the changes are correct or not.

Here is the OFX specification that describes the meaning of each field in detail.

For UNITS it says:

For stocks, MFs, other, number of shares held.
Bonds = face value.
Options = number of contracts

Meanwhile for UNITPRICE it saysBonds = percentage of par. Meaning a number from 0 to 100, not a dollar amount.
We could just ignore all this and record UNITPRICE as the price, but it would produce incorrect (about 100X inflated) asset values. This also explains why Fidelity has that 100X factor from your original commit.

There are two ways out: either we modify the amount, or the price.
I'm leaning towards transforming the price to match beancount't expectation of what a price is (market value/quantity of security) rather than modifying the quantity/units.
There's a subcase where we detect that it's a bond, and simply divide the price by 100X. But my suggested code takes care of that AND of situations where the multiplier is not 100.

There are a number of ofx test files where market value != price * units. That is why the tests are failing.
For example testdata/source/ofx/vanguard_roth_ira.ofx has:

  <UNITS>46.872
  <UNITPRICE>84.20
  <MKTVAL>72.85

But it's not difficult to fix them, and it would result in a PR that is more general and more robust, in my opinion.
The work consists of inspecting each failed test and seeing that 1) no balance entries are modified 2) the new price indeed produces the correct market value.

We're really close here. Are you willing to get it over the finish line?

@zhou13
Copy link
Contributor Author

zhou13 commented Feb 26, 2023

Do you know how to reproduce the CI error locally? Currently running

pytest -s beancount_import/source/ofx_test.py

on my branch give me no error (only warnings).

@Zburatorul
Copy link
Collaborator

Maybe you have a local file that that you forgot to check in?

@Zburatorul
Copy link
Collaborator

Here's a sample error:

E - 2018-07-03 price TYCDT 1.554232804232804232804232804 USD
E + 2018-07-03 price TYCDT 84.20 USD

It indicates that you changed the price, but did not modify the expected output file. Please double-check that the new price is actually correct.

@zhou13
Copy link
Contributor Author

zhou13 commented Feb 26, 2023

Here is what I just did:

git clone https://github.com/zhou13/beancount-import bc2
cd bc2
git checkout fidelity-buydebt
pip install git+https://github.com/zhou13/beancount-import@fidelity-buydebt
pytest -s beancount_import/source/ofx_test.py

It passes all the test. The change E - 2018-07-03 price TYCDT 1.554232804232804232804232804 USD is from executing check_source_example(..., write=True) on my local machine. I don't understand why the result is different on the CI.

@Zburatorul
Copy link
Collaborator

Yeah it is strange. I don't understand why the file in the PR is different from the one in the test.
Try git push --force once again, and let's re-run the test.

If nothing changes, then I suggest you revert the testdata/source/ofx/test_vanguard_roth_ira_matching/import_results.beancount back to master, commit & force push that. We might get a new clue from that test passing/failing in CI.

@zhou13
Copy link
Contributor Author

zhou13 commented Apr 16, 2023

@Zburatorul I think this PR is ready to go. Previously I missed a test, which is why it fails. I checked investment_buy_sell_income.ofx and I believe the new result is more likely to be correct as MKTVAL is a more reliable entry when it conflicts with UNITPRICE.

Copy link
Collaborator

@Zburatorul Zburatorul left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few questions

@zhou13 zhou13 force-pushed the fidelity-buydebt branch from e25268f to 19e9317 Compare July 11, 2023 22:02
@zhou13
Copy link
Contributor Author

zhou13 commented Jul 12, 2023

@Zburatorul I have been using this branch for a while for Fidelity Treasury bill and it works well for me. I also add tolerance to workaround buggy rounding of the unit entry in Vanguard's bond ofx so it can generate reasonable balance. Let me know if you have any additional concerns.

@Zburatorul Zburatorul merged commit e790b64 into jbms:master Jul 15, 2023
@Zburatorul
Copy link
Collaborator

Thank you for the contribution @zhou13!

@zhou13 zhou13 deleted the fidelity-buydebt branch July 16, 2023 03:13
@@ -797,6 +833,10 @@ def get_security(unique_id: str) -> Optional[str]:
return None
sec = securities_map[unique_id]
ticker = sec.ticker
# Treasury bill and bond start with 912
if ticker.startswith("912"):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, this will crash if the ticker is None, rather than printing a helpful error message as it used to.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants