Skip to content

Commit 6b6111d

Browse files
authored
Merge pull request #1692 from rodrigobnogueira/feature/decimal-support-finance
feat(finance): add Decimal support with precision control
2 parents 2c36c22 + 6a53f6e commit 6b6111d

2 files changed

Lines changed: 70 additions & 15 deletions

File tree

mimesis/providers/finance.py

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Business data provider."""
22

3+
from decimal import Decimal
4+
35
from mimesis.datasets import (
46
CRYPTOCURRENCY_ISO_CODES,
57
CRYPTOCURRENCY_SYMBOLS,
@@ -82,31 +84,51 @@ def cryptocurrency_symbol(self) -> str:
8284
"""
8385
return self.random.choice(CRYPTOCURRENCY_SYMBOLS)
8486

85-
def price(self, minimum: float = 500, maximum: float = 1500) -> float:
87+
def price(
88+
self,
89+
minimum: float = 500,
90+
maximum: float = 1500,
91+
precision: int = 2,
92+
as_decimal: bool = False,
93+
) -> float | Decimal:
8694
"""Generate a random price.
8795
8896
:param minimum: Minimum value of price.
8997
:param maximum: Maximum value of price.
90-
:return: Price.
98+
:param precision: Number of decimal places (default 2).
99+
:param as_decimal: If True, returns Decimal for high-precision.
100+
:return: Price as float or Decimal.
91101
"""
92-
return self.random.uniform(
93-
minimum,
94-
maximum,
95-
precision=2,
96-
)
97-
98-
def price_in_btc(self, minimum: float = 0, maximum: float = 2) -> float:
102+
if as_decimal:
103+
factor = Decimal(10) ** precision
104+
min_units = int(Decimal(str(minimum)) * factor)
105+
max_units = int(Decimal(str(maximum)) * factor)
106+
units = self.random.randint(min_units, max_units)
107+
return Decimal(units) / factor
108+
return self.random.uniform(minimum, maximum, precision=precision)
109+
110+
def price_in_btc(
111+
self,
112+
minimum: float = 0,
113+
maximum: float = 2,
114+
precision: int = 8,
115+
as_decimal: bool = False,
116+
) -> float | Decimal:
99117
"""Generates a random price in BTC.
100118
101119
:param minimum: Minimum value of price.
102120
:param maximum: Maximum value of price.
103-
:return: Price in BTC.
121+
:param precision: Number of decimal places (default 8 for satoshi).
122+
:param as_decimal: If True, returns Decimal for high-precision.
123+
:return: Price in BTC as float or Decimal.
104124
"""
105-
return self.random.uniform(
106-
minimum,
107-
maximum,
108-
precision=7,
109-
)
125+
if as_decimal:
126+
factor = Decimal(10) ** precision
127+
min_units = int(Decimal(str(minimum)) * factor)
128+
max_units = int(Decimal(str(maximum)) * factor)
129+
units = self.random.randint(min_units, max_units)
130+
return Decimal(units) / factor
131+
return self.random.uniform(minimum, maximum, precision=precision)
110132

111133
def stock_ticker(self) -> str:
112134
"""Generates a random stock ticker.

tests/test_providers/test_localized/test_finance.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import re
2+
from decimal import Decimal
23

34
import pytest
45

@@ -79,6 +80,12 @@ def test_price(self, finance):
7980
result = finance.price(minimum=100.00, maximum=1999.99)
8081
assert isinstance(result, float)
8182

83+
def test_price_as_decimal(self, finance):
84+
result = finance.price(minimum=100.00, maximum=1999.99, as_decimal=True)
85+
assert isinstance(result, Decimal)
86+
assert result >= Decimal("100.00")
87+
assert result <= Decimal("1999.99")
88+
8289
@pytest.mark.parametrize(
8390
"minimum, maximum",
8491
[
@@ -92,6 +99,26 @@ def test_price_in_btc(self, _finance, minimum, maximum):
9299
assert float(price) >= minimum
93100
assert float(price) <= maximum
94101

102+
def test_price_in_btc_as_decimal(self, _finance):
103+
result = _finance.price_in_btc(minimum=0.5, maximum=1.5, as_decimal=True)
104+
assert isinstance(result, Decimal)
105+
assert result >= Decimal("0.5")
106+
assert result <= Decimal("1.5")
107+
108+
def test_price_precision(self, _finance):
109+
result = _finance.price(minimum=10, maximum=100, precision=4, as_decimal=True)
110+
assert isinstance(result, Decimal)
111+
decimal_places = abs(result.as_tuple().exponent)
112+
assert decimal_places <= 4
113+
114+
def test_price_in_btc_precision(self, _finance):
115+
result = _finance.price_in_btc(
116+
minimum=0, maximum=1, precision=4, as_decimal=True
117+
)
118+
assert isinstance(result, Decimal)
119+
decimal_places = abs(result.as_tuple().exponent)
120+
assert decimal_places <= 4
121+
95122

96123
class TestSeededFinance:
97124
@pytest.fixture()
@@ -138,5 +165,11 @@ def test_price_in_btc(self, f1, f2):
138165
assert f1.price_in_btc() == f2.price_in_btc()
139166
assert f1.price_in_btc(1.11, 22.2) == f2.price_in_btc(1.11, 22.2)
140167

168+
def test_price_as_decimal(self, f1, f2):
169+
assert f1.price(as_decimal=True) == f2.price(as_decimal=True)
170+
171+
def test_price_in_btc_as_decimal(self, f1, f2):
172+
assert f1.price_in_btc(as_decimal=True) == f2.price_in_btc(as_decimal=True)
173+
141174
def test_bank(self, f1, f2):
142175
assert f1.bank() == f2.bank()

0 commit comments

Comments
 (0)