Trading bot grain (#33)

* Trading bot Grain

* Fix a bit more of the trading bot

* Advance on the tradingbot grain

* Fix build

* Fix db script

* Fix user login

* Fix a bit backtest

* Fix cooldown and backtest

* start fixing bot start

* Fix startup

* Setup local db

* Fix build and update candles and scenario

* Add bot registry

* Add reminder

* Updateing the grains

* fix bootstraping

* Save stats on tick

* Save bot data every tick

* Fix serialization

* fix save bot stats

* Fix get candles

* use dict instead of list for position

* Switch hashset to dict

* Fix a bit

* Fix bot launch and bot view

* add migrations

* Remove the tolist

* Add agent grain

* Save agent summary

* clean

* Add save bot

* Update get bots

* Add get bots

* Fix stop/restart

* fix Update config

* Update scanner table on new backtest saved

* Fix backtestRowDetails.tsx

* Fix agentIndex

* Update agentIndex

* Fix more things

* Update user cache

* Fix

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

View File

@@ -0,0 +1,244 @@
using Managing.Core;
using Managing.Domain.Indicators;
using Managing.Domain.Shared.Rules;
using Managing.Domain.Strategies.Base;
using Skender.Stock.Indicators;
using static Managing.Common.Enums;
using Candle = Managing.Domain.Candles.Candle;
namespace Managing.Domain.Strategies.Signals;
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 RsiDivergenceIndicatorBase(string name, int period) : base(name, IndicatorType.RsiDivergence)
{
Period = period;
Signals = new List<LightSignal>();
}
/// <summary>
/// Get RSI signals
/// </summary>
/// <returns></returns>
public override List<LightSignal> Run(HashSet<Candle> candles)
{
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, candles);
GetShortSignals(candlesRsi, candles);
return Signals;
}
catch (RuleException)
{
return null;
}
}
public override IndicatorsResultBase GetIndicatorValues(HashSet<Candle> candles)
{
return new IndicatorsResultBase()
{
Rsi = candles.GetRsi(Period.Value).ToList()
};
}
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);
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, TradeDirection.Long, candles);
}
}
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, HashSet<Candle> candles)
{
// 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, TradeDirection.Short, candles);
}
}
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, 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 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; }
}
}