BigBot/DifferentingBot.cs

445 lines
14 KiB
C#

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<DifferentialSetup>
{
[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<double,double> 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<double, double> 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;
}
}