use common williams market structure calcs
This commit is contained in:
@@ -19,16 +19,19 @@ import logging
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import json
|
import json
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
# Import core components from main system
|
# Import core components from main system
|
||||||
try:
|
try:
|
||||||
from core.data_provider import DataProvider
|
from core.data_provider import DataProvider
|
||||||
from core.orchestrator import TradingOrchestrator
|
from core.orchestrator import TradingOrchestrator
|
||||||
from core.config import get_config
|
from core.config import get_config
|
||||||
|
from core.williams_market_structure import WilliamsMarketStructure
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
print(f"Warning: Could not import main system components: {e}")
|
print(f"Warning: Could not import main system components: {e}")
|
||||||
print("Running in standalone mode with limited functionality")
|
print("Running in standalone mode with limited functionality")
|
||||||
DataProvider = None
|
DataProvider = None
|
||||||
|
WilliamsMarketStructure = None
|
||||||
TradingOrchestrator = None
|
TradingOrchestrator = None
|
||||||
get_config = lambda: {}
|
get_config = lambda: {}
|
||||||
|
|
||||||
@@ -200,66 +203,90 @@ class AnnotationDashboard:
|
|||||||
|
|
||||||
def _get_pivot_markers_for_timeframe(self, symbol: str, timeframe: str, df: pd.DataFrame) -> dict:
|
def _get_pivot_markers_for_timeframe(self, symbol: str, timeframe: str, df: pd.DataFrame) -> dict:
|
||||||
"""
|
"""
|
||||||
Get pivot markers for a specific timeframe
|
Get pivot markers for a specific timeframe using WilliamsMarketStructure directly
|
||||||
Returns dict with indices mapping to pivot info for that candle
|
Returns dict with all pivot points and identifies which are the last high/low per level
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if not self.data_provider:
|
if WilliamsMarketStructure is None:
|
||||||
|
logger.warning("WilliamsMarketStructure not available")
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
# Get Williams pivot levels for this timeframe
|
if df is None or len(df) < 10:
|
||||||
pivot_levels = self.data_provider.get_williams_pivot_levels(
|
logger.warning(f"Insufficient data for pivot calculation: {len(df) if df is not None else 0} bars")
|
||||||
symbol=symbol,
|
return {}
|
||||||
base_timeframe=timeframe,
|
|
||||||
limit=len(df)
|
# Convert DataFrame to numpy array format expected by Williams Market Structure
|
||||||
|
ohlcv_array = df[['open', 'high', 'low', 'close', 'volume']].copy()
|
||||||
|
|
||||||
|
# Add timestamp as first column (convert to milliseconds)
|
||||||
|
timestamps = df.index.astype(np.int64) // 10**6 # pandas index is ns -> convert to ms
|
||||||
|
ohlcv_array.insert(0, 'timestamp', timestamps)
|
||||||
|
ohlcv_array = ohlcv_array.to_numpy()
|
||||||
|
|
||||||
|
# Initialize Williams Market Structure with default distance
|
||||||
|
# We'll override it in the calculation call
|
||||||
|
williams = WilliamsMarketStructure(min_pivot_distance=1)
|
||||||
|
|
||||||
|
# Calculate recursive pivot points with min_pivot_distance=2
|
||||||
|
# This ensures 5 candles per pivot (tip + 2 prev + 2 next)
|
||||||
|
pivot_levels = williams.calculate_recursive_pivot_points(
|
||||||
|
ohlcv_array,
|
||||||
|
min_pivot_distance=2
|
||||||
)
|
)
|
||||||
|
|
||||||
if not pivot_levels:
|
if not pivot_levels:
|
||||||
|
logger.debug(f"No pivot levels found for {symbol} {timeframe}")
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
# Build a map of timestamp -> pivot info
|
# Build a map of timestamp -> pivot info
|
||||||
|
# Also track last high/low per level for drawing horizontal lines
|
||||||
pivot_map = {}
|
pivot_map = {}
|
||||||
|
last_pivots = {} # {level: {'high': (ts_str, idx), 'low': (ts_str, idx)}}
|
||||||
|
|
||||||
# For each level (1-5), find the last high and last low pivot
|
# For each level (1-5), collect ALL pivot points
|
||||||
for level_num, trend_level in pivot_levels.items():
|
for level_num, trend_level in pivot_levels.items():
|
||||||
if not hasattr(trend_level, 'pivot_points') or not trend_level.pivot_points:
|
if not hasattr(trend_level, 'pivot_points') or not trend_level.pivot_points:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Find last high and last low for this level
|
last_pivots[level_num] = {'high': None, 'low': None}
|
||||||
last_high = None
|
|
||||||
last_low = None
|
|
||||||
|
|
||||||
|
# Add ALL pivot points to the map
|
||||||
for pivot in trend_level.pivot_points:
|
for pivot in trend_level.pivot_points:
|
||||||
|
ts_str = pivot.timestamp.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
if ts_str not in pivot_map:
|
||||||
|
pivot_map[ts_str] = {'highs': [], 'lows': []}
|
||||||
|
|
||||||
|
pivot_info = {
|
||||||
|
'level': level_num,
|
||||||
|
'price': pivot.price,
|
||||||
|
'strength': pivot.strength,
|
||||||
|
'is_last': False # Will be updated below
|
||||||
|
}
|
||||||
|
|
||||||
if pivot.pivot_type == 'high':
|
if pivot.pivot_type == 'high':
|
||||||
last_high = pivot
|
pivot_map[ts_str]['highs'].append(pivot_info)
|
||||||
|
last_pivots[level_num]['high'] = (ts_str, len(pivot_map[ts_str]['highs']) - 1)
|
||||||
elif pivot.pivot_type == 'low':
|
elif pivot.pivot_type == 'low':
|
||||||
last_low = pivot
|
pivot_map[ts_str]['lows'].append(pivot_info)
|
||||||
|
last_pivots[level_num]['low'] = (ts_str, len(pivot_map[ts_str]['lows']) - 1)
|
||||||
# Add to pivot map
|
|
||||||
if last_high:
|
|
||||||
ts_str = last_high.timestamp.strftime('%Y-%m-%d %H:%M:%S')
|
|
||||||
if ts_str not in pivot_map:
|
|
||||||
pivot_map[ts_str] = {'highs': [], 'lows': []}
|
|
||||||
pivot_map[ts_str]['highs'].append({
|
|
||||||
'level': level_num,
|
|
||||||
'price': last_high.price,
|
|
||||||
'strength': last_high.strength
|
|
||||||
})
|
|
||||||
|
|
||||||
if last_low:
|
|
||||||
ts_str = last_low.timestamp.strftime('%Y-%m-%d %H:%M:%S')
|
|
||||||
if ts_str not in pivot_map:
|
|
||||||
pivot_map[ts_str] = {'highs': [], 'lows': []}
|
|
||||||
pivot_map[ts_str]['lows'].append({
|
|
||||||
'level': level_num,
|
|
||||||
'price': last_low.price,
|
|
||||||
'strength': last_low.strength
|
|
||||||
})
|
|
||||||
|
|
||||||
|
# Mark the last high and last low for each level
|
||||||
|
for level_num, last_info in last_pivots.items():
|
||||||
|
if last_info['high']:
|
||||||
|
ts_str, idx = last_info['high']
|
||||||
|
pivot_map[ts_str]['highs'][idx]['is_last'] = True
|
||||||
|
if last_info['low']:
|
||||||
|
ts_str, idx = last_info['low']
|
||||||
|
pivot_map[ts_str]['lows'][idx]['is_last'] = True
|
||||||
|
|
||||||
|
logger.info(f"Found {len(pivot_map)} pivot candles for {symbol} {timeframe} (from {len(df)} candles)")
|
||||||
return pivot_map
|
return pivot_map
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error getting pivot markers for {timeframe}: {e}")
|
logger.error(f"Error getting pivot markers for {timeframe}: {e}")
|
||||||
|
import traceback
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def _setup_routes(self):
|
def _setup_routes(self):
|
||||||
|
|||||||
@@ -149,87 +149,117 @@ class ChartManager {
|
|||||||
// Prepare chart data with pivot bounds
|
// Prepare chart data with pivot bounds
|
||||||
const chartData = [candlestickTrace, volumeTrace];
|
const chartData = [candlestickTrace, volumeTrace];
|
||||||
|
|
||||||
// Add pivot markers from chart data (last high/low for each level L1-L5)
|
// Add pivot markers from chart data
|
||||||
const shapes = [];
|
const shapes = [];
|
||||||
const annotations = [];
|
const annotations = [];
|
||||||
|
const pivotDots = { x: [], y: [], text: [], marker: { color: [], size: [], symbol: [] }, mode: 'markers', hoverinfo: 'text', showlegend: false };
|
||||||
|
|
||||||
if (data.pivot_markers && Object.keys(data.pivot_markers).length > 0) {
|
if (data.pivot_markers && Object.keys(data.pivot_markers).length > 0) {
|
||||||
const xMin = data.timestamps[0];
|
const xMin = data.timestamps[0];
|
||||||
const xMax = data.timestamps[data.timestamps.length - 1];
|
const xMax = data.timestamps[data.timestamps.length - 1];
|
||||||
|
|
||||||
// Process each timestamp that has pivot markers
|
// Process each timestamp that has pivot markers
|
||||||
Object.entries(data.pivot_markers).forEach(([timestamp, pivots]) => {
|
Object.entries(data.pivot_markers).forEach(([timestamp, pivots]) => {
|
||||||
// Draw horizontal lines for last high pivots (resistance)
|
// Process high pivots
|
||||||
if (pivots.highs && pivots.highs.length > 0) {
|
if (pivots.highs && pivots.highs.length > 0) {
|
||||||
pivots.highs.forEach(pivot => {
|
pivots.highs.forEach(pivot => {
|
||||||
const color = this._getPivotColor(pivot.level, 'high');
|
const color = this._getPivotColor(pivot.level, 'high');
|
||||||
shapes.push({
|
|
||||||
type: 'line',
|
// Draw dot on the pivot candle (above the high)
|
||||||
x0: xMin,
|
pivotDots.x.push(timestamp);
|
||||||
y0: pivot.price,
|
pivotDots.y.push(pivot.price);
|
||||||
x1: xMax,
|
pivotDots.text.push(`L${pivot.level} High Pivot<br>Price: $${pivot.price.toFixed(2)}<br>Strength: ${(pivot.strength * 100).toFixed(0)}%`);
|
||||||
y1: pivot.price,
|
pivotDots.marker.color.push(color);
|
||||||
line: {
|
pivotDots.marker.size.push(8);
|
||||||
color: color,
|
pivotDots.marker.symbol.push('triangle-down');
|
||||||
width: 1,
|
|
||||||
dash: 'dash'
|
// Draw horizontal line ONLY for last pivot of this level
|
||||||
},
|
if (pivot.is_last) {
|
||||||
layer: 'below'
|
shapes.push({
|
||||||
});
|
type: 'line',
|
||||||
|
x0: xMin,
|
||||||
// Add label for the level
|
y0: pivot.price,
|
||||||
annotations.push({
|
x1: xMax,
|
||||||
x: xMax,
|
y1: pivot.price,
|
||||||
y: pivot.price,
|
line: {
|
||||||
text: `L${pivot.level}H`,
|
color: color,
|
||||||
showarrow: false,
|
width: 1,
|
||||||
xanchor: 'left',
|
dash: 'dash'
|
||||||
font: {
|
},
|
||||||
size: 9,
|
layer: 'below'
|
||||||
color: color
|
});
|
||||||
},
|
|
||||||
bgcolor: '#1f2937',
|
// Add label for the level
|
||||||
borderpad: 2
|
annotations.push({
|
||||||
});
|
x: xMax,
|
||||||
|
y: pivot.price,
|
||||||
|
text: `L${pivot.level}H`,
|
||||||
|
showarrow: false,
|
||||||
|
xanchor: 'left',
|
||||||
|
font: {
|
||||||
|
size: 9,
|
||||||
|
color: color
|
||||||
|
},
|
||||||
|
bgcolor: '#1f2937',
|
||||||
|
borderpad: 2
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw horizontal lines for last low pivots (support)
|
// Process low pivots
|
||||||
if (pivots.lows && pivots.lows.length > 0) {
|
if (pivots.lows && pivots.lows.length > 0) {
|
||||||
pivots.lows.forEach(pivot => {
|
pivots.lows.forEach(pivot => {
|
||||||
const color = this._getPivotColor(pivot.level, 'low');
|
const color = this._getPivotColor(pivot.level, 'low');
|
||||||
shapes.push({
|
|
||||||
type: 'line',
|
// Draw dot on the pivot candle (below the low)
|
||||||
x0: xMin,
|
pivotDots.x.push(timestamp);
|
||||||
y0: pivot.price,
|
pivotDots.y.push(pivot.price);
|
||||||
x1: xMax,
|
pivotDots.text.push(`L${pivot.level} Low Pivot<br>Price: $${pivot.price.toFixed(2)}<br>Strength: ${(pivot.strength * 100).toFixed(0)}%`);
|
||||||
y1: pivot.price,
|
pivotDots.marker.color.push(color);
|
||||||
line: {
|
pivotDots.marker.size.push(8);
|
||||||
color: color,
|
pivotDots.marker.symbol.push('triangle-up');
|
||||||
width: 1,
|
|
||||||
dash: 'dash'
|
// Draw horizontal line ONLY for last pivot of this level
|
||||||
},
|
if (pivot.is_last) {
|
||||||
layer: 'below'
|
shapes.push({
|
||||||
});
|
type: 'line',
|
||||||
|
x0: xMin,
|
||||||
// Add label for the level
|
y0: pivot.price,
|
||||||
annotations.push({
|
x1: xMax,
|
||||||
x: xMax,
|
y1: pivot.price,
|
||||||
y: pivot.price,
|
line: {
|
||||||
text: `L${pivot.level}L`,
|
color: color,
|
||||||
showarrow: false,
|
width: 1,
|
||||||
xanchor: 'left',
|
dash: 'dash'
|
||||||
font: {
|
},
|
||||||
size: 9,
|
layer: 'below'
|
||||||
color: color
|
});
|
||||||
},
|
|
||||||
bgcolor: '#1f2937',
|
// Add label for the level
|
||||||
borderpad: 2
|
annotations.push({
|
||||||
});
|
x: xMax,
|
||||||
|
y: pivot.price,
|
||||||
|
text: `L${pivot.level}L`,
|
||||||
|
showarrow: false,
|
||||||
|
xanchor: 'left',
|
||||||
|
font: {
|
||||||
|
size: 9,
|
||||||
|
color: color
|
||||||
|
},
|
||||||
|
bgcolor: '#1f2937',
|
||||||
|
borderpad: 2
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add pivot dots trace if we have any
|
||||||
|
if (pivotDots.x.length > 0) {
|
||||||
|
chartData.push(pivotDots);
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`Added ${shapes.length} pivot levels to ${timeframe} chart`);
|
console.log(`Added ${shapes.length} pivot levels to ${timeframe} chart`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -370,85 +400,115 @@ class ChartManager {
|
|||||||
// Add pivot markers from chart data
|
// Add pivot markers from chart data
|
||||||
const shapes = [];
|
const shapes = [];
|
||||||
const annotations = [];
|
const annotations = [];
|
||||||
|
const pivotDots = { x: [], y: [], text: [], marker: { color: [], size: [], symbol: [] }, mode: 'markers', hoverinfo: 'text', showlegend: false };
|
||||||
|
|
||||||
if (data.pivot_markers && Object.keys(data.pivot_markers).length > 0) {
|
if (data.pivot_markers && Object.keys(data.pivot_markers).length > 0) {
|
||||||
const xMin = data.timestamps[0];
|
const xMin = data.timestamps[0];
|
||||||
const xMax = data.timestamps[data.timestamps.length - 1];
|
const xMax = data.timestamps[data.timestamps.length - 1];
|
||||||
|
|
||||||
// Process each timestamp that has pivot markers
|
// Process each timestamp that has pivot markers
|
||||||
Object.entries(data.pivot_markers).forEach(([timestamp, pivots]) => {
|
Object.entries(data.pivot_markers).forEach(([timestamp, pivots]) => {
|
||||||
// Draw horizontal lines for last high pivots
|
// Process high pivots
|
||||||
if (pivots.highs && pivots.highs.length > 0) {
|
if (pivots.highs && pivots.highs.length > 0) {
|
||||||
pivots.highs.forEach(pivot => {
|
pivots.highs.forEach(pivot => {
|
||||||
const color = this._getPivotColor(pivot.level, 'high');
|
const color = this._getPivotColor(pivot.level, 'high');
|
||||||
shapes.push({
|
|
||||||
type: 'line',
|
// Draw dot on the pivot candle
|
||||||
x0: xMin,
|
pivotDots.x.push(timestamp);
|
||||||
y0: pivot.price,
|
pivotDots.y.push(pivot.price);
|
||||||
x1: xMax,
|
pivotDots.text.push(`L${pivot.level} High Pivot<br>Price: $${pivot.price.toFixed(2)}<br>Strength: ${(pivot.strength * 100).toFixed(0)}%`);
|
||||||
y1: pivot.price,
|
pivotDots.marker.color.push(color);
|
||||||
line: {
|
pivotDots.marker.size.push(8);
|
||||||
color: color,
|
pivotDots.marker.symbol.push('triangle-down');
|
||||||
width: 1,
|
|
||||||
dash: 'dash'
|
// Draw horizontal line ONLY for last pivot
|
||||||
},
|
if (pivot.is_last) {
|
||||||
layer: 'below'
|
shapes.push({
|
||||||
});
|
type: 'line',
|
||||||
|
x0: xMin,
|
||||||
annotations.push({
|
y0: pivot.price,
|
||||||
x: xMax,
|
x1: xMax,
|
||||||
y: pivot.price,
|
y1: pivot.price,
|
||||||
text: `L${pivot.level}H`,
|
line: {
|
||||||
showarrow: false,
|
color: color,
|
||||||
xanchor: 'left',
|
width: 1,
|
||||||
font: {
|
dash: 'dash'
|
||||||
size: 9,
|
},
|
||||||
color: color
|
layer: 'below'
|
||||||
},
|
});
|
||||||
bgcolor: '#1f2937',
|
|
||||||
borderpad: 2
|
annotations.push({
|
||||||
});
|
x: xMax,
|
||||||
|
y: pivot.price,
|
||||||
|
text: `L${pivot.level}H`,
|
||||||
|
showarrow: false,
|
||||||
|
xanchor: 'left',
|
||||||
|
font: {
|
||||||
|
size: 9,
|
||||||
|
color: color
|
||||||
|
},
|
||||||
|
bgcolor: '#1f2937',
|
||||||
|
borderpad: 2
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw horizontal lines for last low pivots
|
// Process low pivots
|
||||||
if (pivots.lows && pivots.lows.length > 0) {
|
if (pivots.lows && pivots.lows.length > 0) {
|
||||||
pivots.lows.forEach(pivot => {
|
pivots.lows.forEach(pivot => {
|
||||||
const color = this._getPivotColor(pivot.level, 'low');
|
const color = this._getPivotColor(pivot.level, 'low');
|
||||||
shapes.push({
|
|
||||||
type: 'line',
|
// Draw dot on the pivot candle
|
||||||
x0: xMin,
|
pivotDots.x.push(timestamp);
|
||||||
y0: pivot.price,
|
pivotDots.y.push(pivot.price);
|
||||||
x1: xMax,
|
pivotDots.text.push(`L${pivot.level} Low Pivot<br>Price: $${pivot.price.toFixed(2)}<br>Strength: ${(pivot.strength * 100).toFixed(0)}%`);
|
||||||
y1: pivot.price,
|
pivotDots.marker.color.push(color);
|
||||||
line: {
|
pivotDots.marker.size.push(8);
|
||||||
color: color,
|
pivotDots.marker.symbol.push('triangle-up');
|
||||||
width: 1,
|
|
||||||
dash: 'dash'
|
// Draw horizontal line ONLY for last pivot
|
||||||
},
|
if (pivot.is_last) {
|
||||||
layer: 'below'
|
shapes.push({
|
||||||
});
|
type: 'line',
|
||||||
|
x0: xMin,
|
||||||
annotations.push({
|
y0: pivot.price,
|
||||||
x: xMax,
|
x1: xMax,
|
||||||
y: pivot.price,
|
y1: pivot.price,
|
||||||
text: `L${pivot.level}L`,
|
line: {
|
||||||
showarrow: false,
|
color: color,
|
||||||
xanchor: 'left',
|
width: 1,
|
||||||
font: {
|
dash: 'dash'
|
||||||
size: 9,
|
},
|
||||||
color: color
|
layer: 'below'
|
||||||
},
|
});
|
||||||
bgcolor: '#1f2937',
|
|
||||||
borderpad: 2
|
annotations.push({
|
||||||
});
|
x: xMax,
|
||||||
|
y: pivot.price,
|
||||||
|
text: `L${pivot.level}L`,
|
||||||
|
showarrow: false,
|
||||||
|
xanchor: 'left',
|
||||||
|
font: {
|
||||||
|
size: 9,
|
||||||
|
color: color
|
||||||
|
},
|
||||||
|
bgcolor: '#1f2937',
|
||||||
|
borderpad: 2
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add pivot dots trace if we have any
|
||||||
|
if (pivotDots.x.length > 0) {
|
||||||
|
chartData.push(pivotDots);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use Plotly.react for efficient updates
|
// Use Plotly.react for efficient updates
|
||||||
const update = {
|
const update = {
|
||||||
shapes: shapes,
|
shapes: shapes,
|
||||||
annotations: annotations
|
annotations: annotations
|
||||||
};
|
};
|
||||||
@@ -739,11 +799,11 @@ class ChartManager {
|
|||||||
// Different colors for different levels
|
// Different colors for different levels
|
||||||
const highColors = ['#dc3545', '#ff6b6b', '#ff8787', '#ffa8a8', '#ffc9c9'];
|
const highColors = ['#dc3545', '#ff6b6b', '#ff8787', '#ffa8a8', '#ffc9c9'];
|
||||||
const lowColors = ['#28a745', '#51cf66', '#69db7c', '#8ce99a', '#b2f2bb'];
|
const lowColors = ['#28a745', '#51cf66', '#69db7c', '#8ce99a', '#b2f2bb'];
|
||||||
|
|
||||||
const colors = type === 'high' ? highColors : lowColors;
|
const colors = type === 'high' ? highColors : lowColors;
|
||||||
return colors[Math.min(level - 1, colors.length - 1)];
|
return colors[Math.min(level - 1, colors.length - 1)];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable crosshair cursor
|
* Enable crosshair cursor
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -65,18 +65,22 @@ class WilliamsMarketStructure:
|
|||||||
|
|
||||||
logger.info(f"Williams Market Structure initialized with {self.max_levels} levels")
|
logger.info(f"Williams Market Structure initialized with {self.max_levels} levels")
|
||||||
|
|
||||||
def calculate_recursive_pivot_points(self, ohlcv_data: np.ndarray) -> Dict[int, TrendLevel]:
|
def calculate_recursive_pivot_points(self, ohlcv_data: np.ndarray, min_pivot_distance: int = None) -> Dict[int, TrendLevel]:
|
||||||
"""
|
"""
|
||||||
Calculate recursive pivot points following Williams Market Structure methodology
|
Calculate recursive pivot points following Williams Market Structure methodology
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
ohlcv_data: OHLCV data array with shape (N, 6) [timestamp, O, H, L, C, V]
|
ohlcv_data: OHLCV data array with shape (N, 6) [timestamp, O, H, L, C, V]
|
||||||
|
min_pivot_distance: Override the instance's min_pivot_distance for this calculation (default: None uses instance value)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Dictionary of trend levels with pivot points
|
Dictionary of trend levels with pivot points
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if len(ohlcv_data) < self.min_pivot_distance * 2 + 1:
|
# Use provided min_pivot_distance or fall back to instance default
|
||||||
|
pivot_distance = min_pivot_distance if min_pivot_distance is not None else self.min_pivot_distance
|
||||||
|
|
||||||
|
if len(ohlcv_data) < pivot_distance * 2 + 1:
|
||||||
logger.warning(f"Insufficient data for pivot calculation: {len(ohlcv_data)} bars")
|
logger.warning(f"Insufficient data for pivot calculation: {len(ohlcv_data)} bars")
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@@ -87,6 +91,10 @@ class WilliamsMarketStructure:
|
|||||||
# Initialize pivot levels
|
# Initialize pivot levels
|
||||||
self.pivot_levels = {}
|
self.pivot_levels = {}
|
||||||
|
|
||||||
|
# Temporarily set the pivot distance for this calculation
|
||||||
|
original_distance = self.min_pivot_distance
|
||||||
|
self.min_pivot_distance = pivot_distance
|
||||||
|
|
||||||
# Level 1: Calculate pivot points from raw OHLCV data
|
# Level 1: Calculate pivot points from raw OHLCV data
|
||||||
level_1_pivots = self._calculate_level_1_pivots(df)
|
level_1_pivots = self._calculate_level_1_pivots(df)
|
||||||
if level_1_pivots:
|
if level_1_pivots:
|
||||||
@@ -110,6 +118,9 @@ class WilliamsMarketStructure:
|
|||||||
else:
|
else:
|
||||||
break # No more higher level pivots possible
|
break # No more higher level pivots possible
|
||||||
|
|
||||||
|
# Restore original pivot distance
|
||||||
|
self.min_pivot_distance = original_distance
|
||||||
|
|
||||||
logger.debug(f"Calculated {len(self.pivot_levels)} pivot levels")
|
logger.debug(f"Calculated {len(self.pivot_levels)} pivot levels")
|
||||||
return self.pivot_levels
|
return self.pivot_levels
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user