75 lines
2.5 KiB
Python
75 lines
2.5 KiB
Python
class CategoricalStrategy:
|
|
def __init__(self, lookback=20, range_threshold=0.4, atr_multiplier=0.5):
|
|
self.lookback = lookback
|
|
self.range_threshold = range_threshold
|
|
self.atr_multiplier = atr_multiplier
|
|
|
|
def get_atr1(self, candle):
|
|
return candle.high - candle.low
|
|
|
|
def classify(self, history):
|
|
if len(history) < self.lookback:
|
|
return None
|
|
|
|
window = history[-self.lookback:]
|
|
highest = max(c.high for c in window)
|
|
lowest = min(c.low for c in window)
|
|
full_range = highest - lowest
|
|
|
|
# Check how much of the range was used early vs late
|
|
first_half = window[:len(window) // 2]
|
|
second_half = window[len(window) // 2:]
|
|
|
|
first_high = max(c.high for c in first_half)
|
|
first_low = min(c.low for c in first_half)
|
|
second_high = max(c.high for c in second_half)
|
|
second_low = min(c.low for c in second_half)
|
|
|
|
# If second half is expanding beyond first half range, it's direction
|
|
expansion = 0
|
|
if second_high > first_high:
|
|
expansion += second_high - first_high
|
|
if second_low < first_low:
|
|
expansion += first_low - second_low
|
|
|
|
avg_candle = sum(self.get_atr1(c) for c in window) / len(window)
|
|
|
|
if expansion > avg_candle * self.range_threshold:
|
|
return "direction"
|
|
return "consolidation"
|
|
|
|
def check_signal(self, history):
|
|
if len(history) < self.lookback + 1:
|
|
return None
|
|
|
|
category = self.classify(history)
|
|
if category is None:
|
|
return None
|
|
|
|
window = history[-self.lookback:]
|
|
highest = max(c.high for c in window)
|
|
lowest = min(c.low for c in window)
|
|
mid = (highest + lowest) / 2
|
|
candle = history[-1]
|
|
prev = history[-2]
|
|
|
|
atr = self.get_atr1(candle)
|
|
bracket = atr * self.atr_multiplier
|
|
|
|
if category == "consolidation":
|
|
# Near top of range and candle turning down: sell
|
|
if candle.close > mid and candle.close < prev.close:
|
|
return "SELL"
|
|
# Near bottom of range and candle turning up: buy
|
|
if candle.close < mid and candle.close > prev.close:
|
|
return "BUY"
|
|
|
|
elif category == "direction":
|
|
# Price pushing up: follow
|
|
if candle.close > prev.close and candle.close > mid:
|
|
return "BUY"
|
|
# Price pushing down: follow
|
|
if candle.close < prev.close and candle.close < mid:
|
|
return "SELL"
|
|
|
|
return None |