commit 7c28bbbc60c4cc7a19cde17c8942932a3c7b6efa Author: Harald Wolff Date: Thu Nov 23 13:08:10 2017 +0100 WIP diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4e82d27 --- /dev/null +++ b/.gitignore @@ -0,0 +1,40 @@ +# Autosave files +*~ + +# build +[Oo]bj/ +[Bb]in/ +packages/ +TestResults/ + +# globs +Makefile.in +*.DS_Store +*.sln.cache +*.suo +*.cache +*.pidb +*.userprefs +*.usertasks +config.log +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.user +*.tar.gz +tarballs/ +test-results/ +Thumbs.db + +# Mac bundle stuff +*.dmg +*.app + +# resharper +*_Resharper.* +*.Resharper + +# dotCover +*.dotCover diff --git a/BigBot.cs b/BigBot.cs new file mode 100644 index 0000000..df0923d --- /dev/null +++ b/BigBot.cs @@ -0,0 +1,255 @@ +using System; +using sharp.tradebot; +using sharp.json; +using sharp.trading; +using System.IO; +using sharp.extensions; +using sharp.json.attributes; +using System.Globalization; +namespace BigBot +{ + [JSONClassPolicy( Policy = JSONPolicy.ATTRIBUTED)] + public class BigBot : TradingBot + { + FileBackedJSONValue Setup; + + public BigBot() + { + } + + sharp.tradebot.TradeBotBalance balanceBase; + sharp.tradebot.TradeBotBalance balanceMarket; + + [JSONField] + public double CurrentMarketCosts; + + [JSONField] + double lastCenterRate; + [JSONField] + double lastDT; + + [JSONField] + public SmoothValue[] Gliders; + [JSONField] + public SmoothValue[] dT; + [JSONField] + public SmoothValue[] dTdT; + + [JSONField] + public string currentOrderID; + + + + + public override void Prepare() + { + base.Prepare(); + + Setup = new FileBackedJSONValue(Path.Combine(BaseDataPath, "setup.json")); + if (Setup.CurrentValue == null){ + Setup.CurrentValue = new BigBotSetup(); + Setup.Save(); + } + + if (this.dT.IsNull()) + { + initialize_dT(); + } + if (this.dTdT.IsNull()) + { + initialize_dTdT(); + } + + + TradingBotEnvironment.RegisterPeriodic(Worker,15); + Log("Prepared"); + } + + public override void Save(){ + Setup.Save(); + base.Save(); + } + + public override void Unprepare() + { + TradingBotEnvironment.UnregisterPeriodic(Worker); + + Save(); + + Log("Unprepared"); + + base.Unprepare(); + } + + private bool Check(){ + balanceBase = getBalance(Setup.CurrentValue.BaseSymbol); + balanceMarket = getBalance(Setup.CurrentValue.MarketSymbol); + + if (balanceBase.IsNull() || balanceMarket.IsNull()){ + Log("check failed: balances not available"); + return false; + } + + return true; + } + + private void CheckCurrentOrder(){ + if (!currentOrderID.IsNull()){ + Order order = getOrder(currentOrderID); + if (!order.IsNull() && !order.IsOpen){ + balanceBase.CurrentBalance -= order.PayedFees; + CurrentMarketCosts += order.PayedFees; + + if (order.OrderType == OrderType.BUY){ + CurrentMarketCosts += order.PayedPrice; + balanceBase.CurrentBalance -= order.PayedPrice; + balanceMarket.CurrentBalance += order.FilledVolume; + } else { + CurrentMarketCosts -= order.PayedPrice; + balanceBase.CurrentBalance += order.PayedPrice; + balanceMarket.CurrentBalance -= order.FilledVolume; + } + } + } + } + + private void Worker() + { + if (Setup.CurrentValue.Enabled && Check()) + { + Log("BigBot: Worker() called and enabled!"); + Log("Next run..."); + + Market market = TradingBotEnvironment.TradingConnection.openMarket(Setup.CurrentValue.MarketSymbol, Setup.CurrentValue.BaseSymbol); + OrderBook orderBook = market.getOrderBook(); + orderBook.Refresh(); + System.Tuple currentCheck = orderBook.getVolumePrice(Setup.CurrentValue.MarketCheckVolume); + double centerPrice = (currentCheck.Item1 + currentCheck.Item2) / 2; + double centerRate = centerPrice / Setup.CurrentValue.MarketCheckVolume; + + if (this.Gliders.IsNull()) + { + initializeGliders(centerRate); + } + else + { + foreach (SmoothValue glide in this.Gliders) + { + glide.Add(centerRate); + } + } + + double _dT = (((centerRate - lastCenterRate) / centerRate)) * 100.0; + double _dTdT = (_dT - lastDT); + + foreach (SmoothValue dt in this.dT) + { + dt.Add(_dT); + } + foreach (SmoothValue dtdt in this.dTdT) + { + dtdt.Add(_dTdT); + } + + double[] Indicators = new double[Gliders.Length - 1]; + for (int n = 0; n < Indicators.Length; n++) + { + Indicators[n] = 1.0 - (this.Gliders[0].CurrentValue / this.Gliders[n + 1].CurrentValue); + } + + lastCenterRate = centerRate; + lastDT = _dT; + + Log("Current volumized Market Values: Ask: {0,11:####0.00000000} {2} Bid: {1,11:####0.00000000} {2}", currentCheck.Item1, currentCheck.Item2, Setup.CurrentValue.BaseSymbol); + Log("Current volumized Market Spread: {0,6:##0.00}%", ((currentCheck.Item1 / currentCheck.Item2)-1) * 100); + Log("Current volumized Center Rate: {0,11:####0.00000000} {1}", centerRate, Setup.CurrentValue.BaseSymbol); + Log("Current indicators [ 0..3 ]: {0,6:##0.00}% / {1,6:##0.00}% / {2,6:##0.00}% / {3,6:##0.00}%", Indicators[0] * 100,Indicators[1] * 100,Indicators[2] * 100,Indicators[3] * 100); + + WriteHistory(); + + Save(); + + market.Close(); + } + } + + private void initializeGliders(double center) + { + double k = 1; + this.Gliders = new SmoothValue[10]; + for (int n = 0; n < this.Gliders.Length; n++) + { + this.Gliders[n] = new SmoothValue(k); + this.Gliders[n].Set(center); + k /= 10; + } + } + + private void initialize_dT() + { + double k = 1; + this.dT = new SmoothValue[8]; + for (int n = 0; n < this.dT.Length; n++) + { + this.dT[n] = new SmoothValue(k); + k /= 2; + } + } + + private void initialize_dTdT() + { + double k = 1; + this.dTdT = new SmoothValue[8]; + for (int n = 0; n < this.dTdT.Length; n++) + { + this.dTdT[n] = new SmoothValue(k); + k /= 2; + } + } + + public void WriteHistory() + { + FileStream s = new FileStream(Path.Combine(BaseDataPath, "gliders.csv"), FileMode.Append); + StreamWriter w = new StreamWriter(s); + + foreach (SmoothValue glide in this.Gliders) + { + w.Write(glide.CurrentValue.ToString(CultureInfo.InvariantCulture)); + w.Write('\t'); + } + w.WriteLine(); + w.Close(); + + + s = new FileStream(Path.Combine(BaseDataPath, "dt.csv"), FileMode.Append); + w = new StreamWriter(s); + + foreach (SmoothValue dt in this.dT) + { + w.Write(dt.CurrentValue.ToString(CultureInfo.InvariantCulture)); + w.Write('\t'); + } + w.WriteLine(); + w.Close(); + + + s = new FileStream(Path.Combine(BaseDataPath, "dtdt.csv"), FileMode.Append); + w = new StreamWriter(s); + + foreach (SmoothValue dtdt in this.dTdT) + { + w.Write(dtdt.CurrentValue.ToString(CultureInfo.InvariantCulture)); + w.Write('\t'); + } + w.WriteLine(); + w.Close(); + + + + } + } + public class BigBotSetup : BasicBotSetup + { + public double MarketCheckVolume = 1; + } +} diff --git a/BigBot.csproj b/BigBot.csproj new file mode 100644 index 0000000..91d99d8 --- /dev/null +++ b/BigBot.csproj @@ -0,0 +1,63 @@ + + + + Debug + AnyCPU + {690293DB-4E8D-44E6-B0AD-3A4FF66E6042} + Library + BigBot + BigBot + v4.7 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + false + + + true + bin\Release + prompt + 4 + false + + + + + + + + + + + + + + + + + + + {798D4516-84F8-436D-BD7F-17AD288C6776} + sharp.tradebot + + + {CAAC53CC-671C-4B1E-8403-1E53D1D40D66} + sharp.trading + + + {97CA3CA9-98B3-4492-B072-D7A5995B68E9} + sharp.extensions + + + {D9342117-3249-4D8B-87C9-51A50676B158} + sharp.json + + + + \ No newline at end of file diff --git a/DifferentingBot.cs b/DifferentingBot.cs new file mode 100644 index 0000000..5c08cce --- /dev/null +++ b/DifferentingBot.cs @@ -0,0 +1,444 @@ +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; + + } +} diff --git a/HistoryBot.cs b/HistoryBot.cs new file mode 100644 index 0000000..51a4ab2 --- /dev/null +++ b/HistoryBot.cs @@ -0,0 +1,206 @@ +using System; +using sharp.json; +using System.IO; +using sharp.trading; +using sharp.trading.cache; +using sharp.extensions; +namespace sharp.tradebot +{ + public class HistoryBot : TradingBot + { + public FileBackedJSONValue Setup; + public FileBackedJSONValue CurrentOrderID; + + public HistoryBot() + { + initialize(); + } + + private void initialize() + { + Setup = new FileBackedJSONValue(Path.Combine(DataDirectory, "setup.json")); + CurrentOrderID = new FileBackedJSONValue(Path.Combine(DataDirectory, "currentorder.json")); + } + + public override void Prepare() + { + base.Prepare(); + + if (Setup.CurrentValue == null){ + Setup.CurrentValue = new SetupClass(); + } + + } + + public void Trade() + { + TradeBotBalance balanceBase = getBalance(Setup.CurrentValue.BaseSymbol); + TradeBotBalance balanceMarket = getBalance(Setup.CurrentValue.MarketSymbol); + + if (CurrentOrderID.CurrentValue != null){ + Order o = getOrder(CurrentOrderID.CurrentValue); + if (!o.IsOpen){ + if (o.OrderType == OrderType.BUY){ + balanceBase.CurrentBalance -= o.PayedFees + o.PayedPrice; + balanceMarket.CurrentBalance += o.FilledVolume; + } else { + balanceBase.CurrentBalance += o.PayedPrice - o.PayedFees; + balanceMarket.CurrentBalance -= o.FilledVolume; + } + CurrentOrderID.CurrentValue = null; + + Save(); + } + } + + if (Setup.CurrentValue.Enabled){ + Log("Bot enabled"); + + /* Manage Balances */ + if (Setup.CurrentValue.BaseBalanceChange != 0) + { + balanceBase.CurrentBalance += Setup.CurrentValue.BaseBalanceChange; + Setup.CurrentValue.BaseBalanceChange = 0; + Setup.Save(); + } + if (Setup.CurrentValue.MarketBalanceChange != 0) + { + + balanceMarket.CurrentBalance += Setup.CurrentValue.MarketBalanceChange; + Setup.CurrentValue.MarketBalanceChange = 0; + Setup.Save(); + } + + Market market = TradingEnvironment.DefaultConnection.openMarket(Setup.CurrentValue.MarketSymbol, Setup.CurrentValue.BaseSymbol); + HistoryCache cache = HistoryCache.getCache(market); + + double indication = 0; + + WeightedIndicator[] indicators = Setup.CurrentValue.Indicators; + foreach (WeightedIndicator wi in indicators){ + double ind = cache.calulateWeightedAveragesPerVolume(wi.Volume).AveragePrice; + double weight = wi.Weight; + Log("Indicator for Volume {0,10} = {1,11:####0.00000000} with a weight of {2,7:#0.####}",wi.Volume,ind,weight); + indication += ind * wi.Weight; + } + + Log("Indicator Sum: {0,11:####0.00000000}", indication); + + OrderBook orderBook = market.getOrderBook(); + + System.Tuple bidask = orderBook.getVolumePrice(5.0); + + Log("Current Small-Volumes: {0} / {1}",bidask.Item1,bidask.Item2); + + double smallbid = bidask.Item2 / 5; + double smallask = bidask.Item1 / 5; + + Log("Current Small Ask: {0} Bid: {1}", smallask, smallbid); + + double smallsum = (smallbid + smallask) / 2; + Log("Current Small-Indicator: {0}", smallsum); + + double final = indication / (smallsum + indication); + + Log("Current Final Indication: {0}", final); + + double marketBalanceAsBase = orderBook.getVolumePrice(balanceMarket.CurrentBalance).Item2; + + double balancesum = (balanceBase.CurrentBalance + marketBalanceAsBase); + double currentRatio = marketBalanceAsBase / balancesum; + + Log("Current Balance Values: {0,11:####0.00000000} {1} / {2,11:####0.00000000} {1} = {3:#0.0000}", balanceBase.CurrentBalance, balanceBase.Currency, marketBalanceAsBase, currentRatio); + Log("Current Balance Sum: {0,11:####0.00000000} {1}", balancesum, balanceBase.Currency); + + double targetMarket = balancesum / (1 + final); + double targetBase = balancesum - targetMarket; + + Log("Target Balance Values: {0,11:####0.00000000} {2} / {1,11:####0.00000000} {2}", targetBase, targetMarket, balanceBase.Currency); + + double diff = currentRatio - final; + diff = Math.Abs(diff); + + Log("Current Indicator Diff: {0}", diff); + + if (diff > Setup.CurrentValue.DiffMargin){ + if (CurrentOrderID.CurrentValue != null) + { + Log("Canceling old Order before creating new one."); + TradingEnvironment.DefaultConnection.cancelOrder(CurrentOrderID.CurrentValue); + } + else + { + double deltaMarket = targetMarket - marketBalanceAsBase; + OrderType otype; + double orderVolume; + double limit; + + if (deltaMarket > 0) + { + // BUY + otype = OrderType.BUY; + orderVolume = deltaMarket / orderBook.CurrentAsk; + limit = orderBook.CurrentAsk; + } + else + { + // SELL + otype = OrderType.SELL; + orderVolume = -deltaMarket / orderBook.CurrentBid; + limit = orderBook.CurrentBid; + } + + try { + //Order o = createLimitOrder(otype, Setup.CurrentValue.MarketSymbol, Setup.CurrentValue.BaseSymbol, orderVolume, limit); + //CurrentOrderID.CurrentValue = o.OrderID; + Log("Created balancing Order {0}", CurrentOrderID.CurrentValue); + } catch (Exception e){ + Log("Exception creating Order: {0}", e.ToString()); + } + + } + + } + + DumpBalances(); + } + } + + public override void Save() + { + Setup.Save(); + base.Save(); + } + + public override int SchedulingIntervall() + { + return 10; + } + + + + public class WeightedIndicator{ + public double Volume; + public double Weight; + } + + public class SetupClass { + public bool Enabled; + + public string MarketSymbol = ""; + public string BaseSymbol = ""; + + public double BaseBalanceChange; + public double MarketBalanceChange; + + public double DiffMargin = 0.1; + + public WeightedIndicator[] Indicators = new WeightedIndicator[]{ + new WeightedIndicator(), + new WeightedIndicator() + }; + + } + + } +} diff --git a/MarketValueBot.cs b/MarketValueBot.cs new file mode 100644 index 0000000..8e0a867 --- /dev/null +++ b/MarketValueBot.cs @@ -0,0 +1,178 @@ +using System; +using sharp.json; +using System.IO; +using sharp.trading; +using sharp.trading.cache; +using sharp.extensions; +using sharp.tradebot; + +namespace BigBot +{ + public class MarketValueBot : TradingBot + { + public FileBackedJSONValue Setup; + public FileBackedJSONValue CurrentOrderID; + + public MarketValueBot() + { + initialize(); + } + + private void initialize() + { + Setup = new FileBackedJSONValue(Path.Combine(DataDirectory, "setup.json")); + CurrentOrderID = new FileBackedJSONValue(Path.Combine(DataDirectory, "currentorder.json")); + } + + public override void Prepare() + { + base.Prepare(); + + if (Setup.CurrentValue == null){ + Setup.CurrentValue = new SetupClass(); + } + + } + + public void Trade() + { + sharp.tradebot.TradeBotBalance balanceBase = getBalance(Setup.CurrentValue.BaseSymbol); + sharp.tradebot.TradeBotBalance balanceMarket = getBalance(Setup.CurrentValue.MarketSymbol); + + if (CurrentOrderID.CurrentValue != null){ + + Order o = getOrder(CurrentOrderID.CurrentValue); + if (o.IsOpen) + { + TradingEnvironment.DefaultConnection.refreshOrder(o); + } + if (!o.IsOpen){ + if (o.OrderType == OrderType.BUY){ + balanceBase.CurrentBalance -= o.PayedFees + o.PayedPrice; + balanceMarket.CurrentBalance += o.FilledVolume; + } else { + balanceBase.CurrentBalance += o.PayedPrice - o.PayedFees; + balanceMarket.CurrentBalance -= o.FilledVolume; + } + + Log("Order has been closed: {0}", o.ToString()); + + CurrentOrderID.CurrentValue = null; + + Save(); + } + } + + if (Setup.CurrentValue.Enabled){ + Log("Bot enabled"); + + /* Manage Balances */ + if (Setup.CurrentValue.BaseBalanceChange != 0) + { + balanceBase.CurrentBalance += Setup.CurrentValue.BaseBalanceChange; + Setup.CurrentValue.BaseBalanceChange = 0; + Setup.Save(); + } + if (Setup.CurrentValue.MarketBalanceChange != 0) + { + + balanceMarket.CurrentBalance += Setup.CurrentValue.MarketBalanceChange; + Setup.CurrentValue.MarketBalanceChange = 0; + Setup.Save(); + } + + Market market = TradingEnvironment.DefaultConnection.openMarket(Setup.CurrentValue.MarketSymbol, Setup.CurrentValue.BaseSymbol); + OrderBook orderBook = market.getOrderBook(); + System.Tuple currentValues = orderBook.getVolumePrice(balanceMarket.CurrentBalance); + + Log("Current Market Values: REBUY: {0,11:####0.00000000} {2} SELL: {1,11:####0.00000000} {2}", currentValues.Item1, currentValues.Item2, Setup.CurrentValue.BaseSymbol); + Log("Current Market Spread: {0,6:##0.00}%", ((currentValues.Item1 / currentValues.Item2)-1)*100); + + double currentAbsDiff = Setup.CurrentValue.TargetValue - currentValues.Item2; + double currentDiff = currentAbsDiff / Setup.CurrentValue.TargetValue; + + Log("Current Diff: {0,8:##0.0000} {1} [ {2,6:##0.00}% ] Limit: {3,6:##0.00}% / {4,6:##0.00}%", currentAbsDiff, Setup.CurrentValue.BaseSymbol, currentDiff * 100, Setup.CurrentValue.DiffMargin * 100, Setup.CurrentValue.DiffMarginBuy * 100); + + if ((currentDiff > Setup.CurrentValue.DiffMarginBuy) || (-currentDiff > Setup.CurrentValue.DiffMargin)) + { + if (CurrentOrderID.CurrentValue != null) + { + Log("Canceling old Order before creating new one."); + TradingEnvironment.DefaultConnection.cancelOrder(CurrentOrderID.CurrentValue); + } else + { + double diffMarket = currentAbsDiff / orderBook.CurrentBid; + + if (diffMarket > 0){ + // Buy + double window = orderBook.getVolumePrice(diffMarket).Item1 - orderBook.getVolumePrice(diffMarket).Item2; + double price = orderBook.getVolumePrice(diffMarket).Item1 + (Setup.CurrentValue.WindowLevelToBuy * window); + + if (price >= balanceBase.CurrentBalance){ + diffMarket *= balanceBase.CurrentBalance / price; + price = orderBook.getVolumePrice(diffMarket).Item1; + } + Log("Will buy {0,11:####0.00000000} {1} @ {2,11:####0.00000000} {3} = {4,11:####0.00000000} {3}", diffMarket, Setup.CurrentValue.MarketSymbol, orderBook.CurrentAsk, Setup.CurrentValue.BaseSymbol, price); + Order order = createLimitOrder(OrderType.BUY, Setup.CurrentValue.MarketSymbol, Setup.CurrentValue.BaseSymbol, diffMarket, orderBook.CurrentAsk); + CurrentOrderID.CurrentValue = order.OrderID; + } else { + // SELL + diffMarket = -diffMarket; + double window = orderBook.getVolumePrice(diffMarket).Item1 - orderBook.getVolumePrice(diffMarket).Item2; + double price = orderBook.getVolumePrice(diffMarket).Item1 + (Setup.CurrentValue.WindowLevelToBuy * window); + + Log("Will sell {0,11:####0.00000000} {1} @ {2,11:####0.00000000} {3} = {4,11:####0.00000000} {3}", diffMarket, Setup.CurrentValue.MarketSymbol, orderBook.CurrentAsk, Setup.CurrentValue.BaseSymbol, price); + Order order = createLimitOrder(OrderType.SELL, Setup.CurrentValue.MarketSymbol, Setup.CurrentValue.BaseSymbol, diffMarket, orderBook.CurrentBid); + CurrentOrderID.CurrentValue = order.OrderID; + } + + } + + } + + DumpBalances(); + + Log("--------------------------------------------------"); + Log("Current Total: {0,11:####0.00000000} {1} ", balanceBase.CurrentBalance + currentValues.Item2, balanceBase.Currency); + Log("=================================================="); + + } + } + + public override void Save() + { + Setup.Save(); + base.Save(); + } + + public override int SchedulingIntervall() + { + return 15; + } + + + + public class WeightedIndicator{ + public double Volume; + public double Weight; + } + + public class SetupClass { + public bool Enabled; + + public string MarketSymbol = ""; + public string BaseSymbol = ""; + + public double BaseBalanceChange; + public double MarketBalanceChange; + + public double TargetValue = 0; + public double DiffMargin = 0.02; + public double DiffMarginBuy = 0.03; + + public double WindowLevelToBuy = 0.1; + public double WindowLevelToSell = 0.8; + } + + } +} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..84a386e --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("BigBot")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] diff --git a/SimpleBot.cs b/SimpleBot.cs new file mode 100644 index 0000000..2637f83 --- /dev/null +++ b/SimpleBot.cs @@ -0,0 +1,260 @@ +using System; +using System.Net.Sockets; +using sharp.trading; +namespace sharp.tradebot +{ + public class SimpleBot : TradingBot + { + public SimpleBotSetup Setup { get; private set; } = new SimpleBotSetup(); + + TradeStep[] TradeSteps; + Market market; + + public SimpleBot() + { + } + + + public override void Prepare() + { + base.Prepare(); + + Setup = loadJSON("setup.json"); + TradeSteps = loadJSON("tradesteps.json"); + + checkTradeSteps(); + } + + void checkTradeSteps(){ + if (TradeSteps.Length == 0){ + TradeSteps = new TradeStep[Setup.HalfSteps*2]; + for (int n = 0; n < TradeSteps.Length; n++){ + TradeSteps[n] = new TradeStep(); + TradeSteps[n].price_buy = Setup.CenterPrice * ((1.0 - Setup.HalfSpread) + ((double)n * Setup.HalfSpread / (Setup.HalfSteps))); + TradeSteps[n].price_sell = TradeSteps[n].price_buy * (1.0 + Setup.Offset); + TradeSteps[n].current_base_volume = StepBaseVolume(); + TradeSteps[n].current_market_volume = StepMarketVolume(); + } + } + } + + double StepBaseVolume() + { + return Setup.ActiveBaseBalance / (2 * Setup.HalfSteps); + } + double StepMarketVolume() + { + return Setup.ActiveMarketBalance / (2 * Setup.HalfSteps); + } + + public bool IsRunnable(){ + if (Setup.marketSymbol == null || Setup.marketSymbol.Equals("")) + return false; + if (Setup.baseSymbol == null || Setup.baseSymbol.Equals("")) + return false; + + return true; + } + + public void Trade() + { + if (IsRunnable()){ + market = TradingEnvironment.DefaultConnection.openMarket(Setup.marketSymbol, Setup.baseSymbol); + } + + if (Setup.Enabled && IsRunnable()){ + + Log("SimpleBot is trading"); + + checkOrders(); + + } else if (IsRunnable()){ + Console.WriteLine("SimpleBot instance is not enabled: {0}",this.UID); + + Market market = TradingEnvironment.DefaultConnection.openMarket(Setup.marketSymbol, Setup.baseSymbol); + OrderBook orderbook = market.getOrderBook(); + + Console.WriteLine("Current Minimum Tradesize is {0:#0.00000000} {1}",market.MinimumTradeVolume,market.TradedSymbol); + Console.WriteLine("Current Best Bid: {0:#0.00000000} {1} Current best Ask: {2:#0.00000000} {3} , so at the Moment I would...",orderbook.CurrentBid,Setup.baseSymbol,orderbook.CurrentAsk,Setup.baseSymbol); + + foreach (TradeStep step in TradeSteps) + { + if (step.price_buy < orderbook.CurrentAsk) + { + Console.WriteLine("Buy {0,8:##0.0000} {1} @ {2,11:##0.000000} {3} := {4,11:##0.000000} {5}", step.volume_to_buy(step.current_base_volume), Setup.marketSymbol, step.price_buy, Setup.baseSymbol, step.volume_to_buy(step.current_base_volume) * step.price_buy, Setup.baseSymbol); + } + } + foreach (TradeStep step in TradeSteps) + { + if (step.price_sell > orderbook.CurrentBid) + { + + Console.WriteLine("Sell {0,8:##0.0000} {1} @ {2,11:##0.000000} {3} := {4,11:##0.000000} {5}", step.volume_to_sell(step.current_market_volume * Setup.CenterPrice), Setup.marketSymbol, step.price_sell, Setup.baseSymbol, step.volume_to_sell(step.current_market_volume * Setup.CenterPrice) * step.price_sell, Setup.baseSymbol); + } + } + } else { + Console.WriteLine("SimpleBot instance is not enabled and not ready to run: {0}",this.UID); + } + + } + + void Sell(TradeStep step){ + if (!Setup.CancelAllOrders) + { + double volsell = step.volume_to_sell(step.current_market_volume * Setup.CenterPrice); + + Order sell = createLimitOrder(OrderType.SELL, Setup.marketSymbol, Setup.baseSymbol, volsell, step.price_sell); + step.current_sell_order_id = sell.OrderID; + } + } + + void Buy(TradeStep step) + { + if (!Setup.CancelAllOrders) + { + double volbuy = step.volume_to_buy(step.current_base_volume); + + Order buyorder = createLimitOrder(OrderType.BUY, Setup.marketSymbol, Setup.baseSymbol, volbuy, step.price_buy); + step.current_buy_order_id = buyorder.OrderID; + } + } + + + void checkOrders() + { + foreach (TradeStep step in TradeSteps) + { + switch (step.getState()) + { + case StepState.BUYING: + { + Order order = getOrder(step.current_buy_order_id); + + if (!order.IsOpen) + { + double mRes = order.FilledVolume * (Setup.Offset - Setup.Fee) * Setup.Reserve; + + Setup.ActiveBaseBalance -= order.PayedPrice; + Setup.ActiveBaseBalance -= order.PayedFees; + Setup.ActiveMarketBalance += order.FilledVolume - mRes; + Setup.ReservedMarketBalance += mRes; + step.current_buy_order_id = null; + + Log("BUYED: {0} {1} @ {2} {3} Cost: {4} {5}", order.FilledVolume, Setup.marketSymbol, order.LimitPrice, Setup.baseSymbol, order.PayedPrice + order.PayedFees, Setup.baseSymbol); + + Sell(step); + } else if (Setup.CancelAllOrders){ + TradingEnvironment.DefaultConnection.cancelOrder(order); + } + } + break; + case StepState.SELLING: + { + Order order = getOrder(step.current_sell_order_id); + + if (!order.IsOpen) + { + double bRes = order.PayedPrice * (Setup.Offset - Setup.Fee) * Setup.Reserve; + + Setup.ActiveBaseBalance += order.PayedPrice - bRes; + Setup.ActiveBaseBalance -= order.PayedFees; + Setup.ReservedBaseBalance += bRes; + Setup.ActiveMarketBalance -= order.FilledVolume; + step.current_sell_order_id = null; + + Log("SOLD: {0} {1} @ {2} {3} Cost: {4} {5}", order.FilledVolume, Setup.marketSymbol, order.LimitPrice, Setup.baseSymbol, order.PayedPrice + order.PayedFees, Setup.baseSymbol); + + Buy(step); + } else if (Setup.CancelAllOrders){ + TradingEnvironment.DefaultConnection.cancelOrder(order); + } + } + break; + case StepState.NONE: + if (step.price_buy < market.getOrderBook().CurrentAsk) + { + Buy(step); + } + else if (step.price_sell > market.getOrderBook().CurrentBid) + { + Sell(step); + } + break; + } + } + } + + + public override void Save() + { + if (Setup.Enabled) + { + saveJSON(Setup,"setup.json"); + saveJSON(TradeSteps, "tradesteps.json"); + } + + base.Save(); + } + + + public class SimpleBotSetup { + public bool Enabled = false; + public bool CancelAllOrders = false; + + public string marketSymbol = ""; + public string baseSymbol = ""; + + public double CenterPrice; + public double HalfSpread = 0.20; + public double Offset = 0.0125; + public int HalfSteps = 40; + + public double ActiveMarketBalance = 0; + public double ReservedMarketBalance = 0; + public double ActiveBaseBalance = 0; + public double ReservedBaseBalance = 0; + + public double Reserve = 0.5; + public double Fee = 0.0025; + } + + public enum StepState { + NONE, SELLING, BUYING + } + + public class TradeStep { + public double price_buy; + public double price_sell; + + public double current_base_volume; + public double current_market_volume; + + public string current_buy_order_id; + public string current_sell_order_id; + + public double volume_to_buy(double base_volume){ + return Math.Round(base_volume / price_buy, 4); + } + public double volume_to_sell(double base_volume) + { + return Math.Round(base_volume / price_sell, 4); + } + + public StepState getState(){ + if (current_buy_order_id != null){ + return StepState.BUYING; + } + if (current_sell_order_id != null){ + return StepState.SELLING; + } + return StepState.NONE; + } + + public override string ToString() + { + return string.Format("[TradeStep buy={0,11:##.000000} sell={0,11:##.000000}]",price_buy,price_sell); + } + } + + } +} diff --git a/SmoothValue.cs b/SmoothValue.cs new file mode 100644 index 0000000..f1d555f --- /dev/null +++ b/SmoothValue.cs @@ -0,0 +1,60 @@ +using System; +using sharp.json.attributes; +namespace BigBot +{ + [JSONClassPolicy( Policy = JSONPolicy.ATTRIBUTED)] + public class SmoothValue + { + [JSONField(Alias = "last")] + public double LastValue { get; private set; } + + [JSONField( Alias = "current")] + public double CurrentValue { get; private set; } + + [JSONField(Alias = "Kp")] + public double Kp { get; set; } + + [JSONField(Alias = "sum")] + private double sum; + + public SmoothValue() + { + } + public SmoothValue(double Kp) + { + this.Kp = Kp; + } + + public void Clear() + { + this.sum = 0; + } + + public void Set(double value){ + this.sum = (value / this.Kp) - value; + this.CurrentValue = value; + this.LastValue = value; + } + + public void Add(double value) + { + LastValue = CurrentValue; + + this.sum += value; + CurrentValue = this.sum * this.Kp; + this.sum -= CurrentValue; + } + + public double dT { + get { + return this.CurrentValue - this.LastValue; + } + } + + public static implicit operator double(SmoothValue svalue){ + return svalue.CurrentValue; + } + + + } +} diff --git a/SpreadBot.cs b/SpreadBot.cs new file mode 100644 index 0000000..1fc990d --- /dev/null +++ b/SpreadBot.cs @@ -0,0 +1,20 @@ +using System; +namespace sharp.tradebot +{ + public class SpreadBot: TradingBot + { + public SpreadBot() + { + } + + public override int SchedulingIntervall() + { + return 30; + } + + + public class Setup { + + } + } +} diff --git a/StatisticalBot.cs b/StatisticalBot.cs new file mode 100644 index 0000000..ad41627 --- /dev/null +++ b/StatisticalBot.cs @@ -0,0 +1,68 @@ +using System; +using sharp.json; +using sharp.trading; +using System.IO; +namespace sharp.tradebot +{ + public class StatisticalBot : TradingBot + { + FileBackedJSONValue Setup; + + public StatisticalBot() + { + initialize(); + } + + private void initialize() + { + Setup = new FileBackedJSONValue(Path.Combine(DataDirectory, "setup.json")); + } + + + public override void Prepare() + { + base.Prepare(); + + if (Setup.CurrentValue == null){ + Setup.CurrentValue = new BotSetup(); + Setup.Save(); + } + } + + public void Trade() + { + if (Setup.CurrentValue.Enabled) + { + Market market = TradingEnvironment.DefaultConnection.openMarket(Setup.CurrentValue.MarketSymbol, Setup.CurrentValue.BaseSymbol); + OrderBook orderbook = market.getOrderBook(); + + OrderbookVolume[] obvolumes = orderbook.calculateVolumes(Setup.CurrentValue.OrderBookIndicationVolumina); + Log("Current Orderbook Indications:"); + foreach (OrderbookVolume ovol in obvolumes) + { + Log("Volume: {0,15:#########0.0000} {1} BUY: {2,11:####0.00000000} {3} SELL: {4,11:####0.00000000} {3}", ovol.VolumeBuy, Setup.CurrentValue.MarketSymbol, ovol.PriceBuy, Setup.CurrentValue.BaseSymbol, ovol.PriceSell); + Log(" RATIO: {0,11:####0.00000000} AVERAGE: {1,11:####0.00000000} {2}", ovol.PriceBuy / ovol.PriceSell, (ovol.AverageUnitPriceBuy + ovol.AverageUnitPriceSell) / 2, Setup.CurrentValue.BaseSymbol); + } + + } + + } + + public override int SchedulingIntervall() + { + return 60; + } + + + class BotSetup { + public bool Enabled; + + public string MarketSymbol = ""; + public string BaseSymbol = ""; + + public double[] OrderBookIndicationVolumina = new double[] { 10, 25, 50, 100, 200, 400, 800, 1600, 3200 }; + + } + + } +} diff --git a/TargetValueBot.cs b/TargetValueBot.cs new file mode 100644 index 0000000..7d5bcf0 --- /dev/null +++ b/TargetValueBot.cs @@ -0,0 +1,151 @@ +using System; +using sharp.json; +using System.IO; +using sharp.trading; +using sharp.trading.cache; +using sharp.extensions; +using sharp.json.attributes; +using sharp.tradebot; + +namespace BigBot +{ + [JSONClassPolicy(Policy = JSONPolicy.ATTRIBUTED)] + public class TargetValueBot : TradingBot + { + [JSONField] + public string CurrentOrderID; + + public override void Prepare() + { + base.Prepare(); + + TradingBotEnvironment.RegisterPeriodic(Trade,10); + } + + public override void Unprepare() + { + TradingBotEnvironment.UnregisterPeriodic(Trade); + + base.Unprepare(); + } + + + public void Trade() + { + TradeBotBalance balanceBase = getBalance(BasicSetup.BaseSymbol); + TradeBotBalance balanceMarket = getBalance(BasicSetup.MarketSymbol); + + if (BasicSetup.Enabled){ + Log("TargetValueBot V2 is trading..."); + OrderBook orderBook = BotMarket.getOrderBook(); + + double testVolume = balanceMarket.CurrentBalance; + if (testVolume == 0){ + testVolume = 1; + } + + System.Tuple currentValues = orderBook.getVolumePrice(testVolume); + + // Log("Current Market Values: REBUY: {0,11:####0.00000000} {2} SELL: {1,11:####0.00000000} {2}", currentValues.Item1, currentValues.Item2, BasicSetup.BaseSymbol); + //1 Log("Current Market Spread: {0,6:##0.00}%", ((currentValues.Item1 / currentValues.Item2)-1)*100); + double currentMarketValue = currentValues.Item2; + + double currentUnitValue = currentMarketValue / testVolume; + double alpha = BasicSetup.TargetPrice / currentUnitValue; + double beta = Math.Pow(alpha, BasicSetup.TargetExponent); + + double finalTargetValue = BasicSetup.TargetValue * beta; + + if (balanceMarket.CurrentBalance <= 0){ + currentMarketValue = 0; + } + + + double currentAbsDiff = finalTargetValue - currentMarketValue; + double currentDiff = currentAbsDiff / finalTargetValue; + + Log("Current Market Balance: {0,10:###0.0000} {1} Current Market Value: {2,10:###0.0000} {3}", balanceMarket.CurrentBalance, balanceMarket.Currency, currentMarketValue, balanceBase.Currency); + Log("Current Market Price (bid): {0,10:###0.0000} {1}",currentUnitValue,balanceBase.Currency); + Log("Current Target Value: {0,10:###0.0000} {1} Target Value Diff: {2,10:###0.0000} {1} [ {3,6:##0.00}% ]", finalTargetValue, balanceBase.Currency, currentAbsDiff,currentDiff * 100); + + if (!CurrentOrderID.IsNull()) + { + Order order = getOrder(CurrentOrderID); + if (!order.IsOpen) + { + CurrentOrderID = null; + } + } + + if ((currentDiff > BasicSetup.MarginBuy) || (-currentDiff > BasicSetup.MarginSell)) + { + if (!CurrentOrderID.IsNull()){ + Log("Canceling old Order before creating new one."); + TradingEnvironment.DefaultConnection.cancelOrder(CurrentOrderID); + } else + { + double diffMarket = currentAbsDiff / orderBook.CurrentBid; + + if (diffMarket > 0){ + // Buy + double window = orderBook.getVolumePrice(diffMarket).Item1 - orderBook.getVolumePrice(diffMarket).Item2; + double price = orderBook.getVolumePrice(diffMarket).Item1 + (BasicSetup.WindowLevelToBuy * window); + + if (price >= balanceBase.CurrentBalance){ + diffMarket *= balanceBase.CurrentBalance / price; + price = orderBook.getVolumePrice(diffMarket).Item1; + } + Log("Will buy {0,11:####0.00000000} {1} @ {2,11:####0.00000000} {3} = {4,11:####0.00000000} {3}", diffMarket, BasicSetup.MarketSymbol, orderBook.CurrentAsk, BasicSetup.BaseSymbol, price); + Order order = createLimitOrder(OrderType.BUY, BasicSetup.MarketSymbol, BasicSetup.BaseSymbol, diffMarket, orderBook.CurrentAsk); + if (!order.IsNull()){ + CurrentOrderID = order.OrderID; + } + } else { + // SELL + diffMarket = -diffMarket; + double window = orderBook.getVolumePrice(diffMarket).Item1 - orderBook.getVolumePrice(diffMarket).Item2; + double price = orderBook.getVolumePrice(diffMarket).Item1 + (BasicSetup.WindowLevelToBuy * window); + + Log("Will sell {0,11:####0.00000000} {1} @ {2,11:####0.00000000} {3} = {4,11:####0.00000000} {3}", diffMarket, BasicSetup.MarketSymbol, orderBook.CurrentBid, BasicSetup.BaseSymbol, price); + Order order = createLimitOrder(OrderType.SELL, BasicSetup.MarketSymbol, BasicSetup.BaseSymbol, diffMarket, orderBook.CurrentBid); + if (!order.IsNull()){ + CurrentOrderID = order.OrderID; + } + } + + } + + } + + DumpBalances(); + Save(); + } + } + + public override int SchedulingIntervall() + { + return 15; + } + + + + public class WeightedIndicator{ + public double Volume; + public double Weight; + } + + } + + public class SetupClass : BasicBotSetup + { + public double TargetValue = 0; + public double TargetPrice = 0; + public double TargetExponent = 3; + + public double MarginSell = 0.02; + public double MarginBuy = 0.02; + + public double WindowLevelToBuy = 0.1; + public double WindowLevelToSell = 0.8; + } +}