docker files fixes from liaqat

This commit is contained in:
alirehmani
2024-05-03 16:39:25 +05:00
commit 464a8730e8
587 changed files with 44288 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
using Managing.Common;
using Managing.Domain.Candles;
using Skender.Stock.Indicators;
namespace Managing.Domain.Strategies.Base;
public abstract class EmaBaseStrategy : Strategy
{
protected EmaBaseStrategy(string name, Enums.Timeframe timeframe, Enums.StrategyType type) : base(name, timeframe, type)
{
}
protected List<CandleEma> MapEmaToCandle(List<EmaResult> ema, IEnumerable<Candle> candles)
{
var emaList = new List<CandleEma>();
foreach (var candle in candles)
{
var currentEma = ema.Find(candle.Date);
if (currentEma != null && currentEma.Ema.HasValue)
{
emaList.Add(new CandleEma()
{
Close = candle.Close,
Open = candle.Open,
Date = candle.Date,
Ticker = candle.Ticker,
Exchange = candle.Exchange,
Ema = currentEma.Ema.Value,
});
}
}
return emaList;
}
public class CandleEma : Candle
{
public double Ema { get; set; }
}
}

View File

@@ -0,0 +1,115 @@
using Managing.Core;
using Managing.Domain.Candles;
using Managing.Domain.Shared.Rules;
using Skender.Stock.Indicators;
using static Managing.Common.Enums;
namespace Managing.Domain.Strategies;
public class ChandelierExitStrategy : Strategy
{
public List<Signal> Signals { get; set; }
public ChandelierExitStrategy(string name, Timeframe timeframe, int period, double multiplier) : base(name, timeframe, StrategyType.ChandelierExit)
{
Signals = new List<Signal>();
Period = period;
Multiplier = multiplier;
MinimumHistory = 1 + Period.Value;
}
public override List<Signal> Run()
{
if (Candles.Count <= MinimumHistory)
{
return null;
}
try
{
GetSignals(ChandelierType.Long);
GetSignals(ChandelierType.Short);
return Signals.Where(s => s.Confidence != Confidence.None).OrderBy(s => s.Date).ToList();
}
catch (RuleException)
{
return null;
}
}
private void GetSignals(ChandelierType chandelierType)
{
var chandelier = Candles.GetChandelier(Period.Value, Multiplier.Value, chandelierType).Where(s => s.ChandelierExit.HasValue).ToList();
var chandelierCandle = MapChandelierToCandle(chandelier, Candles.TakeLast(MinimumHistory));
var previousCandle = chandelierCandle[0];
foreach (var currentCandle in chandelierCandle.Skip(1))
{
// Short
if (currentCandle.Close < previousCandle.ChandelierExit &&
previousCandle.Close > previousCandle.ChandelierExit &&
currentCandle.Close < previousCandle.Open &&
chandelierType == ChandelierType.Short)
{
AddSignal(currentCandle, Timeframe, TradeDirection.Short, Confidence.Medium);
}
// Long
if (currentCandle.Close > previousCandle.ChandelierExit &&
previousCandle.Close < previousCandle.ChandelierExit &&
currentCandle.Close > currentCandle.Open &&
chandelierType == ChandelierType.Long)
{
AddSignal(currentCandle, Timeframe, TradeDirection.Long, Confidence.Medium);
}
previousCandle = currentCandle;
}
}
private List<CandleChandelier> MapChandelierToCandle(List<ChandelierResult> superTrend, IEnumerable<Candle> candles)
{
var superTrends = new List<CandleChandelier>();
foreach (var candle in candles)
{
var currentChandelier = superTrend.Find(candle.Date);
if (currentChandelier != null)
{
superTrends.Add(new CandleChandelier()
{
Close = candle.Close,
Open = candle.Open,
Date = candle.Date,
Ticker = candle.Ticker,
Exchange = candle.Exchange,
ChandelierExit = (decimal)currentChandelier.ChandelierExit.Value,
});
}
}
return superTrends;
}
private void AddSignal(CandleChandelier candleSignal, Timeframe timeframe, TradeDirection direction, Confidence confidence)
{
var signal = new Signal(
MiscExtensions.ParseEnum<Ticker>(candleSignal.Ticker),
direction,
confidence,
candleSignal,
candleSignal.Date,
candleSignal.Exchange,
timeframe,
Type, SignalType);
if (!Signals.Any(s => s.Identifier == signal.Identifier))
{
Signals.AddItem(signal);
}
}
private class CandleChandelier : Candle
{
public decimal ChandelierExit { get; internal set; }
}
}

View File

