Browse Source

Merge branch 'main' into bugfix/portfolio-performance-of-closed-position

pull/7610/head
Thomas Kaul 2 weeks ago
committed by GitHub
parent
commit
9c78c51c5d
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 8
      CHANGELOG.md
  2. 127
      apps/api/src/assets/cryptocurrencies/cryptocurrencies.json
  3. 2
      apps/api/src/helper/country.helper.ts
  4. 22
      apps/api/src/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service.ts
  5. 8
      apps/api/src/services/data-provider/data-provider.service.ts
  6. 3
      apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts
  7. 63
      apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts
  8. 38
      libs/common/src/lib/personal-finance-tools.ts
  9. 8
      package-lock.json
  10. 2
      package.json

8
CHANGELOG.md

@ -7,9 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased ## Unreleased
### Changed
- Refreshed the cryptocurrencies list
- Upgraded `countries-list` from version `3.4.0` to `3.4.1`
### Fixed ### Fixed
- Fixed the performance of closed positions in the portfolio calculator caused by a rounding remainder in the investment - Fixed the performance of closed positions in the portfolio calculator caused by a rounding remainder in the investment
- Fixed the missing mapping for Turkey in the country weightings of the _Financial Modeling Prep_ service
- Fixed the missing mapping for Czech Republic and Turkey in the data enhancer for asset profile data via _Yahoo Finance_
- Resolved an error when fetching dividends from _Yahoo Finance_ for date ranges without events
## 3.49.0 - 2026-08-12 ## 3.49.0 - 2026-08-12

127
apps/api/src/assets/cryptocurrencies/cryptocurrencies.json

