using System; using System.Collections.Generic; using sharp.trading; using System.IO; using sharp.json; using System.Linq; using sharp.extensions; using System.Diagnostics; using sharp.trading.services; using sharp.trading.logging; using sharp.json.attributes; namespace sharp.tradebot { public abstract class TradingBot : TradingBot where T:BasicBotSetup { public T BasicSetup { get { return this.setup.CurrentValue; } set { this.setup.CurrentValue = value; } } protected override BasicBotSetup BasicBotSetup { get { return BasicSetup; } set { this.BasicSetup = (T)value; } } protected FileBackedJSONValue setup; protected override void EnvironmentAssigned(TradingBotEnvironment env){ this.setup = new FileBackedJSONValue(Path.Combine(BaseDataPath,"botsetup.json")); if (this.setup.CurrentValue.IsNull()){ this.setup.CurrentValue = Activator.CreateInstance(); } } public override void Save() { setup.Save(); base.Save(); } } [JSONClassPolicy( Policy = JSONPolicy.ATTRIBUTED)] public abstract class TradingBot : MarshalByRefObject { public TradingBotEnvironment TradingBotEnvironment { get; private set; } public TradeBotLoader TradeBotLoader { get; private set; } public Guid UID { get; private set; } public Logger Logger { get; private set; } public string BaseDataPath { get; private set; } List orders = new List(); List balances = new List(); Market botMarket; TextWriter balanceWriter; protected abstract BasicBotSetup BasicBotSetup { get; set; } protected abstract void EnvironmentAssigned(TradingBotEnvironment env); [JSONField] TradeBotBalance[] __balances { get { return balances.ToArray(); } set { this.balances.Clear(); if (value != null) { this.balances.AddRange(value); } } } [JSONField] Order[] __orders { get { return orders.ToArray(); } set { this.orders.Clear(); if (value != null) { this.orders.AddRange(value); } } } public bool IsPrepared { get; private set; } public TradingBot() { } public Market BotMarket { get { if (this.botMarket.IsNull() && !this.BasicBotSetup.BaseSymbol.Equals("") && !this.BasicBotSetup.BaseSymbol.Equals("")) { botMarket = TradingBotEnvironment.TradingConnection.openMarket(BasicBotSetup.MarketSymbol, BasicBotSetup.BaseSymbol); } return this.botMarket; } } public void AfterLoad(TradingBotEnvironment environment,Guid uid,Logger logger,TradeBotLoader loader){ this.TradingBotEnvironment = environment; this.UID = uid; this.Logger = logger; this.BaseDataPath = Path.Combine(environment.BaseDataDirectory, "bots", uid.ToString()); if (!Directory.Exists(DataDirectory)){ Directory.CreateDirectory(DataDirectory); } EnvironmentAssigned(environment); Log("TradingBot [{0}] loaded to AppDomain [{1}]",GetType().Name,AppDomain.CurrentDomain.FriendlyName); } public virtual void Prepare() { //if (File.Exists(Path.Combine(DataDirectory,"orders.json"))){ // JSON jsonorders = JSON.ReadFrom(Path.Combine(DataDirectory,"orders.json")); // string[] orderids = jsonorders.To(); // foreach (string oid in orderids){ // this.orders.Add(TradingEnvironment.DefaultConnection.getOrder(oid)); // } //} //if (File.Exists(Path.Combine(DataDirectory,"balances.json"))){ // JSON jsonbalances = JSON.ReadFrom(Path.Combine(DataDirectory,"balances.json")); // this.balances.Clear(); // this.balances.AddRange( jsonbalances.To()); //} if (File.Exists(Path.Combine(DataDirectory,"botsetup.json"))){ JSON jbasicsetup = JSON.ReadFrom(Path.Combine(DataDirectory, "botsetup.json")); this.BasicBotSetup = (BasicBotSetup)JSONConverter.To(BasicBotSetup.GetType(),jbasicsetup); } JSON jstate = JSON.ReadFrom(Path.Combine(BaseDataPath, "state.json")); if (jstate != null){ JSONConverter.ApplyObject(jstate, this); } balanceWriter = new StreamWriter(new FileStream(Path.Combine(BaseDataPath, "balance.log"),FileMode.Append)); this.TradingBotEnvironment.RegisterPeriodic(Balancing,10); IsPrepared = true; } public virtual void Unprepare() { IsPrepared = false; this.TradingBotEnvironment.UnregisterPeriodic(Balancing); balanceWriter.Close(); Save(); } public virtual void Save(){ JSON.From(this).WriteTo(Path.Combine(BaseDataPath, "state.json"),true); } public Order[] Orders { get { return this.orders.ToArray(); } } public Order getOrder(string orderID){ foreach (Order o in this.orders){ if (o.OrderID.Equals(orderID)){ return o; } } Order order = TradingEnvironment.DefaultConnection.getOrder(orderID); return order; } public void cancelOrder(Order order){ TradingEnvironment.DefaultConnection.cancelOrder(order); } public void cancelOrder(string orderID) { cancelOrder(getOrder(orderID)); } public Order createLimitOrder(OrderType orderType,string marketCurrency,string baseCurrency,double quantity,double limit){ TradeBotBalance bBase = getBalance(baseCurrency); TradeBotBalance bMarket = getBalance(marketCurrency); Market orderMarket = TradingEnvironment.DefaultConnection.openMarket(marketCurrency, baseCurrency); if (orderMarket.MinimumTradeVolume > quantity){ Log("Refusing to create order below minimum trading volume for market! Volume: {0:#0.000000} {1} < {2:#0.000000} {1}", quantity, marketCurrency, orderMarket.MinimumTradeVolume); return null; } if (orderType == OrderType.BUY){ if (bBase.CurrentBalance < (limit * quantity)){ Log("Refusing order to buy {0} @{1} with balance of {2}", quantity, limit, bBase.CurrentBalance); return null; } } else { if (bMarket.CurrentBalance < (quantity)){ Log("Refusing order to sell {0} @{1} with balance of {2}", quantity, limit, bMarket.CurrentBalance); return null; } } Log("createLimitOrder({0},{1},{2},{3},{4})", orderType, marketCurrency, baseCurrency, quantity, limit); try { Order o = TradingEnvironment.DefaultConnection.createOrder(orderType, OrderTarget.LIMIT, marketCurrency, baseCurrency, quantity, limit); this.orders.Add(o); return o; } catch (Exception e){ Log("createLimitOrder(): threw Exception: {0}", e); } return null; } public void RemoveOrder(Order order){ this.orders.Remove(order); } public VolumeRate getVolumeOnOrders(){ VolumeRate vrate = new VolumeRate(); foreach (Order order in this.orders){ if (order.OrderType == OrderType.BUY){ vrate.Volume += order.OrderVolume; vrate.Price += order.LimitPrice * order.OrderVolume; } else { vrate.Volume -= order.OrderVolume; vrate.Price -= order.LimitPrice * order.OrderVolume; } } vrate.Price /= vrate.Volume; return vrate; } /* Balances */ public TradeBotBalance getBalance(string symbol){ if (symbol.IsNull() || symbol.Equals("")) { return null; } foreach (TradeBotBalance b in balances){ if (b.Currency.Equals(symbol)){ return b; } } TradeBotBalance balance = new TradeBotBalance(symbol); balances.Add(balance); return balance; } public T loadJSON(string filename){ if (File.Exists(Path.Combine(DataDirectory, filename))) { return JSON.ReadFrom(Path.Combine(DataDirectory, filename)).To(); } if (typeof(T).IsArray){ return (T)(object)Array.CreateInstance(typeof(T).GetElementType(), 0); } return Activator.CreateInstance(); } public void saveJSON(object o, string filename){ JSONConverter.From(o).WriteTo(Path.Combine(DataDirectory, filename),true); } public string DataDirectory { get { return BaseDataPath; } } public void Log(string format,params object[] args){ Logger.Log(format,args); } public void DumpBalances(){ foreach (TradeBotBalance balance in balances){ Log(balance.ToString()); } } public virtual int SchedulingIntervall() { return 60; } public virtual void Balancing(){ if (BasicBotSetup.Enabled) { TradeBotBalance bMarket = getBalance(BasicBotSetup.MarketSymbol); TradeBotBalance bBase = getBalance(BasicBotSetup.BaseSymbol); bool charged = false; if (BasicBotSetup.ChargeBaseBalance != 0){ bBase.CurrentBalance += BasicBotSetup.ChargeBaseBalance; charged = true; } if (BasicBotSetup.ChargeMarketBalance != 0){ bMarket.CurrentBalance += BasicBotSetup.ChargeMarketBalance; charged = true; } if (charged){ Log("Charged balances: {0,11:####0.00000000} {1} / {2,11:####0.00000000} {3}", BasicBotSetup.ChargeBaseBalance, BasicBotSetup.BaseSymbol, BasicBotSetup.ChargeMarketBalance, BasicBotSetup.MarketSymbol); balanceWriter.WriteLine("{0}\t{1}\t{2}\t{3,12:0.00000000}\t{4,12:0.00000000}\t{5,12:0.00000000}\t{6,12:0.00000000}\t{7,12:0.00000000}", DateTime.Now, "-", "CHARGE", BasicBotSetup.ChargeMarketBalance,BasicBotSetup.ChargeBaseBalance,0,bBase.CurrentBalance,bMarket.CurrentBalance); balanceWriter.Flush(); BasicBotSetup.ChargeMarketBalance = 0; BasicBotSetup.ChargeBaseBalance = 0; } foreach (Order order in this.__orders) { TradingBotEnvironment.TradingConnection.refreshOrder(order); if (!order.IsOpen) { bBase.CurrentBalance -= order.PayedFees; if (order.OrderType == OrderType.BUY){ bBase.CurrentBalance -= order.PayedPrice; bMarket.CurrentBalance += order.FilledVolume; } else { bBase.CurrentBalance += order.PayedPrice; bMarket.CurrentBalance -= order.FilledVolume; } this.RemoveOrder(order); Log("Order has been closed: {5} {0,11:####0.00000000} {1} / {2,11:####0.00000000} {3} [ FEE: {4,11:####0.00000000} {3} ]", order.FilledVolume, BasicBotSetup.MarketSymbol, order.PayedPrice, BasicBotSetup.BaseSymbol, order.PayedFees, order.OrderType ); balanceWriter.WriteLine("{0}\t{1}\t{2}\t{3:#0.00000000}\t{4:#0.00000000}\t{5:#0.00000000}\t{6:#0.00000000}\t{7:#0.00000000}", DateTime.Now, order.OrderID, order.OrderType, order.FilledVolume,order.PayedPrice,order.PayedFees,bBase.CurrentBalance,bMarket.CurrentBalance); balanceWriter.Flush(); } } Save(); } } } }