Package com.collective2.signalEntry.adapter

Source Code of com.collective2.signalEntry.adapter.DynamicSimulationAdapter

/**
* This notice shall not be removed.
* See the "LICENSE.txt" file found in the root folder
* for the full license governing this code.
* Nathan Tippy  7/28/12
*/
package com.collective2.signalEntry.adapter;

import com.collective2.signalEntry.*;
import com.collective2.signalEntry.adapter.dynamicSimulator.DataProvider;
import com.collective2.signalEntry.adapter.dynamicSimulator.GainListener;
import com.collective2.signalEntry.adapter.dynamicSimulator.GainListenerManager;
import com.collective2.signalEntry.adapter.dynamicSimulator.SystemManager;
import com.collective2.signalEntry.adapter.dynamicSimulator.order.Order;
import com.collective2.signalEntry.adapter.dynamicSimulator.portfolio.Portfolio;
import com.collective2.signalEntry.adapter.dynamicSimulator.portfolio.SimplePortfolioFactory;
import com.collective2.signalEntry.adapter.simulationXML.*;
import com.collective2.signalEntry.implementation.Command;
import com.collective2.signalEntry.implementation.Request;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamException;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
* Dynamic simulation adapter
*
* Simulates collective2 responses given a data feed.
*
* Has built in support for simplistic position based portfolio.
* Uses portfolio interface for a more complex/persistent implementation.
*
* Has built in support for CAGR periodic reporting to logger or system out.
* Uses listener interface for a more complex/persistent implementation of CAGR gain listener.
*
* Limitations:
*  this simulation does not take into account splits or dividends
*  this simulation requires user to call tick and supply data via DataProvider interface
*
*  this simulation has no support for setting signalId from the client side (caller)
*  this simulation has not be tested with multiple systems
*
*/
public class DynamicSimulationAdapter implements C2EntryServiceAdapter {

    private static final Logger logger = LoggerFactory.getLogger(DynamicSimulationAdapter.class);
    private static final String OK = "ok";
    private static final String ERROR = "error";
   
    private final List<SystemManager> systems = new ArrayList<SystemManager>();
    private final Object lock = new Object();
    private final boolean marginAccount;
   
    private DataProvider lastTickDataProvider;
    private long time;

