Remove candle from backtest
This commit is contained in:
@@ -1,6 +1,15 @@
|
||||
import {CardPositionItem, TradeChart} from '..'
|
||||
import {Backtest} from '../../../generated/ManagingApi'
|
||||
import {
|
||||
Backtest,
|
||||
CandlesWithIndicatorsResponse,
|
||||
DataClient,
|
||||
GetCandlesWithIndicatorsRequest,
|
||||
IndicatorType,
|
||||
SignalType
|
||||
} from '../../../generated/ManagingApi'
|
||||
import {CardPosition, CardText} from '../../mollecules'
|
||||
import {useQuery} from '@tanstack/react-query'
|
||||
import useApiUrlStore from '../../../app/store/apiStore'
|
||||
|
||||
interface IBacktestRowDetailsProps {
|
||||
backtest: Backtest;
|
||||
@@ -9,11 +18,60 @@ interface IBacktestRowDetailsProps {
|
||||
const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
|
||||
backtest
|
||||
}) => {
|
||||
const {apiUrl} = useApiUrlStore();
|
||||
const dataClient = new DataClient({}, apiUrl);
|
||||
|
||||
// Use TanStack Query to load candles with indicators
|
||||
const {data: candlesData, isLoading: isLoadingCandles, error} = useQuery({
|
||||
queryKey: ['candlesWithIndicators', backtest.id, backtest.config?.scenario?.name],
|
||||
queryFn: async (): Promise<CandlesWithIndicatorsResponse> => {
|
||||
// Only fetch if no candles are present
|
||||
if (backtest.candles && backtest.candles.length > 0) {
|
||||
return {
|
||||
candles: backtest.candles,
|
||||
indicatorsValues: backtest.indicatorsValues || {}
|
||||
};
|
||||
}
|
||||
|
||||
const request: GetCandlesWithIndicatorsRequest = {
|
||||
ticker: backtest.config.ticker,
|
||||
startDate: backtest.startDate,
|
||||
endDate: backtest.endDate,
|
||||
timeframe: backtest.config.timeframe,
|
||||
scenario: backtest.config?.scenario ? {
|
||||
name: backtest.config.scenario.name || '',
|
||||
indicators: backtest.config.scenario.indicators?.map(indicator => ({
|
||||
name: indicator.name || '',
|
||||
type: indicator.type || IndicatorType.RsiDivergence,
|
||||
signalType: indicator.signalType || SignalType.Signal,
|
||||
minimumHistory: indicator.minimumHistory || 0,
|
||||
period: indicator.period,
|
||||
fastPeriods: indicator.fastPeriods,
|
||||
slowPeriods: indicator.slowPeriods,
|
||||
signalPeriods: indicator.signalPeriods,
|
||||
multiplier: indicator.multiplier,
|
||||
smoothPeriods: indicator.smoothPeriods,
|
||||
stochPeriods: indicator.stochPeriods,
|
||||
cyclePeriods: indicator.cyclePeriods
|
||||
})) || [],
|
||||
loopbackPeriod: backtest.config.scenario.loopbackPeriod
|
||||
} : undefined
|
||||
};
|
||||
|
||||
return await dataClient.data_GetCandlesWithIndicators(request);
|
||||
},
|
||||
enabled: !backtest.candles || backtest.candles.length === 0, // Only run query if no candles exist
|
||||
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||
gcTime: 10 * 60 * 1000, // 10 minutes (formerly cacheTime)
|
||||
});
|
||||
|
||||
// Use the data from query or fallback to backtest data
|
||||
const candles = candlesData?.candles || backtest.candles || [];
|
||||
const indicatorsValues = candlesData?.indicatorsValues || backtest.indicatorsValues || {};
|
||||
|
||||
const {
|
||||
candles,
|
||||
positions,
|
||||
walletBalances,
|
||||
indicatorsValues,
|
||||
signals,
|
||||
statistics,
|
||||
config
|
||||
@@ -243,6 +301,12 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
|
||||
return (
|
||||
<>
|
||||
<div className="grid grid-flow-row">
|
||||
{isLoadingCandles && (
|
||||
<div className="flex justify-center items-center p-4">
|
||||
<div className="loading loading-spinner loading-lg"></div>
|
||||
<span className="ml-2">Loading candles with indicators...</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="grid grid-cols-4 p-5">
|
||||
<CardPosition
|
||||
positivePosition={true}
|
||||
@@ -351,18 +415,20 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
|
||||
content={getAverageTradesPerDay() + " trades/day"}
|
||||
></CardText>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<figure className="w-full">
|
||||
<TradeChart
|
||||
candles={candles}
|
||||
positions={positions}
|
||||
walletBalances={walletBalances}
|
||||
indicatorsValues={indicatorsValues}
|
||||
signals={signals}
|
||||
height={1000}
|
||||
></TradeChart>
|
||||
</figure>
|
||||
</div>
|
||||
{!isLoadingCandles && (
|
||||
<div className="w-full">
|
||||
<figure className="w-full">
|
||||
<TradeChart
|
||||
candles={candles}
|
||||
positions={positions}
|
||||
walletBalances={walletBalances}
|
||||
indicatorsValues={indicatorsValues}
|
||||
signals={signals}
|
||||
height={1000}
|
||||
></TradeChart>
|
||||
</figure>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -1087,29 +1087,17 @@ export class DataClient extends AuthorizedApiBase {
|
||||
return Promise.resolve<SpotlightOverview>(null as any);
|
||||
}
|
||||
|
||||
data_GetCandles(exchange: TradingExchanges | undefined, ticker: Ticker | undefined, startDate: Date | undefined, timeframe: Timeframe | undefined): Promise<Candle[]> {
|
||||
let url_ = this.baseUrl + "/Data/GetCandles?";
|
||||
if (exchange === null)
|
||||
throw new Error("The parameter 'exchange' cannot be null.");
|
||||
else if (exchange !== undefined)
|
||||
url_ += "exchange=" + encodeURIComponent("" + exchange) + "&";
|
||||
if (ticker === null)
|
||||
throw new Error("The parameter 'ticker' cannot be null.");
|
||||
else if (ticker !== undefined)
|
||||
url_ += "ticker=" + encodeURIComponent("" + ticker) + "&";
|
||||
if (startDate === null)
|
||||
throw new Error("The parameter 'startDate' cannot be null.");
|
||||
else if (startDate !== undefined)
|
||||
url_ += "startDate=" + encodeURIComponent(startDate ? "" + startDate.toISOString() : "") + "&";
|
||||
if (timeframe === null)
|
||||
throw new Error("The parameter 'timeframe' cannot be null.");
|
||||
else if (timeframe !== undefined)
|
||||
url_ += "timeframe=" + encodeURIComponent("" + timeframe) + "&";
|
||||
data_GetCandlesWithIndicators(request: GetCandlesWithIndicatorsRequest): Promise<CandlesWithIndicatorsResponse> {
|
||||
let url_ = this.baseUrl + "/Data/GetCandlesWithIndicators";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
const content_ = JSON.stringify(request);
|
||||
|
||||
let options_: RequestInit = {
|
||||
method: "GET",
|
||||
body: content_,
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json"
|
||||
}
|
||||
};
|
||||
@@ -1117,17 +1105,17 @@ export class DataClient extends AuthorizedApiBase {
|
||||
return this.transformOptions(options_).then(transformedOptions_ => {
|
||||
return this.http.fetch(url_, transformedOptions_);
|
||||
}).then((_response: Response) => {
|
||||
return this.processData_GetCandles(_response);
|
||||
return this.processData_GetCandlesWithIndicators(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processData_GetCandles(response: Response): Promise<Candle[]> {
|
||||
protected processData_GetCandlesWithIndicators(response: Response): Promise<CandlesWithIndicatorsResponse> {
|
||||
const status = response.status;
|
||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||
if (status === 200) {
|
||||
return response.text().then((_responseText) => {
|
||||
let result200: any = null;
|
||||
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as Candle[];
|
||||
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as CandlesWithIndicatorsResponse;
|
||||
return result200;
|
||||
});
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
@@ -1135,7 +1123,7 @@ export class DataClient extends AuthorizedApiBase {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<Candle[]>(null as any);
|
||||
return Promise.resolve<CandlesWithIndicatorsResponse>(null as any);
|
||||
}
|
||||
|
||||
data_GetStrategiesStatistics(): Promise<StrategiesStatisticsViewModel> {
|
||||
@@ -3470,6 +3458,7 @@ export interface RunBacktestRequest {
|
||||
startDate?: Date;
|
||||
endDate?: Date;
|
||||
save?: boolean;
|
||||
withCandles?: boolean;
|
||||
}
|
||||
|
||||
export interface TradingBotConfigRequest {
|
||||
@@ -3589,6 +3578,19 @@ export interface TickerSignal {
|
||||
oneDay: Signal[];
|
||||
}
|
||||
|
||||
export interface CandlesWithIndicatorsResponse {
|
||||
candles?: Candle[] | null;
|
||||
indicatorsValues?: { [key in keyof typeof IndicatorType]?: IndicatorsResultBase; } | null;
|
||||
}
|
||||
|
||||
export interface GetCandlesWithIndicatorsRequest {
|
||||
ticker?: Ticker;
|
||||
startDate?: Date;
|
||||
endDate?: Date;
|
||||
timeframe?: Timeframe;
|
||||
scenario?: ScenarioRequest | null;
|
||||
}
|
||||
|
||||
export interface StrategiesStatisticsViewModel {
|
||||
totalStrategiesRunning?: number;
|
||||
changeInLast24Hours?: number;
|
||||
|
||||
@@ -598,6 +598,7 @@ export interface RunBacktestRequest {
|
||||
startDate?: Date;
|
||||
endDate?: Date;
|
||||
save?: boolean;
|
||||
withCandles?: boolean;
|
||||
}
|
||||
|
||||
export interface TradingBotConfigRequest {
|
||||
@@ -717,6 +718,19 @@ export interface TickerSignal {
|
||||
oneDay: Signal[];
|
||||
}
|
||||
|
||||
export interface CandlesWithIndicatorsResponse {
|
||||
candles?: Candle[] | null;
|
||||
indicatorsValues?: { [key in keyof typeof IndicatorType]?: IndicatorsResultBase; } | null;
|
||||
}
|
||||
|
||||
export interface GetCandlesWithIndicatorsRequest {
|
||||
ticker?: Ticker;
|
||||
startDate?: Date;
|
||||
endDate?: Date;
|
||||
timeframe?: Timeframe;
|
||||
scenario?: ScenarioRequest | null;
|
||||
}
|
||||
|
||||
export interface StrategiesStatisticsViewModel {
|
||||
totalStrategiesRunning?: number;
|
||||
changeInLast24Hours?: number;
|
||||
|
||||
Reference in New Issue
Block a user