org.mot.core.MyOpenTraderCore.java Source code

Java tutorial

Introduction

Here is the source code for org.mot.core.MyOpenTraderCore.java

Source

/*
 * Copyright (C) 2015 Stephan Grotz - stephan@myopentrader.org
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package org.mot.core;

import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import javax.jms.Destination;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;

import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.log4j.PropertyConfigurator;
import org.mot.common.cache.CacheFactory;
import org.mot.common.conversion.LoadValueConverter;
import org.mot.common.db.AverageCalculationDAO;
import org.mot.common.db.OrderDAO;
import org.mot.common.db.StrategyAnalysisDAO;
import org.mot.common.db.StrategyDAO;
import org.mot.common.db.TickPriceDAO;
import org.mot.common.db.WatchListDAO;
import org.mot.common.mq.ActiveMQFactory;
import org.mot.common.objects.LoadValue;
import org.mot.common.objects.Strategy;
import org.mot.common.objects.WatchList;
import org.mot.common.tools.PropertiesFactory;
import org.mot.common.tools.QuartzScheduler;
import org.mot.common.util.StrategyAnalyser;
import org.mot.core.algo.GenericAlgorithm;
import org.mot.core.listener.TickHistoryMessageListener;
import org.mot.core.listener.TickMessageListener;
import org.mot.core.listener.TickSizeMessageListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This is the main MyOpenTrader Core class, used to start up the core engine. 
 * The class has 2 main entry points - as it can be executed as a war file, but also through command line. For that reason
 * you will find the dependencies on the httpServlet class left in here. 
 * 
 * Make sure to provide at least a configuration directory, as well as an executor name (see 
 * http://wiki.myopentrader.org/confluence/display/MOTD/Executor for more details) to the main class. 
 * 
 * Copyright (C) 2015 Stephan Grotz - stephan@myopentrader.org
 */