@@ -0,0 +1,69 @@
using Managing.Core;
using Managing.Domain.Shared.Rules;
using Managing.Domain.Strategies;
using Managing.Domain.Strategies.Base;
using Skender.Stock.Indicators;
using static Managing.Common.Enums;
namespace Managing.Application.Strategies;
public class EmaCrossStrategy : EmaBaseStrategy
{
public List<Signal> Signals { get; set; }
public EmaCrossStrategy(string name, Timeframe timeframe, int period) : base(name, timeframe, StrategyType.EmaCross)
{
Signals = new List<Signal>();
Period = period;
}
public override List<Signal> Run()
{
if (Candles.Count <= Period)
{
return null;
}
try
{
var ema = Candles.GetEma(Period.Value).ToList();
var emaCandles = MapEmaToCandle(ema, Candles.TakeLast(Period.Value));
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, Timeframe, TradeDirection.Short, Confidence.Medium);
}
if (previousCandle.Close < (decimal)currentCandle.Ema &&
currentCandle.Close > (decimal)currentCandle.Ema)
{
AddSignal(currentCandle, Timeframe, 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, Timeframe timeframe, TradeDirection direction, Confidence confidence)
{
var signal = new Signal(MiscExtensions.ParseEnum<Ticker>(candleSignal.Ticker), direction, confidence, candleSignal, candleSignal.Date, candleSignal.Exchange, timeframe, Type, SignalType);
if (!Signals.Any(s => s.Identifier == signal.Identifier))
{
Signals.AddItem(signal);
}
}
}

View File

@@ -0,0 +1,65 @@
using Managing.Core;
using Managing.Domain.Shared.Rules;
using Managing.Domain.Strategies.Base;
using Skender.Stock.Indicators;
using static Managing.Common.Enums;
namespace Managing.Domain.Strategies;
public class EmaTrendStrategy : EmaBaseStrategy
{
public List<Signal> Signals { get; set; }
public EmaTrendStrategy(string name, Timeframe timeframe, int period) : base(name, timeframe, StrategyType.EmaTrend)
{
Signals = new List<Signal>();
Period = period;
}
public override List<Signal> Run()
{
if (Candles.Count <= 2 * Period)
{
return null;
}
try
{
var ema = Candles.GetEma(Period.Value).ToList();
var emaCandles = MapEmaToCandle(ema, Candles.TakeLast(Period.Value));
if (ema.Count == 0)
return null;
var previousCandle = emaCandles[0];
foreach (var currentCandle in emaCandles.Skip(1))
{
if (currentCandle.Close > (decimal)currentCandle.Ema)
{
AddSignal(currentCandle, Timeframe, TradeDirection.Long, Confidence.None);
}
else
{
AddSignal(currentCandle, Timeframe, TradeDirection.Short, Confidence.None);
}
previousCandle = currentCandle;
}
return Signals.OrderBy(s => s.Date).ToList();
}
catch (RuleException)
{
return null;
}
}
public void AddSignal(CandleEma candleSignal, Timeframe timeframe, TradeDirection direction, Confidence confidence)
{
var signal = new Signal(MiscExtensions.ParseEnum<Ticker>(candleSignal.Ticker), direction, confidence, candleSignal, candleSignal.Date, candleSignal.Exchange, timeframe, Type, SignalType);
if (!Signals.Any(s => s.Identifier == signal.Identifier))
{
Signals.AddItem(signal);
}
}
}

View File

@@ -0,0 +1,20 @@
using Managing.Domain.Candles;
using static Managing.Common.Enums;
namespace Managing.Domain.Strategies
{
public interface IStrategy
{
string Name { get; set; }
StrategyType Type { get; set; }
SignalType SignalType { get; set; }
int? Period { get; set; }
int? FastPeriods { get; set; }
int? SlowPeriods { get; set; }
int? SignalPeriods { get; set; }
List<Signal> Run();
void UpdateCandles(HashSet<Candle> newCandles);
string GetName();
}
}

View File

@@ -0,0 +1,102 @@
using Managing.Core;
using Managing.Domain.Candles;
using Managing.Domain.Shared.Rules;
using Skender.Stock.Indicators;
using static Managing.Common.Enums;
namespace Managing.Domain.Strategies;
public class MACDCrossStrategy : Strategy
{
public List<Signal> Signals { get; set; }
public MACDCrossStrategy(string name, Timeframe timeframe, int fastPeriods, int slowPeriods, int signalPeriods) : base(name, timeframe, StrategyType.MacdCross)
{
Signals = new List<Signal>();
FastPeriods = fastPeriods;
SlowPeriods = slowPeriods;
SignalPeriods = signalPeriods;
}
public override List<Signal> Run()
{
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));
if (macd.Count == 0)
return null;
var previousCandle = macdCandle[0];
foreach (var currentCandle in macdCandle.Skip(1))
{
if (previousCandle.Histogram > 0 && currentCandle.Histogram < 0 && currentCandle.Macd < 0)
{
AddSignal(currentCandle, Timeframe, TradeDirection.Short, Confidence.Medium);
}
if (previousCandle.Histogram < 0 && currentCandle.Histogram > 0 && currentCandle.Macd > 0)
{
AddSignal(currentCandle, Timeframe, TradeDirection.Long, Confidence.Medium);
}
previousCandle = currentCandle;
}
return Signals.Where(s => s.Confidence != Confidence.None).OrderBy(s => s.Date).ToList();
}
catch (RuleException)
{
return null;
}
}
private List<CandleMacd> MapMacdToCandle(List<MacdResult> macd, IEnumerable<Candle> candles)
{
var macdList = new List<CandleMacd>();
foreach (var candle in candles)
{
var currentMacd = macd.Find(candle.Date);
if (currentMacd != null)
{
macdList.Add(new CandleMacd()
{
Close = candle.Close,
Open = candle.Open,
Date = candle.Date,
Ticker = candle.Ticker,
Exchange = candle.Exchange,
FastEma = currentMacd.FastEma.Value,
SlowEma = currentMacd.SlowEma.Value,
Macd = currentMacd.Macd.Value,
Histogram = currentMacd.Histogram.Value
});
}
}
return macdList;
}
private void AddSignal(CandleMacd candleSignal, Timeframe timeframe, TradeDirection direction, Confidence confidence)
{
var signal = new Signal(MiscExtensions.ParseEnum<Ticker>(candleSignal.Ticker), direction, confidence, candleSignal, candleSignal.Date, candleSignal.Exchange, timeframe, Type, SignalType);
if (!Signals.Any(s => s.Identifier == signal.Identifier))
{
Signals.AddItem(signal);
}
}
private class CandleMacd : Candle
{
public double Macd { get; set; }
public double Signal { get; set; }
public double Histogram { get; set; }
public double FastEma { get; set; }
public double SlowEma { get; set; }
}
}

