Browse Source

响应多交叉信号,空交叉信号

master
shanji 3 years ago
parent
commit
3def6fc747
  1. 1
      Binance.TradeRobot.API/Startup.cs
  2. 22
      Binance.TradeRobot.Business/Business/RobotBusiness.cs
  3. 30
      Binance.TradeRobot.Business/Business/SingalBusiness.cs
  4. 7
      Binance.TradeRobot.Business/Business/TradeBusiness/BaseTradeBusiness.cs
  5. 130
      Binance.TradeRobot.Business/Business/TradeBusiness/D21TradeBusiness.cs
  6. 17
      Binance.TradeRobot.Business/TaskSchedulerManager.cs
  7. 13
      Binance.TradeRobot.Model/Dto/Response/Robot/RobotResponse.cs

1
Binance.TradeRobot.API/Startup.cs

@ -33,6 +33,7 @@ namespace Binance.TradeRobot.API
services.AddMemoryCache();
services.AddSingleton<NLogManager>();
services.AddSingleton<RestApiService>();
services.AddSingleton<TaskSchedulerManager>();
var fsql = new FreeSql.FreeSqlBuilder().UseConnectionString(FreeSql.DataType.MySql, Configuration.GetConnectionString("DB")).Build();
services.AddSingleton(typeof(IFreeSql), fsql);

22
Binance.TradeRobot.Business/Business/RobotBusiness.cs

