package dailyBot.control.connection.zulutrade; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.TreeMap; import java.util.TreeSet; import java.util.concurrent.atomic.AtomicLong; import com.zulutrade.configuration.ZuluTradeAuthenticationInfo; import com.zulutrade.configuration.ZuluTradingServerConfig; import com.zulutrade.trading.UniqueIdGenerator; import com.zulutrade.trading.ZuluTradingGateway; import com.zulutrade.trading.ZuluTradingGatewayImpl; import com.zulutrade.trading.dtos.NewMarketTradeResponseMessage; import com.zulutrade.trading.dtos.TradeClosedResponseMessage; import com.zulutrade.trading.dtos.UpdateValueResponseMessage; import dailyBot.control.DailyLog; import dailyBot.control.DailyProperties; import dailyBot.control.DailyUtils; import dailyBot.control.connection.EmailConnection; import dailyBot.model.Broker; import dailyBot.model.Pair; import dailyBot.model.SignalProvider.SignalProviderId; import dailyBot.model.Strategy.StrategyId; import dailyBot.model.StrategySignal; import dailyBot.model.UniqueIdSignal; import dailyBot.model.Utils; public class ZulutradeConnection implements Broker { private class InstantZulutradeConnection { ZuluTradingServerConfig cfg; ZuluTradeAuthenticationInfo ai; ZuluTradingGateway tradingGateway; private InstantZulutradeConnection(SignalProviderId id) { cfg = new ZuluTradingServerConfig("http://tradingserver.zulutrade.com/"); ai = new ZuluTradeAuthenticationInfo( DailyProperties.getProperty("dailyBot.control.connection.zulutrade.ZulutradeConnection." + id + ".username"), DailyProperties.getProperty("dailyBot.control.connection.zulutrade.ZulutradeConnection." + id + ".password")); tradingGateway = new ZuluTradingGatewayImpl(ai, cfg); } } public static class ZulutradeException extends Exception { private static final long serialVersionUID = -3162825748109413066L; public ZulutradeException(Exception cause) { super(cause); } } public class ZulutradeSignal { public final boolean isError; public final int errorCode; public final String errorMessage; public final long uniqueId; public final long dateOpened; public final Pair currency; public final boolean isBuy; public final double entryPrice; public final double stop; public final double limit; public ZulutradeSignal(NewMarketTradeResponseMessage signal) throws ZulutradeException { try { if(signal.getErrorCode() != 0) { isError = true; errorCode = signal.getErrorCode(); errorMessage = signal.getErrorMessage(); uniqueId = 0; dateOpened = 0; currency = null; isBuy = true; entryPrice = stop = limit = 0; } else { isError = false; errorCode = 0; errorMessage = null; if(signal.getUniqueId().contains(".")) uniqueId = Long .parseLong(signal.getUniqueId().substring(signal.getUniqueId().indexOf('.') + 1)); else uniqueId = Long.parseLong(signal.getUniqueId()); dateOpened = signal.getOpenUtcTimestamp(); currency = Pair.stringToPair(signal.getCurrencyName().replace("/", "")); isBuy = signal.isBuy(); entryPrice = signal.getEntryRate(); stop = signal.getStopValue(); limit = signal.getLimitValue(); } } catch(Exception e) { throw new ZulutradeException(e); } } @Override public String toString() { return "ZulutradeSignal [isError=" + isError + ", errorCode=" + errorCode + ", errorMessage=" + errorMessage + ", uniqueId=" + uniqueId + ", dateOpened=" + dateOpened + ", currency=" + currency + ", isBuy=" + isBuy + ", entryPrice=" + entryPrice + ", stop=" + stop + ", limit=" + limit + "]"; } } SignalProviderId id; public ZulutradeConnection(SignalProviderId id) { this.id = id; } public ZulutradeSignal[] listOpenSignals() throws ZulutradeException { try { ArrayList <ZulutradeSignal> all = new ArrayList <ZulutradeSignal>(); for(NewMarketTradeResponseMessage marketTrade : new InstantZulutradeConnection(id).tradingGateway .getOpenTrades().getOpenPositions()) all.add(new ZulutradeSignal(marketTrade)); return all.toArray(new ZulutradeSignal[0]); } catch(Exception e) { throw new ZulutradeException(e); } } public ZulutradeSignal findSignalById(long id) throws ZulutradeException { try { for(ZulutradeSignal signal : listOpenSignals()) if(signal.uniqueId == id) return signal; return null; } catch(Exception e) { throw new ZulutradeException(e); } } public boolean changeLimit(long id, double newLimit) throws ZulutradeException { try { ZulutradeSignal signal = findSignalById(id); if(signal == null || signal.isError) throw new ZulutradeException(new Exception("Error changing limit: " + signal)); Pair currency = signal.currency; UpdateValueResponseMessage updateResponse = new InstantZulutradeConnection(this.id).tradingGateway .updateLimit(currency.toString().substring(0, 3) + "/" + currency.toString().substring(3), signal.isBuy, 1, id + "", newLimit); if(updateResponse == null) throw new ZulutradeException(new Exception("Null change limit response for id: " + id)); if(!updateResponse.isSuccess()) DailyLog.logError("Not possible to change limit: " + updateResponse.getErrorCode() + " " + updateResponse.getErrorMessage()); return updateResponse.isSuccess(); } catch(Exception e) { throw new ZulutradeException(e); } } public boolean changeStop(long id, double newStop) throws ZulutradeException { try { ZulutradeSignal signal = findSignalById(id); if(signal == null || signal.isError) throw new ZulutradeException(new Exception("Error changing stop: " + signal)); Pair currency = signal.currency; UpdateValueResponseMessage updateResponse = new InstantZulutradeConnection(this.id).tradingGateway .updateStop(currency.toString().substring(0, 3) + "/" + currency.toString().substring(3), signal.isBuy, 1, id + "", newStop); if(updateResponse == null) throw new ZulutradeException(new Exception("Null change stop response for id: " + id)); if(!updateResponse.isSuccess()) DailyLog.logError("Not possible to change stop: " + updateResponse.getErrorCode() + " " + updateResponse.getErrorMessage()); return updateResponse.isSuccess(); } catch(Exception e) { throw new ZulutradeException(e); } } private final AtomicLong lastOpened = new AtomicLong(0); public synchronized ZulutradeSignal openTrade(Pair currency, boolean isBuy, int limit, int stop) throws ZulutradeException { try { long difference = System.currentTimeMillis() - lastOpened.get(); if(difference >= 0 && difference < 16000) DailyUtils.sleep(16000 - difference); lastOpened.set(System.currentTimeMillis()); double stopPrice = currency.getCurrentPriceMinus(stop, isBuy); double limitPrice = currency.getCurrentPriceMinus(-limit, isBuy); long uniqueId = Long.parseLong(UniqueIdGenerator.getNextUniqueId()); ZulutradeSignal answer = new ZulutradeSignal(new InstantZulutradeConnection(id).tradingGateway.openMarket( currency.toString().substring(0, 3) + "/" + currency.toString().substring(3), 1, isBuy, currency.getCurrentPrice(isBuy), uniqueId + "")); try { stopPrice = currency.getPriceMinus(answer.entryPrice, stop, isBuy); limitPrice = currency.getPriceMinus(answer.entryPrice, -limit, isBuy); } catch(Exception e) { DailyLog.logError("Error leyendo precio de entrada zulutrade " + answer); } if(answer.isError) throw new ZulutradeException(new Exception("Not possible to open: " + answer)); if(!changeStop(answer.uniqueId, stopPrice)) throw new ZulutradeException(new Exception( "Error setting initial stop, trade possibly opened without stop")); if(!changeLimit(answer.uniqueId, limitPrice)) throw new ZulutradeException(new Exception( "Error setting initial limit, trade possibly opened without limit")); ZulutradeSignal signal = findSignalById(answer.uniqueId); if(signal == null) throw new ZulutradeException(new Exception("Not possible to open: " + answer)); return signal; } catch(Exception e) { throw new ZulutradeException(e); } } public boolean closeTrade(long id) throws ZulutradeException { try { ZulutradeSignal signal = findSignalById(id); if(signal == null) throw new ZulutradeException(new Exception("Signal with id: " + id + ", not found")); TradeClosedResponseMessage closedResponse = new InstantZulutradeConnection(this.id).tradingGateway .closeMarket( signal.currency.toString().substring(0, 3) + "/" + signal.currency.toString().substring(3), 1, signal.isBuy, signal.uniqueId + "", signal.currency.getCurrentPrice(signal.isBuy)); if(closedResponse == null) throw new ZulutradeException(new Exception("Null close response for id: " + id)); return closedResponse.isSuccess(); } catch(Exception e) { throw new ZulutradeException(e); } } SignalProviderId idProveedor; public static AtomicLong lastCheckTime = new AtomicLong(); public static AtomicLong lastChangeTime = new AtomicLong(); @Override public void checkConsistency() { try { ZulutradeSignal[] signals = listOpenSignals(); TreeSet <Long> ids = new TreeSet <Long>(); for(ZulutradeSignal s : signals) ids.add(s.uniqueId); StrategySignal[] strategySignals = Utils.getAllSignals(); for(StrategySignal signal : strategySignals) { if(signal != null && signal.getUniqueId("zulutrade-" + id.toString()) != 0 && !ids.contains(signal.getUniqueId("zulutrade-" + id.toString()))) { if(signal.getStrategyId() != StrategyId.BREAKOUT1) { lastChangeTime.set(System.currentTimeMillis()); DailyLog.addRangeInfo("zulutrade", "Senal: " + signal + ", no existia realmente en zulutrade, quitando id. Senales existentes: " + Arrays.toString(signals)); } signal.setUniqueId("zulutrade-" + id.toString() + "-old", signal.getUniqueId("zulutrade-" + id.toString())); signal.setUniqueId("zulutrade-" + id.toString(), 0L); } else if(signal != null && signal.getUniqueId("zulutrade-" + id.toString()) != 0) { if(signal.getPair().differenceInPips(signal.getStop(), signal.isBuy()) < 0 && Math.abs(signal.getStop()) > 1e-5d && (signal.getPair().differenceInPips(signal.getStop(), signal.stopDaily(), signal.isBuy()) > 10)) { closeSignal(signal, signal.getStrategyId(), signal.getPair(), signal.isBuy()); if(signal.getStrategyId() != StrategyId.BREAKOUT1) { lastChangeTime.set(System.currentTimeMillis()); DailyLog.addRangeInfo("cambios", "Senal: " + signal + " toco stop, cerrando. Precio actual = " + signal.getPair().getCurrentPrice(signal.isBuy())); } signal.setStopTouched(true); } else signal.setUniqueId("zulutrade-" + id.toString() + "-lastchecktime", System.currentTimeMillis()); } else if(signal != null && signal.getUniqueId("zulutrade-" + id.toString() + "-old") != 0 && ids.contains(signal.getUniqueId("zulutrade-" + id.toString() + "-old"))) { DailyLog.logError("Senal: " + signal + ", volvio a aparecer, restaurando: " + Arrays.toString(signals)); signal.setUniqueId("zulutrade-" + id.toString(), signal.getUniqueId("zulutrade-" + id.toString() + "-old")); signal.setUniqueId("zulutrade-" + id.toString() + "-old", 0L); } } lastCheckTime.set(System.currentTimeMillis()); synchronized(ZulutradeConnection.class) { countErrorsZulu.set(0); } } catch(Exception e) { synchronized(ZulutradeConnection.class) { if(countErrorsZulu.incrementAndGet() >= 5) DailyLog.logError(e.getMessage()); } } } private String zuluString(ZulutradeSignal zuluSignal, StrategySignal dailySignal) { return dailySignal.getStrategyId() + " " + (dailySignal.isBuy() ? "Compra" : "Venta") + " " + dailySignal.getLotNumber() + " Lotes de " + zuluSignal.currency + " a: " + zuluSignal.entryPrice + " Stop: " + zuluSignal.stop; } @Override public String checkConsistencyFull(boolean sendMessage) { checkConsistency(); try { ZulutradeSignal[] signals = listOpenSignals(); TreeMap <Long, ZulutradeSignal> ids = new TreeMap <Long, ZulutradeSignal>(); for(ZulutradeSignal s : signals) ids.put(s.uniqueId, s); StrategySignal[] strategySignals = Utils.getAllSignals(); String messages = id.toString(); for(StrategySignal signal : strategySignals) { if(signal != null && signal.getUniqueId("zulutrade-" + id.toString()) != 0) { messages += "\n"; messages += signal.getUniqueId("zulutrade-" + id.toString()) + "\n"; messages += signal + "\n"; messages += zuluString(ids.get(signal.getUniqueId("zulutrade-" + id.toString())), signal) + "\n"; ids.remove(signal.getUniqueId("zulutrade-" + id.toString())); } else if(signal != null) { messages += "\n"; messages += signal + ", not opened\n"; } } if(sendMessage) EmailConnection.sendEmail("DailyBot-info", messages, (Calendar.getInstance().get(Calendar.DAY_OF_WEEK) == Calendar.FRIDAY) ? (EmailConnection.ADMINS | EmailConnection.WATCHERS | EmailConnection.SUPERADMINS) : EmailConnection.WATCHERS); messages = ""; for(ZulutradeSignal signal : ids.values()) { messages += "\n"; messages += signal.uniqueId + "\n"; messages += signal + " abierta manualmente.\n"; } if(!messages.trim().isEmpty()) DailyLog.logInfo(messages); lastCheckTime.set(System.currentTimeMillis()); synchronized(ZulutradeConnection.class) { countErrorsZulu.set(0); } return messages; } catch(Exception e) { synchronized(ZulutradeConnection.class) { if(countErrorsZulu.incrementAndGet() >= 5) DailyLog.logError(e.getMessage()); } return e.getMessage(); } } private static final AtomicLong countErrorsZulu = new AtomicLong(0); @Override public boolean openSignal(UniqueIdSignal affected, StrategyId strategyId, Pair pair, boolean buy) { try { if(strategyId != StrategyId.BREAKOUT1) { lastChangeTime.set(System.currentTimeMillis()); DailyLog.addRangeInfo("intentos de apertura", id + " abriendo senal: " + strategyId + ", " + pair); DailyLog.addRangeInfo("intentos de apertura", "Abriendo zulutrade: "); } int limit = Integer.parseInt(DailyProperties.getProperty("dailyBot.control.connection.zulutrade.ZulutradeConnection." + id + ".limit")); int stop = Integer.parseInt(DailyProperties.getProperty("dailyBot.control.connection.zulutrade.ZulutradeConnection." + id + ".stop")); ZulutradeSignal signal = openTrade(pair, buy, limit, stop); affected.setUniqueId("zulutrade-" + id.toString(), signal.uniqueId); if(strategyId != StrategyId.BREAKOUT1) { lastChangeTime.set(System.currentTimeMillis()); DailyLog.addRangeInfo("intentos de apertura", "Abierta: " + signal + ""); } return true; } catch(Exception e) { DailyLog.logError("Zulutrade error: " + e.getMessage()); return false; } } @Override public boolean closeSignal(UniqueIdSignal affected, StrategyId strategyId, Pair pair, boolean buy) { try { if(strategyId != StrategyId.BREAKOUT1) { lastChangeTime.set(System.currentTimeMillis()); DailyLog.addRangeInfo("cambios", id + " cerrando senal: " + strategyId + ", " + pair + ", zulutrade: " + findSignalById(affected.getUniqueId("zulutrade-" + id.toString()))); } closeTrade(affected.getUniqueId("zulutrade-" + id.toString())); affected.setUniqueId("zulutrade-" + id.toString(), 0L); return true; } catch(Exception e) { DailyLog.logError("Zulutrade error: " + e.getMessage()); return false; } } @Override public long getUniqueId(UniqueIdSignal signal) { return signal.getUniqueId("zulutrade-" + id.toString()); } @Override public void setUniqueId(UniqueIdSignal signal, long value) { signal.setUniqueId("zulutrade-" + id.toString(), value); } @Override public boolean openManualSignal(Pair pair, boolean buy) { try { return !openTrade(pair, buy, 300, 75).isError; } catch(Exception e) { DailyLog.logError("Zulutrade error: " + e.getMessage()); return false; } } @Override public boolean closeManualSignal(long id) { try { return closeTrade(id); } catch(Exception e) { DailyLog.logError("Zulutrade error: " + e.getMessage()); return false; } } }