Esempio n. 1
0
        /// <summary>
        /// Begins logging exchanges - writes errors to console. You should block the app using Console.ReadLine.
        /// </summary>
        /// <param name="path">Path to write files to</param>
        /// <param name="intervalSeconds">Interval in seconds in between each log calls for each exchange</param>
        /// <param name="terminateAction">Call this when the process is about to exit, like a WM_CLOSE message on Windows.</param>
        /// <param name="compress">Whether to compress the log files</param>
        /// <param name="exchangeNamesAndSymbols">Exchange names and symbols to log</param>
        public static void LogExchanges(string path, float intervalSeconds, out Action terminateAction, bool compress, params string[] exchangeNamesAndSymbols)
        {
            bool   terminating = false;
            Action terminator  = null;

            path = (string.IsNullOrWhiteSpace(path) ? "./" : path);
            Dictionary <ExchangeLogger, int> errors  = new Dictionary <ExchangeLogger, int>();
            List <ExchangeLogger>            loggers = new List <ExchangeLogger>();

            for (int i = 0; i < exchangeNamesAndSymbols.Length;)
            {
                loggers.Add(new ExchangeLogger(ExchangeAPI.GetExchangeAPI(exchangeNamesAndSymbols[i++]), exchangeNamesAndSymbols[i++], intervalSeconds, path, compress));
            }
            ;
            foreach (ExchangeLogger logger in loggers)
            {
                logger.Start();
                logger.Error += (log, ex) =>
                {
                    int errorCount;
                    lock (errors)
                    {
                        if (!errors.TryGetValue(log, out errorCount))
                        {
                            errorCount = 0;
                        }
                        errors[log] = ++errorCount;
                    }
                    Logger.Info("Errors for {0}: {1}", log.API.Name, errorCount);
                };
            }
            terminator = () =>
            {
                if (!terminating)
                {
                    terminating = true;
                    foreach (ExchangeLogger logger in loggers.ToArray())
                    {
                        logger.Stop();
                        logger.Dispose();
                    }
                    loggers.Clear();
                }
            };
            terminateAction = terminator;

            // make sure to close properly
            Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e)
            {
                terminator();
            };
            AppDomain.CurrentDomain.ProcessExit += (object sender, EventArgs e) =>
            {
                terminator();
            };
            Logger.Info("Loggers \"{0}\" started, press ENTER or CTRL-C to terminate.", string.Join(", ", loggers.Select(l => l.API.Name)));
        }
Esempio n. 2
0
        /// <summary>
        /// Static constructor
        /// </summary>
        static ExchangeAPI()
        {
            foreach (Type type in typeof(ExchangeAPI).Assembly.GetTypes().Where(type => type.IsSubclassOf(typeof(ExchangeAPI)) && !type.IsAbstract))
            {
                // lazy create, we just create an instance to get the name, nothing more
                // we don't want to pro-actively create all of these becanse an API
                // may be running a timer or other house-keeping which we don't want
                // the overhead of if a user is only using one or a handful of the apis
                using (ExchangeAPI api = Activator.CreateInstance(type) as ExchangeAPI)
                {
                    if (string.IsNullOrEmpty(api.Name))
                    {
                        continue;
                    }

                    Apis[api.Name] = null;
                }
            }
        }
Esempio n. 3
0
            } = true;                                              // some exchanges support going from most recent to oldest, but others, like Gemini must go from oldest to newest

            public ExchangeHistoricalTradeHelper(ExchangeAPI api)
            {
                this.api = api;
            }