View File

@@ -0,0 +1,252 @@
using Managing.Core;
using Managing.Domain.Shared.Rules;
using Skender.Stock.Indicators;
using static Managing.Common.Enums;
using Candle = Managing.Domain.Candles.Candle;
namespace Managing.Domain.Strategies;
public class RSIDivergenceConfirmStrategy : Strategy
{
public List<Signal> Signals { get; set; }
public RSIDivergenceConfirmStrategy(string name, Timeframe timeframe, int period) : base(name, timeframe, StrategyType.RsiDivergenceConfirm)
{
Period = period;
Signals = new List<Signal>();
}
/// <summary>
/// Get RSI signals
/// </summary>
/// <returns></returns>
public override List<Signal> Run()
{
if (Candles.Count <= Period)
{
return null;
}
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));
if (candlesRsi.Count(c => c.Rsi > 0) == 0)
return null;
GetLongSignals(candlesRsi);
GetShortSignals(candlesRsi);
return Signals.Where(s => s.Confidence != Confidence.None).OrderBy(s => s.Date).ToList();
}
catch (RuleException)
{
return null;
}
}
private void GetLongSignals(List<CandleRsi> candlesRsi)
{
// Set the low and high for first candle
var firstCandleRsi = candlesRsi.First(c => c.Rsi > 0);
var highPrices = new List<CandleRsi>();
var lowPrices = new List<CandleRsi>();
var highRsi = new List<CandleRsi>();
var lowRsi = new List<CandleRsi>();
highPrices.Add(firstCandleRsi);
lowPrices.Add(firstCandleRsi);
highRsi.Add(firstCandleRsi);
lowRsi.Add(firstCandleRsi);
var previousCandle = firstCandleRsi;
// For a long
foreach (var currentCandle in candlesRsi.FindAll(r => r.Rsi > 0).Skip(1))
{
// If price go down
if (previousCandle.Close > currentCandle.Close)
{
// because the last price is upper than the current
highPrices.AddItem(previousCandle);
// Check if rsi is higher than the last lowest
if (currentCandle.Rsi > lowRsi.TakeLast(Period.Value).Min(r => r.Rsi))
{
// If new higher high, we set it
if (currentCandle.Rsi > highRsi.Last().Rsi)
highRsi.AddItem(currentCandle);
if (currentCandle.Rsi > lowRsi.Last().Rsi)
lowRsi.AddItem(currentCandle);
// Price go down but RSI go up
if (currentCandle.Close < lowPrices.TakeLast(Period.Value).Min(p => p.Close))
{
AddSignal(currentCandle, Timeframe, TradeDirection.Long, Confidence.None);
}
}
else
{
// No divergence, price go down, rsi go down
lowRsi.AddItem(currentCandle);
}
lowPrices.AddItem(currentCandle);
}
else
{
// Price go up, so we have to update if price is a new higher high than previous candle
// Normally always true
if (previousCandle.Close < currentCandle.Close)
highPrices.AddItem(currentCandle); //15-15-12-14-17
// If rsi is lower low or not set
if (currentCandle.Rsi < lowRsi.Last().Rsi || lowRsi.Last().Rsi == 0)
lowRsi.AddItem(currentCandle);
// Price going up, so if its a new high we set it
if (currentCandle.Rsi > highRsi.Last().Rsi)
highRsi.AddItem(currentCandle);
}
CheckIfConfimation(currentCandle, TradeDirection.Long);
previousCandle = currentCandle;
}
}
private void GetShortSignals(List<CandleRsi> candlesRsi)
{
// Set the low and high for first candle
var firstCandleRsi = candlesRsi.First(c => c.Rsi > 0);
var signals = new List<Signal>();
var highPrices = new List<CandleRsi>();
var lowPrices = new List<CandleRsi>();
var highRsi = new List<CandleRsi>();
var lowRsi = new List<CandleRsi>();
highPrices.Add(firstCandleRsi);
lowPrices.Add(firstCandleRsi);
highRsi.Add(firstCandleRsi);
lowRsi.Add(firstCandleRsi);
var previousCandle = firstCandleRsi;
// For a short
foreach (var currentCandle in candlesRsi.FindAll(r => r.Rsi > 0).Skip(1))
{
// If price go up
if (previousCandle.Close < currentCandle.Close)
{
// because the last price is lower than the current
lowPrices.AddItem(previousCandle);
// Check if rsi is lower than the last high
if (currentCandle.Rsi < highRsi.TakeLast(Period.Value).Max(r => r.Rsi))
{
// If new lower low, we set it
if (currentCandle.Rsi < lowRsi.Last().Rsi)
lowRsi.AddItem(currentCandle);
if (currentCandle.Rsi < highRsi.Last().Rsi)
highRsi.AddItem(currentCandle);
// Price go up but RSI go down
if (currentCandle.Close > highPrices.TakeLast(Period.Value).Max(p => p.Close))
{
AddSignal(currentCandle, Timeframe, TradeDirection.Short, Confidence.None);
}
}
else
{
// No divergence, price go up, rsi go up
highRsi.AddItem(currentCandle);
}
highPrices.AddItem(currentCandle);
}
else
{
// Price go down, so we have to update if price is a new lower low than previous candle
if (previousCandle.Close > currentCandle.Close)
lowPrices.AddItem(currentCandle);
// If rsi is higher high or not set
if (currentCandle.Rsi > highRsi.Last().Rsi || highRsi.Last().Rsi == 0)
highRsi.AddItem(currentCandle);
// Price going down, so if its a new low we set it
if (currentCandle.Rsi < lowRsi.Last().Rsi)
lowRsi.AddItem(currentCandle);
}
CheckIfConfimation(currentCandle, TradeDirection.Short);
previousCandle = currentCandle;
}
}
private void CheckIfConfimation(CandleRsi currentCandle, TradeDirection direction)
{
var lastCandleOnPeriod = Candles.TakeLast(Period.Value).ToList();
var signalsOnPeriod = Signals.Where(s => s.Date >= lastCandleOnPeriod[0].Date
&& s.Date < currentCandle.Date
&& s.Direction == direction
&& s.Confidence == Confidence.None
&& s.Status != SignalStatus.Expired
&& s.Status != SignalStatus.PositionOpen).ToList();
foreach (var signal in signalsOnPeriod)
{
if (direction == TradeDirection.Short && currentCandle.Close < signal.Candle.Open)
{
AddSignal(currentCandle, Timeframe, direction, Confidence.High);
Signals.FirstOrDefault(s => s.Identifier == signal.Identifier).Status = SignalStatus.Expired;
}
if (direction == TradeDirection.Long && currentCandle.Close > signal.Candle.Open)
{
AddSignal(currentCandle, Timeframe, direction, Confidence.High);
Signals.FirstOrDefault(s => s.Identifier == signal.Identifier).Status = SignalStatus.Expired;
}
}
}
private void AddSignal(CandleRsi candleSignal, Timeframe timeframe, TradeDirection direction, Confidence confidence)
{
var signal = new Signal(MiscExtensions.ParseEnum<Ticker>(candleSignal.Ticker), direction, confidence, candleSignal, candleSignal.Date, candleSignal.Exchange, timeframe, Type, SignalType);
if (!Signals.Any(s => s.Identifier == signal.Identifier))
{
Signals.AddItem(signal);
}
}
private List<CandleRsi> MapRsiToCandle(IReadOnlyCollection<RsiResult> rsiResult,
IEnumerable<Candle> candles)
{
return candles.Select(c => new CandleRsi()
{
Close = c.Close,
Open = c.Open,
Rsi = rsiResult.Find(c.Date).Rsi.GetValueOrDefault(),
Date = c.Date,
Ticker = c.Ticker,
Exchange = c.Exchange
}).ToList();
}
private class CandleRsi : Candle
{
public double Rsi { get; set; }
}
}

