sharp-tradebot/TradingBot.cs

378 lines
10 KiB
C#

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<T> : 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<T> setup;
protected override void EnvironmentAssigned(TradingBotEnvironment env){
this.setup = new FileBackedJSONValue<T>(Path.Combine(BaseDataPath,"botsetup.json"));
if (this.setup.CurrentValue.IsNull()){
this.setup.CurrentValue = Activator.CreateInstance<T>();
}
}
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<Order> orders = new List<Order>();
List<TradeBotBalance> balances = new List<TradeBotBalance>();
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<string[]>();
// 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<TradeBotBalance[]>());
//}
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<T>(string filename){
if (File.Exists(Path.Combine(DataDirectory, filename)))
{
return JSON.ReadFrom(Path.Combine(DataDirectory, filename)).To<T>();
}
if (typeof(T).IsArray){
return (T)(object)Array.CreateInstance(typeof(T).GetElementType(), 0);
}
return Activator.CreateInstance<T>();
}
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();
}
}
}
}