    private final List<GainListenerManager> gainListeners = new ArrayList<GainListenerManager>();
    private final static ThreadFactory threadFactory = new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {

            Thread thread = new Thread(r);
            thread.setDaemon(true);
            thread.setName("DynamicSimulationAdapter GainListenerManager");
            return thread;

        }
    };
    private final ExecutorService gainExecutor = Executors.newSingleThreadExecutor(threadFactory);

    //C2 always uses margin but simulator can also be run without margin.
    public DynamicSimulationAdapter(boolean marginAccount) {
        this.marginAccount = marginAccount;
     }

    public void tick(DataProvider dataProvider, C2EntryService entryService) {

        //ensure existing requests are submitted.
        entryService.awaitPending();

        SystemManager system = systems.get(entryService.systemId());

        //can not tick and transmit at same moment in order to keep simulator repeatable
        synchronized(lock) {
               //look at ordered list of signals for each system and do all those older than time now.
            this.time = dataProvider.endingTime();
            system.tick(time, dataProvider);
            lastTickDataProvider = dataProvider;
        }

        for(GainListenerManager manager:gainListeners) {
            manager.send(gainExecutor, dataProvider, system);
        }
       
    };

    public void sendFinalGainEvent(DataProvider dataProvider, C2EntryService entryService) {
        for(GainListenerManager manager:gainListeners) {
            manager.sendFinal(gainExecutor, dataProvider, systems.get(entryService.systemId()));
        }
        awaitGainListeners();
        gainExecutor.shutdownNow();
    }

    public void awaitGainListeners() {
        Future<?> future = gainExecutor.submit(new Runnable() {
            @Override
            public void run() {
                //do nothing, just wait
            }
        });
        try {
            future.get();//blocks until this runnable completes
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return;
        } catch (ExecutionException e) {
            logger.warn("unable to wait for gain listener", e);
        }

    }

    public void addGainListener(long start, long period, GainListener listener) {

        gainListeners.add(new GainListenerManager(start,period,listener));

    }

    @Override
    public IterableXMLEventReader transmit(Request request) {
        try {
            return new IterableXMLEventReader(simpleTransmit(request));
        } catch (XMLStreamException e) {
            logger.warn("Simulator did not produce valid XML, should not happen.", e);
            throw new C2ServiceException(e,false);
        }
    };

    private XMLEventReader simpleTransmit(Request request) {

        logger.trace("transmit "+request.toString());

        synchronized(lock) {
            SystemManager system = null;
            if (request.containsKey(Parameter.SystemId)) {
                system = lookupSystem(request);
                if (system==null) {
                    return new SimulatedResponseError(ERROR,"no system","");
                }
            }

            XMLEventReader xmlEventReader;
            Command command = request.command();
            switch (command) {
                case Signal:

                     long timeToExecute       = extractTimeToExecute(request);
                     int[] signalIdArray      = system.scheduleSignal(timeToExecute,request);
                     int signalId             = signalIdArray[0];
                     int stopLossSignalId     = signalIdArray[1];
                     int profitTargetSignalId = signalIdArray[2];
                     return new SimulatedResponseSignal(signalId,
                                                        stopLossSignalId==SystemManager.NO_ID?null:stopLossSignalId,
                                                        profitTargetSignalId==SystemManager.NO_ID?null:profitTargetSignalId,
                                                        OK);

                case GetBuyPower:
                   //  long lastTickTime = lastTickDataProvider.endingTime();
                     BigDecimal buyPower = system.portfolio().cash().add(system.totalMargin());
                     return new SimulatedResponseGetBuyPower(OK,time,buyPower);

                case Cancel:

                    Integer id = (Integer)request.get(Parameter.SignalId);
                    assert(id>=0) : "DynamicSimulator only uses positive signalIds";

                    system.cancelSignal(id,time);
                    return new SimulatedResponseCancel(OK);

                case CancelAllPending:

                    system.cancelAllPending(time);
                    return new SimulatedResponseCancelAllPending(OK);

                case CloseAllPositions:

                    system.portfolio().closeAllPositions();
                    return new SimulatedResponseCloseAllPositions(OK);

                case FlushPendingSignals:

                    system.flushPendingSignals(lastTickDataProvider);
                    return new SimulatedResponseFlushPendingSignals(OK);

                case AllSignals:
                    //not filled, cancelled or expired!
                    //only for this subscriber
                    String subscriberEmail = (String)request.get(Parameter.EMail);
                    Map<Integer,List<Integer>> allPendingSignals = new HashMap<Integer,List<Integer>>();
                    for(SystemManager sys:systems) {
                        if (sys.isSubscribed(subscriberEmail)) {
                            Integer sysId = sys.id();
                            allPendingSignals.put(sysId,sys.allPendingSignals());
                        }
                    }
                    return new SimulatedResponseGetAllSignals(OK, allPendingSignals);

                case GetSystemEquity:

                    long lastTickTime = lastTickDataProvider.endingTime();
                    Number equity = system.portfolio().equity(lastTickDataProvider);
                    return new SimulatedResponseGetSystemEquity(OK,lastTickTime,equity);

                case GetSystemHypothetical:

                    List<Map<C2Element, Object>> data = new ArrayList<Map<C2Element, Object>>();

                    for(SystemManager sys:systems) {
                        Map<C2Element, Object> map = new HashMap<C2Element, Object>();
                        //populate this map for this system.
                        map.put(C2Element.ElementSystemId, sys.id());
                        map.put(C2Element.ElementSystemName, sys.name());

                        //NOTE: very basic margin calculations where negative cash is margin, should improve some day.
                        BigDecimal sysCash = sys.portfolio().cash();
                        BigDecimal sysEquity = sys.portfolio().equity(lastTickDataProvider);
                        BigDecimal totalEquityAvail = sysCash.add(sysEquity);
                        BigDecimal marginUsed = (sysCash.compareTo(BigDecimal.ZERO)<0 ? sysCash.negate() : BigDecimal.ZERO);

                        map.put(C2Element.ElementTotalEquityAvail, totalEquityAvail);
                        map.put(C2Element.ElementCash, sysCash);
                        map.put(C2Element.ElementEquity, sysEquity);
                        map.put(C2Element.ElementMarginUsed, marginUsed);

                        data.add(map);
                    }

                    return new SimulatedResponseGetSystemHypothetical(data);

                case PositionStatus:

                    String symbol = (String)request.get(Parameter.Symbol);
                    Integer position = system.portfolio().position(symbol).quantity();
                    return new SimulatedResponsePositionStatus(OK,time,symbol,position);

                case RequestOCAId:

                    Integer ocaid = system.generateNewOCAId();
                    return new SimulatedResponseRequestOCAId(ocaid,OK);

                case Reverse:
                    //the same as a signal but its done now and can not be parked
                    Integer reverseId = system.scheduleSignal(time, request)[0];
                    return new SimulatedResponseReverse(OK);

                case SendSubscriberBroadcast:

                    String email = (String)request.get(Parameter.EMail);
                    String message = (String)request.get(Parameter.Message);
                    system.sendSubscriberBroadcast(email,message);
                    return new SimulatedResponseSendSubscriberBroadcast(OK);

                case SetMinBuyPower:

                    Number minBuyPower = (Number)request.get(Parameter.BuyPower);
                    system.minBuyPower(minBuyPower);
                    return new SimulatedResponseSetMinBuyPower(OK);

                case NewComment:

                    Integer signalIdForComment = (Integer)request.get(Parameter.SignalId);
                    String comment   = (String)request.get(Parameter.Commentary);
                    String previousComment = system.newComment(signalIdForComment,comment);
                    return new SimulatedResponseNewComment(OK,signalIdForComment,previousComment);

                case AllSystems:     //lookup the systems here
                    throw new UnsupportedOperationException("Not implemented at this time");

                case AddToOCAGroup:   //lookup existing signalid and add it to this existing oca group
                    throw new UnsupportedOperationException("Not implemented at this time");

                case SignalStatus:

                    Integer signalIdInput = (Integer)request.get(Parameter.SignalId);
                    Integer systemId = SystemManager.extractSystemId(signalIdInput);

                    SystemManager systemForSignal = systems.get(systemId);


                    String signalSubscriberEmail = (String)request.get(Parameter.EMail);
                    boolean isSubscribed = systemForSignal.isSubscribed(signalSubscriberEmail);
                    if (!isSubscribed) {
                        return new SimulatedResponseError(ERROR,"email not subscribed",signalSubscriberEmail);
                    }

                    String subscriberPassword = (String)request.get(Parameter.Password);
                    boolean isSystemPassword = systemForSignal.isPassword(signalSubscriberEmail,subscriberPassword);
                    if (!isSystemPassword) {
                        return new SimulatedResponseError(ERROR,"not system password",signalSubscriberEmail);
                    }

                    Order order = systemForSignal.lookupOrder(signalIdInput);

                    String systemName = systemForSignal.name();
                    String postedwhen = order.postedWhen();
                    String emailedWhen = order.eMailedWhen();
                    String killedWhen = order.killedWhen();
                    String tradedWhen = order.tradedWhen();
                    BigDecimal tradePrice = order.tradePrice();

                    SimulatedResponseSignalStatus response;


                    Related showRelated = (Related)request.get(Parameter.ShowRelated);
                    Integer showDetails = (Integer)request.get(Parameter.ShowDetails);
                    response = new SimulatedResponseSignalStatus(
                                                                systemName,
                                                                (showDetails!=null && 1==showDetails.intValue()),
                                                                showRelated,
                                                                order);

                     return response;

            }
            throw new C2ServiceException("Unsupported command :" + command,false);
        }
    }

    private long extractTimeToExecute(Request request) {
        long timeToExecute = time;
        if (request.containsKey(Parameter.ParkUntil)) {
            timeToExecute = 1000l*(Long)request.get(Parameter.ParkUntil);// time in seconds
            request.remove(Parameter.ParkUntil);//allows tracking of what remains to be done
        }
        if (request.containsKey(Parameter.ParkUntilDateTime)) {
            String text = (String)request.get(Parameter.ParkUntilDateTime); //full time must convert to seconds
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddhhmmss");
            try {
                timeToExecute = sdf.parse(text).getTime();
            } catch (ParseException e) {
                logger.error("Unable to park signal, ran immediately", e);
            }
            request.remove(Parameter.ParkUntilDateTime);//allows tracking of what remains to be done
        }
        if (request.containsKey(Parameter.Delay)) {
            Integer delay = (Integer)request.get(Parameter.Delay);// delay n seconds
            timeToExecute += (delay*1000l);
            request.remove(Parameter.Delay);//allows tracking of what remains to be done
        }
        return timeToExecute;
    }


    private SystemManager lookupSystem(Request request) {

        String password = (String)request.get(Parameter.Password);
        Integer systemId = (Integer)request.get(Parameter.SystemId);
        if (systemId>systems.size()) {
            return null;
        }
        SystemManager system = systems.get(systemId);
        if (system.isPassword(password)) {
            return system;
        } else {
            return null;
        }

    }

    public Integer createSystem(BigDecimal buyPower, String name, String password, BigDecimal commission) {
        Portfolio portfolio = new SimplePortfolioFactory().createPortfolio(buyPower);
        return createSystem(name,password, portfolio, commission);
    }

    public synchronized Integer createSystem(String name, String password, Portfolio portfolio, BigDecimal commission) {

        Integer systemId = systems.size();

        SystemManager system = new SystemManager(portfolio, systemId, name, password, commission, marginAccount);

        systems.add(system);

        return systemId;
    }

    public void subscribe(String eMail, Integer systemId, String userPassword) {
        SystemManager system = systems.get(systemId);
        system.subscribe(eMail, userPassword);
    }
}
TOP

Related Classes of com.collective2.signalEntry.adapter.DynamicSimulationAdapter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.