Implement Incremental Trading Framework
- Introduced a comprehensive framework for incremental trading strategies, including modules for strategy execution, backtesting, and data processing. - Added key components such as `IncTrader`, `IncBacktester`, and various trading strategies (e.g., `MetaTrendStrategy`, `BBRSStrategy`, `RandomStrategy`) to facilitate real-time trading and backtesting. - Implemented a robust backtesting framework with configuration management, parallel execution, and result analysis capabilities. - Developed an incremental indicators framework to support real-time data processing with constant memory usage. - Enhanced documentation to provide clear usage examples and architecture overview, ensuring maintainability and ease of understanding for future development. - Ensured compatibility with existing strategies and maintained a focus on performance and scalability throughout the implementation.
This commit is contained in:
35
IncrementalTrader/trader/__init__.py
Normal file
35
IncrementalTrader/trader/__init__.py
Normal file
@@ -0,0 +1,35 @@
|
||||
"""
|
||||
Incremental Trading Execution
|
||||
|
||||
This module provides trading execution and position management for incremental strategies.
|
||||
It handles real-time trade execution, risk management, and performance tracking.
|
||||
|
||||
Components:
|
||||
- IncTrader: Main trader class for strategy execution
|
||||
- PositionManager: Position state and trade execution management
|
||||
- TradeRecord: Data structure for completed trades
|
||||
- MarketFees: Fee calculation utilities
|
||||
|
||||
Example:
|
||||
from IncrementalTrader.trader import IncTrader, PositionManager
|
||||
from IncrementalTrader.strategies import MetaTrendStrategy
|
||||
|
||||
strategy = MetaTrendStrategy("metatrend")
|
||||
trader = IncTrader(strategy, initial_usd=10000)
|
||||
|
||||
# Process data stream
|
||||
for timestamp, ohlcv in data_stream:
|
||||
trader.process_data_point(timestamp, ohlcv)
|
||||
|
||||
results = trader.get_results()
|
||||
"""
|
||||
|
||||
from .trader import IncTrader
|
||||
from .position import PositionManager, TradeRecord, MarketFees
|
||||
|
||||
__all__ = [
|
||||
"IncTrader",
|
||||
"PositionManager",
|
||||
"TradeRecord",
|
||||
"MarketFees",
|
||||
]
|
||||
301
IncrementalTrader/trader/position.py
Normal file
301
IncrementalTrader/trader/position.py
Normal file
@@ -0,0 +1,301 @@
|
||||
"""
|
||||
Position Management for Incremental Trading
|
||||
|
||||
This module handles position state, balance tracking, and trade calculations
|
||||
for the incremental trading system.
|
||||
"""
|
||||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from typing import Dict, Optional, List, Any
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class TradeRecord:
|
||||
"""Record of a completed trade."""
|
||||
entry_time: pd.Timestamp
|
||||
exit_time: pd.Timestamp
|
||||
entry_price: float
|
||||
exit_price: float
|
||||
entry_fee: float
|
||||
exit_fee: float
|
||||
profit_pct: float
|
||||
exit_reason: str
|
||||
strategy_name: str
|
||||
|
||||
|
||||
class MarketFees:
|
||||
"""Market fee calculations for different exchanges."""
|
||||
|
||||
@staticmethod
|
||||
def calculate_okx_taker_maker_fee(amount: float, is_maker: bool = True) -> float:
|
||||
"""Calculate OKX trading fees."""
|
||||
fee_rate = 0.0008 if is_maker else 0.0010
|
||||
return amount * fee_rate
|
||||
|
||||
@staticmethod
|
||||
def calculate_binance_fee(amount: float, is_maker: bool = True) -> float:
|
||||
"""Calculate Binance trading fees."""
|
||||
fee_rate = 0.001 if is_maker else 0.001
|
||||
return amount * fee_rate
|
||||
|
||||
|
||||
class PositionManager:
|
||||
"""
|
||||
Manages trading position state and calculations.
|
||||
|
||||
This class handles:
|
||||
- USD/coin balance tracking
|
||||
- Position state management
|
||||
- Trade execution calculations
|
||||
- Fee calculations
|
||||
- Performance metrics
|
||||
"""
|
||||
|
||||
def __init__(self, initial_usd: float = 10000, fee_calculator=None):
|
||||
"""
|
||||
Initialize position manager.
|
||||
|
||||
Args:
|
||||
initial_usd: Initial USD balance
|
||||
fee_calculator: Fee calculation function (defaults to OKX)
|
||||
"""
|
||||
self.initial_usd = initial_usd
|
||||
self.fee_calculator = fee_calculator or MarketFees.calculate_okx_taker_maker_fee
|
||||
|
||||
# Position state
|
||||
self.usd = initial_usd
|
||||
self.coin = 0.0
|
||||
self.position = 0 # 0 = no position, 1 = long position
|
||||
self.entry_price = 0.0
|
||||
self.entry_time = None
|
||||
|
||||
# Performance tracking
|
||||
self.max_balance = initial_usd
|
||||
self.drawdowns = []
|
||||
self.trade_records = []
|
||||
|
||||
logger.debug(f"PositionManager initialized with ${initial_usd}")
|
||||
|
||||
def is_in_position(self) -> bool:
|
||||
"""Check if currently in a position."""
|
||||
return self.position == 1
|
||||
|
||||
def get_current_balance(self, current_price: float) -> float:
|
||||
"""Get current total balance value."""
|
||||
if self.position == 0:
|
||||
return self.usd
|
||||
else:
|
||||
return self.coin * current_price
|
||||
|
||||
def execute_entry(self, entry_price: float, timestamp: pd.Timestamp,
|
||||
strategy_name: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Execute entry trade.
|
||||
|
||||
Args:
|
||||
entry_price: Entry price
|
||||
timestamp: Entry timestamp
|
||||
strategy_name: Name of the strategy
|
||||
|
||||
Returns:
|
||||
Dict with entry details
|
||||
"""
|
||||
if self.position == 1:
|
||||
raise ValueError("Cannot enter position: already in position")
|
||||
|
||||
# Calculate fees
|
||||
entry_fee = self.fee_calculator(self.usd, is_maker=False)
|
||||
usd_after_fee = self.usd - entry_fee
|
||||
|
||||
# Execute entry
|
||||
self.coin = usd_after_fee / entry_price
|
||||
self.entry_price = entry_price
|
||||
self.entry_time = timestamp
|
||||
self.usd = 0.0
|
||||
self.position = 1
|
||||
|
||||
entry_details = {
|
||||
'entry_price': entry_price,
|
||||
'entry_time': timestamp,
|
||||
'entry_fee': entry_fee,
|
||||
'coin_amount': self.coin,
|
||||
'strategy_name': strategy_name
|
||||
}
|
||||
|
||||
logger.debug(f"ENTRY executed: ${entry_price:.2f}, fee=${entry_fee:.2f}")
|
||||
return entry_details
|
||||
|
||||
def execute_exit(self, exit_price: float, timestamp: pd.Timestamp,
|
||||
exit_reason: str, strategy_name: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Execute exit trade.
|
||||
|
||||
Args:
|
||||
exit_price: Exit price
|
||||
timestamp: Exit timestamp
|
||||
exit_reason: Reason for exit
|
||||
strategy_name: Name of the strategy
|
||||
|
||||
Returns:
|
||||
Dict with exit details and trade record
|
||||
"""
|
||||
if self.position == 0:
|
||||
raise ValueError("Cannot exit position: not in position")
|
||||
|
||||
# Calculate exit
|
||||
usd_gross = self.coin * exit_price
|
||||
exit_fee = self.fee_calculator(usd_gross, is_maker=False)
|
||||
self.usd = usd_gross - exit_fee
|
||||
|
||||
# Calculate profit
|
||||
profit_pct = (exit_price - self.entry_price) / self.entry_price
|
||||
|
||||
# Calculate entry fee (for record keeping)
|
||||
entry_fee = self.fee_calculator(self.coin * self.entry_price, is_maker=False)
|
||||
|
||||
# Create trade record
|
||||
trade_record = TradeRecord(
|
||||
entry_time=self.entry_time,
|
||||
exit_time=timestamp,
|
||||
entry_price=self.entry_price,
|
||||
exit_price=exit_price,
|
||||
entry_fee=entry_fee,
|
||||
exit_fee=exit_fee,
|
||||
profit_pct=profit_pct,
|
||||
exit_reason=exit_reason,
|
||||
strategy_name=strategy_name
|
||||
)
|
||||
self.trade_records.append(trade_record)
|
||||
|
||||
# Reset position
|
||||
coin_amount = self.coin
|
||||
self.coin = 0.0
|
||||
self.position = 0
|
||||
entry_price = self.entry_price
|
||||
entry_time = self.entry_time
|
||||
self.entry_price = 0.0
|
||||
self.entry_time = None
|
||||
|
||||
exit_details = {
|
||||
'exit_price': exit_price,
|
||||
'exit_time': timestamp,
|
||||
'exit_fee': exit_fee,
|
||||
'profit_pct': profit_pct,
|
||||
'exit_reason': exit_reason,
|
||||
'trade_record': trade_record,
|
||||
'final_usd': self.usd
|
||||
}
|
||||
|
||||
logger.debug(f"EXIT executed: ${exit_price:.2f}, reason={exit_reason}, "
|
||||
f"profit={profit_pct*100:.2f}%, fee=${exit_fee:.2f}")
|
||||
return exit_details
|
||||
|
||||
def update_performance_metrics(self, current_price: float) -> None:
|
||||
"""Update performance tracking metrics."""
|
||||
current_balance = self.get_current_balance(current_price)
|
||||
|
||||
# Update max balance and drawdown
|
||||
if current_balance > self.max_balance:
|
||||
self.max_balance = current_balance
|
||||
|
||||
drawdown = (self.max_balance - current_balance) / self.max_balance
|
||||
self.drawdowns.append(drawdown)
|
||||
|
||||
def check_stop_loss(self, current_price: float, stop_loss_pct: float) -> bool:
|
||||
"""Check if stop loss should be triggered."""
|
||||
if self.position == 0 or stop_loss_pct <= 0:
|
||||
return False
|
||||
|
||||
stop_loss_price = self.entry_price * (1 - stop_loss_pct)
|
||||
return current_price <= stop_loss_price
|
||||
|
||||
def check_take_profit(self, current_price: float, take_profit_pct: float) -> bool:
|
||||
"""Check if take profit should be triggered."""
|
||||
if self.position == 0 or take_profit_pct <= 0:
|
||||
return False
|
||||
|
||||
take_profit_price = self.entry_price * (1 + take_profit_pct)
|
||||
return current_price >= take_profit_price
|
||||
|
||||
def get_performance_summary(self) -> Dict[str, Any]:
|
||||
"""Get performance summary statistics."""
|
||||
final_balance = self.usd
|
||||
n_trades = len(self.trade_records)
|
||||
|
||||
# Calculate statistics
|
||||
if n_trades > 0:
|
||||
profits = [trade.profit_pct for trade in self.trade_records]
|
||||
wins = [p for p in profits if p > 0]
|
||||
win_rate = len(wins) / n_trades
|
||||
avg_trade = np.mean(profits)
|
||||
total_fees = sum(trade.entry_fee + trade.exit_fee for trade in self.trade_records)
|
||||
else:
|
||||
win_rate = 0.0
|
||||
avg_trade = 0.0
|
||||
total_fees = 0.0
|
||||
|
||||
max_drawdown = max(self.drawdowns) if self.drawdowns else 0.0
|
||||
profit_ratio = (final_balance - self.initial_usd) / self.initial_usd
|
||||
|
||||
return {
|
||||
"initial_usd": self.initial_usd,
|
||||
"final_usd": final_balance,
|
||||
"profit_ratio": profit_ratio,
|
||||
"n_trades": n_trades,
|
||||
"win_rate": win_rate,
|
||||
"max_drawdown": max_drawdown,
|
||||
"avg_trade": avg_trade,
|
||||
"total_fees_usd": total_fees
|
||||
}
|
||||
|
||||
def get_trades_as_dicts(self) -> List[Dict[str, Any]]:
|
||||
"""Convert trade records to dictionaries."""
|
||||
trades = []
|
||||
for trade in self.trade_records:
|
||||
trades.append({
|
||||
'entry_time': trade.entry_time,
|
||||
'exit_time': trade.exit_time,
|
||||
'entry': trade.entry_price,
|
||||
'exit': trade.exit_price,
|
||||
'profit_pct': trade.profit_pct,
|
||||
'type': trade.exit_reason,
|
||||
'fee_usd': trade.entry_fee + trade.exit_fee,
|
||||
'strategy': trade.strategy_name
|
||||
})
|
||||
return trades
|
||||
|
||||
def get_current_state(self) -> Dict[str, Any]:
|
||||
"""Get current position state."""
|
||||
return {
|
||||
"position": self.position,
|
||||
"usd": self.usd,
|
||||
"coin": self.coin,
|
||||
"entry_price": self.entry_price,
|
||||
"entry_time": self.entry_time,
|
||||
"n_trades": len(self.trade_records),
|
||||
"max_balance": self.max_balance
|
||||
}
|
||||
|
||||
def reset(self) -> None:
|
||||
"""Reset position manager to initial state."""
|
||||
self.usd = self.initial_usd
|
||||
self.coin = 0.0
|
||||
self.position = 0
|
||||
self.entry_price = 0.0
|
||||
self.entry_time = None
|
||||
self.max_balance = self.initial_usd
|
||||
self.drawdowns.clear()
|
||||
self.trade_records.clear()
|
||||
|
||||
logger.debug("PositionManager reset to initial state")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""String representation of position manager."""
|
||||
return (f"PositionManager(position={self.position}, "
|
||||
f"usd=${self.usd:.2f}, coin={self.coin:.6f}, "
|
||||
f"trades={len(self.trade_records)})")
|
||||
301
IncrementalTrader/trader/trader.py
Normal file
301
IncrementalTrader/trader/trader.py
Normal file
@@ -0,0 +1,301 @@
|
||||
"""
|
||||
Incremental Trader for backtesting incremental strategies.
|
||||
|
||||
This module provides the IncTrader class that manages a single incremental strategy
|
||||
during backtesting, handling strategy execution, trade decisions, and performance tracking.
|
||||
"""
|
||||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from typing import Dict, Optional, List, Any
|
||||
import logging
|
||||
|
||||
# Use try/except for imports to handle both relative and absolute import scenarios
|
||||
try:
|
||||
from ..strategies.base import IncStrategyBase, IncStrategySignal
|
||||
from .position import PositionManager, TradeRecord
|
||||
except ImportError:
|
||||
# Fallback for direct execution
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
from strategies.base import IncStrategyBase, IncStrategySignal
|
||||
from position import PositionManager, TradeRecord
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IncTrader:
|
||||
"""
|
||||
Incremental trader that manages a single strategy during backtesting.
|
||||
|
||||
This class handles:
|
||||
- Strategy initialization and data feeding
|
||||
- Trade decision logic based on strategy signals
|
||||
- Risk management (stop loss, take profit)
|
||||
- Performance tracking and metrics collection
|
||||
|
||||
The trader processes data points sequentially, feeding them to the strategy
|
||||
and executing trades based on the generated signals.
|
||||
|
||||
Example:
|
||||
from IncrementalTrader.strategies import MetaTrendStrategy
|
||||
from IncrementalTrader.trader import IncTrader
|
||||
|
||||
strategy = MetaTrendStrategy("metatrend", params={"timeframe": "15min"})
|
||||
trader = IncTrader(
|
||||
strategy=strategy,
|
||||
initial_usd=10000,
|
||||
params={"stop_loss_pct": 0.02}
|
||||
)
|
||||
|
||||
# Process data sequentially
|
||||
for timestamp, ohlcv_data in data_stream:
|
||||
trader.process_data_point(timestamp, ohlcv_data)
|
||||
|
||||
# Get results
|
||||
results = trader.get_results()
|
||||
"""
|
||||
|
||||
def __init__(self, strategy: IncStrategyBase, initial_usd: float = 10000,
|
||||
params: Optional[Dict] = None):
|
||||
"""
|
||||
Initialize the incremental trader.
|
||||
|
||||
Args:
|
||||
strategy: Incremental strategy instance
|
||||
initial_usd: Initial USD balance
|
||||
params: Trader parameters (stop_loss_pct, take_profit_pct, etc.)
|
||||
"""
|
||||
self.strategy = strategy
|
||||
self.initial_usd = initial_usd
|
||||
self.params = params or {}
|
||||
|
||||
# Initialize position manager
|
||||
self.position_manager = PositionManager(initial_usd)
|
||||
|
||||
# Current state
|
||||
self.current_timestamp = None
|
||||
self.current_price = None
|
||||
|
||||
# Strategy state tracking
|
||||
self.data_points_processed = 0
|
||||
self.warmup_complete = False
|
||||
|
||||
# Risk management parameters
|
||||
self.stop_loss_pct = self.params.get("stop_loss_pct", 0.0)
|
||||
self.take_profit_pct = self.params.get("take_profit_pct", 0.0)
|
||||
|
||||
# Performance tracking
|
||||
self.portfolio_history = []
|
||||
|
||||
logger.info(f"IncTrader initialized: strategy={strategy.name}, "
|
||||
f"initial_usd=${initial_usd}, stop_loss={self.stop_loss_pct*100:.1f}%")
|
||||
|
||||
def process_data_point(self, timestamp: pd.Timestamp, ohlcv_data: Dict[str, float]) -> None:
|
||||
"""
|
||||
Process a single data point through the strategy and handle trading logic.
|
||||
|
||||
Args:
|
||||
timestamp: Data point timestamp
|
||||
ohlcv_data: OHLCV data dictionary with keys: open, high, low, close, volume
|
||||
"""
|
||||
self.current_timestamp = timestamp
|
||||
self.current_price = ohlcv_data['close']
|
||||
self.data_points_processed += 1
|
||||
|
||||
try:
|
||||
# Feed data to strategy and get signal
|
||||
signal = self.strategy.process_data_point(timestamp, ohlcv_data)
|
||||
|
||||
# Check if strategy is warmed up
|
||||
if not self.warmup_complete and self.strategy.is_warmed_up:
|
||||
self.warmup_complete = True
|
||||
logger.info(f"Strategy {self.strategy.name} warmed up after "
|
||||
f"{self.data_points_processed} data points")
|
||||
|
||||
# Only process signals if strategy is warmed up
|
||||
if self.warmup_complete:
|
||||
self._process_trading_logic(signal)
|
||||
|
||||
# Update performance tracking
|
||||
self._update_performance_tracking()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing data point at {timestamp}: {e}")
|
||||
raise
|
||||
|
||||
def _process_trading_logic(self, signal: Optional[IncStrategySignal]) -> None:
|
||||
"""Process trading logic based on current position and strategy signals."""
|
||||
if not self.position_manager.is_in_position():
|
||||
# No position - check for entry signals
|
||||
self._check_entry_signals(signal)
|
||||
else:
|
||||
# In position - check for exit signals
|
||||
self._check_exit_signals(signal)
|
||||
|
||||
def _check_entry_signals(self, signal: Optional[IncStrategySignal]) -> None:
|
||||
"""Check for entry signals when not in position."""
|
||||
try:
|
||||
# Check if we have a valid entry signal
|
||||
if signal and signal.signal_type == "ENTRY" and signal.confidence > 0:
|
||||
self._execute_entry(signal)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error checking entry signals: {e}")
|
||||
|
||||
def _check_exit_signals(self, signal: Optional[IncStrategySignal]) -> None:
|
||||
"""Check for exit signals when in position."""
|
||||
try:
|
||||
# Check strategy exit signals first
|
||||
if signal and signal.signal_type == "EXIT" and signal.confidence > 0:
|
||||
exit_reason = signal.metadata.get("type", "STRATEGY_EXIT")
|
||||
exit_price = signal.price if signal.price else self.current_price
|
||||
self._execute_exit(exit_reason, exit_price)
|
||||
return
|
||||
|
||||
# Check stop loss
|
||||
if self.position_manager.check_stop_loss(self.current_price, self.stop_loss_pct):
|
||||
self._execute_exit("STOP_LOSS", self.current_price)
|
||||
return
|
||||
|
||||
# Check take profit
|
||||
if self.position_manager.check_take_profit(self.current_price, self.take_profit_pct):
|
||||
self._execute_exit("TAKE_PROFIT", self.current_price)
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error checking exit signals: {e}")
|
||||
|
||||
def _execute_entry(self, signal: IncStrategySignal) -> None:
|
||||
"""Execute entry trade."""
|
||||
entry_price = signal.price if signal.price else self.current_price
|
||||
|
||||
try:
|
||||
entry_details = self.position_manager.execute_entry(
|
||||
entry_price, self.current_timestamp, self.strategy.name
|
||||
)
|
||||
|
||||
logger.info(f"ENTRY: {self.strategy.name} at ${entry_price:.2f}, "
|
||||
f"confidence={signal.confidence:.2f}, "
|
||||
f"fee=${entry_details['entry_fee']:.2f}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error executing entry: {e}")
|
||||
raise
|
||||
|
||||
def _execute_exit(self, exit_reason: str, exit_price: Optional[float] = None) -> None:
|
||||
"""Execute exit trade."""
|
||||
exit_price = exit_price if exit_price else self.current_price
|
||||
|
||||
try:
|
||||
exit_details = self.position_manager.execute_exit(
|
||||
exit_price, self.current_timestamp, exit_reason, self.strategy.name
|
||||
)
|
||||
|
||||
logger.info(f"EXIT: {self.strategy.name} at ${exit_price:.2f}, "
|
||||
f"reason={exit_reason}, "
|
||||
f"profit={exit_details['profit_pct']*100:.2f}%, "
|
||||
f"fee=${exit_details['exit_fee']:.2f}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error executing exit: {e}")
|
||||
raise
|
||||
|
||||
def _update_performance_tracking(self) -> None:
|
||||
"""Update performance tracking metrics."""
|
||||
# Update position manager metrics
|
||||
self.position_manager.update_performance_metrics(self.current_price)
|
||||
|
||||
# Track portfolio value over time
|
||||
current_balance = self.position_manager.get_current_balance(self.current_price)
|
||||
self.portfolio_history.append({
|
||||
'timestamp': self.current_timestamp,
|
||||
'balance': current_balance,
|
||||
'price': self.current_price,
|
||||
'position': self.position_manager.position
|
||||
})
|
||||
|
||||
def finalize(self) -> None:
|
||||
"""Finalize trading session (close any open positions)."""
|
||||
if self.position_manager.is_in_position():
|
||||
self._execute_exit("EOD", self.current_price)
|
||||
logger.info(f"Closed final position for {self.strategy.name} at EOD")
|
||||
|
||||
def get_results(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Get comprehensive trading results.
|
||||
|
||||
Returns:
|
||||
Dict containing performance metrics, trade records, and statistics
|
||||
"""
|
||||
# Get performance summary from position manager
|
||||
performance = self.position_manager.get_performance_summary()
|
||||
|
||||
# Get trades as dictionaries
|
||||
trades = self.position_manager.get_trades_as_dicts()
|
||||
|
||||
# Build comprehensive results
|
||||
results = {
|
||||
"strategy_name": self.strategy.name,
|
||||
"strategy_params": self.strategy.params,
|
||||
"trader_params": self.params,
|
||||
"data_points_processed": self.data_points_processed,
|
||||
"warmup_complete": self.warmup_complete,
|
||||
"trades": trades,
|
||||
"portfolio_history": self.portfolio_history,
|
||||
**performance # Include all performance metrics
|
||||
}
|
||||
|
||||
# Add first and last trade info if available
|
||||
if len(trades) > 0:
|
||||
results["first_trade"] = {
|
||||
"entry_time": trades[0]["entry_time"],
|
||||
"entry": trades[0]["entry"]
|
||||
}
|
||||
results["last_trade"] = {
|
||||
"exit_time": trades[-1]["exit_time"],
|
||||
"exit": trades[-1]["exit"]
|
||||
}
|
||||
|
||||
# Add final balance for compatibility
|
||||
results["final_balance"] = performance["final_usd"]
|
||||
|
||||
return results
|
||||
|
||||
def get_current_state(self) -> Dict[str, Any]:
|
||||
"""Get current trader state for debugging."""
|
||||
position_state = self.position_manager.get_current_state()
|
||||
|
||||
return {
|
||||
"strategy": self.strategy.name,
|
||||
"current_price": self.current_price,
|
||||
"current_timestamp": self.current_timestamp,
|
||||
"data_points_processed": self.data_points_processed,
|
||||
"warmup_complete": self.warmup_complete,
|
||||
"strategy_state": self.strategy.get_current_state_summary(),
|
||||
**position_state # Include all position state
|
||||
}
|
||||
|
||||
def get_portfolio_value(self) -> float:
|
||||
"""Get current portfolio value."""
|
||||
return self.position_manager.get_current_balance(self.current_price)
|
||||
|
||||
def reset(self) -> None:
|
||||
"""Reset trader to initial state."""
|
||||
self.position_manager.reset()
|
||||
self.strategy.reset_calculation_state()
|
||||
self.current_timestamp = None
|
||||
self.current_price = None
|
||||
self.data_points_processed = 0
|
||||
self.warmup_complete = False
|
||||
self.portfolio_history.clear()
|
||||
|
||||
logger.info(f"IncTrader reset for strategy {self.strategy.name}")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""String representation of the trader."""
|
||||
return (f"IncTrader(strategy={self.strategy.name}, "
|
||||
f"position={self.position_manager.position}, "
|
||||
f"balance=${self.position_manager.get_current_balance(self.current_price or 0):.2f}, "
|
||||
f"trades={len(self.position_manager.trade_records)})")
|
||||
Reference in New Issue
Block a user