misc
This commit is contained in:
@ -103,7 +103,12 @@ class WilliamsMarketStructure:
|
||||
|
||||
def calculate_recursive_pivot_points(self, ohlcv_data: np.ndarray) -> Dict[str, MarketStructureLevel]:
|
||||
"""
|
||||
Calculate 5 levels of recursive pivot points
|
||||
Calculate 5 levels of recursive pivot points using TRUE recursion
|
||||
|
||||
Level 1: Calculated from 1s OHLCV data
|
||||
Level 2: Calculated from Level 1 pivot points treated as individual price bars
|
||||
Level 3: Calculated from Level 2 pivot points treated as individual price bars
|
||||
etc.
|
||||
|
||||
Args:
|
||||
ohlcv_data: OHLCV data array with columns [timestamp, open, high, low, close, volume]
|
||||
@ -116,13 +121,18 @@ class WilliamsMarketStructure:
|
||||
return self._create_empty_structure()
|
||||
|
||||
levels = {}
|
||||
current_data = ohlcv_data.copy()
|
||||
current_price_points = ohlcv_data.copy() # Start with raw price data
|
||||
|
||||
for level in range(self.max_levels):
|
||||
logger.debug(f"Analyzing level {level} with {len(current_data)} data points")
|
||||
logger.debug(f"Analyzing level {level} with {len(current_price_points)} data points")
|
||||
|
||||
# Find swing points for this level
|
||||
swing_points = self._find_swing_points_multi_strength(current_data)
|
||||
if level == 0:
|
||||
# Level 0 (Level 1): Calculate from raw OHLCV data
|
||||
swing_points = self._find_swing_points_multi_strength(current_price_points)
|
||||
else:
|
||||
# Level 1+ (Level 2+): Calculate from previous level's pivot points
|
||||
# Treat pivot points as individual price bars
|
||||
swing_points = self._find_pivot_points_from_pivot_points(current_price_points, level)
|
||||
|
||||
if len(swing_points) < self.min_swings_for_trend:
|
||||
logger.debug(f"Not enough swings at level {level}: {len(swing_points)}")
|
||||
@ -136,14 +146,14 @@ class WilliamsMarketStructure:
|
||||
|
||||
# Find support/resistance levels
|
||||
support_levels, resistance_levels = self._find_support_resistance(
|
||||
swing_points, current_data
|
||||
swing_points, current_price_points if level == 0 else None
|
||||
)
|
||||
|
||||
# Determine current market bias
|
||||
current_bias = self._determine_market_bias(swing_points, trend_analysis)
|
||||
|
||||
# Detect structure breaks
|
||||
structure_breaks = self._detect_structure_breaks(swing_points, current_data)
|
||||
structure_breaks = self._detect_structure_breaks(swing_points, current_price_points if level == 0 else None)
|
||||
|
||||
# Create level data
|
||||
levels[f'level_{level}'] = MarketStructureLevel(
|
||||
@ -156,11 +166,11 @@ class WilliamsMarketStructure:
|
||||
structure_breaks=structure_breaks
|
||||
)
|
||||
|
||||
# Prepare data for next level (use swing points as input)
|
||||
# Prepare data for next level: convert swing points to "price points"
|
||||
if len(swing_points) >= 5:
|
||||
current_data = self._convert_swings_to_ohlcv(swing_points)
|
||||
if len(current_data) < 10:
|
||||
logger.debug(f"Insufficient converted data for level {level + 1}")
|
||||
current_price_points = self._convert_pivots_to_price_points(swing_points)
|
||||
if len(current_price_points) < 10:
|
||||
logger.debug(f"Insufficient pivot data for level {level + 1}")
|
||||
break
|
||||
else:
|
||||
logger.debug(f"Not enough swings to continue to level {level + 1}")
|
||||
@ -490,41 +500,89 @@ class WilliamsMarketStructure:
|
||||
|
||||
return structure_breaks
|
||||
|
||||
def _convert_swings_to_ohlcv(self, swing_points: List[SwingPoint]) -> np.ndarray:
|
||||
"""Convert swing points to OHLCV format for next level analysis"""
|
||||
def _find_pivot_points_from_pivot_points(self, pivot_array: np.ndarray, level: int) -> List[SwingPoint]:
|
||||
"""
|
||||
Find pivot points from previous level's pivot points
|
||||
|
||||
For Level 2+: A Level N low pivot is when a Level N-1 pivot low is surrounded
|
||||
by higher Level N-1 pivot lows (and vice versa for highs)
|
||||
|
||||
Args:
|
||||
pivot_array: Array of pivot points as [timestamp, price, price, price, price, 0] format
|
||||
level: Current level being calculated
|
||||
"""
|
||||
swings = []
|
||||
|
||||
if len(pivot_array) < 5:
|
||||
return swings
|
||||
|
||||
# Use configurable strength for higher levels (more conservative)
|
||||
strength = min(2 + level, 5) # Level 1: 3 bars, Level 2: 4 bars, Level 3+: 5 bars
|
||||
|
||||
for i in range(strength, len(pivot_array) - strength):
|
||||
current_price = pivot_array[i, 1] # Use the price from pivot point
|
||||
current_timestamp = pivot_array[i, 0]
|
||||
|
||||
# Check for swing high (pivot high surrounded by lower pivot highs)
|
||||
is_swing_high = True
|
||||
for j in range(i - strength, i + strength + 1):
|
||||
if j != i and pivot_array[j, 1] >= current_price:
|
||||
is_swing_high = False
|
||||
break
|
||||
|
||||
if is_swing_high:
|
||||
swings.append(SwingPoint(
|
||||
timestamp=datetime.fromtimestamp(current_timestamp) if current_timestamp > 1e9 else datetime.now(),
|
||||
price=current_price,
|
||||
index=i,
|
||||
swing_type=SwingType.SWING_HIGH,
|
||||
strength=strength,
|
||||
volume=0.0 # Pivot points don't have volume
|
||||
))
|
||||
|
||||
# Check for swing low (pivot low surrounded by higher pivot lows)
|
||||
is_swing_low = True
|
||||
for j in range(i - strength, i + strength + 1):
|
||||
if j != i and pivot_array[j, 1] <= current_price:
|
||||
is_swing_low = False
|
||||
break
|
||||
|
||||
if is_swing_low:
|
||||
swings.append(SwingPoint(
|
||||
timestamp=datetime.fromtimestamp(current_timestamp) if current_timestamp > 1e9 else datetime.now(),
|
||||
price=current_price,
|
||||
index=i,
|
||||
swing_type=SwingType.SWING_LOW,
|
||||
strength=strength,
|
||||
volume=0.0 # Pivot points don't have volume
|
||||
))
|
||||
|
||||
return swings
|
||||
|
||||
def _convert_pivots_to_price_points(self, swing_points: List[SwingPoint]) -> np.ndarray:
|
||||
"""
|
||||
Convert swing points to price point array for next level calculation
|
||||
|
||||
Each swing point becomes a "price bar" where OHLC = pivot price
|
||||
This allows the next level to treat pivot points as individual price data
|
||||
"""
|
||||
if len(swing_points) < 2:
|
||||
return np.array([])
|
||||
|
||||
ohlcv_data = []
|
||||
price_points = []
|
||||
|
||||
for i in range(len(swing_points) - 1):
|
||||
current_swing = swing_points[i]
|
||||
next_swing = swing_points[i + 1]
|
||||
|
||||
# Create synthetic OHLCV bar from swing to swing
|
||||
if current_swing.swing_type == SwingType.SWING_HIGH:
|
||||
# From high to next point
|
||||
open_price = current_swing.price
|
||||
high_price = current_swing.price
|
||||
low_price = min(current_swing.price, next_swing.price)
|
||||
close_price = next_swing.price
|
||||
else:
|
||||
# From low to next point
|
||||
open_price = current_swing.price
|
||||
high_price = max(current_swing.price, next_swing.price)
|
||||
low_price = current_swing.price
|
||||
close_price = next_swing.price
|
||||
|
||||
ohlcv_data.append([
|
||||
current_swing.timestamp.timestamp(),
|
||||
open_price,
|
||||
high_price,
|
||||
low_price,
|
||||
close_price,
|
||||
current_swing.volume
|
||||
for swing in swing_points:
|
||||
# Each pivot point becomes a price point where OHLC = pivot price
|
||||
price_points.append([
|
||||
swing.timestamp.timestamp(),
|
||||
swing.price, # Open = pivot price
|
||||
swing.price, # High = pivot price
|
||||
swing.price, # Low = pivot price
|
||||
swing.price, # Close = pivot price
|
||||
0.0 # Volume = 0 (not applicable for pivot points)
|
||||
])
|
||||
|
||||
return np.array(ohlcv_data)
|
||||
return np.array(price_points)
|
||||
|
||||
def _create_empty_structure(self) -> Dict[str, MarketStructureLevel]:
|
||||
"""Create empty structure when insufficient data"""
|
||||
|
Reference in New Issue
Block a user