Trading bot grain (#33)

* Trading bot Grain

* Fix a bit more of the trading bot

* Advance on the tradingbot grain

* Fix build

* Fix db script

* Fix user login

* Fix a bit backtest

* Fix cooldown and backtest

* start fixing bot start

* Fix startup

* Setup local db

* Fix build and update candles and scenario

* Add bot registry

* Add reminder

* Updateing the grains

* fix bootstraping

* Save stats on tick

* Save bot data every tick

* Fix serialization

* fix save bot stats

* Fix get candles

* use dict instead of list for position

* Switch hashset to dict

* Fix a bit

* Fix bot launch and bot view

* add migrations

* Remove the tolist

* Add agent grain

* Save agent summary

* clean

* Add save bot

* Update get bots

* Add get bots

* Fix stop/restart

* fix Update config

* Update scanner table on new backtest saved

* Fix backtestRowDetails.tsx

* Fix agentIndex

* Update agentIndex

* Fix more things

* Update user cache

* Fix

* Fix account load/start/restart/run
This commit is contained in:
Oda
2025-08-04 23:07:06 +02:00
committed by GitHub
parent cd378587aa
commit 082ae8714b
215 changed files with 9562 additions and 14028 deletions

View File

@@ -86,8 +86,8 @@ public class PostgreSqlTradingRepository : ITradingRepository
var existingScenario = await _context.Scenarios
.AsNoTracking()
.FirstOrDefaultAsync(s => s.Name == scenario.Name &&
((scenario.User == null && s.UserName == null) ||
(scenario.User != null && s.UserName == scenario.User.Name)));
((scenario.User == null && s.UserName == null) ||
(scenario.User != null && s.UserName == scenario.User.Name)));
if (existingScenario != null)
{
@@ -107,8 +107,8 @@ public class PostgreSqlTradingRepository : ITradingRepository
var indicatorEntity = await _context.Indicators
.AsNoTracking()
.FirstOrDefaultAsync(i => i.Name == indicator.Name &&
((indicator.User == null && i.UserName == null) ||
(indicator.User != null && i.UserName == indicator.User.Name)));
((indicator.User == null && i.UserName == null) ||
(indicator.User != null && i.UserName == indicator.User.Name)));
if (indicatorEntity != null)
{
@@ -120,6 +120,7 @@ public class PostgreSqlTradingRepository : ITradingRepository
_context.ScenarioIndicators.Add(junction);
}
}
await _context.SaveChangesAsync();
}
}
@@ -135,7 +136,7 @@ public class PostgreSqlTradingRepository : ITradingRepository
entity.LoopbackPeriod = scenario.LoopbackPeriod ?? 1;
entity.UserName = scenario.User?.Name;
entity.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
}
}
@@ -149,7 +150,7 @@ public class PostgreSqlTradingRepository : ITradingRepository
var indicator = _context.Indicators
.AsTracking()
.FirstOrDefault(i => i.Name == name);
if (indicator != null)
{
_context.Indicators.Remove(indicator);
@@ -164,7 +165,7 @@ public class PostgreSqlTradingRepository : ITradingRepository
await _context.SaveChangesAsync();
}
public async Task<IEnumerable<Indicator>> GetIndicatorsAsync()
public async Task<IEnumerable<IndicatorBase>> GetIndicatorsAsync()
{
var indicators = await _context.Indicators
.AsNoTracking()
@@ -174,7 +175,7 @@ public class PostgreSqlTradingRepository : ITradingRepository
return PostgreSqlMappers.Map(indicators);
}
public async Task<IEnumerable<Indicator>> GetStrategiesAsync()
public async Task<IEnumerable<IndicatorBase>> GetStrategiesAsync()
{
var indicators = await _context.Indicators
.AsNoTracking()
@@ -183,7 +184,7 @@ public class PostgreSqlTradingRepository : ITradingRepository
return PostgreSqlMappers.Map(indicators);
}
public async Task<Indicator> GetStrategyByNameAsync(string name)
public async Task<IndicatorBase> GetStrategyByNameAsync(string name)
{
var indicator = await _context.Indicators
.AsNoTracking()
@@ -193,48 +194,48 @@ public class PostgreSqlTradingRepository : ITradingRepository
return PostgreSqlMappers.Map(indicator);
}
public async Task InsertStrategyAsync(Indicator indicator)
public async Task InsertIndicatorAsync(IndicatorBase indicatorBase)
{
// Check if indicator already exists for the same user
var existingIndicator = await _context.Indicators
.AsNoTracking()
.FirstOrDefaultAsync(i => i.Name == indicator.Name &&
((indicator.User == null && i.UserName == null) ||
(indicator.User != null && i.UserName == indicator.User.Name)));
.FirstOrDefaultAsync(i => i.Name == indicatorBase.Name &&
((indicatorBase.User == null && i.UserName == null) ||
(indicatorBase.User != null && i.UserName == indicatorBase.User.Name)));
if (existingIndicator != null)
{
throw new InvalidOperationException(
$"Indicator with name '{indicator.Name}' already exists for user '{indicator.User?.Name}'");
$"Indicator with name '{indicatorBase.Name}' already exists for user '{indicatorBase.User?.Name}'");
}
var entity = PostgreSqlMappers.Map(indicator);
var entity = PostgreSqlMappers.Map(indicatorBase);
_context.Indicators.Add(entity);
await _context.SaveChangesAsync();
}
public async Task UpdateStrategyAsync(Indicator indicator)
public async Task UpdateStrategyAsync(IndicatorBase indicatorBase)
{
var entity = _context.Indicators
.AsTracking()
.FirstOrDefault(i => i.Name == indicator.Name);
.FirstOrDefault(i => i.Name == indicatorBase.Name);
if (entity != null)
{
entity.Type = indicator.Type;
entity.SignalType = indicator.SignalType;
entity.MinimumHistory = indicator.MinimumHistory;
entity.Period = indicator.Period;
entity.FastPeriods = indicator.FastPeriods;
entity.SlowPeriods = indicator.SlowPeriods;
entity.SignalPeriods = indicator.SignalPeriods;
entity.Multiplier = indicator.Multiplier;
entity.SmoothPeriods = indicator.SmoothPeriods;
entity.StochPeriods = indicator.StochPeriods;
entity.CyclePeriods = indicator.CyclePeriods;
entity.UserName = indicator.User?.Name;
entity.Type = indicatorBase.Type;
entity.SignalType = indicatorBase.SignalType;
entity.MinimumHistory = indicatorBase.MinimumHistory;
entity.Period = indicatorBase.Period;
entity.FastPeriods = indicatorBase.FastPeriods;
entity.SlowPeriods = indicatorBase.SlowPeriods;
entity.SignalPeriods = indicatorBase.SignalPeriods;
entity.Multiplier = indicatorBase.Multiplier;
entity.SmoothPeriods = indicatorBase.SmoothPeriods;
entity.StochPeriods = indicatorBase.StochPeriods;
entity.CyclePeriods = indicatorBase.CyclePeriods;
entity.UserName = indicatorBase.User?.Name;
entity.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
}
}
@@ -242,15 +243,9 @@ public class PostgreSqlTradingRepository : ITradingRepository
#endregion
#region Position Methods
public Position GetPositionByIdentifier(string identifier)
{
return GetPositionByIdentifierAsync(identifier).Result;
}
public async Task<Position> GetPositionByIdentifierAsync(string identifier)
public async Task<Position> GetPositionByIdentifierAsync(Guid identifier)
{
var position = await _context.Positions
.AsNoTracking()
@@ -310,8 +305,8 @@ public class PostgreSqlTradingRepository : ITradingRepository
var existingPosition = await _context.Positions
.AsNoTracking()
.FirstOrDefaultAsync(p => p.Identifier == position.Identifier &&
((position.User == null && p.UserName == null) ||
(position.User != null && p.UserName == position.User.Name)));
((position.User == null && p.UserName == null) ||
(position.User != null && p.UserName == position.User.Name)));
if (existingPosition != null)
{
@@ -320,7 +315,7 @@ public class PostgreSqlTradingRepository : ITradingRepository
}
var entity = PostgreSqlMappers.Map(position);
// Handle related trades
if (position.Open != null)
{
@@ -370,11 +365,11 @@ public class PostgreSqlTradingRepository : ITradingRepository
entity.ProfitAndLoss = position.ProfitAndLoss?.Realized ?? 0;
entity.Status = position.Status;
entity.SignalIdentifier = position.SignalIdentifier;
entity.MoneyManagementJson = position.MoneyManagement != null
? JsonConvert.SerializeObject(position.MoneyManagement)
entity.MoneyManagementJson = position.MoneyManagement != null
? JsonConvert.SerializeObject(position.MoneyManagement)
: null;
entity.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
}
}
@@ -393,7 +388,7 @@ public class PostgreSqlTradingRepository : ITradingRepository
var signals = await _context.Signals
.AsNoTracking()
.Where(s => (user == null && s.UserName == null) ||
(user != null && s.UserName == user.Name))
(user != null && s.UserName == user.Name))
.ToListAsync()
.ConfigureAwait(false);
@@ -410,8 +405,8 @@ public class PostgreSqlTradingRepository : ITradingRepository
var signal = await _context.Signals
.AsNoTracking()
.FirstOrDefaultAsync(s => s.Identifier == identifier &&
((user == null && s.UserName == null) ||
(user != null && s.UserName == user.Name)))
((user == null && s.UserName == null) ||
(user != null && s.UserName == user.Name)))
.ConfigureAwait(false);
return PostgreSqlMappers.Map(signal);
@@ -423,9 +418,9 @@ public class PostgreSqlTradingRepository : ITradingRepository
var existingSignal = _context.Signals
.AsNoTracking()
.FirstOrDefault(s => s.Identifier == signal.Identifier &&
s.Date == signal.Date &&
((s.UserName == null && signal.User == null) ||
(s.UserName != null && signal.User != null && s.UserName == signal.User.Name)));
s.Date == signal.Date &&
((s.UserName == null && signal.User == null) ||
(s.UserName != null && signal.User != null && s.UserName == signal.User.Name)));
if (existingSignal != null)
{
@@ -438,7 +433,25 @@ public class PostgreSqlTradingRepository : ITradingRepository
await _context.SaveChangesAsync();
}
public async Task<IndicatorBase> GetStrategyByNameUserAsync(string name, User user)
{
var indicator = await _context.Indicators
.AsNoTracking()
.FirstOrDefaultAsync(i => i.Name == name &&
((user == null && i.UserName == null) ||
(user != null && i.UserName == user.Name)));
return PostgreSqlMappers.Map(indicator);
}
public async Task<Scenario> GetScenarioByNameUserAsync(string scenarioName, User user)
{
var scenario = await _context.Scenarios
.AsNoTracking()
.FirstOrDefaultAsync(s => s.Name == scenarioName &&
((user == null && s.UserName == null) ||
(user != null && s.UserName == user.Name)));
return PostgreSqlMappers.Map(scenario);
}
#endregion
}
}