View File

@@ -0,0 +1,234 @@
using Managing.Core;
using Managing.Domain.Shared.Rules;
using Skender.Stock.Indicators;
using static Managing.Common.Enums;
using Candle = Managing.Domain.Candles.Candle;
namespace Managing.Domain.Strategies;
public class RSIDivergenceStrategy : Strategy
{
public List<Signal> Signals { get; set; }
public TradeDirection Direction { get; set; }
private const int UpperBand = 70;
private const int LowerBand = 30;
public RSIDivergenceStrategy(string name, Timeframe timeframe, int period) : base(name, timeframe, StrategyType.RsiDivergence)
{
Period = period;
Signals = new List<Signal>();
}
/// <summary>
/// Get RSI signals
/// </summary>
/// <returns></returns>
public override List<Signal> Run()
{
if (!Period.HasValue || Candles.Count <= Period)
{
return null;
}
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));
if (candlesRsi.Count(c => c.Rsi > 0) == 0)
return null;
GetLongSignals(candlesRsi);
GetShortSignals(candlesRsi);
return Signals;
}
catch (RuleException)
{
return null;
}
}
private void GetLongSignals(List<CandleRsi> candlesRsi)
{
// Set the low and high for first candle
var firstCandleRsi = candlesRsi.First(c => c.Rsi > 0);
var highPrices = new List<CandleRsi>();
var lowPrices = new List<CandleRsi>();
var highRsi = new List<CandleRsi>();
var lowRsi = new List<CandleRsi>();
highPrices.Add(firstCandleRsi);
lowPrices.Add(firstCandleRsi);
highRsi.Add(firstCandleRsi);
lowRsi.Add(firstCandleRsi);
var previousCandle = firstCandleRsi;
// For a long
foreach (var currentCandle in candlesRsi.FindAll(r => r.Rsi > 0).Skip(1))
{
// If price go down
if (previousCandle.Close > currentCandle.Close)
{
// because the last price is upper than the current
highPrices.AddItem(previousCandle);
// Check if rsi is higher than the last lowest
if (currentCandle.Rsi > lowRsi.TakeLast(Period.Value).Min(r => r.Rsi))
{
// If new higher high, we set it
if (currentCandle.Rsi > highRsi.Last().Rsi)
highRsi.AddItem(currentCandle);
if (currentCandle.Rsi > lowRsi.Last().Rsi)
lowRsi.AddItem(currentCandle);
// Price go down but RSI go up
if (currentCandle.Close < lowPrices.TakeLast(Period.Value).Min(p => p.Close))
{
AddSignal(currentCandle, Timeframe, TradeDirection.Long);
}
}
else
{
// No divergence, price go down, rsi go down
lowRsi.AddItem(currentCandle);
}
lowPrices.AddItem(currentCandle);
}
else
{
// Price go up, so we have to update if price is a new higher high than previous candle
// Normally always true
if (previousCandle.Close < currentCandle.Close)
highPrices.AddItem(currentCandle); //15-15-12-14-17
// If rsi is lower low or not set
if (currentCandle.Rsi < lowRsi.Last().Rsi || lowRsi.Last().Rsi == 0)
lowRsi.AddItem(currentCandle);
// Price going up, so if its a new high we set it
if (currentCandle.Rsi > highRsi.Last().Rsi)
highRsi.AddItem(currentCandle);
}
previousCandle = currentCandle;
}
}
private void GetShortSignals(List<CandleRsi> candlesRsi)
{
// Set the low and high for first candle
var firstCandleRsi = candlesRsi.First(c => c.Rsi > 0);
var signals = new List<Signal>();
var highPrices = new List<CandleRsi>();
var lowPrices = new List<CandleRsi>();
var highRsi = new List<CandleRsi>();
var lowRsi = new List<CandleRsi>();
highPrices.Add(firstCandleRsi);
lowPrices.Add(firstCandleRsi);
highRsi.Add(firstCandleRsi);
lowRsi.Add(firstCandleRsi);
var previousCandle = firstCandleRsi;
// For a short
foreach (var currentCandle in candlesRsi.FindAll(r => r.Rsi > 0).Skip(1))
{
// If price go up
if (previousCandle.Close < currentCandle.Close)
{
// because the last price is lower than the current
lowPrices.AddItem(previousCandle);
// Check if rsi is lower than the last high
if (currentCandle.Rsi < highRsi.TakeLast(Period.Value).Max(r => r.Rsi))
{
// If new lower low, we set it
if (currentCandle.Rsi < lowRsi.Last().Rsi)
lowRsi.AddItem(currentCandle);
if (currentCandle.Rsi < highRsi.Last().Rsi)
highRsi.AddItem(currentCandle);
// Price go up but RSI go down
if (currentCandle.Close > highPrices.TakeLast(Period.Value).Max(p => p.Close))
{
AddSignal(currentCandle, Timeframe, TradeDirection.Short);
}
}
else
{
// No divergence, price go up, rsi go up
highRsi.AddItem(currentCandle);
}
highPrices.AddItem(currentCandle);
}
else
{
// Price go down, so we have to update if price is a new lower low than previous candle
if (previousCandle.Close > currentCandle.Close)
lowPrices.AddItem(currentCandle);
// If rsi is higher high or not set
if (currentCandle.Rsi > highRsi.Last().Rsi || highRsi.Last().Rsi == 0)
highRsi.AddItem(currentCandle);
// Price going down, so if its a new low we set it
if (currentCandle.Rsi < lowRsi.Last().Rsi)
lowRsi.AddItem(currentCandle);
}
previousCandle = currentCandle;
}
}
private void AddSignal(CandleRsi candleSignal, Timeframe timeframe, TradeDirection direction)
{
var signal = new Signal(MiscExtensions.ParseEnum<Ticker>(candleSignal.Ticker), direction, Confidence.Low, candleSignal, candleSignal.Date, candleSignal.Exchange, timeframe, Type, SignalType);
if (Signals.Count(s => s.Identifier == signal.Identifier) < 1)
{
var lastCandleOnPeriod = Candles.TakeLast(Period.Value).ToList();
var signalsOnPeriod = Signals.Where(s => s.Date >= lastCandleOnPeriod[0].Date).ToList();
if (signalsOnPeriod.Count == 1)
signal.SetConfidence(Confidence.Medium);
if (signalsOnPeriod.Count >= 2)
signal.SetConfidence(Confidence.High);
Signals.AddItem(signal);
}
}
private List<CandleRsi> MapRsiToCandle(IReadOnlyCollection<RsiResult> rsiResult,
IEnumerable<Candle> candles)
{
return candles.Select(c => new CandleRsi()
{
Close = c.Close,
Rsi = rsiResult.Find(c.Date).Rsi.GetValueOrDefault(),
Date = c.Date,
Ticker = c.Ticker,
Exchange = c.Exchange
}).ToList();
}
private class CandleRsi : Candle
{
public double Rsi { get; set; }
}
}

