private void MakeSignalNewDeal( int accountId, string ticker, int side, int volume, decimal?stopLoss, decimal?takeProfit, int orderId, float priceEnter) { // разослать сигнал подписчикам? var signalCat = ManagementTraderList.Instance.IsSignaller(accountId); if (!signalCat.HasValue) { return; } Logger.InfoFormat("MakeSignalNewDeal (cat={0})", signalCat); // посчитать плечо сделки var leverage = CalculateDealLeverage(accountId, ticker, volume); if (leverage == null) { Logger.InfoFormat("MakeSignalNewDeal({0}, {1}, {2}, {3}) - leverage is null", accountId, ticker, side, volume); return; } // таки разослать var action = new TradeSignalActionTrade { ServiceId = signalCat.Value, Price = priceEnter, Side = side, Ticker = ticker, StopLoss = stopLoss ?? 0, TakeProfit = takeProfit ?? 0, OrderId = orderId, Leverage = leverage.Value }; Logger.InfoFormat("MakeSignalNewDeal({0}, {1}, {2}, {3}) - sending signal (leverage is {4:f2})", accountId, ticker, side, volume, leverage.Value); ServiceManagerClientManagerProxy.Instance.ProcessTradeSignalAction(action); }
public void TestCalcVolume() { var action = new TradeSignalActionTrade { Leverage = 1, OrderId = 9999999, Price = 1.3820f, Side = -1, ServiceId = serviceId, Ticker = "EURUSD" }; var tradeSets = new SUBSCRIPTION_SIGNAL { TargetAccount = testAccountId, AutoTrade = true, MaxLeverage = 10, MaxVolume = 100000, PercentLeverage = 120, HedgingOrdersEnabled = true, User = 0 }; // расчет объема обычного ордера var volume = Dealer.GetNewOrderVolume(action, connection, tradeSets); Assert.Greater(volume, 0, "TradeSignalDealer - объем больше 0"); // превышение плеча action.Leverage = 4.1M; tradeSets.MaxLeverage = 5; volume = Dealer.GetNewOrderVolume(action, connection, tradeSets); Assert.AreEqual(volume, 0, "TradeSignalDealer - объем 0 (превышение плеча)"); // просто сделка по йене action.Ticker = "USDJPY"; action.Price = 92.1f; action.Leverage = 2; tradeSets.MaxLeverage = 7.6f; volume = Dealer.GetNewOrderVolume(action, connection, tradeSets); Assert.AreEqual(70000, volume, "TradeSignalDealer - объем USDJPY больше 0"); }
/// <summary> /// торгануть по сигналу /// </summary> private void ProcessTradeSignalActionTrade(TradeSignalActionTrade trade) { }
public static int GetNewOrderVolume(TradeSignalActionTrade action, TradeSharpConnection ctx, SUBSCRIPTION_SIGNAL tradeSets) { // получить ордера по счету, посчитать актуальный баланс (средства) счета try { var account = ctx.ACCOUNT.FirstOrDefault(a => a.ID == tradeSets.TargetAccount.Value); if (account == null) { Logger.ErrorFormat("Ошибка при расчете объема по счету {0}, ордер {1}: счет не найден", tradeSets.TargetAccount, action.OrderId); return(0); } var equity = (double)account.Balance; // ордера var orders = ctx.POSITION.Where(p => p.AccountID == tradeSets.TargetAccount && p.State == (int)PositionState.Opened).ToList().Select(o => LinqToEntity.DecorateOrder(o)).ToList(); // проверить - нет ли противоположного ордера? var quotes = Contract.Util.BL.QuoteStorage.Instance.ReceiveAllData(); if (orders.Count > 0) { if (!(tradeSets.HedgingOrdersEnabled ?? false)) { var hasOrdersCounter = orders.Any(p => p.Symbol == action.Ticker && p.Side != action.Side); if (hasOrdersCounter) { logNoFlood.LogMessageFormatCheckFlood(LogEntryType.Info, LogMsgContraOrders, 1000 * 60 * 90, "Ордер {0} {1}, счет {2} - есть встречные ордера", action.Side > 0 ? "BUY" : "SELL", action.Ticker, tradeSets.TargetAccount); return(0); } } // посчитать профит по позам bool noQuoteError; var openResult = DalSpot.Instance.CalculateOpenedPositionsCurrentResult( orders, quotes, account.Currency, out noQuoteError); if (noQuoteError) { logNoFlood.LogMessageFormatCheckFlood(LogEntryType.Error, LogMsgVolmCalcNoQuote, 1000 * 60 * 90, "GetNewOrderVolume - нет котировки"); return(0); } equity += openResult; } if (equity <= 0) { logNoFlood.LogMessageFormatCheckFlood(LogEntryType.Error, LogMsgEquityIsNil, 1000 * 60 * 90, "Баланс счета {0}: {1}", account.ID, equity); return(0); } // посчитать объем var volumeDepo = (tradeSets.PercentLeverage ?? 100) / 100f * equity * (double)action.Leverage; // пересчитать объем в базовую валюту string errorStr; var volumeBase = DalSpot.Instance.ConvertToTargetCurrency(action.Ticker, true, account.Currency, volumeDepo, quotes, out errorStr); if (!volumeBase.HasValue) { logNoFlood.LogMessageFormatCheckFlood(LogEntryType.Error, LogMsgVolmToBase, 1000 * 60 * 90, "GetNewOrderVolume - невозможно посчитать объем по сделке {0}, валюта счета {1}: {2}", action.Ticker, account.Currency, errorStr); return(0); } // округлить объем var volume = MarketOrder.RoundDealVolume((int)volumeBase, (VolumeRoundType)(tradeSets.VolumeRound ?? (int)VolumeRoundType.Ближайшее), tradeSets.MinVolume ?? 10000, tradeSets.StepVolume ?? 10000); if (volume == 0) { var msg = string.Format("Полученный объем входа ({0}) округлен до 0", (int)volumeBase); logNoFlood.LogMessageFormatCheckFlood(LogEntryType.Error, LogMsgVolmRoundToZero, 1000 * 60 * 90, msg); return(0); } // проверить на превышение плеча if ((tradeSets.MaxLeverage ?? 0) > 0) { var totalExposure = 0M; var ordersBySignaller = orders.Where(position => { int orderSignalCatId, parentDealId; if (MarketOrder.GetTradeSignalFromDeal(position, out orderSignalCatId, out parentDealId)) { return(orderSignalCatId == action.ServiceId); } return(false); }).GroupBy(o => o.Symbol).ToDictionary(o => o.Key, o => o.ToList()); foreach (var orderByTicker in ordersBySignaller) { // суммарная экспозиция var exp = orderByTicker.Value.Sum(o => o.Side * o.Volume); if (orderByTicker.Key == action.Ticker) { exp += action.Side * volume; } if (exp == 0) { continue; } // перевести экспозицию в валюту депозита var expBase = DalSpot.Instance.ConvertToTargetCurrency(action.Ticker, true, account.Currency, volumeDepo, quotes, out errorStr); if (!expBase.HasValue) { logNoFlood.LogMessageFormatCheckFlood(LogEntryType.Error, LogMsgExposureNoQuote, 1000 * 60 * 90, "GetNewOrderVolume - невозможно посчитать экспозицию по сделке " + "{0}, тикер {1}: {2}", action.Ticker, orderByTicker.Key, errorStr); return(0); } totalExposure += Math.Abs(expBase.Value); } var totalLeverage = (double)totalExposure / equity; // ReSharper disable PossibleInvalidOperationException if (totalLeverage > tradeSets.MaxLeverage.Value) // ReSharper restore PossibleInvalidOperationException { logNoFlood.LogMessageFormatCheckFlood(LogEntryType.Error, LogMsgMaxLevExceeded, 1000 * 60 * 90, "GetNewOrderVolume - макс плечо ({0}) превышено, " + "тикер {1}: плечо составит {2}", tradeSets.MaxLeverage.Value, action.Ticker, totalLeverage); return(0); } } return(volume); } catch (Exception ex) { Logger.ErrorFormat("Ошибка при расчете объема по счету {0}, ордер {1}: {2}", tradeSets.TargetAccount, action.OrderId, ex); } return(0); }
private static void ProcessSignalOpenOrder(TradeSignalActionTrade action) { try { var signalRecord = EnqueueProcessedSignalRecord(action); using (var ctx = DatabaseContext.Instance.Make()) { // получить информацию по настройкам торговых сигналов var subscriptions = (from subsSig in ctx.SUBSCRIPTION_SIGNAL where (subsSig.AutoTrade ?? false) && subsSig.Service == action.ServiceId && subsSig.TargetAccount.HasValue select subsSig).ToList(); foreach (var sub in subscriptions) { var volm = sub.FixedVolume ?? 0; if (volm == 0) { volm = GetNewOrderVolume(action, ctx, sub); } // объем входа недопустимо мал либо ошибка в расчете if (volm == 0) { return; } var order = new MarketOrder { // ReSharper disable PossibleInvalidOperationException AccountID = sub.TargetAccount.Value, // ReSharper restore PossibleInvalidOperationException Volume = volm, Side = action.Side, Symbol = action.Ticker, StopLoss = (float?)action.StopLoss, TakeProfit = (float?)action.TakeProfit, Magic = action.OrderId, ExpertComment = MarketOrder.MakeSignalComment( action.ServiceId), MasterOrder = action.OrderId }; try { var status = ProxyTrade.SendNewOrderRequest(ProtectedOperationContext.MakeServerSideContext(), action.OrderId, order, OrderType.Market, (decimal)action.Price, 0); if (signalRecord.subscriberAccountProcessingStatus.ContainsKey(order.AccountID)) { Logger.ErrorFormat("Сигнал {0} - повторная отправка на счет {1}", action, order.AccountID); } else { signalRecord.subscriberAccountProcessingStatus[order.AccountID] = status; } if (status != RequestStatus.OK) { Logger.ErrorFormat("ProcessSignalOpenOrder - ошибка обработки сервером" + " запроса SendNewOrderRequest, master order: {0}, account: {1}, {2}", action.OrderId, sub.TargetAccount, status); } } catch (Exception ex) { Logger.ErrorFormat( "ProcessSignalOpenOrder - ошибка отправки запроса SendNewOrderRequest, master order: {0}, account: {1}, {2}", action.OrderId, sub.TargetAccount, ex); } } } } catch (Exception ex) { Logger.DebugFormat("ProcessSignalOpenOrder(#{0} - {1}) - исключение: {2}", action.OrderId, action.Ticker, ex); } }
public static int GetNewOrderVolume(TradeSignalActionTrade action, TradeSharpConnection ctx, SUBSCRIPTION_SIGNAL tradeSets) { // получить ордера по счету, посчитать актуальный баланс (средства) счета try { var account = ctx.ACCOUNT.FirstOrDefault(a => a.ID == tradeSets.TargetAccount.Value); if (account == null) { Logger.ErrorFormat("Ошибка при расчете объема по счету {0}, ордер {1}: счет не найден", tradeSets.TargetAccount, action.OrderId); return 0; } var equity = (double)account.Balance; // ордера var orders = ctx.POSITION.Where(p => p.AccountID == tradeSets.TargetAccount && p.State == (int)PositionState.Opened).ToList().Select(o => LinqToEntity.DecorateOrder(o)).ToList(); // проверить - нет ли противоположного ордера? var quotes = Contract.Util.BL.QuoteStorage.Instance.ReceiveAllData(); if (orders.Count > 0) { if (!(tradeSets.HedgingOrdersEnabled ?? false)) { var hasOrdersCounter = orders.Any(p => p.Symbol == action.Ticker && p.Side != action.Side); if (hasOrdersCounter) { logNoFlood.LogMessageFormatCheckFlood(LogEntryType.Info, LogMsgContraOrders, 1000 * 60 * 90, "Ордер {0} {1}, счет {2} - есть встречные ордера", action.Side > 0 ? "BUY" : "SELL", action.Ticker, tradeSets.TargetAccount); return 0; } } // посчитать профит по позам bool noQuoteError; var openResult = DalSpot.Instance.CalculateOpenedPositionsCurrentResult( orders, quotes, account.Currency, out noQuoteError); if (noQuoteError) { logNoFlood.LogMessageFormatCheckFlood(LogEntryType.Error, LogMsgVolmCalcNoQuote, 1000 * 60 * 90, "GetNewOrderVolume - нет котировки"); return 0; } equity += openResult; } if (equity <= 0) { logNoFlood.LogMessageFormatCheckFlood(LogEntryType.Error, LogMsgEquityIsNil, 1000 * 60 * 90, "Баланс счета {0}: {1}", account.ID, equity); return 0; } // посчитать объем var volumeDepo = (tradeSets.PercentLeverage ?? 100) / 100f * equity * (double)action.Leverage; // пересчитать объем в базовую валюту string errorStr; var volumeBase = DalSpot.Instance.ConvertToTargetCurrency(action.Ticker, true, account.Currency, volumeDepo, quotes, out errorStr); if (!volumeBase.HasValue) { logNoFlood.LogMessageFormatCheckFlood(LogEntryType.Error, LogMsgVolmToBase, 1000 * 60 * 90, "GetNewOrderVolume - невозможно посчитать объем по сделке {0}, валюта счета {1}: {2}", action.Ticker, account.Currency, errorStr); return 0; } // округлить объем var volume = MarketOrder.RoundDealVolume((int)volumeBase, (VolumeRoundType)(tradeSets.VolumeRound ?? (int)VolumeRoundType.Ближайшее), tradeSets.MinVolume ?? 10000, tradeSets.StepVolume ?? 10000); if (volume == 0) { var msg = string.Format("Полученный объем входа ({0}) округлен до 0", (int)volumeBase); logNoFlood.LogMessageFormatCheckFlood(LogEntryType.Error, LogMsgVolmRoundToZero, 1000 * 60 * 90, msg); return 0; } // проверить на превышение плеча if ((tradeSets.MaxLeverage ?? 0) > 0) { var totalExposure = 0M; var ordersBySignaller = orders.Where(position => { int orderSignalCatId, parentDealId; if (MarketOrder.GetTradeSignalFromDeal(position, out orderSignalCatId, out parentDealId)) return orderSignalCatId == action.ServiceId; return false; }).GroupBy(o => o.Symbol).ToDictionary(o => o.Key, o => o.ToList()); foreach (var orderByTicker in ordersBySignaller) { // суммарная экспозиция var exp = orderByTicker.Value.Sum(o => o.Side * o.Volume); if (orderByTicker.Key == action.Ticker) exp += action.Side * volume; if (exp == 0) continue; // перевести экспозицию в валюту депозита var expBase = DalSpot.Instance.ConvertToTargetCurrency(action.Ticker, true, account.Currency, volumeDepo, quotes, out errorStr); if (!expBase.HasValue) { logNoFlood.LogMessageFormatCheckFlood(LogEntryType.Error, LogMsgExposureNoQuote, 1000 * 60 * 90, "GetNewOrderVolume - невозможно посчитать экспозицию по сделке " + "{0}, тикер {1}: {2}", action.Ticker, orderByTicker.Key, errorStr); return 0; } totalExposure += Math.Abs(expBase.Value); } var totalLeverage = (double)totalExposure / equity; // ReSharper disable PossibleInvalidOperationException if (totalLeverage > tradeSets.MaxLeverage.Value) // ReSharper restore PossibleInvalidOperationException { logNoFlood.LogMessageFormatCheckFlood(LogEntryType.Error, LogMsgMaxLevExceeded, 1000 * 60 * 90, "GetNewOrderVolume - макс плечо ({0}) превышено, " + "тикер {1}: плечо составит {2}", tradeSets.MaxLeverage.Value, action.Ticker, totalLeverage); return 0; } } return volume; } catch (Exception ex) { Logger.ErrorFormat("Ошибка при расчете объема по счету {0}, ордер {1}: {2}", tradeSets.TargetAccount, action.OrderId, ex); } return 0; }
private static void ProcessSignalOpenOrder(TradeSignalActionTrade action) { try { var signalRecord = EnqueueProcessedSignalRecord(action); using (var ctx = DatabaseContext.Instance.Make()) { // получить информацию по настройкам торговых сигналов var subscriptions = (from subsSig in ctx.SUBSCRIPTION_SIGNAL where (subsSig.AutoTrade ?? false) && subsSig.Service == action.ServiceId && subsSig.TargetAccount.HasValue select subsSig).ToList(); foreach (var sub in subscriptions) { var volm = sub.FixedVolume ?? 0; if (volm == 0) volm = GetNewOrderVolume(action, ctx, sub); // объем входа недопустимо мал либо ошибка в расчете if (volm == 0) return; var order = new MarketOrder { // ReSharper disable PossibleInvalidOperationException AccountID = sub.TargetAccount.Value, // ReSharper restore PossibleInvalidOperationException Volume = volm, Side = action.Side, Symbol = action.Ticker, StopLoss = (float?)action.StopLoss, TakeProfit = (float?)action.TakeProfit, Magic = action.OrderId, ExpertComment = MarketOrder.MakeSignalComment( action.ServiceId), MasterOrder = action.OrderId }; try { var status = ProxyTrade.SendNewOrderRequest(ProtectedOperationContext.MakeServerSideContext(), action.OrderId, order, OrderType.Market, (decimal)action.Price, 0); if (signalRecord.subscriberAccountProcessingStatus.ContainsKey(order.AccountID)) Logger.ErrorFormat("Сигнал {0} - повторная отправка на счет {1}", action, order.AccountID); else signalRecord.subscriberAccountProcessingStatus[order.AccountID] = status; if (status != RequestStatus.OK) Logger.ErrorFormat("ProcessSignalOpenOrder - ошибка обработки сервером" + " запроса SendNewOrderRequest, master order: {0}, account: {1}, {2}", action.OrderId, sub.TargetAccount, status); } catch (Exception ex) { Logger.ErrorFormat( "ProcessSignalOpenOrder - ошибка отправки запроса SendNewOrderRequest, master order: {0}, account: {1}, {2}", action.OrderId, sub.TargetAccount, ex); } } } } catch (Exception ex) { Logger.DebugFormat("ProcessSignalOpenOrder(#{0} - {1}) - исключение: {2}", action.OrderId, action.Ticker, ex); } }
private void MakeSignalNewDeal( int accountId, string ticker, int side, int volume, decimal? stopLoss, decimal? takeProfit, int orderId, float priceEnter) { // разослать сигнал подписчикам? var signalCat = ManagementTraderList.Instance.IsSignaller(accountId); if (!signalCat.HasValue) return; Logger.InfoFormat("MakeSignalNewDeal (cat={0})", signalCat); // посчитать плечо сделки var leverage = CalculateDealLeverage(accountId, ticker, volume); if (leverage == null) { Logger.InfoFormat("MakeSignalNewDeal({0}, {1}, {2}, {3}) - leverage is null", accountId, ticker, side, volume); return; } // таки разослать var action = new TradeSignalActionTrade { ServiceId = signalCat.Value, Price = priceEnter, Side = side, Ticker = ticker, StopLoss = stopLoss ?? 0, TakeProfit = takeProfit ?? 0, OrderId = orderId, Leverage = leverage.Value }; Logger.InfoFormat("MakeSignalNewDeal({0}, {1}, {2}, {3}) - sending signal (leverage is {4:f2})", accountId, ticker, side, volume, leverage.Value); ServiceManagerClientManagerProxy.Instance.ProcessTradeSignalAction(action); }