@ -64,14 +64,21 @@ namespace Binance.TradeRobot.Business
var robot = GetRobotList(robotId).FirstOrDefault();
fsql.Update<Robot>(robotId).Set(r => r.State, Enums.RobotState.Stop).ExecuteAffrows();
//取消监听K线和订单
var sameRunningCount = GetRobotList(symbol: robot.Symbol, robotState: Enums.RobotState.Runing, exchange: robot.ExchangeId).Count();
if (sameRunningCount == 0)
{
//取消监听K线和订单
globalContext.UnSubscribeKLine(robot);
}
//取消订单监听
var symbolParam = string.Empty;
if (robot.ExchangeId == Enums.Exchange.Binance && robot.BusinessType == Enums.BusinessType.IsolateMargin)
symbolParam = robot.Symbol;
var sameRunningCount2 = GetRobotList(symbol: symbolParam,
robotState: Enums.RobotState.Runing,
exchange: robot.ExchangeId,
accountId: robot.ExchangeAPIKey.AccountId).Count();
if (sameRunningCount2 == 0)
globalContext.UnSubscribeOrderPublish(robot);
}
/// <summary>
@ -156,11 +163,13 @@ namespace Binance.TradeRobot.Business
/// <param name="symbol"></param>
/// <param name="robotState"></param>
/// <param name="exchange"></param>
/// <param name="accountId">交易所账号Id</param>
/// <returns></returns>
public IList<RobotResponse> GetRobotList(long? robotId = null,
string symbol = "",
Enums.RobotState? robotState = null,
Enums.Exchange? exchange = null)
Enums.Exchange? exchange = null,
long? accountId = null)
{
var select = fsql.Select<Robot, ExchangeAPIKey>().InnerJoin((r, e) => r.Id == e.RobotId);
if (robotId != null)
@ -168,7 +177,8 @@ namespace Binance.TradeRobot.Business
else
select = select.WhereIf(!string.IsNullOrEmpty(symbol), (r, e) => r.Symbol == symbol)
.WhereIf(robotState != null, (r, e) => r.State == robotState)
.WhereIf(exchange != null, (r, e) => r.ExchangeId == exchange);
.WhereIf(exchange != null, (r, e) => r.ExchangeId == exchange)
.WhereIf(accountId != null, (r, e) => e.AccountId == accountId);
return select.ToList((r, e) => new Robot()
{

30
Binance.TradeRobot.Business/Business/SingalBusiness.cs

@ -6,6 +6,7 @@ using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Yitter.IdGenerator;
@ -17,6 +18,7 @@ namespace Binance.TradeRobot.Business
private RobotBusiness robotBusiness;
private ExchangeBusiness exchangeBusiness;
private IEnumerable<ITradeBusiness> tradeBusinessList;
private TaskSchedulerManager taskSchedulerManager;
public SingalBusiness(IFreeSql fsql,
NLogManager logManager,
@ -24,21 +26,28 @@ namespace Binance.TradeRobot.Business
IMemoryCache memoryCache,
RobotBusiness robotBusiness,
ExchangeBusiness exchangeBusiness,
IEnumerable<ITradeBusiness> tradeBusinessList) : base(fsql, logManager, idGenerator, memoryCache)
IEnumerable<ITradeBusiness> tradeBusinessList,
TaskSchedulerManager taskSchedulerManager) : base(fsql, logManager, idGenerator, memoryCache)
{
this.robotBusiness = robotBusiness;
this.exchangeBusiness = exchangeBusiness;
this.tradeBusinessList = tradeBusinessList;
this.taskSchedulerManager = taskSchedulerManager;
}
public void D21Singal(D21SingalRequest d21SingalRequest)
{
//logManager.GetLogger("D21").Info(JsonConvert.SerializeObject(d21SingalRequest));
var robotList = robotBusiness.GetD21PolicyRobotList(Enums.RobotState.Runing, d21SingalRequest.KLinePeriodic, d21SingalRequest.Symbol, false, true);
var robotList = robotBusiness.GetD21PolicyRobotList(Enums.RobotState.Runing,
d21SingalRequest.KLinePeriodic,
d21SingalRequest.Symbol,
false,
true);
if (robotList == null || robotList.Count() == 0)
throw new BusinessException("未找到符合条件的机器人");
var symbolInfo = exchangeBusiness.GetSymbol(robotList[0].ExchangeId, robotList[0].Symbol);
var symbolInfo = exchangeBusiness.GetSymbol(robotList[0].ExchangeId,
robotList[0].Symbol);
if (symbolInfo == null)
throw new BusinessException($"未找到交易对{robotList[0].Symbol}({robotList[0].ExchangeId})");
var d21TradeBusiness = tradeBusinessList.FirstOrDefault(t => t.TradePolicy == Enums.TradePolicy.D21);
@ -49,13 +58,22 @@ namespace Binance.TradeRobot.Business
{
case Enums.SingalType.:
case Enums.SingalType.:
Task.Factory.StartNew(() => d21TradeBusiness.TrendChanged(d21SingalRequest, robot, symbolInfo));
Task.Factory.StartNew(() => d21TradeBusiness.TrendChanged(d21SingalRequest, robot, symbolInfo),
CancellationToken.None,
TaskCreationOptions.LongRunning,
taskSchedulerManager.SingalTaskScheduler);
break;
case Enums.SingalType.:
Task.Factory.StartNew(() => d21TradeBusiness.LongCross(d21SingalRequest, robot, false, symbolInfo),
CancellationToken.None,
TaskCreationOptions.LongRunning,
taskSchedulerManager.SingalTaskScheduler);
break;
case Enums.SingalType.:
Task.Factory.StartNew(() => d21TradeBusiness.ShortCross(d21SingalRequest, robot, false, symbolInfo),
CancellationToken.None,
TaskCreationOptions.LongRunning,
taskSchedulerManager.SingalTaskScheduler);
break;
}
}

7
Binance.TradeRobot.Business/Business/TradeBusiness/BaseTradeBusiness.cs

@ -40,5 +40,12 @@ namespace Binance.TradeRobot.Business
logManager.GetLogger(robot.ExecuteKey).Error(ex, errorMsg);
dingBusiness.Send($"{errorMsg} {ex.Message}");
}
protected string CreateClientOrderId(long robotId)
{
var guid = Guid.NewGuid();
var random = new Random(guid.GetHashCode());
return $"{Convert.ToChar(random.Next(97, 123))}{guid.ToString().Substring(0, 4)}_{robotId}";
}
}
}

130
Binance.TradeRobot.Business/Business/TradeBusiness/D21TradeBusiness.cs

