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

Kraken: Action Types in CSV Exports #100

Merged
merged 26 commits into from
Mar 19, 2022
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ad3563d
updated Kraken mapping
Griffsano Jan 2, 2022
75e027d
different way of handling Kraken exports
Griffsano Jan 2, 2022
7cef9ac
removed trailing whitespace
Griffsano Jan 2, 2022
d49bd63
try inverse pair if Kraken pair is invalid
Griffsano Jan 3, 2022
fbf0609
reduce number of margin trading warnings
Griffsano Jan 3, 2022
9f377f3
allow future timestamp for virtual sells
Griffsano Jan 3, 2022
c71d9e1
address review comments - WIP
Griffsano Jan 4, 2022
e9d809d
reworked Kraken API warnings/errors
Griffsano Jan 5, 2022
1b78544
throw error for margin trades
Griffsano Jan 6, 2022
883c3e5
updated handling of Kraken deposits/withdrawals
Griffsano Jan 15, 2022
4722b63
updated Kraken API handling of latest trade prices
Griffsano Jan 16, 2022
5870134
updated type annotation
Griffsano Jan 23, 2022
c03b90f
added Optional[str] to str conversion
Griffsano Jan 23, 2022
31cba9a
stake rewarded coins
Griffsano Jan 26, 2022
1870cd1
UPDATE Specify error message when refid parameters doe not match
provinzio Feb 5, 2022
ca11a5d
UPDATE autoformat
provinzio Feb 5, 2022
adb45e4
UPDATE deposit logic, ADD comment for staking rewards
Griffsano Feb 6, 2022
e250176
REFACTOR store invalid API Kraken pairs in list and don't try them again
Griffsano Feb 19, 2022
f002f02
REFACTOR handling of Kraken deposits/withdrawals
Griffsano Feb 19, 2022
2385dfe
ADD create_operation and append_created_operation
Griffsano Feb 20, 2022
f461f5f
REFACTOR handling of deposits/withdrawals based on stored operation c…
Griffsano Feb 20, 2022
99baef5
REFACTOR create_operation, append_operation
Griffsano Feb 20, 2022
09817ed
AUTOFORMAT book
provinzio Mar 19, 2022
7a05a50
FIX typo
provinzio Mar 19, 2022
20d521d
CHANGE raise TypeError instead of RuntimeError for wrong type
provinzio Mar 19, 2022
6ccfdc0
UPDATE shorten comment to fit into line
provinzio Mar 19, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 120 additions & 49 deletions src/book.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,30 +452,15 @@ def _read_kraken_ledgers(self, file_path: Path) -> None:
operation_mapping = {
"spend": "Sell", # Sell ordered via 'Buy Crypto' button
"receive": "Buy", # Buy ordered via 'Buy Crypto' button
"transfer": "Airdrop",
"reward": "StakingInterest",
"staking": "StakingInterest",
"deposit": "Deposit",
"withdrawal": "Withdrawal",
}

# Need to track state of "duplicate entries"
# for deposits / withdrawals;
# the second deposit and the first withdrawal entry
# need to be skipped.
# dup_state["deposit"] == 0:
# Deposit is broadcast to blockchain
# > Taxable event (is in public trade history)
# dup_state["deposit"] == 1:
# Deposit is credited to Kraken account
# > Skipped
# dup_state["withdrawal"] == 0:
# Withdrawal is requested in Kraken account
# > Skipped
# dup_state["withdrawal"] == 1:
# Withdrawal is broadcast to blockchain
# > Taxable event (is in public trade history)
dup_state = {"deposit": 0, "withdrawal": 0}
dup_skip = {"deposit": 1, "withdrawal": 0}
# Need to track state of duplicate deposit/withdrawal entries
# All deposits/withdrawals are held back until they occur a second time
held_operations = []
Griffsano marked this conversation as resolved.
Show resolved Hide resolved

