Files
2026-02-21 07:43:33 +01:00

282 lines
9.5 KiB
Python

"""Comprehensive tests for the price_utils module.
Tests cover all 8 pure utility functions:
- get_price_diff_pct
- get_price_in_range_pct
- get_price_at_pct
- extend_range_by_pct
- get_price_change_pct
- increase_value_by_pct
- decrease_value_by_pct
"""
import pytest
from aiomql.utils.price_utils import (
get_price_diff_pct,
get_price_in_range_pct,
get_price_at_pct,
extend_range_by_pct,
get_price_change_pct,
increase_value_by_pct,
decrease_value_by_pct,
)
class TestGetPriceDiffPct:
"""Tests for get_price_diff_pct function."""
def test_equal_values(self):
"""Test zero difference when values are equal."""
assert get_price_diff_pct(50, 50) == 0.0
def test_small_difference(self):
"""Test small percentage difference."""
result = get_price_diff_pct(100, 110)
assert pytest.approx(result, rel=1e-6) == 9.523809523809524
def test_large_difference(self):
"""Test large percentage difference."""
result = get_price_diff_pct(200, 100)
assert pytest.approx(result, rel=1e-6) == 66.66666666666666
def test_order_does_not_matter(self):
"""Test that argument order gives same result (symmetric)."""
assert get_price_diff_pct(100, 110) == get_price_diff_pct(110, 100)
def test_with_decimals(self):
"""Test with decimal values (common in forex pricing)."""
result = get_price_diff_pct(1.1000, 1.1050)
assert result > 0
def test_very_close_values(self):
"""Test with very close values."""
result = get_price_diff_pct(1.10000, 1.10001)
assert result > 0
assert result < 0.01 # Very small percentage
class TestGetPriceInRangePct:
"""Tests for get_price_in_range_pct function."""
def test_midpoint(self):
"""Test value at midpoint returns 50%."""
assert get_price_in_range_pct(0, 100, 50) == 50.0
def test_start_value(self):
"""Test value at start returns 0%."""
assert get_price_in_range_pct(0, 100, 0) == 0.0
def test_end_value(self):
"""Test value at end returns 100%."""
assert get_price_in_range_pct(0, 100, 100) == 100.0
def test_quarter(self):
"""Test value at 25%."""
assert get_price_in_range_pct(0, 100, 25) == 25.0
def test_offset_range(self):
"""Test with non-zero start."""
assert get_price_in_range_pct(10, 20, 15) == 50.0
def test_beyond_range(self):
"""Test value beyond the range returns > 100%."""
result = get_price_in_range_pct(0, 100, 150)
assert result == 150.0
def test_below_range(self):
"""Test value below the range returns negative."""
result = get_price_in_range_pct(10, 20, 5)
assert result == -50.0
def test_forex_prices(self):
"""Test with realistic forex price ranges."""
# Price at 80% of move from 1.1000 to 1.1100
result = get_price_in_range_pct(1.1000, 1.1100, 1.1080)
assert pytest.approx(result, rel=1e-6) == 80.0
class TestGetPriceAtPct:
"""Tests for get_price_at_pct function."""
def test_zero_percent(self):
"""Test 0% returns start value."""
assert get_price_at_pct(0, 100, 0) == 0.0
def test_hundred_percent(self):
"""Test 100% returns end value."""
assert get_price_at_pct(0, 100, 100) == 100.0
def test_fifty_percent(self):
"""Test 50% returns midpoint."""
assert get_price_at_pct(0, 100, 50) == 50.0
def test_offset_range(self):
"""Test with non-zero start."""
assert get_price_at_pct(10, 20, 50) == 15.0
def test_twenty_five_percent(self):
"""Test 25%."""
assert get_price_at_pct(0, 200, 25) == 50.0
def test_over_hundred_percent(self):
"""Test beyond 100% extends past end."""
result = get_price_at_pct(0, 100, 150)
assert result == 150.0
def test_inverse_of_get_price_in_range_pct(self):
"""Test that get_price_at_pct is the inverse of get_price_in_range_pct."""
start, end = 1.1000, 1.1100
pct = 75.0
value = get_price_at_pct(start, end, pct)
recovered_pct = get_price_in_range_pct(start, end, value)
assert pytest.approx(recovered_pct, rel=1e-6) == pct
class TestExtendRangeByPct:
"""Tests for extend_range_by_pct function."""
def test_extend_by_fifty_percent(self):
"""Test extending range by 50%."""
assert extend_range_by_pct(0, 100, 50) == 150.0
def test_extend_by_hundred_percent(self):
"""Test extending range by 100% (doubles the span beyond end)."""
assert extend_range_by_pct(10, 20, 100) == 30.0
def test_extend_by_twenty_percent(self):
"""Test extending range by 20%."""
assert extend_range_by_pct(0, 50, 20) == 60.0
def test_extend_by_zero(self):
"""Test extending by 0% returns original end."""
assert extend_range_by_pct(0, 100, 0) == 100.0
def test_forex_take_profit_extension(self):
"""Test realistic forex TP extension scenario."""
# Extend TP from 1.1100 (opened at 1.1000) by 20%
new_tp = extend_range_by_pct(1.1000, 1.1100, 20)
expected = 1.1100 + (0.0100 * 0.20) # 1.1120
assert pytest.approx(new_tp, rel=1e-6) == expected
def test_small_extension(self):
"""Test small percentage extension."""
result = extend_range_by_pct(100, 200, 10)
assert result == 210.0
class TestGetPriceChangePct:
"""Tests for get_price_change_pct function."""
def test_no_change(self):
"""Test zero change."""
assert get_price_change_pct(50, 50) == 0.0
def test_increase(self):
"""Test positive price change."""
assert get_price_change_pct(100, 150) == 50.0
def test_decrease(self):
"""Test negative price change."""
assert get_price_change_pct(200, 100) == -50.0
def test_double(self):
"""Test 100% increase (doubling)."""
assert get_price_change_pct(100, 200) == 100.0
def test_small_change(self):
"""Test small forex-like price change."""
result = get_price_change_pct(1.1000, 1.1010)
assert pytest.approx(result, abs=0.01) == pytest.approx(0.0909, abs=0.01)
def test_negative_values(self):
"""Test with signed values (e.g. profit going more negative)."""
result = get_price_change_pct(-100, -50)
assert result == -50.0
class TestIncreaseValueByPct:
"""Tests for increase_value_by_pct function."""
def test_increase_by_ten_percent(self):
"""Test 10% increase."""
assert round(increase_value_by_pct(100, 10), 2) == 110.0
def test_increase_by_twenty_percent(self):
"""Test 20% increase."""
assert increase_value_by_pct(50, 20) == 60.0
def test_increase_by_fifty_percent(self):
"""Test 50% increase."""
assert increase_value_by_pct(200, 50) == 300.0
def test_increase_by_zero(self):
"""Test 0% increase returns original value."""
assert increase_value_by_pct(100, 0) == 100.0
def test_increase_by_hundred_percent(self):
"""Test 100% increase doubles the value."""
assert increase_value_by_pct(100, 100) == 200.0
def test_increase_with_decimals(self):
"""Test increase with decimal input."""
result = increase_value_by_pct(1.1000, 5)
assert pytest.approx(result, rel=1e-6) == 1.155
class TestDecreaseValueByPct:
"""Tests for decrease_value_by_pct function."""
def test_decrease_by_ten_percent(self):
"""Test 10% decrease."""
assert decrease_value_by_pct(100, 10) == 90.0
def test_decrease_by_twenty_percent(self):
"""Test 20% decrease."""
assert decrease_value_by_pct(50, 20) == 40.0
def test_decrease_by_fifty_percent(self):
"""Test 50% decrease halves the value."""
assert decrease_value_by_pct(200, 50) == 100.0
def test_decrease_by_zero(self):
"""Test 0% decrease returns original value."""
assert decrease_value_by_pct(100, 0) == 100.0
def test_decrease_by_hundred_percent(self):
"""Test 100% decrease returns zero."""
assert decrease_value_by_pct(100, 100) == 0.0
def test_decrease_with_decimals(self):
"""Test decrease with decimal input."""
result = decrease_value_by_pct(1.1000, 5)
assert pytest.approx(result, rel=1e-6) == 1.045
class TestFunctionInteractions:
"""Tests verifying relationships between price utility functions."""
def test_increase_then_decrease_returns_original(self):
"""Test that increase then decrease by same rate does NOT return original
(this is expected due to compounding)."""
original = 100.0
increased = increase_value_by_pct(original, 10)
result = decrease_value_by_pct(increased, 10)
# 100 * 1.1 * 0.9 = 99.0, NOT 100 (compounding effect)
assert pytest.approx(result, rel=1e-6) == 99.0
def test_extend_range_consistent_with_range_pct(self):
"""Test that extended range position is beyond 100%."""
start, end = 0, 100
extended = extend_range_by_pct(start, end, 50)
pct = get_price_in_range_pct(start, end, extended)
assert pct == 150.0
def test_price_change_consistent_with_increase(self):
"""Test that increase_value_by_pct result matches get_price_change_pct."""
original = 100.0
rate = 25.0
increased = increase_value_by_pct(original, rate)
change = get_price_change_pct(original, increased)
assert pytest.approx(change, rel=1e-6) == rate