private int CalcTradeSharpDealVolume(int srcVolume) { if (FixedVolume.HasValue && FixedVolume.Value > 0) { return(FixedVolume.Value); } // посчитать объем из плеча var eqMt4 = mt4Equity ?? 0; // ReSharper disable CompareOfFloatsByEqualityOperator if (eqMt4 == 0) { return(0); } var ownEq = robotContext.AccountInfo.Equity; if (ownEq == 0) { return(0); } // ReSharper restore CompareOfFloatsByEqualityOperator var koeff = PercentScale / 100M * ownEq / eqMt4; srcVolume = (int)Math.Round(srcVolume * koeff); // выровнять объем return(MarketOrder.RoundDealVolume(srcVolume, VolumeRoundType.Ближайшее, 10000, 10000)); }
private void CalcDealVolume() { var account = AccountStatus.Instance.AccountData ?? new Account { Equity = 10000, Balance = 10000, Currency = "USD", Group = "Demo" }; var balance = tbBalance.Text.Replace(" ", "").ToFloatUniformSafe() ?? 10000; var leverage = tbOrderLeverage.Text.ToFloatUniformSafe() ?? 1; var volumeDepo = balance * leverage; // пересчитать в базовую валюту string errorStr; var volmBase = DalSpot.Instance.ConvertToTargetCurrency(cbTicker.Text, true, account.Currency, volumeDepo, QuoteStorage.Instance.ReceiveAllData(), out errorStr); if (!string.IsNullOrEmpty(errorStr)) { Logger.Error("CalcDealVolume() - ошибка перевода в валюту депо " + cbTicker.Text + " " + errorStr); } tbResultedVolume.Text = volmBase.HasValue ? volmBase.Value.ToStringUniformMoneyFormat(false) : "-"; // округлить объем var lotSize = DalSpot.Instance.GetMinStepLot(cbTicker.Text, account.Group); tbResultRounded.Text = volmBase.HasValue ? MarketOrder.RoundDealVolume((int)Math.Round(volmBase.Value), ((EnumItem <VolumeRoundType>)cbRoundVolume.SelectedItem).Value, lotSize.a, lotSize.b).ToStringUniformMoneyFormat() : "-"; }
protected int CalculateVolumeInBaseCurrency(decimal volumeDepo, string tradeTicker, VolumeRoundType roundType, QuoteData quoteByTicker = null) { var volumeBase = volumeDepo; var depoCurx = robotContext.AccountInfo.Currency; var quotes = QuoteStorage.Instance.ReceiveAllData(); bool inverse, pairsEqual; var tickerTrans = DalSpot.Instance.FindSymbol(tradeTicker, true, depoCurx, out inverse, out pairsEqual); if (!pairsEqual) { QuoteData quote; if (tickerTrans == tradeTicker && quoteByTicker != null) { quote = quoteByTicker; } else { quotes.TryGetValue(tickerTrans, out quote); } if (quote == null) { var msgError = string.Format( "Невозможно рассчитать объем - отсутствует котировка \"{0}\"", tickerTrans); Logger.Info(msgError); return(0); } var priceTrans = inverse ? 1 / quote.bid : quote.ask; volumeBase /= (decimal)priceTrans; } return(MarketOrder.RoundDealVolume((int)volumeBase, roundType, RoundMinVolume, RoundVolumeStep)); }
/// <summary> /// Расчёт объёма сделки в валюте депозита с учётом наличия или отсутствия фиксированного объёма /// </summary> protected int CalculateVolume(string ticker, decimal?calculateLeverage = null, int?fixedVolm = null) { int?baseDealVolume = fixedVolm ?? FixedVolume; if (!fixedVolm.HasValue || fixedVolm.Value == 0) { calculateLeverage = calculateLeverage ?? Leverage; Account ac; robotContext.GetAccountInfo(robotContext.AccountInfo.ID, true, out ac); if (ac == null || ac.Equity <= 0) { var errorStr = ac == null ? string.Format("Счет {0} не найден", robotContext.AccountInfo.ID) : string.Format("На счете {0} недостаточно средств ({1})", robotContext.AccountInfo.ID, ac.Equity.ToStringUniformMoneyFormat()); Logger.Info(errorStr); return(0); } var leverageVolume = ac.Equity * calculateLeverage.Value; string error; var depoVolm = DalSpot.Instance.ConvertToTargetCurrency(ticker, true, ac.Currency, (double)leverageVolume, robotContext.QuotesStorage.ReceiveAllData(), out error) ?? 0M; if (depoVolm == 0) { Logger.InfoFormat("Не удалось перевести средства в валюту депозита. " + error); return(0); } baseDealVolume = (int)Math.Round(leverageVolume * leverageVolume / depoVolm); } var roundMinVolm = RoundMinVolume; var roundVolmStep = RoundVolumeStep; if (roundMinVolm == 0 || roundVolmStep == 0) { var minStepLot = DalSpot.Instance.GetMinStepLot(ticker, robotContext.AccountInfo.Group); if (roundMinVolm == 0) { roundMinVolm = minStepLot.b; } if (roundVolmStep == 0) { roundVolmStep = minStepLot.a; } } var volume = MarketOrder.RoundDealVolume((int)baseDealVolume.Value, RoundType, roundMinVolm, roundVolmStep); if (volume == 0) { Logger.InfoFormat("{0}: OpenDeal({0} {1}) - объем в валюте депозита ({2:f2}) недостаточен", TypeName, ticker, volume); } return(volume); }
private void AutoCalcVolume() { var account = AccountStatus.Instance.AccountData; if (account == null) { return; } var symbol = cbCurx.Text; var leverage = UserSettings.Instance.DealLeverage ?? 0; if (leverage == 0) { return; } // из плеча получить объем базовой валюты var volmDepo = leverage * account.Equity; string errorString; var volmBase = DalSpot.Instance.ConvertToTargetCurrency(symbol, true, account.Currency, (double)volmDepo, QuoteStorage.Instance.ReceiveAllData(), out errorString); if (!string.IsNullOrEmpty(errorString)) { return; } if (!volmBase.HasValue) { return; } // оркуглить if (UserSettings.Instance.RoundCalculatedDealVolume) { var lotSize = DalSpot.Instance.GetMinStepLot(symbol, account.Group); volmBase = MarketOrder.RoundDealVolume((int)volmBase.Value, UserSettings.Instance.VolumeRoundType, lotSize.minVolume, lotSize.volumeStep); } cbVolume.Text = ((int)volmBase.Value).ToStringUniformMoneyFormat(); }
public override string ActivateScript(string ticker) { if (!AccountStatus.Instance.isAuthorized) { MessageBox.Show("Не авторизован"); return("Не авторизован"); } var account = AccountStatus.Instance.AccountData; if (account == null) { MessageBox.Show("Не авторизован"); return("Не авторизован"); } var equity = account.Equity; if (equity <= 0) { MessageBox.Show("На счете отсутствуют денежные средства"); return("На счете отсутствуют денежные средства"); } var dealSide = side; decimal?sl = null, tp = null; float? trailLevel = null, trailTarget = null; // предполагаемая цена входа var quotes = QuoteStorage.Instance.ReceiveAllData(); QuoteData currentQuote; quotes.TryGetValue(ticker, out currentQuote); var reqPrice = currentQuote == null ? 0 : Side == DealType.Buy ? currentQuote.ask : currentQuote.bid; if (OrderType == ScriptOrderType.Отложенный && currentQuote == null) { return("Отложенный ордер не создан - нет котировки " + ticker); } // для отложенного ордера обязательно запросим цену decimal priceEnter = 0; float dealPrice = 0; if (RequestSide || OrderType == ScriptOrderType.Отложенный) { var dlg = new ScriptPartDepoFormDialog( (int)side, currentQuote == null ? (float?)null : currentQuote.bid, OrderType == ScriptOrderType.Отложенный) { TradeTicker = ticker }; if (dlg.ShowDialog() == DialogResult.Cancel) { return("Отменен"); } priceEnter = dlg.Price ?? 0; sl = dlg.SL; tp = dlg.TP; dealSide = (DealType)dlg.Side; if (dlg.Trailing != null && dlg.Trailing.Length == 2 && (dlg.Trailing[0] != 0 || dlg.Trailing[1] != 0)) { if (currentQuote == null) { var msg = string.Format("Нет котировки \"{0}\" для расчета пп трейлинга", ticker); MessageBox.Show(msg); return(msg); } var trailPrice = dlg.Trailing[0]; dealPrice = OrderType == ScriptOrderType.Отложенный ? (float)priceEnter : (dealSide == DealType.Buy ? currentQuote.ask : currentQuote.bid); var trailPips = DalSpot.Instance.GetPointsValue(ticker, ((int)dealSide) * (trailPrice - dealPrice)); // преобразовать цену в пп от входа trailLevel = trailPips; trailTarget = dlg.Trailing[1]; } } // объем в валюте депозита var volumeDepo = equity * leverage; var volumeBase = volumeDepo; // из объема депо пересчитать в объем базовой валюты var depoCurx = AccountStatus.Instance.AccountData.Currency; bool inverse, pairsEqual; var tickerTrans = DalSpot.Instance.FindSymbol(ticker, true, depoCurx, out inverse, out pairsEqual); if (!pairsEqual) { QuoteData quote; quotes.TryGetValue(tickerTrans, out quote); if (quote == null) { var msgError = string.Format( "Невозможно рассчитать объем - отсутствует котировка \"{0}\"", tickerTrans); MessageBox.Show(msgError); return(msgError); } var priceTrans = inverse ? 1 / quote.bid : quote.ask; volumeBase /= (decimal)priceTrans; } // округлить объем var lotSize = DalSpot.Instance.GetMinStepLot(ticker, account.Group); volumeBase = MarketOrder.RoundDealVolume((int)volumeBase, VolumeRound, lotSize.minVolume, lotSize.volumeStep); if (volumeBase == 0) { var msgError = $"Рассчетный объем входа ({volumeBase}) меньше допустимого ({lotSize.minVolume})"; MessageBox.Show(msgError); return(msgError); } // проверить количество входов (по тикеру либо вообще) var ordersCount = DealCounting == DealCountingType.ОбщийУчет ? MarketOrdersStorage.Instance.MarketOrders.Count : MarketOrdersStorage.Instance.MarketOrders.Count(o => o.Symbol == ticker); if (ordersCount >= dealsMax) { var msgError = DealCounting == DealCountingType.ОбщийУчет ? $"Уже открыто {ordersCount} сделок из {dealsMax} макс." : $"По \"{ticker}\" уже открыто {ordersCount} сделок из {dealsMax} макс."; MessageBox.Show(msgError); return(msgError); } // подтверждение var confirmPrice = dealPrice; if (confirmPrice == 0) { confirmPrice = (dealSide == DealType.Buy ? currentQuote.ask : currentQuote.bid); } if (confirmTrade) { if (MessageBox.Show(string.Format("Будет совершена {0} {1} {2} по {3}. Продолжить?", dealSide == DealType.Buy ? "покупка" : "продажа", volumeBase.ToStringUniformMoneyFormat(), depoCurx, confirmPrice.ToStringUniformPriceFormat()), "Подтверждение", MessageBoxButtons.YesNo) == DialogResult.No) { return("отменено пользователем"); } } // таки войти в рынок if (OrderType == ScriptOrderType.ыночный) { MainForm.Instance.SendNewOrderRequestSafe( RequestUniqueId.Next(), AccountStatus.Instance.accountID, new MarketOrder { Volume = (int)volumeBase, Side = (int)dealSide, Symbol = ticker, StopLoss = (float?)sl, TakeProfit = (float?)tp, TrailLevel1 = trailLevel, TrailTarget1 = trailTarget }, priceEnter, 0, Contract.Entity.OrderType.Market); } else { // создать отложенник var orderType = PendingOrderType.Limit; if (((int)dealSide == 1 && currentQuote.ask < (float)priceEnter) || ((int)dealSide == -1 && currentQuote.bid > (float)priceEnter)) { orderType = PendingOrderType.Stop; } var pendingOrder = new PendingOrder { AccountID = AccountStatus.Instance.accountID, Symbol = ticker, Volume = (int)volumeBase, Side = (int)dealSide, PriceSide = orderType, PriceFrom = (float)priceEnter, StopLoss = (float?)sl, TakeProfit = (float?)tp, TrailLevel1 = trailLevel, TrailTarget1 = trailTarget }; MainForm.Instance.SendNewPendingOrderRequestSafe(RequestUniqueId.Next(), pendingOrder); } return("Сделка с фиксированным плечом (" + dealSide + " " + ticker + ") совершена"); }
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); }
public override string ActivateScript(string ticker) { if (!AccountStatus.Instance.isAuthorized) { MessageBox.Show("Не авторизован"); return("Не авторизован"); } var equity = AccountStatus.Instance.AccountData.Equity; if (equity <= 0) { MessageBox.Show("На счете отсутствуют денежные средства"); return("На счете отсутствуют денежные средства"); } var orders = MarketOrdersStorage.Instance.MarketOrders.Where(o => o.Symbol == ticker).ToList(); var side = orders.Count == 0 ? DealType.Buy : (DealType)orders[0].Side; float?sl = orders.Count == 0 ? null : orders[0].StopLoss; // открыть окно выбора направления входа и SL var dlg = new TradeFromSLDlg(orders.Count, side, sl); if (dlg.ShowDialog() != DialogResult.OK) { return("Отменено пользователем"); } side = dlg.Side; var targetSl = dlg.StopLoss; // выставить стоп остальным ордерам foreach (var order in orders) { var delta = Math.Abs((order.StopLoss ?? 0) - targetSl); var deltaPoints = DalSpot.Instance.GetPointsValue(ticker, delta); if (deltaPoints < 2) { continue; } // редактировать ордер order.StopLoss = targetSl; MainForm.Instance.SendEditMarketRequestSafe(order); } // получить текущую цену var quotes = QuoteStorage.Instance.ReceiveAllData(); QuoteData quote; quotes.TryGetValue(ticker, out quote); if (quote == null) { return("Нет котировки " + ticker); } // коэфф пересчета из контрвалюты в валюту депо var kCounterDepo = 1f; if (!ticker.EndsWith("USD")) { kCounterDepo = 1 / ((quote.ask + quote.bid) * 0.5f); } // посчитать макс. допустимый объем входа исходя из просадки var ordersResult = orders.Sum(o => o.Volume * o.Side * (targetSl - o.PriceEnter) * kCounterDepo); // убыток (прибыль) по сделкам var priceEnter = side == DealType.Buy ? quote.ask : quote.bid; var orderLossAbs = ((int)side) * (decimal)(targetSl - priceEnter); decimal volumeMax = decimal.MaxValue; // от текущего баланса считать сумму потерь var loss = equity * lossPercent / 100; if (orderLossAbs < 0) { volumeMax = (decimal)kCounterDepo * (loss + (decimal)ordersResult) / (-orderLossAbs); } // вход невозможен if (volumeMax < 0) { var err = string.Format( "Потери по уже открытым позициям для SL {0:f4} ({1:f0}) превысят допустимый процент " + "потерь ({2:f1}%, {3:f0} USD)", targetSl, ordersResult, lossPercent, loss); MessageBox.Show(err); return(err); } // множитель для объема var indexK = orders.Count; if (indexK >= volumePercent.Length) { indexK = volumePercent.Length - 1; } var koeffVolume = volumePercent[indexK]; if (volumeMax < decimal.MaxValue) { volumeMax *= (koeffVolume / 100M); } // сравнить объем с предельно допустимым var maxVolumeDepo = equity * LeverageMax; var maxVolumeBase = maxVolumeDepo / (decimal)((quote.bid + quote.ask) * 0.5f); var volumeEnter = Math.Min(volumeMax, maxVolumeBase); // округлить объем var volume = MarketOrder.RoundDealVolume((int)volumeEnter, VolumeRound, volumeMin, volumeStep); if (volume == 0) { var err = string.Format("Объем входа ({0:f0}) после округления равен 0", volumeEnter); MessageBox.Show(err); return(err); } // подтвердить вход) if (confirmTrade) { var orderLoss = volume * ((int)side) * (targetSl - priceEnter); var lossStr = ordersResult != 0 ? string.Format("{0}{1:f0} по уже открытым ордерам и {2:f0} по новому ордеру", ordersResult > 0 ? "прибыль" : "", Math.Abs(ordersResult), (-orderLoss)) : (-orderLoss).ToString("f0"); var prompt = string.Format("Будет открыта позиция: {0} {1} {2}, SL={3:f4}. Потери при SL: {4}. Продолжить?", side, volume, ticker, targetSl, lossStr); if (MessageBox.Show(prompt, "Подтвердить сделку", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) { return("Отменено пользователем"); } } // войти в рынок MainForm.Instance.SendNewOrderRequestSafe( RequestUniqueId.Next(), AccountStatus.Instance.accountID, new MarketOrder { Volume = volume, Side = (int)side, Symbol = ticker, StopLoss = targetSl }, (decimal)priceEnter, 0, OrderType.Market); return("Операция успешна"); }