with open(file_path, encoding="utf8") as f:
reader = csv.reader(f)
Expand Down Expand Up @@ -515,42 +500,58 @@ def _read_kraken_ledgers(self, file_path: Path) -> None:
balance,
) = columns
else:
raise RuntimeError(
"Unknown Kraken ledgers format: "
log.error(
"{file_path}: Unknown Kraken ledgers format: "
"Number of rows do not match known versions."
)
raise RuntimeError

row = reader.line_num

# Skip "duplicate entries" for deposits / withdrawals
if _type in dup_state.keys():
skip = dup_state[_type] == dup_skip[_type]
dup_state[_type] = (dup_state[_type] + 1) % 2
if skip:
continue

# Parse data.
utc_time = datetime.datetime.strptime(_utc_time, "%Y-%m-%d %H:%M:%S")
utc_time = utc_time.replace(tzinfo=datetime.timezone.utc)
change = misc.force_decimal(_amount)
# remove the appended .S for staked assets
_asset = _asset.removesuffix(".S")
coin = kraken_asset_map.get(_asset, _asset)
fee = misc.force_decimal(_fee)
operation = operation_mapping.get(_type)
if operation is None:
if _type == "trade":
operation = "Sell" if change < 0 else "Buy"
elif _type in ["margin trade", "rollover", "settled"]:
elif _type in ["margin trade", "rollover", "settled", "margin"]:
log.error(
f"{file_path}: {row}: Margin trading is "
"currently not supported. "
"Please create an Issue or PR."
f"{file_path} row {row}: Margin trading is currently not "
"supported. Please create an Issue or PR."
)
raise RuntimeError
elif _type == "transfer":
if num_columns == 9:
# for backwards compatibility assume Airdrop for staking
log.warning(
f"{file_path} row {row}: Staking is not supported for"
"old Kraken ledger formats. "
"Please create an Issue or PR."
)
operation = "Airdrop"
elif subtype == "stakingfromspot":
operation = "Staking"
elif subtype == "stakingtospot":
operation = "StakingEnd"
elif subtype in ["spottostaking", "spotfromstaking"]:
# duplicate entries for staking actions
continue
else:
log.error(
f"{file_path} row {row}: Order subtype '{subtype}' is "
"currently not supported. Please create an Issue or PR."
)
raise RuntimeError
else:
log.error(
f"{file_path}: {row}: Other order type '{_type}' "
"is currently not supported. "
"Please create an Issue or PR."
f"{file_path} row {row}: Other order type '{_type}' is "
"currently not supported. Please create an Issue or PR."
)
raise RuntimeError
change = abs(change)
Expand All @@ -560,22 +561,92 @@ def _read_kraken_ledgers(self, file_path: Path) -> None:
assert coin
assert change

self.append_operation(
operation, utc_time, platform, change, coin, row, file_path
)

if fee != 0:
# append all operations to main list per default
# will be overwritten for deposits / withdrawals
append_operation = True

# Skip duplicate entries for deposits / withdrawals and additional
# deposit / withdrawal lines for staking / unstaking / staking reward
# actions.
# The second deposit and the first withdrawal need to be considered,
# since these are the points in time where the user actually has the
# assets at their disposal. The first deposit and second withdrawal are
# in the public trade history and are skipped.
# For staking / unstaking / staking reward actions, deposits /
# withdrawals only occur once and will be ignored.
if operation in ["Deposit", "Withdrawal"]:
# search for refid in refids list
refid_idxs = [
idx for idx, op in enumerate(held_operations) \
if op["refid"] == refid
]
Griffsano marked this conversation as resolved.
Show resolved Hide resolved
# refid should not exist more than once in list
if len(refid_idxs) > 1:
log.error(
f"{file_path} row {row}: More than two entries with refid "
f"{refid} should not exist ({operation}). "
"Please create an Issue or PR."
)
raise RuntimeError
# if refid already exists, assert that data of operations agree
if refid_idxs:
idx = refid_idxs[0]
try:
assert operation == held_operations[idx]["operation"]
assert change == held_operations[idx]["change"]
assert coin == held_operations[idx]["coin"]
except AssertionError:
log.error(
f"{file_path} row {row}: Parameters for refid {refid} "
f"({operation}) do not agree. "
"Please create an Issue or PR."
)
raise RuntimeError
# add all entries to refid list and held operations list
held_operations.append({
"refid": refid,
"operation": operation,
"utc_time": utc_time,
"platform": platform,
"change": change,
"coin": coin,
"row": row,
"file_path": file_path,
"fee": fee,
})

if operation == "Deposit":
# append only the second deposit to the operations list
if refid_idxs:
append_operation = True
else:
append_operation = False
Griffsano marked this conversation as resolved.
Show resolved Hide resolved
elif operation == "Withdrawal":
# Append the first withdrawal to the operations list as soon
# as the second withdrawal occurs. Therefore, for the second
# withdrawal, overwrite the variables with the data of the first
# withdrawal and append this to the operations list.
if refid_idxs:
append_operation = True
operation = held_operations[idx]["operation"]
utc_time = held_operations[idx]["utc_time"]
platform = held_operations[idx]["platform"]
change = held_operations[idx]["change"]
coin = held_operations[idx]["coin"]
row = held_operations[idx]["row"]
file_path = held_operations[idx]["file_path"]
fee = held_operations[idx]["fee"]
else:
append_operation = False
provinzio marked this conversation as resolved.
Show resolved Hide resolved

if append_operation:
self.append_operation(
"Fee", utc_time, platform, fee, coin, row, file_path
operation, utc_time, platform, change, coin, row, file_path
)

assert dup_state["deposit"] == 0, (
"Orphaned deposit. (Must always come in pairs). " "Is your file corrupted?"
)
assert dup_state["withdrawal"] == 0, (
"Orphaned withdrawal. (Must always come in pairs). "
"Is your file corrupted?"
)
if fee != 0:
self.append_operation(
"Fee", utc_time, platform, fee, coin, row, file_path
)

def _read_kraken_ledgers_old(self, file_path: Path) -> None:

Expand Down
69 changes: 39 additions & 30 deletions src/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,32 +202,41 @@ class Fiat(Enum):
# Converts clean fiat / clean crypto pairs to "dirty" API asset pairs
# (e.g. ETHEUR -> XETHZEUR)
# Analyzed using asset pairs API data:
# https://api.kraken.com/0/public/AssetPairs (retrieved at 2021-02-21)
# https://api.kraken.com/0/public/AssetPairs (retrieved at 2022-01-02)
kraken_asset_map = {
# Fiat:
"ZUSD": "USD",
"ZEUR": "EUR",
"ZCAD": "CAD",
"ZJPY": "JPY",
"ZGBP": "GBP",
"CHF": "CHF",
"ZAUD": "AUD",
"ZCAD": "CAD",
"ZEUR": "EUR",
"ZGBP": "GBP",
"ZJPY": "JPY",
"ZUSD": "USD",
# Crypto:
"XXBT": "XBT",
"XETC": "ETC",
"XETH": "ETH",
"XLTC": "LTC",
"XMLN": "MLN",
"XREP": "REP",
"XXBT": "XBT",
"XXDG": "XDG",
"XXLM": "XLM",
"XXMR": "XMR",
"XXRP": "XRP",
"XZEC": "ZEC",
}

# Only these asset pairs violate the rule
# "clean name" + "clean name" = "asset pair"
kraken_pair_map = {
"USDTUSD": "USDTZUSD",
"XETCETH": "XETCXETH",
"XETCXBT": "XETCXXBT",
"XETCEUR": "XETCZEUR",
"XETCUSD": "XETCZUSD",
"ETCETH": "XETCXETH",
"ETCXBT": "XETCXXBT",
"ETCEUR": "XETCZEUR",
"ETCUSD": "XETCZUSD",
"ETH2ETH": "ETH2.SETH",
"ETH2EUR": "XETHZEUR",
"ETH2USD": "XETHZUSD",
"ETHXBT": "XETHXXBT",
"ETHCAD": "XETHZCAD",
"ETHEUR": "XETHZEUR",
Expand All @@ -238,36 +247,36 @@ class Fiat(Enum):
"LTCEUR": "XLTCZEUR",
"LTCJPY": "XLTCZJPY",
"LTCUSD": "XLTCZUSD",
"XMLNETH": "XMLNXETH",
"XMLNXBT": "XMLNXXBT",
"XMLNEUR": "XMLNZEUR",
"XMLNUSD": "XMLNZUSD",
"XREPETH": "XREPXETH",
"XREPXBT": "XREPXXBT",
"XREPEUR": "XREPZEUR",
"XREPUSD": "XREPZUSD",
"MLNETH": "XMLNXETH",
"MLNXBT": "XMLNXXBT",
"MLNEUR": "XMLNZEUR",
"MLNUSD": "XMLNZUSD",
"REPETH": "XREPXETH",
"REPXBT": "XREPXXBT",
"REPEUR": "XREPZEUR",
"REPUSD": "XREPZUSD",
"XBTCAD": "XXBTZCAD",
"XBTEUR": "XXBTZEUR",
"XBTGBP": "XXBTZGBP",
"XBTJPY": "XXBTZJPY",
"XBTUSD": "XXBTZUSD",
"XDGXBT": "XXDGXXBT",
"XXLMXBT": "XXLMXXBT",
"XXLMAUD": "XXLMZAUD",
"XXLMEUR": "XXLMZEUR",
"XXLMGBP": "XXLMZGBP",
"XXLMUSD": "XXLMZUSD",
"XXMRXBT": "XXMRXXBT",
"XXMREUR": "XXMRZEUR",
"XXMRUSD": "XXMRZUSD",
"XLMXBT": "XXLMXXBT",
"XLMAUD": "XXLMZAUD",
"XLMEUR": "XXLMZEUR",
"XLMGBP": "XXLMZGBP",
"XLMUSD": "XXLMZUSD",
"XMRXBT": "XXMRXXBT",
"XMREUR": "XXMRZEUR",
"XMRUSD": "XXMRZUSD",
"XRPXBT": "XXRPXXBT",
"XRPCAD": "XXRPZCAD",
"XRPEUR": "XXRPZEUR",
"XRPJPY": "XXRPZJPY",
"XRPUSD": "XXRPZUSD",
"XZECXBT": "XZECXXBT",
"XZECEUR": "XZECZEUR",
"XZECUSD": "XZECZUSD",
"ZECXBT": "XZECXXBT",
"ZECEUR": "XZECZEUR",
"ZECUSD": "XZECZUSD",
"EURUSD": "ZEURZUSD",
"GBPUSD": "ZGBPZUSD",
"USDCAD": "ZUSDZCAD",
Expand Down
Loading