train works
This commit is contained in:
@ -209,11 +209,20 @@ class DataInterface:
|
||||
price_changes = (next_close - curr_close) / curr_close
|
||||
|
||||
# Define thresholds for price movement classification
|
||||
threshold = 0.001 # 0.1% threshold
|
||||
threshold = 0.0005 # 0.05% threshold - smaller to encourage more signals
|
||||
y = np.zeros(len(price_changes), dtype=int)
|
||||
y[price_changes > threshold] = 2 # Up
|
||||
y[price_changes < -threshold] = 0 # Down
|
||||
y[(price_changes >= -threshold) & (price_changes <= threshold)] = 1 # Neutral
|
||||
|
||||
# Log the target distribution to understand our data better
|
||||
sell_count = np.sum(y == 0)
|
||||
hold_count = np.sum(y == 1)
|
||||
buy_count = np.sum(y == 2)
|
||||
total_count = len(y)
|
||||
logger.info(f"Target distribution for {self.symbol} {self.timeframes[0]}: SELL: {sell_count} ({sell_count/total_count:.2%}), " +
|
||||
f"HOLD: {hold_count} ({hold_count/total_count:.2%}), BUY: {buy_count} ({buy_count/total_count:.2%})")
|
||||
|
||||
logger.info(f"Created features - X shape: {X.shape}, y shape: {y.shape}")
|
||||
return X, y, timestamps[window_size:]
|
||||
|
||||
@ -295,73 +304,107 @@ class DataInterface:
|
||||
|
||||
def calculate_pnl(self, predictions, actual_prices, position_size=1.0):
|
||||
"""
|
||||
Calculate PnL and win rates based on predictions and actual price movements.
|
||||
Robust PnL calculator that handles:
|
||||
- Action predictions (0=SELL, 1=HOLD, 2=BUY)
|
||||
- Probability predictions (array of [sell_prob, hold_prob, buy_prob])
|
||||
- Single price array or OHLC data
|
||||
|
||||
Args:
|
||||
predictions: Array of predicted actions (0=SELL, 1=HOLD, 2=BUY) or probabilities
|
||||
actual_prices: Array of actual close prices
|
||||
position_size: Position size for each trade
|
||||
predictions: Array of predicted actions or probabilities
|
||||
actual_prices: Array of actual prices (can be 1D or 2D OHLC format)
|
||||
position_size: Position size multiplier
|
||||
|
||||
Returns:
|
||||
tuple: (pnl, win_rate, trades) where:
|
||||
pnl is the total profit and loss
|
||||
win_rate is the ratio of winning trades
|
||||
trades is a list of trade dictionaries
|
||||
tuple: (total_pnl, win_rate, trades)
|
||||
"""
|
||||
# Ensure we have enough prices for the predictions
|
||||
if len(actual_prices) <= 1:
|
||||
logger.error("Not enough price data for PnL calculation")
|
||||
# Convert inputs to numpy arrays if they aren't already
|
||||
try:
|
||||
predictions = np.array(predictions)
|
||||
actual_prices = np.array(actual_prices)
|
||||
except Exception as e:
|
||||
logger.error(f"Error converting inputs: {str(e)}")
|
||||
return 0.0, 0.0, []
|
||||
|
||||
# Validate input shapes
|
||||
if len(predictions.shape) > 2 or len(actual_prices.shape) > 2:
|
||||
logger.error("Invalid input dimensions")
|
||||
return 0.0, 0.0, []
|
||||
|
||||
# Convert OHLC data to close prices if needed
|
||||
if len(actual_prices.shape) == 2 and actual_prices.shape[1] >= 4:
|
||||
prices = actual_prices[:, 3] # Use close prices
|
||||
else:
|
||||
prices = actual_prices
|
||||
|
||||
# Adjust predictions length to match available price data
|
||||
n_prices = len(actual_prices) - 1 # We need current and next price for each prediction
|
||||
if len(predictions) > n_prices:
|
||||
predictions = predictions[:n_prices]
|
||||
elif len(predictions) < n_prices:
|
||||
n_prices = len(predictions)
|
||||
actual_prices = actual_prices[:n_prices + 1] # +1 to include the next price
|
||||
# Handle case where prices is 2D with single column
|
||||
if len(prices.shape) == 2 and prices.shape[1] == 1:
|
||||
prices = prices.flatten()
|
||||
|
||||
# Convert probabilities to actions if needed
|
||||
if len(predictions.shape) == 2 and predictions.shape[1] > 1:
|
||||
actions = np.argmax(predictions, axis=1)
|
||||
else:
|
||||
actions = predictions
|
||||
|
||||
pnl = 0.0
|
||||
trades = 0
|
||||
wins = 0
|
||||
trade_history = []
|
||||
# Ensure we have enough prices
|
||||
if len(prices) < 2:
|
||||
logger.error("Not enough price data")
|
||||
return 0.0, 0.0, []
|
||||
|
||||
# Trim to matching length
|
||||
min_length = min(len(actions), len(prices)-1)
|
||||
actions = actions[:min_length]
|
||||
prices = prices[:min_length+1]
|
||||
|
||||
for i in range(len(predictions)):
|
||||
pred = predictions[i]
|
||||
current_price = actual_prices[i]
|
||||
next_price = actual_prices[i + 1]
|
||||
|
||||
# Calculate price change percentage
|
||||
pnl = 0.0
|
||||
wins = 0
|
||||
trades = []
|
||||
|
||||
for i in range(min_length):
|
||||
current_price = prices[i]
|
||||
next_price = prices[i+1]
|
||||
action = actions[i]
|
||||
|
||||
# Skip HOLD actions
|
||||
if action == 1:
|
||||
continue
|
||||
|
||||
price_change = (next_price - current_price) / current_price
|
||||
|
||||
# Calculate PnL based on prediction
|
||||
if pred == 2: # Buy
|
||||
if action == 2: # BUY
|
||||
trade_pnl = price_change * position_size
|
||||
trades += 1
|
||||
if trade_pnl > 0:
|
||||
wins += 1
|
||||
trade_history.append({
|
||||
'type': 'buy',
|
||||
'price': current_price,
|
||||
'pnl': trade_pnl,
|
||||
'timestamp': self.dataframes[self.timeframes[0]]['timestamp'].iloc[i] if self.dataframes[self.timeframes[0]] is not None else None
|
||||
})
|
||||
elif pred == 0: # Sell
|
||||
trade_type = 'BUY'
|
||||
is_win = price_change > 0
|
||||
elif action == 0: # SELL
|
||||
trade_pnl = -price_change * position_size
|
||||
trades += 1
|
||||
if trade_pnl > 0:
|
||||
wins += 1
|
||||
trade_history.append({
|
||||
'type': 'sell',
|
||||
'price': current_price,
|
||||
'pnl': trade_pnl,
|
||||
'timestamp': self.dataframes[self.timeframes[0]]['timestamp'].iloc[i] if self.dataframes[self.timeframes[0]] is not None else None
|
||||
})
|
||||
trade_type = 'SELL'
|
||||
is_win = price_change < 0
|
||||
else:
|
||||
continue # Invalid action
|
||||
|
||||
pnl += trade_pnl
|
||||
wins += int(is_win)
|
||||
|
||||
pnl += trade_pnl if pred in [0, 2] else 0
|
||||
|
||||
win_rate = wins / trades if trades > 0 else 0.0
|
||||
return pnl, win_rate, trade_history
|
||||
# Track trade details
|
||||
trades.append({
|
||||
'type': trade_type,
|
||||
'entry': current_price,
|
||||
'exit': next_price,
|
||||
'pnl': trade_pnl,
|
||||
'win': is_win,
|
||||
'duration': 1 # In number of candles
|
||||
})
|
||||
|
||||
win_rate = wins / len(trades) if trades else 0.0
|
||||
|
||||
# Add timestamps to trades if available
|
||||
if hasattr(self, 'dataframes') and self.timeframes and self.timeframes[0] in self.dataframes:
|
||||
df = self.dataframes[self.timeframes[0]]
|
||||
if df is not None and 'timestamp' in df.columns:
|
||||
for i, trade in enumerate(trades[:len(df)]):
|
||||
trade['timestamp'] = df['timestamp'].iloc[i]
|
||||
|
||||
return pnl, win_rate, trades
|
||||
|
||||
def get_future_prices(self, prices, n_candles=3):
|
||||
"""
|
||||
|
391
NN/utils/signal_interpreter.py
Normal file
391
NN/utils/signal_interpreter.py
Normal file
@ -0,0 +1,391 @@
|
||||
"""
|
||||
Signal Interpreter for Neural Network Trading System
|
||||
Converts model predictions into actionable trading signals with enhanced profitability filters
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import logging
|
||||
from collections import deque
|
||||
import time
|
||||
|
||||
logger = logging.getLogger('NN.utils.signal_interpreter')
|
||||
|
||||
class SignalInterpreter:
|
||||
"""
|
||||
Enhanced signal interpreter for short-term high-leverage trading
|
||||
Converts model predictions to trading signals with adaptive filters
|
||||
"""
|
||||
|
||||
def __init__(self, config=None):
|
||||
"""
|
||||
Initialize signal interpreter with configuration parameters
|
||||
|
||||
Args:
|
||||
config (dict): Configuration dictionary with parameters
|
||||
"""
|
||||
self.config = config or {}
|
||||
|
||||
# Signal thresholds - higher thresholds for high-leverage trading
|
||||
self.buy_threshold = self.config.get('buy_threshold', 0.65)
|
||||
self.sell_threshold = self.config.get('sell_threshold', 0.65)
|
||||
self.hold_threshold = self.config.get('hold_threshold', 0.75)
|
||||
|
||||
# Adaptive parameters
|
||||
self.confidence_multiplier = self.config.get('confidence_multiplier', 1.0)
|
||||
self.signal_history = deque(maxlen=20) # Store recent signals for pattern recognition
|
||||
self.price_history = deque(maxlen=20) # Store recent prices for trend analysis
|
||||
|
||||
# Performance tracking
|
||||
self.trade_count = 0
|
||||
self.profitable_trades = 0
|
||||
self.unprofitable_trades = 0
|
||||
self.avg_profit_per_trade = 0
|
||||
self.last_trade_time = None
|
||||
self.last_trade_price = None
|
||||
self.current_position = None # None = no position, 'long' = buy, 'short' = sell
|
||||
|
||||
# Filters for better signal quality
|
||||
self.trend_filter_enabled = self.config.get('trend_filter_enabled', True)
|
||||
self.volume_filter_enabled = self.config.get('volume_filter_enabled', True)
|
||||
self.oscillation_filter_enabled = self.config.get('oscillation_filter_enabled', True)
|
||||
|
||||
# Sensitivity parameters
|
||||
self.min_price_movement = self.config.get('min_price_movement', 0.0005) # 0.05% minimum expected movement
|
||||
self.hold_cooldown = self.config.get('hold_cooldown', 3) # Minimum periods to wait after a HOLD
|
||||
self.consecutive_signals_required = self.config.get('consecutive_signals_required', 2)
|
||||
|
||||
# State tracking
|
||||
self.consecutive_buy_signals = 0
|
||||
self.consecutive_sell_signals = 0
|
||||
self.consecutive_hold_signals = 0
|
||||
self.periods_since_last_trade = 0
|
||||
|
||||
logger.info("Signal interpreter initialized with enhanced filters for short-term trading")
|
||||
|
||||
def interpret_signal(self, action_probs, price_prediction=None, market_data=None):
|
||||
"""
|
||||
Interpret model predictions to generate trading signal
|
||||
|
||||
Args:
|
||||
action_probs (ndarray): Model action probabilities [SELL, HOLD, BUY]
|
||||
price_prediction (float): Predicted price change (optional)
|
||||
market_data (dict): Additional market data for filtering (optional)
|
||||
|
||||
Returns:
|
||||
dict: Trading signal with action and metadata
|
||||
"""
|
||||
# Extract probabilities
|
||||
sell_prob, hold_prob, buy_prob = action_probs
|
||||
|
||||
# Apply confidence multiplier - amplifies the signal when model is confident
|
||||
adjusted_buy_prob = min(buy_prob * self.confidence_multiplier, 1.0)
|
||||
adjusted_sell_prob = min(sell_prob * self.confidence_multiplier, 1.0)
|
||||
|
||||
# Incorporate price prediction if available
|
||||
if price_prediction is not None:
|
||||
# Strengthen buy signal if price is predicted to rise
|
||||
if price_prediction > self.min_price_movement:
|
||||
adjusted_buy_prob *= (1.0 + price_prediction * 5)
|
||||
adjusted_sell_prob *= (1.0 - price_prediction * 2)
|
||||
# Strengthen sell signal if price is predicted to fall
|
||||
elif price_prediction < -self.min_price_movement:
|
||||
adjusted_sell_prob *= (1.0 + abs(price_prediction) * 5)
|
||||
adjusted_buy_prob *= (1.0 - abs(price_prediction) * 2)
|
||||
|
||||
# Track consecutive signals to reduce false signals
|
||||
raw_signal = self._get_raw_signal(adjusted_buy_prob, adjusted_sell_prob, hold_prob)
|
||||
|
||||
# Update consecutive signal counters
|
||||
if raw_signal == 'BUY':
|
||||
self.consecutive_buy_signals += 1
|
||||
self.consecutive_sell_signals = 0
|
||||
self.consecutive_hold_signals = 0
|
||||
elif raw_signal == 'SELL':
|
||||
self.consecutive_buy_signals = 0
|
||||
self.consecutive_sell_signals += 1
|
||||
self.consecutive_hold_signals = 0
|
||||
else: # HOLD
|
||||
self.consecutive_buy_signals = 0
|
||||
self.consecutive_sell_signals = 0
|
||||
self.consecutive_hold_signals += 1
|
||||
|
||||
# Apply trend filter if enabled and market data available
|
||||
if self.trend_filter_enabled and market_data and 'trend' in market_data:
|
||||
raw_signal = self._apply_trend_filter(raw_signal, market_data['trend'])
|
||||
|
||||
# Apply volume filter if enabled and market data available
|
||||
if self.volume_filter_enabled and market_data and 'volume' in market_data:
|
||||
raw_signal = self._apply_volume_filter(raw_signal, market_data['volume'])
|
||||
|
||||
# Apply oscillation filter to prevent excessive trading
|
||||
if self.oscillation_filter_enabled:
|
||||
raw_signal = self._apply_oscillation_filter(raw_signal)
|
||||
|
||||
# Create final signal with confidence metrics and metadata
|
||||
signal = {
|
||||
'action': raw_signal,
|
||||
'timestamp': time.time(),
|
||||
'confidence': self._calculate_confidence(adjusted_buy_prob, adjusted_sell_prob, hold_prob),
|
||||
'price_prediction': price_prediction if price_prediction is not None else 0.0,
|
||||
'consecutive_signals': max(self.consecutive_buy_signals, self.consecutive_sell_signals),
|
||||
'periods_since_last_trade': self.periods_since_last_trade
|
||||
}
|
||||
|
||||
# Update signal history
|
||||
self.signal_history.append(signal)
|
||||
self.periods_since_last_trade += 1
|
||||
|
||||
# Track trade if action taken
|
||||
if signal['action'] in ['BUY', 'SELL']:
|
||||
self._track_trade(signal, market_data)
|
||||
|
||||
return signal
|
||||
|
||||
def _get_raw_signal(self, buy_prob, sell_prob, hold_prob):
|
||||
"""
|
||||
Get raw signal based on adjusted probabilities
|
||||
|
||||
Args:
|
||||
buy_prob (float): Buy probability
|
||||
sell_prob (float): Sell probability
|
||||
hold_prob (float): Hold probability
|
||||
|
||||
Returns:
|
||||
str: Raw signal ('BUY', 'SELL', or 'HOLD')
|
||||
"""
|
||||
# Require higher consecutive signals for high-leverage actions
|
||||
if buy_prob > self.buy_threshold and self.consecutive_buy_signals >= self.consecutive_signals_required:
|
||||
return 'BUY'
|
||||
elif sell_prob > self.sell_threshold and self.consecutive_sell_signals >= self.consecutive_signals_required:
|
||||
return 'SELL'
|
||||
elif hold_prob > self.hold_threshold:
|
||||
return 'HOLD'
|
||||
elif buy_prob > sell_prob:
|
||||
# If close to threshold but not quite there, still prefer action over hold
|
||||
if buy_prob > self.buy_threshold * 0.8:
|
||||
return 'BUY'
|
||||
else:
|
||||
return 'HOLD'
|
||||
elif sell_prob > buy_prob:
|
||||
# If close to threshold but not quite there, still prefer action over hold
|
||||
if sell_prob > self.sell_threshold * 0.8:
|
||||
return 'SELL'
|
||||
else:
|
||||
return 'HOLD'
|
||||
else:
|
||||
return 'HOLD'
|
||||
|
||||
def _apply_trend_filter(self, raw_signal, trend):
|
||||
"""
|
||||
Apply trend filter to align signals with overall market trend
|
||||
|
||||
Args:
|
||||
raw_signal (str): Raw signal
|
||||
trend (str or float): Market trend indicator
|
||||
|
||||
Returns:
|
||||
str: Filtered signal
|
||||
"""
|
||||
# Skip if fresh signal doesn't match trend
|
||||
if isinstance(trend, str):
|
||||
if raw_signal == 'BUY' and trend == 'downtrend':
|
||||
return 'HOLD'
|
||||
elif raw_signal == 'SELL' and trend == 'uptrend':
|
||||
return 'HOLD'
|
||||
elif isinstance(trend, (int, float)):
|
||||
# Trend as numerical value (positive = uptrend, negative = downtrend)
|
||||
if raw_signal == 'BUY' and trend < -0.2:
|
||||
return 'HOLD'
|
||||
elif raw_signal == 'SELL' and trend > 0.2:
|
||||
return 'HOLD'
|
||||
|
||||
return raw_signal
|
||||
|
||||
def _apply_volume_filter(self, raw_signal, volume):
|
||||
"""
|
||||
Apply volume filter to ensure sufficient liquidity for trade
|
||||
|
||||
Args:
|
||||
raw_signal (str): Raw signal
|
||||
volume (dict): Volume data
|
||||
|
||||
Returns:
|
||||
str: Filtered signal
|
||||
"""
|
||||
# Skip trading when volume is too low
|
||||
if volume.get('is_low', False) and raw_signal in ['BUY', 'SELL']:
|
||||
return 'HOLD'
|
||||
|
||||
# Reduce sensitivity during volume spikes to avoid getting caught in volatility
|
||||
if volume.get('is_spike', False):
|
||||
# For short-term trading, a spike could be an opportunity if it confirms our signal
|
||||
if volume.get('direction', 0) > 0 and raw_signal == 'BUY':
|
||||
# Volume spike in buy direction - strengthen buy signal
|
||||
return raw_signal
|
||||
elif volume.get('direction', 0) < 0 and raw_signal == 'SELL':
|
||||
# Volume spike in sell direction - strengthen sell signal
|
||||
return raw_signal
|
||||
else:
|
||||
# Volume spike against our signal - be cautious
|
||||
return 'HOLD'
|
||||
|
||||
return raw_signal
|
||||
|
||||
def _apply_oscillation_filter(self, raw_signal):
|
||||
"""
|
||||
Apply oscillation filter to prevent excessive trading
|
||||
|
||||
Returns:
|
||||
str: Filtered signal
|
||||
"""
|
||||
# Implement a cooldown period after HOLD signals
|
||||
if self.consecutive_hold_signals < self.hold_cooldown:
|
||||
# Check if we're switching positions too quickly
|
||||
if len(self.signal_history) >= 2:
|
||||
last_action = self.signal_history[-1]['action']
|
||||
if last_action in ['BUY', 'SELL'] and raw_signal != last_action and raw_signal != 'HOLD':
|
||||
# We're trying to reverse position immediately after taking one
|
||||
# For high-leverage trading, this could be allowed if signal is very strong
|
||||
if raw_signal == 'BUY' and self.consecutive_buy_signals >= self.consecutive_signals_required * 1.5:
|
||||
# Extra strong buy signal - allow reversal
|
||||
return raw_signal
|
||||
elif raw_signal == 'SELL' and self.consecutive_sell_signals >= self.consecutive_signals_required * 1.5:
|
||||
# Extra strong sell signal - allow reversal
|
||||
return raw_signal
|
||||
else:
|
||||
# Not strong enough to justify immediate reversal
|
||||
return 'HOLD'
|
||||
|
||||
# Check for oscillation patterns over time
|
||||
if len(self.signal_history) >= 4:
|
||||
# Look for alternating BUY/SELL pattern which indicates indecision
|
||||
actions = [s['action'] for s in list(self.signal_history)[-4:]]
|
||||
if actions.count('BUY') >= 2 and actions.count('SELL') >= 2:
|
||||
# Oscillating pattern detected, force a HOLD
|
||||
return 'HOLD'
|
||||
|
||||
return raw_signal
|
||||
|
||||
def _calculate_confidence(self, buy_prob, sell_prob, hold_prob):
|
||||
"""
|
||||
Calculate confidence score for the signal
|
||||
|
||||
Args:
|
||||
buy_prob (float): Buy probability
|
||||
sell_prob (float): Sell probability
|
||||
hold_prob (float): Hold probability
|
||||
|
||||
Returns:
|
||||
float: Confidence score (0.0-1.0)
|
||||
"""
|
||||
# Maximum probability indicates confidence level
|
||||
max_prob = max(buy_prob, sell_prob, hold_prob)
|
||||
|
||||
# Calculate the gap between highest and second highest probability
|
||||
sorted_probs = sorted([buy_prob, sell_prob, hold_prob], reverse=True)
|
||||
prob_gap = sorted_probs[0] - sorted_probs[1]
|
||||
|
||||
# Combine both factors - higher max and larger gap mean more confidence
|
||||
confidence = (max_prob * 0.7) + (prob_gap * 0.3)
|
||||
|
||||
# Scale to ensure output is between 0 and 1
|
||||
return min(max(confidence, 0.0), 1.0)
|
||||
|
||||
def _track_trade(self, signal, market_data):
|
||||
"""
|
||||
Track trade for performance monitoring
|
||||
|
||||
Args:
|
||||
signal (dict): Trading signal
|
||||
market_data (dict): Market data including price
|
||||
"""
|
||||
self.trade_count += 1
|
||||
self.periods_since_last_trade = 0
|
||||
|
||||
# Update position state
|
||||
if signal['action'] == 'BUY':
|
||||
self.current_position = 'long'
|
||||
elif signal['action'] == 'SELL':
|
||||
self.current_position = 'short'
|
||||
|
||||
# Store trade time and price if available
|
||||
current_time = time.time()
|
||||
current_price = market_data.get('price', None) if market_data else None
|
||||
|
||||
# Record profitability if we have both current and previous trade data
|
||||
if self.last_trade_time and self.last_trade_price and current_price:
|
||||
# Calculate holding period
|
||||
holding_period = current_time - self.last_trade_time
|
||||
|
||||
# Calculate profit/loss based on position
|
||||
if self.current_position == 'long' and signal['action'] == 'SELL':
|
||||
# Closing a long position
|
||||
profit_pct = (current_price - self.last_trade_price) / self.last_trade_price
|
||||
|
||||
# Update trade statistics
|
||||
if profit_pct > 0:
|
||||
self.profitable_trades += 1
|
||||
else:
|
||||
self.unprofitable_trades += 1
|
||||
|
||||
# Update average profit
|
||||
total_trades = self.profitable_trades + self.unprofitable_trades
|
||||
self.avg_profit_per_trade = ((self.avg_profit_per_trade * (total_trades - 1)) + profit_pct) / total_trades
|
||||
|
||||
logger.info(f"Closed LONG position with {profit_pct:.4%} profit after {holding_period:.1f}s")
|
||||
|
||||
elif self.current_position == 'short' and signal['action'] == 'BUY':
|
||||
# Closing a short position
|
||||
profit_pct = (self.last_trade_price - current_price) / self.last_trade_price
|
||||
|
||||
# Update trade statistics
|
||||
if profit_pct > 0:
|
||||
self.profitable_trades += 1
|
||||
else:
|
||||
self.unprofitable_trades += 1
|
||||
|
||||
# Update average profit
|
||||
total_trades = self.profitable_trades + self.unprofitable_trades
|
||||
self.avg_profit_per_trade = ((self.avg_profit_per_trade * (total_trades - 1)) + profit_pct) / total_trades
|
||||
|
||||
logger.info(f"Closed SHORT position with {profit_pct:.4%} profit after {holding_period:.1f}s")
|
||||
|
||||
# Update last trade info
|
||||
self.last_trade_time = current_time
|
||||
self.last_trade_price = current_price
|
||||
|
||||
def get_performance_stats(self):
|
||||
"""
|
||||
Get trading performance statistics
|
||||
|
||||
Returns:
|
||||
dict: Performance statistics
|
||||
"""
|
||||
total_trades = self.profitable_trades + self.unprofitable_trades
|
||||
win_rate = self.profitable_trades / total_trades if total_trades > 0 else 0
|
||||
|
||||
return {
|
||||
'total_trades': self.trade_count,
|
||||
'profitable_trades': self.profitable_trades,
|
||||
'unprofitable_trades': self.unprofitable_trades,
|
||||
'win_rate': win_rate,
|
||||
'avg_profit_per_trade': self.avg_profit_per_trade
|
||||
}
|
||||
|
||||
def reset(self):
|
||||
"""Reset all trading statistics and state"""
|
||||
self.signal_history.clear()
|
||||
self.price_history.clear()
|
||||
self.trade_count = 0
|
||||
self.profitable_trades = 0
|
||||
self.unprofitable_trades = 0
|
||||
self.avg_profit_per_trade = 0
|
||||
self.last_trade_time = None
|
||||
self.last_trade_price = None
|
||||
self.current_position = None
|
||||
self.consecutive_buy_signals = 0
|
||||
self.consecutive_sell_signals = 0
|
||||
self.consecutive_hold_signals = 0
|
||||
self.periods_since_last_trade = 0
|
||||
|
||||
logger.info("Signal interpreter reset")
|
Reference in New Issue
Block a user