Add MasterBotUserId and MasterAgentName for copy trading support
- Introduced MasterBotUserId and MasterAgentName properties to facilitate copy trading functionality. - Updated relevant models, controllers, and database entities to accommodate these new properties. - Enhanced validation logic in StartCopyTradingCommandHandler to ensure proper ownership checks for master strategies.
This commit is contained in:
@@ -993,6 +993,7 @@ public class DataController : ControllerBase
|
|||||||
StartupTime = item.StartupTime,
|
StartupTime = item.StartupTime,
|
||||||
Name = item.Name,
|
Name = item.Name,
|
||||||
Ticker = item.Ticker,
|
Ticker = item.Ticker,
|
||||||
|
MasterAgentName = item.MasterBotUser?.AgentName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,5 +81,10 @@ namespace Managing.Api.Models.Responses
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[Required]
|
[Required]
|
||||||
public Ticker Ticker { get; set; }
|
public Ticker Ticker { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The agent name of the master bot's owner (for copy trading bots)
|
||||||
|
/// </summary>
|
||||||
|
public string MasterAgentName { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -930,7 +930,8 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
|
|||||||
Pnl = 0,
|
Pnl = 0,
|
||||||
Roi = 0,
|
Roi = 0,
|
||||||
Volume = 0,
|
Volume = 0,
|
||||||
Fees = 0
|
Fees = 0,
|
||||||
|
MasterBotUserId = _state.State.Config.MasterBotUserId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -993,7 +994,8 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
|
|||||||
Volume = agentMetrics.TotalVolume,
|
Volume = agentMetrics.TotalVolume,
|
||||||
Fees = agentMetrics.TotalFees,
|
Fees = agentMetrics.TotalFees,
|
||||||
LongPositionCount = longPositionCount,
|
LongPositionCount = longPositionCount,
|
||||||
ShortPositionCount = shortPositionCount
|
ShortPositionCount = shortPositionCount,
|
||||||
|
MasterBotUserId = _state.State.Config.MasterBotUserId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using Managing.Domain.Accounts;
|
|||||||
using Managing.Domain.Bots;
|
using Managing.Domain.Bots;
|
||||||
using Managing.Domain.Users;
|
using Managing.Domain.Users;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
using System;
|
||||||
using static Managing.Common.Enums;
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
namespace Managing.Application.ManageBot
|
namespace Managing.Application.ManageBot
|
||||||
@@ -47,6 +48,12 @@ namespace Managing.Application.ManageBot
|
|||||||
throw new ArgumentException($"Master bot with identifier {request.MasterBotIdentifier} not found");
|
throw new ArgumentException($"Master bot with identifier {request.MasterBotIdentifier} not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if copy trading validation should be bypassed (for testing)
|
||||||
|
var enableValidation = Environment.GetEnvironmentVariable("ENABLE_COPY_TRADING_VALIDATION")?
|
||||||
|
.Equals("true", StringComparison.OrdinalIgnoreCase) == true;
|
||||||
|
|
||||||
|
if (enableValidation)
|
||||||
|
{
|
||||||
// Special validation for Kudai strategy - check staking requirements
|
// Special validation for Kudai strategy - check staking requirements
|
||||||
if (string.Equals(request.MasterBotIdentifier.ToString(), "Kudai", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(request.MasterBotIdentifier.ToString(), "Kudai", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
@@ -67,6 +74,7 @@ namespace Managing.Application.ManageBot
|
|||||||
"You must own at least 1 key for this strategy to copy trade from it.");
|
"You must own at least 1 key for this strategy to copy trade from it.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get the master bot configuration
|
// Get the master bot configuration
|
||||||
var masterConfig = await _botService.GetBotConfig(request.MasterBotIdentifier);
|
var masterConfig = await _botService.GetBotConfig(request.MasterBotIdentifier);
|
||||||
@@ -156,6 +164,7 @@ namespace Managing.Application.ManageBot
|
|||||||
// Set copy trading specific properties
|
// Set copy trading specific properties
|
||||||
IsForCopyTrading = true,
|
IsForCopyTrading = true,
|
||||||
MasterBotIdentifier = request.MasterBotIdentifier,
|
MasterBotIdentifier = request.MasterBotIdentifier,
|
||||||
|
MasterBotUserId = masterBot.User.Id,
|
||||||
|
|
||||||
// Set computed/default properties
|
// Set computed/default properties
|
||||||
IsForBacktest = false,
|
IsForBacktest = false,
|
||||||
|
|||||||
@@ -28,6 +28,16 @@ namespace Managing.Domain.Bots
|
|||||||
public int LongPositionCount { get; set; }
|
public int LongPositionCount { get; set; }
|
||||||
public int ShortPositionCount { get; set; }
|
public int ShortPositionCount { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The user ID of the master bot's owner when this bot is for copy trading
|
||||||
|
/// </summary>
|
||||||
|
public int? MasterBotUserId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The user object of the master bot's owner when this bot is for copy trading
|
||||||
|
/// </summary>
|
||||||
|
public User MasterBotUser { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the total runtime in seconds, including the current session if the bot is running
|
/// Gets the total runtime in seconds, including the current session if the bot is running
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -115,4 +115,10 @@ public class TradingBotConfig
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[Id(22)]
|
[Id(22)]
|
||||||
public Guid? MasterBotIdentifier { get; set; }
|
public Guid? MasterBotIdentifier { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The user ID of the master bot's owner when IsForCopyTrading is true
|
||||||
|
/// </summary>
|
||||||
|
[Id(23)]
|
||||||
|
public int? MasterBotUserId { get; set; }
|
||||||
}
|
}
|
||||||
1732
src/Managing.Infrastructure.Database/Migrations/20251119165943_AddMasterBotUserIdToBots.Designer.cs
generated
Normal file
1732
src/Managing.Infrastructure.Database/Migrations/20251119165943_AddMasterBotUserIdToBots.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,28 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Managing.Infrastructure.Databases.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddMasterBotUserIdToBots : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "MasterBotUserId",
|
||||||
|
table: "Bots",
|
||||||
|
type: "integer",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "MasterBotUserId",
|
||||||
|
table: "Bots");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -318,6 +318,9 @@ namespace Managing.Infrastructure.Databases.Migrations
|
|||||||
b.Property<int>("LongPositionCount")
|
b.Property<int>("LongPositionCount")
|
||||||
.HasColumnType("integer");
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int?>("MasterBotUserId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(255)
|
.HasMaxLength(255)
|
||||||
|
|||||||
@@ -36,4 +36,15 @@ public class BotEntity
|
|||||||
public decimal Fees { get; set; }
|
public decimal Fees { get; set; }
|
||||||
public int LongPositionCount { get; set; }
|
public int LongPositionCount { get; set; }
|
||||||
public int ShortPositionCount { get; set; }
|
public int ShortPositionCount { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The user ID of the master bot's owner when this bot is for copy trading
|
||||||
|
/// </summary>
|
||||||
|
[ForeignKey("MasterBotUser")]
|
||||||
|
public int? MasterBotUserId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Navigation property for the master bot's owner when this bot is for copy trading
|
||||||
|
/// </summary>
|
||||||
|
public virtual UserEntity? MasterBotUser { get; set; }
|
||||||
}
|
}
|
||||||
@@ -548,6 +548,12 @@ public class ManagingDbContext : DbContext
|
|||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey(e => e.UserId)
|
.HasForeignKey(e => e.UserId)
|
||||||
.OnDelete(DeleteBehavior.SetNull);
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
// Configure relationship with MasterBotUser
|
||||||
|
entity.HasOne(e => e.MasterBotUser)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(e => e.MasterBotUserId)
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Configure MoneyManagement entity
|
// Configure MoneyManagement entity
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ public class PostgreSqlBotRepository : IBotRepository
|
|||||||
existingEntity.LastStartTime = bot.LastStartTime;
|
existingEntity.LastStartTime = bot.LastStartTime;
|
||||||
existingEntity.LastStopTime = bot.LastStopTime;
|
existingEntity.LastStopTime = bot.LastStopTime;
|
||||||
existingEntity.AccumulatedRunTimeSeconds = bot.AccumulatedRunTimeSeconds;
|
existingEntity.AccumulatedRunTimeSeconds = bot.AccumulatedRunTimeSeconds;
|
||||||
|
existingEntity.MasterBotUserId = bot.MasterBotUserId;
|
||||||
|
|
||||||
await _context.SaveChangesAsync().ConfigureAwait(false);
|
await _context.SaveChangesAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@@ -114,10 +115,26 @@ public class PostgreSqlBotRepository : IBotRepository
|
|||||||
var entities = await _context.Bots
|
var entities = await _context.Bots
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Include(m => m.User)
|
.Include(m => m.User)
|
||||||
|
.Include(m => m.MasterBotUser)
|
||||||
.Where(b => b.UserId == id)
|
.Where(b => b.UserId == id)
|
||||||
.ToListAsync()
|
.ToListAsync()
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
return PostgreSqlMappers.Map(entities);
|
|
||||||
|
// Map entities to domain objects
|
||||||
|
var bots = PostgreSqlMappers.Map(entities).ToList();
|
||||||
|
|
||||||
|
// Attach master bot users to domain objects
|
||||||
|
foreach (var entity in entities.Where(e => e.MasterBotUser != null))
|
||||||
|
{
|
||||||
|
var bot = bots.FirstOrDefault(b => b.MasterBotUserId == entity.MasterBotUserId);
|
||||||
|
if (bot != null)
|
||||||
|
{
|
||||||
|
// Convert UserEntity to User domain object
|
||||||
|
bot.MasterBotUser = PostgreSqlMappers.Map(entity.MasterBotUser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bots;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<Bot>> GetBotsByStatusAsync(BotStatus status)
|
public async Task<IEnumerable<Bot>> GetBotsByStatusAsync(BotStatus status)
|
||||||
|
|||||||
@@ -753,7 +753,8 @@ public static class PostgreSqlMappers
|
|||||||
Volume = entity.Volume,
|
Volume = entity.Volume,
|
||||||
Fees = entity.Fees,
|
Fees = entity.Fees,
|
||||||
LongPositionCount = entity.LongPositionCount,
|
LongPositionCount = entity.LongPositionCount,
|
||||||
ShortPositionCount = entity.ShortPositionCount
|
ShortPositionCount = entity.ShortPositionCount,
|
||||||
|
MasterBotUserId = entity.MasterBotUserId
|
||||||
};
|
};
|
||||||
|
|
||||||
return bot;
|
return bot;
|
||||||
@@ -784,6 +785,7 @@ public static class PostgreSqlMappers
|
|||||||
Fees = bot.Fees,
|
Fees = bot.Fees,
|
||||||
LongPositionCount = bot.LongPositionCount,
|
LongPositionCount = bot.LongPositionCount,
|
||||||
ShortPositionCount = bot.ShortPositionCount,
|
ShortPositionCount = bot.ShortPositionCount,
|
||||||
|
MasterBotUserId = bot.MasterBotUserId,
|
||||||
UpdatedAt = DateTime.UtcNow
|
UpdatedAt = DateTime.UtcNow
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user