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:
@@ -1,6 +1,8 @@
|
||||
using Managing.Core;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Indicators;
|
||||
using Managing.Domain.MoneyManagements;
|
||||
using Managing.Domain.Scenarios;
|
||||
using Managing.Domain.Strategies;
|
||||
using Managing.Domain.Trades;
|
||||
using static Managing.Common.Enums;
|
||||
@@ -49,28 +51,28 @@ public static class TradingBox
|
||||
{
|
||||
private static readonly IndicatorComboConfig _defaultConfig = new();
|
||||
|
||||
public static LightSignal GetSignal(HashSet<Candle> newCandles, HashSet<IIndicator> strategies,
|
||||
HashSet<LightSignal> previousSignal, int? loopbackPeriod = 1)
|
||||
public static LightSignal GetSignal(HashSet<Candle> newCandles, LightScenario scenario,
|
||||
Dictionary<string, LightSignal> previousSignal, int? loopbackPeriod = 1)
|
||||
{
|
||||
return GetSignal(newCandles, strategies, previousSignal, _defaultConfig, loopbackPeriod);
|
||||
return GetSignal(newCandles, scenario, previousSignal, _defaultConfig, loopbackPeriod);
|
||||
}
|
||||
|
||||
public static LightSignal GetSignal(HashSet<Candle> newCandles, HashSet<IIndicator> strategies,
|
||||
HashSet<LightSignal> previousSignal, IndicatorComboConfig config, int? loopbackPeriod = 1)
|
||||
public static LightSignal GetSignal(HashSet<Candle> newCandles, LightScenario lightScenario,
|
||||
Dictionary<string, LightSignal> previousSignal, IndicatorComboConfig config, int? loopbackPeriod = 1)
|
||||
{
|
||||
var signalOnCandles = new List<LightSignal>();
|
||||
var limitedCandles = newCandles.ToList().TakeLast(600).ToList();
|
||||
|
||||
foreach (var strategy in strategies)
|
||||
foreach (var indicator in lightScenario.Indicators)
|
||||
{
|
||||
strategy.UpdateCandles(limitedCandles.ToHashSet());
|
||||
var signals = strategy.Run();
|
||||
IIndicator indicatorInstance = indicator.ToInterface();
|
||||
var signals = indicatorInstance.Run(newCandles);
|
||||
|
||||
if (signals == null || signals.Count == 0)
|
||||
if (signals == null || signals.Count() == 0)
|
||||
{
|
||||
// For trend and context strategies, lack of signal might be meaningful
|
||||
// Signal strategies are expected to be sparse, so we continue
|
||||
if (strategy.SignalType == SignalType.Signal)
|
||||
if (indicator.SignalType == SignalType.Signal)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -96,10 +98,10 @@ public static class TradingBox
|
||||
|
||||
foreach (var signal in signals.Where(s => s.Date >= loopbackStartDate))
|
||||
{
|
||||
var hasExistingSignal = previousSignal.Any(s => s.Identifier == signal.Identifier);
|
||||
var hasExistingSignal = previousSignal.ContainsKey(signal.Identifier);
|
||||
if (!hasExistingSignal)
|
||||
{
|
||||
bool shouldAdd = previousSignal.Count == 0 || previousSignal.Last().Date < signal.Date;
|
||||
bool shouldAdd = previousSignal.Count == 0 || previousSignal.Values.Last().Date < signal.Date;
|
||||
if (shouldAdd)
|
||||
{
|
||||
signalOnCandles.Add(signal);
|
||||
@@ -122,22 +124,22 @@ public static class TradingBox
|
||||
}
|
||||
|
||||
var data = newCandles.First();
|
||||
return ComputeSignals(strategies, latestSignalsPerIndicator, MiscExtensions.ParseEnum<Ticker>(data.Ticker),
|
||||
return ComputeSignals(lightScenario, latestSignalsPerIndicator, MiscExtensions.ParseEnum<Ticker>(data.Ticker),
|
||||
data.Timeframe, config);
|
||||
}
|
||||
|
||||
public static LightSignal ComputeSignals(HashSet<IIndicator> strategies, HashSet<LightSignal> signalOnCandles,
|
||||
public static LightSignal ComputeSignals(LightScenario scenario, HashSet<LightSignal> signalOnCandles,
|
||||
Ticker ticker,
|
||||
Timeframe timeframe)
|
||||
{
|
||||
return ComputeSignals(strategies, signalOnCandles, ticker, timeframe, _defaultConfig);
|
||||
return ComputeSignals(scenario, signalOnCandles, ticker, timeframe, _defaultConfig);
|
||||
}
|
||||
|
||||
public static LightSignal ComputeSignals(HashSet<IIndicator> strategies, HashSet<LightSignal> signalOnCandles,
|
||||
public static LightSignal ComputeSignals(LightScenario scenario, HashSet<LightSignal> signalOnCandles,
|
||||
Ticker ticker,
|
||||
Timeframe timeframe, IndicatorComboConfig config)
|
||||
{
|
||||
if (strategies.Count == 1)
|
||||
if (scenario.Indicators.Count == 1)
|
||||
{
|
||||
// Only one strategy, return the single signal
|
||||
return signalOnCandles.Single();
|
||||
@@ -146,7 +148,7 @@ public static class TradingBox
|
||||
signalOnCandles = signalOnCandles.OrderBy(s => s.Date).ToHashSet();
|
||||
|
||||
// Check if all strategies produced signals - this is required for composite signals
|
||||
var strategyNames = strategies.Select(s => s.Name).ToHashSet();
|
||||
var strategyNames = scenario.Indicators.Select(s => s.Name).ToHashSet();
|
||||
var signalIndicatorNames = signalOnCandles.Select(s => s.IndicatorName).ToHashSet();
|
||||
|
||||
if (!strategyNames.SetEquals(signalIndicatorNames))
|
||||
@@ -161,7 +163,7 @@ public static class TradingBox
|
||||
var contextSignals = signalOnCandles.Where(s => s.SignalType == SignalType.Context).ToList();
|
||||
|
||||
// Context validation - evaluates market conditions based on confidence levels
|
||||
if (!ValidateContextStrategies(strategies, contextSignals, config))
|
||||
if (!ValidateContextStrategies(scenario, contextSignals, config))
|
||||
{
|
||||
return null; // Context strategies are blocking the trade
|
||||
}
|
||||
@@ -233,10 +235,10 @@ public static class TradingBox
|
||||
/// <summary>
|
||||
/// Validates context strategies based on confidence levels indicating market condition quality
|
||||
/// </summary>
|
||||
private static bool ValidateContextStrategies(HashSet<IIndicator> allStrategies, List<LightSignal> contextSignals,
|
||||
private static bool ValidateContextStrategies(LightScenario scenario, List<LightSignal> contextSignals,
|
||||
IndicatorComboConfig config)
|
||||
{
|
||||
var contextStrategiesCount = allStrategies.Count(s => s.SignalType == SignalType.Context);
|
||||
var contextStrategiesCount = scenario.Indicators.Count(s => s.SignalType == SignalType.Context);
|
||||
|
||||
if (contextStrategiesCount == 0)
|
||||
{
|
||||
@@ -453,11 +455,11 @@ public static class TradingBox
|
||||
/// </summary>
|
||||
/// <param name="positions">List of positions to analyze</param>
|
||||
/// <returns>The total volume traded in decimal</returns>
|
||||
public static decimal GetTotalVolumeTraded(List<Position> positions)
|
||||
public static decimal GetTotalVolumeTraded(Dictionary<Guid, Position> positions)
|
||||
{
|
||||
decimal totalVolume = 0;
|
||||
|
||||
foreach (var position in positions)
|
||||
foreach (var position in positions.Values)
|
||||
{
|
||||
// Add entry volume
|
||||
totalVolume += position.Open.Quantity * position.Open.Price;
|
||||
@@ -487,12 +489,12 @@ public static class TradingBox
|
||||
/// </summary>
|
||||
/// <param name="positions">List of positions to analyze</param>
|
||||
/// <returns>The volume traded in the last 24 hours in decimal</returns>
|
||||
public static decimal GetLast24HVolumeTraded(List<Position> positions)
|
||||
public static decimal GetLast24HVolumeTraded(Dictionary<Guid, Position> positions)
|
||||
{
|
||||
decimal last24hVolume = 0;
|
||||
DateTime cutoff = DateTime.UtcNow.AddHours(-24);
|
||||
|
||||
foreach (var position in positions)
|
||||
foreach (var position in positions.Values)
|
||||
{
|
||||
// Check if any part of this position was traded in the last 24 hours
|
||||
|
||||
@@ -528,24 +530,20 @@ public static class TradingBox
|
||||
/// </summary>
|
||||
/// <param name="positions">List of positions to analyze</param>
|
||||
/// <returns>A tuple containing (wins, losses)</returns>
|
||||
public static (int Wins, int Losses) GetWinLossCount(List<Position> positions)
|
||||
public static (int Wins, int Losses) GetWinLossCount(Dictionary<Guid, Position> positions)
|
||||
{
|
||||
int wins = 0;
|
||||
int losses = 0;
|
||||
|
||||
foreach (var position in positions)
|
||||
foreach (var position in positions.Values)
|
||||
{
|
||||
// Only count finished positions
|
||||
if (position.IsFinished())
|
||||
if (position.ProfitAndLoss != null && position.ProfitAndLoss.Realized > 0)
|
||||
{
|
||||
if (position.ProfitAndLoss != null && position.ProfitAndLoss.Realized > 0)
|
||||
{
|
||||
wins++;
|
||||
}
|
||||
else
|
||||
{
|
||||
losses++;
|
||||
}
|
||||
wins++;
|
||||
}
|
||||
else
|
||||
{
|
||||
losses++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -557,13 +555,13 @@ public static class TradingBox
|
||||
/// </summary>
|
||||
/// <param name="positions">List of positions to analyze</param>
|
||||
/// <returns>The ROI for the last 24 hours as a percentage</returns>
|
||||
public static decimal GetLast24HROI(List<Position> positions)
|
||||
public static decimal GetLast24HROI(Dictionary<Guid, Position> positions)
|
||||
{
|
||||
decimal profitLast24h = 0;
|
||||
decimal investmentLast24h = 0;
|
||||
DateTime cutoff = DateTime.UtcNow.AddHours(-24);
|
||||
|
||||
foreach (var position in positions)
|
||||
foreach (var position in positions.Values)
|
||||
{
|
||||
// Only count positions that were opened or closed within the last 24 hours
|
||||
if (position.IsFinished() &&
|
||||
|
||||
Reference in New Issue
Block a user