|
| 1 | +import pandas as pd |
| 2 | +import numpy as np |
| 3 | +import pytest |
| 4 | +from indicators import add_moving_average, add_52w_high_low, calculate_rsi |
| 5 | + |
| 6 | +@pytest.fixture |
| 7 | +def sample_price_df(): |
| 8 | + # Create a simple DataFrame for testing |
| 9 | + dates = pd.date_range(start='2023-01-01', periods=100) |
| 10 | + data = { |
| 11 | + 'AAPL': np.arange(100, 200), # Linear increase |
| 12 | + 'GOOG': np.random.rand(100) * 100 |
| 13 | + } |
| 14 | + return pd.DataFrame(data, index=dates) |
| 15 | + |
| 16 | +def test_add_moving_average(sample_price_df): |
| 17 | + df = sample_price_df.copy() |
| 18 | + df = add_moving_average(df, ma_periods=[10, 20]) |
| 19 | + |
| 20 | + assert 'AAPL_MA10' in df.columns |
| 21 | + assert 'AAPL_MA20' in df.columns |
| 22 | + assert 'GOOG_MA10' in df.columns |
| 23 | + |
| 24 | + # Check simple calculation for linear increase (avg of last 10 of 100..109 is roughly 104.5) |
| 25 | + # The first 9 values should be NaN if min_periods wasn't 1, but logic uses min_periods=1 |
| 26 | + assert not df['AAPL_MA10'].isnull().all() |
| 27 | + |
| 28 | + # Verify values for a known sequence |
| 29 | + # AAPL is 100, 101, 102... |
| 30 | + # At index 0 (value 100), MA10 (min_periods=1) should be 100 |
| 31 | + assert df['AAPL_MA10'].iloc[0] == 100.0 |
| 32 | + |
| 33 | + # At index 9 (value 109), MA10 is avg(100...109) = 104.5 |
| 34 | + assert df['AAPL_MA10'].iloc[9] == 104.5 |
| 35 | + |
| 36 | +def test_add_52w_high_low(sample_price_df): |
| 37 | + df = sample_price_df.copy() |
| 38 | + # Ensure enough data or check min_periods behavior |
| 39 | + # Code uses min_periods=1 |
| 40 | + df = add_52w_high_low(df) |
| 41 | + |
| 42 | + assert 'AAPL_52w_high' in df.columns |
| 43 | + assert 'AAPL_52w_low' in df.columns |
| 44 | + |
| 45 | + # For monotonically increasing AAPL (100 to 199) |
| 46 | + # The 52w high at the last point should be the current price |
| 47 | + assert df['AAPL_52w_high'].iloc[-1] == 199 |
| 48 | + # The 52w low at the last point should be the price 52 weeks ago (or start of data) |
| 49 | + # Since we only have 100 days, it's the min of the whole window so far, which is 100 |
| 50 | + assert df['AAPL_52w_low'].iloc[-1] == 100 |
| 51 | + |
| 52 | +def test_calculate_rsi(sample_price_df): |
| 53 | + df = sample_price_df.copy() |
| 54 | + rsi_df = calculate_rsi(df) |
| 55 | + |
| 56 | + assert 'AAPL' in rsi_df.columns |
| 57 | + assert 'GOOG' in rsi_df.columns |
| 58 | + |
| 59 | + # RSI should be between 0 and 100 |
| 60 | + assert rsi_df.max().max() <= 100 |
| 61 | + assert rsi_df.min().min() >= 0 |
| 62 | + |
| 63 | + # For a constantly increasing stock (AAPL), RSI should be high (near 100) |
| 64 | + # Use a slice to avoid initial warmup period |
| 65 | + assert rsi_df['AAPL'].iloc[-1] > 70 |
0 commit comments