csharp/AlexWan/OsEngine/project/OsEngine/Market/Servers/BitStamp/BitStampClient.cs

BitStampClient.cs
/*
 *Your rights to use the code are governed by this license https://github.com/AlexWan/OsEngine/blob/master/LICENSE
 *Ваши права на использование кода регулируются данной лицензией http://o-s-a.net/doc/license_simple_engine.pdf
*/

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Net;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using Newtonsoft.Json;
using OsEngine.Ensaty;
using OsEngine.Logging;
using OsEngine.Market.Servers.BitStamp.BitStampEnsaty;
using RestSharp;
using Trade = OsEngine.Ensaty.Trade;
using Newtonsoft.Json.Linq;


namespace OsEngine.Market.Servers.BitStamp
{

    /// 
    /// client for access to cryptocurrency exchange BitStamp
    /// клиент доступа к бирже криптовалют BitStamp
    /// 
    public clast BitstampClient
    {

        // service 
        // сервис

        /// 
        /// constructor
        /// конструктор
        /// 
        /// API-key / ключ для апи
        /// Secret key / секретный ключ
        /// client id / Id клиента
        public BitstampClient(string apiKey, string apiSecret, string clientId)
        {
            _requestAuthenticator = new RequestAuthenticator(apiKey, apiSecret, clientId);

            _apiKeyPublic = apiKey;
            _apiKeySecret = apiSecret;
            _clientId = clientId;

            Thread worker = new Thread(ThreadListenDataArea);
            worker.IsBackground = true;
            worker.Start();
        }

        /// 
        /// public API-key
        /// публичный ключ для апи
        /// 
        private string _apiKeyPublic;

        /// 
        /// secret API-key
        /// секретный ключ для апи
        /// 
        private string _apiKeySecret;

        /// 
        /// client number on the exchange
        /// номер клиента на бирже
        /// 
        private string _clientId;

        /// 
        /// object is responsible for the correct headers and tags when sending messages with using HTTP
        /// объект отвечающий за правильные заголовки и метки при отправке сообщений через HTTP
        /// 
        private readonly RequestAuthenticator _requestAuthenticator;

        /// 
        /// connect to exchange
        /// установить соединение с биржей 
        /// 
        public void Connect()
        {
            if (string.IsNullOrWhiteSpace(_apiKeyPublic) ||
                string.IsNullOrWhiteSpace(_apiKeySecret) ||
                string.IsNullOrWhiteSpace(_clientId))
            {
                return;
            }
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
            // check server availability for HTTP communication with it / проверяем доступность сервера для HTTP общения с ним
            Uri uri = new Uri("https://www.bitstamp.net");
            try
            {
                HttpWebRequest httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(uri);

                HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();
            }
            catch
            {
                LogMessageEvent("Сервер не доступен. Отсутствуюет интернет. ", LogMessageType.Error);
                return;
            }

            IsConnected = true;

            if (Connected != null)
            {
                Connected();
            }

            // start stream data through WebSocket / запускаем потоковые данные через WebSocket

            _ws = new ClientWebSocket();

            Uri wsUri = new Uri("wss://ws.bitstamp.net");
            _ws.ConnectAsync(wsUri, CancellationToken.None).Wait();

            if (_ws.State == WebSocketState.Open)
            {
                if (Connected != null)
                {
                    Connected.Invoke();
                }
                IsConnected = true;
            }

            Thread worker = new Thread(GetRes);
            worker.CurrentCulture = new CultureInfo("ru-RU");
            worker.IsBackground = true;
            worker.Start(_ws);

            Thread converter = new Thread(Converter);
            converter.CurrentCulture = new CultureInfo("ru-RU");
            converter.IsBackground = true;
            converter.Start();

        }

        ClientWebSocket _ws;

        /// 
        /// queue of new messages from the exchange server
        /// очередь новых сообщений, пришедших с сервера биржи
        /// 
        private ConcurrentQueue _newMessage = new ConcurrentQueue();

        private void GetRes(object clientWebSocket)
        {
            ClientWebSocket ws = (ClientWebSocket)clientWebSocket;

            string res = "";

            while (true)
            {
                if (_isDisposed)
                {
                    return;
                }
                try
                {
                    if (_ws.State == WebSocketState.CloseReceived)
                    {
                        Thread.Sleep(10000);

                        if (_ws.State == WebSocketState.CloseReceived)
                        {
                            IsConnected = false;
                            Disconnected();
                            return;
                        }

                    }

                    if (ws.State != WebSocketState.Open)
                    {
                        continue;
                    }

                    if (IsConnected)
                    {
                        var buffer = new ArraySegment(new byte[1024]);
                        var result = ws.ReceiveAsync(buffer, CancellationToken.None).Result;

                        if (result.Count == 0)
                        {
                            Thread.Sleep(1);
                            continue;
                        }

                        if (result.EndOfMessage == false)
                        {
                            res += Encoding.UTF8.GetString(buffer.Array, 0, result.Count);
                        }
                        else
                        {
                            res += Encoding.UTF8.GetString(buffer.Array, 0, result.Count);
                            _newMessage.Enqueue(res);
                            res = "";
                        }
                    }
                }
                catch (Exception exception)
                {
                    SendLogMessage(exception.ToString(), LogMessageType.Error);
                }
            }
        }

        private void Converter()
        {
            while (true)
            {
                if (_isDisposed)
                {
                    return;
                }
                try
                {
                    if (!_newMessage.IsEmpty)
                    {
                        string mes;

                        if (_newMessage.TryDequeue(out mes))
                        {
                            List values = new List();
                            int firstInd = 0;
                            for (int i = 1; i < mes.Length - 1; i++)
                            {
                                if (string.Equals((mes[i].ToString() + mes[i + 1].ToString()), "}{"))
                                {
                                    String newString = mes.Substring(firstInd, i - firstInd + 1);
                                    firstInd = i + 1;
                                    values.Add(newString);
                                }
                            }

                            String lastString = mes.Substring(firstInd, mes.Length - firstInd);
                            values.Add(lastString);

                            for (int i = 0; i < values.Count; i++)
                            {
                                ParseMessage(values[i]);
                            }

                        }

                    }
                    else
                    {
                        Thread.Sleep(1);
                    }
                }
                catch (Exception exception)
                {
                    SendLogMessage(exception.ToString(), LogMessageType.Error);
                }
            }
        }

        string _orderBookStr = "order_book";
        string _tradesStr = "live_trades";

        private void ParseMessage(string mes)
        {
            mes = "[" + mes + "]";
            var jProperties = JToken.Parse(mes);

            foreach (var data in jProperties)
            {
                BitstampData d = new BitstampData();
                d.Event = data.SelectToken("event").ToString();
                d.channel = data.SelectToken("channel").ToString();
                d.data = data.SelectToken("data").ToString();


                if (d.data.Length < 10)
                {
                    return;
                }

                if (d.channel.StartsWith(_orderBookStr))
                {
                    OrderBook_Income(d.channel.Split('_')[2], d.data);
                }
                if (d.channel.StartsWith(_tradesStr))
                {
                    Trades_Income(d.channel.Split('_')[2], d.data);
                }
            }
        }

        /// 
        /// bring the program to the start. Clear all objects involved in connecting to the server
        /// привести программу к моменту запуска. Очистить все объекты участвующие в подключении к серверу
        /// 
        public void Dispose()
        {
            IsConnected = false;

            _requestAuthenticator.Dispose();

            if (_ws != null)
            {
                _ws.Abort();
            }

            if (Disconnected != null)
            {
                Disconnected();
            }

            _isDisposed = true;
        }

        /// 
        /// shows whether connection works
        /// работает ли соединение
        /// 
        public bool IsConnected;

        // stream data from WEBSOCKET
        // потоковые данные из WEBSOCKET


        /// 
        /// subscribed to depths and trades securities
        /// бумаги уже подписаные на получение стаканов и трейдов
        /// 
        private List _subscribedSec = new List();

        /// 
        /// subscribe this security to get depths and trades
        /// подписать данную бумагу на получение стаканов и трейдов
        /// 
        public void SubscribleTradesAndDepths(Security security)
        {
            if (_subscribedSec.Find(s => s.Name == security.Name) == null)
            {
                _subscribedSec.Add(security);

                string str1 = "{\"event\": \"bts:subscribe\",\"data\": {\"channel\": \"";

                string channelTrades = str1 + "live_trades_" + security.Name + "\"}}"; //[currency_pair]
                string depthTrades = str1 + "order_book_" + security.Name + "\"}}"; //[currency_pair]

                var reqAsBytes = Encoding.UTF8.GetBytes(channelTrades);
                var ticksRequest = new ArraySegment(reqAsBytes);

                _ws.SendAsync(ticksRequest,
                WebSocketMessageType.Text,
                true,
                CancellationToken.None).Wait();


                var reqAsBytes2 = Encoding.UTF8.GetBytes(depthTrades);
                var depthRequest = new ArraySegment(reqAsBytes2);

                _ws.SendAsync(depthRequest,
                WebSocketMessageType.Text,
                true,
                CancellationToken.None).Wait();


                /*  Channel channelTrades = null;

                  if (security.Name == "btcusd")
                  {
                      channelTrades = _pusher.Subscribe("live_trades");
                  }
                  else
                  {
                      channelTrades = _pusher.Subscribe("live_trades_" + security.Name);
                  }

                  if (security.Name == "btcusd")
                  { channelTrades.Bind("trade", (dynamic data) => { Trades_Income("btcusd", data.ToString()); }); }
                  else if (security.Name == "btceur")
                  { channelTrades.Bind("trade", (dynamic data) => { Trades_Income("btceur", data.ToString()); }); }
                  else if (security.Name == "eurusd")
                  { channelTrades.Bind("trade", (dynamic data) => { Trades_Income("eurusd", data.ToString()); }); }
                  else if (security.Name == "xrpusd")
                  { channelTrades.Bind("trade", (dynamic data) => { Trades_Income("xrpusd", data.ToString()); }); }
                  else if (security.Name == "xrpeur")
                  { channelTrades.Bind("trade", (dynamic data) => { Trades_Income("xrpeur", data.ToString()); }); }
                  else if (security.Name == "xrpbtc")
                  { channelTrades.Bind("trade", (dynamic data) => { Trades_Income("xrpbtc", data.ToString()); }); }
                  else if (security.Name == "ltcusd")
                  { channelTrades.Bind("trade", (dynamic data) => { Trades_Income("ltcusd", data.ToString()); }); }
                  else if (security.Name == "ltceur")
                  { channelTrades.Bind("trade", (dynamic data) => { Trades_Income("ltceur", data.ToString()); }); }
                  else if (security.Name == "ltcbtc")
                  { channelTrades.Bind("trade", (dynamic data) => { Trades_Income("ltcbtc", data.ToString()); }); }
                  else if (security.Name == "ethusd")
                  { channelTrades.Bind("trade", (dynamic data) => { Trades_Income("ethusd", data.ToString()); }); }
                  else if (security.Name == "etheur")
                  { channelTrades.Bind("trade", (dynamic data) => { Trades_Income("etheur", data.ToString()); }); }
                  else if (security.Name == "ethbtc")
                  { channelTrades.Bind("trade", (dynamic data) => { Trades_Income("ethbtc", data.ToString()); }); }
                  else if (security.Name == "bcheur")
                  { channelTrades.Bind("trade", (dynamic data) => { Trades_Income("bcheur", data.ToString()); }); }
                  else if (security.Name == "bchusd")
                  { channelTrades.Bind("trade", (dynamic data) => { Trades_Income("bchusd", data.ToString()); }); }

                  Channel channelBook = null;

                  if (security.Name == "btcusd")
                  {
                      channelBook = _pusher.Subscribe("order_book");
                  }
                  else
                  {
                      channelBook = _pusher.Subscribe("order_book_" + security.Name);
                  }


                  if (security.Name == "btcusd")
                  { channelBook.Bind("data", (dynamic data) => { OrderBook_Income("btcusd", data.ToString()); }); }
                  else if (security.Name == "btceur")
                  { channelBook.Bind("data", (dynamic data) => { OrderBook_Income("btceur", data.ToString()); }); }
                  else if (security.Name == "eurusd")
                  { channelBook.Bind("data", (dynamic data) => { OrderBook_Income("eurusd", data.ToString()); }); }
                  else if (security.Name == "xrpusd")
                  { channelBook.Bind("data", (dynamic data) => { OrderBook_Income("xrpusd", data.ToString()); }); }
                  else if (security.Name == "xrpeur")
                  { channelBook.Bind("data", (dynamic data) => { OrderBook_Income("xrpeur", data.ToString()); }); }
                  else if (security.Name == "xrpbtc")
                  { channelBook.Bind("data", (dynamic data) => { OrderBook_Income("xrpbtc", data.ToString()); }); }
                  else if (security.Name == "ltcusd")
                  { channelBook.Bind("data", (dynamic data) => { OrderBook_Income("ltcusd", data.ToString()); }); }
                  else if (security.Name == "ltceur")
                  { channelBook.Bind("data", (dynamic data) => { OrderBook_Income("ltceur", data.ToString()); }); }
                  else if (security.Name == "ltcbtc")
                  { channelBook.Bind("data", (dynamic data) => { OrderBook_Income("ltcbtc", data.ToString()); }); }
                  else if (security.Name == "ethusd")
                  { channelBook.Bind("data", (dynamic data) => { OrderBook_Income("ethusd", data.ToString()); }); }
                  else if (security.Name == "etheur")
                  { channelBook.Bind("data", (dynamic data) => { OrderBook_Income("etheur", data.ToString()); }); }
                  else if (security.Name == "ethbtc")
                  { channelBook.Bind("data", (dynamic data) => { OrderBook_Income("ethbtc", data.ToString()); }); }
                  else if (security.Name == "bcheur")
                  { channelBook.Bind("data", (dynamic data) => { OrderBook_Income("bcheur", data.ToString()); }); }
                  else if (security.Name == "bchusd")
                  { channelBook.Bind("data", (dynamic data) => { OrderBook_Income("bchusd", data.ToString()); }); }
                  */
            }
        }

        /// 
        /// incoming depth
        /// входящий стакан
        /// 
        /// instrument name / название инструмента
        /// depth / стакан
        private void OrderBook_Income(string namePaper, string message)
        {
            if (_isDisposed)
            {
                return;
            }

            try
            {
                var resp = JsonConvert.DeserializeObject(message);

                MarketDepth depth = new MarketDepth();
                depth.SecurityNameCode = namePaper;

                for (int i = 0; i < 10; i++)
                {
                    MarketDepthLevel ask = new MarketDepthLevel();
                    ask.Price = resp.asks[i][0].ToDecimal();
                    ask.Ask = resp.asks[i][1].ToDecimal();
                    depth.Asks.Add(ask);

                    MarketDepthLevel bid = new MarketDepthLevel();
                    bid.Price = resp.bids[i][0].ToDecimal();
                    bid.Bid = resp.bids[i][1].ToDecimal();
                    depth.Bids.Add(bid);
                }

                if (UpdateMarketDepth != null)
                {
                    UpdateMarketDepth(depth);
                }
            }
            catch (Exception error)
            {
                if (LogMessageEvent != null)
                {
                    //   LogMessageEvent(error.ToString(), LogMessageType.Error);
                }
            }
        }

        /// 
        /// incoming depth
        /// входящий стакан
        /// 
        /// security name/название инструмента
        /// trade/трейд
        private void Trades_Income(string namePaper, string message)
        {
            if (_isDisposed)
            {
                return;
            }
            try
            {
                var resp = JsonConvert.DeserializeObject(message);

                Trade trade = new Trade();
                trade.SecurityNameCode = namePaper;
                trade.Price = resp.price.ToDecimal();
                trade.Volume = resp.amount.ToDecimal();
                trade.Id = resp.id;
                trade.Time = (new DateTime(1970, 1, 1, 0, 0, 0, 0)).AddSeconds(Convert.ToInt32(resp.timestamp));

                if (resp.type == "0")
                {
                    trade.Side = Side.Buy;
                }
                else
                {
                    trade.Side = Side.Sell;
                }

                if (NewTradesEvent != null)
                {
                    NewTradesEvent(trade);
                }
            }
            catch (Exception error)
            {

                if (LogMessageEvent != null)
                {
                    // LogMessageEvent(error.ToString(),LogMessageType.Error);
                }
            }
        }

        // listening thread of data throw HTTP
        // поток прослушки данных через HTTP

        private RestRequest GetAuthenticatedRequest(Method method)
        {
            var request = new RestRequest(method);
            _requestAuthenticator.Authenticate(request);
            return request;
        }

        private object _lock = new object();

        /// 
        /// request to clear the object
        /// произошёл запрос на очистку объекта
        /// 
        private bool _isDisposed;

        /// 
        /// work place of the thread that downloads data from API
        /// место работы потока который скачивает данные из апи
        /// 
        private void ThreadListenDataArea()
        {
            DateTime lastTimeGetPortfolios = DateTime.MinValue;
            DateTime lastTimeGetOrders = DateTime.MinValue;

            while (true)
            {
                try
                {
                    Thread.Sleep(1);
                    if (MainWindow.ProccesIsWorked == false)
                    {
                        return;
                    }

                    if (_isDisposed)
                    {
                        return;
                    }

                    if (IsConnected == false)
                    {
                        continue;
                    }


                    // once every ten seconds we request the portfolio and its components / раз в десять секунд запрашиваем портфель и его составляющие
                    if (lastTimeGetPortfolios.AddSeconds(10) < DateTime.Now)
                    {
                        lastTimeGetPortfolios = DateTime.Now;
                        GetBalance();
                    }

                    // once a second we update the data on our orders / раз секунду обновляем данные по своим ордерам
                    if (lastTimeGetOrders.AddSeconds(1) < DateTime.Now)
                    {
                        lastTimeGetOrders = DateTime.Now;
                        GetOrders();
                    }

                }
                catch (Exception error)
                {
                    if (LogMessageEvent != null)
                    {
                        LogMessageEvent(error.ToString(), LogMessageType.Error);
                    }
                }
            }
        }

        /// 
        /// take balance
        /// взять баланс
        /// 
        public BalanceResponse GetBalance()
        {
            lock (_lock)
            {
                try
                {
                    var request = GetAuthenticatedRequest(Method.POST);
                    var response = new RestClient("https://www.bitstamp.net/api/v2/balance/").Execute(request);

                    if (response.Content.Contains("error"))
                    {
                        SendLogMessage("BitStamp Response Error " + response.Content, LogMessageType.Error);

                        return null;
                    }

                    if (UpdatePortfolio != null)
                    {
                        UpdatePortfolio(JsonConvert.DeserializeObject(response.Content));
                    }

                    return JsonConvert.DeserializeObject(response.Content);
                }
                catch (Exception ex)
                {
                    LogMessageEvent(ex.ToString(), LogMessageType.Error);
                    return null;
                }
            }
        }

        private List _ordersCanseled = new List();

        /// 
        /// update order data
        /// обновить данные по ордерам
        /// 
        private void GetOrders()
        {

            for (int i = 0; i < _osEngineOrders.Count; i++)
            {
                Thread.Sleep(300);

                if (_ordersCanseled.Find(ord => ord.NumberMarket == _osEngineOrders[i].NumberMarket) != null)
                {
                    _osEngineOrders.RemoveAt(i);
                    i--;
                    continue;
                }

                Order order = _osEngineOrders[i];

                OrderStatusResponse response = GetOrderStatus(order.NumberMarket);

                if (response.transactions != null &&
                    response.transactions.Length != 0)
                {
                    MyTrade trade = new MyTrade();
                    trade.NumberOrderParent = order.NumberMarket;
                    trade.NumberTrade = response.transactions[0].tid;
                    trade.SecurityNameCode = order.SecurityNameCode;
                    trade.Volume = order.Volume;
                    trade.Price =
                            response.transactions[0].price.ToDecimal();
                    trade.Side = order.Side;

                    _osEngineOrders.RemoveAt(i);
                    i--;

                    if (MyTradeEvent != null)
                    {
                        MyTradeEvent(trade);
                    }
                }
                else if (response.status == "Finished" ||
                         (response.status == null && string.IsNullOrEmpty(order.NumberMarket)))
                {
                    Order newOrder = new Order();
                    newOrder.SecurityNameCode = order.SecurityNameCode;
                    newOrder.NumberUser = order.NumberUser;
                    newOrder.NumberMarket = order.NumberMarket;
                    newOrder.PortfolioNumber = order.PortfolioNumber;
                    newOrder.Side = order.Side;
                    newOrder.Price = order.Price;
                    newOrder.State = OrderStateType.Cancel;
                    newOrder.Volume = order.Volume;

                    if (MyOrderEvent != null)
                    {
                        MyOrderEvent(newOrder);
                    }

                    _osEngineOrders.RemoveAt(i);
                    i--;
                }
            }
        }

        /// 
        /// update order status
        /// обновить статус ордера
        /// 
        public OrderStatusResponse GetOrderStatus(string orderId)
        {
            lock (_lock)
            {
                try
                {
                    var request = GetAuthenticatedRequest(Method.POST);
                    request.AddParameter("id", orderId);

                    var response = new RestClient("https://www.bitstamp.net/api/order_status/").Execute(request);

                    return JsonConvert.DeserializeObject(response.Content);
                }
                catch (Exception ex)
                {
                    SendLogMessage(ex.ToString(), LogMessageType.Error);
                    return null;
                }
            }
        }

        // public methods for requesting data throw HTTP
        // публичные методы запроса данных через HTTP   

        /// 
        /// take securities
        /// взять бумаги
        /// 
        public List GetSecurities()
        {
            lock (_lock)
            {
                try
                {

                    var request = GetAuthenticatedRequest(Method.GET);
                    var response = new RestClient("https://www.bitstamp.net/api/v2/trading-pairs-info/").Execute(request);

                    if (UpdatePairs != null)
                    {
                        UpdatePairs(JsonConvert.DeserializeObject(response.Content));
                    }

                    return JsonConvert.DeserializeObject(response.Content);
                }
                catch (Exception ex)
                {
                    if (LogMessageEvent != null)
                    {
                        LogMessageEvent(ex.ToString(), LogMessageType.Error);
                    }

                    return null;
                }
            }
        }

        /// 
        /// cancel order
        /// отменить оредр
        /// 
        public void CancelOrder(Order order)
        {
            if (CancelOrder(order.NumberMarket))
            {
                order.State = OrderStateType.Cancel;

                if (MyOrderEvent != null)
                {
                    MyOrderEvent(order);
                }

                _ordersCanseled.Add(order);
            }
        }

        /// 
        /// cancel order by id
        /// снять ордер по id
        /// 
        private bool CancelOrder(string orderId)
        {
            lock (_lock)
            {
                try
                {
                    var request = GetAuthenticatedRequest(Method.POST);
                    request.AddParameter("id", orderId);

                    var response = new RestClient("https://www.bitstamp.net/api/v2/cancel_order/").Execute(request);

                    return true;

                }
                catch (Exception ex)
                {
                    SendLogMessage(ex.ToString(), LogMessageType.Error);
                    return false;
                }
            }
        }

        /// 
        /// cancel all orders
        /// отменить все ордера
        /// 
        public bool CancelAllOrders()
        {
            lock (_lock)
            {
                try
                {
                    var request = GetAuthenticatedRequest(Method.POST);

                    var response = new RestClient("https://www.bitstamp.net/api/cancel_all_orders/").Execute(request);

                    return (response.Content == "true") ? true : false;
                }
                catch (Exception ex)
                {
                    SendLogMessage(ex.ToString(), LogMessageType.Error);
                    return false;
                }
            }
        }

        /// 
        /// outgoing orders in OsEngine format
        /// исходящие ордера в формате OsEngine
        /// 
        private List _osEngineOrders = new List();

        /// 
        /// execute order
        /// исполнить ордер
        /// 
        /// 
        public void ExecuteOrder(Order order)
        {
            if (IsConnected == false)
            {
                return;
            }

            _osEngineOrders.Add(order);

            string url = "https://www.bitstamp.net/api/v2";

            if (order.Side == Side.Buy)
            {
                url += "/buy/";
            }
            else
            {
                url += "/sell/";
            }

            url += order.SecurityNameCode + "/";

            BuySellResponse result = Execute((double)order.Volume, (double)order.Price, url);

            if (result == null)
            {
                return;
            }

            SendLogMessage("order status: " + result.status + "  " + result.reason, LogMessageType.System);

            if (result.id != null)
            {
                Order newOrder = new Order();
                newOrder.SecurityNameCode = order.SecurityNameCode;
                newOrder.TimeCallBack = DateTime.Parse(result.datetime);
                newOrder.NumberUser = order.NumberUser;
                newOrder.NumberMarket = result.id;
                newOrder.PortfolioNumber = order.PortfolioNumber;
                newOrder.Side = order.Side;
                newOrder.Price = order.Price;
                newOrder.State = OrderStateType.Activ;
                newOrder.Volume = order.Volume;

                if (MyOrderEvent != null)
                {
                    MyOrderEvent(newOrder);
                }

            }
            else
            {
                Order newOrder = new Order();
                newOrder.SecurityNameCode = order.SecurityNameCode;
                newOrder.NumberUser = order.NumberUser;
                newOrder.PortfolioNumber = order.PortfolioNumber;
                newOrder.Side = order.Side;
                newOrder.State = OrderStateType.Fail;

                if (MyOrderEvent != null)
                {
                    MyOrderEvent(newOrder);
                }
            }
        }

        /// 
        /// execute order
        /// исполнить ордер
        /// 
        /// volume / объем
        /// price / цена
        /// security / бумага
        /// 
        private BuySellResponse Execute(double amount, double price, string securityUrl)
        {
            lock (_lock)
            {
                try
                {
                    var request = GetAuthenticatedRequest(Method.POST);
                    request.AddParameter("amount", amount.ToString().Replace(",", "."));
                    request.AddParameter("price", price.ToString().Replace(",", "."));

                    var response = new RestClient(securityUrl).Execute(request);

                    if (response.Content.IndexOf("error", StringComparison.Ordinal) > 0)
                    {
                        SendLogMessage("Ошибка на выставлении ордера", LogMessageType.Error);
                        SendLogMessage(response.Content, LogMessageType.Error);
                        return null;
                    }


                    return JsonConvert.DeserializeObject(response.Content);
                }
                catch (Exception ex)
                {
                    SendLogMessage(ex.ToString(), LogMessageType.Error);
                    return null;
                }
            }
        }

        // outgoing events
        // исходящие события

        /// 
        /// my new orders
        /// новые мои ордера
        /// 
        public event Action MyOrderEvent;

        /// 
        /// my new trades
        /// новые мои сделки
        /// 
        public event Action MyTradeEvent;

        /// 
        /// portfolio update event
        /// событие обновления портфеля
        /// 
        public event Action UpdatePortfolio;

        /// 
        /// new securities in the system
        /// новые бумаги в системе
        /// 
        public event Action UpdatePairs;

        /// 
        /// depth updated
        /// обновился стакан
        /// 
        public event Action UpdateMarketDepth;

        /// 
        /// ticks updated
        /// обновились тики
        /// 
        public event Action NewTradesEvent;

        /// 
        /// connection to BitStamp API established
        /// соединение с BitStamp API установлено
        /// 
        public event Action Connected;

        /// 
        /// connection to BitStamp API lost
        /// соединение с BitStamp API разорвано
        /// 
        public event Action Disconnected;

        // log messages
        // сообщения для лога

        /// 
        /// add a new log message
        /// добавить в лог новое сообщение
        /// 
        private void SendLogMessage(string message, LogMessageType type)
        {
            if (LogMessageEvent != null)
            {
                LogMessageEvent(message, type);
            }
        }

        /// 
        /// send exeptions
        /// отправляет исключения
        /// 
        public event Action LogMessageEvent;

    }
}