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
foreach (var botEntry in userBots)
foreach (var botEntry in userBots.Where(b => b.Status == BotStatus.Running))
{
try
{

View File

@@ -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<ILogger<TradingBotBase>>();
var streamProvider = this.GetStreamProvider("ManagingStreamProvider");
// Create the trading bot instance based on TradingType
TradingBotBase tradingBot = config.TradingType switch
{

View File

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

View File

@@ -3986,45 +3986,6 @@ export class TradingClient extends AuthorizedApiBase {
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> {
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;

View File

@@ -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 {

View File

@@ -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<BundleRequestModalProps> = ({
const [selectedTimeframe, setSelectedTimeframe] = useState<Timeframe>(Timeframe.FifteenMinutes);
const [selectedTickers, setSelectedTickers] = useState<Ticker[]>([]);
const [startingCapital, setStartingCapital] = useState<number>(10000);
const [selectedTradingType, setSelectedTradingType] = useState<TradingType>(TradingType.BacktestSpot);
// Advanced parameters state
const [cooldownPeriod, setCooldownPeriod] = useState<number>(0);
@@ -220,6 +222,7 @@ const BundleRequestModal: React.FC<BundleRequestModalProps> = ({
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<BundleRequestModalProps> = ({
</select>
</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 */}
<div>
<h4 className="font-semibold mb-2">Choose your money management approach(s)</h4>