@ -1,16 +1,15 @@
using Binance.TradeRobot.Business.Extensions;
using Binance.TradeRobot.Common.DI;
using Binance.TradeRobot.Common.Extensions;
using Binance.TradeRobot.Model.Base;
using Binance.TradeRobot.Model.Db;
using Binance.TradeRobot.Model.Dto;
using Binance.TradeRobot.Model.RuningInfo;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Yitter.IdGenerator;
@ -57,7 +56,7 @@ namespace Binance.TradeRobot.Business
logList.Add(new ExecutionLog()
{
Id = idGenerator.NewLong(),
SourceSingal = Enums.SingalType.,
SourceSingal = singalRequest.SingalType,
RobotId = robot.Id,
CreateTime = DateTime.Now,
Content = $"收到信号{singalRequest.SingalType}{(isRemedy ? "()" : string.Empty)}"
@ -210,10 +209,12 @@ namespace Binance.TradeRobot.Business
#region 下单
step = "下单";
var clientOrderId = CreateClientOrderId(robot.Id);
var orderId = apiClient.IsolatedMarginPlaceOrder(robot.Symbol,
Enums.TradeDirection.Buy,
Enums.OrderType.MARKET,
quoteAmount: previewTradeAmount);
quoteAmount: previewTradeAmount,
newClientOrderId: clientOrderId);
var buyOrder = new SpotOrder()
{
Id = orderId,
@ -228,6 +229,14 @@ namespace Binance.TradeRobot.Business
TradeDirection = Enums.TradeDirection.Buy
};
logList.Add(new ExecutionLog()
{
Id = idGenerator.NewLong(),
SourceSingal = singalRequest.SingalType,
RobotId = robot.Id,
CreateTime = DateTime.Now,
Content = $"市价买单挂单成功,orderId:{orderId}"
});
fsql.Transaction(() =>
{
fsql.Insert(logList).ExecuteAffrows();
@ -241,24 +250,113 @@ namespace Binance.TradeRobot.Business
catch (Exception ex)
{
HandleError(ex, singalRequest.SingalType, logList, robot, step);
//logList.Add(new ExecutionLog()
//{
// Id = idGenerator.NewLong(),
// SourceSingal = Enums.SingalType.多交叉,
// RobotId = robot.Id,
// CreateTime = DateTime.Now,
// Content = ex.Message
//});
//try { fsql.Insert(logList).ExecuteAffrows(); } catch { }
//var errorMsg = $"交易警报,{singalRequest.SingalType},{robot.ExecuteLogKey},{robot.Id},{step}";
//logManager.GetLogger(robot.ExecuteLogKey).Error(ex, errorMsg);
//dingBusiness.Send($"{errorMsg} {ex.Message}");
}
}
public void ShortCross<T, T1>(T singalRequest, T1 robot, bool isRemedy, SymbolInfoResponse symbolInfo) where T : BaseSingalRequest where T1 : RobotResponse
{
string step = string.Empty;
var logList = new List<ExecutionLog>();
logList.Add(new ExecutionLog()
{
Id = idGenerator.NewLong(),
SourceSingal = singalRequest.SingalType,
RobotId = robot.Id,
CreateTime = DateTime.Now,
Content = $"收到信号{singalRequest.SingalType}{(isRemedy ? "()" : string.Empty)}"
});
try
{
var d21RuningInfo = RedisHelper.Get<D21RuningInfo>(robot.Id.ToString()) ?? new D21RuningInfo() { RobotId = robot.Id };
#region 验证信号
step = "验证信号";
if (!isRemedy)
{
if (d21RuningInfo.ErrorCrossSingal != null)
{
d21RuningInfo.ErrorCrossSingal = null;
d21RuningInfo.ErrorCrossSingalTime = 0;
RedisHelper.Set(robot.Id.ToString(), d21RuningInfo);
throw new BusinessException("前一个信号错误,终止空交叉信号执行");
}
if (d21RuningInfo.RecentSmallTrendSingal == null)
throw new BusinessException("缺少小趋势,终止空交叉信号执行");
if (d21RuningInfo.RecentSmallTrendSingal == Enums.SingalType.)
{
var errorTimeStamp = DateTime.Now.GetKID(singalRequest.KLinePeriodic, false);
Thread.Sleep(5000); //防止空交叉和小趋势看空同时接收造成误判
d21RuningInfo = RedisHelper.Get<D21RuningInfo>(robot.Id.ToString()) ?? new D21RuningInfo() { RobotId = robot.Id };
if (d21RuningInfo.RecentSmallTrendSingal == Enums.SingalType.)
{
d21RuningInfo.ErrorCrossSingal = singalRequest.SingalType;
d21RuningInfo.ErrorCrossSingalTime = errorTimeStamp;
RedisHelper.Set(robot.Id.ToString(), d21RuningInfo);
throw new BusinessException("小趋势看多,终止空交叉信号执行");
}
}
}
#endregion
#region 验证卖币数量
step = "验证卖币数量";
var saleQuantity = robot.RobotAccount.SpotCurrencyQuantity.CutDecimal(symbolInfo.SaleQuantityAccuracy);
if (saleQuantity == 0M)
throw new BusinessException("没有足够的卖币数量");
#endregion
#region 下单
step = "下单";
var newestPrice = globalContext.GetSpotNewestPrice(robot.KLineKey) ?? singalRequest.ClosePrice;
var apiClient = GetBaseAPIClient(robot.ExchangeId, robot.ExchangeAPIKey.AccountId, robot.ExchangeAPIKey.APIKey, robot.ExchangeAPIKey.SecretKey);
var clientOrderId = CreateClientOrderId(robot.Id);
var orderId = apiClient.IsolatedMarginPlaceOrder(robot.Symbol,
Enums.TradeDirection.Sell,
Enums.OrderType.MARKET,
quantity: saleQuantity,
newClientOrderId: clientOrderId);
var sellOrder = new SpotOrder()
{
Id = orderId,
CreateTime = DateTime.Now,
ExchangeId = robot.ExchangeId,
OrderType = Enums.OrderType.MARKET,
PolicyType = Enums.TradePolicy.D21,
RobotId = robot.Id,
State = Enums.SpotOrderState.Created,
Symbol = robot.Symbol,
TradeDirection = Enums.TradeDirection.Sell
};
logList.Add(new ExecutionLog()
{
Id = idGenerator.NewLong(),
SourceSingal = singalRequest.SingalType,
RobotId = robot.Id,
CreateTime = DateTime.Now,
Content = $"市价卖单挂单成功,orderId:{orderId}"
});
fsql.Transaction(() =>
{
fsql.Insert(logList).ExecuteAffrows();
fsql.Insert(sellOrder).ExecuteAffrows();
});
#endregion
#region 更新空交叉卖出成功时的成交价
d21RuningInfo.RecentShortCrossSignalTradePrice = newestPrice;
RedisHelper.Set(robot.Id.ToString(), d21RuningInfo);
#endregion
}
catch (Exception ex)
{
HandleError(ex, singalRequest.SingalType, logList, robot, step);
}
}
}
}

