The test are running and returning feedback. I am just about to implement a better solution for multithreading
This commit is contained in:
parent
0ae835d096
commit
4f11778b09
|
|
@ -1201,7 +1201,7 @@ class PythonGenerator:
|
|||
:param indent_level: Current indentation level.
|
||||
:return: A string representing the variable retrieval.
|
||||
"""
|
||||
variable_name = node.get('variable_name')
|
||||
variable_name = node.get('variable_name', '').strip()
|
||||
if not variable_name:
|
||||
logger.error("get_variable node missing 'variable_name'.")
|
||||
return 'None'
|
||||
|
|
@ -1217,7 +1217,7 @@ class PythonGenerator:
|
|||
"""
|
||||
code_lines = []
|
||||
indent = ' ' * indent_level
|
||||
variable_name = node.get('variable_name')
|
||||
variable_name = node.get('variable_name', '').strip()
|
||||
value_node = node.get('values')
|
||||
|
||||
if not variable_name:
|
||||
|
|
@ -1391,9 +1391,9 @@ class PythonGenerator:
|
|||
if operator not in operator_map:
|
||||
logger.error(f"Invalid operator for math_operation: {operator}. Defaulting to 'ADD'.")
|
||||
|
||||
left_expr = self.process_numeric_list(left_operand, indent_level)
|
||||
right_expr = self.process_numeric_list(right_operand, indent_level)
|
||||
expr = f"{left_expr} {python_operator} {right_expr}"
|
||||
left_expr = self.generate_condition_code(left_operand, indent_level)
|
||||
right_expr = self.generate_condition_code(right_operand, indent_level)
|
||||
expr = f"({left_expr} {python_operator} {right_expr})"
|
||||
logger.debug(f"Generated math_operation expression: {expr}")
|
||||
return expr
|
||||
|
||||
|
|
|
|||
|
|
@ -197,13 +197,60 @@ class Backtester:
|
|||
for name in self.indicator_names:
|
||||
self.indicator_pointers[name] = 0 # Start at the first row
|
||||
|
||||
# Initialize an empty list to store orders
|
||||
self.orders = []
|
||||
self.trade_list = []
|
||||
|
||||
# Initialize any other needed variables
|
||||
self.starting_balance = self.broker.getvalue()
|
||||
|
||||
# Existing code...
|
||||
self.current_step = 0
|
||||
self.last_progress = 0 # Initialize last_progress
|
||||
|
||||
def notify_order(self, order):
|
||||
if order.status in [order.Submitted, order.Accepted]:
|
||||
# Order has been submitted/accepted by broker - nothing to do
|
||||
return
|
||||
|
||||
if order.status in [order.Completed]:
|
||||
if order.isbuy():
|
||||
self.log(f"BUY EXECUTED, Price: {order.executed.price}, Size: {order.executed.size}")
|
||||
elif order.issell():
|
||||
self.log(f"SELL EXECUTED, Price: {order.executed.price}, Size: {order.executed.size}")
|
||||
self.bar_executed = len(self)
|
||||
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
|
||||
self.log('Order Canceled/Margin/Rejected')
|
||||
|
||||
# Remove the order from the list
|
||||
if order in self.orders:
|
||||
self.orders.remove(order)
|
||||
|
||||
def notify_trade(self, trade):
|
||||
if not trade.isclosed:
|
||||
return
|
||||
|
||||
self.log(f"TRADE CLOSED, GROSS P/L: {trade.pnl}, NET P/L: {trade.pnlcomm}")
|
||||
|
||||
# Convert datetime objects to ISO-formatted strings
|
||||
open_datetime = bt.num2date(trade.dtopen).isoformat() if trade.dtopen else None
|
||||
close_datetime = bt.num2date(trade.dtclose).isoformat() if trade.dtclose else None
|
||||
|
||||
# Store the trade details for later use
|
||||
trade_info = {
|
||||
'ref': trade.ref,
|
||||
'size': trade.size,
|
||||
'price': trade.price,
|
||||
'pnl': trade.pnl,
|
||||
'pnlcomm': trade.pnlcomm,
|
||||
'open_datetime': open_datetime,
|
||||
'close_datetime': close_datetime
|
||||
}
|
||||
self.trade_list.append(trade_info)
|
||||
|
||||
def log(self, txt, dt=None):
|
||||
""" Logging function for this strategy"""
|
||||
dt = dt or self.datas[0].datetime.datetime(0)
|
||||
logger.info(f"{dt.isoformat()} - {txt}")
|
||||
|
||||
def next(self):
|
||||
self.current_step += 1
|
||||
# Execute the strategy logic
|
||||
|
|
@ -257,19 +304,6 @@ class Backtester:
|
|||
"""
|
||||
Custom trade_order method for backtesting.
|
||||
Executes trades within the Backtrader environment.
|
||||
|
||||
:param trade_type: Type of trade ('buy' or 'sell').
|
||||
:param size: Size of the trade.
|
||||
:param order_type: Type of order (e.g., 'market').
|
||||
:param source: Dictionary containing additional trade information, including 'market'.
|
||||
:param tif: Time in Force for the order.
|
||||
:param stop_loss: Dictionary with stop loss parameters.
|
||||
:param trailing_stop: Dictionary with trailing stop parameters.
|
||||
:param take_profit: Dictionary with take profit parameters.
|
||||
:param limit: Dictionary with limit order parameters.
|
||||
:param trailing_limit: Dictionary with trailing limit parameters.
|
||||
:param target_market: Dictionary with target market parameters.
|
||||
:param name_order: Dictionary with order name parameters.
|
||||
"""
|
||||
# Validate and extract 'symbol' from 'source'
|
||||
if source and 'market' in source:
|
||||
|
|
@ -279,42 +313,44 @@ class Backtester:
|
|||
logger.error("Symbol not provided in source. Order not executed.")
|
||||
return # Abort the order execution
|
||||
|
||||
price = strategy_instance.backtrader_strategy.data.close[0]
|
||||
|
||||
if trade_type.lower() == 'buy':
|
||||
logger.info(f"Executing BUY order: Size={size}, Symbol={symbol}, Order Type={order_type}")
|
||||
# Execute a buy order in Backtrader via Cerebro
|
||||
order = strategy_instance.backtrader_strategy.buy(size=size, exectype=bt.Order.Market, name=symbol)
|
||||
|
||||
# Prepare bracket order parameters
|
||||
stop_loss_price = stop_loss.get('value') if stop_loss else None
|
||||
take_profit_price = take_profit.get('value') if take_profit else None
|
||||
|
||||
# Create bracket order and store the orders
|
||||
bracket_orders = strategy_instance.backtrader_strategy.buy_bracket(
|
||||
size=size,
|
||||
price=price,
|
||||
stopprice=stop_loss_price,
|
||||
limitprice=take_profit_price,
|
||||
exectype=bt.Order.Market
|
||||
)
|
||||
elif trade_type.lower() == 'sell':
|
||||
logger.info(f"Executing SELL order: Size={size}, Symbol={symbol}, Order Type={order_type}")
|
||||
# Execute a sell order in Backtrader via Cerebro
|
||||
order = strategy_instance.backtrader_strategy.sell(size=size, exectype=bt.Order.Market, name=symbol)
|
||||
|
||||
# Prepare bracket order parameters
|
||||
stop_loss_price = stop_loss.get('value') if stop_loss else None
|
||||
take_profit_price = take_profit.get('value') if take_profit else None
|
||||
|
||||
# Create bracket order and store the orders
|
||||
bracket_orders = strategy_instance.backtrader_strategy.sell_bracket(
|
||||
size=size,
|
||||
price=price,
|
||||
stopprice=stop_loss_price,
|
||||
limitprice=take_profit_price,
|
||||
exectype=bt.Order.Market
|
||||
)
|
||||
else:
|
||||
logger.error(f"Invalid trade_type '{trade_type}'. Order not executed.")
|
||||
return # Abort the order execution
|
||||
|
||||
# Handle trade options like stop_loss and take_profit
|
||||
if stop_loss or take_profit:
|
||||
if stop_loss:
|
||||
stop_price = stop_loss.get('value')
|
||||
if stop_price is not None:
|
||||
logger.info(f"Setting STOP LOSS at {stop_price} for order {order.ref}")
|
||||
strategy_instance.backtrader_strategy.sell(
|
||||
size=size,
|
||||
exectype=bt.Order.Stop,
|
||||
price=stop_price,
|
||||
parent=order,
|
||||
name=f"StopLoss_{order.ref}"
|
||||
)
|
||||
if take_profit:
|
||||
take_profit_price = take_profit.get('value')
|
||||
if take_profit_price is not None:
|
||||
logger.info(f"Setting TAKE PROFIT at {take_profit_price} for order {order.ref}")
|
||||
strategy_instance.backtrader_strategy.sell(
|
||||
size=size,
|
||||
exectype=bt.Order.Limit,
|
||||
price=take_profit_price,
|
||||
parent=order,
|
||||
name=f"TakeProfit_{order.ref}"
|
||||
)
|
||||
# Store the orders for tracking
|
||||
strategy_instance.backtrader_strategy.orders.extend(bracket_orders)
|
||||
|
||||
# Notify user about the trade execution
|
||||
strategy_instance.notify_user(
|
||||
|
|
@ -706,12 +742,12 @@ class Backtester:
|
|||
final_value = cerebro.broker.getvalue()
|
||||
run_duration = (end_time - start_time).total_seconds()
|
||||
|
||||
# Extract equity curve from analyzers
|
||||
equity_curve = results[0].analyzers.equity_curve.get_analysis().get('equity_curve', [])
|
||||
strategy = results[0]
|
||||
|
||||
# Extract trade data from TradeAnalyzer
|
||||
trade_analyzer = results[0].analyzers.trade_analyzer.get_analysis()
|
||||
trades = self.parse_trade_analyzer(trade_analyzer)
|
||||
# Extract equity curve from analyzers
|
||||
equity_curve = strategy.analyzers.equity_curve.get_analysis().get('equity_curve', [])
|
||||
|
||||
trades = strategy.trade_list
|
||||
|
||||
# Send 100% completion
|
||||
self.socketio.emit('message', {'reply': 'progress', 'data': {'progress': 100}}, room=socket_conn_id)
|
||||
|
|
@ -1052,54 +1088,4 @@ class Backtester:
|
|||
# Kept here for backward compatibility or future use
|
||||
return []
|
||||
|
||||
def parse_trade_analyzer(self, trade_analyzer: dict) -> list:
|
||||
"""
|
||||
Parse the TradeAnalyzer results from Backtrader and return a list of trades.
|
||||
:param trade_analyzer: Dictionary containing trade analysis.
|
||||
:return: List of trade dictionaries with relevant information.
|
||||
"""
|
||||
trades = []
|
||||
if not trade_analyzer:
|
||||
logger.warning("No trade data available in TradeAnalyzer.")
|
||||
return trades
|
||||
|
||||
# TradeAnalyzer stores trades under 'trades'
|
||||
trade_list = trade_analyzer.get('trades', {})
|
||||
|
||||
# Check if 'trades' is a dict (with trade references) or a list
|
||||
if isinstance(trade_list, dict):
|
||||
for ref, trade in trade_list.items():
|
||||
trade_info = {
|
||||
'ref': ref,
|
||||
'size': trade.get('size'),
|
||||
'price': trade.get('price'),
|
||||
'value': trade.get('value'),
|
||||
'pnl': trade.get('pnl'),
|
||||
'commission': trade.get('commission'),
|
||||
'opendate': trade.get('opendate'),
|
||||
'closedate': trade.get('closedate'),
|
||||
'status': trade.get('status'),
|
||||
}
|
||||
trades.append(trade_info)
|
||||
logger.debug(f"Parsed trade: {trade_info}")
|
||||
elif isinstance(trade_list, list):
|
||||
for trade in trade_list:
|
||||
trade_info = {
|
||||
'ref': trade.get('ref'),
|
||||
'size': trade.get('size'),
|
||||
'price': trade.get('price'),
|
||||
'value': trade.get('value'),
|
||||
'pnl': trade.get('pnl'),
|
||||
'commission': trade.get('commission'),
|
||||
'opendate': trade.get('opendate'),
|
||||
'closedate': trade.get('closedate'),
|
||||
'status': trade.get('status'),
|
||||
}
|
||||
trades.append(trade_info)
|
||||
logger.debug(f"Parsed trade: {trade_info}")
|
||||
else:
|
||||
logger.error("Unexpected format for 'trades' in TradeAnalyzer.")
|
||||
|
||||
logger.info(f"Parsed {len(trades)} trades from TradeAnalyzer.")
|
||||
return trades
|
||||
|
||||
|
|
|
|||
|
|
@ -165,13 +165,13 @@ export function defineVAFGenerators() {
|
|||
// Retrieve the 'variable_name' field input
|
||||
const variableName = currentBlock.getFieldValue('variable_name');
|
||||
const trimmedName = variableName && variableName.trim() !== "" ? variableName.trim() : 'undefined_var';
|
||||
if (variableName.trim() === "") {
|
||||
if (variableName === "") {
|
||||
console.warn("Empty variable_name in get_variable block. Defaulting to 'undefined_var'.");
|
||||
}
|
||||
|
||||
variables.push({
|
||||
type: 'get_variable',
|
||||
variable_name: variableName
|
||||
variable_name: trimmedName
|
||||
});
|
||||
|
||||
// Process the 'NEXT' connection
|
||||
|
|
|
|||
Loading…
Reference in New Issue