View File

@@ -0,0 +1,24 @@
using Managing.Domain.Candles;
using Managing.Domain.Shared.Rules;
namespace Managing.Domain.Strategies.Rules
{
public class CloseHigherThanThePreviousHigh : IValidationRule
{
private readonly Candle _previousCandles;
private readonly Candle _currentCandle;
public CloseHigherThanThePreviousHigh(Candle previousCandles, Candle currentCandle)
{
_previousCandles = previousCandles;
_currentCandle = currentCandle;
}
public string Message => $"Current candle did close higher than the previous high close candle";
public bool IsValid()
{
return _previousCandles != null ? _currentCandle.Close > _previousCandles.High : false;
}
}
}

View File

@@ -0,0 +1,24 @@
using Managing.Domain.Candles;
using Managing.Domain.Shared.Rules;
namespace Managing.Domain.Strategies.Rules
{
public class CloseLowerThanThePreviousHigh : IValidationRule
{
private readonly Candle _previousCandles;
private readonly Candle _currentCandle;
public CloseLowerThanThePreviousHigh(Candle previousCandles, Candle currentCandle)
{
_previousCandles = previousCandles;
_currentCandle = currentCandle;
}
public string Message => $"Current candle did close lower than the previous high close candle";
public bool IsValid()
{
return _previousCandles != null ? _currentCandle.Close > _previousCandles.Low : false;
}
}
}

