Add average time
This commit is contained in:
@@ -799,6 +799,26 @@ public class TradingBot : Bot, ITradingBot
|
|||||||
{
|
{
|
||||||
if (Positions.Any(p => p.Identifier == position.Identifier))
|
if (Positions.Any(p => p.Identifier == position.Identifier))
|
||||||
{
|
{
|
||||||
|
// Update the close date for the trade that actually closed the position
|
||||||
|
var currentCandle = Config.IsForBacktest
|
||||||
|
? OptimizedCandles.LastOrDefault()
|
||||||
|
: ExchangeService.GetCandle(Account, Config.Ticker, DateTime.UtcNow);
|
||||||
|
|
||||||
|
if (currentCandle != null && position.ProfitAndLoss != null)
|
||||||
|
{
|
||||||
|
// Determine which trade closed the position based on realized P&L
|
||||||
|
if (position.ProfitAndLoss.Realized > 0)
|
||||||
|
{
|
||||||
|
// Profitable close = Take Profit
|
||||||
|
position.TakeProfit1.SetDate(currentCandle.Date);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Loss or breakeven close = Stop Loss
|
||||||
|
position.StopLoss.SetDate(currentCandle.Date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await SetPositionStatus(position.SignalIdentifier, PositionStatus.Finished);
|
await SetPositionStatus(position.SignalIdentifier, PositionStatus.Finished);
|
||||||
Logger.LogInformation(
|
Logger.LogInformation(
|
||||||
$"Position {position.SignalIdentifier} type correctly close. Pnl on position : {position.ProfitAndLoss?.Realized}");
|
$"Position {position.SignalIdentifier} type correctly close. Pnl on position : {position.ProfitAndLoss?.Realized}");
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ namespace Managing.Domain.Trades
|
|||||||
|
|
||||||
public decimal Fee { get; set; }
|
public decimal Fee { get; set; }
|
||||||
[Required]
|
[Required]
|
||||||
public DateTime Date { get; }
|
public DateTime Date { get; private set; }
|
||||||
[Required]
|
[Required]
|
||||||
public TradeDirection Direction { get; }
|
public TradeDirection Direction { get; }
|
||||||
[Required]
|
[Required]
|
||||||
@@ -46,6 +46,11 @@ namespace Managing.Domain.Trades
|
|||||||
Status = status;
|
Status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetDate(DateTime date)
|
||||||
|
{
|
||||||
|
Date = date;
|
||||||
|
}
|
||||||
|
|
||||||
public void SetExchangeOrderId(string exchangeOrderId)
|
public void SetExchangeOrderId(string exchangeOrderId)
|
||||||
{
|
{
|
||||||
ExchangeOrderId = exchangeOrderId;
|
ExchangeOrderId = exchangeOrderId;
|
||||||
|
|||||||
@@ -24,6 +24,63 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
|
|||||||
config
|
config
|
||||||
} = backtest;
|
} = backtest;
|
||||||
|
|
||||||
|
// Helper function to calculate position open time in hours
|
||||||
|
const calculateOpenTimeInHours = (position: any) => {
|
||||||
|
const openDate = new Date(position.open.date);
|
||||||
|
let closeDate: Date | null = null;
|
||||||
|
|
||||||
|
// Determine close date based on realized P&L (matching backend logic)
|
||||||
|
if (position.profitAndLoss?.realized != null) {
|
||||||
|
if (position.profitAndLoss.realized > 0) {
|
||||||
|
// Profitable close = Take Profit
|
||||||
|
closeDate = new Date(position.takeProfit1.date);
|
||||||
|
} else {
|
||||||
|
// Loss or breakeven close = Stop Loss
|
||||||
|
closeDate = new Date(position.stopLoss.date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closeDate) {
|
||||||
|
const diffInMs = closeDate.getTime() - openDate.getTime();
|
||||||
|
return diffInMs / (1000 * 60 * 60); // Convert to hours
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Calculate average open time for winning positions
|
||||||
|
const getAverageOpenTimeWinning = () => {
|
||||||
|
const winningPositions = positions.filter((p) => {
|
||||||
|
const realized = p.profitAndLoss?.realized ?? 0;
|
||||||
|
return realized > 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (winningPositions.length === 0) return "0.00";
|
||||||
|
|
||||||
|
const totalHours = winningPositions.reduce((sum, position) => {
|
||||||
|
return sum + calculateOpenTimeInHours(position);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
const averageHours = totalHours / winningPositions.length;
|
||||||
|
return averageHours.toFixed(2);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Calculate average open time for losing positions
|
||||||
|
const getAverageOpenTimeLosing = () => {
|
||||||
|
const losingPositions = positions.filter((p) => {
|
||||||
|
const realized = p.profitAndLoss?.realized ?? 0;
|
||||||
|
return realized <= 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (losingPositions.length === 0) return "0.00";
|
||||||
|
|
||||||
|
const totalHours = losingPositions.reduce((sum, position) => {
|
||||||
|
return sum + calculateOpenTimeInHours(position);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
const averageHours = totalHours / losingPositions.length;
|
||||||
|
return averageHours.toFixed(2);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="grid grid-flow-row">
|
<div className="grid grid-flow-row">
|
||||||
@@ -81,8 +138,14 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
|
|||||||
"SL: " + backtest.optimizedMoneyManagement?.stopLoss.toFixed(2) + "% TP: " + backtest.optimizedMoneyManagement?.takeProfit.toFixed(2) + "%"
|
"SL: " + backtest.optimizedMoneyManagement?.stopLoss.toFixed(2) + "% TP: " + backtest.optimizedMoneyManagement?.takeProfit.toFixed(2) + "%"
|
||||||
}
|
}
|
||||||
></CardText>
|
></CardText>
|
||||||
|
<CardText
|
||||||
|
title="Avg Open Time (Winning)"
|
||||||
|
content={getAverageOpenTimeWinning() + " hours"}
|
||||||
|
></CardText>
|
||||||
|
<CardText
|
||||||
|
title="Avg Open Time (Losing)"
|
||||||
|
content={getAverageOpenTimeLosing() + " hours"}
|
||||||
|
></CardText>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<figure>
|
<figure>
|
||||||
|
|||||||
Reference in New Issue
Block a user