Files
managing-apps/Plan.md
Oda 3de8b5e00e Orlean (#32)
* Start building with orlean

* Add missing file

* Serialize grain state

* Remove grain and proxies

* update and add plan

* Update a bit

* Fix backtest grain

* Fix backtest grain

* Clean a bit
2025-07-30 16:03:30 +07:00

255 lines
8.9 KiB
Markdown

# Orleans Migration Plan for Managing Apps Trading Bot
## Overview
Migrate the `TradingBot` class to Microsoft Orleans grains for improved performance, scalability, and high availability while maintaining backward compatibility with the `Backtester` class.
## Current Architecture Analysis
### TradingBot Key Characteristics
- Long-running stateful service with complex state (positions, signals, candles, indicators)
- Timer-based execution via `InitWorker(Run)`
- Dependency injection via `IServiceScopeFactory`
- Persistence via `SaveBackup()` and `LoadBackup()`
- SignalR integration for real-time updates
### Backtester Requirements
- Creates TradingBot instances as regular classes (line 198: `_botFactory.CreateBacktestTradingBot`)
- Runs synchronous backtesting without Orleans overhead
- Needs direct object manipulation for performance
## 1. Orleans Grain Design
### A. Create ITradingBotGrain Interface
```csharp
// src/Managing.Application.Abstractions/Grains/ITradingBotGrain.cs
public interface ITradingBotGrain : IGrainWithStringKey
{
Task StartAsync();
Task StopAsync();
Task<BotStatus> GetStatusAsync();
Task<TradingBotConfig> GetConfigurationAsync();
Task<bool> UpdateConfigurationAsync(TradingBotConfig newConfig);
Task<Position> OpenPositionManuallyAsync(TradeDirection direction);
Task ToggleIsForWatchOnlyAsync();
Task<TradingBotResponse> GetBotDataAsync();
Task LoadBackupAsync(BotBackup backup);
}
```
### B. Modify TradingBot Class
```csharp
// src/Managing.Application/Bots/TradingBot.cs
public class TradingBot : Grain, ITradingBotGrain, ITradingBot
{
// Keep existing implementation but add Orleans-specific methods
// Add grain lifecycle management
// Replace IServiceScopeFactory with Orleans DI
}
```
## 2. Program.cs Orleans Configuration
Add to `src/Managing.Api/Program.cs` after line 188:
```csharp
// Orleans Configuration
builder.Host.UseOrleans(siloBuilder =>
{
siloBuilder
.UseLocalhostClustering() // For local development
.ConfigureLogging(logging => logging.AddConsole())
.UseDashboard(options => { options.Port = 8080; })
.AddMemoryGrainStorageAsDefault()
.ConfigureServices(services =>
{
// Register existing services for Orleans DI
services.AddSingleton<IExchangeService, ExchangeService>();
services.AddSingleton<IAccountService, AccountService>();
services.AddSingleton<ITradingService, TradingService>();
services.AddSingleton<IMessengerService, MessengerService>();
services.AddSingleton<IBackupBotService, BackupBotService>();
});
// Production clustering configuration
if (builder.Environment.IsProduction())
{
siloBuilder
.UseAdoNetClustering(options =>
{
options.ConnectionString = postgreSqlConnectionString;
options.Invariant = "Npgsql";
})
.UseAdoNetReminderService(options =>
{
options.ConnectionString = postgreSqlConnectionString;
options.Invariant = "Npgsql";
});
}
});
// Orleans Client Configuration (for accessing grains from controllers)
builder.Services.AddOrleansClient(clientBuilder =>
{
clientBuilder.UseLocalhostClustering();
if (builder.Environment.IsProduction())
{
clientBuilder.UseAdoNetClustering(options =>
{
options.ConnectionString = postgreSqlConnectionString;
options.Invariant = "Npgsql";
});
}
});
```
## 3. Conditional Bot Instantiation Strategy
### A. Enhanced BotFactory Pattern
```csharp
// src/Managing.Application/Bots/Base/BotFactory.cs
public class BotFactory : IBotFactory
{
private readonly IClusterClient _clusterClient;
private readonly IServiceProvider _serviceProvider;
public async Task<ITradingBot> CreateTradingBotAsync(TradingBotConfig config, bool useGrain = true)
{
if (config.IsForBacktest || !useGrain)
{
// For backtesting: Create regular class instance
return new TradingBot(
_serviceProvider.GetService<ILogger<TradingBot>>(),
_serviceProvider.GetService<IServiceScopeFactory>(),
config
);
}
else
{
// For live trading: Use Orleans grain
var grain = _clusterClient.GetGrain<ITradingBotGrain>(config.Name);
return new TradingBotGrainProxy(grain, config);
}
}
}
```
### B. TradingBotGrainProxy (Adapter Pattern)
```csharp
// src/Managing.Application/Bots/TradingBotGrainProxy.cs
public class TradingBotGrainProxy : ITradingBot
{
private readonly ITradingBotGrain _grain;
private TradingBotConfig _config;
public TradingBotGrainProxy(ITradingBotGrain grain, TradingBotConfig config)
{
_grain = grain;
_config = config;
}
public async Task Start() => await _grain.StartAsync();
public async Task Stop() => await _grain.StopAsync();
// Implement all ITradingBot methods by delegating to grain
// This maintains compatibility with existing bot management code
}
```
### C. Backtester Compatibility
In `Backtester.cs` (line 198), the factory call remains unchanged:
```csharp
// This will automatically create a regular class instance due to IsForBacktest = true
var tradingBot = await _botFactory.CreateBacktestTradingBot(config);
```
## 4. Orleans Grain State Management
```csharp
// src/Managing.Application/Bots/TradingBotGrainState.cs
[GenerateSerializer]
public class TradingBotGrainState
{
[Id(0)] public TradingBotConfig Config { get; set; }
[Id(1)] public HashSet<LightSignal> Signals { get; set; }
[Id(2)] public List<Position> Positions { get; set; }
[Id(3)] public Dictionary<DateTime, decimal> WalletBalances { get; set; }
[Id(4)] public BotStatus Status { get; set; }
[Id(5)] public DateTime StartupTime { get; set; }
[Id(6)] public DateTime CreateDate { get; set; }
}
// Updated TradingBot grain
public class TradingBot : Grain<TradingBotGrainState>, ITradingBotGrain
{
private IDisposable _timer;
public override async Task OnActivateAsync(CancellationToken cancellationToken)
{
// Initialize grain state and start timer-based execution
if (State.Config != null && State.Status == BotStatus.Running)
{
await StartTimerAsync();
}
}
private async Task StartTimerAsync()
{
var interval = CandleExtensions.GetIntervalFromTimeframe(State.Config.Timeframe);
_timer = RegisterTimer(async _ => await Run(), null, TimeSpan.Zero, TimeSpan.FromMilliseconds(interval));
}
}
```
## 5. Implementation Roadmap
### Phase 1: Infrastructure Setup
1. **Add Orleans packages** (already done in Managing.Api.csproj)
2. **Configure Orleans in Program.cs** with clustering and persistence
3. **Create grain interfaces and state classes**
### Phase 2: Core Migration
1. **Create ITradingBotGrain interface** with async methods
2. **Modify TradingBot class** to inherit from `Grain<TradingBotGrainState>`
3. **Implement TradingBotGrainProxy** for compatibility
4. **Update BotFactory** with conditional instantiation logic
### Phase 3: Service Integration
1. **Replace IServiceScopeFactory** with Orleans dependency injection
2. **Update timer management** to use Orleans grain timers
3. **Migrate state persistence** from SaveBackup/LoadBackup to Orleans state
4. **Update bot management services** to work with grains
### Phase 4: Testing & Optimization
1. **Test backtesting compatibility** (should remain unchanged)
2. **Performance testing** with multiple concurrent bots
3. **High availability testing** with node failures
4. **Memory and resource optimization**
## Key Benefits
1. **High Availability**: Orleans automatic failover and grain migration
2. **Scalability**: Distributed bot execution across multiple nodes
3. **Performance**: Reduced serialization overhead, efficient state management
4. **Backward Compatibility**: Backtester continues using regular classes
5. **Simplified State Management**: Orleans handles persistence automatically
## Migration Considerations
1. **Async Conversion**: All bot operations become async
2. **State Serialization**: Ensure all state classes are serializable
3. **Timer Management**: Replace custom timers with Orleans grain timers
4. **Dependency Injection**: Adapt from ASP.NET Core DI to Orleans DI
5. **SignalR Integration**: Update to work with distributed grains
## Current Status
- ✅ Orleans package added to Managing.Api.csproj
- ✅ Orleans configuration implemented in Program.cs
- ✅ ITradingBotGrain interface created
- ✅ TradingBotGrainState class created
- ✅ TradingBotGrain implementation completed
- ✅ TradingBotResponse model created
- ✅ TradingBotProxy adapter pattern implemented
- ✅ Original TradingBot class preserved for backtesting
- ✅ BotService conditional logic implemented for all creation methods
- ⏳ Testing Orleans integration