|
|
|
|
@@ -103,13 +103,15 @@ public class TradingBot : Bot, ITradingBot
|
|
|
|
|
await CancelAllOrders();
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
await MessengerService.SendMessage($"Hi everyone, I'm going to run {Name}. \nI will send a message here everytime a signal is triggered by the {string.Join(",", Strategies.Select(s => s.Name))} strategies.");
|
|
|
|
|
{
|
|
|
|
|
await MessengerService.SendMessage(
|
|
|
|
|
$"Hi everyone, I'm going to run {Name}. \nI will send a message here everytime a signal is triggered by the {string.Join(",", Strategies.Select(s => s.Name))} strategies.");
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Logger.LogError(ex, ex.Message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await InitWorker(Run);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -155,7 +157,8 @@ public class TradingBot : Bot, ITradingBot
|
|
|
|
|
public async Task Run()
|
|
|
|
|
{
|
|
|
|
|
Logger.LogInformation($"____________________{Name}____________________");
|
|
|
|
|
Logger.LogInformation($"Time : {DateTime.Now} - Server time {DateTime.Now.ToUniversalTime()} - Bot : {Name} - Type {BotType} - Ticker : {Ticker}");
|
|
|
|
|
Logger.LogInformation(
|
|
|
|
|
$"Time : {DateTime.Now} - Server time {DateTime.Now.ToUniversalTime()} - Bot : {Name} - Type {BotType} - Ticker : {Ticker}");
|
|
|
|
|
|
|
|
|
|
var previousCandleCount = Candles.Count;
|
|
|
|
|
|
|
|
|
|
@@ -183,6 +186,9 @@ public class TradingBot : Bot, ITradingBot
|
|
|
|
|
|
|
|
|
|
private async Task PreloadCandles()
|
|
|
|
|
{
|
|
|
|
|
if (Candles.Any())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
var candles = await ExchangeService.GetCandlesInflux(Account.Exchange, Ticker, PreloadSince, Timeframe);
|
|
|
|
|
|
|
|
|
|
foreach (var candle in candles.Where(c => c.Date < DateTime.Now.ToUniversalTime()))
|
|
|
|
|
@@ -218,7 +224,7 @@ public class TradingBot : Bot, ITradingBot
|
|
|
|
|
signal.Status = SignalStatus.Expired;
|
|
|
|
|
|
|
|
|
|
var signalText = $"{Scenario} trigger a signal. Signal told you " +
|
|
|
|
|
$"to {signal.Direction} {Ticker} on {Timeframe}. The confidence in this signal is {signal.Confidence}. Identifier : {signal.Identifier}";
|
|
|
|
|
$"to {signal.Direction} {Ticker} on {Timeframe}. The confidence in this signal is {signal.Confidence}. Identifier : {signal.Identifier}";
|
|
|
|
|
|
|
|
|
|
Logger.LogInformation(signalText);
|
|
|
|
|
|
|
|
|
|
@@ -280,7 +286,9 @@ public class TradingBot : Bot, ITradingBot
|
|
|
|
|
{
|
|
|
|
|
Logger.LogInformation($"Updating position {positionForSignal.SignalIdentifier}");
|
|
|
|
|
|
|
|
|
|
var position = IsForBacktest ? positionForSignal : TradingService.GetPositionByIdentifier(positionForSignal.Identifier);
|
|
|
|
|
var position = IsForBacktest
|
|
|
|
|
? positionForSignal
|
|
|
|
|
: TradingService.GetPositionByIdentifier(positionForSignal.Identifier);
|
|
|
|
|
|
|
|
|
|
if (position.Status == (PositionStatus.Finished | PositionStatus.Flipped))
|
|
|
|
|
{
|
|
|
|
|
@@ -292,32 +300,41 @@ public class TradingBot : Bot, ITradingBot
|
|
|
|
|
// For backtesting or force close if not executed on exchange :
|
|
|
|
|
// check if position is still open
|
|
|
|
|
// Check status, if still open update the status of the position
|
|
|
|
|
var lastCandle = IsForBacktest ? Candles.Last() : ExchangeService.GetCandle(Account, Ticker, DateTime.UtcNow);
|
|
|
|
|
var lastCandle = IsForBacktest
|
|
|
|
|
? Candles.Last()
|
|
|
|
|
: ExchangeService.GetCandle(Account, Ticker, DateTime.UtcNow);
|
|
|
|
|
|
|
|
|
|
if (positionForSignal.OriginDirection == TradeDirection.Long)
|
|
|
|
|
{
|
|
|
|
|
if (positionForSignal.StopLoss.Price >= lastCandle.Low)
|
|
|
|
|
{
|
|
|
|
|
await LogInformation($"Closing position - SL {positionForSignal.StopLoss.Price} >= Price {lastCandle.Low}");
|
|
|
|
|
await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss, positionForSignal.StopLoss.Price, true);
|
|
|
|
|
await LogInformation(
|
|
|
|
|
$"Closing position - SL {positionForSignal.StopLoss.Price} >= Price {lastCandle.Low}");
|
|
|
|
|
await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss,
|
|
|
|
|
positionForSignal.StopLoss.Price, true);
|
|
|
|
|
positionForSignal.StopLoss.SetStatus(TradeStatus.Filled);
|
|
|
|
|
}
|
|
|
|
|
else if (positionForSignal.TakeProfit1.Price <= lastCandle.High
|
|
|
|
|
&& positionForSignal.TakeProfit1.Status != TradeStatus.Filled)
|
|
|
|
|
&& positionForSignal.TakeProfit1.Status != TradeStatus.Filled)
|
|
|
|
|
{
|
|
|
|
|
await LogInformation($"Closing position - TP1 {positionForSignal.TakeProfit1.Price} <= Price {lastCandle.High}");
|
|
|
|
|
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1, positionForSignal.TakeProfit1.Price, positionForSignal.TakeProfit2 == null);
|
|
|
|
|
await LogInformation(
|
|
|
|
|
$"Closing position - TP1 {positionForSignal.TakeProfit1.Price} <= Price {lastCandle.High}");
|
|
|
|
|
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1,
|
|
|
|
|
positionForSignal.TakeProfit1.Price, positionForSignal.TakeProfit2 == null);
|
|
|
|
|
positionForSignal.TakeProfit1.SetStatus(TradeStatus.Filled);
|
|
|
|
|
}
|
|
|
|
|
else if (positionForSignal.TakeProfit2?.Price <= lastCandle.High)
|
|
|
|
|
{
|
|
|
|
|
await LogInformation($"Closing position - TP2 {positionForSignal.TakeProfit2.Price} <= Price {lastCandle.High}");
|
|
|
|
|
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2, positionForSignal.TakeProfit2.Price, true);
|
|
|
|
|
await LogInformation(
|
|
|
|
|
$"Closing position - TP2 {positionForSignal.TakeProfit2.Price} <= Price {lastCandle.High}");
|
|
|
|
|
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2,
|
|
|
|
|
positionForSignal.TakeProfit2.Price, true);
|
|
|
|
|
positionForSignal.TakeProfit2.SetStatus(TradeStatus.Filled);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Logger.LogInformation($"Position {signal.Identifier} don't need to be update. Position still opened");
|
|
|
|
|
Logger.LogInformation(
|
|
|
|
|
$"Position {signal.Identifier} don't need to be update. Position still opened");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -325,26 +342,33 @@ public class TradingBot : Bot, ITradingBot
|
|
|
|
|
{
|
|
|
|
|
if (positionForSignal.StopLoss.Price <= lastCandle.High)
|
|
|
|
|
{
|
|
|
|
|
await LogInformation($"Closing position - SL {positionForSignal.StopLoss.Price} <= Price {lastCandle.High}");
|
|
|
|
|
await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss, positionForSignal.StopLoss.Price, true);
|
|
|
|
|
await LogInformation(
|
|
|
|
|
$"Closing position - SL {positionForSignal.StopLoss.Price} <= Price {lastCandle.High}");
|
|
|
|
|
await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss,
|
|
|
|
|
positionForSignal.StopLoss.Price, true);
|
|
|
|
|
positionForSignal.StopLoss.SetStatus(TradeStatus.Filled);
|
|
|
|
|
}
|
|
|
|
|
else if (positionForSignal.TakeProfit1.Price >= lastCandle.Low
|
|
|
|
|
&& positionForSignal.TakeProfit1.Status != TradeStatus.Filled)
|
|
|
|
|
&& positionForSignal.TakeProfit1.Status != TradeStatus.Filled)
|
|
|
|
|
{
|
|
|
|
|
await LogInformation($"Closing position - TP1 {positionForSignal.TakeProfit1.Price} >= Price {lastCandle.Low}");
|
|
|
|
|
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1, positionForSignal.TakeProfit1.Price, positionForSignal.TakeProfit2 == null);
|
|
|
|
|
await LogInformation(
|
|
|
|
|
$"Closing position - TP1 {positionForSignal.TakeProfit1.Price} >= Price {lastCandle.Low}");
|
|
|
|
|
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1,
|
|
|
|
|
positionForSignal.TakeProfit1.Price, positionForSignal.TakeProfit2 == null);
|
|
|
|
|
positionForSignal.TakeProfit1.SetStatus(TradeStatus.Filled);
|
|
|
|
|
}
|
|
|
|
|
else if (positionForSignal.TakeProfit2?.Price >= lastCandle.Low)
|
|
|
|
|
{
|
|
|
|
|
await LogInformation($"Closing position - TP2 {positionForSignal.TakeProfit2.Price} >= Price {lastCandle.Low}");
|
|
|
|
|
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2, positionForSignal.TakeProfit2.Price, true);
|
|
|
|
|
await LogInformation(
|
|
|
|
|
$"Closing position - TP2 {positionForSignal.TakeProfit2.Price} >= Price {lastCandle.Low}");
|
|
|
|
|
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2,
|
|
|
|
|
positionForSignal.TakeProfit2.Price, true);
|
|
|
|
|
positionForSignal.TakeProfit2.SetStatus(TradeStatus.Filled);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Logger.LogInformation($"Position {signal.Identifier} don't need to be update. Position still opened");
|
|
|
|
|
Logger.LogInformation(
|
|
|
|
|
$"Position {signal.Identifier} don't need to be update. Position still opened");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -368,16 +392,17 @@ public class TradingBot : Bot, ITradingBot
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private async Task OpenPosition(Signal signal)
|
|
|
|
|
{
|
|
|
|
|
// Check if a position is already open
|
|
|
|
|
Logger.LogInformation($"Opening position for {signal.Identifier}");
|
|
|
|
|
|
|
|
|
|
var openedPosition = Positions.FirstOrDefault(p => p.Status == PositionStatus.Filled
|
|
|
|
|
&& p.SignalIdentifier != signal.Identifier);
|
|
|
|
|
&& p.SignalIdentifier != signal.Identifier);
|
|
|
|
|
|
|
|
|
|
var lastPrice = IsForBacktest ? Candles.Last().Close : ExchangeService.GetPrice(Account, Ticker, DateTime.UtcNow);
|
|
|
|
|
var lastPrice = IsForBacktest
|
|
|
|
|
? Candles.Last().Close
|
|
|
|
|
: ExchangeService.GetPrice(Account, Ticker, DateTime.UtcNow);
|
|
|
|
|
|
|
|
|
|
// If position open
|
|
|
|
|
if (openedPosition != null)
|
|
|
|
|
@@ -388,7 +413,8 @@ public class TradingBot : Bot, ITradingBot
|
|
|
|
|
if (openedPosition.OriginDirection == signal.Direction)
|
|
|
|
|
{
|
|
|
|
|
// An operation is already open for the same direction
|
|
|
|
|
await LogInformation($"Signal {signal.Identifier} try to open a position but {previousSignal.Identifier} is already open for the same direction");
|
|
|
|
|
await LogInformation(
|
|
|
|
|
$"Signal {signal.Identifier} try to open a position but {previousSignal.Identifier} is already open for the same direction");
|
|
|
|
|
SetSignalStatus(signal.Identifier, SignalStatus.Expired);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
@@ -401,11 +427,13 @@ public class TradingBot : Bot, ITradingBot
|
|
|
|
|
await CloseTrade(previousSignal, openedPosition, openedPosition.Open, lastPrice, true);
|
|
|
|
|
await SetPositionStatus(previousSignal.Identifier, PositionStatus.Flipped);
|
|
|
|
|
await OpenPosition(signal);
|
|
|
|
|
await LogInformation($"Position {previousSignal.Identifier} flipped by {signal.Identifier} at {lastPrice}$");
|
|
|
|
|
await LogInformation(
|
|
|
|
|
$"Position {previousSignal.Identifier} flipped by {signal.Identifier} at {lastPrice}$");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
await LogWarning($"A position is already open for signal {previousSignal.Identifier}. Position flipping is currently not enable, the position will not be flipped.");
|
|
|
|
|
await LogWarning(
|
|
|
|
|
$"A position is already open for signal {previousSignal.Identifier}. Position flipping is currently not enable, the position will not be flipped.");
|
|
|
|
|
SetSignalStatus(signal.Identifier, SignalStatus.Expired);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -414,12 +442,14 @@ public class TradingBot : Bot, ITradingBot
|
|
|
|
|
{
|
|
|
|
|
if (!CanOpenPosition(signal))
|
|
|
|
|
{
|
|
|
|
|
await LogInformation("Tried to open position but last position was a loss. Wait for an opposition direction side or wait x candles to open a new position");
|
|
|
|
|
await LogInformation(
|
|
|
|
|
"Tried to open position but last position was a loss. Wait for an opposition direction side or wait x candles to open a new position");
|
|
|
|
|
SetSignalStatus(signal.Identifier, SignalStatus.Expired);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await LogInformation($"Open position - Date: {signal.Date:T} - SignalIdentifier : {signal.Identifier} - Strategie : {signal.StrategyType}");
|
|
|
|
|
await LogInformation(
|
|
|
|
|
$"Open position - Date: {signal.Date:T} - SignalIdentifier : {signal.Identifier} - Strategie : {signal.StrategyType}");
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
@@ -474,9 +504,9 @@ public class TradingBot : Bot, ITradingBot
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
var lastPosition = Positions.LastOrDefault(p => p.IsFinished()
|
|
|
|
|
&& p.SignalIdentifier != signal.Identifier
|
|
|
|
|
&& p.ProfitAndLoss.Realized < 0
|
|
|
|
|
&& p.OriginDirection == signal.Direction);
|
|
|
|
|
&& p.SignalIdentifier != signal.Identifier
|
|
|
|
|
&& p.ProfitAndLoss.Realized < 0
|
|
|
|
|
&& p.OriginDirection == signal.Direction);
|
|
|
|
|
|
|
|
|
|
if (lastPosition == null)
|
|
|
|
|
return true;
|
|
|
|
|
@@ -487,15 +517,18 @@ public class TradingBot : Bot, ITradingBot
|
|
|
|
|
return positionSignal.Date < tenCandleAgo.Date;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task CloseTrade(Signal signal, Position position, Trade tradeToClose, decimal lastPrice, bool tradeClosingPosition = false)
|
|
|
|
|
private async Task CloseTrade(Signal signal, Position position, Trade tradeToClose, decimal lastPrice,
|
|
|
|
|
bool tradeClosingPosition = false)
|
|
|
|
|
{
|
|
|
|
|
if (position.TakeProfit2 != null && position.TakeProfit1.Status == TradeStatus.Filled && tradeToClose.TradeType == TradeType.StopMarket)
|
|
|
|
|
if (position.TakeProfit2 != null && position.TakeProfit1.Status == TradeStatus.Filled &&
|
|
|
|
|
tradeToClose.TradeType == TradeType.StopMarket)
|
|
|
|
|
{
|
|
|
|
|
// If trade is the 2nd Take profit
|
|
|
|
|
tradeToClose.Quantity = position.TakeProfit2.Quantity;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await LogInformation($"Trying to close trade {Ticker} at {lastPrice} - Type : {tradeToClose.TradeType} - Quantity : {tradeToClose.Quantity} " +
|
|
|
|
|
await LogInformation(
|
|
|
|
|
$"Trying to close trade {Ticker} at {lastPrice} - Type : {tradeToClose.TradeType} - Quantity : {tradeToClose.Quantity} " +
|
|
|
|
|
$"- Closing Position : {tradeClosingPosition}");
|
|
|
|
|
|
|
|
|
|
// Get status of position before closing it. The position might be already close by the exchange
|
|
|
|
|
@@ -509,8 +542,9 @@ public class TradingBot : Bot, ITradingBot
|
|
|
|
|
var command = new ClosePositionCommand(position, lastPrice);
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var closedPosition = await (new ClosePositionCommandHandler(ExchangeService, AccountService, TradingService))
|
|
|
|
|
.Handle(command);
|
|
|
|
|
var closedPosition =
|
|
|
|
|
await (new ClosePositionCommandHandler(ExchangeService, AccountService, TradingService))
|
|
|
|
|
.Handle(command);
|
|
|
|
|
|
|
|
|
|
if (closedPosition.Status == (PositionStatus.Finished | PositionStatus.Flipped))
|
|
|
|
|
{
|
|
|
|
|
@@ -536,7 +570,6 @@ public class TradingBot : Bot, ITradingBot
|
|
|
|
|
await SetPositionStatus(signal.Identifier, PositionStatus.Finished);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -549,7 +582,8 @@ public class TradingBot : Bot, ITradingBot
|
|
|
|
|
position.SignalIdentifier = previousPosition.SignalIdentifier;
|
|
|
|
|
Positions[positionIndex] = position;
|
|
|
|
|
SetSignalStatus(position.SignalIdentifier, SignalStatus.Expired);
|
|
|
|
|
Logger.LogInformation($"Position {position.SignalIdentifier} type correctly close. Pnl on position : {position.ProfitAndLoss.Realized}");
|
|
|
|
|
Logger.LogInformation(
|
|
|
|
|
$"Position {position.SignalIdentifier} type correctly close. Pnl on position : {position.ProfitAndLoss.Realized}");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
@@ -689,6 +723,12 @@ public class TradingBot : Bot, ITradingBot
|
|
|
|
|
Signals = data.Signals;
|
|
|
|
|
Positions = data.Positions;
|
|
|
|
|
WalletBalances = data.WalletBalances;
|
|
|
|
|
MoneyManagement = data.MoneyManagement;
|
|
|
|
|
Timeframe = data.Timeframe;
|
|
|
|
|
Ticker = data.Ticker;
|
|
|
|
|
Scenario = data.Scenario;
|
|
|
|
|
AccountName = data.AccountName;
|
|
|
|
|
IsForWatchingOnly = data.IsForWatchingOnly;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|