public class MyOpenTraderCore extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * This is required for the servlet implementation - always call the super method.
     * 
     * @see HttpServlet#HttpServlet()
     */
    public MyOpenTraderCore() {
        super();
        // TODO Auto-generated constructor stub
    }

    private static final Logger logger = LoggerFactory.getLogger(MyOpenTraderCore.class);
    PropertiesFactory pf = PropertiesFactory.getInstance();

    /**
     * The init method is the main starting point for both, the servlet (war) and the main method
     * in case it is executed from the command line...
     * 
     * @see Servlet#init(ServletConfig)
     */
    public void init(ServletConfig config) throws ServletException {
        // TODO Auto-generated method stub

        logger.info("*** Initializing Interactive Broker CORE Servlet ...");
        String pathToConfigDir = config.getServletContext().getRealPath("/WEB-INF/conf");
        startWorkers(pathToConfigDir, null);

    }

    /**
     * This is the main starting point for the CORE class. From here all other subclasses are initiated and triggered. 
     * Make sure to be careful about changes in here!
     * 
     * @param PathToConfigDir - provide a configuration directory to use
     * @param name - the executor name
     */
    protected void startWorkers(String PathToConfigDir, String name) {

        // First make sure to set the config directory
        pf.setConfigDir(PathToConfigDir);

        try {
            // Read in the properties
            Configuration genericProperties = new PropertiesConfiguration(PathToConfigDir + "/config.properties");

            // Make sure to set the global property and initiate logging
            PropertyConfigurator.configure(pf.getConfigDir() + "/log4j.properties");
            logger.debug("Setting PathToCoreConfigDir property to: " + PathToConfigDir);

            // If the executor is left empty, use wildcard ALL instead.
            if (name == null) {
                // Get the engine name
                name = genericProperties.getString("engine.core.name", "ALL");
            }

            // Start the tick message listener
            if (genericProperties.getBoolean("engine.startup.core.tickListener.enabled", true)) {
                startIndiviualTickMessageListener(name, genericProperties);
            }
            ;

            // Start the tick size listener
            if (genericProperties.getBoolean("engine.startup.core.tickSizeListener.enabled", true)) {
                startTickSizeListener();
            }
            ;

            // Start the tick history listener
            if (genericProperties.getBoolean("engine.startup.core.historyListener.enabled", true)) {
                startTickMessageHistoryListener();
            }
            ;

            // Start the internal scheduling engine
            startScheduler(name);

        } catch (ConfigurationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    /**
     * This method is used to start all internal schedulers. It uses the Quartz scheduling engine for this. 
     * Make sure the configuration in the conf/scheduler.conf is up2date. 
     * 
     * @param name
     */
    private void startScheduler(String name) {

        int seconds = 60;
        PropertiesFactory pf = PropertiesFactory.getInstance();
        String pathToConfigDir = pf.getConfigDir();
        try {
            PropertiesConfiguration config = new PropertiesConfiguration(pathToConfigDir + "/scheduler.properties");

            // Make sure the scheduler only runs on the designated node. 
            // Otherwise with multiple nodes running, each job will run several times! 
            String runOn;
            if (!name.equals("ALL")) {
                runOn = config.getString("scheduler.runOn", "mot1");
            } else {
                // For development (single node) purposes - just overwrite the value to be identical!
                runOn = "ALL";
            }
            seconds = config.getInt("watchdog.frequency.seconds", 60);
            boolean enabled = config.getBoolean("scheduler.enabled", true);

            if ((enabled) && (name.equals(runOn))) {
                QuartzScheduler qs = QuartzScheduler.getInstance();

                boolean watchDogEnabled = config.getBoolean("watchdog.enabled", true);
                String wdClass = config.getString("watchdog.class", "org.mot.core.scheduler.WatchDog");

                if (watchDogEnabled) {
                    qs.scheduleNewRepeatingJob(wdClass, "WatchDog", "base", seconds);
                }

                String archiverClass = config.getString("tickarchiver.class",
                        "org.mot.core.scheduler.TickArchiver");
                boolean archiverEnabled = config.getBoolean("tickarchiver.enabled", true);

                if (archiverEnabled) {
                    qs.scheduleNewJob(archiverClass, "Archiver", "base");
                }

                String sdrClass = config.getString("staticdatareader.class",
                        "org.mot.core.scheduler.StaticDataCollector");
                boolean sdrEnabled = config.getBoolean("staticdatareader.enabled", true);
                int sdrFrequency = config.getInt("staticdatareader.frequency", 360000);

                if (sdrEnabled) {
                    qs.scheduleNewRepeatingJob(sdrClass, "StaticDataReader", "base", sdrFrequency);
                }

                String simClass = config.getString("backtest.simulator.class",
                        "org.mot.core.scheduler.BackTestSimulator");
                boolean simEnabled = config.getBoolean("backtest.simulator.enabled", true);

                if (simEnabled) {
                    qs.scheduleNewJob(simClass, "Simulator", "base");
                }

                boolean emailEnabled = config.getBoolean("email.engine.enabled", true);

                if (emailEnabled) {
                    String emailClass = config.getString("email.engine.class",
                            "org.mot.core.scheduler.EmailEngine");
                    String cronExpression = config.getString("email.engine.cronExpression", "0 1 * * *");
                    qs.scheduleCronJob(emailClass, "EmailEngine", "base", cronExpression);
                }

            }

        } catch (ConfigurationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    /**
     * This method is just a wrapper to start each tick message listener individually.
     * Each stock will therefore have its own dedicated tickChannel listener - this prevents bottlenecks in the messsaging layer 
     * 
     * @param name - The executor name
     * @param genericProperties - pass in the generic properties
     */
    private void startIndiviualTickMessageListener(String name, Configuration genericProperties) {

        WatchListDAO wld = new WatchListDAO();
        final WatchList[] wl = wld.getWatchlistByExecutorAsObject(name);

        for (int u = 0; u < wl.length; u++) {
            String symbol = wl[u].getSymbol();
            String type = wl[u].getType();
            String currency = wl[u].getCurrency();

            // Check if the CEP Engine is supposed to be started... it only makes sense to start the engine, if the tick feed was also started!
            if (genericProperties.getBoolean("engine.startup.core.enabled", true)) {
                createOrdersForStrategies(genericProperties, symbol);
            }
            ;

            startTickMessageListener(symbol, type, currency, genericProperties);

        }
    }

    /**
     * This method creates the actual JMS message listener for all stock prices. 
     * 
     * @param symbol - which symbol to listen for
     * @param type - type of the symbol (FX, STK etc)
     * @param currency - which currency is in use
     * @param genericProperties - generic properties
     */
    private void startTickMessageListener(String symbol, String type, String currency,
            Configuration genericProperties) {

        AverageCalculationDAO acd = new AverageCalculationDAO();
        ActiveMQFactory amf = new ActiveMQFactory();
        logger.debug("Creating new tick message listener ...");
        TickMessageListener tml = new TickMessageListener();
        Destination ticks = amf.createDestination("tickPriceChannel");

        /*
         * The following section would automatically create a moving average algo with the best known combination
         */

        if (genericProperties.getBoolean("engine.startup.autoCreateBestCombination")) {
            Iterator<Entry<Integer, Integer>> lm = acd
                    .getBestAverageCombination(symbol, "TICK",
                            genericProperties.getInt("engine.startup.autoCreateBestCombineation.top"))
                    .entrySet().iterator();
            if (lm.hasNext()) {
                Map.Entry<Integer, Integer> lm2 = lm.next();
                String name = symbol + lm2.getKey() * 10 + "-" + lm2.getValue() * 10 + "-mvg";
                //loadAlgorithm(genericProperties.getString("engine.startup.autoCreateBestCombineation.class"), name, symbol, lm2.getKey()*10, lm2.getValue()*10, 10, true);

            }
        }

        if (type.equals("FX")) {
            // If type is FX also provide the currency
            amf.createMessageConsumer(ticks, tml, symbol, currency);
        } else {
            amf.createMessageConsumer(ticks, tml, symbol);
        }

    }

    /**
     * This method starts the tick size listener, containing volume information etc
     */
    private void startTickSizeListener() {

        ActiveMQFactory amf = new ActiveMQFactory();
        logger.debug("Creating new tick size message listener ...");
        TickSizeMessageListener tsml = new TickSizeMessageListener();
        Destination tickSize = amf.createDestination("tickSizeChannel");

        amf.createMessageConsumer(tickSize, tsml);

    }

    /**
     * Receive the historic feed for a particular stock
     */
    private void startTickMessageHistoryListener() {
        ActiveMQFactory amf = new ActiveMQFactory();
        logger.debug("Creating new tickHistory message listener ...");
        TickHistoryMessageListener thml = new TickHistoryMessageListener();
        Destination tickHistory = amf.createDestination("tickHistoryChannel");
        amf.createMessageConsumer(tickHistory, thml);
    }

    /**
     * Automatically create orders for a particular strategy. 
     * 
     * @param genericProperties
     * @param symbol
     */
    private void createOrdersForStrategies(Configuration genericProperties, String symbol) {
        StrategyDAO sd = new StrategyDAO();
        TickPriceDAO tpd = new TickPriceDAO();
        Strategy[] strategies = sd.getStrategiesForSymbolAsObject(symbol);

        try {

            for (int u = 0; u < strategies.length; u++) {

                // Read out the dynamic properties for startup ...   
                LoadValueConverter lvc = new LoadValueConverter();
                LoadValue[] values = lvc.convertStringToValues(strategies[u].getLoadValues());

                // Default simulated flag to true - just to be save! 
                // This is overwritten with the strategy value later on
                boolean simulated = true;

                // Default the amount to 10 shares
                int amount = 10;

                // Find out how many shares can be bought for the investment amount
                if (strategies[u].getAmount() != null) {
                    amount = (int) (strategies[u].getAmount()
                            / tpd.getLastPriceForSymbol(strategies[u].getSymbol(), "ASK"));
                    simulated = strategies[u].isSimulated();
                }

                new GenericAlgorithm(strategies[u].getType(), strategies[u].getName(), strategies[u].getSymbol(),
                        values, simulated, amount);

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        try {
            Options options = new Options();
            options.addOption("c", true, "Config directory");
            options.addOption("e", true, "Executor name (ALL is default wildcard)");
            options.addOption("h", false, "Print the command line help");

            CommandLineParser parser = new BasicParser();
            CommandLine cmd = parser.parse(options, args);

            if (args.length == 0 || cmd.hasOption("h")) {
                System.out.println("*** Missing arguments: ***");
                HelpFormatter formatter = new HelpFormatter();
                formatter.printHelp("runMyOpenTraderCore.sh|.bat", options);
                System.exit(0);
            }

            String confDir = "conf";
            // Get the configuration directory
            if (cmd.getOptionValue("c") != null) {
                confDir = cmd.getOptionValue("c");
            }

            String executor = "ALL";
            // Get the executor
            if (cmd.getOptionValue("e") != null) {
                executor = cmd.getOptionValue("e");
            }

            System.out.println("Setting config path to: " + confDir);
            System.out.println("Running as executor: " + executor);

            new MyOpenTraderCore().startWorkers(confDir, executor);

        } catch (Exception e) {
            // TODO Auto-generated catch block
            System.out.println("*** Please provide the config directory as an command line argument ...! ");
            e.printStackTrace();
        }

    }

}