diff --git a/src/Managing.Application/Bots/Grains/AgentGrain.cs b/src/Managing.Application/Bots/Grains/AgentGrain.cs index 9d522699..1eb2d07a 100644 --- a/src/Managing.Application/Bots/Grains/AgentGrain.cs +++ b/src/Managing.Application/Bots/Grains/AgentGrain.cs @@ -536,7 +536,7 @@ public class AgentGrain : Grain, IAgentGrain } // Check each bot for open positions - foreach (var botEntry in userBots) + foreach (var botEntry in userBots.Where(b => b.Status == BotStatus.Running)) { try { diff --git a/src/Managing.Application/Bots/Grains/LiveTradingBotGrain.cs b/src/Managing.Application/Bots/Grains/LiveTradingBotGrain.cs index a58b4f51..3c63efad 100644 --- a/src/Managing.Application/Bots/Grains/LiveTradingBotGrain.cs +++ b/src/Managing.Application/Bots/Grains/LiveTradingBotGrain.cs @@ -173,6 +173,18 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable { // Handle fallback for empty AccountName before creating the trading bot instance 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)) { // Fallback: Get the first account for the user @@ -531,7 +543,7 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable using var scope = _scopeFactory.CreateScope(); var logger = scope.ServiceProvider.GetRequiredService>(); var streamProvider = this.GetStreamProvider("ManagingStreamProvider"); - + // Create the trading bot instance based on TradingType TradingBotBase tradingBot = config.TradingType switch { diff --git a/src/Managing.Domain/Backtests/BundleBacktestUniversalConfig.cs b/src/Managing.Domain/Backtests/BundleBacktestUniversalConfig.cs index 4aa95855..fa3daf18 100644 --- a/src/Managing.Domain/Backtests/BundleBacktestUniversalConfig.cs +++ b/src/Managing.Domain/Backtests/BundleBacktestUniversalConfig.cs @@ -33,6 +33,12 @@ public class BundleBacktestUniversalConfig [Required] public string BotName { get; set; } = string.Empty; + /// + /// The type of trading (spot or futures) + /// + [Required] + public TradingType TradingType { get; set; } + /// /// Whether to flip positions /// diff --git a/src/Managing.WebApp/src/generated/ManagingApi.ts b/src/Managing.WebApp/src/generated/ManagingApi.ts index 9b15f1e7..ebedf224 100644 --- a/src/Managing.WebApp/src/generated/ManagingApi.ts +++ b/src/Managing.WebApp/src/generated/ManagingApi.ts @@ -3986,45 +3986,6 @@ export class TradingClient extends AuthorizedApiBase { return Promise.resolve(null as any); } - trading_ClosePosition(identifier: string | undefined): Promise { - 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 { - 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(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 { let url_ = this.baseUrl + "/Trading/OpenPosition?"; if (accountName !== undefined && accountName !== null) @@ -4883,7 +4844,7 @@ export interface TradingBotConfig { timeframe: Timeframe; isForWatchingOnly: boolean; botTradingBalance: number; - isForBacktest: boolean; + tradingType: TradingType; cooldownPeriod: number; maxLossStreak: number; flipPosition: boolean; @@ -4921,6 +4882,13 @@ export enum Timeframe { OneMinute = "OneMinute", } +export enum TradingType { + Futures = "Futures", + BacktestFutures = "BacktestFutures", + BacktestSpot = "BacktestSpot", + Spot = "Spot", +} + export interface RiskManagement { adverseProbabilityThreshold: number; favorableProbabilityThreshold: number; @@ -5022,6 +4990,7 @@ export interface Position { user: User; initiatorIdentifier: string; recoveryAttempted?: boolean; + tradingType?: TradingType; } export enum TradeDirection { @@ -5332,6 +5301,7 @@ export interface BundleBacktestUniversalConfig { flipPosition: boolean; cooldownPeriod?: number | null; maxLossStreak?: number; + tradingType?: TradingType; scenario?: ScenarioRequest | null; scenarioName?: string | null; maxPositionTimeHours?: number | null; diff --git a/src/Managing.WebApp/src/generated/ManagingApiTypes.ts b/src/Managing.WebApp/src/generated/ManagingApiTypes.ts index 345f97ed..b72d0461 100644 --- a/src/Managing.WebApp/src/generated/ManagingApiTypes.ts +++ b/src/Managing.WebApp/src/generated/ManagingApiTypes.ts @@ -349,7 +349,7 @@ export interface TradingBotConfig { timeframe: Timeframe; isForWatchingOnly: boolean; botTradingBalance: number; - isForBacktest: boolean; + tradingType: TradingType; cooldownPeriod: number; maxLossStreak: number; flipPosition: boolean; @@ -387,6 +387,13 @@ export enum Timeframe { OneMinute = "OneMinute", } +export enum TradingType { + Futures = "Futures", + BacktestFutures = "BacktestFutures", + BacktestSpot = "BacktestSpot", + Spot = "Spot", +} + export interface RiskManagement { adverseProbabilityThreshold: number; favorableProbabilityThreshold: number; @@ -488,6 +495,7 @@ export interface Position { user: User; initiatorIdentifier: string; recoveryAttempted?: boolean; + tradingType?: TradingType; } export enum TradeDirection { diff --git a/src/Managing.WebApp/src/pages/backtestPage/BundleRequestModal.tsx b/src/Managing.WebApp/src/pages/backtestPage/BundleRequestModal.tsx index 994b167d..aef6457b 100644 --- a/src/Managing.WebApp/src/pages/backtestPage/BundleRequestModal.tsx +++ b/src/Managing.WebApp/src/pages/backtestPage/BundleRequestModal.tsx @@ -13,7 +13,8 @@ import { RunBundleBacktestRequest, SignalType, Ticker, - Timeframe + Timeframe, + TradingType } from '../../generated/ManagingApi'; import useApiUrlStore from '../../app/store/apiStore'; import Toast from '../../components/mollecules/Toast/Toast'; @@ -49,6 +50,7 @@ const BundleRequestModal: React.FC = ({ const [selectedTimeframe, setSelectedTimeframe] = useState(Timeframe.FifteenMinutes); const [selectedTickers, setSelectedTickers] = useState([]); const [startingCapital, setStartingCapital] = useState(10000); + const [selectedTradingType, setSelectedTradingType] = useState(TradingType.BacktestSpot); // Advanced parameters state const [cooldownPeriod, setCooldownPeriod] = useState(0); @@ -220,6 +222,7 @@ const BundleRequestModal: React.FC = ({ flipPosition: flipPosition, cooldownPeriod: cooldownPeriod, maxLossStreak: maxLossStreak, + tradingType: selectedTradingType, scenario: scenario ? { name: scenario.name || 'Custom Scenario', indicators: (scenario.indicators || []).map(indicator => ({ @@ -516,6 +519,19 @@ const BundleRequestModal: React.FC = ({ + {/* Trading Type Selection */} + +

Choose between spot or futures backtesting

+ +
+ {/* Money Management Variants */}

Choose your money management approach(s)