View File

@@ -0,0 +1,21 @@
using Managing.Domain.Shared.Rules;
namespace Managing.Domain.Strategies.Rules
{
public class RSIShouldBeBullish : IValidationRule
{
private int _rsiValue;
public RSIShouldBeBullish(int rsiValue)
{
_rsiValue = rsiValue;
}
public string Message => $"RSI is not bullish, current value : {_rsiValue}";
public bool IsValid()
{
return _rsiValue > 70;
}
}
}

View File

@@ -0,0 +1,103 @@
using Managing.Core;
using Managing.Domain.Candles;
using Managing.Domain.Shared.Rules;
using Skender.Stock.Indicators;
using static Managing.Common.Enums;
namespace Managing.Domain.Strategies;
public class STCStrategy : Strategy
{
public List<Signal> Signals { get; set; }
public STCStrategy(string name, Timeframe timeframe, int cyclePeriods, int fastPeriods, int slowPeriods) : base(name, timeframe, StrategyType.Stc)
{
Signals = new List<Signal>();
FastPeriods = fastPeriods;
SlowPeriods = slowPeriods;
CyclePeriods = cyclePeriods;
}
public override List<Signal> Run()
{
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));
if (stc.Count == 0)
return null;
var previousCandle = stcCandles[0];
foreach (var currentCandle in stcCandles.Skip(1))
{
if (previousCandle.Stc > 75 && currentCandle.Stc <= 75)
{
AddSignal(currentCandle, Timeframe, TradeDirection.Short, Confidence.Medium);
}
if (previousCandle.Stc < 25 && currentCandle.Stc >= 25)
{
AddSignal(currentCandle, Timeframe, TradeDirection.Long, Confidence.Medium);
}
previousCandle = currentCandle;
}
return Signals.Where(s => s.Confidence != Confidence.None).OrderBy(s => s.Date).ToList();
}
catch (RuleException)
{
return null;
}
}
private List<CandleSct> MapStcToCandle(List<StcResult> stc, IEnumerable<Candle> candles)
{
var sctList = new List<CandleSct>();
foreach (var candle in candles)
{
var currentSct = stc.Find(candle.Date);
if (currentSct != null)
{
sctList.Add(new CandleSct()
{
Close = candle.Close,
Open = candle.Open,
Date = candle.Date,
Ticker = candle.Ticker,
Exchange = candle.Exchange,
Stc = currentSct.Stc
});
}
}
return sctList;
}
private void AddSignal(CandleSct candleSignal, Timeframe timeframe, TradeDirection direction, Confidence confidence)
{
var signal = new Signal(
MiscExtensions.ParseEnum<Ticker>(candleSignal.Ticker),
direction,
confidence,
candleSignal,
candleSignal.Date,
candleSignal.Exchange,
timeframe,
Type, SignalType);
if (!Signals.Any(s => s.Identifier == signal.Identifier))
{
Signals.AddItem(signal);
}
}
private class CandleSct : Candle
{
public double? Stc { get; internal set; }
}
}

View File

@@ -0,0 +1,62 @@
using Managing.Core;
using Managing.Domain.Candles;
using System.ComponentModel.DataAnnotations;
using static Managing.Common.Enums;
namespace Managing.Domain.Strategies
{
public class Signal : ValueObject
{
[Required]
public SignalStatus Status { get; set; }
[Required]
public TradeDirection Direction { get; }
[Required]
public Confidence Confidence { get; private set; }
[Required]
public Timeframe Timeframe { get; }
[Required]
public DateTime Date { get; private set; }
[Required]
public Candle Candle { get; }
[Required]
public string Identifier { get; }
[Required]
public Ticker Ticker { get; }
[Required]
public TradingExchanges Exchange { get; set; }
[Required]
public StrategyType StrategyType { get; set; }
[Required]
public SignalType SignalType { get; set; }
public Signal(Ticker ticker, TradeDirection direction, Confidence confidence, Candle candle, DateTime date,
TradingExchanges exchange, Timeframe timeframe, StrategyType strategyType, SignalType signalType)
{
Direction = direction;
Confidence = confidence;
Candle = candle;
Date = date;
Ticker = ticker;
Exchange = exchange;
Status = SignalStatus.WaitingForPosition;
Timeframe = timeframe;
StrategyType = strategyType;
Identifier = $"{StrategyType}-{direction}-{ticker}-{Timeframe}-{candle?.Close}-{date:yyyyMMdd-HHmmss}";
SignalType = signalType;
}
public void SetConfidence(Confidence confidence)
{
Confidence = confidence;
}
protected override IEnumerable<object> GetEqualityComponents()
{
yield return Direction;
yield return Confidence;
yield return Date;
}
}
}

View File

