Fix backtest spot

This commit is contained in:
2025-12-03 16:47:32 +07:00
parent c932fef289
commit a07d7ede18
6 changed files with 56 additions and 44 deletions

View File

@@ -536,7 +536,7 @@ public class AgentGrain : Grain, IAgentGrain
} }
// Check each bot for open positions // Check each bot for open positions
foreach (var botEntry in userBots) foreach (var botEntry in userBots.Where(b => b.Status == BotStatus.Running))
{ {
try try
{ {

View File

@@ -173,6 +173,18 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
{ {
// Handle fallback for empty AccountName before creating the trading bot instance // Handle fallback for empty AccountName before creating the trading bot instance
var config = _state.State.Config; var config = _state.State.Config;
if (config.TradingType == TradingType.BacktestFutures)
{
config.TradingType = TradingType.Futures;
_state.State.Config = config;
}
else if (config.TradingType == TradingType.BacktestSpot)
{
config.TradingType = TradingType.Spot;
_state.State.Config = config;
}
if (string.IsNullOrEmpty(config.AccountName)) if (string.IsNullOrEmpty(config.AccountName))
{ {
// Fallback: Get the first account for the user // Fallback: Get the first account for the user

View File

@@ -33,6 +33,12 @@ public class BundleBacktestUniversalConfig
[Required] [Required]
public string BotName { get; set; } = string.Empty; public string BotName { get; set; } = string.Empty;
/// <summary>
/// The type of trading (spot or futures)
/// </summary>
[Required]
public TradingType TradingType { get; set; }
/// <summary> /// <summary>
/// Whether to flip positions /// Whether to flip positions
/// </summary> /// </summary>

View File

@@ -3986,45 +3986,6 @@ export class TradingClient extends AuthorizedApiBase {
return Promise.resolve<Trade>(null as any); return Promise.resolve<Trade>(null as any);
} }
trading_ClosePosition(identifier: string | undefined): Promise<Position> {
let url_ = this.baseUrl + "/Trading/ClosePosition?";
if (identifier === null)
throw new Error("The parameter 'identifier' cannot be null.");
else if (identifier !== undefined)
url_ += "identifier=" + encodeURIComponent("" + identifier) + "&";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
method: "POST",
headers: {
"Accept": "application/json"
}
};
return this.transformOptions(options_).then(transformedOptions_ => {
return this.http.fetch(url_, transformedOptions_);
}).then((_response: Response) => {
return this.processTrading_ClosePosition(_response);
});
}
protected processTrading_ClosePosition(response: Response): Promise<Position> {
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 Position;
return result200;
});
} else if (status !== 200 && status !== 204) {
return response.text().then((_responseText) => {
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
});
}
return Promise.resolve<Position>(null as any);
}
trading_Trade(accountName: string | null | undefined, moneyManagementName: string | null | undefined, direction: TradeDirection | undefined, ticker: Ticker | undefined, riskLevel: RiskLevel | undefined, isForPaperTrading: boolean | undefined, openPrice: number | null | undefined, moneyManagement: MoneyManagement | undefined): Promise<Position> { trading_Trade(accountName: string | null | undefined, moneyManagementName: string | null | undefined, direction: TradeDirection | undefined, ticker: Ticker | undefined, riskLevel: RiskLevel | undefined, isForPaperTrading: boolean | undefined, openPrice: number | null | undefined, moneyManagement: MoneyManagement | undefined): Promise<Position> {
let url_ = this.baseUrl + "/Trading/OpenPosition?"; let url_ = this.baseUrl + "/Trading/OpenPosition?";
if (accountName !== undefined && accountName !== null) if (accountName !== undefined && accountName !== null)
@@ -4883,7 +4844,7 @@ export interface TradingBotConfig {
timeframe: Timeframe; timeframe: Timeframe;
isForWatchingOnly: boolean; isForWatchingOnly: boolean;
botTradingBalance: number; botTradingBalance: number;
isForBacktest: boolean; tradingType: TradingType;
cooldownPeriod: number; cooldownPeriod: number;
maxLossStreak: number; maxLossStreak: number;
flipPosition: boolean; flipPosition: boolean;
@@ -4921,6 +4882,13 @@ export enum Timeframe {
OneMinute = "OneMinute", OneMinute = "OneMinute",
} }
export enum TradingType {
Futures = "Futures",
BacktestFutures = "BacktestFutures",
BacktestSpot = "BacktestSpot",
Spot = "Spot",
}
export interface RiskManagement { export interface RiskManagement {
adverseProbabilityThreshold: number; adverseProbabilityThreshold: number;
favorableProbabilityThreshold: number; favorableProbabilityThreshold: number;
@@ -5022,6 +4990,7 @@ export interface Position {
user: User; user: User;
initiatorIdentifier: string; initiatorIdentifier: string;
recoveryAttempted?: boolean; recoveryAttempted?: boolean;
tradingType?: TradingType;
} }
export enum TradeDirection { export enum TradeDirection {
@@ -5332,6 +5301,7 @@ export interface BundleBacktestUniversalConfig {
flipPosition: boolean; flipPosition: boolean;
cooldownPeriod?: number | null; cooldownPeriod?: number | null;
maxLossStreak?: number; maxLossStreak?: number;
tradingType?: TradingType;
scenario?: ScenarioRequest | null; scenario?: ScenarioRequest | null;
scenarioName?: string | null; scenarioName?: string | null;
maxPositionTimeHours?: number | null; maxPositionTimeHours?: number | null;

View File

@@ -349,7 +349,7 @@ export interface TradingBotConfig {
timeframe: Timeframe; timeframe: Timeframe;
isForWatchingOnly: boolean; isForWatchingOnly: boolean;
botTradingBalance: number; botTradingBalance: number;
isForBacktest: boolean; tradingType: TradingType;
cooldownPeriod: number; cooldownPeriod: number;
maxLossStreak: number; maxLossStreak: number;
flipPosition: boolean; flipPosition: boolean;
@@ -387,6 +387,13 @@ export enum Timeframe {
OneMinute = "OneMinute", OneMinute = "OneMinute",
} }
export enum TradingType {
Futures = "Futures",
BacktestFutures = "BacktestFutures",
BacktestSpot = "BacktestSpot",
Spot = "Spot",
}
export interface RiskManagement { export interface RiskManagement {
adverseProbabilityThreshold: number; adverseProbabilityThreshold: number;
favorableProbabilityThreshold: number; favorableProbabilityThreshold: number;
@@ -488,6 +495,7 @@ export interface Position {
user: User; user: User;
initiatorIdentifier: string; initiatorIdentifier: string;
recoveryAttempted?: boolean; recoveryAttempted?: boolean;
tradingType?: TradingType;
} }
export enum TradeDirection { export enum TradeDirection {

View File

@@ -13,7 +13,8 @@ import {
RunBundleBacktestRequest, RunBundleBacktestRequest,
SignalType, SignalType,
Ticker, Ticker,
Timeframe Timeframe,
TradingType
} from '../../generated/ManagingApi'; } from '../../generated/ManagingApi';
import useApiUrlStore from '../../app/store/apiStore'; import useApiUrlStore from '../../app/store/apiStore';
import Toast from '../../components/mollecules/Toast/Toast'; import Toast from '../../components/mollecules/Toast/Toast';
@@ -49,6 +50,7 @@ const BundleRequestModal: React.FC<BundleRequestModalProps> = ({
const [selectedTimeframe, setSelectedTimeframe] = useState<Timeframe>(Timeframe.FifteenMinutes); const [selectedTimeframe, setSelectedTimeframe] = useState<Timeframe>(Timeframe.FifteenMinutes);
const [selectedTickers, setSelectedTickers] = useState<Ticker[]>([]); const [selectedTickers, setSelectedTickers] = useState<Ticker[]>([]);
const [startingCapital, setStartingCapital] = useState<number>(10000); const [startingCapital, setStartingCapital] = useState<number>(10000);
const [selectedTradingType, setSelectedTradingType] = useState<TradingType>(TradingType.BacktestSpot);
// Advanced parameters state // Advanced parameters state
const [cooldownPeriod, setCooldownPeriod] = useState<number>(0); const [cooldownPeriod, setCooldownPeriod] = useState<number>(0);
@@ -220,6 +222,7 @@ const BundleRequestModal: React.FC<BundleRequestModalProps> = ({
flipPosition: flipPosition, flipPosition: flipPosition,
cooldownPeriod: cooldownPeriod, cooldownPeriod: cooldownPeriod,
maxLossStreak: maxLossStreak, maxLossStreak: maxLossStreak,
tradingType: selectedTradingType,
scenario: scenario ? { scenario: scenario ? {
name: scenario.name || 'Custom Scenario', name: scenario.name || 'Custom Scenario',
indicators: (scenario.indicators || []).map(indicator => ({ indicators: (scenario.indicators || []).map(indicator => ({
@@ -516,6 +519,19 @@ const BundleRequestModal: React.FC<BundleRequestModalProps> = ({
</select> </select>
</FormInput> </FormInput>
{/* Trading Type Selection */}
<FormInput label="Select trading type" htmlFor="tradingType">
<p className="text-sm text-gray-600 mb-2">Choose between spot or futures backtesting</p>
<select
className="select select-bordered w-full"
value={selectedTradingType}
onChange={(e) => setSelectedTradingType(e.target.value as TradingType)}
>
<option value={TradingType.BacktestSpot}>Spot</option>
<option value={TradingType.BacktestFutures}>Futures</option>
</select>
</FormInput>
{/* Money Management Variants */} {/* Money Management Variants */}
<div> <div>
<h4 className="font-semibold mb-2">Choose your money management approach(s)</h4> <h4 className="font-semibold mb-2">Choose your money management approach(s)</h4>