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

feat: support BitMEX wallet history imports #6426

Merged
merged 1 commit into from Aug 1, 2023

Conversation

IanMichaelHarper
Copy link
Contributor

@IanMichaelHarper IanMichaelHarper commented Jul 27, 2023

Bitmex Wallet History CSV Import

The lack of this feature is one of the reasons I never used Rotki for my own taxes, as I had traded on BitMEX a bunch in the past but then got locked out of my account and only had the walletHistory.csv file.

I am not sure if the file I have is the "official format" for the file. I can't remember if I got it via download or just copy and pasting into an excel sheet, but it looks sufficiently similar to what is used in the bitmex api test to base this functionality off. It is missing a few of the fields that exist in that example string (such as currency and transactID) so this may need to be iterated on based on demands from users and if they have data in a different format. For now, all rows are assumed to be denominated in satoshis and only Deposit, Withdrawal and RealisedPNL events are supported.

Importing the walletHistory.csv file allows RealisedPNL events to show up on History Events tab as profit from a margin trade. A profit is marked as a Receive whilst a loss is marked as a Spend. Both are shown as positive values (should the Spends be negative?). Deposits and withdrawals show up on their tab but the Exch. Trades tab is not populated (as these are not really exchange trades, but profit or loss from an exchange trade. Is is more akin to income/capital gains in that regard). the margin_positions table is populated in the DB and is used for the Profit and Loss Report.

Testing

I did a bunch of user acceptance testing, importing different files with different date formats with and without custom date format field checked. Seems to work fine. History events look correct, Deposits and Withdrawals look correct. Nothing in Exch. Trades. PnL Report is successfully generated. Values look realistic. No double counting between History Events and Margin Positions. Everything looks good except sometimes the taxable PnL sometimes shows 0 for some entries (see image below). I can't figure it out, maybe someone could point me in the right direction there?
image

Checklist

  • The PR modified the frontend, backend and updated the user guide to reflect the changes.

@codecov
Copy link

codecov bot commented Jul 27, 2023

Codecov Report

Merging #6426 (e5284f1) into develop (28e8137) will decrease coverage by 0.20%.
Report is 2 commits behind head on develop.
The diff coverage is 90.69%.

❗ Current head e5284f1 differs from pull request most recent head 8c826e1. Consider uploading reports for the commit 8c826e1 to get more accurate results

@@             Coverage Diff             @@
##           develop    #6426      +/-   ##
===========================================
- Coverage    74.62%   74.43%   -0.20%     
===========================================
  Files         1120     1121       +1     
  Lines       120505   120695     +190     
  Branches     11435    11474      +39     
