Policy
Policies are immutable configuration objects that control how financial calculations are performed, formatted, and displayed throughout Metric Engine. They provide consistent, predictable behavior across all operations.
What is a Policy?
A Policy defines the rules for:
Formatting - Decimal places, thousands separators, currency symbols
Rounding - How numbers are rounded during calculations and display
Display preferences - How percentages, money, and null values appear
Behavior controls - Error handling, validation rules, arithmetic modes
Localization - Currency positioning, regional formatting preferences
Policies ensure consistent behavior across all financial calculations and provide fine-grained control over how results are presented.
Core Configuration Options
Precision and Rounding
from metricengine import Policy, money
from decimal import ROUND_DOWN, ROUND_UP
# Default policy: 2 decimal places, round half up
default_policy = Policy()
amount = money(123.456, policy=default_policy)
print(amount) # "$123.46"
# High precision policy
precision_policy = Policy(decimal_places=4, rounding=ROUND_DOWN)
precise_amount = money(123.456789, policy=precision_policy)
print(precise_amount) # "$123.4567"
# Conservative rounding for financial reporting
conservative_policy = Policy(decimal_places=2, rounding=ROUND_DOWN)
conservative_amount = money(999.999, policy=conservative_policy)
print(conservative_amount) # "$999.99"
Currency Formatting
# US Dollar formatting (default)
usd_policy = Policy(
currency_symbol="$",
currency_position="prefix",
thousands_sep=True,
negative_parentheses=False
)
# European Euro formatting
eur_policy = Policy(
currency_symbol="€",
currency_position="suffix",
thousands_sep=True,
decimal_places=2
)
# Accounting style with parentheses for negatives
accounting_policy = Policy(
currency_symbol="$",
currency_position="prefix",
negative_parentheses=True,
thousands_sep=True
)
amount = money(1234.56)
usd_amount = amount.with_policy(usd_policy)
eur_amount = amount.with_policy(eur_policy)
accounting_negative = money(-1234.56, policy=accounting_policy)
print(usd_amount) # "$1,234.56"
print(eur_amount) # "1,234.56€"
print(accounting_negative) # "($1,234.56)"
Percentage Display
from metricengine import percent, Policy
# Standard percentage display (multiply by 100)
percent_policy = Policy(percent_display="percent", decimal_places=2)
growth = percent(0.155, input="ratio", policy=percent_policy)
print(growth) # "15.50%"
# Ratio display (show as decimal)
ratio_policy = Policy(percent_display="ratio", decimal_places=3)
multiplier = percent(0.155, input="ratio", policy=ratio_policy)
print(multiplier) # "0.155"
# Capped percentages for display purposes
capped_policy = Policy(
percent_display="percent",
cap_percentage_at=Decimal("100.00"), # Cap at 100%
decimal_places=1
)
Null Value Handling
# Custom null text
custom_null_policy = Policy(none_text="N/A")
invalid_amount = money(None, policy=custom_null_policy)
print(invalid_amount) # "N/A"
# Different null representations
dash_policy = Policy(none_text="—") # Em dash (default)
blank_policy = Policy(none_text="") # Empty string
na_policy = Policy(none_text="Not Available") # Descriptive text
Policy Context Management
Using Policies with Context Managers
Metric Engine provides context managers for applying policies across multiple operations:
from metricengine import use_policy, Policy, money, percent
# Create some data
base_amount = money(10000)
growth_rate = percent(15, input="percent")
# Standard precision context
standard_policy = Policy(decimal_places=2)
with use_policy(standard_policy):
result = base_amount * growth_rate
final = base_amount + result
print(final) # "$11,500.00"
# High precision context for detailed analysis
analysis_policy = Policy(decimal_places=4)
with use_policy(analysis_policy):
precise_result = base_amount * growth_rate
precise_final = base_amount + precise_result
print(precise_final) # "$11,500.0000"
Policy Resolution Modes
Control how policies are chosen when operations involve values with different policies:
from metricengine import use_policy_resolution, PolicyResolution
from metricengine import Policy, money
# Create values with different policies
amount1 = money(100, policy=Policy(decimal_places=2))
amount2 = money(200, policy=Policy(decimal_places=4))
# LEFT_OPERAND: Use policy from left operand
with use_policy_resolution(PolicyResolution.LEFT_OPERAND):
result = amount1 + amount2 # Uses 2 decimal places
print(result) # "$300.00"
# CONTEXT: Use active context policy
high_precision = Policy(decimal_places=6)
with use_policy(high_precision):
with use_policy_resolution(PolicyResolution.CONTEXT):
result = amount1 + amount2 # Uses context policy (6 decimal places)
print(result) # "$300.000000"
# STRICT_MATCH: Require matching policies
with use_policy_resolution(PolicyResolution.STRICT_MATCH):
try:
result = amount1 + amount2 # Raises ValueError
except ValueError as e:
print("Policies must match in STRICT_MATCH mode")
Advanced Policy Features
Custom Quantizer Functions
Create specialized rounding behaviors:
from decimal import Decimal
def quarter_rounding(decimal_places: int) -> Decimal:
"""Round to nearest quarter (0.25)."""
if decimal_places == 2:
return Decimal("0.25")
return Decimal(1).scaleb(-decimal_places)
quarter_policy = Policy(
quantizer_factory=quarter_rounding,
decimal_places=2
)
# Prices rounded to nearest quarter
price = money(12.37, policy=quarter_policy)
print(price) # "$12.25" (rounded down to nearest quarter)
price2 = money(12.63, policy=quarter_policy)
print(price2) # "$12.75" (rounded up to nearest quarter)
Behavior Control Flags
# Control specific calculation behaviors
strict_policy = Policy(
negative_sales_is_none=True, # Treat negative sales as None
arithmetic_strict=True, # Strict arithmetic error handling
compare_none_as_minus_infinity=False # None comparison behavior
)
# Apply to calculations
from metricengine import Engine
engine = Engine(policy=strict_policy)
Real-World Policy Applications
Financial Reporting
# GAAP-compliant financial reporting policy
gaap_policy = Policy(
decimal_places=2,
currency_symbol="$",
currency_position="prefix",
thousands_sep=True,
negative_parentheses=True, # Standard accounting format
none_text="—"
)
# International reporting
ifrs_policy = Policy(
decimal_places=2,
currency_symbol="€",
currency_position="suffix",
thousands_sep=True,
negative_parentheses=False,
none_text="N/A"
)
Trading Systems
# High-frequency trading: maximum precision
hft_policy = Policy(
decimal_places=8,
rounding=ROUND_DOWN, # Conservative rounding
thousands_sep=False, # Clean numeric display
currency_symbol=None # No currency decoration
)
# Portfolio analysis: balanced precision and readability
portfolio_policy = Policy(
decimal_places=4,
currency_symbol="$",
thousands_sep=True,
percent_display="percent",
none_text="No Data"
)
Dashboard Applications
# Executive dashboard: clean, readable format
dashboard_policy = Policy(
decimal_places=0, # Round to whole dollars
thousands_sep=True,
currency_symbol="$",
negative_parentheses=False
)
# Analyst dashboard: detailed precision
analyst_policy = Policy(
decimal_places=2,
thousands_sep=True,
currency_symbol="$",
percent_display="percent",
cap_percentage_at=Decimal("999.99") # Cap extreme percentages
)
# Create dashboard metrics
revenue = money(1_234_567.89)
print(revenue.with_policy(dashboard_policy)) # "$1,234,568"
print(revenue.with_policy(analyst_policy)) # "$1,234,567.89"
Policy Inheritance and Propagation
Automatic Policy Inheritance
Financial values inherit policies through operations:
# Base policy
base_policy = Policy(decimal_places=3, currency_symbol="$")
amount = money(100.1234, policy=base_policy)
# Operations inherit the policy
doubled = amount * 2
print(doubled) # "$200.247" (3 decimal places inherited)
# Percentage calculations maintain policy context
rate = percent(10, input="percent")
increase = amount * rate
print(increase) # "$10.012" (policy inherited from amount)
Policy Precedence Rules
Context Policy (via
use_policy) takes highest precedenceOperand Policies - resolved based on resolution mode
Default Policy - fallback when no specific policy is set
# Demonstrate policy precedence
amount1 = money(100, policy=Policy(decimal_places=1))
amount2 = money(200, policy=Policy(decimal_places=3))
# Context overrides operand policies
context_policy = Policy(decimal_places=5)
with use_policy(context_policy):
result = amount1 + amount2
print(result.policy.decimal_places) # 5 (context wins)
Performance Considerations
Policy Reuse
Policies are immutable and can be safely reused:
# Create shared policies for performance
STANDARD_MONEY = Policy(decimal_places=2, currency_symbol="$", thousands_sep=True)
HIGH_PRECISION = Policy(decimal_places=6, currency_symbol="$")
# Reuse across many values
amounts = [money(val, policy=STANDARD_MONEY) for val in [100, 200, 300]]
precise_amounts = [money(val, policy=HIGH_PRECISION) for val in [1.234567, 2.345678]]
Context Manager Efficiency
Use context managers for batch operations:
# Efficient: Set policy once for many operations
with use_policy(STANDARD_MONEY):
results = []
for value in large_dataset:
processed = money(value) * growth_rate
results.append(processed)
# Less efficient: Set policy per operation
results = []
for value in large_dataset:
processed = money(value, policy=STANDARD_MONEY) * growth_rate
results.append(processed)
Best Practices
1. Define Domain-Specific Policies
# Create policies for specific use cases
ACCOUNTING_POLICY = Policy(
decimal_places=2,
currency_symbol="$",
negative_parentheses=True,
thousands_sep=True
)
TRADING_POLICY = Policy(
decimal_places=4,
currency_symbol=None,
thousands_sep=False,
rounding=ROUND_DOWN
)
REPORTING_POLICY = Policy(
decimal_places=2,
currency_symbol="$",
thousands_sep=True,
none_text="N/A"
)
2. Use Context Managers for Consistency
def generate_financial_report(data):
with use_policy(REPORTING_POLICY):
revenue = money(data['revenue'])
costs = money(data['costs'])
profit = revenue - costs
margin = (profit / revenue).as_percentage()
return {
'revenue': str(revenue),
'costs': str(costs),
'profit': str(profit),
'margin': str(margin)
}
3. Validate Policy Configuration
def create_validated_policy(**kwargs):
"""Create policy with validation."""
if 'decimal_places' in kwargs and kwargs['decimal_places'] < 0:
raise ValueError("decimal_places must be non-negative")
if 'currency_symbol' in kwargs:
symbol = kwargs['currency_symbol']
if symbol is not None and not symbol.strip():
raise ValueError("currency_symbol must be non-empty or None")
return Policy(**kwargs)
4. Document Policy Choices
class PolicyPresets:
"""Standard policy configurations for different use cases."""
# General purpose financial calculations
STANDARD = Policy(
decimal_places=2,
currency_symbol="$",
thousands_sep=True,
negative_parentheses=False
)
# High-precision scientific calculations
SCIENTIFIC = Policy(
decimal_places=10,
currency_symbol=None,
thousands_sep=False,
rounding=ROUND_HALF_UP
)
# Accounting and financial reporting
ACCOUNTING = Policy(
decimal_places=2,
currency_symbol="$",
thousands_sep=True,
negative_parentheses=True, # Standard accounting format
none_text="—"
)
Policies provide the foundation for consistent, predictable financial calculations throughout Metric Engine, ensuring your results are formatted correctly and calculations behave as expected across different contexts and use cases.