You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
236 lines
12 KiB
236 lines
12 KiB
using Binance.TradeRobot.Business.Extensions;
|
|
using Binance.TradeRobot.Common.DI;
|
|
using Binance.TradeRobot.Common.Extensions;
|
|
using Binance.TradeRobot.Common.Http;
|
|
using Binance.TradeRobot.Model.Base;
|
|
using Binance.TradeRobot.Model.Db;
|
|
using Binance.TradeRobot.Model.Dto;
|
|
using Microsoft.Extensions.Caching.Memory;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Newtonsoft.Json.Linq;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Net.Http;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Yitter.IdGenerator;
|
|
|
|
namespace Binance.TradeRobot.Business
|
|
{
|
|
[BatchRegistration(ServiceLifetime.Singleton, RegistrationType.Self)]
|
|
public class ExchangeBusiness : BaseBusiness
|
|
{
|
|
private RestApiService restApiService;
|
|
|
|
public ExchangeBusiness(IFreeSql fsql,
|
|
NLogManager logManager,
|
|
IIdGenerator idGenerator,
|
|
IMemoryCache memoryCache,
|
|
RestApiService restApiService) : base(fsql, logManager, idGenerator, memoryCache)
|
|
{
|
|
this.restApiService = restApiService;
|
|
}
|
|
|
|
public void SyncBinanceSymbol()
|
|
{
|
|
var apiResult = restApiService.SendRequest("https://api.binance.com/", "api/v3/exchangeInfo", null, null, HttpMethod.Get);
|
|
if (apiResult.StatusCode != System.Net.HttpStatusCode.OK)
|
|
throw new BusinessException($"同步币安交易对失败 StatusCode {apiResult.StatusCode} {apiResult.Content}");
|
|
var jobject = JObject.Parse(apiResult.Content);
|
|
var jarray_symbols = (JArray)jobject["symbols"];
|
|
var symbolList = new List<SymbolInfo>();
|
|
foreach (JToken jtoken_symbol in jarray_symbols)
|
|
{
|
|
var symbol = jtoken_symbol.Value<string>("symbol").ToUpper();
|
|
if (!symbol.EndsWith("USDT"))
|
|
continue;
|
|
var stepSize = jtoken_symbol["filters"]?.FirstOrDefault(jtoken_filters => jtoken_filters.Value<string>("filterType") == "LOT_SIZE")?.Value<decimal>("stepSize") ?? 0M;
|
|
var tickSize = jtoken_symbol["filters"]?.FirstOrDefault(jtoken_filters => jtoken_filters.Value<string>("filterType") == "PRICE_FILTER")?.Value<decimal>("tickSize") ?? 0M;
|
|
var saleAccuracy = stepSize != 0 ? (1 / stepSize).ToString().Length - 1 : 0;
|
|
var priceAccuracy = tickSize != 0 ? (1 / tickSize).ToString().Length - 1 : 0;
|
|
var symbolInfo = new SymbolInfo()
|
|
{
|
|
Id = idGenerator.NewLong(),
|
|
ExchangeId = Enums.Exchange.Binance,
|
|
CreateTime = DateTime.Now,
|
|
Symbol = symbol,
|
|
SaleQuantityAccuracy = saleAccuracy,
|
|
PriceAccuracy = priceAccuracy
|
|
};
|
|
symbolList.Add(symbolInfo);
|
|
}
|
|
fsql.Transaction(() =>
|
|
{
|
|
fsql.Delete<SymbolInfo>().Where(s => s.ExchangeId == Enums.Exchange.Binance).ExecuteAffrows();
|
|
fsql.Insert(symbolList).ExecuteAffrows();
|
|
});
|
|
}
|
|
|
|
public void SyncGateIOSymbol()
|
|
{
|
|
|
|
}
|
|
|
|
public IList<SymbolInfoResponse> GetSymbolList(Enums.Exchange exchange)
|
|
{
|
|
return fsql.Select<SymbolInfo>().Where(s => s.ExchangeId == exchange).ToList<SymbolInfoResponse>();
|
|
}
|
|
|
|
public SymbolInfoResponse GetSymbol(Enums.Exchange exchange, string symbol)
|
|
{
|
|
return fsql.Select<SymbolInfo>().Where(s => s.ExchangeId == exchange && s.Symbol == symbol).ToOne<SymbolInfoResponse>();
|
|
}
|
|
|
|
public void AddExchangeAccount(AddExchangeAccountRequest addExchangeAccountRequest)
|
|
{
|
|
if (addExchangeAccountRequest.Id == 0 ||
|
|
string.IsNullOrEmpty(addExchangeAccountRequest.LoginName) ||
|
|
string.IsNullOrEmpty(addExchangeAccountRequest.APIKey) ||
|
|
string.IsNullOrEmpty(addExchangeAccountRequest.SecretKey))
|
|
throw new BusinessException("交易所账号参数有误");
|
|
if (fsql.Select<ExchangeAccount>(addExchangeAccountRequest.Id).Any())
|
|
throw new BusinessException("交易所账号重复");
|
|
if (fsql.Select<ExchangeAPIKey>().Where(k => k.APIKey == addExchangeAccountRequest.APIKey || k.SecretKey == addExchangeAccountRequest.SecretKey).Any())
|
|
throw new BusinessException("重复的APIKey或SecretKey");
|
|
|
|
var exchangeAccount = addExchangeAccountRequest.Map<ExchangeAccount>();
|
|
exchangeAccount.BusinessType = addExchangeAccountRequest.TradePolicy.GetBusinessType();
|
|
var exchangeAPIKey = new ExchangeAPIKey()
|
|
{
|
|
Id = idGenerator.NewLong(),
|
|
AccountId = addExchangeAccountRequest.Id,
|
|
APIKey = addExchangeAccountRequest.APIKey,
|
|
SecretKey = addExchangeAccountRequest.SecretKey
|
|
};
|
|
|
|
fsql.Transaction(() =>
|
|
{
|
|
fsql.Insert(exchangeAPIKey).ExecuteAffrows();
|
|
fsql.Insert(exchangeAccount).ExecuteAffrows();
|
|
});
|
|
|
|
}
|
|
|
|
public void AddExchangeAPIKey(AddExchangeAPIKeyRequest addExchangeAPIKeyRequest)
|
|
{
|
|
if (addExchangeAPIKeyRequest.AccountId == 0 ||
|
|
string.IsNullOrEmpty(addExchangeAPIKeyRequest.APIKey) ||
|
|
string.IsNullOrEmpty(addExchangeAPIKeyRequest.SecretKey))
|
|
throw new BusinessException("参数有误");
|
|
|
|
if (fsql.Select<ExchangeAPIKey>().Where(k => k.APIKey == addExchangeAPIKeyRequest.APIKey || k.SecretKey == addExchangeAPIKeyRequest.SecretKey).Any())
|
|
throw new BusinessException("重复的APIKey或SecretKey");
|
|
|
|
var exchangeAPIKey = addExchangeAPIKeyRequest.Map<ExchangeAPIKey>();
|
|
exchangeAPIKey.Id = idGenerator.NewLong();
|
|
fsql.Insert(exchangeAPIKey).ExecuteAffrows();
|
|
}
|
|
|
|
public IList<ExchangeAccountResponse> GetExchangeAccountList(Enums.TradePolicy tradePolicy)
|
|
{
|
|
var exchangeAccountList = fsql.Select<ExchangeAccount>().Where(ea => ea.TradePolicy == tradePolicy).ToList().Map<IList<ExchangeAccountResponse>>();
|
|
var accountIdList = exchangeAccountList.Select(ea => ea.Id);
|
|
var exchangeAPIKeyList = fsql.Select<ExchangeAPIKey, Robot>().LeftJoin((k, r) => k.RobotId == r.Id)
|
|
.Where((k, r) => accountIdList.Contains(k.AccountId))
|
|
.ToList((k, r) => new ExchangeAPIKeyResponse()
|
|
{
|
|
Id = k.Id,
|
|
AccountId = k.AccountId,
|
|
APIKey = k.APIKey,
|
|
SecretKey = k.SecretKey,
|
|
CreateTime = k.CreateTime,
|
|
RobotId = k.RobotId,
|
|
RobotSymbol = r.Symbol
|
|
});
|
|
|
|
var waitList = new List<WaitHandle>();
|
|
foreach (var exchangeAccount in exchangeAccountList)
|
|
{
|
|
var currentExchangeAccountAPIKeyList = exchangeAPIKeyList.Where(k => k.AccountId == exchangeAccount.Id);
|
|
if (currentExchangeAccountAPIKeyList.Count() == 0)
|
|
continue;
|
|
exchangeAccount.ExchangeAPIKeyList.AddRange(currentExchangeAccountAPIKeyList);
|
|
|
|
var ewh = new ManualResetEvent(false);
|
|
waitList.Add(ewh);
|
|
Task.Factory.StartNew(() => GetExchangeAssets(exchangeAccount, ewh));
|
|
}
|
|
WaitHandle.WaitAll(waitList.ToArray(), 5000);
|
|
return exchangeAccountList;
|
|
}
|
|
|
|
private void GetExchangeAssets(ExchangeAccountResponse exchangeAccount, EventWaitHandle ewh)
|
|
{
|
|
try
|
|
{
|
|
var firstAPIKey = exchangeAccount.ExchangeAPIKeyList.FirstOrDefault();
|
|
var apiClient = GetBaseAPIClient(exchangeAccount.ExchangeId, exchangeAccount.Id, firstAPIKey.APIKey, firstAPIKey.SecretKey);
|
|
if (exchangeAccount.BusinessType == Enums.BusinessType.UPrep)
|
|
{
|
|
//var result = binanceClient.UsdFuturesApi.Account.GetBalancesAsync().Result;
|
|
//if (result.Success)
|
|
//{
|
|
// exchangeAccount.UPrepUSDT = result.Data.FirstOrDefault(b => b.Asset == "USDT")?.WalletBalance ?? 0;
|
|
//}
|
|
}
|
|
else if (exchangeAccount.BusinessType == Enums.BusinessType.Spot)
|
|
{
|
|
|
|
}
|
|
else if (exchangeAccount.BusinessType == Enums.BusinessType.IsolateMargin)
|
|
{
|
|
var isolatedMarginAccountAssetList = apiClient.GetIsolatedMarginAccountAssets();
|
|
foreach (var apiKey in exchangeAccount.ExchangeAPIKeyList)
|
|
{
|
|
if (apiKey.RobotId == null)
|
|
continue;
|
|
var asset = isolatedMarginAccountAssetList.FirstOrDefault(s => s.Symbol == apiKey.RobotSymbol);
|
|
if (asset != null)
|
|
{
|
|
apiKey.BaseAmount = asset.BaseFree;
|
|
apiKey.QuoteAmount = asset.QuoteFree;
|
|
apiKey.QuoteNetAmount = asset.QuoteNetAsset;
|
|
apiKey.QuoteLoanAmount = asset.QuoteBorrowed;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
|
|
}
|
|
finally
|
|
{
|
|
foreach (var apiKey in exchangeAccount.ExchangeAPIKeyList)
|
|
apiKey.SecretKey = string.Empty;
|
|
ewh.Set();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取APIKey未使用交易所账户列表
|
|
/// </summary>
|
|
/// <param name="tradePolicy"></param>
|
|
/// <param name="exchangeId"></param>
|
|
/// <returns></returns>
|
|
public IList<ExchangeAccountResponse> GetNoUsedExchangeAccountList(Enums.TradePolicy tradePolicy, Enums.Exchange exchangeId)
|
|
{
|
|
var exchangeAccountList = fsql.Select<ExchangeAccount>().Where(ea => ea.TradePolicy == tradePolicy && ea.ExchangeId == exchangeId)
|
|
.ToList().Map<IList<ExchangeAccountResponse>>();
|
|
var accountIdList = exchangeAccountList.Select(ea => ea.Id);
|
|
var exchangeAPIKeyList = fsql.Select<ExchangeAPIKey>().Where(k => k.RobotId == null && accountIdList.Contains(k.AccountId))
|
|
.ToList()
|
|
.Map<IList<ExchangeAPIKeyResponse>>();
|
|
|
|
foreach (var exchangeAccount in exchangeAccountList)
|
|
{
|
|
var currentExchangeAccountAPIKeyList = exchangeAPIKeyList.Where(k => k.AccountId == exchangeAccount.Id);
|
|
if (currentExchangeAccountAPIKeyList.Count() == 0)
|
|
continue;
|
|
exchangeAccount.ExchangeAPIKeyList.AddRange(currentExchangeAccountAPIKeyList);
|
|
}
|
|
return exchangeAccountList;
|
|
}
|
|
}
|
|
}
|
|
|