private void CheckMyTrades() { while (true) { Thread.Sleep(1000); if (_myTrades.IsEmpty) { Thread.Sleep(1000); continue; } MyTrade myTrade = null; _myTrades.TryDequeue(out myTrade); if (myTrade == null) { Thread.Sleep(1000); continue; } if (string.IsNullOrEmpty(myTrade.SecurityNameCode)) { SendLogMessage("MyTrade Error. No SecurityNameCode ", LogMessageType.Error); continue; } if (string.IsNullOrEmpty(myTrade.NumberOrderParent)) { SendLogMessage("MyTrade Error. No number Order Parent. " + myTrade.SecurityNameCode, LogMessageType.Error); continue; } if (string.IsNullOrEmpty(myTrade.NumberTrade)) { SendLogMessage("MyTrade Error. No NumberTrade " + myTrade.SecurityNameCode, LogMessageType.Error); continue; } if (myTrade.Price == 0) { SendLogMessage("MyTrade Error. No Price " + myTrade.SecurityNameCode, LogMessageType.Error); continue; } if (myTrade.Side == Side.None) { SendLogMessage("MyTrade Error. No Side " + myTrade.SecurityNameCode, LogMessageType.Error); continue; } if (myTrade.Time == DateTime.MinValue) { SendLogMessage("MyTrade Error. No Time " + myTrade.SecurityNameCode, LogMessageType.Error); continue; } if (myTrade.Volume == 0) { SendLogMessage("MyTrade Error. No Volume " + myTrade.SecurityNameCode, LogMessageType.Error); continue; } } }
/// <summary> /// входящие из системы мои сделки /// </summary> private void NewMyTrade(MyTrade trade) { _myTradesToSend.Enqueue(trade); _myTrades.Add(trade); }
private void OrderCreatorOnNewMyTrade(MyTrade myTrade) { OnMyTradeEvent(myTrade); }
/// <summary> /// загрузить трейд в хранилище /// </summary> public void SetNewTrade(MyTrade trade) { if (_deals == null) { return; } for (int i = _deals.Count - 1; i > _deals.Count - 150 && i > -1; i--) { bool isCloseOrder = false; if (_deals[i].CloseOrders != null) { for (int indexCloseOrd = 0; indexCloseOrd < _deals[i].CloseOrders.Count; indexCloseOrd++) { if (_deals[i].CloseOrders[indexCloseOrd].NumberMarket == trade.NumberOrderParent || _deals[i].CloseOrders[indexCloseOrd].NumberUser.ToString() == trade.NumberOrderParent) { isCloseOrder = true; break; } } } bool isOpenOrder = false; if (isCloseOrder == false || _deals[i].OpenOrders != null && _deals[i].OpenOrders.Count > 0) { for (int indOpenOrd = 0; indOpenOrd < _deals[i].OpenOrders.Count; indOpenOrd++) { if (_deals[i].OpenOrders[indOpenOrd].NumberMarket == trade.NumberOrderParent || _deals[i].OpenOrders[indOpenOrd].NumberUser.ToString() == trade.NumberOrderParent) { isOpenOrder = true; break; } } } if (isOpenOrder || isCloseOrder) { PositionStateType positionState = _deals[i].State; _deals[i].SetTrade(trade); if (positionState != _deals[i].State) { UpdeteOpenPositionArray(_deals[i]); _openLongChanged = true; _openShortChanged = true; _closePositionChanged = true; _closeShortChanged = true; _closeLongChanged = true; } if (positionState != _deals[i].State && PositionChangeEvent != null) { PositionChangeEvent(_deals[i]); } ProcesPosition(_deals[i]); } } _neadToSave = true; }
/// <summary> /// обработчик отчета о моих ордерах и сделках /// </summary> private void ExecutionReportHandler(FixEntity entity) { try { string type = entity.GetFieldByTag((int)Tags.ExecType); if (type != "F") { Order order = new Order(); order.ServerType = ServerType.Lmax; var time = entity.GetFieldByTag((int)Tags.TransactTime); order.TimeCallBack = DateTime.ParseExact(time, "yyyyMMdd-HH:mm:ss.fff", CultureInfo.CurrentCulture); order.SecurityNameCode = entity.GetFieldByTag((int)Tags.SecurityID); var numUser = entity.GetFieldByTag((int)Tags.ClOrdID); if (type == "0") { try { // если пришел сигнал о новом ордере, но ClOrdID не может быть конвертирован в int, значит ордер создавался не в OsEngine, игнорим его order.NumberUser = Convert.ToInt32(numUser); } catch (Exception e) { return; } order.State = OrderStateType.Activ; order.NumberMarket = entity.GetFieldByTag((int)Tags.OrderID); order.Side = entity.GetFieldByTag((int)Tags.Side) == "1" ? Side.Buy : Side.Sell; order.Volume = Convert.ToDecimal(entity.GetFieldByTag((int)Tags.OrderQty), CultureInfo.InvariantCulture); if (entity.GetFieldByTag((int)Tags.OrdType) == "2") { order.Price = Convert.ToDecimal(entity.GetFieldByTag((int)Tags.Price), CultureInfo.InvariantCulture); } MyOrderEvent?.Invoke(order); return; } if (type == "8") { try { order.NumberUser = Convert.ToInt32(numUser); } catch (Exception e) { return; } order.State = OrderStateType.Fail; string rej = entity.GetFieldByTag((int)Tags.OrdRejReason); SendLogMessage( "Ошибка выставления ордера: " + _errorDictionary.OrdRejReason[rej] + "-" + entity.GetFieldByTag((int)Tags.Text), LogMessageType.System); MyOrderEvent?.Invoke(order); return; } if (type == "4") { order.State = OrderStateType.Cancel; order.TimeCancel = order.TimeCallBack; var oldNumUser = entity.GetFieldByTag((int)Tags.OrigClOrdID); try { order.NumberUser = Convert.ToInt32(oldNumUser); } catch (Exception e) { return; } order.NumberMarket = entity.GetFieldByTag((int)Tags.OrderID); order.Side = entity.GetFieldByTag((int)Tags.Side) == "1" ? Side.Buy : Side.Sell; order.Volume = Convert.ToDecimal(entity.GetFieldByTag((int)Tags.OrderQty), CultureInfo.InvariantCulture); if (entity.GetFieldByTag((int)Tags.OrdType) == "2") { order.Price = Convert.ToDecimal(entity.GetFieldByTag((int)Tags.Price), CultureInfo.InvariantCulture); } MyOrderEvent?.Invoke(order); } else if (type == "I") { } } else { MyTrade trade = new MyTrade(); trade.Time = DateTime.ParseExact(entity.GetFieldByTag((int)Tags.TransactTime), "yyyyMMdd-HH:mm:ss.fff", CultureInfo.CurrentCulture); trade.NumberOrderParent = entity.GetFieldByTag((int)Tags.OrderID); trade.NumberTrade = entity.GetFieldByTag((int)Tags.ExecID); trade.Volume = Convert.ToDecimal(entity.GetFieldByTag((int)Tags.LastQty), CultureInfo.InvariantCulture); trade.Price = Convert.ToDecimal(entity.GetFieldByTag((int)Tags.LastPx), CultureInfo.InvariantCulture); trade.SecurityNameCode = entity.GetFieldByTag((int)Tags.SecurityID); MyTradeEvent?.Invoke(trade); } } catch (ArgumentException e) { SendLogMessage(e.Message, LogMessageType.Error); } catch (Exception e) { SendLogMessage("ExecutionReportHandlerError " + e.Message, LogMessageType.Error); } }
private void RaiseNewMyTrade(MyTrade trade) { NewMyTrade?.Invoke(trade); NewMyTrades?.Invoke(new[] { trade }); }
/// <summary> /// Получить позицию ликвидации. /// </summary> /// <param name="trade">Собственная сделка.</param> /// <returns>Позиция ликвидации. Если позиция отсутствует, то будет возвращено <see langword="null"/>.</returns> public static int?GetLiquidation(this MyTrade trade) { return(trade.GetValue <int?>(_liquidation)); }
public async Task <MyTrade[]> FetchMyTrades(DateTime since, IEnumerable <string> symbols = null, uint limit = 100) { //Number of records (Max: 10000) if (limit > 10000) { limit = 10000; } var response = await Request(new Base.Request() { ApiType = "private", BaseUri = ApiPrivateV2, Path = "v2/auth/r/trades/hist", Method = HttpMethod.Post, Params = new Dictionary <string, object>() { ["start"] = since.Subtract(new DateTime(1970, 1, 1)).TotalSeconds.ToString(CultureInfo.InvariantCulture), //["limit"] = limit //Error when both start and limit } }).ConfigureAwait(false); var markets = await FetchMarkets(); var ordersJson = JsonSerializer.Deserialize <JsonElement[][]>(response.Text); var result = new List <MyTrade>(); foreach (var item in ordersJson) { var market = markets.FirstOrDefault(m => "t" + m.Id == item[1].GetString()); if (market == null) { continue; } var myTrade = new MyTrade { Id = item[0].GetUInt64().ToString(CultureInfo.InvariantCulture), Timestamp = item[2].GetUInt64(), DateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(item[2].GetUInt64()), Symbol = market.Symbol, OrderId = item[3].GetUInt64().ToString(CultureInfo.InvariantCulture), Side = item[4].GetDecimal() > 0 ? Side.Buy : Side.Sell, Amount = Math.Abs(item[4].GetDecimal()), Price = item[5].GetDecimal(), FeeCost = Math.Abs(item[9].GetDecimal()), FeeCurrency = GetCommonCurrencyCode(item[10].GetString()), Info = item }; if (myTrade.FeeCurrency == market.Base) { myTrade.FeeRate = Math.Abs(Math.Round(myTrade.FeeCost / myTrade.Amount, 4)); } else { myTrade.FeeRate = Math.Abs(Math.Round(myTrade.FeeCost / (myTrade.Amount * myTrade.Price), 4)); } result.Add(myTrade); } return(result.ToArray()); }
private void NewMyTradeHandler(MyTrade trade) { AddGuiAction(() => NewMyTrade.SafeInvoke(trade)); }
/// <summary> /// Получить идентификатор клиента. /// </summary> /// <param name="trade">Собственная сделка.</param> /// <returns>Идентификатор клиента. Если идентификатор отсутствует, то будет возвращено <see langword="null"/>.</returns> public static int?GetClientId(this MyTrade trade) { return(trade.GetValue <int?>(_clientId)); }
public override void AgentAction(float[] vectorAction, string textAction) { if (brain.brainParameters.vectorActionSpaceType == SpaceType.continuous && chart.Ready) { float action_trade = Mathf.Clamp(vectorAction[0], -1f, 1f); float action_valueBitcoin = Mathf.Clamp(vectorAction[1], 0f, bitcoins); float action_valueCrypto = Mathf.Clamp(vectorAction[2], 0f, cryptos); // Buy Cryptos if (action_trade > 0) { if (action_valueBitcoin > 0) { if (nomralizedChartValue > 0.5) { float a, b; a = Mathf.InverseLerp(0.5f, 1f, nomralizedChartValue); b = Mathf.InverseLerp(0f, 1f, normalizedBitcoins); AddReward(0.1f * a * b); } chart.BuyCrypto(action_valueBitcoin, chart.ChartValue, this); MyTrade trade = new MyTrade(); trade.Amount = action_valueBitcoin / chart.ChartValue; trade.Cost = chart.ChartValue; MyTrades.Add(trade); GetAvarageTradesCost(); } } // Do nothing if (action_trade > 0) { if (nomralizedChartValue > 0.8f) { AddReward(0.15f * Mathf.InverseLerp(0.5f, 0f, normalizedBitcoins)); } if (nomralizedChartValue < 0.2f) { AddReward(0.15f * Mathf.InverseLerp(0.5f, 0f, normalizedCryptos)); } } // Buy Bitcoins if (action_trade < 0) { if (action_valueCrypto > 0 && avarageCost > 0) { if (nomralizedChartValue < 0.5) { float a, b; a = Mathf.InverseLerp(0.5f, 1f, nomralizedChartValue); b = Mathf.InverseLerp(0f, 1f, normalizedCryptos); AddReward(0.1f * a * b); } chart.SellCrypto(action_valueCrypto, chart.ChartValue, this); UpdateTrades(action_valueCrypto); } } } }
/// <summary> /// загрузить ордер /// </summary> private void LoadOrder() { // разбираем сообщение int msgVersion = TcpReadInt(); int id = TcpReadInt(); string status = TcpReadString(); int filled = TcpReadInt(); TcpReadInt(); TcpReadDouble(); if (msgVersion >= 2) { TcpReadInt(); } if (msgVersion >= 3) { TcpReadInt(); } double lasPrice = 0; if (msgVersion >= 4) { lasPrice = TcpReadDouble(); } if (msgVersion >= 5) { TcpReadInt(); } if (msgVersion >= 6) { TcpReadString(); } // parent.Wrapper.orderStatus(id, status, filled, remaining, avgFillPrice, permId, parentId, lastFillPrice, clientId, whyHeld); // комплектуем ордер if (_orders == null) { _orders = new List <Order>(); } Order osOrder = _orders.Find(order1 => Convert.ToInt32(order1.NumberMarket) == id); if (osOrder == null) { return; } Order newOsOrder = new Order(); newOsOrder.IsStopOrProfit = osOrder.IsStopOrProfit; newOsOrder.LifeTime = osOrder.LifeTime; newOsOrder.NumberMarket = osOrder.NumberMarket; newOsOrder.NumberUser = osOrder.NumberUser; newOsOrder.PortfolioNumber = osOrder.PortfolioNumber; newOsOrder.Price = osOrder.Price; newOsOrder.SecurityNameCode = osOrder.SecurityNameCode; newOsOrder.ServerType = osOrder.ServerType; newOsOrder.Side = osOrder.Side; newOsOrder.TimeCallBack = osOrder.TimeCallBack; newOsOrder.TimeCancel = osOrder.TimeCancel; newOsOrder.TimeCreate = osOrder.TimeCreate; newOsOrder.TypeOrder = osOrder.TypeOrder; newOsOrder.Volume = osOrder.Volume; newOsOrder.Comment = osOrder.Comment; if (newOsOrder.TimeCallBack == DateTime.MinValue) { newOsOrder.TimeCallBack = DateTime.Now; osOrder.TimeCallBack = DateTime.Now; } if (status == "Inactive") { newOsOrder.State = OrderStateType.Fail; } else if (status == "PreSubmitted" || status == "Submitted") { newOsOrder.State = OrderStateType.Activ; newOsOrder.TimeCallBack = DateTime.Now; osOrder.TimeCallBack = DateTime.Now; } else if (status == "Cancelled") { newOsOrder.State = OrderStateType.Fail; } else if (status == "Filled") { newOsOrder.State = OrderStateType.Done; newOsOrder.VolumeExecute = filled; } if (_myTradeCreate == null) { _myTradeCreate = new List <MyTradeCreate>(); } if (status == "Filled" && osOrder.VolumeExecute != newOsOrder.VolumeExecute) { // надо сгенерить мои трейды int volume = newOsOrder.VolumeExecute - osOrder.VolumeExecute; List <MyTradeCreate> myTradeCreates = _myTradeCreate.FindAll(create => create.idOrder == id); if (myTradeCreates.Count != 0) { volume = newOsOrder.VolumeExecute - myTradeCreates[myTradeCreates.Count - 1].FillOrderToCreateMyTrade; } if (volume == 0) { return; } MyTradeCreate newTradeCreate = new MyTradeCreate { idOrder = id, FillOrderToCreateMyTrade = newOsOrder.VolumeExecute }; _myTradeCreate.Add(newTradeCreate); MyTrade trade = new MyTrade(); trade.NumberOrderParent = osOrder.NumberMarket; trade.Price = Convert.ToDecimal(lasPrice); trade.Time = DateTime.Now; trade.NumberTrade = trade.Time.ToBinary().ToString(); trade.SecurityNameCode = osOrder.SecurityNameCode; trade.Volume = volume; trade.Side = osOrder.Side; if (NewMyTradeEvent != null) { NewMyTradeEvent(trade); } } if (NewOrderEvent != null) { NewOrderEvent(newOsOrder); } }
// returns a string of mytrade private static string MyTradeToString(MyTrade trade) { //securityId;tradeId;time;volume;price;orderdirection;orderId return string.Format("{0};{1};{2};{3};{4};{5};{6}", trade.Trade.Security.Id, trade.Trade.Id, trade.Trade.Time, trade.Trade.Volume, trade.Trade.Price, trade.Trade.OrderDirection, trade.Order.Id); }
/// <summary> /// order and trade came /// пришел ордер и трейд /// </summary> /// <param name="bitMaxOrder"></param> private void ClientMyOrderEvent(BitMaxOrder bitMaxOrder) { OrderCoupler needCoupler; if (bitMaxOrder.status == "Canceled") { needCoupler = _couplers.Find(c => c.OrderCancelId == bitMaxOrder.coid); } else { needCoupler = _couplers.Find(c => c.OrderNumberMarket == bitMaxOrder.coid); } if (needCoupler == null) { return; } if (bitMaxOrder.status == "PartiallyFilled" || bitMaxOrder.status == "Filled") { var partialVolume = bitMaxOrder.f.ToDecimal(); var tradeVolume = partialVolume - needCoupler.CurrentVolume; needCoupler.CurrentVolume += tradeVolume; MyTrade myTrade = new MyTrade() { NumberOrderParent = bitMaxOrder.coid, Side = bitMaxOrder.side == "Sell" ? Side.Sell : Side.Buy, NumberPosition = bitMaxOrder.coid, SecurityNameCode = bitMaxOrder.s.Replace('/', '-'), Price = bitMaxOrder.p.ToDecimal() , Volume = tradeVolume, NumberTrade = Guid.NewGuid().ToString(), Time = TimeManager.GetDateTimeFromTimeStamp(Convert.ToInt64(bitMaxOrder.t)), }; MyTradeEvent?.Invoke(myTrade); } Order order = new Order(); order.NumberUser = needCoupler.OsOrderNumberUser; order.NumberMarket = bitMaxOrder.coid; order.PortfolioNumber = bitMaxOrder.s.Split('/')[1]; order.Price = bitMaxOrder.p.ToDecimal(); order.Volume = bitMaxOrder.q.ToDecimal(); order.Side = bitMaxOrder.side == "Sell" ? Side.Sell : Side.Buy; order.SecurityNameCode = bitMaxOrder.s.Replace('/', '-'); order.ServerType = ServerType; order.TimeCallBack = TimeManager.GetDateTimeFromTimeStamp(Convert.ToInt64(bitMaxOrder.t)); order.TypeOrder = OrderPriceType.Limit; if (bitMaxOrder.status == "New") { order.State = OrderStateType.Activ; } else if (bitMaxOrder.status == "PartiallyFilled") { order.State = OrderStateType.Patrial; } else if (bitMaxOrder.status == "Filled") { order.State = OrderStateType.Done; _couplers.Remove(needCoupler); } else if (bitMaxOrder.status == "Canceled") { order.State = OrderStateType.Cancel; _couplers.Remove(needCoupler); } else if (bitMaxOrder.status == "Rejected") { order.State = OrderStateType.Fail; } MyOrderEvent?.Invoke(order); }
/// <summary> /// Получить количество прибыльных контрактов в сделке. /// </summary> /// <param name="trade">>Собственная сделка.</param> /// <returns>Количество прибыльных контрактов в сделке. Если количество отсутствует, то будет возвращено <see langword="null"/>.</returns> public static int?GetCumulativeQuantity(this MyTrade trade) { return(trade.GetValue <int?>(_cumulativeQuantity)); }
/// <summary> /// order and trade came /// пришел ордер и трейд /// </summary> /// <param name="bitMaxOrder"></param> private void ClientMyOrderEvent(OrderState bitMaxOrder) { var data = bitMaxOrder.Data; OrderCoupler needCoupler = _couplers.Find(c => c.OrderNumberMarket == data.OrderId); if (needCoupler == null) { return; } Order order = new Order(); order.NumberUser = needCoupler.OsOrderNumberUser; order.NumberMarket = data.OrderId; order.PortfolioNumber = data.S.Split('/')[1]; order.Price = data.P.ToDecimal(); order.Volume = data.Q.ToDecimal(); order.Side = data.Sd == "Buy" ? Side.Buy : Side.Sell; order.SecurityNameCode = data.S; order.ServerType = ServerType; order.TimeCallBack = TimeManager.GetDateTimeFromTimeStamp(Convert.ToInt64(data.T)); order.TypeOrder = OrderPriceType.Limit; if (data.St == "New") { order.State = OrderStateType.Activ; } else if (data.St == "Canceled") { order.State = OrderStateType.Cancel; _couplers.Remove(needCoupler); } else if (data.St == "PartiallyFilled") { order.State = OrderStateType.Patrial; } else if (data.St == "Filled") { order.State = OrderStateType.Done; _couplers.Remove(needCoupler); } else if (data.St == "Rejected") { order.State = OrderStateType.Fail; } if (bitMaxOrder.Data.St == "PartiallyFilled" || bitMaxOrder.Data.St == "Filled") { var cumVolume = data.Cfq.ToDecimal(); var tradeVolume = cumVolume - needCoupler.CurrentVolume; needCoupler.CurrentVolume += tradeVolume; MyTrade myTrade = new MyTrade { NumberOrderParent = data.OrderId, Side = data.Sd == "Buy" ? Side.Buy : Side.Sell, SecurityNameCode = data.S, Price = data.Ap.ToDecimal(), Volume = tradeVolume, NumberTrade = data.Sn.ToString(), Time = TimeManager.GetDateTimeFromTimeStamp(Convert.ToInt64(data.T)), }; MyTradeEvent?.Invoke(myTrade); } MyOrderEvent?.Invoke(order); }
/// <summary> /// Получить среднюю цену исполнения сделки. /// </summary> /// <param name="trade">Собственная сделка.</param> /// <returns>Средняя цена исполнения сделки. Если цена отсутствует, то будет возвращено <see langword="null"/>.</returns> public static decimal?GetAveragePrice(this MyTrade trade) { return(trade.GetValue <decimal?>(_averagePrice)); }
/// <summary> /// takes messages from the common queue, converts them to C # classes and sends them to up /// берет сообщения из общей очереди, конвертирует их в классы C# и отправляет на верх /// </summary> public void Converter() { while (true) { try { if (_isDisposed) { return; } if (!_newMessage.IsEmpty) { string mes; if (_newMessage.TryDequeue(out mes)) { if (mes.Contains("\"event\":\"error\"")) { lock (_senderLocker) { SendLogMessage(mes, LogMessageType.Error); } } else if (mes.StartsWith("{\"event\":\"info\"")) { lock (_senderLocker) { if (mes.Contains("\"code\":20051") || mes.Contains("\"code\":20060")) { SendLogMessage("остановка/перезапуск сервера Websocket", LogMessageType.Error); if (Disconnected != null) { Disconnected(); } } } } else if (mes.Contains("\"event\":\"auth\"")) { } else if (mes.Contains("\"os\"")) // снимок моих ордеров { } else if (mes.Contains("\"on\"")) { } else if (mes.Contains("\"ou\"") || mes.Contains("\"oc\"")) // обновление или закрытие моего ордера { lock (_senderLocker) { Thread.Sleep(300); var values = ParseData(mes); int numUser = _osOrders[values[0]]; var order = new Order(); order.NumberUser = numUser; order.SecurityNameCode = values[1]; order.PortfolioNumber = values[1].Substring(3); order.Side = values[2].ToDecimal() > 0 ? Side.Buy : Side.Sell; order.NumberMarket = values[0]; order.Price = values[6].ToDecimal(); order.Volume = Math.Abs(values[2].ToDecimal()); order.TimeCallBack = DateTime.Parse(values[8].TrimEnd('Z')); if (values[5].Contains("EXECUTED")) { order.State = OrderStateType.Done; } else { switch (values[5]) { case "ACTIVE": order.State = OrderStateType.Activ; break; case "PARTIALLY FILLED": order.State = OrderStateType.Patrial; break; case "CANCELED": order.TimeCancel = order.TimeCallBack; order.State = OrderStateType.Cancel; _osOrders.Remove(order.NumberMarket); break; default: order.State = OrderStateType.None; break; } } if (MyOrderEvent != null) { MyOrderEvent(order); } } } else if (mes.Contains("[0,\"tu\",[")) // my new trade / новая моя сделка { lock (_senderLocker) { // [0,"tu",["5809001-IOTBTC",275774974,"IOTBTC",1533415991,15073784940,13,0.00012184,"EXCHANGE LIMIT",0.00012184,-0.026,"IOT"]] моя сделка var valuesMyTrade = ParseData(mes); Thread.Sleep(300); MyTrade myTrade = new MyTrade(); myTrade.Price = valuesMyTrade[6].ToDecimal(); myTrade.NumberTrade = valuesMyTrade[1]; myTrade.SecurityNameCode = valuesMyTrade[2]; myTrade.Side = valuesMyTrade[5].Contains("-") ? Side.Sell : Side.Buy; myTrade.Volume = Math.Abs(valuesMyTrade[5].ToDecimal()); myTrade.Time = new DateTime(1970, 01, 01) + TimeSpan.FromSeconds(Convert.ToDouble(valuesMyTrade[3])); myTrade.NumberOrderParent = valuesMyTrade[4]; if (MyTradeEvent != null) { MyTradeEvent(myTrade); } } } else if (mes.Contains("\"ws\"")) // snapshot of portfolios / снимок портфелей { lock (_senderLocker) { var res = Walets.FromJson(mes)[2].AllWallets; if (NewPortfolio != null) { NewPortfolio(res); } } } else if (mes.Contains("\"wu\"")) // portfolio update / обновление портфеля { lock (_senderLocker) { var res = WalletUpdate.FromJson(mes)[2].UpdatedWallet;; if (UpdatePortfolio != null) { UpdatePortfolio(res); } } } else if (mes.Contains("[0,\"ps\",[")) // shapshot of position / снимок позиций { } else if (mes.Contains("\"pn\"") || mes.Contains("\"pu\"") || mes.Contains("\"pc\"")) // снимок позиций { } else if (mes.Contains("\"event\":\"subscribed\"") && mes.Contains("\"chanId\"")) { lock (_senderLocker) { // inform about successful subscription / информируем об успешной подписке var info = JsonConvert.DeserializeAnonymousType(mes, new SubscriptionInformation()); if (info.channel == "trades") { _subscribedTradesSecurity.Add(info.pair, info.chanId); } if (info.channel == "book") { _subscribedBooksSecurity.Add(info.pair, info.chanId); } string msgInfo = string.Format( "Инструмент {0} успешно подписан на канал : {1}, Id канала = {2}", info.pair, info.channel, info.chanId); SendLogMessage(msgInfo, LogMessageType.System); } } else if (mes.Contains("\"tu\"")) // new tick / новый тик { lock (_senderLocker) { var bitfinexTick = UpdateDataBitfinex.FromJson(mes); // find the security that owns this snapshot / находим бумагу которой принадлежит этот снимок var namePair = _subscribedTradesSecurity.FirstOrDefault( dic => dic.Value == Convert.ToInt32(bitfinexTick[0].Double)); if (NewTradesEvent != null) { NewTradesEvent(bitfinexTick, namePair.Key); } } } else if (mes.Contains("[[")) { lock (_senderLocker) { var countParams = NumberParametersSnapshot(mes); if (countParams == 3) { // process a shapshot of depth / обрабатываем снимок стакана var orderBook = BitfinexSnapshotParser.FromJson(mes); // find the security that owns this snapshot / находим бумагу которой принадлежит этот снимок var namePair = _subscribedBooksSecurity.FirstOrDefault( dic => dic.Value == Convert.ToInt32(orderBook[0].IdChanel)); if (NewMarketDepth != null) { NewMarketDepth(orderBook, namePair.Key); } } } } else if (mes.Contains("hb")) { // heartbeat message came / пришло сообщение серцебиения } else if (!mes.Contains("[[") && !mes.Contains("\"te\"") && !mes.Contains("\"ws\"")) { lock (_senderLocker) { var bitfinexChangeOrderBook = UpdateDataBitfinex.FromJson(mes); // find the security that owns this snapshot / находим бумагу которой принадлежит этот снимок var namePair = _subscribedBooksSecurity.FirstOrDefault( dic => dic.Value == Convert.ToInt32(bitfinexChangeOrderBook[0].Double)); if (UpdateMarketDepth != null) { UpdateMarketDepth(bitfinexChangeOrderBook, namePair.Key); } } } } } else { Thread.Sleep(1); } } catch (Exception exception) { SendLogMessage(exception.Message, LogMessageType.Connect); } } }
/// <summary> /// Получить описание заявки. /// </summary> /// <param name="trade">Собственная сделка.</param> /// <returns>Описание заявки. Если описание отсутствует, то будет возвращено <see langword="null"/>.</returns> public static string GetOrderRef(this MyTrade trade) { return(trade.GetValue <string>(_orderRef)); }
protected void OnMyTradeEvent(MyTrade myTrade) { MyTradeEvent?.Invoke(myTrade); }
/// <summary> /// Получить Economic Value правило. /// </summary> /// <param name="trade">Собственная сделка.</param> /// <returns>Economic Value правило. Если правило отсутствует, то будет возвращено <see langword="null"/>.</returns> public static string GetEvRule(this MyTrade trade) { return(trade.GetValue <string>(_evRule)); }
private void _client_MyOrderEvent(Result result) { OrderCoupler needCoupler; needCoupler = _couplers.Find(c => c.OrderNumberMarket == result.clientOrderId); if (needCoupler == null) { return; } if (result.status == "partiallyFilled" || result.status == "filled") { var partialVolume = result.quantity.ToDecimal(); var tradeVolume = partialVolume - needCoupler.CurrentVolume; needCoupler.CurrentVolume += tradeVolume; MyTrade myTrade = new MyTrade() { NumberOrderParent = result.clientOrderId, Side = result.side == "sell" ? Side.Sell : Side.Buy, NumberPosition = Convert.ToString(needCoupler.OsOrderNumberUser), SecurityNameCode = result.symbol, Price = result.price.ToDecimal() , Volume = tradeVolume, NumberTrade = result.id, Time = result.updatedAt, }; MyTradeEvent?.Invoke(myTrade); } Order order = new Order(); order.NumberUser = needCoupler.OsOrderNumberUser; order.NumberMarket = result.clientOrderId; order.PortfolioNumber = result.symbol.Substring(result.symbol.Length - 3); order.Price = result.price.ToDecimal(); order.Volume = result.quantity.ToDecimal(); order.Side = result.side == "sell" ? Side.Sell : Side.Buy; order.SecurityNameCode = result.symbol; order.ServerType = ServerType; order.TimeCallBack = Convert.ToDateTime(result.createdAt); order.TypeOrder = result.type == "limit" ? OrderPriceType.Limit : OrderPriceType.Market; if (result.status == "new") { order.State = OrderStateType.Activ; } else if (result.status == "partiallyFilled") { order.State = OrderStateType.Patrial; } else if (result.status == "filled") { order.State = OrderStateType.Done; _couplers.Remove(needCoupler); } else if (result.status == "canceled") { order.State = OrderStateType.Cancel; _couplers.Remove(needCoupler); } else if (result.status == "expired") { order.State = OrderStateType.Fail; } MyOrderEvent?.Invoke(order); _incominOrders.Add(order); _client.GetBalance(); }
/// <summary> /// Получить множитель рыночной цены контракта при изменении цены на 1. /// </summary> /// <param name="trade">Собственная сделка.</param> /// <returns>Множитель рыночной цены. Если множитель отсутствует, то будет возвращено <see langword="null"/>.</returns> public static decimal?GetEvMultiplier(this MyTrade trade) { return(trade.GetValue <decimal?>(_evMultiplier)); }
/// <summary> /// берет сообщения из общей очереди, конвертирует их в классы C# и отправляет на верх /// </summary> public void ConverterUserData() { while (true) { try { if (_isDisposed) { return; } if (!_newUserDataMessage.IsEmpty) { string mes; if (_newUserDataMessage.TryDequeue(out mes)) { if (mes.Contains("code")) { SendLogMessage(JsonConvert.DeserializeAnonymousType(mes, new ErrorMessage()).msg, LogMessageType.Error); } else if (mes.Contains("\"e\"" + ":" + "\"executionReport\"")) { var order = JsonConvert.DeserializeAnonymousType(mes, new ExecutionReport()); if (order.x == "NEW") { Order newOrder = new Order(); newOrder.SecurityNameCode = order.s; newOrder.TimeCallBack = new DateTime(1970, 1, 1).AddMilliseconds(Convert.ToDouble(order.E)); newOrder.NumberUser = Convert.ToInt32(order.c); newOrder.NumberMarket = order.i.ToString(); //newOrder.PortfolioNumber = order.PortfolioNumber; добавить в сервере newOrder.Side = order.S == "BUY" ? Side.Buy : Side.Sell; newOrder.State = OrderStateType.Activ; newOrder.Volume = Convert.ToDecimal(order.q.Replace(".", CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator), CultureInfo.InvariantCulture); newOrder.Price = Convert.ToDecimal(order.p.Replace(".", CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator), CultureInfo.InvariantCulture); if (MyOrderEvent != null) { MyOrderEvent(newOrder); } } else if (order.x == "CANCELED") { Order newOrder = new Order(); newOrder.SecurityNameCode = order.s; newOrder.TimeCallBack = new DateTime(1970, 1, 1).AddMilliseconds(Convert.ToDouble(order.E)); newOrder.NumberUser = Convert.ToInt32(order.C); newOrder.NumberMarket = order.i.ToString(); newOrder.Side = order.S == "BUY" ? Side.Buy : Side.Sell; newOrder.State = OrderStateType.Cancel; newOrder.Volume = Convert.ToDecimal(order.q.Replace(".", CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator), CultureInfo.InvariantCulture); newOrder.Price = Convert.ToDecimal(order.p.Replace(".", CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator), CultureInfo.InvariantCulture); if (MyOrderEvent != null) { MyOrderEvent(newOrder); } } else if (order.x == "REJECTED") { Order newOrder = new Order(); newOrder.SecurityNameCode = order.s; newOrder.TimeCallBack = new DateTime(1970, 1, 1).AddMilliseconds(Convert.ToDouble(order.E)); newOrder.NumberUser = Convert.ToInt32(order.c); newOrder.NumberMarket = order.i.ToString(); newOrder.Side = order.S == "BUY" ? Side.Buy : Side.Sell; newOrder.State = OrderStateType.Fail; newOrder.Volume = Convert.ToDecimal(order.q.Replace(".", CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator), CultureInfo.InvariantCulture); newOrder.Price = Convert.ToDecimal(order.p.Replace(".", CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator), CultureInfo.InvariantCulture); if (MyOrderEvent != null) { MyOrderEvent(newOrder); } } else if (order.x == "TRADE") { MyTrade trade = new MyTrade(); trade.Time = new DateTime(1970, 1, 1).AddMilliseconds(Convert.ToDouble(order.T)); trade.NumberOrderParent = order.i.ToString(); trade.NumberTrade = order.t.ToString(); trade.Volume = Convert.ToDecimal(order.l.Replace(".", CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator), CultureInfo.InvariantCulture); trade.Price = Convert.ToDecimal(order.L.Replace(".", CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator), CultureInfo.InvariantCulture); trade.SecurityNameCode = order.s; if (MyTradeEvent != null) { MyTradeEvent(trade); } } else if (order.x == "EXPIRED") { Order newOrder = new Order(); newOrder.SecurityNameCode = order.s; newOrder.TimeCallBack = new DateTime(1970, 1, 1).AddMilliseconds(Convert.ToDouble(order.E)); newOrder.NumberUser = Convert.ToInt32(order.c); newOrder.NumberMarket = order.i.ToString(); newOrder.Side = order.S == "BUY" ? Side.Buy : Side.Sell; newOrder.State = OrderStateType.Cancel; newOrder.Volume = Convert.ToDecimal(order.q.Replace(".", CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator), CultureInfo.InvariantCulture); newOrder.Price = Convert.ToDecimal(order.p.Replace(".", CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator), CultureInfo.InvariantCulture); if (MyOrderEvent != null) { MyOrderEvent(newOrder); } } continue; } else if (mes.Contains("\"e\"" + ":" + "\"outboundAccountInfo\"")) { var portfolios = JsonConvert.DeserializeAnonymousType(mes, new OutboundAccountInfo()); if (UpdatePortfolio != null) { UpdatePortfolio(portfolios); } continue; } } } } catch (Exception exception) { SendLogMessage(exception.Message, LogMessageType.Connect); } Thread.Sleep(1); } }
public void Selected(string item) { switch (item) { case "Home": if (Device.RuntimePlatform == Device.iOS) { if (home == null) { home = new NavigationPage(new Home()); } } else { if (home == null) { home = new Home(); } } rootPage.Detail = home; rootPage.Title = "Home"; break; case "Trade": if (Device.RuntimePlatform == Device.iOS) { Trade = new NavigationPage(new TradePage()); } else { Trade = new TradePage(); } rootPage.Detail = Trade; rootPage.Title = "Trade"; break; case "MyTrade": if (Device.RuntimePlatform == Device.iOS) { myTrade = new NavigationPage(new MyTrade()); } else { myTrade = new MyTrade(); } rootPage.Detail = myTrade; rootPage.Title = "My Trade"; break; case "Notification": if (Device.RuntimePlatform == Device.iOS) { Notification = new NavigationPage(new TabView()); } else { Notification = new TabView(); } rootPage.Detail = Notification; rootPage.Title = "Notificatin"; break; case "About": About = new AboutPage() { // BarBackgroundColor = App.BrandColor, ///// BarTextColor = Color.White }; rootPage.Detail = About; break; } ; rootPage.IsPresented = false; // close the slide-out }
void server_NewMyTradeEvent(MyTrade myTrade) { _myTrades.Enqueue(myTrade); }
/// <summary> /// проверить ордера на состояние /// </summary> public bool GetAllOrders(List <Order> oldOpenOrders) { List <string> namesSec = new List <string>(); for (int i = 0; i < oldOpenOrders.Count; i++) { if (namesSec.Find(name => name.Contains(oldOpenOrders[i].SecurityNameCode)) == null) { namesSec.Add(oldOpenOrders[i].SecurityNameCode); } } string endPoint = "/api/v3/allOrders"; List <HistoryOrderReport> allOrders = new List <HistoryOrderReport>(); for (int i = 0; i < namesSec.Count; i++) { var param = new Dictionary <string, string>(); param.Add("symbol=", namesSec[i].ToUpper()); //param.Add("&recvWindow=" , "100"); //param.Add("&limit=", GetNonce()); param.Add("&limit=", "500"); //"symbol={symbol.ToUpper()}&recvWindow={recvWindow}" var res = CreateQuery(Method.GET, endPoint, param, true); HistoryOrderReport[] orders = JsonConvert.DeserializeObject <HistoryOrderReport[]>(res); if (orders != null && orders.Length != 0) { allOrders.AddRange(orders); } } for (int i = 0; i < oldOpenOrders.Count; i++) { HistoryOrderReport myOrder = allOrders.Find(ord => ord.orderId == oldOpenOrders[i].NumberMarket); if (myOrder == null) { continue; } if (myOrder.status == "NEW") { // ордер активен. Ничего не делаем continue; } else if (myOrder.status == "FILLED" || myOrder.status == "PARTIALLY_FILLED") { // ордер исполнен MyTrade trade = new MyTrade(); trade.NumberOrderParent = oldOpenOrders[i].NumberMarket; trade.NumberTrade = NumberGen.GetNumberOrder(StartProgram.IsOsTrader).ToString(); trade.SecurityNameCode = oldOpenOrders[i].SecurityNameCode; trade.Time = new DateTime(1970, 1, 1).AddMilliseconds(Convert.ToDouble(myOrder.updateTime)); trade.Side = oldOpenOrders[i].Side; if (MyTradeEvent != null) { MyTradeEvent(trade); } } else { Order newOrder = new Order(); newOrder.NumberMarket = oldOpenOrders[i].NumberMarket; newOrder.NumberUser = oldOpenOrders[i].NumberUser; newOrder.SecurityNameCode = oldOpenOrders[i].SecurityNameCode; newOrder.State = OrderStateType.Cancel; newOrder.Volume = oldOpenOrders[i].Volume; newOrder.VolumeExecute = oldOpenOrders[i].VolumeExecute; newOrder.Price = oldOpenOrders[i].Price; newOrder.TypeOrder = oldOpenOrders[i].TypeOrder; newOrder.TimeCallBack = new DateTime(1970, 1, 1).AddMilliseconds(Convert.ToDouble(myOrder.updateTime)); newOrder.ServerType = ServerType.Binance; newOrder.PortfolioNumber = oldOpenOrders[i].PortfolioNumber; if (MyOrderEvent != null) { MyOrderEvent(newOrder); } } } return(true); }
void _transactionsSession_DataReceived(OkonkwoOandaV20.TradeLibrary.DataTypes.Stream.TransactionStreamResponse data) { if (data.transaction.type == "LIMIT_ORDER") { LimitOrderTransaction order = (LimitOrderTransaction)data.transaction; Order newOrder = new Order(); newOrder.NumberUser = Convert.ToInt32(order.clientExtensions.id); newOrder.NumberMarket = order.id.ToString(); newOrder.SecurityNameCode = order.instrument; newOrder.Price = Convert.ToDecimal(order.price); newOrder.TimeCallBack = DateTime.Parse(order.time); newOrder.State = OrderStateType.Activ; if (NewOrderEvent != null) { NewOrderEvent(newOrder); } } else if (data.transaction.type == "LIMIT_ORDER_REJECT") { LimitOrderRejectTransaction order = (LimitOrderRejectTransaction)data.transaction; Order newOrder = new Order(); newOrder.NumberUser = Convert.ToInt32(order.clientExtensions.id); newOrder.NumberMarket = order.id.ToString(); newOrder.SecurityNameCode = order.instrument; newOrder.Price = Convert.ToDecimal(order.price); newOrder.State = OrderStateType.Fail; newOrder.TimeCallBack = DateTime.Parse(order.time); if (NewOrderEvent != null) { NewOrderEvent(newOrder); } } else if (data.transaction.type == "ORDER_FILL") { OrderFillTransaction order = (OrderFillTransaction)data.transaction; MyTrade trade = new MyTrade(); trade.NumberOrderParent = order.clientOrderID; trade.NumberTrade = order.id.ToString(); trade.Price = Convert.ToDecimal(order.price); trade.Volume = Convert.ToDecimal(order.units); trade.Time = DateTime.Parse(order.time); trade.SecurityNameCode = order.instrument; Order myOrder = _orders.Find(o => o.NumberUser.ToString() == order.clientOrderID); if (myOrder == null) { return; } trade.Volume = myOrder.Volume; if (order.units == 1) { trade.Side = Side.Buy; } else { trade.Side = Side.Sell; } if (NewMyTradeEvent != null) { NewMyTradeEvent(trade); } } else if (data.transaction.type == "SystemOrderReject") { Order newOrder = new Order(); // newOrder.NumberUser = order.NumberUser; newOrder.State = OrderStateType.Fail; //trade.Time = DateTime.Parse(order.time); if (NewOrderEvent != null) { NewOrderEvent(newOrder); } } else if (data.transaction.type == "ORDER_CANCEL") { OrderCancelTransaction order = (OrderCancelTransaction)data.transaction; Order newOrder = new Order(); newOrder.NumberUser = Convert.ToInt32(order.clientOrderID); newOrder.State = OrderStateType.Cancel; newOrder.TimeCallBack = DateTime.Parse(order.time); newOrder.TimeCancel = newOrder.TimeCallBack; if (NewOrderEvent != null) { NewOrderEvent(newOrder); } } }
public void UpdateMyTrades() { try { //curl - X GET "https://api-invest.tinkoff.ru/openapi/operations?from=2019-11-01T01%3A00%3A00.0%2B00%3A00&to=2019-11-01T23%3A50%3A00.0%2B00%3A00" //- H "accept: application/json" //- H "Authorization: Bearer t.ZmEaoirPe5unR6Cw0o7YSq-Hl4lCkGES-0XgZmOg9XGl_Ds6OeAzdS9P-x1lRmQjzu7Ol6cMTgN-QUv9ISvyGQ" DateTime now = DateTime.Now; DateTime from = new DateTime(now.Year, now.Month, now.Day, 1, 0, 0); DateTime to = new DateTime(now.Year, now.Month, now.Day, 23, 50, 0); string url = _url + "operations?"; url += "from=" + ToIso8601(from) + "&"; url += "to=" + ToIso8601(to) + "&"; var jsonCurrency = ApiQuery(url, "GET", new Dictionary <string, string>()); if (jsonCurrency == null) { return; } var jOrders = JToken.Parse(jsonCurrency).SelectToken("payload").SelectToken("operations");//.Children<JProperty>(); foreach (var order in jOrders) { Order newOrder = new Order(); newOrder.NumberMarket = order.SelectToken("id").ToString(); string state = order.SelectToken("status").ToString(); if (state == "Decline") { newOrder.State = OrderStateType.Cancel; if (_canselOrders.Find(ord => ord.NumberMarket == newOrder.NumberMarket) == null && MyOrderEvent != null) { string figa = order.SelectToken("figi").ToString(); Security security = _allSecurities.Find(sec => sec.NameId == figa); if (security != null) { newOrder.SecurityNameCode = security.Name; } _canselOrders.Add(newOrder); MyOrderEvent(newOrder); } } var jTrades = order.SelectToken("trades"); if (jTrades != null) { foreach (var trade in jTrades) { string num = trade.SelectToken("tradeId").ToString(); if (_myTrades.Find(t => t.NumberTrade == num) == null && MyTradeEvent != null) { // "operationType": "Sell", MyTrade myTrade = new MyTrade(); myTrade.NumberTrade = num; myTrade.NumberOrderParent = newOrder.NumberMarket; myTrade.Price = trade.SelectToken("price").ToString().ToDecimal(); myTrade.Volume = trade.SelectToken("quantity").ToString().ToDecimal(); myTrade.Time = Convert.ToDateTime(trade.SelectToken("date").ToString()); Enum.TryParse(order.SelectToken("operationType").ToString(), out myTrade.Side); string figa = order.SelectToken("figi").ToString(); Security security = _allSecurities.Find(sec => sec.NameId == figa); if (security != null) { myTrade.SecurityNameCode = security.Name; } MyTradeEvent(myTrade); _myTrades.Add(myTrade); } } } } } catch (Exception e) { SendLogMessage(e.ToString(), LogMessageType.Error); } }
DateTimeOffset IMarketDataStorageInfo <MyTrade> .GetTime(MyTrade data) { return(data.Trade.Time); }
public async Task <MyTrade[]> FetchMyTrades(DateTime since, IEnumerable <string> symbols = null, uint limit = 1000) { var markets = await FetchMarkets(); if (symbols == null) { symbols = markets.Select(m => m.Symbol); } var symbolsHashSet = symbols.ToHashSet(); var paramPairs = markets.Where(m => symbolsHashSet.Contains(m.Symbol)).Select(m => m.Id); var response = await Request(new Base.Request() { ApiType = "private", BaseUri = ApiPrivateV1_1, Path = "user_trades", Method = HttpMethod.Post, Params = new Dictionary <string, object>() { ["pair"] = String.Join(",", paramPairs), ["offset"] = "0", ["limit"] = limit.ToString(CultureInfo.InvariantCulture), } }).ConfigureAwait(false); var jsonResponse = JsonSerializer.Deserialize <Dictionary <string, ExmoMyTrade[]> >(response.Text); var result = new List <MyTrade>(); foreach (var kvp in jsonResponse.Where(m => m.Value.Length != 0)) { var market = markets.FirstOrDefault(m => m.Id == kvp.Key); if (market == null) { continue; } foreach (var item in kvp.Value) { var myTrade = new MyTrade { Id = item.trade_id.ToString(CultureInfo.InvariantCulture), Timestamp = item.date, DateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(item.date), Symbol = market.Symbol, OrderId = item.order_id.ToString(CultureInfo.InvariantCulture), TakerOrMaker = item.exec_type == "maker" ? TakerOrMaker.Maker : TakerOrMaker.Taker, Side = item.type == "buy" ? Side.Buy : Side.Sell, Price = JsonSerializer.Deserialize <decimal>(item.price), Amount = JsonSerializer.Deserialize <decimal>(item.quantity), Info = item }; if (!String.IsNullOrEmpty(item.commission_amount)) { myTrade.FeeCost = JsonSerializer.Deserialize <decimal>(item.commission_amount); } if (!String.IsNullOrEmpty(item.commission_currency)) { myTrade.FeeCurrency = GetCommonCurrencyCode(item.commission_currency); } if (String.IsNullOrEmpty(myTrade.FeeCurrency)) { myTrade.FeeCurrency = myTrade.Side == Side.Buy ? market.Quote : market.Base; } if (!String.IsNullOrEmpty(item.commission_percent)) { myTrade.FeeRate = JsonSerializer.Deserialize <decimal>(item.commission_percent) / 100; } result.Add(myTrade); } } return(result.ToArray()); }