Update get backtests
This commit is contained in:
@@ -153,7 +153,7 @@ public class BacktestRepository : IBacktestRepository
|
|||||||
|
|
||||||
public Backtest GetBacktestByIdForUser(User user, string id)
|
public Backtest GetBacktestByIdForUser(User user, string id)
|
||||||
{
|
{
|
||||||
var backtest = _backtestRepository.FindById(id);
|
var backtest = _backtestRepository.FindOne(b => b.Identifier == id);
|
||||||
|
|
||||||
// Check if backtest exists and belongs to the user
|
// Check if backtest exists and belongs to the user
|
||||||
if (backtest != null && backtest.User != null && backtest.User.Name == user.Name)
|
if (backtest != null && backtest.User != null && backtest.User.Name == user.Name)
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ public class KaigenService : IKaigenService
|
|||||||
|
|
||||||
// Check if credits are enabled via environment variable
|
// Check if credits are enabled via environment variable
|
||||||
var creditsEnabledEnv = Environment.GetEnvironmentVariable("KAIGEN_CREDITS_ENABLED");
|
var creditsEnabledEnv = Environment.GetEnvironmentVariable("KAIGEN_CREDITS_ENABLED");
|
||||||
_creditsEnabled = string.IsNullOrEmpty(creditsEnabledEnv) || creditsEnabledEnv.ToLower() == "true";
|
_creditsEnabled = !string.IsNullOrEmpty(creditsEnabledEnv) && creditsEnabledEnv.ToLower() == "true";
|
||||||
|
|
||||||
if (!_creditsEnabled)
|
if (!_creditsEnabled)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Kaigen credits are disabled via KAIGEN_CREDITS_ENABLED environment variable");
|
_logger.LogInformation("Kaigen credits are disabled via KAIGEN_CREDITS_ENABLED environment variable");
|
||||||
@@ -59,7 +59,8 @@ public class KaigenService : IKaigenService
|
|||||||
// Validate required settings only if credits are enabled
|
// Validate required settings only if credits are enabled
|
||||||
if (string.IsNullOrEmpty(_settings.PrivateKey))
|
if (string.IsNullOrEmpty(_settings.PrivateKey))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Kaigen PrivateKey is not configured. Please set the KAIGEN_PRIVATE_KEY environment variable.");
|
throw new InvalidOperationException(
|
||||||
|
"Kaigen PrivateKey is not configured. Please set the KAIGEN_PRIVATE_KEY environment variable.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(_settings.BaseUrl))
|
if (string.IsNullOrEmpty(_settings.BaseUrl))
|
||||||
@@ -74,7 +75,8 @@ public class KaigenService : IKaigenService
|
|||||||
if (!_creditsEnabled)
|
if (!_creditsEnabled)
|
||||||
{
|
{
|
||||||
var dummyRequestId = Guid.NewGuid().ToString();
|
var dummyRequestId = Guid.NewGuid().ToString();
|
||||||
_logger.LogInformation("Credits disabled - skipping debit for user {UserName}. Returning dummy request ID {RequestId}",
|
_logger.LogInformation(
|
||||||
|
"Credits disabled - skipping debit for user {UserName}. Returning dummy request ID {RequestId}",
|
||||||
user.Name, dummyRequestId);
|
user.Name, dummyRequestId);
|
||||||
return dummyRequestId;
|
return dummyRequestId;
|
||||||
}
|
}
|
||||||
@@ -83,10 +85,10 @@ public class KaigenService : IKaigenService
|
|||||||
{
|
{
|
||||||
var walletAddress = GetUserWalletAddress(user);
|
var walletAddress = GetUserWalletAddress(user);
|
||||||
var requestId = Guid.NewGuid().ToString();
|
var requestId = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
// Create the message to sign (concatenate the values)
|
// Create the message to sign (concatenate the values)
|
||||||
var message = $"{requestId}{walletAddress}{debitAmount}";
|
var message = $"{requestId}{walletAddress}{debitAmount}";
|
||||||
|
|
||||||
// Sign the message
|
// Sign the message
|
||||||
var signature = SignMessage(message, _settings.PrivateKey);
|
var signature = SignMessage(message, _settings.PrivateKey);
|
||||||
|
|
||||||
@@ -99,24 +101,26 @@ public class KaigenService : IKaigenService
|
|||||||
signature = signature
|
signature = signature
|
||||||
};
|
};
|
||||||
|
|
||||||
_logger.LogInformation("Debiting {Amount} credits for user {UserName} (wallet: {WalletAddress}) with request ID {RequestId}",
|
_logger.LogInformation(
|
||||||
|
"Debiting {Amount} credits for user {UserName} (wallet: {WalletAddress}) with request ID {RequestId}",
|
||||||
debitAmount, user.Name, walletAddress, requestId);
|
debitAmount, user.Name, walletAddress, requestId);
|
||||||
|
|
||||||
var response = await _httpClient.PutAsJsonAsync(
|
var response = await _httpClient.PutAsJsonAsync(
|
||||||
$"{_settings.BaseUrl}{_settings.DebitEndpoint}",
|
$"{_settings.BaseUrl}{_settings.DebitEndpoint}",
|
||||||
requestPayload,
|
requestPayload,
|
||||||
_jsonOptions);
|
_jsonOptions);
|
||||||
|
|
||||||
if (!response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
var errorContent = await response.Content.ReadAsStringAsync();
|
var errorContent = await response.Content.ReadAsStringAsync();
|
||||||
_logger.LogError("Failed to debit credits. Status: {StatusCode}, Error: {Error}",
|
_logger.LogError("Failed to debit credits. Status: {StatusCode}, Error: {Error}",
|
||||||
response.StatusCode, errorContent);
|
response.StatusCode, errorContent);
|
||||||
throw new Exception($"Failed to debit credits: {response.StatusCode} - {errorContent}");
|
throw new Exception($"Failed to debit credits: {response.StatusCode} - {errorContent}");
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await response.Content.ReadFromJsonAsync<KaigenResponse>(_jsonOptions);
|
var result = await response.Content.ReadFromJsonAsync<KaigenResponse>(_jsonOptions);
|
||||||
_logger.LogInformation("Successfully debited {Amount} credits for user {UserName} (wallet: {WalletAddress})",
|
_logger.LogInformation(
|
||||||
|
"Successfully debited {Amount} credits for user {UserName} (wallet: {WalletAddress})",
|
||||||
debitAmount, user.Name, walletAddress);
|
debitAmount, user.Name, walletAddress);
|
||||||
|
|
||||||
return requestId;
|
return requestId;
|
||||||
@@ -133,7 +137,7 @@ public class KaigenService : IKaigenService
|
|||||||
// If credits are disabled, return true (success) immediately
|
// If credits are disabled, return true (success) immediately
|
||||||
if (!_creditsEnabled)
|
if (!_creditsEnabled)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Credits disabled - skipping refund for user {UserName} with request ID {RequestId}",
|
_logger.LogInformation("Credits disabled - skipping refund for user {UserName} with request ID {RequestId}",
|
||||||
user.Name, requestId);
|
user.Name, requestId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -141,10 +145,10 @@ public class KaigenService : IKaigenService
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var walletAddress = GetUserWalletAddress(user);
|
var walletAddress = GetUserWalletAddress(user);
|
||||||
|
|
||||||
// Create the message to sign (concatenate the values)
|
// Create the message to sign (concatenate the values)
|
||||||
var message = $"{requestId}{walletAddress}";
|
var message = $"{requestId}{walletAddress}";
|
||||||
|
|
||||||
// Sign the message
|
// Sign the message
|
||||||
var signature = SignMessage(message, _settings.PrivateKey);
|
var signature = SignMessage(message, _settings.PrivateKey);
|
||||||
|
|
||||||
@@ -156,23 +160,25 @@ public class KaigenService : IKaigenService
|
|||||||
signature = signature
|
signature = signature
|
||||||
};
|
};
|
||||||
|
|
||||||
_logger.LogInformation("Refunding credits for user {UserName} (wallet: {WalletAddress}) with request ID {RequestId}",
|
_logger.LogInformation(
|
||||||
|
"Refunding credits for user {UserName} (wallet: {WalletAddress}) with request ID {RequestId}",
|
||||||
user.Name, walletAddress, requestId);
|
user.Name, walletAddress, requestId);
|
||||||
|
|
||||||
var response = await _httpClient.PutAsJsonAsync(
|
var response = await _httpClient.PutAsJsonAsync(
|
||||||
$"{_settings.BaseUrl}{_settings.RefundEndpoint}",
|
$"{_settings.BaseUrl}{_settings.RefundEndpoint}",
|
||||||
requestPayload,
|
requestPayload,
|
||||||
_jsonOptions);
|
_jsonOptions);
|
||||||
|
|
||||||
if (!response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
var errorContent = await response.Content.ReadAsStringAsync();
|
var errorContent = await response.Content.ReadAsStringAsync();
|
||||||
_logger.LogError("Failed to refund credits. Status: {StatusCode}, Error: {Error}",
|
_logger.LogError("Failed to refund credits. Status: {StatusCode}, Error: {Error}",
|
||||||
response.StatusCode, errorContent);
|
response.StatusCode, errorContent);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation("Successfully refunded credits for user {UserName} (wallet: {WalletAddress})", user.Name, walletAddress);
|
_logger.LogInformation("Successfully refunded credits for user {UserName} (wallet: {WalletAddress})",
|
||||||
|
user.Name, walletAddress);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -191,7 +197,7 @@ public class KaigenService : IKaigenService
|
|||||||
|
|
||||||
// Use the first account's key as the wallet address
|
// Use the first account's key as the wallet address
|
||||||
var walletAddress = user.Accounts[0].Key;
|
var walletAddress = user.Accounts[0].Key;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(walletAddress))
|
if (string.IsNullOrEmpty(walletAddress))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"No wallet address found for user {user.Name}");
|
throw new InvalidOperationException($"No wallet address found for user {user.Name}");
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import {CardPositionItem, TradeChart} from '..'
|
import {CardPositionItem, TradeChart} from '..'
|
||||||
import {
|
import {
|
||||||
Backtest,
|
Backtest,
|
||||||
|
BacktestClient,
|
||||||
CandlesWithIndicatorsResponse,
|
CandlesWithIndicatorsResponse,
|
||||||
DataClient,
|
DataClient,
|
||||||
GetCandlesWithIndicatorsRequest,
|
GetCandlesWithIndicatorsRequest,
|
||||||
@@ -20,6 +21,17 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const {apiUrl} = useApiUrlStore();
|
const {apiUrl} = useApiUrlStore();
|
||||||
const dataClient = new DataClient({}, apiUrl);
|
const dataClient = new DataClient({}, apiUrl);
|
||||||
|
const backtestClient = new BacktestClient({}, apiUrl);
|
||||||
|
|
||||||
|
// Use TanStack Query to load full backtest data
|
||||||
|
const {data: fullBacktestData, isLoading: isLoadingFullBacktest, error: fullBacktestError} = useQuery({
|
||||||
|
queryKey: ['fullBacktest', backtest.id],
|
||||||
|
queryFn: async (): Promise<Backtest> => {
|
||||||
|
return await backtestClient.backtest_Backtest(backtest.id);
|
||||||
|
},
|
||||||
|
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||||
|
gcTime: 10 * 60 * 1000, // 10 minutes (formerly cacheTime)
|
||||||
|
});
|
||||||
|
|
||||||
// Use TanStack Query to load candles with indicators
|
// Use TanStack Query to load candles with indicators
|
||||||
const {data: candlesData, isLoading: isLoadingCandles, error} = useQuery({
|
const {data: candlesData, isLoading: isLoadingCandles, error} = useQuery({
|
||||||
@@ -65,9 +77,12 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
|
|||||||
gcTime: 10 * 60 * 1000, // 10 minutes (formerly cacheTime)
|
gcTime: 10 * 60 * 1000, // 10 minutes (formerly cacheTime)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Use the full backtest data if available, otherwise fallback to the original backtest
|
||||||
|
const currentBacktest = fullBacktestData || backtest;
|
||||||
|
|
||||||
// Use the data from query or fallback to backtest data
|
// Use the data from query or fallback to backtest data
|
||||||
const candles = candlesData?.candles || backtest.candles || [];
|
const candles = candlesData?.candles || currentBacktest.candles || [];
|
||||||
const indicatorsValues = candlesData?.indicatorsValues || backtest.indicatorsValues || {};
|
const indicatorsValues = candlesData?.indicatorsValues || currentBacktest.indicatorsValues || {};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
positions,
|
positions,
|
||||||
@@ -75,7 +90,7 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
|
|||||||
signals,
|
signals,
|
||||||
statistics,
|
statistics,
|
||||||
config
|
config
|
||||||
} = backtest;
|
} = currentBacktest;
|
||||||
|
|
||||||
// Helper function to calculate position open time in hours
|
// Helper function to calculate position open time in hours
|
||||||
const calculateOpenTimeInHours = (position: any) => {
|
const calculateOpenTimeInHours = (position: any) => {
|
||||||
@@ -301,10 +316,12 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="grid grid-flow-row">
|
<div className="grid grid-flow-row">
|
||||||
{isLoadingCandles && (
|
{(isLoadingCandles || isLoadingFullBacktest) && (
|
||||||
<div className="flex justify-center items-center p-4">
|
<div className="flex justify-center items-center p-4">
|
||||||
<div className="loading loading-spinner loading-lg"></div>
|
<div className="loading loading-spinner loading-lg"></div>
|
||||||
<span className="ml-2">Loading candles with indicators...</span>
|
<span className="ml-2">
|
||||||
|
{isLoadingFullBacktest ? "Loading backtest data..." : "Loading candles with indicators..."}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="grid grid-cols-4 p-5">
|
<div className="grid grid-cols-4 p-5">
|
||||||
@@ -358,7 +375,7 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
|
|||||||
<CardText
|
<CardText
|
||||||
title="Optimized Money Management"
|
title="Optimized Money Management"
|
||||||
content={
|
content={
|
||||||
"SL: " + backtest.optimizedMoneyManagement?.stopLoss.toFixed(2) + "% TP: " + backtest.optimizedMoneyManagement?.takeProfit.toFixed(2) + "%"
|
"SL: " + currentBacktest.optimizedMoneyManagement?.stopLoss.toFixed(2) + "% TP: " + currentBacktest.optimizedMoneyManagement?.takeProfit.toFixed(2) + "%"
|
||||||
}
|
}
|
||||||
></CardText>
|
></CardText>
|
||||||
<CardText
|
<CardText
|
||||||
@@ -393,7 +410,7 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
|
|||||||
></CardText>
|
></CardText>
|
||||||
<CardText
|
<CardText
|
||||||
title="Total Fees"
|
title="Total Fees"
|
||||||
content={"$" + backtest.fees.toLocaleString('en-US', {
|
content={"$" + currentBacktest.fees.toLocaleString('en-US', {
|
||||||
minimumFractionDigits: 2,
|
minimumFractionDigits: 2,
|
||||||
maximumFractionDigits: 2
|
maximumFractionDigits: 2
|
||||||
})}
|
})}
|
||||||
@@ -415,7 +432,7 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
|
|||||||
content={getAverageTradesPerDay() + " trades/day"}
|
content={getAverageTradesPerDay() + " trades/day"}
|
||||||
></CardText>
|
></CardText>
|
||||||
</div>
|
</div>
|
||||||
{!isLoadingCandles && (
|
{!isLoadingCandles && !isLoadingFullBacktest && (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<figure className="w-full">
|
<figure className="w-full">
|
||||||
<TradeChart
|
<TradeChart
|
||||||
|
|||||||
Reference in New Issue
Block a user