445 lines
14 KiB
C#
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;
|
|
|
|
}
|
|
}
|