@@ -0,0 +1,102 @@
using Managing.Core;
using Managing.Domain.Candles;
using Managing.Domain.Shared.Rules;
using Skender.Stock.Indicators;
using static Managing.Common.Enums;
namespace Managing.Domain.Strategies;
public class StDevContext : Strategy
{
public List<Signal> Signals { get; set; }
public StDevContext(string name, Timeframe timeframe, int period) : base(name, timeframe, StrategyType.StDev)
{
Signals = new List<Signal>();
Period = period;
}
public override List<Signal> Run()
{
if (Candles.Count <= Period)
{
return null;
}
try
{
var stDev = Candles.GetStdDev(Period.Value).ToList();
var stDevCandles = MapStDev(stDev, Candles.TakeLast(Period.Value));
if (stDev.Count == 0)
return null;
var lastCandle = stDevCandles.Last();
if (lastCandle.ZScore is < 1.2 and > (-1.2))
{
AddSignal(lastCandle, Timeframe, TradeDirection.None, Confidence.Medium);
}
else
{
Console.WriteLine("Bad zscore");
}
return Signals.Where(s => s.Confidence != Confidence.None).OrderBy(s => s.Date).ToList();
}
catch (RuleException)
{
return null;
}
}
private List<CandleStDev> MapStDev(List<StdDevResult> stDev, IEnumerable<Candle> candles)
{
var sctList = new List<CandleStDev>();
foreach (var candle in candles)
{
var currentSct = stDev.Find(candle.Date);
if (currentSct != null)
{
sctList.Add(new CandleStDev()
{
Close = candle.Close,
Open = candle.Open,
Date = candle.Date,
Ticker = candle.Ticker,
Exchange = candle.Exchange,
StDev = currentSct.StdDev,
ZScore = currentSct.ZScore,
StdDevSma = currentSct.StdDevSma,
Mean = currentSct.Mean
});
}
}
return sctList;
}
private void AddSignal(CandleStDev candleSignal, Timeframe timeframe, TradeDirection direction, Confidence confidence)
{
var signal = new Signal(
MiscExtensions.ParseEnum<Ticker>(candleSignal.Ticker),
direction,
confidence,
candleSignal,
candleSignal.Date,
candleSignal.Exchange,
timeframe,
Type, SignalType);
if (!Signals.Any(s => s.Identifier == signal.Identifier))
{
Signals.AddItem(signal);
}
}
private class CandleStDev : Candle
{
public double? StDev { get; internal set; }
public double? ZScore { get; internal set; }
public double? StdDevSma { get; internal set; }
public double? Mean { get; internal set; }
}
}

View File

@@ -0,0 +1,112 @@
using Managing.Core;
using Managing.Domain.Candles;
using Managing.Domain.Shared.Rules;
using Skender.Stock.Indicators;
using static Managing.Common.Enums;
namespace Managing.Domain.Strategies;
public class StochRsiTrendStrategy : Strategy
{
public List<Signal> Signals { get; set; }
public StochRsiTrendStrategy(
string name,
Timeframe timeframe,
int period,
int stochPeriod,
int signalPeriod,
int smoothPeriods) : base(name, timeframe, StrategyType.StochRsiTrend)
{
Signals = new List<Signal>();
StochPeriods = stochPeriod;
SignalPeriods = signalPeriod;
SmoothPeriods = smoothPeriods;
Period = period;
}
public override List<Signal> Run()
{
if (Candles.Count <= 10 * Period + 50)
{
return null;
}
try
{
var stochRsi = Candles.GetStochRsi(Period.Value, StochPeriods.Value, SignalPeriods.Value, SmoothPeriods.Value).RemoveWarmupPeriods().ToList();
var stochRsiCandles = MapStochRsiToCandle(stochRsi, Candles.TakeLast(Period.Value));
if (stochRsi.Count == 0)
return null;
var previousCandle = stochRsiCandles[0];
foreach (var currentCandle in stochRsiCandles.Skip(1))
{
if (currentCandle.Signal < 20)
{
AddSignal(currentCandle, Timeframe, TradeDirection.Long, Confidence.None);
}
else if (currentCandle.Signal > 80)
{
AddSignal(currentCandle, Timeframe, TradeDirection.Short, Confidence.None);
}
previousCandle = currentCandle;
}
return Signals.OrderBy(s => s.Date).ToList();
}
catch (RuleException)
{
return null;
}
}
private List<CandleStochRsi> MapStochRsiToCandle(List<StochRsiResult> ema, IEnumerable<Candle> candles)
{
var emaList = new List<CandleStochRsi>();
foreach (var candle in candles)
{
var currentEma = ema.Find(candle.Date);
if (currentEma != null)
{
emaList.Add(new CandleStochRsi()
{
Close = candle.Close,
Open = candle.Open,
Date = candle.Date,
Ticker = candle.Ticker,
Exchange = candle.Exchange,
Signal = currentEma.Signal.Value,
StochRsi = currentEma.StochRsi.Value
});
}
}
return emaList;
}
private void AddSignal(CandleStochRsi candleSignal, Timeframe timeframe, TradeDirection direction, Confidence confidence)
{
var signal = new Signal(
MiscExtensions.ParseEnum<Ticker>(candleSignal.Ticker),
direction,
confidence,
candleSignal,
candleSignal.Date,
candleSignal.Exchange,
timeframe,
Type,
SignalType);
if (!Signals.Any(s => s.Identifier == signal.Identifier))
{
Signals.AddItem(signal);
}
}
private class CandleStochRsi : Candle
{
public double Signal { get; internal set; }
public double StochRsi { get; internal set; }
}
}

