fix(journal): keep fiat currency pairs (EUR/USD) out of the crypto market
Signed-off-by: Rudy Celekli <rudy@gradiahq.com>
This commit is contained in:
1 parent
d7f1b9e22b
commit
5ef6d37b84
2 files changed
+55
-2
No files matched your search
@@ -24,6 +24,14 @@ _CURRENCY_TOKEN_RE = re.compile(
|
||||
r"(?i)(?<![A-Za-z])(?:USDT|USDC|USD|EUR|GBP|JPY|CNY|HKD)(?![A-Za-z])|[$€£¥¥]"
|
||||
)
|
||||
|
||||
# ISO codes of the fiat currencies a broker export can quote as a pair
|
||||
# (EUR/USD); a pair with two fiat legs is forex, never crypto.
|
||||
_FIAT_CODES = frozenset({
|
||||
"USD", "EUR", "GBP", "JPY", "CHF", "AUD", "CAD", "NZD", "CNY", "CNH", "HKD",
|
||||
"SGD", "SEK", "NOK", "DKK", "MXN", "ZAR", "INR", "KRW", "TRY", "PLN", "CZK",
|
||||
"HUF", "ILS", "THB",
|
||||
})
|
||||
|
||||
FormatName = str # "tonghuashun" | "eastmoney" | "futu" | "generic" | "unknown"
|
||||
|
||||
_A_SHARE_EXCHANGE_MAP = {
|
||||
@@ -658,8 +666,14 @@ def _infer_market_from_symbol(symbol: str) -> str:
|
||||
return "uk"
|
||||
if s.endswith(".SH") or s.endswith(".SZ") or s.endswith(".BJ"):
|
||||
return "china_a"
|
||||
if ("-" in s or "/" in s) and any(quote in s for quote in ("USDT", "USDC", "BTC", "USD")):
|
||||
return "crypto"
|
||||
if "-" in s or "/" in s:
|
||||
legs = [leg for leg in re.split(r"[-/]", s) if leg]
|
||||
if len(legs) == 2 and all(leg in _FIAT_CODES for leg in legs):
|
||||
# EUR/USD, USD-JPY ...: a currency cross, not a crypto pair. The
|
||||
# shadow-account markets have no forex bucket, so it stays "other".
|
||||
return "other"
|
||||
if any(quote in s for quote in ("USDT", "USDC", "BTC", "USD")):
|
||||
return "crypto"
|
||||
# Binance-style concatenated pairs (BTCUSDT) are purely alphabetic, so the
|
||||
# isalpha() US-equity branch below would mis-label them without this check.
|
||||
for quote in ("USDT", "USDC", "BUSD"):
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
"""A fiat/fiat currency pair in a trade journal must not be labelled crypto."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from src.tools.trade_journal_parsers import _infer_market_from_symbol, parse_generic
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"symbol",
|
||||
["EUR/USD", "GBP/USD", "USD/JPY", "usd-cnh", "AUD-USD", "USD/CHF"],
|
||||
)
|
||||
def test_fiat_cross_is_not_crypto(symbol: str) -> None:
|
||||
assert _infer_market_from_symbol(symbol) == "other"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"symbol",
|
||||
["BTC-USD", "BTC/USD", "ETH/BTC", "ETH-USDT", "SOL/USDC", "BTC-EUR", "ETH-USD"],
|
||||
)
|
||||
def test_crypto_pairs_stay_crypto(symbol: str) -> None:
|
||||
assert _infer_market_from_symbol(symbol) == "crypto"
|
||||
|
||||
|
||||
def test_generic_journal_row_for_a_currency_cross_gets_other_market() -> None:
|
||||
df = pd.DataFrame(
|
||||
{
|
||||
"datetime": ["2026-01-02 10:00:00"],
|
||||
"symbol": ["EUR/USD"],
|
||||
"side": ["buy"],
|
||||
"quantity": ["1000"],
|
||||
"price": ["1.10"],
|
||||
}
|
||||
)
|
||||
(record,) = parse_generic(df)
|
||||
assert record.market == "other"
|
||||
Reference in new issue
Block a user