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 "Операция успешна"; }
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("Операция успешна"); }