using System; using sharp.tradebot; using sharp.json.attributes; using sharp.extensions; using sharp.trading; using System.IO; using System.Globalization; namespace BigBot { public class DifferentingBot : TradingBot { [JSONField] GlidingHistory currentValue = new GlidingHistory(1); [JSONField] GlidingHistory[] GlidingHistories; [JSONField] double targetVolume = 1; [JSONField] string currentOrderID; [JSONField] VolumeRate currentVolumeRate; [JSONField] double lastOrderLimit; [JSONField] SimpleHighLowDetector detector = new SimpleHighLowDetector(); TextWriter historyWriter; public override void Prepare() { base.Prepare(); double k = 0.25; if (this.GlidingHistories.IsNull()) { k = 0.25; this.GlidingHistories = new GlidingHistory[4]; for (int n = 0; n < this.GlidingHistories.Length;n++){ this.GlidingHistories[n] = new GlidingHistory(k); k /= 2; } } if (lastOrderLimit == 0){ lastOrderLimit = currentVolumeRate.UnityPrice; } historyWriter = new StreamWriter(new FileStream(Path.Combine(BaseDataPath, "history.csv"), FileMode.Append)); TradingBotEnvironment.RegisterPeriodic(Trade,60); } public override void Unprepare() { TradingBotEnvironment.UnregisterPeriodic(Trade); historyWriter.Close(); base.Unprepare(); } private void Trade(){ if (BasicSetup.Enabled){ TradeBotBalance bMarket = getBalance(BasicSetup.MarketSymbol); OrderBook orderBook = BotMarket.getOrderBook(); System.Tuple marketCheck = orderBook.getVolumePrice(BasicSetup.MarketCheckVolume); double currentAskRate = marketCheck.Item1 / BasicSetup.MarketCheckVolume; double currentBidRate = marketCheck.Item2 / BasicSetup.MarketCheckVolume; double currentCenterRate = (currentAskRate + currentBidRate) / 2; currentValue.Add(currentCenterRate); foreach (GlidingHistory gh in this.GlidingHistories) { gh.Add(currentCenterRate); } //detector.Add(this.GlidingHistories[0].centerRate); detector.Add(currentCenterRate); writeHistory(currentCenterRate); /* Indicators... */ double qEMA = (this.GlidingHistories[2].centerRate - currentCenterRate) / this.GlidingHistories[2].centerRate; double currentDTDT0 = this.GlidingHistories[0].dTdT; /* if ( ((qEMA > 0) && (targetVolume < 1.0 )) || ((qEMA < 0) && (targetVolume > 1.0 )) ){ targetVolume = 1; } */ /* if (!BasicSetup.ResetTargetVolume) { targetVolume *= (1.0 + qEMA); } else { targetVolume = 1.0; BasicSetup.ResetTargetVolume = false; } if (currentCenterRate > this.currentVolumeRate.UnityPrice){ targetVolume = 0.85; } else { targetVolume = 1.15; } double currentOrderVolume = (bMarket.CurrentBalance * targetVolume) - bMarket.CurrentBalance; if ((Math.Abs(currentOrderVolume) < BotMarket.MinimumTradeVolume) && (BasicSetup.IncreaseOrderVolumeToMinimum)){ currentOrderVolume *= BotMarket.MinimumTradeVolume / Math.Abs(currentOrderVolume); } */ double currentOrderVolume = currentVolumeRate.Volume * BasicSetup.RelativeTradeVolume; if (currentOrderVolume < BotMarket.MinimumTradeVolume) { currentOrderVolume = BotMarket.MinimumTradeVolume; } double diffDT01 = this.GlidingHistories[0].dT - this.GlidingHistories[1].dT; System.Tuple currentOrderPrices = orderBook.getVolumePrice(Math.Abs(currentOrderVolume)); double currentBuyRate = currentOrderPrices.Item1 / Math.Abs(currentOrderVolume); double currentSellRate = currentOrderPrices.Item2 / Math.Abs(currentOrderVolume); double currentBuyMargin = lastOrderLimit * (1.0 - BasicSetup.MarginBuy); double currentSellMargin = lastOrderLimit * (1.0 + BasicSetup.MarginSell); Log("Current: {0,11:###0.000000} {1,4} EMA: {2,11:###0.000000} {1}", currentCenterRate, BasicSetup.BaseSymbol, this.GlidingHistories[2].centerRate.CurrentValue); Log("qEMA: {0,8:###0.0000} % Target Volume: {1,11:###0.000000}", qEMA * 100, targetVolume); Log("Current Market Balance: {0,11:###0.000000} {3,4} / {1,11:###0.000000} {4,4} = {2,11:###0.000000} {3,4}/{4,4}", currentVolumeRate.Price, currentVolumeRate.Volume, currentVolumeRate.UnityPrice, BasicSetup.BaseSymbol, BasicSetup.MarketSymbol); Log("Current dT(0) - dT(1): {0,8:###0.0000} % / min Current dT(center): {1,8:###0.0000} %", diffDT01, currentValue.dT.CurrentValue); Log("Current Order Volume: {0,11:###0.000000} {2,4} ( Minimum Order Volume: {1,11:###0.000000} {2,4} )", currentOrderVolume, BotMarket.MinimumTradeVolume, BasicSetup.MarketSymbol); Log("Current Order Volume rates: {0,11:###0.000000} {2,4} [ BUY ] / {1,11:###0.000000} {2,4} [ SELL ]",currentBuyRate,currentSellRate,BasicSetup.BaseSymbol); Log("Current Order Margins: {0,11:###0.000000} {2,4} [ BUY ] / {1,11:###0.000000} {2,4} [ SELL ]", currentBuyMargin, currentSellMargin, BasicSetup.BaseSymbol); Log("{0}", this.detector.ToString()); if (BasicSetup.OrderFixedVolume != 0){ if (!currentOrderID.IsNull()){ cancelOrder(currentOrderID); } else { double orderVolume = Math.Abs(BasicSetup.OrderFixedVolume); OrderType orderType = BasicSetup.OrderFixedVolume > 0 ? OrderType.BUY : OrderType.SELL; double orderLimit = orderType == OrderType.BUY ? orderBook.CurrentAsk : orderBook.CurrentBid; if (orderVolume >= BotMarket.MinimumTradeVolume) { Log("Creating fixed volume Order: {2,4} {0,11:###0.000000} {1,4}", orderVolume, BasicSetup.MarketSymbol, orderType); Order o = createLimitOrder(orderType, BasicSetup.MarketSymbol, BasicSetup.BaseSymbol, orderVolume, orderLimit); if (o != null){ currentOrderID = o.OrderID; } BasicSetup.OrderFixedVolume = 0; Save(); } else { Log("Fixed volume too low for minimum Trade Size of {0,11:###0.000000} {1,4}", BotMarket.MinimumTradeVolume, BasicSetup.MarketSymbol); } } } else { if (!currentOrderID.IsNull()) { cancelOrder(currentOrderID); /* } else if ( (this.GlidingHistories[2].dT.LastValue > 0) && (this.GlidingHistories[2].dT.CurrentValue <= 0) && (this.GlidingHistories[0].centerRate.CurrentValue < this.GlidingHistories[2].centerRate.CurrentValue) && (currentSellRate >= currentSellMargin) ) {*/ } else if (this.detector.HighDetected && (currentSellRate >= currentSellMargin)) { // Sell Signal Order o = createLimitOrder(OrderType.SELL, BasicSetup.MarketSymbol, BasicSetup.BaseSymbol, currentOrderVolume, currentSellRate); if (o != null) { currentOrderID = o.OrderID; lastOrderLimit = currentSellRate; } /* } else if ( (this.GlidingHistories[2].dT.LastValue < 0) && (this.GlidingHistories[2].dT.CurrentValue >= 0) && (this.GlidingHistories[0].centerRate.CurrentValue >= this.GlidingHistories[2].centerRate.CurrentValue) && (currentBuyRate <= currentBuyMargin) )*/ } else if (this.detector.LowDetected && (currentBuyRate <= currentBuyMargin)) { // Buy Signal Order o = createLimitOrder(OrderType.BUY, BasicSetup.MarketSymbol, BasicSetup.BaseSymbol, currentOrderVolume, currentBuyRate); if (o != null) { currentOrderID = o.OrderID; lastOrderLimit = currentBuyRate; } } /* double orderVolume = Math.Abs(currentOrderVolume); OrderType orderType = currentOrderVolume < 0 ? OrderType.SELL : OrderType.BUY; if (orderVolume >= BotMarket.MinimumTradeVolume) { double margin = (orderType == OrderType.BUY) ? BasicSetup.MarginBuy : BasicSetup.MarginSell; double orderMargin = (orderType == OrderType.BUY) ? (currentVolumeRate.UnityPrice / currentBuyRate) - 1.0 : (currentSellRate / currentVolumeRate.UnityPrice) - 1.0; double orderLimit = (orderType == OrderType.BUY) ? currentBuyRate : currentSellRate; if (orderMargin > margin){ Log("Order Margin reached"); if (currentOrderID.IsNull()) { if ( ((orderType == OrderType.BUY) && (diffDT01 >= 0) && (this.currentValue.dT.CurrentValue > -0.0025)) || ((orderType == OrderType.SELL) && (diffDT01 <= 0) && (this.currentValue.dT.CurrentValue < 0.0025)) ) { Order order = createLimitOrder(orderType, BasicSetup.MarketSymbol, BasicSetup.BaseSymbol, orderVolume, orderLimit); if (!order.IsNull()){ currentOrderID = order.OrderID; targetVolume = 1.0; } } else { Log("waiting for dt..."); } } else { cancelOrder(currentOrderID); } } } */ } DumpBalances(); } } public override void Balancing() { if (!this.currentOrderID.IsNull()){ Order currentOrder = getOrder(currentOrderID); if (!currentOrder.IsOpen) { if (currentOrder.OrderType == OrderType.BUY){ this.currentVolumeRate.Volume += currentOrder.FilledVolume; this.currentVolumeRate.Price += currentOrder.PayedPrice + currentOrder.PayedFees; } else { this.currentVolumeRate.Volume -= currentOrder.FilledVolume; this.currentVolumeRate.Price -= currentOrder.PayedPrice - currentOrder.PayedFees; } currentOrderID = null; } } base.Balancing(); } private void writeHistory(double currentCenterRate){ historyWriter.Write("{0}\t{1}",DateTime.Now.ToString(),currentCenterRate.ToString(CultureInfo.InvariantCulture)); /* for (int n = 0; n < this.GlidingCenterRates.Length;n++){ historyWriter.Write("\t{0}\t{1}\t{2}", GlidingCenterRates[n].CurrentValue.ToString(CultureInfo.InvariantCulture), this.dT[n].ToString(CultureInfo.InvariantCulture), this.dTdT[n].ToString(CultureInfo.InvariantCulture) ); } */ foreach (GlidingHistory gh in this.GlidingHistories) { historyWriter.Write("\t{0}\t{1}\t{2}", gh.centerRate.CurrentValue.ToString(CultureInfo.InvariantCulture), gh.dT.CurrentValue.ToString(CultureInfo.InvariantCulture), gh.dTdT.CurrentValue.ToString(CultureInfo.InvariantCulture) ); } historyWriter.Write("\t{0}\t{1}\t{2}", currentValue.centerRate.CurrentValue.ToString(CultureInfo.InvariantCulture), currentValue.dT.CurrentValue.ToString(CultureInfo.InvariantCulture), currentValue.dTdT.CurrentValue.ToString(CultureInfo.InvariantCulture) ); historyWriter.Write("\t{0}", targetVolume.ToString(CultureInfo.InvariantCulture)); historyWriter.WriteLine(); historyWriter.Flush(); } public enum RiseFall { NONE, DETECT, RISING, FALLING }; private class SimpleHighLowDetector { public RiseFall Currently { get; private set; } = RiseFall.NONE; public double Margin { get; set; } = 0.005; public double CurrentExtremum { get; private set; } public bool LowDetected { get; private set; } public bool HighDetected { get; private set; } public double LastLow { get; private set; } public double LastHigh { get; private set; } public void Add(double value){ LowDetected = false; HighDetected = false; if (Currently == RiseFall.NONE) { CurrentExtremum = value; Currently = RiseFall.DETECT; } else if (Currently == RiseFall.DETECT) { if (value > (CurrentExtremum * (1 + Margin))){ Currently = RiseFall.RISING; CurrentExtremum = value; } else if (value < (CurrentExtremum * ( 1 - Margin))){ Currently = RiseFall.FALLING; CurrentExtremum = value; } } else { if (Currently == RiseFall.FALLING) { if (value < CurrentExtremum){ CurrentExtremum = value; } else if (value > ((1.0 + Margin) * CurrentExtremum)){ Currently = RiseFall.RISING; LowDetected = true; LastLow = CurrentExtremum; } } else if (Currently == RiseFall.RISING) { if (value > CurrentExtremum){ CurrentExtremum = value; } else if (value < ((1.0 - Margin) * CurrentExtremum)){ Currently = RiseFall.FALLING; HighDetected = true; LastHigh = CurrentExtremum; } } } } public override string ToString() { return string.Format("[SimpleHighLowDetector: Currently={0}, Margin={1}, CurrentExtremum={2}, LowDetected={3}, HighDetected={4}]", Currently, Margin, CurrentExtremum, LowDetected, HighDetected); } } private class GlidingHistory { public SmoothValue centerRate; public SmoothValue dT; public SmoothValue dTdT; public GlidingHistory() : this(0.25) { } public GlidingHistory(double k){ this.centerRate = new SmoothValue(k); this.dT = new SmoothValue(k); this.dTdT = new SmoothValue(k); } public Double Kp { get { return this.centerRate.Kp; } set { this.centerRate.Kp = value; this.dT.Kp = value; this.dTdT.Kp = value; } } public void Add(double centerRateValue){ if (centerRate.CurrentValue == 0){ this.centerRate.Set(centerRateValue); } this.centerRate.Add(centerRateValue); this.dT.Add(this.centerRate.dT); this.dTdT.Add(this.dT.dT); } } } public class DifferentialSetup : BasicBotSetup { public double MarketCheckVolume = 1; public double OrderFixedVolume; public bool IncreaseOrderVolumeToMinimum; public bool ResetTargetVolume; public double MarginBuy = 0.02; public double MarginSell = 0.02; public double RelativeTradeVolume = 0.2; } }