===========================================
- Hits         89924    89834      -90     
- Misses       28988    29253     +265     
- Partials      1593     1608      +15     
Flag Coverage Δ
backend 63.66% <90.00%> (-0.50%) ⬇️
frontend_unit 79.57% <100.00%> (-0.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files Changed Coverage Δ
rotkehlchen/types.py 87.10% <ø> (ø)
rotkehlchen/data_import/manager.py 97.05% <75.00%> (+0.18%) ⬆️
rotkehlchen/data_import/utils.py 97.29% <87.50%> (+0.23%) ⬆️
rotkehlchen/data_import/importers/bitmex.py 91.17% <91.17%> (ø)
frontend/app/src/data/defaults.ts 100.00% <100.00%> (ø)
frontend/app/src/types/upload-types.ts 100.00% <100.00%> (ø)

... and 58 files with indirect coverage changes

@LefterisJP
Copy link
Member

No double counting between History Events and Margin Positions. Everything looks good except sometimes the taxable PnL shows 0 (see image below)

I see margin position in the screenshot. So it's quite possible it due to something weird with this. We don't really do proper margin trades at all in rotki. Had some half-assed poloniex specific work for their margin trades since I used it a tiny bit in 2015 but we removed most of the logic.

The MarginPosition structure remains for the future and is also used in Bitmex in which all of the events are simple margin position with a profit/loss. But have not tested it in literally years. So could be something wrong with that.

Here it would probably make sense to put some debugging breakpoints into the pnl report process and check what happens.

We want to do radical changes in the way PnL report is generated in the future, to make it easier to maintain: #6427

@IanMichaelHarper
Copy link
Contributor Author

Turns out the issue with taxable PnL being 0 for some rows is because I didn't have a cost basis for them. After manually adding a large buy before the trade period, taxable PnL is correct for all rows.

@LefterisJP
Copy link
Member

Turns out the issue with taxable PnL being 0 for some rows is because I didn't have a cost basis for them. After manually adding a large buy before the trade period, taxable PnL is correct for all rows.

So is this reviewable now? Also seems to need a rebase already, with a conflict in changelog 😅

@IanMichaelHarper
Copy link
Contributor Author

Turns out the issue with taxable PnL being 0 for some rows is because I didn't have a cost basis for them. After manually adding a large buy before the trade period, taxable PnL is correct for all rows.

So is this reviewable now? Also seems to need a rebase already, with a conflict in changelog 😅

Yes It's ready for review. Will I rebase now?

@LefterisJP
Copy link
Member

Turns out the issue with taxable PnL being 0 for some rows is because I didn't have a cost basis for them. After manually adding a large buy before the trade period, taxable PnL is correct for all rows.

So is this reviewable now? Also seems to need a rebase already, with a conflict in changelog sweat_smile

Yes It's ready for review. Will I rebase now?

Yeah please do it, since I have not looked at this yet anyway, to run tests again and be mergable

@IanMichaelHarper
Copy link
Contributor Author

Turns out the issue with taxable PnL being 0 for some rows is because I didn't have a cost basis for them. After manually adding a large buy before the trade period, taxable PnL is correct for all rows.

So is this reviewable now? Also seems to need a rebase already, with a conflict in changelog sweat_smile

Yes It's ready for review. Will I rebase now?

Yeah please do it, since I have not looked at this yet anyway, to run tests again and be mergable

Rebased. Ready for review now.

Copy link
Member

@LefterisJP LefterisJP left a comment

Choose a reason for hiding this comment

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

Few comments

rotkehlchen/data_import/importers/bitmex.py Outdated Show resolved Hide resolved

event_identifier = f'BMEX_{hash_csv_row(csv_row)}'
usd_price = PriceHistorian().query_historical_price(A_BTC, A_USD, close_time)
abs_amount = realised_pnl.__abs__() # pylint: disable=no-member
Copy link
Member

Choose a reason for hiding this comment

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

Wouldn't this work?

Suggested change
abs_amount = realised_pnl.__abs__() # pylint: disable=no-member
abs_amount = abs(realised_pnl)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah cool it does. I'll change it.

rotkehlchen/data_import/importers/bitmex.py Show resolved Hide resolved
rotkehlchen/data_import/importers/bitmex.py Show resolved Hide resolved
@@ -44,7 +44,7 @@ def deserialize_fee(fee: Optional[str]) -> Fee:

Can throw DeserializationError if the fee is not as expected
"""
if fee is None:
if fee is None or fee == 'null':
Copy link
Member

Choose a reason for hiding this comment

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

Hmm I don't like this. The string here should only be a number. Can you handle the extra 'null' case in the single source case where you encountered it?

@IanMichaelHarper
Copy link
Contributor Author

@LefterisJP I updated the PR with changes from your comments. While I was working on them I realised an AssetMovement was being added for for a Canceled withdrawal, so I just added this line to skip it. Do we want to show cancelled/failed events in general? If so, maybe work here could be paired with #6169 in terms of the design of how to show incompleted events.

@lukicenturi
Copy link
Contributor

Frontend part are okay

Copy link
Member

@LefterisJP LefterisJP left a comment

Choose a reason for hiding this comment

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

Looks good, just needs to remove a now unneeded change.

While I was working on them I realised an AssetMovement was being added for for a Canceled withdrawal, so I just added this line to skip it. Do we want to show cancelled/failed events in general? If so, maybe work here could be paired with #6169 in terms of the design of how to show incompleted events.

The issues are different. One is in the API of kraken and the way we query it. Since we query only historical ledger entries and not the specific withdrawal info we don't get the "Canceled" information. So it actually shows very wrong.

While here you already see it's canceled from the CSV status. I don't think you need to let the user know anything. There is no need to display or take canceled events into account. Is there?

@@ -67,6 +67,7 @@ class HistoryEventType(SerializableEnumNameMixin):
INFORMATIONAL = auto()
MIGRATE = auto()
RENEW = auto()
MARGIN = auto()
Copy link
Member

Choose a reason for hiding this comment

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

Can you remove this then since it's not used? Let's maybe add it when we need it. Not sure yet how we would like to represent it. On the type or subtype.

@IanMichaelHarper
Copy link
Contributor Author

IanMichaelHarper commented Jul 31, 2023

I don't think you need to let the user know anything. There is no need to display or take canceled events into account. Is there?

Depends how complete we want the data to be. I'd say most users won't care about cancelled/failed events, they might even find them annoying and want to hide them, but others might want the full picture to see exactly what they did in the past. Overall giving the user the option would be good, but I agree should probably be left as a stretch goal.

Copy link
Member

@LefterisJP LefterisJP left a comment

Choose a reason for hiding this comment

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

LGTM.

Test that failed is rotkehlchen/tests/unit/test_inquirer.py::test_saddle_oracle[ which is being fixed by yabir.

But I want 2 things:

  1. I suspect there is an unused Enum being introduced by the PR. Am I blind or is it not being used anywhere?
  2. Generally keep commits limited/combined if they target the same thing. So here both commits would need to be squashed into one.

log = RotkehlchenLogsAdapter(logger)


class BitMEXWalletHistoryColnames(Enum):
Copy link
Member

Choose a reason for hiding this comment

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

Is this Enum used anywhere?

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 added it after typing out all the column names when accessing csv_row but I never ended up using it. Do you think it's better to use something like this or just access columns of csv_row by typing them out directly?

Copy link
Member

Choose a reason for hiding this comment

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

I added it after typing out all the column names when accessing csv_row but I never ended up using it. Do you think it's better to use something like this or just access columns of csv_row by typing them out directly?

Hmmm if it's used in multiple places maybe this is not bad, but using them directly if they are just used in the parser is fine. No need to create in memory structures like this enum which will persists for the entire lifetime of the app.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, thanks. I wasn't sure so good to know. I'll remove it, squash my commits and push again

Copy link
Member

@LefterisJP LefterisJP left a comment

Choose a reason for hiding this comment

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

LGTM

@LefterisJP LefterisJP merged commit d3ff0d5 into rotki:develop Aug 1, 2023
12 of 15 checks passed
@LefterisJP LefterisJP temporarily deployed to cassette-merge August 1, 2023 07:43 — with GitHub Actions Inactive
@IanMichaelHarper IanMichaelHarper deleted the bitmex-csv-import branch August 1, 2023 08:14
This was referenced Aug 6, 2023
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.

None yet

3 participants