Esempio n. 4
0
        /// <summary>
        ///     Place a limit order by first querying the order book and then placing the order for a threshold below the bid or
        ///     above the ask that would fully fulfill the amount.
        ///     The order book is scanned until an amount of bids or asks that will fulfill the order is found and then the order
        ///     is placed at the lowest bid or highest ask price multiplied
        ///     by priceThreshold.
        /// </summary>
        /// <param name="api"></param>
        /// <param name="symbol">Symbol to sell</param>
        /// <param name="amount">Amount to sell</param>
        /// <param name="isBuy">True for buy, false for sell</param>
        /// <param name="orderBookCount">Amount of bids/asks to request in the order book</param>
        /// <param name="priceThreshold">
        ///     Threshold below the lowest bid or above the highest ask to set the limit order price at. For buys, this is
        ///     converted to 1 / priceThreshold.
        ///     This can be set to 0 if you want to set the price like a market order.
        /// </param>
        /// <param name="thresholdToAbort">
        ///     If the lowest bid/highest ask price divided by the highest bid/lowest ask price is below this threshold, throw an
        ///     exception.
        ///     This ensures that your order does not buy or sell at an extreme margin.
        /// </param>
        /// <param name="abortIfOrderBookTooSmall">
        ///     Whether to abort if the order book does not have enough bids or ask amounts to
        ///     fulfill the order.
        /// </param>
        /// <returns>Order result</returns>
        public static async Task <List <ExchangeOrderResult> > PlaceSafeMarketOrderAsync(
            this ExchangeAPI api, string symbol,
            decimal amount, bool isBuy, int orderBookCount = 100, decimal priceThreshold = 0.9m,
            decimal thresholdToAbort = 0.75m, bool abortIfOrderBookTooSmall = false)
        {
            if (priceThreshold > 0.9m)
            {
                throw new APIException(
                          "You cannot specify a price threshold above 0.9m, otherwise there is a chance your order will never be fulfilled. For buys, this is " +
                          "converted to 1.0m / priceThreshold, so always specify the value below 0.9m");
            }
            if (priceThreshold <= 0m)
            {
                priceThreshold = 1m;
            }
            else if (isBuy && priceThreshold > 0m)
            {
                priceThreshold = 1.0m / priceThreshold;
            }

            ExchangeDepth book = null; //todo await api.GetDepthAsync(symbol, orderBookCount);

            if (book == null || isBuy && book.Asks.Count == 0 || !isBuy && book.Bids.Count == 0)
            {
                throw new APIException($"Error getting order book for {symbol}");
            }

            var counter   = 0m;
            var highPrice = decimal.MinValue;
            var lowPrice  = decimal.MaxValue;

            if (isBuy)
            {
                foreach (var ask in book.Asks.Values)
                {
                    counter  += ask.Amount;
                    highPrice = Math.Max(highPrice, ask.Price);
                    lowPrice  = Math.Min(lowPrice, ask.Price);
                    if (counter >= amount)
                    {
                        break;
                    }
                }
            }
            else
            {
                foreach (var bid in book.Bids.Values)
                {
                    counter  += bid.Amount;
                    highPrice = Math.Max(highPrice, bid.Price);
                    lowPrice  = Math.Min(lowPrice, bid.Price);
                    if (counter >= amount)
                    {
                        break;
                    }
                }
            }

            if (abortIfOrderBookTooSmall && counter < amount)
            {
                throw new APIException(
                          $"{(isBuy ? "Buy" : "Sell")} order for {symbol} and amount {amount} cannot be fulfilled because the order book is too thin.");
            }
            if (lowPrice / highPrice < thresholdToAbort)
            {
                throw new APIException(
                          $"{(isBuy ? "Buy" : "Sell")} order for {symbol} and amount {amount} would place for a price below threshold of {thresholdToAbort}, aborting.");
            }

            var request = new ExchangeOrderRequest
            {
                Amount            = amount,
                OrderType         = OrderType.Limit,
                Price             = CryptoUtility.RoundAmount((isBuy ? highPrice : lowPrice) * priceThreshold),
                ShouldRoundAmount = true,
                Symbol            = null //todo symbol
            };
            var result = await api.PlaceOrdersAsync(request);

            // wait about 10 seconds until the order is fulfilled
            var       i        = 0;
            const int maxTries = 20; // 500 ms for each try

            for (; i < maxTries; i++)
            {
                await Task.Delay(500);
            }
            //TODO
            //result = await api.GetCanceledOrdersAsync(result.OrderId, symbol);
            //switch (result.Result)
            //{
            //    case ExchangeAPIOrderResult.Filled:
            //    case ExchangeAPIOrderResult.Canceled:
            //    case ExchangeAPIOrderResult.Error:
            //        break;
            //}

            if (i == maxTries)
            {
                throw new APIException(
                          $"{(isBuy ? "Buy" : "Sell")} order for {symbol} and amount {amount} timed out and may not have been fulfilled");
            }

            return(result);
        }