17
Binance.TradeRobot.Business/TaskSchedulerManager.cs

@ -0,0 +1,17 @@
using Binance.TradeRobot.Common.Tasks;
namespace Binance.TradeRobot.Business
{
public class TaskSchedulerManager
{
public LimitedConcurrencyLevelTaskScheduler SingalTaskScheduler { get; private set; }
public LimitedConcurrencyLevelTaskScheduler OrderPublishTaskScheduler { get; private set; }
public TaskSchedulerManager()
{
SingalTaskScheduler = new LimitedConcurrencyLevelTaskScheduler(10);
OrderPublishTaskScheduler = new LimitedConcurrencyLevelTaskScheduler(10);
}
}
}

13
Binance.TradeRobot.Model/Dto/Response/Robot/RobotResponse.cs

@ -28,20 +28,15 @@ namespace Binance.TradeRobot.Model.Dto
/// <summary>
/// 订单推送监听实例Key
/// <para>币安逐仓杠杆需要单独的运行实例</para>
/// </summary>
public virtual string OrderPublishListenKey
{
get
{
string key = string.Empty;
if (ExchangeId == Enums.Exchange.Binance)
{
if (BusinessType == Enums.BusinessType.IsolateMargin)
key = $"{BusinessType}-{ExchangeAPIKey.AccountId}-{Symbol}"; //币安逐仓杠杆,同一个账户内的每个交易对需要区分websocket实例
else
key = $"{BusinessType}-{ExchangeAPIKey.AccountId}"; //币安现货,币安合约,同一个账户内不区分websocket实例
}
string key = $"{BusinessType}-{ExchangeAPIKey.AccountId}"; //币安现货,币安合约,同一个账户内不区分websocket实例
if (ExchangeId == Enums.Exchange.Binance && BusinessType == Enums.BusinessType.IsolateMargin)
key = $"{BusinessType}-{ExchangeAPIKey.AccountId}-{Symbol}"; //币安逐仓杠杆,同一个账户内的每个交易对需要区分websocket实例
return $"OrderPublish(Origin)-{ExchangeId}-{key}";
}
}

Loading…
Cancel
Save