@ -113,7 +113,7 @@
"2OMB": "2omb Finance", "2OMB": "2omb Finance",
"2SHARES": "2SHARE", "2SHARES": "2SHARE",
"2TF": "2TF", "2TF": "2TF",
"2Z": "DoubleZero USD Price", "2Z": "DoubleZero",
"300F": "300FIT", "300F": "300FIT",
"314DAO": "Tonken 314 DAO", "314DAO": "Tonken 314 DAO",
"32BIT": "32Bitcoin", "32BIT": "32Bitcoin",
@ -212,7 +212,7 @@
"AAVEGOTCHIFOMO": "Aavegotchi FOMO", "AAVEGOTCHIFOMO": "Aavegotchi FOMO",
"AAX": "Academic Labs", "AAX": "Academic Labs",
"AAZ": "ATLAZ", "AAZ": "ATLAZ",
"AB": "Arma The Battle Ground USD Price", "AB": "Arma The Battle Ground",
"AB1INCH": "1inch (Avalanche Bride)", "AB1INCH": "1inch (Avalanche Bride)",
"ABA": "EcoBall", "ABA": "EcoBall",
"ABAT": "Aave BAT", "ABAT": "Aave BAT",
@ -806,7 +806,7 @@
"AMY": "Amygws", "AMY": "Amygws",
"AMZE": "The Amaze World", "AMZE": "The Amaze World",
"AMZNON": "Amazon (Ondo Tokenized)", "AMZNON": "Amazon (Ondo Tokenized)",
"AMZNX": "Amazon tokenized stock (xStock) USD Price", "AMZNX": "Amazon tokenized stock (xStock)",
"ANA": "Nirvana ANA", "ANA": "Nirvana ANA",
"ANAL": "AnalCoin", "ANAL": "AnalCoin",
"ANALOS": "analoS", "ANALOS": "analoS",
@ -845,7 +845,7 @@
"ANIM": "Animalia", "ANIM": "Animalia",
"ANIMA": "Realm Anima", "ANIMA": "Realm Anima",
"ANIME": "Animecoin", "ANIME": "Animecoin",
"ANIME35319": "Animecoin USD Price", "ANIME35319": "Animecoin",
"ANIMECOIN": "Animecoin", "ANIMECOIN": "Animecoin",
"ANIMEONBASE": "Anime", "ANIMEONBASE": "Anime",
"ANIMETOKEN": "Anime Token (animetoken.in)", "ANIMETOKEN": "Anime Token (animetoken.in)",
@ -1016,7 +1016,7 @@
"ARBUZ": "ARBUZ", "ARBUZ": "ARBUZ",
"ARBYS": "Arbys Token", "ARBYS": "Arbys Token",
"ARC": "AI Rig Complex", "ARC": "AI Rig Complex",
"ARC23486": "Arcadeum USD Price", "ARC23486": "Arcadeum",
"ARCA": "Legend of Arcadia", "ARCA": "Legend of Arcadia",
"ARCAD": "Arcadeum", "ARCAD": "Arcadeum",
"ARCADE": "ARCADE", "ARCADE": "ARCADE",
@ -1367,7 +1367,7 @@
"AVGOX": "Broadcom tokenized stock (xStock)", "AVGOX": "Broadcom tokenized stock (xStock)",
"AVH": "Animation Vision Cash", "AVH": "Animation Vision Cash",
"AVI": "Aviator", "AVI": "Aviator",
"AVICI": "Avici USD Price", "AVICI": "Avici",
"AVINOC": "AVINOC", "AVINOC": "AVINOC",
"AVIVE": "Avive World", "AVIVE": "Avive World",
"AVL": "Aston Villa Fan Token", "AVL": "Aston Villa Fan Token",
@ -1390,7 +1390,7 @@
"AWAX": "AWAX", "AWAX": "AWAX",
"AWBTC": "Aave interest bearing WBTC", "AWBTC": "Aave interest bearing WBTC",
"AWC": "Atomic Wallet Coin", "AWC": "Atomic Wallet Coin",
"AWE": "AWE Network USD Price", "AWE": "AWE Network",
"AWK": "Awkward Monkey Base", "AWK": "Awkward Monkey Base",
"AWM": "Another World", "AWM": "Another World",
"AWNEX": "AWNEX token", "AWNEX": "AWNEX token",
@ -1452,7 +1452,7 @@
"AZUR": "Azuro Protocol", "AZUR": "Azuro Protocol",
"AZURE": "Azure Wallet", "AZURE": "Azure Wallet",
"AZY": "Amazy", "AZY": "Amazy",
"B": "BUILDon USD Price", "B": "BUILDon",
"B01": "b0rder1ess", "B01": "b0rder1ess",
"B1P": "B ONE PAYMENT", "B1P": "B ONE PAYMENT",
"B2": "B² Network", "B2": "B² Network",
@ -1974,7 +1974,7 @@
"BEPE": "Blast Pepe", "BEPE": "Blast Pepe",
"BEPR": "Blockchain Euro Project", "BEPR": "Blockchain Euro Project",
"BEPRO": "BEPRO Network", "BEPRO": "BEPRO Network",
"BERA": "Berachain USD Price", "BERA": "Berachain",
"BERAETH": "Berachain Staked ETH", "BERAETH": "Berachain Staked ETH",
"BERASTONE": "StakeStone Berachain Vault Token", "BERASTONE": "StakeStone Berachain Vault Token",
"BERC": "Fair BERC20", "BERC": "Fair BERC20",
@ -2129,6 +2129,7 @@
"BIKE": "White Bike", "BIKE": "White Bike",
"BIKI": "BIKI", "BIKI": "BIKI",
"BILL": "TillBilly", "BILL": "TillBilly",
"BILL39545": "Billions Network",
"BILLI": "Billi", "BILLI": "Billi",
"BILLICAT": "BilliCat", "BILLICAT": "BilliCat",
"BILLY": "Billy", "BILLY": "Billy",
@ -2614,7 +2615,7 @@
"BONDLYV1": "Bondly Finance", "BONDLYV1": "Bondly Finance",
"BONDX": "BondX", "BONDX": "BondX",
"BONE": "Shibarium Wrapped BONE", "BONE": "Shibarium Wrapped BONE",
"BONE11865": "Bone ShibaSwap USD Price", "BONE11865": "Bone ShibaSwap",
"BONEBONE": "Bone", "BONEBONE": "Bone",
"BONES": "Moonshots Farm", "BONES": "Moonshots Farm",
"BONESCOIN": "BonesCoin", "BONESCOIN": "BonesCoin",
@ -3715,7 +3716,7 @@
"CHILL": "ChillPill", "CHILL": "ChillPill",
"CHILLAX": "Chillax", "CHILLAX": "Chillax",
"CHILLCAT": "Chillchat", "CHILLCAT": "Chillchat",
"CHILLGUY": "Just a chill guy USD Price", "CHILLGUY": "Just a chill guy",
"CHILLHOUSE": "Chill House", "CHILLHOUSE": "Chill House",
"CHIM": "Chimera", "CHIM": "Chimera",
"CHINA": "China Coin", "CHINA": "China Coin",
@ -3965,7 +3966,7 @@
"CO2": "CO2 Token", "CO2": "CO2 Token",
"COA": "Alliance Games", "COA": "Alliance Games",
"COAI": "ChainOpera AI", "COAI": "ChainOpera AI",
"COAI38489": "ChainOpera AI USD Price", "COAI38489": "ChainOpera AI",
"COAL": "BitCoal", "COAL": "BitCoal",
"COB": "Cobinhood", "COB": "Cobinhood",
"COBE": "Castle of Blackwater", "COBE": "Castle of Blackwater",
@ -5260,7 +5261,7 @@
"DOFI": "Doge Floki Coin", "DOFI": "Doge Floki Coin",
"DOG": "Dog (Runes)", "DOG": "Dog (Runes)",
"DOG11557": "The Doge NFT", "DOG11557": "The Doge NFT",
"DOG30933": "Dog (Runes) USD Price", "DOG30933": "Dog (Runes)",
"DOGA": "DOGAMÍ", "DOGA": "DOGAMÍ",
"DOGACOIN": "DogaCoin", "DOGACOIN": "DogaCoin",
"DOGAI": "Dogai", "DOGAI": "Dogai",
@ -5527,7 +5528,7 @@
"DRT": "DomRaider", "DRT": "DomRaider",
"DRUGS": "Big Pharmai", "DRUGS": "Big Pharmai",
"DRV": "Derive", "DRV": "Derive",
"DRV35014": "Derive USD Price", "DRV35014": "Derive",
"DRX": "DRX Token", "DRX": "DRX Token",
"DRXNE": "Droxne", "DRXNE": "Droxne",
"DRZ": "Droidz", "DRZ": "Droidz",
@ -6077,7 +6078,7 @@
"ENRON": "Enron", "ENRON": "Enron",
"ENRX": "Enrex", "ENRX": "Enrex",
"ENS": "Ethereum Name Service", "ENS": "Ethereum Name Service",
"ENSO": "Enso USD Price", "ENSO": "Enso",
"ENT": "Eternity", "ENT": "Eternity",
"ENTC": "ENTERBUTTON", "ENTC": "ENTERBUTTON",
"ENTER": "EnterCoin", "ENTER": "EnterCoin",
@ -6629,7 +6630,7 @@
"FEY": "Feyorra", "FEY": "Feyorra",
"FF": "Forefront", "FF": "Forefront",
"FF1": "Two Prime FF1 Token", "FF1": "Two Prime FF1 Token",
"FF38482": "Falcon Finance USD Price", "FF38482": "Falcon Finance",
"FFA": "Cryptofifa", "FFA": "Cryptofifa",
"FFC": "FireflyCoin", "FFC": "FireflyCoin",
"FFCT": "FortFC", "FFCT": "FortFC",
@ -6678,7 +6679,7 @@
"FIGHTPEPE": "FIGHT PEPE", "FIGHTPEPE": "FIGHT PEPE",
"FIGHTRUMP": "FIGHT TRUMP", "FIGHTRUMP": "FIGHT TRUMP",
"FIGMA": "Figments Club", "FIGMA": "Figments Club",
"FIGRHELOC": "Figure HELOC USD Price", "FIGRHELOC": "Figure HELOC",
"FIH": "Fidelity House", "FIH": "Fidelity House",
"FIII": "Fiii", "FIII": "Fiii",
"FIL": "Filecoin", "FIL": "Filecoin",
@ -6955,7 +6956,7 @@
"FORGE": "Forge Finance", "FORGE": "Forge Finance",
"FORK": "Gastro Advisor Token", "FORK": "Gastro Advisor Token",
"FORM": "Formation Fi", "FORM": "Formation Fi",
"FORM23635": "Four USD Price", "FORM23635": "Four",
"FORMATION": "Formation FI", "FORMATION": "Formation FI",
"FORMNET": "Form", "FORMNET": "Form",
"FORS": "Foresight", "FORS": "Foresight",
@ -7555,7 +7556,7 @@
"GIFT": "GiftNet", "GIFT": "GiftNet",
"GIG": "GigaCoin", "GIG": "GigaCoin",
"GIGA": "Gigachad", "GIGA": "Gigachad",
"GIGA30063": "Gigachad USD Price", "GIGA30063": "Gigachad",
"GIGABRAIN": "Gigabrain by virtuals", "GIGABRAIN": "Gigabrain by virtuals",
"GIGACAT": "GIGACAT", "GIGACAT": "GIGACAT",
"GIGACHAD": "GigaChad", "GIGACHAD": "GigaChad",
@ -7911,7 +7912,7 @@
"GRID": "Grid+", "GRID": "Grid+",
"GRIDCOIN": "GridCoin", "GRIDCOIN": "GridCoin",
"GRIDZ": "GridZone.io", "GRIDZ": "GridZone.io",
"GRIFFAIN": "GRIFFAIN USD Price", "GRIFFAIN": "GRIFFAIN",
"GRIFT": "ORBIT", "GRIFT": "ORBIT",
"GRIM": "GrimToken", "GRIM": "GrimToken",
"GRIMACE": "GrimaceCoin", "GRIMACE": "GrimaceCoin",
@ -8108,7 +8109,7 @@
"GZT": "Golden Zen Token", "GZT": "Golden Zen Token",
"GZX": "GreenZoneX", "GZX": "GreenZoneX",
"Glo Dollar": "USDGLO", "Glo Dollar": "USDGLO",
"H": "Humanity Protocol USD Price", "H": "Humanity Protocol",
"H1": "Haven1", "H1": "Haven1",
"H1DR4": "H1DR4 by Virtuals", "H1DR4": "H1DR4 by Virtuals",
"H2O": "H2O DAO", "H2O": "H2O DAO",
@ -8545,7 +8546,7 @@
"HOSTAI": "Host AI", "HOSTAI": "Host AI",
"HOT": "Holo", "HOT": "Holo",
"HOT1": "Holo", "HOT1": "Holo",
"HOT2682": "Holo USD Price", "HOT2682": "Holo",
"HOTCROSS": "Hot Cross", "HOTCROSS": "Hot Cross",
"HOTDOGE": "Hot Doge", "HOTDOGE": "Hot Doge",
"HOTKEY": "HotKeySwap", "HOTKEY": "HotKeySwap",
@ -8952,7 +8953,7 @@
"INFX": "Influxcoin", "INFX": "Influxcoin",
"ING": "Infinity Games", "ING": "Infinity Games",
"INI": "InitVerse", "INI": "InitVerse",
"INIT": "Initia USD Price", "INIT": "Initia",
"INJ": "Injective", "INJ": "Injective",
"INK": "Ink", "INK": "Ink",
"INN": "Innova", "INN": "Innova",
@ -9293,7 +9294,7 @@
"JINDO": "JINDOGE", "JINDO": "JINDOGE",
"JINDOGE": "Jindoge", "JINDOGE": "Jindoge",
"JIO": "JIO Token", "JIO": "JIO Token",
"JITOSOL": "Jito Staked SOL USD Price", "JITOSOL": "Jito Staked SOL",
"JIZZ": "JizzRocket", "JIZZ": "JizzRocket",
"JIZZLORD": "JizzLord", "JIZZLORD": "JizzLord",
"JIZZUS": "JIZZUS CHRIST", "JIZZUS": "JIZZUS CHRIST",
@ -9301,7 +9302,7 @@
"JK": "JK Coin", "JK": "JK Coin",
"JKC": "JunkCoin", "JKC": "JunkCoin",
"JKL": "Jackal Protocol", "JKL": "Jackal Protocol",
"JLP": "Jupiter Perps LP USD Price", "JLP": "Jupiter Perps LP",
"JLY": "Jellyverse", "JLY": "Jellyverse",
"JM": "JustMoney", "JM": "JustMoney",
"JMC": "Junson Ming Chan Coin", "JMC": "Junson Ming Chan Coin",
@ -9457,7 +9458,7 @@
"KAIM": "Kai Meme", "KAIM": "Kai Meme",
"KAINET": "KAINET", "KAINET": "KAINET",
"KAIRO": "Kairo", "KAIRO": "Kairo",
"KAITO": "KAITO USD Price", "KAITO": "KAITO",
"KAKA": "KAKA NFT World", "KAKA": "KAKA NFT World",
"KAKAXA": "KAKAXA", "KAKAXA": "KAKAXA",
"KAKI": "Doge KaKi", "KAKI": "Doge KaKi",
@ -9694,7 +9695,7 @@
"KISHU": "Kishu Inu", "KISHU": "Kishu Inu",
"KIT": "DexKit", "KIT": "DexKit",
"KITA": "KITA INU", "KITA": "KITA INU",
"KITE": "Kite USD Price", "KITE": "Kite",
"KITEAI": "KITEAI", "KITEAI": "KITEAI",
"KITEHAI": "Kite", "KITEHAI": "Kite",
"KITKAT": "Remember KitKat", "KITKAT": "Remember KitKat",
@ -10274,7 +10275,7 @@
"LISUSD": "lisUSD", "LISUSD": "lisUSD",
"LIT": "Lition", "LIT": "Lition",
"LIT1": "Litentry", "LIT1": "Litentry",
"LIT39125": "Lighter USD Price", "LIT39125": "Lighter",
"LITCOIN": "Litcoin", "LITCOIN": "Litcoin",
"LITE": "Lite USD", "LITE": "Lite USD",
"LITEBTC": "LiteBitcoin", "LITEBTC": "LiteBitcoin",
@ -10362,7 +10363,7 @@
"LOCG": "LOCGame", "LOCG": "LOCGame",
"LOCI": "LociCoin", "LOCI": "LociCoin",
"LOCK": "Meridian Network", "LOCK": "Meridian Network",
"LOCKIN": "LOCK IN USD Price", "LOCKIN": "LOCK IN",
"LOCO": "Loco", "LOCO": "Loco",
"LOCOM": "Locomotir", "LOCOM": "Locomotir",
"LOCUS": "Locus Chain", "LOCUS": "Locus Chain",
@ -10370,7 +10371,7 @@
"LOE": "Legends of Elysium", "LOE": "Legends of Elysium",
"LOF": "Land of Fantasy", "LOF": "Land of Fantasy",
"LOFI": "LOFI", "LOFI": "LOFI",
"LOFI34187": "LOFI USD Price", "LOFI34187": "LOFI",
"LOFIBUZZ": "LOFI", "LOFIBUZZ": "LOFI",
"LOG": "Wood Coin", "LOG": "Wood Coin",
"LOGO": "LOGOS", "LOGO": "LOGOS",
@ -10640,7 +10641,7 @@
"M0": "M by M^0", "M0": "M by M^0",
"M1": "SupplyShock", "M1": "SupplyShock",
"M2O": "M2O Token", "M2O": "M2O Token",
"M35491": "MemeCore USD Price", "M35491": "MemeCore",
"M3H": "MehVerseCoin", "M3H": "MehVerseCoin",
"M3M3": "M3M3", "M3M3": "M3M3",
"M87": "MESSIER", "M87": "MESSIER",
@ -11011,7 +11012,7 @@
"MEFAI": "META FINANCIAL AI", "MEFAI": "META FINANCIAL AI",
"MEFI": "Meo Finance", "MEFI": "Meo Finance",
"MEGA": "MegaCryptoPolis", "MEGA": "MegaCryptoPolis",
"MEGA38770": "MegaETH USD Price", "MEGA38770": "MegaETH",
"MEGABOT": "Megabot", "MEGABOT": "Megabot",
"MEGAD": "Mega Dice Casino", "MEGAD": "Mega Dice Casino",
"MEGAHERO": "MEGAHERO", "MEGAHERO": "MEGAHERO",
@ -11026,7 +11027,7 @@
"MEL": "MELX", "MEL": "MELX",
"MELAN": "Melania Meme", "MELAN": "Melania Meme",
"MELANIA": "Melania Meme", "MELANIA": "Melania Meme",
"MELANIA35347": "Official Melania Meme USD Price", "MELANIA35347": "Official Melania Meme",
"MELANIATRUMP": "Melania Trump", "MELANIATRUMP": "Melania Trump",
"MELB": "Minelab", "MELB": "Minelab",
"MELD": "MELD", "MELD": "MELD",
@ -11106,7 +11107,7 @@
"MESSI": "MESSI COIN", "MESSI": "MESSI COIN",
"MESSU": "Loinel Messu", "MESSU": "Loinel Messu",
"MET": "Metronome", "MET": "Metronome",
"MET38353": "Meteora USD Price", "MET38353": "Meteora",
"META": "Metadium", "META": "Metadium",
"METAA": "META ARENA", "METAA": "META ARENA",
"METABOT": "Robot Warriors", "METABOT": "Robot Warriors",
@ -11513,7 +11514,7 @@
"MOBYONBASEV1": "Moby v1", "MOBYONBASEV1": "Moby v1",
"MOC": "Moss Coin", "MOC": "Moss Coin",
"MOCA": "Moca Network", "MOCA": "Moca Network",
"MOCA31526": "Moca Network USD Price", "MOCA31526": "Moca Network",
"MOCHI": "Mochiswap", "MOCHI": "Mochiswap",
"MOCHI14315": "Mochi", "MOCHI14315": "Mochi",
"MOCHICAT": "MochiCat", "MOCHICAT": "MochiCat",
@ -11576,7 +11577,7 @@
"MOMO2": "MOMO 2.0", "MOMO2": "MOMO 2.0",
"MOMO2025": "momo", "MOMO2025": "momo",
"MON": "PocMon", "MON": "PocMon",
"MON30495": "Monad USD Price", "MON30495": "Monad",
"MON30950": "MON", "MON30950": "MON",
"MONA": "MonaCoin", "MONA": "MonaCoin",
"MONAI": "MONAI", "MONAI": "MONAI",
@ -11631,7 +11632,7 @@
"MOOBIFI": "Staked BIFI", "MOOBIFI": "Staked BIFI",
"MOOCAT": "MooCat", "MOOCAT": "MooCat",
"MOODENG": "Moo Deng (moodengsol.com)", "MOODENG": "Moo Deng (moodengsol.com)",
"MOODENG33093": "Moo Deng USD Price", "MOODENG33093": "Moo Deng",
"MOODENGBNB": "MOODENG (moodengbnb.com)", "MOODENGBNB": "MOODENG (moodengbnb.com)",
"MOODENGSBS": "Moo Deng (moodeng.sbs)", "MOODENGSBS": "Moo Deng (moodeng.sbs)",
"MOODENGSPACE": "MOO DENG", "MOODENGSPACE": "MOO DENG",
@ -12352,7 +12353,7 @@
"NIK": "NIKPLACE", "NIK": "NIKPLACE",
"NIKO": "NikolAI", "NIKO": "NikolAI",
"NIL": "Nillion", "NIL": "Nillion",
"NIL35702": "Nillion USD Price", "NIL35702": "Nillion",
"NILA": "MindWave", "NILA": "MindWave",
"NILE": "Nile", "NILE": "Nile",
"NIM": "Nimiq", "NIM": "Nimiq",
@ -13672,7 +13673,7 @@
"PIPL": "PiplCoin", "PIPL": "PiplCoin",
"PIPO": "Pipo", "PIPO": "Pipo",
"PIPONHL": "PiP", "PIPONHL": "PiP",
"PIPPIN": "pippin USD Price", "PIPPIN": "pippin",
"PIPPKIN": "Pippkin The Horse", "PIPPKIN": "Pippkin The Horse",
"PIPT": "Power Index Pool Token", "PIPT": "Power Index Pool Token",
"PIRATE": "PirateCash", "PIRATE": "PirateCash",
@ -14094,7 +14095,7 @@
"PROLIFIC": "Prolific Game Studio", "PROLIFIC": "Prolific Game Studio",
"PROM": "Prom", "PROM": "Prom",
"PROME": "Prometheus Trading", "PROME": "Prometheus Trading",
"PROMPT": "Wayfinder USD Price", "PROMPT": "Wayfinder",
"PRON": "Rise Of Nebula", "PRON": "Rise Of Nebula",
"PROOF": "PROVER", "PROOF": "PROVER",
"PROP": "Propeller", "PROP": "Propeller",
@ -14250,7 +14251,7 @@
"PURPE": "Purple Pepe", "PURPE": "Purple Pepe",
"PURPLEBTC": "Purple Bitcoin", "PURPLEBTC": "Purple Bitcoin",
"PURR": "Purr", "PURR": "Purr",
"PURR34332": "Purr USD Price", "PURR34332": "Purr",
"PURRC": "Purrcoin", "PURRC": "Purrcoin",
"PURSE": "Pundi X PURSE", "PURSE": "Pundi X PURSE",
"PUS": "Pussy Cat", "PUS": "Pussy Cat",
@ -14357,6 +14358,7 @@
"QGOLD": "Quorium", "QGOLD": "Quorium",
"QGOV": "Q Protocol", "QGOV": "Q Protocol",
"QI": "QiSwap", "QI": "QiSwap",
"QI9288": "BENQI",
"QIE": "QI Blockchain", "QIE": "QI Blockchain",
"QINGWA": "ShangXin QingWa", "QINGWA": "ShangXin QingWa",
"QISWAP": "QiSwap", "QISWAP": "QiSwap",
@ -14546,7 +14548,7 @@
"RATWIF": "RatWifHat", "RATWIF": "RatWifHat",
"RAV": "Ravelin Finance", "RAV": "Ravelin Finance",
"RAVE": "Ravendex", "RAVE": "Ravendex",
"RAVE38967": "RaveDAO USD Price", "RAVE38967": "RaveDAO",
"RAVELOUS": "Ravelous", "RAVELOUS": "Ravelous",
"RAVEN": "Raven Protocol", "RAVEN": "Raven Protocol",
"RAVENCOINC": "Ravencoin Classic", "RAVENCOINC": "Ravencoin Classic",
@ -14620,6 +14622,7 @@
"RDT": "Ridotto", "RDT": "Ridotto",
"RDWON": "Redwire (Ondo Tokenized)", "RDWON": "Redwire (Ondo Tokenized)",
"RDX": "Redux Protocol", "RDX": "Redux Protocol",
"RE": "Re",
"REA": "Realisto", "REA": "Realisto",
"REACH": "/Reach", "REACH": "/Reach",
"REACT": "Reactive Network", "REACT": "Reactive Network",
@ -14706,7 +14709,7 @@
"REINDEER": "Reindeer", "REINDEER": "Reindeer",
"REK": "Rekt", "REK": "Rekt",
"REKT": "Rekt", "REKT": "Rekt",
"REKT34434": "Rekt USD Price", "REKT34434": "Rekt",
"REKTV1": "REKT", "REKTV1": "REKT",
"REKTV2": "REKT 2.0", "REKTV2": "REKT 2.0",
"REKTV3": "REKT v3 (rekt.game)", "REKTV3": "REKT v3 (rekt.game)",
@ -15053,7 +15056,7 @@
"RRT": "Recovery Right Tokens", "RRT": "Recovery Right Tokens",
"RS": "ReadySwap", "RS": "ReadySwap",
"RSC": "ResearchCoin", "RSC": "ResearchCoin",
"RSC27054": "ResearchCoin USD Price", "RSC27054": "ResearchCoin",
"RSETH": "Kelp DAO Restaked ETH", "RSETH": "Kelp DAO Restaked ETH",
"RSF": "Royal Sting", "RSF": "Royal Sting",
"RSG": "RSG TOKEN", "RSG": "RSG TOKEN",
@ -15202,7 +15205,7 @@
"SAD": "SadCat", "SAD": "SadCat",
"SAF": "Safcoin", "SAF": "Safcoin",
"SAFE": "SafeCoin", "SAFE": "SafeCoin",
"SAFE21585": "Safe USD Price", "SAFE21585": "Safe",
"SAFEBTC": "SafeBTC", "SAFEBTC": "SafeBTC",
"SAFEBULL": "SafeBull", "SAFEBULL": "SafeBull",
"SAFECOIN": "SafeCoin", "SAFECOIN": "SafeCoin",
@ -15289,7 +15292,7 @@
"SAO": "Sator", "SAO": "Sator",
"SAP": "SwapAll", "SAP": "SwapAll",
"SAPE": "SolanaApe", "SAPE": "SolanaApe",
"SAPIEN": "Sapien USD Price", "SAPIEN": "Sapien",
"SAPP": "Sapphire", "SAPP": "Sapphire",
"SAPPC": "SappChat", "SAPPC": "SappChat",
"SAR": "Saren", "SAR": "Saren",
@ -15315,7 +15318,7 @@
"SATOX": "Satoxcoin", "SATOX": "Satoxcoin",
"SATOZ": "Satozhi", "SATOZ": "Satozhi",
"SATS": "SATS (Ordinals)", "SATS": "SATS (Ordinals)",
"SATS28194": "SATS (Ordinals) USD Price", "SATS28194": "SATS (Ordinals)",
"SATSALL": "ALL BEST ICO SATOSHI", "SATSALL": "ALL BEST ICO SATOSHI",
"SATT": "SaTT", "SATT": "SaTT",
"SATX": "SATX", "SATX": "SATX",
@ -15514,7 +15517,7 @@
"SENATE": "SENATE", "SENATE": "SENATE",
"SENC": "Sentinel Chain", "SENC": "Sentinel Chain",
"SEND": "Social Send", "SEND": "Social Send",
"SEND34611": "Suilend USD Price", "SEND34611": "Suilend",
"SENDCOIN": "Sendcoin", "SENDCOIN": "Sendcoin",
"SENDOR": "Sendor", "SENDOR": "Sendor",
"SENK": "Senk", "SENK": "Senk",
@ -16125,7 +16128,7 @@
"SNRG": "Synergy", "SNRG": "Synergy",
"SNRK": "Snark Launch", "SNRK": "Snark Launch",
"SNS": "Synesis One", "SNS": "Synesis One",
"SNS36468": "Solana Name Service USD Price", "SNS36468": "Solana Name Service",
"SNST": "Smooth Network Solutions Token", "SNST": "Smooth Network Solutions Token",
"SNSY": "Sensay", "SNSY": "Sensay",
"SNT": "Status", "SNT": "Status",
@ -16283,7 +16286,7 @@
"SONOF": "Son of Solana", "SONOF": "Son of Solana",
"SONOR": "SonorusToken", "SONOR": "SonorusToken",
"SOON": "SoonVerse", "SOON": "SoonVerse",
"SOON36542": "SOON USD Price", "SOON36542": "SOON",
"SOONAVERSE": "Soonaverse", "SOONAVERSE": "Soonaverse",
"SOONCOIN": "SoonCoin", "SOONCOIN": "SoonCoin",
"SOONTOKEN": "SOON", "SOONTOKEN": "SOON",
@ -17340,7 +17343,7 @@
"THOR": "THORSwap", "THOR": "THORSwap",
"THOREUM": "Thoreum V3", "THOREUM": "Thoreum V3",
"THP": "TurboHigh Performance", "THP": "TurboHigh Performance",
"THQ": "Theoriq Token", "THQ": "Theoriq",
"THR": "Thorecoin", "THR": "Thorecoin",
"THREE": "Three Protocol Token", "THREE": "Three Protocol Token",
"THRT": "ThriveToken", "THRT": "ThriveToken",
@ -17751,7 +17754,7 @@
"TRUMP2024": "Donald Trump", "TRUMP2024": "Donald Trump",
"TRUMP27872": "MAGA", "TRUMP27872": "MAGA",
"TRUMP3": "Trump MP3", "TRUMP3": "Trump MP3",
"TRUMP35336": "OFFICIAL TRUMP USD Price", "TRUMP35336": "OFFICIAL TRUMP",
"TRUMP47": "47th President of the United States", "TRUMP47": "47th President of the United States",
"TRUMPA": "TRUMP AI", "TRUMPA": "TRUMP AI",
"TRUMPAI": "Trump Maga AI", "TRUMPAI": "Trump Maga AI",
@ -17890,7 +17893,7 @@
"TURT": "TurtSat", "TURT": "TurtSat",
"TURT28826": "TurtSat", "TURT28826": "TurtSat",
"TURTLE": "Turtle", "TURTLE": "Turtle",
"TURTLE38671": "Turtle USD Price", "TURTLE38671": "Turtle",
"TUS": "Treasure Under Sea", "TUS": "Treasure Under Sea",
"TUSD": "TrueUSD", "TUSD": "TrueUSD",
"TUSDV1": "True USD v1", "TUSDV1": "True USD v1",
@ -18065,7 +18068,7 @@
"UMAMI": "Umami Finance", "UMAMI": "Umami Finance",
"UMB": "Umbrella Network", "UMB": "Umbrella Network",
"UMBR": "Umbria Network", "UMBR": "Umbria Network",
"UMBRA": "Umbra USD Price", "UMBRA": "Umbra",
"UMC": "Umbrella Coin", "UMC": "Umbrella Coin",
"UMI": "UMI", "UMI": "UMI",
"UMID": "Umi Digital", "UMID": "Umi Digital",
@ -18216,7 +18219,7 @@
"USCR": "United States Crypto Reserve", "USCR": "United States Crypto Reserve",
"USD0": "Usual USD", "USD0": "Usual USD",
"USD1": "World Liberty Financial USD", "USD1": "World Liberty Financial USD",
"USD136148": "World Liberty Financial USD USD Price", "USD136148": "World Liberty Financial USD",
"USD3": "Web 3 Dollar", "USD3": "Web 3 Dollar",
"USDA": "USDA", "USDA": "USDA",
"USDACC": "USDA", "USDACC": "USDA",
@ -18296,7 +18299,7 @@
"USE": "Usechain Token", "USE": "Usechain Token",
"USEDCAR": "A Gently Used 2001 Honda", "USEDCAR": "A Gently Used 2001 Honda",
"USELESS": "USELESS COIN", "USELESS": "USELESS COIN",
"USELESS36828": "Useless Coin USD Price", "USELESS36828": "Useless Coin",
"USETH": "USETH", "USETH": "USETH",
"USF": "Unslashed Finance", "USF": "Unslashed Finance",
"USG": "USGold", "USG": "USGold",
@ -18363,7 +18366,7 @@
"UWULEND": "UwU Lend", "UWULEND": "UwU Lend",
"UX": "UX Chain", "UX": "UX Chain",
"UXD": "UXD Stablecoin", "UXD": "UXD Stablecoin",
"UXLINK": "UXLINK USD Price", "UXLINK": "UXLINK",
"UXLINKV1": "UXLINK v1", "UXLINKV1": "UXLINK v1",
"UXOS": "UXOS", "UXOS": "UXOS",
"UXP": "UXD Protocol", "UXP": "UXD Protocol",
@ -19177,7 +19180,7 @@
"WLSC": "WESTLAND SMART CITY", "WLSC": "WESTLAND SMART CITY",
"WLTH": "Common Wealth", "WLTH": "Common Wealth",
"WLUNA": "Wrapped LUNA Token", "WLUNA": "Wrapped LUNA Token",
"WLUNC": "Wrapped LUNA Classic USD Price", "WLUNC": "Wrapped LUNA Classic",
"WLXT": "Wallex Token", "WLXT": "Wallex Token",
"WM": "WrappedM by M^0", "WM": "WrappedM by M^0",
"WMATIC": "Wrapped Matic", "WMATIC": "Wrapped Matic",
@ -19509,7 +19512,7 @@
"XCXT": "CoinonatX", "XCXT": "CoinonatX",
"XD": "Data Transaction Token", "XD": "Data Transaction Token",
"XDAG": "XDAG", "XDAG": "XDAG",
"XDAI": "xDAI", "XDAI": "XDAI",
"XDAO": "XDAO", "XDAO": "XDAO",
"XDATA": "Streamr XDATA", "XDATA": "Streamr XDATA",
"XDB": "DigitalBits", "XDB": "DigitalBits",
@ -19587,7 +19590,7 @@
"XIDR": "XIDR", "XIDR": "XIDR",
"XIL": "Project X", "XIL": "Project X",
"XIN": "Mixin", "XIN": "Mixin",
"XIN2349": "Mixin USD Price", "XIN2349": "Mixin",
"XING": "Xing Xing", "XING": "Xing Xing",
"XINGXING": "星星", "XINGXING": "星星",
"XINU": "XINU", "XINU": "XINU",
@ -19848,7 +19851,7 @@
"YAXIS": "yAxis", "YAXIS": "yAxis",
"YAY": "YAY Games", "YAY": "YAY Games",
"YAYCOIN": "YAYcoin", "YAYCOIN": "YAYcoin",
"YB": "YieldBasis USD Price", "YB": "YieldBasis",
"YBC": "YbCoin", "YBC": "YbCoin",
"YBDBD": "YBDBD", "YBDBD": "YBDBD",
"YBNB": "Yellow BNB 4", "YBNB": "Yellow BNB 4",
@ -20034,7 +20037,7 @@
"ZBCN": "Zebec Network", "ZBCN": "Zebec Network",
"ZBIT": "zbit", "ZBIT": "zbit",
"ZBT": "ZB Token", "ZBT": "ZB Token",
"ZBT38427": "ZEROBASE USD Price", "ZBT38427": "ZEROBASE",
"ZBU": "Zeebu", "ZBU": "Zeebu",
"ZBUV1": "ZEEBU v1", "ZBUV1": "ZEEBU v1",
"ZCC": "ZCC Coin", "ZCC": "ZCC Coin",

