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:
@@ -3,10 +3,9 @@ using System.ComponentModel.DataAnnotations;
|
||||
using Exilion.TradingAtomics;
|
||||
using Managing.Domain.Bots;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Strategies.Base;
|
||||
using Managing.Domain.Indicators;
|
||||
using Managing.Domain.Trades;
|
||||
using Managing.Domain.Users;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Domain.Backtests;
|
||||
|
||||
@@ -14,16 +13,15 @@ public class Backtest
|
||||
{
|
||||
public Backtest(
|
||||
TradingBotConfig config,
|
||||
List<Position> positions,
|
||||
List<LightSignal> signals,
|
||||
List<Candle> candles = null)
|
||||
Dictionary<Guid, Position> positions,
|
||||
Dictionary<string, LightSignal> signals,
|
||||
HashSet<Candle> candles = null)
|
||||
{
|
||||
Config = config;
|
||||
Positions = positions;
|
||||
Signals = signals;
|
||||
Candles = candles != null ? candles : new List<Candle>();
|
||||
Candles = candles != null ? candles : new HashSet<Candle>();
|
||||
WalletBalances = new List<KeyValuePair<DateTime, decimal>>();
|
||||
IndicatorsValues = new Dictionary<IndicatorType, IndicatorsResultBase>();
|
||||
|
||||
// Initialize start and end dates if candles are provided
|
||||
if (candles != null && candles.Count > 0)
|
||||
@@ -44,16 +42,15 @@ public class Backtest
|
||||
[Required] public decimal GrowthPercentage { get; set; }
|
||||
[Required] public decimal HodlPercentage { get; set; }
|
||||
[Required] public TradingBotConfig Config { get; }
|
||||
[Required] public List<Position> Positions { get; }
|
||||
[Required] public List<LightSignal> Signals { get; }
|
||||
[Required] public List<Candle> Candles { get; set; }
|
||||
[Required] public Dictionary<Guid, Position> Positions { get; }
|
||||
[Required] public Dictionary<string, LightSignal> Signals { get; }
|
||||
[Required] public HashSet<Candle> Candles { get; set; }
|
||||
[Required] public DateTime StartDate { get; set; }
|
||||
[Required] public DateTime EndDate { get; set; }
|
||||
[Required] public PerformanceMetrics Statistics { get; set; }
|
||||
[Required] public decimal Fees { get; set; }
|
||||
[Required] public List<KeyValuePair<DateTime, decimal>> WalletBalances { get; set; }
|
||||
[Required] public User User { get; set; }
|
||||
[Required] public Dictionary<IndicatorType, IndicatorsResultBase> IndicatorsValues { get; set; }
|
||||
[Required] public double Score { get; set; }
|
||||
public string RequestId { get; set; }
|
||||
public object? Metadata { get; set; }
|
||||
|
||||
@@ -3,123 +3,22 @@ using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Domain.Bots
|
||||
{
|
||||
/// <summary>
|
||||
/// A bot define what code should be run.
|
||||
/// To run a code you have to herit from this class and implement the Run() method
|
||||
/// </summary>
|
||||
public abstract class Bot : IBot
|
||||
public class Bot
|
||||
{
|
||||
public int ExecutionCount;
|
||||
public string Identifier { get; set; }
|
||||
public string Name { get; set; }
|
||||
public int Interval { get; set; }
|
||||
public BotStatus Status { get; set; }
|
||||
public User User { get; set; }
|
||||
public Guid Identifier { get; set; }
|
||||
public string Name { get; set; }
|
||||
public Ticker Ticker { get; set; }
|
||||
public BotStatus Status { get; set; }
|
||||
public DateTime StartupTime { get; set; }
|
||||
public DateTime CreateDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The time when the bot was first started (creation date)
|
||||
/// </summary>
|
||||
public DateTime StartupTime { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The time when the bot was created
|
||||
/// </summary>
|
||||
public DateTime CreateDate { get; protected set; }
|
||||
|
||||
private CancellationTokenSource CancellationToken { get; set; }
|
||||
|
||||
public Bot(string name)
|
||||
{
|
||||
Identifier = $"{name}-{DateTime.Now:yyyyMMdd-hhmm}-{Guid.NewGuid()}";
|
||||
Name = name;
|
||||
Status = BotStatus.Down;
|
||||
CancellationToken = new CancellationTokenSource();
|
||||
ExecutionCount = 0;
|
||||
Interval = 3000;
|
||||
CreateDate = DateTime.UtcNow; // Set the creation time when the bot is instantiated
|
||||
StartupTime = DateTime.UtcNow; // Set the startup time to creation date initially
|
||||
}
|
||||
|
||||
public virtual void Start()
|
||||
{
|
||||
Status = BotStatus.Up;
|
||||
// StartupTime remains unchanged on first start (it's already set to creation date)
|
||||
}
|
||||
|
||||
public async Task InitWorker(Func<Task> action)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
while (Status == BotStatus.Up && !CancellationToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
await action();
|
||||
ExecutionCount++;
|
||||
if (CancellationToken.IsCancellationRequested)
|
||||
break;
|
||||
}
|
||||
catch (TaskCanceledException) when (CancellationToken.IsCancellationRequested)
|
||||
{
|
||||
// Graceful shutdown when cancellation is requested
|
||||
break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SentrySdk.CaptureException(ex);
|
||||
Console.WriteLine(ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await Task.Delay(Interval, CancellationToken.Token);
|
||||
}
|
||||
}
|
||||
}, CancellationToken.Token);
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
{
|
||||
Console.WriteLine($"Bot was cancelled: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
Status = BotStatus.Down;
|
||||
_ = Task.Run(async () => await SaveBackup());
|
||||
// CancellationToken.Cancel();
|
||||
}
|
||||
|
||||
public void Restart()
|
||||
{
|
||||
Status = BotStatus.Up;
|
||||
StartupTime = DateTime.UtcNow; // Update the startup time when the bot is restarted
|
||||
}
|
||||
|
||||
public string GetStatus()
|
||||
{
|
||||
return Status.ToString();
|
||||
}
|
||||
|
||||
public string GetName()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total runtime of the bot since it was started
|
||||
/// </summary>
|
||||
/// <returns>TimeSpan representing the runtime, or TimeSpan.Zero if the bot is not running</returns>
|
||||
public TimeSpan GetRuntime()
|
||||
{
|
||||
if (Status != BotStatus.Up)
|
||||
return TimeSpan.Zero;
|
||||
|
||||
return DateTime.UtcNow - StartupTime;
|
||||
}
|
||||
|
||||
public abstract Task SaveBackup();
|
||||
public abstract void LoadBackup(BotBackup backup);
|
||||
public int TradeWins { get; set; }
|
||||
public int TradeLosses { get; set; }
|
||||
public decimal Pnl { get; set; }
|
||||
public decimal Roi { get; set; }
|
||||
public decimal Volume { get; set; }
|
||||
public decimal Fees { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
using Managing.Domain.Users;
|
||||
using Newtonsoft.Json;
|
||||
using Orleans;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Domain.Bots;
|
||||
|
||||
[GenerateSerializer]
|
||||
public class BotBackup
|
||||
{
|
||||
[Id(0)]
|
||||
public string Identifier { get; set; }
|
||||
|
||||
[Id(1)]
|
||||
public User User { get; set; }
|
||||
|
||||
[Id(2)]
|
||||
public TradingBotBackup Data { get; set; }
|
||||
|
||||
[Id(3)]
|
||||
public BotStatus LastStatus { get; set; }
|
||||
|
||||
[Id(4)]
|
||||
public DateTime CreateDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the TradingBotBackup data to JSON string
|
||||
/// </summary>
|
||||
/// <returns>JSON string representation of the data</returns>
|
||||
public string SerializeData()
|
||||
{
|
||||
if (Data == null) return null;
|
||||
|
||||
return JsonConvert.SerializeObject(Data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes JSON string to TradingBotBackup data
|
||||
/// </summary>
|
||||
/// <param name="jsonData">JSON string to deserialize</param>
|
||||
public void DeserializeData(string jsonData)
|
||||
{
|
||||
if (string.IsNullOrEmpty(jsonData))
|
||||
{
|
||||
Data = null;
|
||||
return;
|
||||
}
|
||||
|
||||
Data = JsonConvert.DeserializeObject<TradingBotBackup>(jsonData);
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
using Managing.Domain.Users;
|
||||
|
||||
namespace Managing.Domain.Bots
|
||||
{
|
||||
public interface IBot
|
||||
{
|
||||
User User { get; set; }
|
||||
string Name { get; set; }
|
||||
void Start();
|
||||
void Stop();
|
||||
void Restart();
|
||||
string GetStatus();
|
||||
string GetName();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total runtime of the bot since it was started
|
||||
/// </summary>
|
||||
/// <returns>TimeSpan representing the runtime, or TimeSpan.Zero if the bot is not running</returns>
|
||||
TimeSpan GetRuntime();
|
||||
|
||||
/// <summary>
|
||||
/// The time when the bot was first started (creation date)
|
||||
/// </summary>
|
||||
DateTime StartupTime { get; }
|
||||
|
||||
string Identifier { get; set; }
|
||||
Task SaveBackup();
|
||||
void LoadBackup(BotBackup backup);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using Managing.Domain.Indicators;
|
||||
using Managing.Domain.Trades;
|
||||
using Orleans;
|
||||
|
||||
@@ -19,10 +20,10 @@ public class TradingBotBackup
|
||||
public HashSet<LightSignal> Signals { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Runtime state: Open and closed positions for the bot
|
||||
/// Runtime state: Open and closed positions for the bot, keyed by position identifier
|
||||
/// </summary>
|
||||
[Id(2)]
|
||||
public List<Position> Positions { get; set; }
|
||||
public Dictionary<Guid, Position> Positions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Runtime state: Historical wallet balances over time
|
||||
@@ -41,4 +42,4 @@ public class TradingBotBackup
|
||||
/// </summary>
|
||||
[Id(5)]
|
||||
public DateTime CreateDate { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -32,18 +32,42 @@ public static class CandleExtensions
|
||||
|
||||
public static int GetIntervalFromTimeframe(Timeframe timeframe)
|
||||
{
|
||||
var seconds = timeframe switch
|
||||
{
|
||||
Timeframe.OneDay => 86400,
|
||||
Timeframe.FourHour => 14400,
|
||||
Timeframe.OneHour => 3600,
|
||||
Timeframe.ThirtyMinutes => 1800,
|
||||
Timeframe.FifteenMinutes => 900,
|
||||
Timeframe.FiveMinutes => 300,
|
||||
_ => 300,
|
||||
};
|
||||
// Run every 1/5th of the candle duration
|
||||
return seconds / 5 * 1000; // milliseconds
|
||||
var seconds = GetBaseIntervalInSeconds(timeframe);
|
||||
// Run every 1/5th of the candle duration
|
||||
return seconds / 5 * 1000; // milliseconds
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the interval in minutes for the given timeframe.
|
||||
/// This is useful for cooldown period calculations.
|
||||
/// </summary>
|
||||
/// <param name="timeframe">The timeframe to get the interval for</param>
|
||||
/// <returns>The interval in minutes</returns>
|
||||
public static double GetIntervalInMinutes(Timeframe timeframe)
|
||||
{
|
||||
var seconds = GetBaseIntervalInSeconds(timeframe);
|
||||
// Run every 1/5th of the candle duration
|
||||
return (seconds / 5.0) / 60.0; // minutes
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the base interval in seconds for the given timeframe.
|
||||
/// This is the core interval logic that can be used for various calculations.
|
||||
/// </summary>
|
||||
/// <param name="timeframe">The timeframe to get the base interval for</param>
|
||||
/// <returns>The base interval in seconds</returns>
|
||||
public static int GetBaseIntervalInSeconds(Timeframe timeframe)
|
||||
{
|
||||
return timeframe switch
|
||||
{
|
||||
Timeframe.OneDay => 86400,
|
||||
Timeframe.FourHour => 14400,
|
||||
Timeframe.OneHour => 3600,
|
||||
Timeframe.ThirtyMinutes => 1800,
|
||||
Timeframe.FifteenMinutes => 900,
|
||||
Timeframe.FiveMinutes => 300,
|
||||
_ => 300,
|
||||
};
|
||||
}
|
||||
|
||||
public static int GetUnixInterval(this Timeframe timeframe)
|
||||
|
||||
@@ -4,9 +4,9 @@ using Skender.Stock.Indicators;
|
||||
|
||||
namespace Managing.Domain.Strategies.Base;
|
||||
|
||||
public abstract class EmaBaseIndicator : Indicator
|
||||
public abstract class EmaBaseIndicatorBase : IndicatorBase
|
||||
{
|
||||
protected EmaBaseIndicator(string name, Enums.IndicatorType type) : base(name, type)
|
||||
protected EmaBaseIndicatorBase(string name, Enums.IndicatorType type) : base(name, type)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Managing.Core;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Indicators;
|
||||
using Managing.Domain.Shared.Rules;
|
||||
using Managing.Domain.Strategies.Base;
|
||||
using Skender.Stock.Indicators;
|
||||
@@ -7,7 +8,7 @@ using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Domain.Strategies.Context;
|
||||
|
||||
public class StDevContext : Indicator
|
||||
public class StDevContext : IndicatorBase
|
||||
{
|
||||
public List<LightSignal> Signals { get; set; }
|
||||
|
||||
@@ -17,17 +18,17 @@ public class StDevContext : Indicator
|
||||
Period = period;
|
||||
}
|
||||
|
||||
public override List<LightSignal> Run()
|
||||
public override List<LightSignal> Run(HashSet<Candle> candles)
|
||||
{
|
||||
if (Candles.Count <= Period)
|
||||
if (candles.Count <= Period)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var stDev = Candles.GetStdDev(Period.Value).ToList();
|
||||
var stDevCandles = MapStDev(stDev, Candles.TakeLast(Period.Value));
|
||||
var stDev = candles.GetStdDev(Period.Value).ToList();
|
||||
var stDevCandles = MapStDev(stDev, candles.TakeLast(Period.Value));
|
||||
|
||||
if (stDev.Count == 0)
|
||||
return null;
|
||||
@@ -73,11 +74,11 @@ public class StDevContext : Indicator
|
||||
}
|
||||
}
|
||||
|
||||
public override IndicatorsResultBase GetIndicatorValues()
|
||||
public override IndicatorsResultBase GetIndicatorValues(HashSet<Candle> candles)
|
||||
{
|
||||
var test = new IndicatorsResultBase()
|
||||
{
|
||||
StdDev = Candles.GetStdDev(Period.Value).ToList()
|
||||
StdDev = candles.GetStdDev(Period.Value).ToList()
|
||||
};
|
||||
|
||||
return test;
|
||||
@@ -1,5 +1,5 @@
|
||||
using Managing.Core.FixedSizedQueue;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Indicators;
|
||||
using Managing.Domain.Strategies.Base;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
@@ -14,11 +14,12 @@ namespace Managing.Domain.Strategies
|
||||
int? FastPeriods { get; set; }
|
||||
int? SlowPeriods { get; set; }
|
||||
int? SignalPeriods { get; set; }
|
||||
FixedSizeQueue<Candle> Candles { get; set; }
|
||||
double? Multiplier { get; set; }
|
||||
int? StochPeriods { get; set; }
|
||||
int? SmoothPeriods { get; set; }
|
||||
int? CyclePeriods { get; set; }
|
||||
|
||||
List<LightSignal> Run();
|
||||
IndicatorsResultBase GetIndicatorValues();
|
||||
void UpdateCandles(HashSet<Candle> newCandles);
|
||||
string GetName();
|
||||
List<LightSignal> Run(HashSet<Candle> candles);
|
||||
IndicatorsResultBase GetIndicatorValues(HashSet<Candle> candles);
|
||||
}
|
||||
}
|
||||
54
src/Managing.Domain/Indicators/IndicatorBase.cs
Normal file
54
src/Managing.Domain/Indicators/IndicatorBase.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Indicators;
|
||||
using Managing.Domain.Scenarios;
|
||||
using Managing.Domain.Strategies.Base;
|
||||
using Managing.Domain.Users;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Domain.Strategies
|
||||
{
|
||||
public class IndicatorBase : IIndicator
|
||||
{
|
||||
public IndicatorBase(string name, IndicatorType type)
|
||||
{
|
||||
Name = name;
|
||||
Type = type;
|
||||
SignalType = ScenarioHelpers.GetSignalType(type);
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
public IndicatorType Type { get; set; }
|
||||
|
||||
public SignalType SignalType { get; set; }
|
||||
|
||||
public int MinimumHistory { get; set; }
|
||||
|
||||
public int? Period { get; set; }
|
||||
|
||||
public int? FastPeriods { get; set; }
|
||||
|
||||
public int? SlowPeriods { get; set; }
|
||||
|
||||
public int? SignalPeriods { get; set; }
|
||||
|
||||
public double? Multiplier { get; set; }
|
||||
|
||||
public int? SmoothPeriods { get; set; }
|
||||
|
||||
public int? StochPeriods { get; set; }
|
||||
|
||||
public int? CyclePeriods { get; set; }
|
||||
|
||||
public User User { get; set; }
|
||||
|
||||
public virtual List<LightSignal> Run(HashSet<Candle> candles)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual IndicatorsResultBase GetIndicatorValues(HashSet<Candle> candles)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,29 +45,35 @@ public class LightIndicator
|
||||
/// <summary>
|
||||
/// Converts a full Indicator to a LightIndicator
|
||||
/// </summary>
|
||||
public static LightIndicator FromIndicator(Indicator indicator)
|
||||
public static LightIndicator BaseToLight(IndicatorBase indicatorBase)
|
||||
{
|
||||
return new LightIndicator(indicator.Name, indicator.Type)
|
||||
return new LightIndicator(indicatorBase.Name, indicatorBase.Type)
|
||||
{
|
||||
SignalType = indicator.SignalType,
|
||||
MinimumHistory = indicator.MinimumHistory,
|
||||
Period = indicator.Period,
|
||||
FastPeriods = indicator.FastPeriods,
|
||||
SlowPeriods = indicator.SlowPeriods,
|
||||
SignalPeriods = indicator.SignalPeriods,
|
||||
Multiplier = indicator.Multiplier,
|
||||
SmoothPeriods = indicator.SmoothPeriods,
|
||||
StochPeriods = indicator.StochPeriods,
|
||||
CyclePeriods = indicator.CyclePeriods
|
||||
SignalType = indicatorBase.SignalType,
|
||||
MinimumHistory = indicatorBase.MinimumHistory,
|
||||
Period = indicatorBase.Period,
|
||||
FastPeriods = indicatorBase.FastPeriods,
|
||||
SlowPeriods = indicatorBase.SlowPeriods,
|
||||
SignalPeriods = indicatorBase.SignalPeriods,
|
||||
Multiplier = indicatorBase.Multiplier,
|
||||
SmoothPeriods = indicatorBase.SmoothPeriods,
|
||||
StochPeriods = indicatorBase.StochPeriods,
|
||||
CyclePeriods = indicatorBase.CyclePeriods
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a LightIndicator back to a full Indicator
|
||||
/// </summary>
|
||||
public Indicator ToIndicator()
|
||||
public IIndicator ToInterface()
|
||||
{
|
||||
return new Indicator(Name, Type)
|
||||
// Use the factory method to create the correct indicator type
|
||||
return ScenarioHelpers.BuildIndicator(this);
|
||||
}
|
||||
|
||||
public IndicatorBase LightToBase()
|
||||
{
|
||||
var baseIndicator = new IndicatorBase(Name, Type)
|
||||
{
|
||||
SignalType = SignalType,
|
||||
MinimumHistory = MinimumHistory,
|
||||
@@ -80,5 +86,7 @@ public class LightIndicator
|
||||
StochPeriods = StochPeriods,
|
||||
CyclePeriods = CyclePeriods
|
||||
};
|
||||
|
||||
return baseIndicator;
|
||||
}
|
||||
}
|
||||
71
src/Managing.Domain/Indicators/LightSignal.cs
Normal file
71
src/Managing.Domain/Indicators/LightSignal.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Globalization;
|
||||
using Managing.Domain.Candles;
|
||||
using Orleans;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Domain.Indicators;
|
||||
|
||||
[GenerateSerializer]
|
||||
public class LightSignal
|
||||
{
|
||||
public LightSignal(Ticker ticker, TradeDirection direction, Confidence confidence, Candle candle, DateTime date,
|
||||
TradingExchanges exchange, IndicatorType indicatorType, SignalType signalType, string indicatorName)
|
||||
{
|
||||
Direction = direction;
|
||||
Confidence = confidence;
|
||||
// Cast to base Candle type to avoid Orleans serialization issues with nested types
|
||||
Candle = candle as Candle ?? new Candle
|
||||
{
|
||||
Exchange = candle.Exchange,
|
||||
Ticker = candle.Ticker,
|
||||
OpenTime = candle.OpenTime,
|
||||
Date = candle.Date,
|
||||
Open = candle.Open,
|
||||
Close = candle.Close,
|
||||
High = candle.High,
|
||||
Low = candle.Low,
|
||||
Timeframe = candle.Timeframe,
|
||||
Volume = candle.Volume
|
||||
};
|
||||
Date = date;
|
||||
Ticker = ticker;
|
||||
Exchange = exchange;
|
||||
Status = SignalStatus.WaitingForPosition;
|
||||
IndicatorType = indicatorType;
|
||||
IndicatorName = indicatorName;
|
||||
SignalType = signalType;
|
||||
|
||||
Identifier =
|
||||
$"{indicatorName}-{indicatorType}-{direction}-{ticker}-{Candle?.Close.ToString(CultureInfo.InvariantCulture)}-{date:yyyyMMdd-HHmmss}";
|
||||
}
|
||||
|
||||
[Id(0)] [Required] public SignalStatus Status { get; set; }
|
||||
|
||||
[Id(1)] [Required] public TradeDirection Direction { get; }
|
||||
|
||||
[Id(2)] [Required] public Confidence Confidence { get; set; }
|
||||
|
||||
[Id(3)] [Required] public Timeframe Timeframe { get; }
|
||||
|
||||
[Id(4)] [Required] public DateTime Date { get; private set; }
|
||||
|
||||
[Id(5)] [Required] public Candle Candle { get; }
|
||||
|
||||
[Id(6)] [Required] public string Identifier { get; }
|
||||
|
||||
[Id(7)] [Required] public Ticker Ticker { get; }
|
||||
|
||||
[Id(8)] [Required] public TradingExchanges Exchange { get; set; }
|
||||
|
||||
[Id(9)] [Required] public IndicatorType IndicatorType { get; set; }
|
||||
|
||||
[Id(10)] [Required] public SignalType SignalType { get; set; }
|
||||
|
||||
[Id(11)] [Required] public string IndicatorName { get; set; }
|
||||
|
||||
public void SetConfidence(Confidence confidence)
|
||||
{
|
||||
Confidence = confidence;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Indicators;
|
||||
using Managing.Domain.Users;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Managing.Core;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Indicators;
|
||||
using Managing.Domain.Shared.Rules;
|
||||
using Managing.Domain.Strategies.Base;
|
||||
using Skender.Stock.Indicators;
|
||||
@@ -7,11 +8,11 @@ using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Domain.Strategies.Signals;
|
||||
|
||||
public class ChandelierExitIndicator : Indicator
|
||||
public class ChandelierExitIndicatorBase : IndicatorBase
|
||||
{
|
||||
public List<LightSignal> Signals { get; set; }
|
||||
|
||||
public ChandelierExitIndicator(string name, int period, double multiplier) : base(name,
|
||||
public ChandelierExitIndicatorBase(string name, int period, double multiplier) : base(name,
|
||||
IndicatorType.ChandelierExit)
|
||||
{
|
||||
Signals = new List<LightSignal>();
|
||||
@@ -20,17 +21,17 @@ public class ChandelierExitIndicator : Indicator
|
||||
MinimumHistory = 1 + Period.Value;
|
||||
}
|
||||
|
||||
public override List<LightSignal> Run()
|
||||
public override List<LightSignal> Run(HashSet<Candle> candles)
|
||||
{
|
||||
if (Candles.Count <= MinimumHistory)
|
||||
if (candles.Count <= MinimumHistory)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
GetSignals(ChandelierType.Long);
|
||||
GetSignals(ChandelierType.Short);
|
||||
GetSignals(ChandelierType.Long, candles);
|
||||
GetSignals(ChandelierType.Short, candles);
|
||||
|
||||
return Signals.Where(s => s.Confidence != Confidence.None).OrderBy(s => s.Date).ToList();
|
||||
}
|
||||
@@ -40,20 +41,20 @@ public class ChandelierExitIndicator : Indicator
|
||||
}
|
||||
}
|
||||
|
||||
public override IndicatorsResultBase GetIndicatorValues()
|
||||
public override IndicatorsResultBase GetIndicatorValues(HashSet<Candle> candles)
|
||||
{
|
||||
return new IndicatorsResultBase()
|
||||
{
|
||||
ChandelierLong = Candles.GetChandelier(Period.Value, Multiplier.Value, ChandelierType.Long).ToList(),
|
||||
ChandelierShort = Candles.GetChandelier(Period.Value, Multiplier.Value, ChandelierType.Short).ToList()
|
||||
ChandelierLong = candles.GetChandelier(Period.Value, Multiplier.Value, ChandelierType.Long).ToList(),
|
||||
ChandelierShort = candles.GetChandelier(Period.Value, Multiplier.Value, ChandelierType.Short).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
private void GetSignals(ChandelierType chandelierType)
|
||||
private void GetSignals(ChandelierType chandelierType, HashSet<Candle> candles)
|
||||
{
|
||||
var chandelier = Candles.GetChandelier(Period.Value, Multiplier.Value, chandelierType)
|
||||
var chandelier = candles.GetChandelier(Period.Value, Multiplier.Value, chandelierType)
|
||||
.Where(s => s.ChandelierExit.HasValue).ToList();
|
||||
var chandelierCandle = MapChandelierToCandle(chandelier, Candles.TakeLast(MinimumHistory));
|
||||
var chandelierCandle = MapChandelierToCandle(chandelier, candles.TakeLast(MinimumHistory));
|
||||
var previousCandle = chandelierCandle[0];
|
||||
|
||||
foreach (var currentCandle in chandelierCandle.Skip(1))
|
||||
@@ -1,5 +1,6 @@
|
||||
using Managing.Core;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Indicators;
|
||||
using Managing.Domain.Shared.Rules;
|
||||
using Managing.Domain.Strategies.Base;
|
||||
using Skender.Stock.Indicators;
|
||||
@@ -7,11 +8,12 @@ using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Domain.Strategies.Signals;
|
||||
|
||||
public class DualEmaCrossIndicator : EmaBaseIndicator
|
||||
public class DualEmaCrossIndicatorBase : EmaBaseIndicatorBase
|
||||
{
|
||||
public List<LightSignal> Signals { get; set; }
|
||||
|
||||
public DualEmaCrossIndicator(string name, int fastPeriod, int slowPeriod) : base(name, IndicatorType.DualEmaCross)
|
||||
public DualEmaCrossIndicatorBase(string name, int fastPeriod, int slowPeriod) : base(name,
|
||||
IndicatorType.DualEmaCross)
|
||||
{
|
||||
Signals = new List<LightSignal>();
|
||||
FastPeriods = fastPeriod;
|
||||
@@ -19,28 +21,28 @@ public class DualEmaCrossIndicator : EmaBaseIndicator
|
||||
MinimumHistory = Math.Max(fastPeriod, slowPeriod) * 2;
|
||||
}
|
||||
|
||||
public override IndicatorsResultBase GetIndicatorValues()
|
||||
public override IndicatorsResultBase GetIndicatorValues(HashSet<Candle> candles)
|
||||
{
|
||||
return new IndicatorsResultBase()
|
||||
{
|
||||
FastEma = Candles.GetEma(FastPeriods.Value).ToList(),
|
||||
SlowEma = Candles.GetEma(SlowPeriods.Value).ToList()
|
||||
FastEma = candles.GetEma(FastPeriods.Value).ToList(),
|
||||
SlowEma = candles.GetEma(SlowPeriods.Value).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
public override List<LightSignal> Run()
|
||||
public override List<LightSignal> Run(HashSet<Candle> candles)
|
||||
{
|
||||
if (Candles.Count <= MinimumHistory)
|
||||
if (candles.Count <= MinimumHistory)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var fastEma = Candles.GetEma(FastPeriods.Value).ToList();
|
||||
var slowEma = Candles.GetEma(SlowPeriods.Value).ToList();
|
||||
var fastEma = candles.GetEma(FastPeriods.Value).ToList();
|
||||
var slowEma = candles.GetEma(SlowPeriods.Value).ToList();
|
||||
|
||||
var dualEmaCandles = MapDualEmaToCandle(fastEma, slowEma, Candles.TakeLast(MinimumHistory));
|
||||
var dualEmaCandles = MapDualEmaToCandle(fastEma, slowEma, candles.TakeLast(MinimumHistory));
|
||||
|
||||
if (dualEmaCandles.Count < 2)
|
||||
return null;
|
||||
@@ -1,4 +1,6 @@
|
||||
using Managing.Core;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Indicators;
|
||||
using Managing.Domain.Shared.Rules;
|
||||
using Managing.Domain.Strategies.Base;
|
||||
using Skender.Stock.Indicators;
|
||||
@@ -6,7 +8,7 @@ using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Domain.Strategies.Signals;
|
||||
|
||||
public class EmaCrossIndicator : EmaBaseIndicator
|
||||
public class EmaCrossIndicator : EmaBaseIndicatorBase
|
||||
{
|
||||
public List<LightSignal> Signals { get; set; }
|
||||
|
||||
@@ -16,25 +18,25 @@ public class EmaCrossIndicator : EmaBaseIndicator
|
||||
Period = period;
|
||||
}
|
||||
|
||||
public override IndicatorsResultBase GetIndicatorValues()
|
||||
public override IndicatorsResultBase GetIndicatorValues(HashSet<Candle> candles)
|
||||
{
|
||||
return new IndicatorsResultBase()
|
||||
{
|
||||
Ema = Candles.GetEma(Period.Value).ToList()
|
||||
Ema = candles.GetEma(Period.Value).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
public override List<LightSignal> Run()
|
||||
public override List<LightSignal> Run(HashSet<Candle> candles)
|
||||
{
|
||||
if (Candles.Count <= Period)
|
||||
if (candles.Count <= Period)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var ema = Candles.GetEma(Period.Value).ToList();
|
||||
var emaCandles = MapEmaToCandle(ema, Candles.TakeLast(Period.Value));
|
||||
var ema = candles.GetEma(Period.Value).ToList();
|
||||
var emaCandles = MapEmaToCandle(ema, candles.TakeLast(Period.Value));
|
||||
|
||||
if (ema.Count == 0)
|
||||
return null;
|
||||
@@ -0,0 +1,79 @@
|
||||
using Managing.Core;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Indicators;
|
||||
using Managing.Domain.Shared.Rules;
|
||||
using Managing.Domain.Strategies.Base;
|
||||
using Skender.Stock.Indicators;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Domain.Strategies.Signals;
|
||||
|
||||
public class EmaCrossIndicatorBase : EmaBaseIndicatorBase
|
||||
{
|
||||
public List<LightSignal> Signals { get; set; }
|
||||
|
||||
public EmaCrossIndicatorBase(string name, int period) : base(name, IndicatorType.EmaCross)
|
||||
{
|
||||
Signals = new List<LightSignal>();
|
||||
Period = period;
|
||||
}
|
||||
|
||||
public override IndicatorsResultBase GetIndicatorValues(HashSet<Candle> candles)
|
||||
{
|
||||
return new IndicatorsResultBase()
|
||||
{
|
||||
Ema = candles.GetEma(Period.Value).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
public override List<LightSignal> Run(HashSet<Candle> candles)
|
||||
{
|
||||
if (candles.Count <= Period)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var ema = candles.GetEma(Period.Value).ToList();
|
||||
var emaCandles = MapEmaToCandle(ema, candles.TakeLast(Period.Value).ToHashSet());
|
||||
|
||||
if (ema.Count == 0)
|
||||
return null;
|
||||
|
||||
var previousCandle = emaCandles[0];
|
||||
foreach (var currentCandle in emaCandles.Skip(1))
|
||||
{
|
||||
if (previousCandle.Close > (decimal)currentCandle.Ema &&
|
||||
currentCandle.Close < (decimal)currentCandle.Ema)
|
||||
{
|
||||
AddSignal(currentCandle, TradeDirection.Short, Confidence.Medium);
|
||||
}
|
||||
|
||||
if (previousCandle.Close < (decimal)currentCandle.Ema &&
|
||||
currentCandle.Close > (decimal)currentCandle.Ema)
|
||||
{
|
||||
AddSignal(currentCandle, TradeDirection.Long, Confidence.Medium);
|
||||
}
|
||||
|
||||
previousCandle = currentCandle;
|
||||
}
|
||||
|
||||
return Signals.Where(s => s.Confidence != Confidence.None).OrderBy(s => s.Date).ToList();
|
||||
}
|
||||
catch (RuleException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddSignal(CandleEma candleSignal, TradeDirection direction, Confidence confidence)
|
||||
{
|
||||
var signal = new LightSignal(MiscExtensions.ParseEnum<Ticker>(candleSignal.Ticker), direction, confidence,
|
||||
candleSignal, candleSignal.Date, candleSignal.Exchange, Type, SignalType, Name);
|
||||
if (!Signals.Any(s => s.Identifier == signal.Identifier))
|
||||
{
|
||||
Signals.AddItem(signal);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using Managing.Core;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Indicators;
|
||||
using Managing.Domain.Shared.Rules;
|
||||
using Managing.Domain.Strategies.Base;
|
||||
using Skender.Stock.Indicators;
|
||||
@@ -14,7 +15,7 @@ namespace Managing.Domain.Strategies.Signals;
|
||||
/// 2. Long signals on STC rebound from oversold (25- → ≥25) with recent compressed volatility (max <11)
|
||||
/// 3. Avoids look-ahead bias through proper rolling window implementation
|
||||
/// </summary>
|
||||
public class LaggingSTC : Indicator
|
||||
public class LaggingSTC : IndicatorBase
|
||||
{
|
||||
public List<LightSignal> Signals { get; set; }
|
||||
|
||||
@@ -27,17 +28,17 @@ public class LaggingSTC : Indicator
|
||||
CyclePeriods = cyclePeriods;
|
||||
}
|
||||
|
||||
public override List<LightSignal> Run()
|
||||
public override List<LightSignal> Run(HashSet<Candle> candles)
|
||||
{
|
||||
if (Candles.Count <= 2 * (SlowPeriods + CyclePeriods))
|
||||
if (candles.Count <= 2 * (SlowPeriods + CyclePeriods))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var stc = Candles.GetStc(FastPeriods.Value, FastPeriods.Value, SlowPeriods.Value).ToList();
|
||||
var stcCandles = MapStcToCandle(stc, Candles.TakeLast(CyclePeriods.Value * 3));
|
||||
var stc = candles.GetStc(FastPeriods.Value, FastPeriods.Value, SlowPeriods.Value).ToList();
|
||||
var stcCandles = MapStcToCandle(stc, candles.TakeLast(CyclePeriods.Value * 3));
|
||||
|
||||
if (stcCandles.Count == 0)
|
||||
return null;
|
||||
@@ -89,9 +90,9 @@ public class LaggingSTC : Indicator
|
||||
}
|
||||
}
|
||||
|
||||
public override IndicatorsResultBase GetIndicatorValues()
|
||||
public override IndicatorsResultBase GetIndicatorValues(HashSet<Candle> candles)
|
||||
{
|
||||
var stc = Candles.GetStc(FastPeriods.Value, FastPeriods.Value, SlowPeriods.Value).ToList();
|
||||
var stc = candles.GetStc(FastPeriods.Value, FastPeriods.Value, SlowPeriods.Value).ToList();
|
||||
return new IndicatorsResultBase
|
||||
{
|
||||
Stc = stc
|
||||
@@ -1,5 +1,6 @@
|
||||
using Managing.Core;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Indicators;
|
||||
using Managing.Domain.Shared.Rules;
|
||||
using Managing.Domain.Strategies.Base;
|
||||
using Skender.Stock.Indicators;
|
||||
@@ -7,11 +8,11 @@ using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Domain.Strategies.Signals;
|
||||
|
||||
public class MacdCrossIndicator : Indicator
|
||||
public class MacdCrossIndicatorBase : IndicatorBase
|
||||
{
|
||||
public List<LightSignal> Signals { get; set; }
|
||||
|
||||
public MacdCrossIndicator(string name, int fastPeriods, int slowPeriods, int signalPeriods) :
|
||||
public MacdCrossIndicatorBase(string name, int fastPeriods, int slowPeriods, int signalPeriods) :
|
||||
base(name, IndicatorType.MacdCross)
|
||||
{
|
||||
Signals = new List<LightSignal>();
|
||||
@@ -20,17 +21,17 @@ public class MacdCrossIndicator : Indicator
|
||||
SignalPeriods = signalPeriods;
|
||||
}
|
||||
|
||||
public override List<LightSignal> Run()
|
||||
public override List<LightSignal> Run(HashSet<Candle> candles)
|
||||
{
|
||||
if (Candles.Count <= 2 * (SlowPeriods + SignalPeriods))
|
||||
if (candles.Count <= 2 * (SlowPeriods + SignalPeriods))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var macd = Candles.GetMacd(FastPeriods.Value, SlowPeriods.Value, SignalPeriods.Value).ToList();
|
||||
var macdCandle = MapMacdToCandle(macd, Candles.TakeLast(SignalPeriods.Value));
|
||||
var macd = candles.GetMacd(FastPeriods.Value, SlowPeriods.Value, SignalPeriods.Value).ToList();
|
||||
var macdCandle = MapMacdToCandle(macd, candles.TakeLast(SignalPeriods.Value));
|
||||
|
||||
if (macd.Count == 0)
|
||||
return null;
|
||||
@@ -67,11 +68,11 @@ public class MacdCrossIndicator : Indicator
|
||||
}
|
||||
}
|
||||
|
||||
public override IndicatorsResultBase GetIndicatorValues()
|
||||
public override IndicatorsResultBase GetIndicatorValues(HashSet<Candle> candles)
|
||||
{
|
||||
return new IndicatorsResultBase()
|
||||
{
|
||||
Macd = Candles.GetMacd(FastPeriods.Value, SlowPeriods.Value, SignalPeriods.Value).ToList()
|
||||
Macd = candles.GetMacd(FastPeriods.Value, SlowPeriods.Value, SignalPeriods.Value).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Managing.Core;
|
||||
using Managing.Domain.Indicators;
|
||||
using Managing.Domain.Shared.Rules;
|
||||
using Managing.Domain.Strategies.Base;
|
||||
using Skender.Stock.Indicators;
|
||||
@@ -7,11 +8,11 @@ using Candle = Managing.Domain.Candles.Candle;
|
||||
|
||||
namespace Managing.Domain.Strategies.Signals;
|
||||
|
||||
public class RsiDivergenceConfirmIndicator : Indicator
|
||||
public class RsiDivergenceConfirmIndicatorBase : IndicatorBase
|
||||
{
|
||||
public List<LightSignal> Signals { get; set; }
|
||||
|
||||
public RsiDivergenceConfirmIndicator(string name, int period) : base(name, IndicatorType.RsiDivergenceConfirm)
|
||||
public RsiDivergenceConfirmIndicatorBase(string name, int period) : base(name, IndicatorType.RsiDivergenceConfirm)
|
||||
{
|
||||
Period = period;
|
||||
Signals = new List<LightSignal>();
|
||||
@@ -21,25 +22,25 @@ public class RsiDivergenceConfirmIndicator : Indicator
|
||||
/// Get RSI signals
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override List<LightSignal> Run()
|
||||
public override List<LightSignal> Run(HashSet<Candle> candles)
|
||||
{
|
||||
if (Candles.Count <= Period)
|
||||
if (candles.Count <= Period)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var ticker = Candles.First().Ticker;
|
||||
var ticker = candles.First().Ticker;
|
||||
|
||||
try
|
||||
{
|
||||
var rsiResult = Candles.TakeLast(10 * Period.Value).GetRsi(Period.Value).ToList();
|
||||
var candlesRsi = MapRsiToCandle(rsiResult, Candles.TakeLast(10 * Period.Value));
|
||||
var rsiResult = candles.TakeLast(10 * Period.Value).GetRsi(Period.Value).ToList();
|
||||
var candlesRsi = MapRsiToCandle(rsiResult, candles.TakeLast(10 * Period.Value));
|
||||
|
||||
if (candlesRsi.Count(c => c.Rsi > 0) == 0)
|
||||
return null;
|
||||
|
||||
GetLongSignals(candlesRsi);
|
||||
GetShortSignals(candlesRsi);
|
||||
GetLongSignals(candlesRsi, candles);
|
||||
GetShortSignals(candlesRsi, candles);
|
||||
|
||||
return Signals.Where(s => s.Confidence != Confidence.None).OrderBy(s => s.Date).ToList();
|
||||
}
|
||||
@@ -49,15 +50,15 @@ public class RsiDivergenceConfirmIndicator : Indicator
|
||||
}
|
||||
}
|
||||
|
||||
public override IndicatorsResultBase GetIndicatorValues()
|
||||
public override IndicatorsResultBase GetIndicatorValues(HashSet<Candle> candles)
|
||||
{
|
||||
return new IndicatorsResultBase()
|
||||
{
|
||||
Rsi = Candles.GetRsi(Period.Value).ToList()
|
||||
Rsi = candles.GetRsi(Period.Value).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
private void GetLongSignals(List<CandleRsi> candlesRsi)
|
||||
private void GetLongSignals(List<CandleRsi> candlesRsi, HashSet<Candle> candles)
|
||||
{
|
||||
// Set the low and high for first candle
|
||||
var firstCandleRsi = candlesRsi.First(c => c.Rsi > 0);
|
||||
@@ -124,13 +125,13 @@ public class RsiDivergenceConfirmIndicator : Indicator
|
||||
highRsi.AddItem(currentCandle);
|
||||
}
|
||||
|
||||
CheckIfConfimation(currentCandle, TradeDirection.Long);
|
||||
CheckIfConfimation(currentCandle, TradeDirection.Long, candles);
|
||||
|
||||
previousCandle = currentCandle;
|
||||
}
|
||||
}
|
||||
|
||||
private void GetShortSignals(List<CandleRsi> candlesRsi)
|
||||
private void GetShortSignals(List<CandleRsi> candlesRsi, HashSet<Candle> candles)
|
||||
{
|
||||
// Set the low and high for first candle
|
||||
var firstCandleRsi = candlesRsi.First(c => c.Rsi > 0);
|
||||
@@ -198,15 +199,15 @@ public class RsiDivergenceConfirmIndicator : Indicator
|
||||
lowRsi.AddItem(currentCandle);
|
||||
}
|
||||
|
||||
CheckIfConfimation(currentCandle, TradeDirection.Short);
|
||||
CheckIfConfimation(currentCandle, TradeDirection.Short, candles);
|
||||
|
||||
previousCandle = currentCandle;
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckIfConfimation(CandleRsi currentCandle, TradeDirection direction)
|
||||
private void CheckIfConfimation(CandleRsi currentCandle, TradeDirection direction, HashSet<Candle> candles)
|
||||
{
|
||||
var lastCandleOnPeriod = Candles.TakeLast(Period.Value).ToList();
|
||||
var lastCandleOnPeriod = candles.TakeLast(Period.Value).ToList();
|
||||
var signalsOnPeriod = Signals.Where(s => s.Date >= lastCandleOnPeriod[0].Date
|
||||
&& s.Date < currentCandle.Date
|
||||
&& s.Direction == direction
|
||||
@@ -1,4 +1,5 @@
|
||||
using Managing.Core;
|
||||
using Managing.Domain.Indicators;
|
||||
using Managing.Domain.Shared.Rules;
|
||||
using Managing.Domain.Strategies.Base;
|
||||
using Skender.Stock.Indicators;
|
||||
@@ -7,14 +8,14 @@ using Candle = Managing.Domain.Candles.Candle;
|
||||
|
||||
namespace Managing.Domain.Strategies.Signals;
|
||||
|
||||
public class RsiDivergenceIndicator : Indicator
|
||||
public class RsiDivergenceIndicatorBase : IndicatorBase
|
||||
{
|
||||
public List<LightSignal> Signals { get; set; }
|
||||
public TradeDirection Direction { get; set; }
|
||||
private const int UpperBand = 70;
|
||||
private const int LowerBand = 30;
|
||||
|
||||
public RsiDivergenceIndicator(string name, int period) : base(name, IndicatorType.RsiDivergence)
|
||||
public RsiDivergenceIndicatorBase(string name, int period) : base(name, IndicatorType.RsiDivergence)
|
||||
{
|
||||
Period = period;
|
||||
Signals = new List<LightSignal>();
|
||||
@@ -24,25 +25,25 @@ public class RsiDivergenceIndicator : Indicator
|
||||
/// Get RSI signals
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override List<LightSignal> Run()
|
||||
public override List<LightSignal> Run(HashSet<Candle> candles)
|
||||
{
|
||||
if (!Period.HasValue || Candles.Count <= Period)
|
||||
if (!Period.HasValue || candles.Count <= Period)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var ticker = Candles.First().Ticker;
|
||||
var ticker = candles.First().Ticker;
|
||||
|
||||
try
|
||||
{
|
||||
var rsiResult = Candles.TakeLast(10 * Period.Value).GetRsi(Period.Value).ToList();
|
||||
var candlesRsi = MapRsiToCandle(rsiResult, Candles.TakeLast(10 * Period.Value));
|
||||
var rsiResult = candles.TakeLast(10 * Period.Value).GetRsi(Period.Value).ToList();
|
||||
var candlesRsi = MapRsiToCandle(rsiResult, candles.TakeLast(10 * Period.Value));
|
||||
|
||||
if (candlesRsi.Count(c => c.Rsi > 0) == 0)
|
||||
return null;
|
||||
|
||||
GetLongSignals(candlesRsi);
|
||||
GetShortSignals(candlesRsi);
|
||||
GetLongSignals(candlesRsi, candles);
|
||||
GetShortSignals(candlesRsi, candles);
|
||||
|
||||
return Signals;
|
||||
}
|
||||
@@ -52,15 +53,15 @@ public class RsiDivergenceIndicator : Indicator
|
||||
}
|
||||
}
|
||||
|
||||
public override IndicatorsResultBase GetIndicatorValues()
|
||||
public override IndicatorsResultBase GetIndicatorValues(HashSet<Candle> candles)
|
||||
{
|
||||
return new IndicatorsResultBase()
|
||||
{
|
||||
Rsi = Candles.GetRsi(Period.Value).ToList()
|
||||
Rsi = candles.GetRsi(Period.Value).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
private void GetLongSignals(List<CandleRsi> candlesRsi)
|
||||
private void GetLongSignals(List<CandleRsi> candlesRsi, HashSet<Candle> candles)
|
||||
{
|
||||
// Set the low and high for first candle
|
||||
var firstCandleRsi = candlesRsi.First(c => c.Rsi > 0);
|
||||
@@ -100,7 +101,7 @@ public class RsiDivergenceIndicator : Indicator
|
||||
// Price go down but RSI go up
|
||||
if (currentCandle.Close < lowPrices.TakeLast(Period.Value).Min(p => p.Close))
|
||||
{
|
||||
AddSignal(currentCandle, TradeDirection.Long);
|
||||
AddSignal(currentCandle, TradeDirection.Long, candles);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -131,7 +132,7 @@ public class RsiDivergenceIndicator : Indicator
|
||||
}
|
||||
}
|
||||
|
||||
private void GetShortSignals(List<CandleRsi> candlesRsi)
|
||||
private void GetShortSignals(List<CandleRsi> candlesRsi, HashSet<Candle> candles)
|
||||
{
|
||||
// Set the low and high for first candle
|
||||
var firstCandleRsi = candlesRsi.First(c => c.Rsi > 0);
|
||||
@@ -173,7 +174,7 @@ public class RsiDivergenceIndicator : Indicator
|
||||
// Price go up but RSI go down
|
||||
if (currentCandle.Close > highPrices.TakeLast(Period.Value).Max(p => p.Close))
|
||||
{
|
||||
AddSignal(currentCandle, TradeDirection.Short);
|
||||
AddSignal(currentCandle, TradeDirection.Short, candles);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -203,14 +204,14 @@ public class RsiDivergenceIndicator : Indicator
|
||||
}
|
||||
}
|
||||
|
||||
private void AddSignal(CandleRsi candleSignal, TradeDirection direction)
|
||||
private void AddSignal(CandleRsi candleSignal, TradeDirection direction, HashSet<Candle> candles)
|
||||
{
|
||||
var signal = new LightSignal(MiscExtensions.ParseEnum<Ticker>(candleSignal.Ticker), direction, Confidence.Low,
|
||||
candleSignal, candleSignal.Date, candleSignal.Exchange, Type, SignalType, Name);
|
||||
|
||||
if (Signals.Count(s => s.Identifier == signal.Identifier) < 1)
|
||||
{
|
||||
var lastCandleOnPeriod = Candles.TakeLast(Period.Value).ToList();
|
||||
var lastCandleOnPeriod = candles.TakeLast(Period.Value).ToList();
|
||||
var signalsOnPeriod = Signals.Where(s => s.Date >= lastCandleOnPeriod[0].Date).ToList();
|
||||
|
||||
if (signalsOnPeriod.Count == 1)
|
||||
@@ -1,5 +1,6 @@
|
||||
using Managing.Core;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Indicators;
|
||||
using Managing.Domain.Shared.Rules;
|
||||
using Managing.Domain.Strategies.Base;
|
||||
using Skender.Stock.Indicators;
|
||||
@@ -7,11 +8,12 @@ using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Domain.Strategies.Signals;
|
||||
|
||||
public class StcIndicator : Indicator
|
||||
public class StcIndicatorBase : IndicatorBase
|
||||
{
|
||||
public List<LightSignal> Signals { get; set; }
|
||||
|
||||
public StcIndicator(string name, int cyclePeriods, int fastPeriods, int slowPeriods) : base(name, IndicatorType.Stc)
|
||||
public StcIndicatorBase(string name, int cyclePeriods, int fastPeriods, int slowPeriods) : base(name,
|
||||
IndicatorType.Stc)
|
||||
{
|
||||
Signals = new List<LightSignal>();
|
||||
FastPeriods = fastPeriods;
|
||||
@@ -19,9 +21,9 @@ public class StcIndicator : Indicator
|
||||
CyclePeriods = cyclePeriods;
|
||||
}
|
||||
|
||||
public override List<LightSignal> Run()
|
||||
public override List<LightSignal> Run(HashSet<Candle> candles)
|
||||
{
|
||||
if (Candles.Count <= 2 * (SlowPeriods + CyclePeriods))
|
||||
if (candles.Count <= 2 * (SlowPeriods + CyclePeriods))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -30,10 +32,10 @@ public class StcIndicator : Indicator
|
||||
{
|
||||
if (FastPeriods != null)
|
||||
{
|
||||
var stc = Candles.GetStc(FastPeriods.Value, FastPeriods.Value, SlowPeriods.Value).ToList();
|
||||
var stc = candles.GetStc(FastPeriods.Value, FastPeriods.Value, SlowPeriods.Value).ToList();
|
||||
if (CyclePeriods != null)
|
||||
{
|
||||
var stcCandles = MapStcToCandle(stc, Candles.TakeLast(CyclePeriods.Value));
|
||||
var stcCandles = MapStcToCandle(stc, candles.TakeLast(CyclePeriods.Value));
|
||||
|
||||
if (stc.Count == 0)
|
||||
return null;
|
||||
@@ -64,11 +66,11 @@ public class StcIndicator : Indicator
|
||||
}
|
||||
}
|
||||
|
||||
public override IndicatorsResultBase GetIndicatorValues()
|
||||
public override IndicatorsResultBase GetIndicatorValues(HashSet<Candle> candles)
|
||||
{
|
||||
if (FastPeriods != null && SlowPeriods != null)
|
||||
{
|
||||
var stc = Candles.GetStc(FastPeriods.Value, FastPeriods.Value, SlowPeriods.Value).ToList();
|
||||
var stc = candles.GetStc(FastPeriods.Value, FastPeriods.Value, SlowPeriods.Value).ToList();
|
||||
return new IndicatorsResultBase
|
||||
{
|
||||
Stc = stc
|
||||
@@ -1,5 +1,6 @@
|
||||
using Managing.Core;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Indicators;
|
||||
using Managing.Domain.Shared.Rules;
|
||||
using Managing.Domain.Strategies.Base;
|
||||
using Skender.Stock.Indicators;
|
||||
@@ -7,7 +8,7 @@ using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Domain.Strategies.Signals;
|
||||
|
||||
public class SuperTrendCrossEma : Indicator
|
||||
public class SuperTrendCrossEma : IndicatorBase
|
||||
{
|
||||
public List<LightSignal> Signals { get; set; }
|
||||
|
||||
@@ -19,7 +20,7 @@ public class SuperTrendCrossEma : Indicator
|
||||
MinimumHistory = 100 + Period.Value;
|
||||
}
|
||||
|
||||
public override List<LightSignal> Run()
|
||||
public override List<LightSignal> Run(HashSet<Candle> candles)
|
||||
{
|
||||
// Validate sufficient historical data for all indicators
|
||||
const int emaPeriod = 50;
|
||||
@@ -27,7 +28,7 @@ public class SuperTrendCrossEma : Indicator
|
||||
const int adxThreshold = 25; // Minimum ADX level to confirm a trend
|
||||
|
||||
int minimumRequiredHistory = Math.Max(Math.Max(emaPeriod, adxPeriod), Period.Value * 2); // Ensure enough data
|
||||
if (Candles.Count < minimumRequiredHistory)
|
||||
if (candles.Count < minimumRequiredHistory)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -35,20 +36,20 @@ public class SuperTrendCrossEma : Indicator
|
||||
try
|
||||
{
|
||||
// 1. Calculate indicators
|
||||
var superTrend = Candles.GetSuperTrend(Period.Value, Multiplier.Value)
|
||||
var superTrend = candles.GetSuperTrend(Period.Value, Multiplier.Value)
|
||||
.Where(s => s.SuperTrend.HasValue)
|
||||
.ToList();
|
||||
|
||||
var ema50 = Candles.GetEma(emaPeriod)
|
||||
var ema50 = candles.GetEma(emaPeriod)
|
||||
.Where(e => e.Ema.HasValue)
|
||||
.ToList();
|
||||
|
||||
var adxResults = Candles.GetAdx(adxPeriod)
|
||||
var adxResults = candles.GetAdx(adxPeriod)
|
||||
.Where(a => a.Adx.HasValue && a.Pdi.HasValue && a.Mdi.HasValue) // Ensure all values exist
|
||||
.ToList();
|
||||
|
||||
// 2. Create merged dataset with price + indicators
|
||||
var superTrendCandles = MapSuperTrendToCandle(superTrend, Candles.TakeLast(minimumRequiredHistory));
|
||||
var superTrendCandles = MapSuperTrendToCandle(superTrend, candles.TakeLast(minimumRequiredHistory));
|
||||
if (superTrendCandles.Count == 0)
|
||||
return null;
|
||||
|
||||
@@ -157,11 +158,11 @@ public class SuperTrendCrossEma : Indicator
|
||||
return superTrends;
|
||||
}
|
||||
|
||||
public override IndicatorsResultBase GetIndicatorValues()
|
||||
public override IndicatorsResultBase GetIndicatorValues(HashSet<Candle> candles)
|
||||
{
|
||||
return new IndicatorsResultBase()
|
||||
{
|
||||
SuperTrend = Candles.GetSuperTrend(Period.Value, Multiplier.Value).Where(s => s.SuperTrend.HasValue)
|
||||
SuperTrend = candles.GetSuperTrend(Period.Value, Multiplier.Value).Where(s => s.SuperTrend.HasValue)
|
||||
.ToList()
|
||||
};
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using Managing.Core;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Indicators;
|
||||
using Managing.Domain.Shared.Rules;
|
||||
using Managing.Domain.Strategies.Base;
|
||||
using Skender.Stock.Indicators;
|
||||
@@ -7,11 +8,11 @@ using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Domain.Strategies.Signals;
|
||||
|
||||
public class SuperTrendIndicator : Indicator
|
||||
public class SuperTrendIndicatorBase : IndicatorBase
|
||||
{
|
||||
public List<LightSignal> Signals { get; set; }
|
||||
|
||||
public SuperTrendIndicator(string name, int period, double multiplier) : base(name, IndicatorType.SuperTrend)
|
||||
public SuperTrendIndicatorBase(string name, int period, double multiplier) : base(name, IndicatorType.SuperTrend)
|
||||
{
|
||||
Signals = new List<LightSignal>();
|
||||
Period = period;
|
||||
@@ -19,18 +20,17 @@ public class SuperTrendIndicator : Indicator
|
||||
MinimumHistory = 100 + Period.Value;
|
||||
}
|
||||
|
||||
public override List<LightSignal> Run()
|
||||
public override List<LightSignal> Run(HashSet<Candle> candles)
|
||||
{
|
||||
if (Candles.Count <= MinimumHistory)
|
||||
if (candles.Count <= MinimumHistory)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var superTrend = Candles.GetSuperTrend(Period.Value, Multiplier.Value).Where(s => s.SuperTrend.HasValue)
|
||||
.ToList();
|
||||
var superTrendCandle = MapSuperTrendToCandle(superTrend, Candles.TakeLast(MinimumHistory));
|
||||
var superTrend = candles.GetSuperTrend(Period.Value, Multiplier.Value).Where(s => s.SuperTrend.HasValue);
|
||||
var superTrendCandle = MapSuperTrendToCandle(superTrend, candles.TakeLast(MinimumHistory));
|
||||
|
||||
if (superTrendCandle.Count == 0)
|
||||
return null;
|
||||
@@ -70,16 +70,17 @@ public class SuperTrendIndicator : Indicator
|
||||
}
|
||||
}
|
||||
|
||||
public override IndicatorsResultBase GetIndicatorValues()
|
||||
public override IndicatorsResultBase GetIndicatorValues(HashSet<Candle> candles)
|
||||
{
|
||||
return new IndicatorsResultBase()
|
||||
{
|
||||
SuperTrend = Candles.GetSuperTrend(Period.Value, Multiplier.Value).Where(s => s.SuperTrend.HasValue)
|
||||
SuperTrend = candles.GetSuperTrend(Period.Value, Multiplier.Value).Where(s => s.SuperTrend.HasValue)
|
||||
.ToList()
|
||||
};
|
||||
}
|
||||
|
||||
private List<CandleSuperTrend> MapSuperTrendToCandle(List<SuperTrendResult> superTrend, IEnumerable<Candle> candles)
|
||||
private List<CandleSuperTrend> MapSuperTrendToCandle(IEnumerable<SuperTrendResult> superTrend,
|
||||
IEnumerable<Candle> candles)
|
||||
{
|
||||
var superTrends = new List<CandleSuperTrend>();
|
||||
foreach (var candle in candles)
|
||||
@@ -1,4 +1,5 @@
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Indicators;
|
||||
using Managing.Domain.Shared.Rules;
|
||||
using Managing.Domain.Strategies.Base;
|
||||
using Managing.Domain.Strategies.Rules;
|
||||
@@ -6,9 +7,9 @@ using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Domain.Strategies.Signals
|
||||
{
|
||||
public class ThreeWhiteSoldiersIndicator : Indicator
|
||||
public class ThreeWhiteSoldiersIndicatorBase : IndicatorBase
|
||||
{
|
||||
public ThreeWhiteSoldiersIndicator(string name, int period)
|
||||
public ThreeWhiteSoldiersIndicatorBase(string name, int period)
|
||||
: base(name, IndicatorType.ThreeWhiteSoldiers)
|
||||
{
|
||||
Period = period;
|
||||
@@ -16,18 +17,18 @@ namespace Managing.Domain.Strategies.Signals
|
||||
|
||||
public TradeDirection Direction { get; }
|
||||
|
||||
public override List<LightSignal> Run()
|
||||
public override List<LightSignal> Run(HashSet<Candle> candles)
|
||||
{
|
||||
var signals = new List<LightSignal>();
|
||||
|
||||
if (Candles.Count <= 3)
|
||||
if (candles.Count <= 3)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var lastFourCandles = Candles.TakeLast(4);
|
||||
var lastFourCandles = candles.TakeLast(4);
|
||||
Candle previousCandles = null;
|
||||
|
||||
foreach (var currentCandle in lastFourCandles)
|
||||
@@ -52,7 +53,7 @@ namespace Managing.Domain.Strategies.Signals
|
||||
}
|
||||
}
|
||||
|
||||
public override IndicatorsResultBase GetIndicatorValues()
|
||||
public override IndicatorsResultBase GetIndicatorValues(HashSet<Candle> candles)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using Managing.Core;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Indicators;
|
||||
using Managing.Domain.Shared.Rules;
|
||||
using Managing.Domain.Strategies.Base;
|
||||
using Skender.Stock.Indicators;
|
||||
@@ -6,27 +8,27 @@ using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Domain.Strategies.Trends;
|
||||
|
||||
public class EmaTrendIndicator : EmaBaseIndicator
|
||||
public class EmaTrendIndicatorBase : EmaBaseIndicatorBase
|
||||
{
|
||||
public List<LightSignal> Signals { get; set; }
|
||||
|
||||
public EmaTrendIndicator(string name, int period) : base(name, IndicatorType.EmaTrend)
|
||||
public EmaTrendIndicatorBase(string name, int period) : base(name, IndicatorType.EmaTrend)
|
||||
{
|
||||
Signals = new List<LightSignal>();
|
||||
Period = period;
|
||||
}
|
||||
|
||||
public override List<LightSignal> Run()
|
||||
public override List<LightSignal> Run(HashSet<Candle> candles)
|
||||
{
|
||||
if (Candles.Count <= 2 * Period)
|
||||
if (candles.Count <= 2 * Period)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var ema = Candles.GetEma(Period.Value).ToList();
|
||||
var emaCandles = MapEmaToCandle(ema, Candles.TakeLast(Period.Value));
|
||||
var ema = candles.GetEma(Period.Value).ToList();
|
||||
var emaCandles = MapEmaToCandle(ema, candles.TakeLast(Period.Value));
|
||||
|
||||
if (ema.Count == 0)
|
||||
return null;
|
||||
@@ -54,11 +56,11 @@ public class EmaTrendIndicator : EmaBaseIndicator
|
||||
}
|
||||
}
|
||||
|
||||
public override IndicatorsResultBase GetIndicatorValues()
|
||||
public override IndicatorsResultBase GetIndicatorValues(HashSet<Candle> candles)
|
||||
{
|
||||
return new IndicatorsResultBase()
|
||||
{
|
||||
Ema = Candles.GetEma(Period.Value).ToList()
|
||||
Ema = candles.GetEma(Period.Value).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Managing.Core;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Indicators;
|
||||
using Managing.Domain.Shared.Rules;
|
||||
using Managing.Domain.Strategies.Base;
|
||||
using Skender.Stock.Indicators;
|
||||
@@ -7,11 +8,11 @@ using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Domain.Strategies.Trends;
|
||||
|
||||
public class StochRsiTrendIndicator : Indicator
|
||||
public class StochRsiTrendIndicatorBase : IndicatorBase
|
||||
{
|
||||
public List<LightSignal> Signals { get; set; }
|
||||
|
||||
public StochRsiTrendIndicator(
|
||||
public StochRsiTrendIndicatorBase(
|
||||
string name,
|
||||
int period,
|
||||
int stochPeriod,
|
||||
@@ -25,21 +26,21 @@ public class StochRsiTrendIndicator : Indicator
|
||||
Period = period;
|
||||
}
|
||||
|
||||
public override List<LightSignal> Run()
|
||||
public override List<LightSignal> Run(HashSet<Candle> candles)
|
||||
{
|
||||
if (Candles.Count <= 10 * Period + 50)
|
||||
if (candles.Count <= 10 * Period + 50)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var stochRsi = Candles
|
||||
var stochRsi = candles
|
||||
.GetStochRsi(Period.Value, StochPeriods.Value, SignalPeriods.Value, SmoothPeriods.Value)
|
||||
.RemoveWarmupPeriods().ToList();
|
||||
var stochRsiCandles = MapStochRsiToCandle(stochRsi, Candles.TakeLast(Period.Value));
|
||||
.RemoveWarmupPeriods();
|
||||
var stochRsiCandles = MapStochRsiToCandle(stochRsi, candles.TakeLast(Period.Value));
|
||||
|
||||
if (stochRsi.Count == 0)
|
||||
if (stochRsi.Count() == 0)
|
||||
return null;
|
||||
|
||||
var previousCandle = stochRsiCandles[0];
|
||||
@@ -65,16 +66,16 @@ public class StochRsiTrendIndicator : Indicator
|
||||
}
|
||||
}
|
||||
|
||||
public override IndicatorsResultBase GetIndicatorValues()
|
||||
public override IndicatorsResultBase GetIndicatorValues(HashSet<Candle> candles)
|
||||
{
|
||||
return new IndicatorsResultBase()
|
||||
{
|
||||
StochRsi = Candles.GetStochRsi(Period.Value, StochPeriods.Value, SignalPeriods.Value, SmoothPeriods.Value)
|
||||
StochRsi = candles.GetStochRsi(Period.Value, StochPeriods.Value, SignalPeriods.Value, SmoothPeriods.Value)
|
||||
.ToList()
|
||||
};
|
||||
}
|
||||
|
||||
private List<CandleStochRsi> MapStochRsiToCandle(List<StochRsiResult> ema, IEnumerable<Candle> candles)
|
||||
private List<CandleStochRsi> MapStochRsiToCandle(IEnumerable<StochRsiResult> ema, IEnumerable<Candle> candles)
|
||||
{
|
||||
var emaList = new List<CandleStochRsi>();
|
||||
foreach (var candle in candles)
|
||||
@@ -30,7 +30,7 @@ public class LightScenario
|
||||
{
|
||||
var lightScenario = new LightScenario(scenario.Name, scenario.LoopbackPeriod)
|
||||
{
|
||||
Indicators = scenario.Indicators?.Select(LightIndicator.FromIndicator).ToList() ??
|
||||
Indicators = scenario.Indicators?.Select(LightIndicator.BaseToLight).ToList() ??
|
||||
new List<LightIndicator>()
|
||||
};
|
||||
return lightScenario;
|
||||
@@ -43,15 +43,8 @@ public class LightScenario
|
||||
{
|
||||
var scenario = new Scenario(Name, LoopbackPeriod)
|
||||
{
|
||||
Indicators = Indicators?.Select(li => li.ToIndicator()).ToList() ?? new List<Indicator>()
|
||||
Indicators = Indicators?.Select(li => li.LightToBase()).ToList()
|
||||
};
|
||||
return scenario;
|
||||
}
|
||||
|
||||
public void AddIndicator(LightIndicator indicator)
|
||||
{
|
||||
if (Indicators == null)
|
||||
Indicators = new List<LightIndicator>();
|
||||
Indicators.Add(indicator);
|
||||
}
|
||||
}
|
||||
@@ -10,23 +10,19 @@ namespace Managing.Domain.Scenarios
|
||||
public Scenario(string name, int? loopbackPeriod = 1)
|
||||
{
|
||||
Name = name;
|
||||
Indicators = new List<Indicator>();
|
||||
Indicators = new List<IndicatorBase>();
|
||||
LoopbackPeriod = loopbackPeriod;
|
||||
}
|
||||
|
||||
[Id(0)]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Id(1)]
|
||||
public List<Indicator> Indicators { get; set; }
|
||||
|
||||
[Id(2)]
|
||||
public int? LoopbackPeriod { get; set; }
|
||||
|
||||
[Id(3)]
|
||||
public User User { get; set; }
|
||||
[Id(0)] public string Name { get; set; }
|
||||
|
||||
public void AddIndicator(Indicator indicator)
|
||||
[Id(1)] public List<IndicatorBase> Indicators { get; set; }
|
||||
|
||||
[Id(2)] public int? LoopbackPeriod { get; set; }
|
||||
|
||||
[Id(3)] public User User { get; set; }
|
||||
|
||||
public void AddIndicator(IndicatorBase indicator)
|
||||
{
|
||||
Indicators.Add(indicator);
|
||||
}
|
||||
|
||||
@@ -1,52 +1,97 @@
|
||||
using Managing.Core.FixedSizedQueue;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Strategies;
|
||||
using Managing.Domain.Strategies;
|
||||
using Managing.Domain.Strategies.Context;
|
||||
using Managing.Domain.Strategies.Signals;
|
||||
using Managing.Domain.Strategies.Trends;
|
||||
using Newtonsoft.Json;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Domain.Scenarios;
|
||||
|
||||
public static class ScenarioHelpers
|
||||
{
|
||||
public static IEnumerable<IIndicator> GetIndicatorsFromScenario(Scenario scenario)
|
||||
/// <summary>
|
||||
/// Compares two lists of indicators and returns a list of changes (added, removed, modified).
|
||||
/// </summary>
|
||||
/// <param name="oldIndicators">The previous list of indicators</param>
|
||||
/// <param name="newIndicators">The new list of indicators</param>
|
||||
/// <returns>A list of change descriptions</returns>
|
||||
public static List<string> CompareIndicators(List<LightIndicator> oldIndicators, List<LightIndicator> newIndicators)
|
||||
{
|
||||
var strategies = new List<IIndicator>();
|
||||
foreach (var strategy in scenario.Indicators)
|
||||
var changes = new List<string>();
|
||||
|
||||
// Create dictionaries for easier comparison using Type as key
|
||||
var oldIndicatorDict = oldIndicators.ToDictionary(i => i.Type, i => i);
|
||||
var newIndicatorDict = newIndicators.ToDictionary(i => i.Type, i => i);
|
||||
|
||||
// Find removed indicators
|
||||
var removedTypes = oldIndicatorDict.Keys.Except(newIndicatorDict.Keys);
|
||||
foreach (var removedType in removedTypes)
|
||||
{
|
||||
var result = BuildIndicator(strategy);
|
||||
strategies.Add(result);
|
||||
var indicator = oldIndicatorDict[removedType];
|
||||
changes.Add($"➖ **Removed Indicator:** {removedType} ({indicator.GetType().Name})");
|
||||
}
|
||||
|
||||
return strategies;
|
||||
// Find added indicators
|
||||
var addedTypes = newIndicatorDict.Keys.Except(oldIndicatorDict.Keys);
|
||||
foreach (var addedType in addedTypes)
|
||||
{
|
||||
var indicator = newIndicatorDict[addedType];
|
||||
changes.Add($"➕ **Added Indicator:** {addedType} ({indicator.GetType().Name})");
|
||||
}
|
||||
|
||||
// Find modified indicators (same type but potentially different configuration)
|
||||
var commonTypes = oldIndicatorDict.Keys.Intersect(newIndicatorDict.Keys);
|
||||
foreach (var commonType in commonTypes)
|
||||
{
|
||||
var oldIndicator = oldIndicatorDict[commonType];
|
||||
var newIndicator = newIndicatorDict[commonType];
|
||||
|
||||
// Compare indicators by serializing them (simple way to detect configuration changes)
|
||||
var oldSerialized = JsonConvert.SerializeObject(oldIndicator, Formatting.None);
|
||||
var newSerialized = JsonConvert.SerializeObject(newIndicator, Formatting.None);
|
||||
|
||||
if (oldSerialized != newSerialized)
|
||||
{
|
||||
changes.Add($"🔄 **Modified Indicator:** {commonType} ({newIndicator.GetType().Name})");
|
||||
}
|
||||
}
|
||||
|
||||
// Add summary if there are changes
|
||||
if (changes.Any())
|
||||
{
|
||||
var summary =
|
||||
$"📊 **Indicator Changes:** {addedTypes.Count()} added, {removedTypes.Count()} removed, {commonTypes.Count(c => JsonConvert.SerializeObject(oldIndicatorDict[c]) != JsonConvert.SerializeObject(newIndicatorDict[c]))} modified";
|
||||
changes.Insert(0, summary);
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
public static IIndicator BuildIndicator(Indicator indicator, int size = 600)
|
||||
public static IIndicator BuildIndicator(LightIndicator indicator)
|
||||
{
|
||||
IIndicator result = indicator.Type switch
|
||||
{
|
||||
IndicatorType.StDev => new StDevContext(indicator.Name, indicator.Period.Value),
|
||||
IndicatorType.RsiDivergence => new RsiDivergenceIndicator(indicator.Name,
|
||||
IndicatorType.RsiDivergence => new RsiDivergenceIndicatorBase(indicator.Name,
|
||||
indicator.Period.Value),
|
||||
IndicatorType.RsiDivergenceConfirm => new RsiDivergenceConfirmIndicator(indicator.Name,
|
||||
IndicatorType.RsiDivergenceConfirm => new RsiDivergenceConfirmIndicatorBase(indicator.Name,
|
||||
indicator.Period.Value),
|
||||
IndicatorType.MacdCross => new MacdCrossIndicator(indicator.Name,
|
||||
IndicatorType.MacdCross => new MacdCrossIndicatorBase(indicator.Name,
|
||||
indicator.FastPeriods.Value, indicator.SlowPeriods.Value, indicator.SignalPeriods.Value),
|
||||
IndicatorType.EmaCross => new EmaCrossIndicator(indicator.Name, indicator.Period.Value),
|
||||
IndicatorType.DualEmaCross => new DualEmaCrossIndicator(indicator.Name,
|
||||
IndicatorType.EmaCross => new EmaCrossIndicatorBase(indicator.Name, indicator.Period.Value),
|
||||
IndicatorType.DualEmaCross => new DualEmaCrossIndicatorBase(indicator.Name,
|
||||
indicator.FastPeriods.Value, indicator.SlowPeriods.Value),
|
||||
IndicatorType.ThreeWhiteSoldiers => new ThreeWhiteSoldiersIndicator(indicator.Name,
|
||||
IndicatorType.ThreeWhiteSoldiers => new ThreeWhiteSoldiersIndicatorBase(indicator.Name,
|
||||
indicator.Period.Value),
|
||||
IndicatorType.SuperTrend => new SuperTrendIndicator(indicator.Name,
|
||||
IndicatorType.SuperTrend => new SuperTrendIndicatorBase(indicator.Name,
|
||||
indicator.Period.Value, indicator.Multiplier.Value),
|
||||
IndicatorType.ChandelierExit => new ChandelierExitIndicator(indicator.Name,
|
||||
IndicatorType.ChandelierExit => new ChandelierExitIndicatorBase(indicator.Name,
|
||||
indicator.Period.Value, indicator.Multiplier.Value),
|
||||
IndicatorType.EmaTrend => new EmaTrendIndicator(indicator.Name, indicator.Period.Value),
|
||||
IndicatorType.StochRsiTrend => new StochRsiTrendIndicator(indicator.Name,
|
||||
IndicatorType.EmaTrend => new EmaTrendIndicatorBase(indicator.Name, indicator.Period.Value),
|
||||
IndicatorType.StochRsiTrend => new StochRsiTrendIndicatorBase(indicator.Name,
|
||||
indicator.Period.Value, indicator.StochPeriods.Value, indicator.SignalPeriods.Value,
|
||||
indicator.SmoothPeriods.Value),
|
||||
IndicatorType.Stc => new StcIndicator(indicator.Name, indicator.CyclePeriods.Value,
|
||||
IndicatorType.Stc => new StcIndicatorBase(indicator.Name, indicator.CyclePeriods.Value,
|
||||
indicator.FastPeriods.Value, indicator.SlowPeriods.Value),
|
||||
IndicatorType.LaggingStc => new LaggingSTC(indicator.Name, indicator.CyclePeriods.Value,
|
||||
indicator.FastPeriods.Value, indicator.SlowPeriods.Value),
|
||||
@@ -55,11 +100,10 @@ public static class ScenarioHelpers
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
result.Candles = new FixedSizeQueue<Candle>(size);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Indicator BuildIndicator(
|
||||
public static IIndicator BuildIndicator(
|
||||
IndicatorType type,
|
||||
string name,
|
||||
int? period = null,
|
||||
@@ -71,7 +115,7 @@ public static class ScenarioHelpers
|
||||
int? smoothPeriods = null,
|
||||
int? cyclePeriods = null)
|
||||
{
|
||||
var indicator = new Indicator(name, type);
|
||||
IIndicator indicator = null;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
|
||||
@@ -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() &&
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Exilion.TradingAtomics;
|
||||
using Managing.Domain.Accounts;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Statistics;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
@@ -7,7 +8,7 @@ namespace Managing.Domain.Shared.Helpers;
|
||||
|
||||
public static class TradingHelpers
|
||||
{
|
||||
public static decimal GetHodlPercentage(Candles.Candle candle1, Candles.Candle candle2)
|
||||
public static decimal GetHodlPercentage(Candle candle1, Candle candle2)
|
||||
{
|
||||
return candle2.Close * 100 / candle1.Close - 100;
|
||||
}
|
||||
|
||||
47
src/Managing.Domain/Statistics/AgentSummary.cs
Normal file
47
src/Managing.Domain/Statistics/AgentSummary.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using Managing.Domain.Users;
|
||||
using Orleans;
|
||||
|
||||
namespace Managing.Domain.Statistics;
|
||||
|
||||
[GenerateSerializer]
|
||||
public class AgentSummary
|
||||
{
|
||||
[Id(0)]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Id(1)]
|
||||
public int UserId { get; set; }
|
||||
|
||||
[Id(2)]
|
||||
public string AgentName { get; set; }
|
||||
|
||||
[Id(3)]
|
||||
public decimal TotalPnL { get; set; }
|
||||
|
||||
[Id(4)]
|
||||
public decimal TotalROI { get; set; }
|
||||
|
||||
[Id(5)]
|
||||
public int Wins { get; set; }
|
||||
|
||||
[Id(6)]
|
||||
public int Losses { get; set; }
|
||||
|
||||
[Id(7)]
|
||||
public DateTime? Runtime { get; set; }
|
||||
|
||||
[Id(8)]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
[Id(9)]
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
|
||||
[Id(10)]
|
||||
public User User { get; set; }
|
||||
|
||||
[Id(11)]
|
||||
public int ActiveStrategiesCount { get; set; }
|
||||
|
||||
[Id(12)]
|
||||
public decimal TotalVolume { get; set; }
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Managing.Common;
|
||||
using Managing.Domain.Indicators;
|
||||
using Managing.Domain.Scenarios;
|
||||
|
||||
namespace Managing.Domain.Statistics;
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
using Managing.Core.FixedSizedQueue;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Scenarios;
|
||||
using Managing.Domain.Strategies.Base;
|
||||
using Managing.Domain.Users;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Domain.Strategies
|
||||
{
|
||||
public class Indicator : IIndicator
|
||||
{
|
||||
public Indicator(string name, IndicatorType type)
|
||||
{
|
||||
Name = name;
|
||||
Type = type;
|
||||
SignalType = ScenarioHelpers.GetSignalType(type);
|
||||
Candles = new FixedSizeQueue<Candle>(500);
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public FixedSizeQueue<Candle> Candles { get; set; }
|
||||
|
||||
public IndicatorType Type { get; set; }
|
||||
|
||||
public SignalType SignalType { get; set; }
|
||||
|
||||
public int MinimumHistory { get; set; }
|
||||
|
||||
public int? Period { get; set; }
|
||||
|
||||
public int? FastPeriods { get; set; }
|
||||
|
||||
public int? SlowPeriods { get; set; }
|
||||
|
||||
public int? SignalPeriods { get; set; }
|
||||
|
||||
public double? Multiplier { get; set; }
|
||||
|
||||
public int? SmoothPeriods { get; set; }
|
||||
|
||||
public int? StochPeriods { get; set; }
|
||||
|
||||
public int? CyclePeriods { get; set; }
|
||||
|
||||
public User User { get; set; }
|
||||
|
||||
public virtual List<LightSignal> Run()
|
||||
{
|
||||
return new List<LightSignal>();
|
||||
}
|
||||
|
||||
public virtual IndicatorsResultBase GetIndicatorValues()
|
||||
{
|
||||
return new IndicatorsResultBase();
|
||||
}
|
||||
|
||||
public void UpdateCandles(HashSet<Candle> newCandles)
|
||||
{
|
||||
if (newCandles == null || newCandles.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (Candles)
|
||||
{
|
||||
foreach (var item in newCandles.ToList())
|
||||
{
|
||||
if (Candles.All(c => c.Date != item.Date))
|
||||
{
|
||||
Candles.Enqueue(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string GetName()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Globalization;
|
||||
using Managing.Core;
|
||||
using Managing.Domain.Candles;
|
||||
using Orleans;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
[GenerateSerializer]
|
||||
public class LightSignal : ValueObject
|
||||
{
|
||||
public LightSignal(Ticker ticker, TradeDirection direction, Confidence confidence, Candle candle, DateTime date,
|
||||
TradingExchanges exchange, IndicatorType indicatorType, SignalType signalType, string indicatorName)
|
||||
{
|
||||
Direction = direction;
|
||||
Confidence = confidence;
|
||||
Candle = candle;
|
||||
Date = date;
|
||||
Ticker = ticker;
|
||||
Exchange = exchange;
|
||||
Status = SignalStatus.WaitingForPosition;
|
||||
IndicatorType = indicatorType;
|
||||
IndicatorName = indicatorName;
|
||||
SignalType = signalType;
|
||||
|
||||
Identifier =
|
||||
$"{indicatorName}-{indicatorType}-{direction}-{ticker}-{candle?.Close.ToString(CultureInfo.InvariantCulture)}-{date:yyyyMMdd-HHmmss}";
|
||||
}
|
||||
|
||||
[Id(0)]
|
||||
[Required] public SignalStatus Status { get; set; }
|
||||
|
||||
[Id(1)]
|
||||
[Required] public TradeDirection Direction { get; }
|
||||
|
||||
[Id(2)]
|
||||
[Required] public Confidence Confidence { get; set; }
|
||||
|
||||
[Id(3)]
|
||||
[Required] public Timeframe Timeframe { get; }
|
||||
|
||||
[Id(4)]
|
||||
[Required] public DateTime Date { get; private set; }
|
||||
|
||||
[Id(5)]
|
||||
[Required] public Candle Candle { get; }
|
||||
|
||||
[Id(6)]
|
||||
[Required] public string Identifier { get; }
|
||||
|
||||
[Id(7)]
|
||||
[Required] public Ticker Ticker { get; }
|
||||
|
||||
[Id(8)]
|
||||
[Required] public TradingExchanges Exchange { get; set; }
|
||||
|
||||
[Id(9)]
|
||||
[Required] public IndicatorType IndicatorType { get; set; }
|
||||
|
||||
[Id(10)]
|
||||
[Required] public SignalType SignalType { get; set; }
|
||||
|
||||
[Id(11)]
|
||||
[Required] public string IndicatorName { get; set; }
|
||||
|
||||
protected override IEnumerable<object> GetEqualityComponents()
|
||||
{
|
||||
yield return Direction;
|
||||
yield return Confidence;
|
||||
yield return Date;
|
||||
}
|
||||
|
||||
public void SetConfidence(Confidence confidence)
|
||||
{
|
||||
Confidence = confidence;
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ namespace Managing.Domain.Trades
|
||||
[GenerateSerializer]
|
||||
public class Position
|
||||
{
|
||||
public Position(string identifier, string accountName, TradeDirection originDirection, Ticker ticker,
|
||||
public Position(Guid identifier, string accountName, TradeDirection originDirection, Ticker ticker,
|
||||
LightMoneyManagement moneyManagement, PositionInitiator initiator, DateTime date, User user)
|
||||
{
|
||||
Identifier = identifier;
|
||||
@@ -23,23 +23,20 @@ namespace Managing.Domain.Trades
|
||||
User = user;
|
||||
}
|
||||
|
||||
[Id(0)]
|
||||
[Required] public string AccountName { get; set; }
|
||||
|
||||
[Id(1)]
|
||||
[Required] public DateTime Date { get; set; }
|
||||
|
||||
[Id(2)]
|
||||
[Required] public TradeDirection OriginDirection { get; set; }
|
||||
|
||||
[Id(3)]
|
||||
[Required] public Ticker Ticker { get; set; }
|
||||
|
||||
[Id(4)]
|
||||
[Required] public LightMoneyManagement MoneyManagement { get; set; }
|
||||
|
||||
[Id(0)] [Required] public string AccountName { get; set; }
|
||||
|
||||
[Id(1)] [Required] public DateTime Date { get; set; }
|
||||
|
||||
[Id(2)] [Required] public TradeDirection OriginDirection { get; set; }
|
||||
|
||||
[Id(3)] [Required] public Ticker Ticker { get; set; }
|
||||
|
||||
[Id(4)] [Required] public LightMoneyManagement MoneyManagement { get; set; }
|
||||
|
||||
[Id(5)]
|
||||
[Required] [JsonPropertyName("Open")] public Trade Open { get; set; }
|
||||
[Required]
|
||||
[JsonPropertyName("Open")]
|
||||
public Trade Open { get; set; }
|
||||
|
||||
[Id(6)]
|
||||
[Required]
|
||||
@@ -52,25 +49,22 @@ namespace Managing.Domain.Trades
|
||||
public Trade TakeProfit1 { get; set; }
|
||||
|
||||
[Id(8)]
|
||||
[JsonPropertyName("TakeProfit2")] public Trade TakeProfit2 { get; set; }
|
||||
[JsonPropertyName("TakeProfit2")]
|
||||
public Trade TakeProfit2 { get; set; }
|
||||
|
||||
[Id(9)]
|
||||
[JsonPropertyName("ProfitAndLoss")] public ProfitAndLoss ProfitAndLoss { get; set; }
|
||||
|
||||
[Id(10)]
|
||||
[Required] public PositionStatus Status { get; set; }
|
||||
|
||||
[Id(11)]
|
||||
public string SignalIdentifier { get; set; }
|
||||
|
||||
[Id(12)]
|
||||
[Required] public string Identifier { get; set; }
|
||||
|
||||
[Id(13)]
|
||||
[Required] public PositionInitiator Initiator { get; set; }
|
||||
|
||||
[Id(14)]
|
||||
[Required] public User User { get; set; }
|
||||
[JsonPropertyName("ProfitAndLoss")]
|
||||
public ProfitAndLoss ProfitAndLoss { get; set; }
|
||||
|
||||
[Id(10)] [Required] public PositionStatus Status { get; set; }
|
||||
|
||||
[Id(11)] public string SignalIdentifier { get; set; }
|
||||
|
||||
[Id(12)] [Required] public Guid Identifier { get; set; }
|
||||
|
||||
[Id(13)] [Required] public PositionInitiator Initiator { get; set; }
|
||||
|
||||
[Id(14)] [Required] public User User { get; set; }
|
||||
|
||||
public bool IsFinished()
|
||||
{
|
||||
|
||||
@@ -7,17 +7,20 @@ namespace Managing.Domain.Users;
|
||||
public class User
|
||||
{
|
||||
[Id(0)]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Id(1)]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Id(1)]
|
||||
[Id(2)]
|
||||
public List<Account> Accounts { get; set; }
|
||||
|
||||
[Id(2)]
|
||||
[Id(3)]
|
||||
public string AgentName { get; set; }
|
||||
|
||||
[Id(3)]
|
||||
[Id(4)]
|
||||
public string AvatarUrl { get; set; }
|
||||
|
||||
[Id(4)]
|
||||
[Id(5)]
|
||||
public string TelegramChannel { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user