mirror of
https://github.com/Ichinga-Samuel/aiomql.git
synced 2026-07-30 21:57:44 +00:00
282 lines
9.5 KiB
Python
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
|