2
apps/api/src/helper/country.helper.ts

@ -18,7 +18,7 @@ export function getCountryCodeByName({
} }
} }
if (name) { if (name && name.toLowerCase() !== 'other') {
const logger = new Logger('getCountryCodeByName'); const logger = new Logger('getCountryCodeByName');
logger.warn(`Could not map the country "${name}" to a code`); logger.warn(`Could not map the country "${name}" to a code`);

22
apps/api/src/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service.ts

@ -1,3 +1,4 @@
import { getCountryCodeByName } from '@ghostfolio/api/helper/country.helper';
import { getSectorName } from '@ghostfolio/api/helper/sector.helper'; import { getSectorName } from '@ghostfolio/api/helper/sector.helper';
import { CryptocurrencyService } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.service'; import { CryptocurrencyService } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.service';
import { AssetProfileDelistedError } from '@ghostfolio/api/services/data-provider/errors/asset-profile-delisted.error'; import { AssetProfileDelistedError } from '@ghostfolio/api/services/data-provider/errors/asset-profile-delisted.error';
@ -18,12 +19,16 @@ import {
SymbolProfile SymbolProfile
} from '@prisma/client'; } from '@prisma/client';
import { isISIN } from 'class-validator'; import { isISIN } from 'class-validator';
import { countries } from 'countries-list';
import YahooFinance from 'yahoo-finance2'; import YahooFinance from 'yahoo-finance2';
import type { Price } from 'yahoo-finance2/esm/src/modules/quoteSummary-iface'; import type { Price } from 'yahoo-finance2/esm/src/modules/quoteSummary-iface';
@Injectable() @Injectable()
export class YahooFinanceDataEnhancerService implements DataEnhancerInterface { export class YahooFinanceDataEnhancerService implements DataEnhancerInterface {
private static countriesMapping = {
'Czech Republic': 'CZ',
Turkey: 'TR'
};
private static sectorsMapping: Record<string, SectorName> = { private static sectorsMapping: Record<string, SectorName> = {
basic_materials: 'Basic Materials', basic_materials: 'Basic Materials',
communication_services: 'Communication Services', communication_services: 'Communication Services',
@ -246,15 +251,14 @@ export class YahooFinanceDataEnhancerService implements DataEnhancerInterface {
) { ) {
// Add country if asset is stock and country available // Add country if asset is stock and country available
try { const code = getCountryCodeByName({
const [code] = Object.entries(countries).find(([, country]) => { aliases: YahooFinanceDataEnhancerService.countriesMapping,
return country.name === assetProfile.summaryProfile?.country; name: assetProfile.summaryProfile.country
}); });
if (code) { if (code) {
response.countries = [{ code, weight: 1 }]; response.countries = [{ code, weight: 1 }];
} }
} catch {}
if (assetProfile.summaryProfile?.sector) { if (assetProfile.summaryProfile?.sector) {
response.sectors = [ response.sectors = [

8
apps/api/src/services/data-provider/data-provider.service.ts

@ -340,7 +340,13 @@ export class DataProviderService implements OnModuleInit {
from: Date; from: Date;
granularity: Granularity; granularity: Granularity;
to: Date; to: Date;
} & AssetProfileIdentifier) { } & AssetProfileIdentifier): Promise<{
[date: string]: DataProviderHistoricalResponse;
}> {
if (!isValid(from) || !isValid(to)) {
return {};
}
return this.getDataProvider(DataSource[dataSource]).getDividends({ return this.getDataProvider(DataSource[dataSource]).getDividends({
from, from,
granularity, granularity,

3
apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts

@ -56,7 +56,8 @@ export class FinancialModelingPrepService
private static countriesMapping = { private static countriesMapping = {
'Korea (the Republic of)': 'KR', 'Korea (the Republic of)': 'KR',
'Russian Federation': 'RU', 'Russian Federation': 'RU',
'Taiwan (Province of China)': 'TW' 'Taiwan (Province of China)': 'TW',
Turkey: 'TR'
}; };
private readonly logger = new Logger(FinancialModelingPrepService.name); private readonly logger = new Logger(FinancialModelingPrepService.name);

63
apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts

@ -22,6 +22,7 @@ import {
import { Injectable, Logger } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { DataSource, SymbolProfile } from '@prisma/client'; import { DataSource, SymbolProfile } from '@prisma/client';
import { addDays, format, isSameDay } from 'date-fns'; import { addDays, format, isSameDay } from 'date-fns';
import { ReasonPhrases, StatusCodes } from 'http-status-codes';
import { uniqBy } from 'lodash'; import { uniqBy } from 'lodash';
import YahooFinance from 'yahoo-finance2'; import YahooFinance from 'yahoo-finance2';
import { ChartResultArray } from 'yahoo-finance2/esm/src/modules/chart'; import { ChartResultArray } from 'yahoo-finance2/esm/src/modules/chart';
@ -41,6 +42,12 @@ import { SearchQuoteNonYahoo } from 'yahoo-finance2/esm/src/modules/search';
@Injectable() @Injectable()
export class YahooFinanceService implements DataProviderInterface { export class YahooFinanceService implements DataProviderInterface {
private static readonly DELISTED_ERROR_MESSAGE =
'No data found, symbol may be delisted';
private static readonly RATE_LIMIT_ERROR_MESSAGE =
ReasonPhrases.TOO_MANY_REQUESTS;
private readonly logger = new Logger(YahooFinanceService.name); private readonly logger = new Logger(YahooFinanceService.name);
private readonly yahooFinance = new YahooFinance({ private readonly yahooFinance = new YahooFinance({
@ -77,10 +84,6 @@ export class YahooFinanceService implements DataProviderInterface {
symbol, symbol,
to to
}: GetDividendsParams) { }: GetDividendsParams) {
if (isSameDay(from, to)) {
to = addDays(to, 1);
}
try { try {
const historicalResult = this.convertToDividendResult( const historicalResult = this.convertToDividendResult(
await this.yahooFinance.chart( await this.yahooFinance.chart(
@ -91,7 +94,10 @@ export class YahooFinanceService implements DataProviderInterface {
events: 'dividends', events: 'dividends',
interval: granularity === 'month' ? '1mo' : '1d', interval: granularity === 'month' ? '1mo' : '1d',
period1: format(from, DATE_FORMAT), period1: format(from, DATE_FORMAT),
period2: format(to, DATE_FORMAT) period2: format(
isSameDay(from, to) ? addDays(to, 1) : to,
DATE_FORMAT
)
} }
) )
); );
@ -107,12 +113,26 @@ export class YahooFinanceService implements DataProviderInterface {
return response; return response;
} catch (error) { } catch (error) {
this.logger.error( const message = `Could not get dividends for ${symbol} (${this.getName()}) from ${format(
`Could not get dividends for ${symbol} (${this.getName()}) from ${format( from,
from, DATE_FORMAT
DATE_FORMAT )} to ${format(to, DATE_FORMAT)}`;
)} to ${format(to, DATE_FORMAT)}: [${error.name}] ${error.message}`
); if (error?.message === YahooFinanceService.DELISTED_ERROR_MESSAGE) {
this.logger.warn(
`${message}: ${YahooFinanceService.DELISTED_ERROR_MESSAGE}`
);
} else if (
(error?.name === 'HTTPError' &&
error?.code === StatusCodes.TOO_MANY_REQUESTS) ||
error?.message?.startsWith(YahooFinanceService.RATE_LIMIT_ERROR_MESSAGE)
) {
this.logger.warn(
`${message}: ${YahooFinanceService.RATE_LIMIT_ERROR_MESSAGE}`
);
} else {
this.logger.error(`${message}: [${error?.name}] ${error?.message}`);
}
return {}; return {};
} }
@ -125,10 +145,6 @@ export class YahooFinanceService implements DataProviderInterface {
}: GetHistoricalParams): Promise<{ }: GetHistoricalParams): Promise<{
[date: string]: DataProviderHistoricalResponse; [date: string]: DataProviderHistoricalResponse;
}> { }> {
if (isSameDay(from, to)) {
to = addDays(to, 1);
}
try { try {
const historicalResult = this.convertToHistoricalResult( const historicalResult = this.convertToHistoricalResult(
await this.yahooFinance.chart( await this.yahooFinance.chart(
@ -138,7 +154,10 @@ export class YahooFinanceService implements DataProviderInterface {
{ {
interval: '1d', interval: '1d',
period1: format(from, DATE_FORMAT), period1: format(from, DATE_FORMAT),
period2: format(to, DATE_FORMAT) period2: format(
isSameDay(from, to) ? addDays(to, 1) : to,
DATE_FORMAT
)
} }
) )
); );
@ -155,7 +174,7 @@ export class YahooFinanceService implements DataProviderInterface {
return response; return response;
} catch (error) { } catch (error) {
if (error.message === 'No data found, symbol may be delisted') { if (error?.message === YahooFinanceService.DELISTED_ERROR_MESSAGE) {
throw new AssetProfileDelistedError( throw new AssetProfileDelistedError(
`No data found, ${symbol} (${this.getName()}) may be delisted` `No data found, ${symbol} (${this.getName()}) may be delisted`
); );
@ -164,7 +183,7 @@ export class YahooFinanceService implements DataProviderInterface {
`Could not get historical market data for ${symbol} (${this.getName()}) from ${format( `Could not get historical market data for ${symbol} (${this.getName()}) from ${format(
from, from,
DATE_FORMAT DATE_FORMAT
)} to ${format(to, DATE_FORMAT)}: [${error.name}] ${error.message}` )} to ${format(to, DATE_FORMAT)}: [${error?.name}] ${error?.message}`
); );
} }
} }
@ -343,9 +362,11 @@ export class YahooFinanceService implements DataProviderInterface {
private convertToDividendResult( private convertToDividendResult(
result: ChartResultArray result: ChartResultArray
): HistoricalDividendsResult { ): HistoricalDividendsResult {
return result.events.dividends.map(({ amount: dividends, date }) => { return (result.events?.dividends ?? []).map(
return { date, dividends }; ({ amount: dividends, date }) => {
}); return { date, dividends };
}
);
} }
private convertToHistoricalResult( private convertToHistoricalResult(

38
libs/common/src/lib/personal-finance-tools.ts

@ -1252,6 +1252,19 @@ export const personalFinanceTools: Product[] = [
slogan: 'Know where your money is going', slogan: 'Know where your money is going',
url: 'https://www.pocketsmith.com' url: 'https://www.pocketsmith.com'
}, },
{
categories: ['FINANCIAL_PLANNING', 'NET_WORTH_TRACKING'],
hasFreePlan: true,
hasSelfHostingAbility: false,
key: 'portfolio-coach',
languages: ['English'],
name: 'Portfolio Coach',
origin: 'GB',
platforms: ['WEB'],
pricingPerYear: '£120',
slogan: 'Know your wealth. Ask it anything.',
url: 'https://portfoliocoach.co.uk'
},
{ {
categories: ['DIVIDEND_TRACKING', 'STOCK_TRACKING'], categories: ['DIVIDEND_TRACKING', 'STOCK_TRACKING'],
hasFreePlan: false, hasFreePlan: false,
@ -1276,6 +1289,18 @@ export const personalFinanceTools: Product[] = [
slogan: 'Tools for Better Investors', slogan: 'Tools for Better Investors',
url: 'https://www.portfoliovisualizer.com' url: 'https://www.portfoliovisualizer.com'
}, },
{
categories: ['DIVIDEND_TRACKING', 'ETF_TRACKING', 'STOCK_TRACKING'],
hasFreePlan: true,
hasSelfHostingAbility: false,
key: 'portfolioglance',
languages: ['English', 'Polski'],
name: 'PortfolioGlance',
origin: 'PL',
platforms: ['WEB'],
slogan: 'One look. Clear picture of your portfolio.',
url: 'https://www.portfolioglance.com'
},
{ {
categories: ['STOCK_TRACKING'], categories: ['STOCK_TRACKING'],
hasFreePlan: true, hasFreePlan: true,
@ -1551,6 +1576,19 @@ export const personalFinanceTools: Product[] = [
'Your financial life in a spreadsheet, automatically updated each day', 'Your financial life in a spreadsheet, automatically updated each day',
url: 'https://tiller.com' url: 'https://tiller.com'
}, },
{
categories: ['CRYPTOCURRENCY', 'DIVIDEND_TRACKING', 'STOCK_TRACKING'],
hasFreePlan: true,
hasSelfHostingAbility: false,
key: 'tinywallet',
languages: ['Deutsch', 'English'],
name: 'TinyWallet',
origin: 'DE',
platforms: ['WEB'],
pricingPerYear: '€35',
slogan: 'Your key to empowered wealth management',
url: 'https://www.tinywallet.de'
},
{ {
founded: 2011, founded: 2011,
hasFreePlan: false, hasFreePlan: false,

8
package-lock.json

@ -63,7 +63,7 @@
"color": "5.0.3", "color": "5.0.3",
"cookie-parser": "1.4.7", "cookie-parser": "1.4.7",
"countries-and-timezones": "3.9.0", "countries-and-timezones": "3.9.0",
"countries-list": "3.4.0", "countries-list": "3.4.1",
"countup.js": "2.10.1", "countup.js": "2.10.1",
"date-fns": "4.4.0", "date-fns": "4.4.0",
"dotenv": "17.4.2", "dotenv": "17.4.2",
@ -16600,9 +16600,9 @@
} }
}, },
"node_modules/countries-list": { "node_modules/countries-list": {
"version": "3.4.0", "version": "3.4.1",
"resolved": "https://registry.npmjs.org/countries-list/-/countries-list-3.4.0.tgz", "resolved": "https://registry.npmjs.org/countries-list/-/countries-list-3.4.1.tgz",
"integrity": "sha512-SonKkjlEGUNz/Ugj6akOMUl8/F6EBVbLq/nQbDKhZo60EFyNgsVIgDP521J9pGhywikV514Ct4BueIqiOnvBBA==", "integrity": "sha512-sKLGAZD5EAdzQwQ19vOp/9u33MTjmZ0CkdXPYtp/An6cyIlJ/E8TwGf2FcPe1AnzHO+h7nbTfoHyXIi58Hpa+Q==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/countup.js": { "node_modules/countup.js": {

2
package.json

@ -107,7 +107,7 @@
"color": "5.0.3", "color": "5.0.3",
"cookie-parser": "1.4.7", "cookie-parser": "1.4.7",
"countries-and-timezones": "3.9.0", "countries-and-timezones": "3.9.0",
"countries-list": "3.4.0", "countries-list": "3.4.1",
"countup.js": "2.10.1", "countup.js": "2.10.1",
"date-fns": "4.4.0", "date-fns": "4.4.0",
"dotenv": "17.4.2", "dotenv": "17.4.2",

Loading…
Cancel
Save