Example #1
0
        /// <summary>There's a dump on market, best time to SELL.</summary>
        private static bool dump(TradeStatisticsResponse tradeHistory)
        {
            //Safety in case Huobi returned broken response
            if (null == tradeHistory.trades)
                return false;

            //TODO: do lot of vain runs, watch carefully and tune the magic numbers here

            //Activity criterion: if mean time between recent trades is 5 seconds or less, it's high activity
            const int MAX_SPEED = 5;

            var uniqueTradesCount = 1;
            var upTrades = 0;
            var downTrades = 0;
            var lastTime = tradeHistory.trades.Last().TimeTyped;
            var lastPrice = tradeHistory.trades.Last().price;
            double totalSeconds = 0.0;

            for (int i = tradeHistory.trades.Count-1; i >= 0; i--)
            {
                var trade = tradeHistory.trades[i];

                if (trade.TimeTyped != lastTime)
                {
                    totalSeconds += (trade.TimeTyped - lastTime).TotalSeconds;
                    if (trade.price < lastPrice)
                        downTrades++;
                    else if (trade.price > lastPrice)
                        upTrades++;
                    else if (TradeType.SELL == trade.Type)
                        downTrades++;
                    else
                        upTrades++;

                    uniqueTradesCount++;
                    lastTime = trade.TimeTyped;
                    lastPrice = trade.price;
                }
            }

            double avgSeconds = totalSeconds/uniqueTradesCount;
            if (avgSeconds > MAX_SPEED)
                return false;

            //Trend criterion: at least 75% of recent trades are down trend, means lower price than previous trade
            const double MIN_DOWN_TRADES = 0.75;
            double downPercentage = (double)downTrades / (upTrades + downTrades);
            if (downPercentage < MIN_DOWN_TRADES)
                return false;

            //Price criterion: price has fallen enough, that is at least 6 CNY per BTC
            const double MIN_PRICE_DIFF = 6.0;

            var startPrice = tradeHistory.trades.First().price;
            var endPrice = tradeHistory.trades.Last().price;
            return (startPrice > endPrice && startPrice - endPrice >= MIN_PRICE_DIFF);
        }
Example #2
0
        /// <summary>
        /// Returns non-NULL description of BUY reason for bearish bot.
        /// </summary>
        internal string ReasonToBuyBack(List<Candle> candles, TradeStatisticsResponse tradeHistory)
        {
            if (pump(tradeHistory))
                return "PUMP";
            if (isRising(candles))
                return "Significant rise";

            return null;
        }
Example #3
0
        /// <summary>
        /// Returns NULL if market doesn't give enough SELL signals, description of SELL reason otherwise. Good for BEAR strategy.
        /// </summary>
        internal string ReasonToSell(List<Candle> candles, TradeStatisticsResponse tradeHistory)
        {
            if (dump(tradeHistory))
                return "DUMP";
            if (volumeDeclineAfterPriceRise(candles))
                return "Volume and price decline after price rise";

            //TODO: recognition of all the other patterns

            return null;
        }
Example #4
0
        /// <summary>Returns numeric indicator of market activity. Higher value means higher activity (i.e. lot of trades with higher volume).</summary>
        /// <param name="tradeStats">Huobi statistics about trading activity</param>
        /// <param name="now">Current local time of the exchange</param>
        /// <returns>Coeficient in [0.0, 1.0] where 0.0 means totally peacefull market, 1.0 is wild.</returns>
        internal static float GetMadness(TradeStatisticsResponse tradeStats, DateTime now)
        {
            //For case we have broken data returned from server
            if (null == tradeStats || null == tradeStats.trades || !tradeStats.trades.Any() || DateTime.MinValue == now)
                return 0.75f;

            //There's always exactly 60 past trades
            var trades = tradeStats.trades.Where(trade => trade.TimeTyped >= now.AddSeconds(-120)).ToList();
            if (!trades.Any())
                return 0.0f;

            //Group by time, so that single trade with big volume doesn't look like many trades
            var groupped = new Dictionary<string, Trade>();
            foreach (var trade in trades)
            {
                var key = trade.time + "_" + trade.type;
                if (!groupped.ContainsKey(key))
                    groupped.Add(key, new Trade { time = trade.time, type = trade.type, amount = trade.amount, price = trade.price });
                else
                {
                    groupped[key].amount += trade.amount;
                    if (TradeType.BUY == trade.Type && trade.amount > groupped[key].amount)
                        groupped[key].amount = trade.amount;
                    else if (TradeType.SELL == trade.Type && trade.amount < groupped[key].amount)
                        groupped[key].amount = trade.amount;
                }
            }

            //        Console.WriteLine("DEBUG: past {0} trades, {1} groupped by time", tradeStats.trades.Count, groupped.Count);

            var firstTradeTime = tradeStats.trades.Last().TimeTyped;
            var lastTradeTime = tradeStats.trades.First().TimeTyped;
            var tradesTimeRange = lastTradeTime - firstTradeTime;

            var MIN_SPEED = new TimeSpan(0, 6, 0);  //All the trades spread in 6 minutes
            var MAX_SPEED = new TimeSpan(0, 2, 0);
            float intenseCoef;
            if (tradesTimeRange > MIN_SPEED)        //Trading speed (trades/time) too low
                intenseCoef = 0.0f;
            else if (tradesTimeRange < MAX_SPEED)
                intenseCoef = 1.0f;
            else
                intenseCoef = 1.0f - (float) ((tradesTimeRange - MAX_SPEED).TotalSeconds / (MIN_SPEED - MAX_SPEED).TotalSeconds);

            const double MIN_AVG_VOLUME = 0.8;
            const double MAX_AVG_VOLUME = 20.0;
            float volumeCoef;
            double avgVolume = groupped.Sum(trade => trade.Value.amount) / groupped.Count;
            //        Console.WriteLine("DEBUG: avgVolume={0}", avgVolume);
            if (avgVolume < MIN_AVG_VOLUME)
                volumeCoef = 0.0f;
            else if (avgVolume >= MAX_AVG_VOLUME)
                volumeCoef = 1.0f;
            else
                volumeCoef = (float)((avgVolume - MIN_AVG_VOLUME) / (MAX_AVG_VOLUME - MIN_AVG_VOLUME));

            //        Console.WriteLine("DEBUG: intensityCoef={0}, volumeCoef={1}", intenseCoef, volumeCoef);

            //Average of volume and frequency coeficients
            return (intenseCoef + volumeCoef) / 2;
        }
Example #5
0
 /*
  * TODO:
  * - connect to Huobi
  *
  * - one (or few) out-of-trend intervals can't fool the overall trend
  *
  */
 /// <summary>
 /// Market sentiment indicator in [-1.0, 1.0]. Closer to -1.0 indicates BTC price is falling, 1.0 means it's rising,
 /// around 0.0 is peaceful market
 /// </summary>
 internal float GetSentiment(TradeStatisticsResponse tradeHistory)
 {
     throw new NotImplementedException("TODO");
 }