Library for fetching Peruvian exchange rates
Project description
[pkg]: perexchange
Core library for fetching PEN-USD exchange rates from Peruvian exchange houses. To install it, run:
pip install perexchange
The library provides a single async function that fetches rates from multiple sources concurrently:
import asyncio
import perexchange as px
async def main():
rates = await px.fetch_rates()
best = min(rates, key=lambda r: r.buy_price)
print(f"{best.name}: S/{best.buy_price}")
asyncio.run(main())
Fetching rates
Call fetch_rates() to retrieve current rates. By default it queries all available
sources: cambioseguro, cuantoestaeldolar, tkambio, tucambista, and westernunion. The
function returns a list of ExchangeRate objects.
rates = await px.fetch_rates()
You can fetch from specific sources by passing a list of house names. This is useful when you only need rates from certain providers or want to reduce latency:
rates = await px.fetch_rates(houses=["tkambio", "tucambista"])
The function accepts timeout and retry parameters. Timeout applies per house, not to the entire operation. Retries only trigger on network errors, not parsing failures:
rates = await px.fetch_rates(timeout=15.0, max_retries=5)
Failed sources are silently skipped. The function returns whatever rates it successfully fetched, or an empty list if everything fails. Network errors trigger automatic retries with exponential backoff. Parsing errors fail immediately.
Working with rates
Each ExchangeRate contains the house name, buy and sell prices, and a UTC timestamp. Buy
price is what you pay in soles to buy dollars. Sell price is what you receive in soles
when selling dollars. The object is a frozen dataclass:
rate = rates[0]
name = rate.name
buy = rate.buy_price
sell = rate.sell_price
when = rate.timestamp
spread = rate.spread
The spread property returns the difference between sell and buy prices. Some sources
return multiple tiers with different rates based on transaction amount, like
tkambio_5000 and tkambio_10000.
Find the best rates by sorting or filtering the list:
best_buy = min(rates, key=lambda r: r.buy_price)
best_sell = max(rates, key=lambda r: r.sell_price)
recent = [r for r in rates if (datetime.now(timezone.utc) - r.timestamp).seconds < 300]
Error handling
Invalid house names raise ValueError immediately. All other failures are silent. Check
the returned list to see what succeeded:
try:
rates = await px.fetch_rates(houses=["nonexistent"])
except ValueError as e:
print(f"Unknown house: {e}")
rates = await px.fetch_rates()
if not rates:
print("All sources failed")
The library doesn't distinguish between different failure types. A house might fail due to network issues, API changes, or parsing errors. Failed requests don't pollute your results or logs.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file perexchange-1.0.0.tar.gz.
File metadata
- Download URL: perexchange-1.0.0.tar.gz
- Upload date:
- Size: 10.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
058bb55d9995fc9f8604d270794114618bddb99671e2b8d1e0f981bf608542d2
|
|
| MD5 |
d784d0e1c268afd72a07df0ae7824147
|
|
| BLAKE2b-256 |
9ad318bad61de9310b8afa280c713e94770c65e6c60553e2f16ff4642adacbb1
|
Provenance
The following attestation bundles were made for perexchange-1.0.0.tar.gz:
Publisher:
publish.yml on totallynotdavid/perexchange
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
perexchange-1.0.0.tar.gz -
Subject digest:
058bb55d9995fc9f8604d270794114618bddb99671e2b8d1e0f981bf608542d2 - Sigstore transparency entry: 708381454
- Sigstore integration time:
-
Permalink:
totallynotdavid/perexchange@2d9af53c2d7cbf6ef62ae3911f3b5370b3c94e9d -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/totallynotdavid
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2d9af53c2d7cbf6ef62ae3911f3b5370b3c94e9d -
Trigger Event:
push
-
Statement type:
File details
Details for the file perexchange-1.0.0-py3-none-any.whl.
File metadata
- Download URL: perexchange-1.0.0-py3-none-any.whl
- Upload date:
- Size: 17.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0c039899d99a43046a40ed25d936727cd78ce712487742f65b85c4d6be794e13
|
|
| MD5 |
8051a882c1c669d6f671b5ef25de9ac7
|
|
| BLAKE2b-256 |
68f9c21e660a756b5595a931deeb2069e16f43e7d55badff76a4a19d4cea4388
|
Provenance
The following attestation bundles were made for perexchange-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on totallynotdavid/perexchange
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
perexchange-1.0.0-py3-none-any.whl -
Subject digest:
0c039899d99a43046a40ed25d936727cd78ce712487742f65b85c4d6be794e13 - Sigstore transparency entry: 708381457
- Sigstore integration time:
-
Permalink:
totallynotdavid/perexchange@2d9af53c2d7cbf6ef62ae3911f3b5370b3c94e9d -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/totallynotdavid
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2d9af53c2d7cbf6ef62ae3911f3b5370b3c94e9d -
Trigger Event:
push
-
Statement type: