Fix backtests and indicators
This commit is contained in:
@@ -723,13 +723,13 @@ fi
|
|||||||
SCRIPT_SIZE=$(wc -l < "$MIGRATION_SCRIPT")
|
SCRIPT_SIZE=$(wc -l < "$MIGRATION_SCRIPT")
|
||||||
echo "📄 Migration script contains $SCRIPT_SIZE lines"
|
echo "📄 Migration script contains $SCRIPT_SIZE lines"
|
||||||
|
|
||||||
# Show first 20 lines as preview
|
# Show last 20 lines as preview
|
||||||
echo ""
|
echo ""
|
||||||
echo "📋 PREVIEW (first 20 lines):"
|
echo "📋 PREVIEW (last 20 lines):"
|
||||||
echo "----------------------------------------"
|
echo "----------------------------------------"
|
||||||
head -20 "$MIGRATION_SCRIPT" | sed 's/^/ /'
|
tail -20 "$MIGRATION_SCRIPT" | sed 's/^/ /'
|
||||||
if [ "$SCRIPT_SIZE" -gt 20 ]; then
|
if [ "$SCRIPT_SIZE" -gt 20 ]; then
|
||||||
echo " ... (showing first 20 lines of $SCRIPT_SIZE total)"
|
echo " ... (showing last 20 lines of $SCRIPT_SIZE total)"
|
||||||
fi
|
fi
|
||||||
echo "----------------------------------------"
|
echo "----------------------------------------"
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@@ -701,13 +701,9 @@ public class TradingBotBase : ITradingBot
|
|||||||
{
|
{
|
||||||
if (positionForSignal.StopLoss.Price >= lastCandle.Low)
|
if (positionForSignal.StopLoss.Price >= lastCandle.Low)
|
||||||
{
|
{
|
||||||
// Use actual execution price (lastCandle.Low for SL hit)
|
|
||||||
var executionPrice = lastCandle.Low;
|
|
||||||
positionForSignal.StopLoss.SetPrice(executionPrice, 2);
|
|
||||||
positionForSignal.StopLoss.SetDate(lastCandle.Date);
|
positionForSignal.StopLoss.SetDate(lastCandle.Date);
|
||||||
positionForSignal.StopLoss.SetStatus(TradeStatus.Filled);
|
positionForSignal.StopLoss.SetStatus(TradeStatus.Filled);
|
||||||
|
|
||||||
// Cancel TP trades when SL is hit
|
|
||||||
if (positionForSignal.TakeProfit1 != null)
|
if (positionForSignal.TakeProfit1 != null)
|
||||||
{
|
{
|
||||||
positionForSignal.TakeProfit1.SetStatus(TradeStatus.Cancelled);
|
positionForSignal.TakeProfit1.SetStatus(TradeStatus.Cancelled);
|
||||||
@@ -719,16 +715,13 @@ public class TradingBotBase : ITradingBot
|
|||||||
}
|
}
|
||||||
|
|
||||||
await LogInformation(
|
await LogInformation(
|
||||||
$"🛑 Stop Loss Hit\nClosing LONG position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.StopLoss.Price:F2}`)");
|
$"🛑 Stop Loss Hit\nClosing LONG position\nPrice: `${positionForSignal.StopLoss.Price:F2}`");
|
||||||
await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss,
|
await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss,
|
||||||
executionPrice, true);
|
positionForSignal.StopLoss.Price, true);
|
||||||
}
|
}
|
||||||
else if (positionForSignal.TakeProfit1.Price <= lastCandle.High &&
|
else if (positionForSignal.TakeProfit1.Price <= lastCandle.High &&
|
||||||
positionForSignal.TakeProfit1.Status != TradeStatus.Filled)
|
positionForSignal.TakeProfit1.Status != TradeStatus.Filled)
|
||||||
{
|
{
|
||||||
// Use actual execution price (lastCandle.High for TP hit)
|
|
||||||
var executionPrice = lastCandle.High;
|
|
||||||
positionForSignal.TakeProfit1.SetPrice(executionPrice, 2);
|
|
||||||
positionForSignal.TakeProfit1.SetDate(lastCandle.Date);
|
positionForSignal.TakeProfit1.SetDate(lastCandle.Date);
|
||||||
positionForSignal.TakeProfit1.SetStatus(TradeStatus.Filled);
|
positionForSignal.TakeProfit1.SetStatus(TradeStatus.Filled);
|
||||||
|
|
||||||
@@ -739,15 +732,12 @@ public class TradingBotBase : ITradingBot
|
|||||||
}
|
}
|
||||||
|
|
||||||
await LogInformation(
|
await LogInformation(
|
||||||
$"🎯 Take Profit 1 Hit\nClosing LONG position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.TakeProfit1.Price:F2}`)");
|
$"🎯 Take Profit 1 Hit\nClosing LONG position\nPrice: `${positionForSignal.TakeProfit1.Price:F2}`");
|
||||||
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1,
|
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1,
|
||||||
executionPrice, positionForSignal.TakeProfit2 == null);
|
positionForSignal.TakeProfit1.Price, positionForSignal.TakeProfit2 == null);
|
||||||
}
|
}
|
||||||
else if (positionForSignal.TakeProfit2?.Price <= lastCandle.High)
|
else if (positionForSignal.TakeProfit2?.Price <= lastCandle.High)
|
||||||
{
|
{
|
||||||
// Use actual execution price (lastCandle.High for TP hit)
|
|
||||||
var executionPrice = lastCandle.High;
|
|
||||||
positionForSignal.TakeProfit2.SetPrice(executionPrice, 2);
|
|
||||||
positionForSignal.TakeProfit2.SetDate(lastCandle.Date);
|
positionForSignal.TakeProfit2.SetDate(lastCandle.Date);
|
||||||
positionForSignal.TakeProfit2.SetStatus(TradeStatus.Filled);
|
positionForSignal.TakeProfit2.SetStatus(TradeStatus.Filled);
|
||||||
|
|
||||||
@@ -758,18 +748,15 @@ public class TradingBotBase : ITradingBot
|
|||||||
}
|
}
|
||||||
|
|
||||||
await LogInformation(
|
await LogInformation(
|
||||||
$"🎯 Take Profit 2 Hit\nClosing LONG position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.TakeProfit2.Price:F2}`)");
|
$"🎯 Take Profit 2 Hit\nClosing LONG position\nPrice: `${positionForSignal.TakeProfit2.Price:F2}`");
|
||||||
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2,
|
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2,
|
||||||
executionPrice, true);
|
positionForSignal.TakeProfit2.Price, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (positionForSignal.OriginDirection == TradeDirection.Short)
|
else if (positionForSignal.OriginDirection == TradeDirection.Short)
|
||||||
{
|
{
|
||||||
if (positionForSignal.StopLoss.Price <= lastCandle.High)
|
if (positionForSignal.StopLoss.Price <= lastCandle.High)
|
||||||
{
|
{
|
||||||
// Use actual execution price (lastCandle.High for SL hit on SHORT)
|
|
||||||
var executionPrice = lastCandle.High;
|
|
||||||
positionForSignal.StopLoss.SetPrice(executionPrice, 2);
|
|
||||||
positionForSignal.StopLoss.SetDate(lastCandle.Date);
|
positionForSignal.StopLoss.SetDate(lastCandle.Date);
|
||||||
positionForSignal.StopLoss.SetStatus(TradeStatus.Filled);
|
positionForSignal.StopLoss.SetStatus(TradeStatus.Filled);
|
||||||
|
|
||||||
@@ -785,16 +772,14 @@ public class TradingBotBase : ITradingBot
|
|||||||
}
|
}
|
||||||
|
|
||||||
await LogInformation(
|
await LogInformation(
|
||||||
$"🛑 Stop Loss Hit\nClosing SHORT position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.StopLoss.Price:F2}`)");
|
$"🛑 Stop Loss Hit\nClosing SHORT position\nPrice: `${positionForSignal.StopLoss.Price:F2}`");
|
||||||
await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss,
|
await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss,
|
||||||
executionPrice, true);
|
positionForSignal.StopLoss.Price, true);
|
||||||
}
|
}
|
||||||
else if (positionForSignal.TakeProfit1.Price >= lastCandle.Low &&
|
else if (positionForSignal.TakeProfit1.Price >= lastCandle.Low &&
|
||||||
positionForSignal.TakeProfit1.Status != TradeStatus.Filled)
|
positionForSignal.TakeProfit1.Status != TradeStatus.Filled)
|
||||||
{
|
{
|
||||||
// Use actual execution price (lastCandle.Low for TP hit on SHORT)
|
// Use actual execution price (lastCandle.Low for TP hit on SHORT)
|
||||||
var executionPrice = lastCandle.Low;
|
|
||||||
positionForSignal.TakeProfit1.SetPrice(executionPrice, 2);
|
|
||||||
positionForSignal.TakeProfit1.SetDate(lastCandle.Date);
|
positionForSignal.TakeProfit1.SetDate(lastCandle.Date);
|
||||||
positionForSignal.TakeProfit1.SetStatus(TradeStatus.Filled);
|
positionForSignal.TakeProfit1.SetStatus(TradeStatus.Filled);
|
||||||
|
|
||||||
@@ -805,15 +790,13 @@ public class TradingBotBase : ITradingBot
|
|||||||
}
|
}
|
||||||
|
|
||||||
await LogInformation(
|
await LogInformation(
|
||||||
$"🎯 Take Profit 1 Hit\nClosing SHORT position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.TakeProfit1.Price:F2}`)");
|
$"🎯 Take Profit 1 Hit\nClosing SHORT position\nPrice: `${positionForSignal.TakeProfit1.Price:F2}` (was `${positionForSignal.TakeProfit1.Price:F2}`)");
|
||||||
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1,
|
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1,
|
||||||
executionPrice, positionForSignal.TakeProfit2 == null);
|
positionForSignal.TakeProfit1.Price, positionForSignal.TakeProfit2 == null);
|
||||||
}
|
}
|
||||||
else if (positionForSignal.TakeProfit2?.Price >= lastCandle.Low)
|
else if (positionForSignal.TakeProfit2?.Price >= lastCandle.Low)
|
||||||
{
|
{
|
||||||
// Use actual execution price (lastCandle.Low for TP hit on SHORT)
|
// Use actual execution price (lastCandle.Low for TP hit on SHORT)
|
||||||
var executionPrice = lastCandle.Low;
|
|
||||||
positionForSignal.TakeProfit2.SetPrice(executionPrice, 2);
|
|
||||||
positionForSignal.TakeProfit2.SetDate(lastCandle.Date);
|
positionForSignal.TakeProfit2.SetDate(lastCandle.Date);
|
||||||
positionForSignal.TakeProfit2.SetStatus(TradeStatus.Filled);
|
positionForSignal.TakeProfit2.SetStatus(TradeStatus.Filled);
|
||||||
|
|
||||||
@@ -824,9 +807,9 @@ public class TradingBotBase : ITradingBot
|
|||||||
}
|
}
|
||||||
|
|
||||||
await LogInformation(
|
await LogInformation(
|
||||||
$"🎯 Take Profit 2 Hit\nClosing SHORT position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.TakeProfit2.Price:F2}`)");
|
$"🎯 Take Profit 2 Hit\nClosing SHORT position\nPrice: `${positionForSignal.TakeProfit2.Price:F2}` (was `${positionForSignal.TakeProfit2.Price:F2}`)");
|
||||||
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2,
|
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2,
|
||||||
executionPrice, true);
|
positionForSignal.TakeProfit2.Price, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1530,11 +1513,15 @@ public class TradingBotBase : ITradingBot
|
|||||||
closingPrice = Config.IsForBacktest
|
closingPrice = Config.IsForBacktest
|
||||||
? currentCandle.Close
|
? currentCandle.Close
|
||||||
: 0;
|
: 0;
|
||||||
await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
|
|
||||||
async exchangeService =>
|
if (!Config.IsForBacktest)
|
||||||
{
|
{
|
||||||
closingPrice = await exchangeService.GetCurrentPrice(Account, Config.Ticker);
|
await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
|
||||||
});
|
async exchangeService =>
|
||||||
|
{
|
||||||
|
closingPrice = await exchangeService.GetCurrentPrice(Account, Config.Ticker);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
bool isManualCloseProfitable = position.OriginDirection == TradeDirection.Long
|
bool isManualCloseProfitable = position.OriginDirection == TradeDirection.Long
|
||||||
? closingPrice > position.Open.Price
|
? closingPrice > position.Open.Price
|
||||||
|
|||||||
@@ -451,7 +451,8 @@ public class TradingService : ITradingService
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
indicatorsValues[indicator.Type] = indicator.GetIndicatorValues(candles);
|
var buildedIndicator = ScenarioHelpers.BuildIndicator(ScenarioHelpers.BaseToLight(indicator));
|
||||||
|
indicatorsValues[indicator.Type] = buildedIndicator.GetIndicatorValues(candles);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -42,26 +42,6 @@ public class LightIndicator
|
|||||||
|
|
||||||
[Id(11)] public int? CyclePeriods { get; set; }
|
[Id(11)] public int? CyclePeriods { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts a full Indicator to a LightIndicator
|
|
||||||
/// </summary>
|
|
||||||
public static LightIndicator BaseToLight(IndicatorBase indicatorBase)
|
|
||||||
{
|
|
||||||
return new LightIndicator(indicatorBase.Name, indicatorBase.Type)
|
|
||||||
{
|
|
||||||
SignalType = indicatorBase.SignalType,
|
|
||||||
MinimumHistory = indicatorBase.MinimumHistory,
|
|
||||||
Period = indicatorBase.Period,
|
|
||||||
FastPeriods = indicatorBase.FastPeriods,
|
|
||||||
SlowPeriods = indicatorBase.SlowPeriods,
|
|
||||||
SignalPeriods = indicatorBase.SignalPeriods,
|
|
||||||
Multiplier = indicatorBase.Multiplier,
|
|
||||||
SmoothPeriods = indicatorBase.SmoothPeriods,
|
|
||||||
StochPeriods = indicatorBase.StochPeriods,
|
|
||||||
CyclePeriods = indicatorBase.CyclePeriods
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts a LightIndicator back to a full Indicator
|
/// Converts a LightIndicator back to a full Indicator
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public class LightScenario
|
|||||||
{
|
{
|
||||||
var lightScenario = new LightScenario(scenario.Name, scenario.LoopbackPeriod)
|
var lightScenario = new LightScenario(scenario.Name, scenario.LoopbackPeriod)
|
||||||
{
|
{
|
||||||
Indicators = scenario.Indicators?.Select(LightIndicator.BaseToLight).ToList() ??
|
Indicators = scenario.Indicators?.Select(ScenarioHelpers.BaseToLight).ToList() ??
|
||||||
new List<LightIndicator>()
|
new List<LightIndicator>()
|
||||||
};
|
};
|
||||||
return lightScenario;
|
return lightScenario;
|
||||||
|
|||||||
@@ -103,6 +103,26 @@ public static class ScenarioHelpers
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a full Indicator to a LightIndicator
|
||||||
|
/// </summary>
|
||||||
|
public static LightIndicator BaseToLight(IndicatorBase indicatorBase)
|
||||||
|
{
|
||||||
|
return new LightIndicator(indicatorBase.Name, indicatorBase.Type)
|
||||||
|
{
|
||||||
|
SignalType = indicatorBase.SignalType,
|
||||||
|
MinimumHistory = indicatorBase.MinimumHistory,
|
||||||
|
Period = indicatorBase.Period,
|
||||||
|
FastPeriods = indicatorBase.FastPeriods,
|
||||||
|
SlowPeriods = indicatorBase.SlowPeriods,
|
||||||
|
SignalPeriods = indicatorBase.SignalPeriods,
|
||||||
|
Multiplier = indicatorBase.Multiplier,
|
||||||
|
SmoothPeriods = indicatorBase.SmoothPeriods,
|
||||||
|
StochPeriods = indicatorBase.StochPeriods,
|
||||||
|
CyclePeriods = indicatorBase.CyclePeriods
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static IIndicator BuildIndicator(
|
public static IIndicator BuildIndicator(
|
||||||
IndicatorType type,
|
IndicatorType type,
|
||||||
string name,
|
string name,
|
||||||
|
|||||||
@@ -4070,6 +4070,8 @@ export interface Backtest {
|
|||||||
requestId?: string;
|
requestId?: string;
|
||||||
metadata?: any | null;
|
metadata?: any | null;
|
||||||
scoreMessage?: string;
|
scoreMessage?: string;
|
||||||
|
initialBalance: number;
|
||||||
|
netPnl: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TradingBotConfig {
|
export interface TradingBotConfig {
|
||||||
@@ -4355,6 +4357,8 @@ export interface LightBacktestResponse {
|
|||||||
sharpeRatio: number;
|
sharpeRatio: number;
|
||||||
score: number;
|
score: number;
|
||||||
scoreMessage: string;
|
scoreMessage: string;
|
||||||
|
initialBalance: number;
|
||||||
|
netPnl: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum BacktestSortableColumn {
|
export enum BacktestSortableColumn {
|
||||||
@@ -4389,6 +4393,8 @@ export interface LightBacktest {
|
|||||||
scoreMessage?: string | null;
|
scoreMessage?: string | null;
|
||||||
metadata?: any | null;
|
metadata?: any | null;
|
||||||
ticker?: string | null;
|
ticker?: string | null;
|
||||||
|
initialBalance?: number;
|
||||||
|
netPnl?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RunBacktestRequest {
|
export interface RunBacktestRequest {
|
||||||
|
|||||||
@@ -245,6 +245,8 @@ export interface Backtest {
|
|||||||
requestId?: string;
|
requestId?: string;
|
||||||
metadata?: any | null;
|
metadata?: any | null;
|
||||||
scoreMessage?: string;
|
scoreMessage?: string;
|
||||||
|
initialBalance: number;
|
||||||
|
netPnl: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TradingBotConfig {
|
export interface TradingBotConfig {
|
||||||
@@ -530,6 +532,8 @@ export interface LightBacktestResponse {
|
|||||||
sharpeRatio: number;
|
sharpeRatio: number;
|
||||||
score: number;
|
score: number;
|
||||||
scoreMessage: string;
|
scoreMessage: string;
|
||||||
|
initialBalance: number;
|
||||||
|
netPnl: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum BacktestSortableColumn {
|
export enum BacktestSortableColumn {
|
||||||
@@ -564,6 +568,8 @@ export interface LightBacktest {
|
|||||||
scoreMessage?: string | null;
|
scoreMessage?: string | null;
|
||||||
metadata?: any | null;
|
metadata?: any | null;
|
||||||
ticker?: string | null;
|
ticker?: string | null;
|
||||||
|
initialBalance?: number;
|
||||||
|
netPnl?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RunBacktestRequest {
|
export interface RunBacktestRequest {
|
||||||
|
|||||||
Reference in New Issue
Block a user