feat: add algorithm settings backend + integration guide
- Create /api/v1/settings/algorithms endpoint for algorithm management - Enable/disable Algorithm #15 (liquidity_sweep) and #16 (price_action_reversal) - Settings persist in User.preferences JSON column - Settings wired to signal_scoring filter (disabled algos vote 0.0) - Add comprehensive ALGORITHM_INTEGRATION_GUIDE.md documentation - Add unit tests for both algorithms in isolation and together - Vote weights: liquidity_sweep ±2.0, price_action_reversal ±2.5 - Correlation dampening: 0.45 when both vote same direction (pattern group) - Tested: algorithms called in get_indicators(), passed through signal pipeline
This commit is contained in:
@@ -0,0 +1,258 @@
|
||||
"""Test for Algorithm #15 (liquidity_sweep) and Algorithm #16 (price_action_reversal) integration."""
|
||||
|
||||
from app.services.signal_scoring import _compute_adjusted_score
|
||||
|
||||
|
||||
def test_liquidity_sweep_algorithm_voting():
|
||||
"""Test Algorithm #15 — Liquidity Sweep votes correctly."""
|
||||
# Mock candle data near a liquidity level
|
||||
close_price = 100.0
|
||||
|
||||
# Liquidity data: nearest_high at 101.0 (within 1% proximity)
|
||||
liquidity_data = {
|
||||
"liquidity_highs": [101.0, 98.0],
|
||||
"liquidity_lows": [97.0],
|
||||
"nearest_high": 101.0,
|
||||
"nearest_low": 97.0,
|
||||
}
|
||||
|
||||
# Minimal required data for scoring
|
||||
bb = {
|
||||
"upper": [105.0],
|
||||
"middle": [100.0],
|
||||
"lower": [95.0],
|
||||
"upper_1": [102.0],
|
||||
"lower_1": [98.0],
|
||||
}
|
||||
rsi = [50.0]
|
||||
sma = [100.0]
|
||||
|
||||
# Call the scoring function with liquidity data
|
||||
override_sig, override_str, adjusted_score, confidence, raw_scores = _compute_adjusted_score(
|
||||
close_price=close_price,
|
||||
bb=bb,
|
||||
rsi=rsi,
|
||||
sma=sma,
|
||||
macd_data=None,
|
||||
st_data=None,
|
||||
vol_data=None,
|
||||
ichi_data=None,
|
||||
rsi_div=(None, None),
|
||||
macd_div=(None, None),
|
||||
smc_data=None,
|
||||
mtf_votes=[],
|
||||
obv_data=None,
|
||||
stoch_rsi_data=None,
|
||||
mfi_data=None,
|
||||
fvg_data=None,
|
||||
candlestick_score=None,
|
||||
rates=None,
|
||||
enabled_strategies=None,
|
||||
funding_data=None,
|
||||
liquidity_data=liquidity_data,
|
||||
pa_signal=None,
|
||||
)
|
||||
|
||||
# Algorithm #15 should vote +1.0 (price near nearest_high)
|
||||
assert "liquidity_sweep" in raw_scores
|
||||
assert raw_scores["liquidity_sweep"] != 0.0, "Liquidity sweep should vote when price is near level"
|
||||
print(f"✓ Liquidity Sweep vote: {raw_scores['liquidity_sweep']}")
|
||||
|
||||
|
||||
def test_price_action_reversal_algorithm_voting():
|
||||
"""Test Algorithm #16 — Price Action Reversal votes correctly."""
|
||||
close_price = 100.0
|
||||
|
||||
# Price action signal: bullish pin bar at liquidity level
|
||||
pa_signal = {
|
||||
"pattern_type": "PIN_BAR",
|
||||
"direction": "BULLISH",
|
||||
"strength": 1.5,
|
||||
"proximity_to_level": "AT_LEVEL",
|
||||
"price_level": 99.5,
|
||||
}
|
||||
|
||||
# Minimal required data
|
||||
bb = {
|
||||
"upper": [105.0],
|
||||
"middle": [100.0],
|
||||
"lower": [95.0],
|
||||
"upper_1": [102.0],
|
||||
"lower_1": [98.0],
|
||||
}
|
||||
rsi = [50.0]
|
||||
sma = [100.0]
|
||||
|
||||
# Call the scoring function with price action signal
|
||||
override_sig, override_str, adjusted_score, confidence, raw_scores = _compute_adjusted_score(
|
||||
close_price=close_price,
|
||||
bb=bb,
|
||||
rsi=rsi,
|
||||
sma=sma,
|
||||
macd_data=None,
|
||||
st_data=None,
|
||||
vol_data=None,
|
||||
ichi_data=None,
|
||||
rsi_div=(None, None),
|
||||
macd_div=(None, None),
|
||||
smc_data=None,
|
||||
mtf_votes=[],
|
||||
obv_data=None,
|
||||
stoch_rsi_data=None,
|
||||
mfi_data=None,
|
||||
fvg_data=None,
|
||||
candlestick_score=None,
|
||||
rates=None,
|
||||
enabled_strategies=None,
|
||||
funding_data=None,
|
||||
liquidity_data=None,
|
||||
pa_signal=pa_signal,
|
||||
)
|
||||
|
||||
# Algorithm #16 should vote bullish (positive)
|
||||
assert "price_action_reversal" in raw_scores
|
||||
assert raw_scores["price_action_reversal"] > 0.0, "Price action reversal should vote bullish"
|
||||
print(f"✓ Price Action Reversal vote: {raw_scores['price_action_reversal']}")
|
||||
|
||||
|
||||
def test_both_algorithms_together():
|
||||
"""Test both Algorithm #15 and #16 voting together for confluent signals."""
|
||||
close_price = 100.5
|
||||
|
||||
# Both signals present: liquidity level + price action pattern
|
||||
liquidity_data = {
|
||||
"liquidity_highs": [101.0],
|
||||
"liquidity_lows": [99.0],
|
||||
"nearest_high": 101.0,
|
||||
"nearest_low": 99.0,
|
||||
}
|
||||
|
||||
pa_signal = {
|
||||
"pattern_type": "PIN_BAR",
|
||||
"direction": "BULLISH",
|
||||
"strength": 1.5,
|
||||
"proximity_to_level": "AT_LEVEL",
|
||||
"price_level": 101.0,
|
||||
}
|
||||
|
||||
bb = {
|
||||
"upper": [105.0],
|
||||
"middle": [100.0],
|
||||
"lower": [95.0],
|
||||
"upper_1": [102.0],
|
||||
"lower_1": [98.0],
|
||||
}
|
||||
rsi = [45.0] # Neutral RSI
|
||||
sma = [100.0]
|
||||
|
||||
override_sig, override_str, adjusted_score, confidence, raw_scores = _compute_adjusted_score(
|
||||
close_price=close_price,
|
||||
bb=bb,
|
||||
rsi=rsi,
|
||||
sma=sma,
|
||||
macd_data=None,
|
||||
st_data=None,
|
||||
vol_data=None,
|
||||
ichi_data=None,
|
||||
rsi_div=(None, None),
|
||||
macd_div=(None, None),
|
||||
smc_data=None,
|
||||
mtf_votes=[],
|
||||
obv_data=None,
|
||||
stoch_rsi_data=None,
|
||||
mfi_data=None,
|
||||
fvg_data=None,
|
||||
candlestick_score=None,
|
||||
rates=None,
|
||||
enabled_strategies=None,
|
||||
funding_data=None,
|
||||
liquidity_data=liquidity_data,
|
||||
pa_signal=pa_signal,
|
||||
)
|
||||
|
||||
# Both algorithms should vote
|
||||
liquidity_vote = raw_scores.get("liquidity_sweep", 0.0)
|
||||
pa_vote = raw_scores.get("price_action_reversal", 0.0)
|
||||
|
||||
print(f"✓ Combined signals:")
|
||||
print(f" - Liquidity Sweep: {liquidity_vote}")
|
||||
print(f" - Price Action Reversal: {pa_vote}")
|
||||
print(f" - Adjusted Score: {adjusted_score}")
|
||||
print(f" - Confidence: {confidence}")
|
||||
|
||||
assert liquidity_vote != 0.0, "Liquidity sweep should vote"
|
||||
assert pa_vote != 0.0, "Price action should vote"
|
||||
|
||||
|
||||
def test_algorithm_correlation_dampening():
|
||||
"""Test that Algorithm #15 and #16 correlation dampening works.
|
||||
|
||||
Both algorithms are in the 'pattern' group, so when both vote in the
|
||||
same direction, their combined impact should be dampened by correlation
|
||||
weighting to avoid overconfidence.
|
||||
"""
|
||||
close_price = 100.0
|
||||
|
||||
liquidity_data = {
|
||||
"nearest_high": 101.0,
|
||||
"nearest_low": 99.0,
|
||||
}
|
||||
|
||||
pa_signal = {
|
||||
"pattern_type": "PIN_BAR",
|
||||
"direction": "BULLISH",
|
||||
"strength": 2.0,
|
||||
"proximity_to_level": "AT_LEVEL",
|
||||
}
|
||||
|
||||
bb = {
|
||||
"upper": [105.0],
|
||||
"middle": [100.0],
|
||||
"lower": [95.0],
|
||||
"upper_1": [102.0],
|
||||
"lower_1": [98.0],
|
||||
}
|
||||
rsi = [50.0]
|
||||
sma = [100.0]
|
||||
|
||||
# Compute with both algorithms
|
||||
override_sig, override_str, adjusted_score, confidence, raw_scores = _compute_adjusted_score(
|
||||
close_price=close_price,
|
||||
bb=bb,
|
||||
rsi=rsi,
|
||||
sma=sma,
|
||||
macd_data=None,
|
||||
st_data=None,
|
||||
vol_data=None,
|
||||
ichi_data=None,
|
||||
rsi_div=(None, None),
|
||||
macd_div=(None, None),
|
||||
smc_data=None,
|
||||
mtf_votes=[],
|
||||
obv_data=None,
|
||||
stoch_rsi_data=None,
|
||||
mfi_data=None,
|
||||
fvg_data=None,
|
||||
candlestick_score=None,
|
||||
rates=None,
|
||||
enabled_strategies=None,
|
||||
funding_data=None,
|
||||
liquidity_data=liquidity_data,
|
||||
pa_signal=pa_signal,
|
||||
)
|
||||
|
||||
# Sum should be dampened due to correlation
|
||||
combined_raw = (raw_scores.get("liquidity_sweep", 0.0) +
|
||||
raw_scores.get("price_action_reversal", 0.0))
|
||||
print(f"✓ Correlation dampening test:")
|
||||
print(f" - Raw scores sum: {combined_raw}")
|
||||
print(f" - Adjusted score: {adjusted_score}")
|
||||
print(f" - Dampening applied: {adjusted_score < combined_raw}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_liquidity_sweep_algorithm_voting()
|
||||
test_price_action_reversal_algorithm_voting()
|
||||
test_both_algorithms_together()
|
||||
test_algorithm_correlation_dampening()
|
||||
print("\n✅ All algorithm tests passed!")
|
||||
Reference in New Issue
Block a user