docker files fixes from liaqat
This commit is contained in:
39
src/Managing.Domain/Strategies/Base/EmaBaseStrategy.cs
Normal file
39
src/Managing.Domain/Strategies/Base/EmaBaseStrategy.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
115
src/Managing.Domain/Strategies/ChandelierExitStrategy.cs
Normal file
115
src/Managing.Domain/Strategies/ChandelierExitStrategy.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
69
src/Managing.Domain/Strategies/EmaCrossStrategy.cs
Normal file
69
src/Managing.Domain/Strategies/EmaCrossStrategy.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
65
src/Managing.Domain/Strategies/EmaTrendStrategy.cs
Normal file
65
src/Managing.Domain/Strategies/EmaTrendStrategy.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/Managing.Domain/Strategies/IStrategy.cs
Normal file
20
src/Managing.Domain/Strategies/IStrategy.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
102
src/Managing.Domain/Strategies/MACDCrossStrategy.cs
Normal file
102
src/Managing.Domain/Strategies/MACDCrossStrategy.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
252
src/Managing.Domain/Strategies/RSIDivergenceConfirmStrategy.cs
Normal file
252
src/Managing.Domain/Strategies/RSIDivergenceConfirmStrategy.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
234
src/Managing.Domain/Strategies/RSIDivergenceStrategy.cs
Normal file
234
src/Managing.Domain/Strategies/RSIDivergenceStrategy.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
21
src/Managing.Domain/Strategies/Rules/RSIShouldBeBullish.cs
Normal file
21
src/Managing.Domain/Strategies/Rules/RSIShouldBeBullish.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
103
src/Managing.Domain/Strategies/STCStrategy.cs
Normal file
103
src/Managing.Domain/Strategies/STCStrategy.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
62
src/Managing.Domain/Strategies/Signal.cs
Normal file
62
src/Managing.Domain/Strategies/Signal.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
102
src/Managing.Domain/Strategies/StDevContext.cs
Normal file
102
src/Managing.Domain/Strategies/StDevContext.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
112
src/Managing.Domain/Strategies/StochRsiTrendStrategy.cs
Normal file
112
src/Managing.Domain/Strategies/StochRsiTrendStrategy.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
58
src/Managing.Domain/Strategies/Strategy.cs
Normal file
58
src/Managing.Domain/Strategies/Strategy.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
104
src/Managing.Domain/Strategies/SuperTrendStrategy.cs
Normal file
104
src/Managing.Domain/Strategies/SuperTrendStrategy.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
55
src/Managing.Domain/Strategies/ThreeWhiteSoldiersStrategy.cs
Normal file
55
src/Managing.Domain/Strategies/ThreeWhiteSoldiersStrategy.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user