View File

@@ -0,0 +1,58 @@
using Managing.Domain.Candles;
using Managing.Core;
using static Managing.Common.Enums;
using Managing.Domain.Scenarios;
namespace Managing.Domain.Strategies
{
public class Strategy : IStrategy
{
public Strategy(string name, Timeframe timeframe, StrategyType type)
{
Name = name;
Timeframe = timeframe;
Candles = new List<Candle>();
Type = type;
SignalType = ScenarioHelpers.GetSignalType(type);
}
public string Name { get; set; }
public Timeframe Timeframe { get; set; }
public List<Candle> Candles { get; set; }
public StrategyType 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 virtual List<Signal> Run()
{
return new List<Signal>();
}
public void UpdateCandles(HashSet<Candle> newCandles)
{
lock (Candles)
{
foreach (var item in newCandles.ToList())
{
if (Candles.All(c => c.Date != item.Date))
{
Candles.AddItem(item);
}
}
}
}
public string GetName()
{
return Name;
}
}
}

View File

@@ -0,0 +1,104 @@
using Managing.Core;
using Managing.Domain.Candles;
using Managing.Domain.Shared.Rules;
using Skender.Stock.Indicators;
using static Managing.Common.Enums;
namespace Managing.Domain.Strategies;
public class SuperTrendStrategy : Strategy
{
public List<Signal> Signals { get; set; }
public SuperTrendStrategy(string name, Timeframe timeframe, int period, double multiplier) : base(name, timeframe, StrategyType.SuperTrend)
{
Signals = new List<Signal>();
Period = period;
Multiplier = multiplier;
MinimumHistory = 100 + Period.Value;
}
public override List<Signal> Run()
{
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));
if (superTrendCandle.Count == 0)
return null;
var previousCandle = superTrendCandle[0];
foreach (var currentCandle in superTrendCandle.Skip(1))
{
// Short
if (currentCandle.Close < previousCandle.SuperTrend && previousCandle.Close > previousCandle.SuperTrend)
{
AddSignal(currentCandle, Timeframe, TradeDirection.Short, Confidence.Medium);
}
// Long
if (currentCandle.Close > previousCandle.SuperTrend && previousCandle.Close < previousCandle.SuperTrend)
{
AddSignal(currentCandle, Timeframe, TradeDirection.Long, Confidence.Medium);
}
previousCandle = currentCandle;
}
return Signals.Where(s => s.Confidence != Confidence.None).OrderBy(s => s.Date).ToList();
}
catch (RuleException)
{
return null;
}
}
private List<CandleSuperTrend> MapSuperTrendToCandle(List<SuperTrendResult> superTrend, IEnumerable<Candle> candles)
{
var superTrends = new List<CandleSuperTrend>();
foreach (var candle in candles)
{
var currentSuperTrend = superTrend.Find(candle.Date);
if (currentSuperTrend != null)
{
superTrends.Add(new CandleSuperTrend()
{
Close = candle.Close,
Open = candle.Open,
Date = candle.Date,
Ticker = candle.Ticker,
Exchange = candle.Exchange,
SuperTrend = currentSuperTrend.SuperTrend.Value,
LowerBand = currentSuperTrend.LowerBand,
UpperBand = currentSuperTrend.UpperBand,
});
}
}
return superTrends;
}
private void AddSignal(CandleSuperTrend candleSignal, Timeframe timeframe, TradeDirection direction, Confidence confidence)
{
var signal = new Signal(MiscExtensions.ParseEnum<Ticker>(candleSignal.Ticker), direction, confidence, candleSignal, candleSignal.Date,
candleSignal.Exchange, timeframe, Type, SignalType);
if (!Signals.Any(s => s.Identifier == signal.Identifier))
{
Signals.AddItem(signal);
}
}
private class CandleSuperTrend : Candle
{
public decimal SuperTrend { get; internal set; }
public decimal? LowerBand { get; internal set; }
public decimal? UpperBand { get; internal set; }
}
}

View File

@@ -0,0 +1,55 @@
using Managing.Domain.Candles;
using Managing.Domain.Shared.Rules;
using Managing.Domain.Strategies;
using Managing.Domain.Strategies.Rules;
using static Managing.Common.Enums;
namespace Managing.Application.Strategies
{
public class ThreeWhiteSoldiersStrategy : Strategy
{
public ThreeWhiteSoldiersStrategy(string name, Timeframe timeframe, int period)
: base(name, timeframe, StrategyType.ThreeWhiteSoldiers)
{
Period = period;
}
public TradeDirection Direction { get; }
public override List<Signal> Run()
{
var signals = new List<Signal>();
if (Candles.Count <= 3)
{
return null;
}
try
{
var lastFourCandles = Candles.TakeLast(4);
Candle previousCandles = null;
foreach (var currentCandle in lastFourCandles)
{
if (Direction == TradeDirection.Long)
{
Check.That(new CloseHigherThanThePreviousHigh(previousCandles, currentCandle));
}
else
{
Check.That(new CloseLowerThanThePreviousHigh(previousCandles, currentCandle));
}
previousCandles = currentCandle;
}
return signals;
}
catch (RuleException)
{
return null;
}
}
}
}