Repository: rotki/rotki

Issue:
Wrong date is reported for a coinbase trade
## Problem Definition

In my specific trade, the `payout_at` date is chosen, because `instant` is false. However, Coinbase PDF report quotes the `created_at` date. And I also know that the `payout_at` date (+1 day later) is not correct, because I did another trade with the purchased asset shortly after.  Maybe `payout_at` date is where they settled the trade with my bank but I definitely got the asset at the `created_at` date.

I don't know if this is a bug in rotki, and how other coinbase API readers deal with it. I just wanted to record this, as it is inconsistency between rotki and coinbase PDF report.

## Logs

I can provide the JSON trade and the PDF export (ask me in the Discord)






Discussion:
Looking at https://github.com/ccxt/ccxt/blob/master/js/coinbase.js, `payout_at` is never even used there.
Hey @coinyon I would need to debug it myself with your API key. I have no other way to detect this as coinbase also changes their API some times. The json trade response from the API would also help. Please DM it to me.
Hey @coinyon so after checking the JSON you sent me and the API docs on buys/sells:

- https://developers.coinbase.com/api/v2#sell-resource
- https://developers.coinbase.com/api/v2#buy-resource

I think that the pdf report is wrong. The docs clearly state that any non-instant buy/sell executes at the payout date.
Which would mean rotki is correct.

![2021-09-04_22-29](https://user-images.githubusercontent.com/1658405/132107324-dbdd3d14-70c9-42b9-8b81-04ab4c8ebf9c.png)

![2021-09-04_22-30](https://user-images.githubusercontent.com/1658405/132107318-49b4eb61-122b-4d63-b39f-03dfd76edfcc.png)

If this satisfies your issue please feel free to close it.
As discussed in the chat, I'll contact Coinbase support and get back to you.
Hey @coinyon so any answer? Solving this would be very easy for you. Just want to confirm that this is the right solution as it does not seem to agree with their docs.
Unfortunately, nobody from Coinbase came back to me. They said the only support they can offer is the documentation :-1: 

I'm currently running a patched rotki with the change to always use `created_at` and it worked fine so far. So I'd welcome this change, and I think rotki would be more correct with it. But I can't speak for every user, maybe there is a case where the current impl is correct.
I would welcome a PR for it and if anybody complains we can look at it then.
Ok will prepare one this weekend

Resolving patch:
diff --git a/rotkehlchen/exchanges/coinbase.py b/rotkehlchen/exchanges/coinbase.py
--- a/rotkehlchen/exchanges/coinbase.py
+++ b/rotkehlchen/exchanges/coinbase.py
@@ -66,10 +66,14 @@ def trade_from_coinbase(raw_trade: Dict[str, Any]) -> Optional[Trade]:
         # We only want to deal with completed trades
         return None
 
-    if raw_trade['instant']:
+    # Contrary to the Coinbase documentation we will use created_at, and never
+    # payout_at. It seems like payout_at is not actually the time where the trade is settled.
+    # Reports generated by Coinbase use created_at as well
+    if raw_trade.get('created_at') is not None:
         raw_time = raw_trade['created_at']
     else:
         raw_time = raw_trade['payout_at']
+
     timestamp = deserialize_timestamp_from_date(raw_time, 'iso8601', 'coinbase')
     trade_type = deserialize_trade_type(raw_trade['resource'])
     tx_amount = deserialize_asset_amount(raw_trade['amount']['amount'])
@@ -132,7 +136,7 @@ def trade_from_conversion(trade_a: Dict[str, Any], trade_b: Dict[str, Any]) -> O
         # We have the fee amount in the native currency. To get it in the
         # converted asset we have to get the rate
         asset_native_rate = tx_amount / abs(amount_before_fee)
-        fee_amount = Fee(conversion_native_fee_amount / asset_native_rate)
+        fee_amount = Fee(conversion_native_fee_amount * asset_native_rate)
     else:
         fee_amount = Fee(ZERO)
     fee_asset = asset_from_coinbase(trade_a['amount']['currency'], time=timestamp)
@@ -518,7 +522,6 @@ def query_online_trade_history(
         raw_transactions = []
         for account_id in account_ids:
             raw_transactions.extend(self._api_query(f'accounts/{account_id}/transactions'))
-
         # Maps every trade id to their two transactions
         trade_pairs = defaultdict(list)
         for transaction in raw_transactions:
@@ -530,7 +533,6 @@ def query_online_trade_history(
                         f'Transaction of type trade doesnt have the '
                         f'expected structure {transaction}',
                     )
-
         for trade_id, trades_conversion in trade_pairs.items():
             # Assert that in fact we have two trades
             if len(trades_conversion) != 2:


Test patch:
diff --git a/rotkehlchen/tests/exchanges/test_coinbase.py b/rotkehlchen/tests/exchanges/test_coinbase.py
--- a/rotkehlchen/tests/exchanges/test_coinbase.py
+++ b/rotkehlchen/tests/exchanges/test_coinbase.py
@@ -474,7 +474,7 @@ def test_coinbase_query_trade_history(function_scope_coinbase):
     assert len(errors) == 0
     assert len(trades) == 2
     expected_trades = [Trade(
-        timestamp=1500853448,
+        timestamp=1500705839,
         location=Location.COINBASE,
         base_asset=A_BTC,
         quote_asset=A_USD,
@@ -582,14 +582,14 @@ def test_coinbase_query_trade_history_unexpected_data(function_scope_coinbase):
         expected_errors_num=0,
     )
 
-    # invalid payout_at timestamp
-    broken_response = BUYS_RESPONSE.replace('"2017-07-23T23:44:08Z"', 'true')
+    # fallback to payout_at if created_date is missing
+    broken_response = BUYS_RESPONSE.replace('"created_at": "2017-07-21T23:43:59-07:00",', '')
     query_coinbase_and_test(
         coinbase=coinbase,
         query_fn_name='query_online_trade_history',
         buys_response=broken_response,
         expected_warnings_num=0,
-        expected_errors_num=1,
+        expected_errors_num=0,
     )
 
     # invalid created_at timestamp
@@ -1048,7 +1048,7 @@ def test_asset_conversion_not_stable_coin():
         trade_type=TradeType.SELL,
         amount=FVal('6000.0'),
         rate=FVal('0.000002823608333333333333333333333'),
-        fee=FVal('15'),
+        fee=FVal('540'),
         fee_currency=A_1INCH,
         link='5dceef97-ef34-41e6-9171-3e60cd01639e',
     )