model.utilities.stats.collectors.PeriodicMarketObserver.java Source code

Java tutorial

Introduction

Here is the source code for model.utilities.stats.collectors.PeriodicMarketObserver.java

Source

/*
 * Copyright (c) 2014 by Ernesto Carrella
 * Licensed under MIT license. Basically do what you want with it but cite me and don't sue me. Which is just politeness, really.
 * See the file "LICENSE" for more information
 */

package model.utilities.stats.collectors;

import au.com.bytecode.opencsv.CSVWriter;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Doubles;
import com.google.common.primitives.Ints;
import financial.market.Market;
import model.MacroII;
import model.utilities.ActionOrder;
import model.utilities.Deactivatable;
import model.utilities.scheduler.Priority;
import model.utilities.stats.collectors.enums.MarketDataType;
import org.apache.commons.collections15.Transformer;
import sim.engine.SimState;
import sim.engine.Steppable;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;

/**
 * <h4>Description</h4>
 * <p/> This is a class that observes every now and then price and quantities of a market. This can then be used to run regressions on
 * or whatever.
 * <p/> Transformers can be used to modify observations when recording them.
 * <p/>
 * <h4>Notes</h4>
 * Created with IntelliJ
 * <p/>
 * <p/>
 * <h4>References</h4>
 *
 * @author carrknight
 * @version 2013-08-04
 * @see
 */
public class PeriodicMarketObserver implements Steppable, Deactivatable {

    /**
     * an optional writer so we can output observations
     */
    private CSVWriter writer;

    /**
     * The market we are going to observe!
     */
    private final Market market;

    /**
     * the days were observations made sense
     */
    private final ArrayList<Integer> days;

    private MarketData.MarketDataAcceptor acceptor = new MarketData.MarketDataAcceptor() {
        @Override
        public boolean acceptDay(Double lastPrice, Double volumeTraded, Double volumeProduced,
                Double volumeConsumed, Double demandGap, Double supplyGap) {
            return lastPrice != -1;

        }
    };

    private boolean isActive = true;

    /**
     * A function we can put in to transform the observed price before reading it in
     */

    private Transformer<Double, Double> priceTransformer;

    /**
     *  The inverse function of price transform, required for prediction
     */

    private Transformer<Double, Double> priceInverseTransformer;

    /**
     * A function we can put in to transform the observed price before reading it in
     */

    private Transformer<Double, Double> quantityTransformer;

    /**
     * The probability each day the predictor will memorize today's price and quantity as an observation
     */
    private float dailyProbabilityOfObserving = defaultDailyProbabilityOfObserving; //this corresponds to weekly

    /**
     * The probability of observing placed at constructor if no other is specified
     */
    public static final float defaultDailyProbabilityOfObserving = 0.142f;

    /**
     * if "isExact" is set to true, the market observer doesn't check "at random" but at fixed intervals of size 1/probabilityOfObserving
     */
    public boolean isExact = true;

    /**
     * When created, it automatically schedules itself. It also register itself as deactivable so that it will be turned
     * off when MacroII is finished
     * @param market a link to the market to observe
     * @param macroII a link to the model (to reschedule oneself)
     */
    public PeriodicMarketObserver(Market market, MacroII macroII) {

        this(market, macroII, defaultDailyProbabilityOfObserving);
    }

    public PeriodicMarketObserver(Market market, MacroII macroII, float dailyProbabilityOfObserving) {
        this.market = market;
        this.dailyProbabilityOfObserving = dailyProbabilityOfObserving;
        days = new ArrayList<>();

        if (dailyProbabilityOfObserving < 1) {
            reschedule(macroII);
        } else {
            assert dailyProbabilityOfObserving == 1;
            macroII.scheduleSoon(ActionOrder.CLEANUP_DATA_GATHERING, this, Priority.AFTER_STANDARD);
        }
    }

    private void reschedule(MacroII macroII) {

        if (!isExact)
            macroII.scheduleAnotherDayWithFixedProbability(ActionOrder.CLEANUP_DATA_GATHERING, this,
                    dailyProbabilityOfObserving, Priority.AFTER_STANDARD);
        else
            macroII.scheduleAnotherDay(ActionOrder.CLEANUP_DATA_GATHERING, this,
                    Math.max(1, Math.round(1f / dailyProbabilityOfObserving)), Priority.AFTER_STANDARD);

    }

    /**
     * get the last (newest) observation of price
     * @return
     */
    public Double getLastPriceObserved() {
        double observation = market.getObservationRecordedThisDay(MarketDataType.CLOSING_PRICE,
                days.get(days.size() - 1));
        if (priceTransformer != null)
            observation = priceTransformer.transform(observation);
        return observation;
    }

    /**
     * get the last (newest) observation of quantity
     * @return
     */
    public Double getLastQuantityTradedObserved() {
        double observation = market.getObservationRecordedThisDay(MarketDataType.VOLUME_TRADED,
                days.get(days.size() - 1));
        if (quantityTransformer != null)
            observation = quantityTransformer.transform(observation);
        return observation;
    }

    /**
     * get the last (newest) observation of quantity
     * @return
     */
    public Double getLastQuantityConsumedObserved() {
        double observation = market.getObservationRecordedThisDay(MarketDataType.VOLUME_CONSUMED,
                days.get(days.size() - 1));
        if (quantityTransformer != null)
            observation = quantityTransformer.transform(observation);
        return observation;
    }

    /**
     * get the last (newest) observation of quantity
     * @return
     */
    public Double getLastQuantityProducedObserved() {

        double observation = market.getObservationRecordedThisDay(MarketDataType.VOLUME_PRODUCED,
                days.get(days.size() - 1));
        if (quantityTransformer != null)
            observation = quantityTransformer.transform(observation);
        return observation;

    }

    /**
     * get the last (newest) observation day
     * @return
     */
    public Double getLastDayObserved() {
        return Double.valueOf(days.get(days.size() - 1));
    }

    public int[] getDaysObserved() {
        return Ints.toArray(days);

    }

    @Override
    public void turnOff() {
        isActive = false;

    }

    @Override
    public void step(SimState state) {
        //stop if you are activated
        if (!isActive)
            return;

        //make sure it's the right model
        Preconditions.checkState(state instanceof MacroII);
        MacroII model = (MacroII) state;

        //if some trade actually occurred:
        if (isLastDayAcceptable()) {
            days.add(market.getLastObservedDay());
            if (writer != null)
                outputToFile();
        }

        //reschedule yourself
        reschedule(model);

    }

    private boolean isLastDayAcceptable() {
        if (market.getNumberOfObservations() == 0)
            return false;

        return acceptor.acceptDay(market.getLatestObservation(MarketDataType.CLOSING_PRICE),
                market.getLatestObservation(MarketDataType.VOLUME_TRADED),
                market.getLatestObservation(MarketDataType.VOLUME_PRODUCED),
                market.getLatestObservation(MarketDataType.VOLUME_CONSUMED),
                market.getLatestObservation(MarketDataType.DEMAND_GAP),
                market.getLatestObservation(MarketDataType.SUPPLY_GAP));

    }

    public void attachCSVWriter(CSVWriter writer) throws IOException {
        this.writer = writer;
        LinkedList<String> row = new LinkedList<>();

        row.add("price");
        row.add("traded");
        row.add("produced");
        row.add("consumed");
        row.add("day");
        row.add("demand gap");
        row.add("supply gap");

        writer.writeNext(row.toArray(new String[row.size()]));
        writer.flush();
    }

    private void outputToFile() {
        Preconditions.checkState(writer != null);
        LinkedList<String> row = new LinkedList<>();

        row.add(Double.toString(getLastPriceObserved()));
        row.add(Double.toString(getLastQuantityTradedObserved()));
        row.add(Double.toString(getLastQuantityProducedObserved()));
        row.add(Double.toString(getLastQuantityConsumedObserved()));
        row.add(Double.toString(getLastDayObserved()));
        row.add(Double.toString(
                market.getObservationRecordedThisDay(MarketDataType.DEMAND_GAP, days.get(days.size() - 1))));
        row.add(Double.toString(
                market.getObservationRecordedThisDay(MarketDataType.SUPPLY_GAP, days.get(days.size() - 1))));

        writer.writeNext(row.toArray(new String[row.size()]));
        try {
            writer.flush();
        } catch (IOException e) {

        }

    }

    /**
     * Copies observed prices into a double[] and return it. Useful for regressions and other manipulations
     * @return
     */
    public double[] getPricesObservedAsArray() {
        double[] prices = market.getObservationsRecordedTheseDays(MarketDataType.CLOSING_PRICE, Ints.toArray(days));
        //if needed, transform
        if (priceTransformer != null)
            for (int i = 0; i < prices.length; i++)
                prices[i] = priceTransformer.transform(prices[i]);

        return prices;

    }

    /**
     * Copies quantities observed into a double[] and return it. Useful for regressions and other manipulations
     * @return
     */
    public double[] getQuantitiesTradedObservedAsArray() {
        double[] quantities = market.getObservationsRecordedTheseDays(MarketDataType.VOLUME_TRADED,
                Ints.toArray(days));
        //if needed, transform
        if (quantityTransformer != null)
            for (int i = 0; i < quantities.length; i++)
                quantities[i] = quantityTransformer.transform(quantities[i]);

        return quantities;
    }

    /**
     * Copies quantities observed into a double[] and return it. Useful for regressions and other manipulations
     * @return
     */
    public double[] getQuantitiesProducedObservedAsArray() {
        double[] quantities = market.getObservationsRecordedTheseDays(MarketDataType.VOLUME_PRODUCED,
                Ints.toArray(days));
        //if needed, transform
        if (quantityTransformer != null)
            for (int i = 0; i < quantities.length; i++)
                quantities[i] = quantityTransformer.transform(quantities[i]);

        return quantities;
    }

    /**
     * Copies quantities observed into a double[] and return it. Useful for regressions and other manipulations
     * @return
     */
    public double[] getQuantitiesConsumedObservedAsArray() {
        double[] quantities = market.getObservationsRecordedTheseDays(MarketDataType.VOLUME_CONSUMED,
                Ints.toArray(days));
        //if needed, transform
        if (quantityTransformer != null)
            for (int i = 0; i < quantities.length; i++)
                quantities[i] = quantityTransformer.transform(quantities[i]);

        return quantities;
    }

    /**
     * Copies observation days into a double[] and return sit. Useful for regressions and other manipulations
     * @return
     */
    public double[] getObservationDaysAsArray() {
        return Doubles.toArray(days);
    }

    /**
     * Copies quantities observed into a double[] and return it. Useful for regressions and other manipulations
     * @return
     */
    public double[] getDemandGapsAsArray() {
        return market.getObservationsRecordedTheseDays(MarketDataType.DEMAND_GAP, Ints.toArray(days));

    }

    /**
     * Copies quantities observed into a double[] and return it. Useful for regressions and other manipulations
     * @return
     */
    public double[] getSupplyGapsAsArray() {
        return market.getObservationsRecordedTheseDays(MarketDataType.SUPPLY_GAP, Ints.toArray(days));
    }

    /**
     * if this is called, the observer will forget the oldest set of observations (prices, quantities, day)
     */
    public void forgetOldestObservation() {

        int size = getNumberOfObservations();
        days.remove(0);

        assert size == getNumberOfObservations() + 1;

    }

    public int getNumberOfObservations() {
        //all sizes should be the same

        return days.size();

    }

    /**
     * Sets new A function we can put in to transform the observed price before reading it in.
     *
     * @param quantityTransformer New value of A function we can put in to transform the observed price before reading it in.
     */
    public void setQuantityTransformer(Transformer<Double, Double> quantityTransformer) {
        this.quantityTransformer = quantityTransformer;
    }

    /**
     * Gets A function we can put in to transform the observed price before reading it in.
     *
     * @return Value of A function we can put in to transform the observed price before reading it in.
     */
    public Transformer<Double, Double> getQuantityTransformer() {
        return quantityTransformer;
    }

    /**
     * Sets new A function we can put in to transform the observed price before reading it in.
     *
     * @param priceTransformer New value of A function we can put in to transform the observed price before reading it in.
     */
    public void setPriceTransformer(Transformer<Double, Double> priceTransformer,
            Transformer<Double, Double> priceInverseTransformer) {
        this.priceTransformer = priceTransformer;
        this.priceInverseTransformer = priceInverseTransformer;
    }

    /**
     * Gets A function we can put in to transform the observed price before reading it in.
     *
     * @return Value of A function we can put in to transform the observed price before reading it in.
     */
    public Transformer<Double, Double> getPriceTransformer() {
        return priceTransformer;
    }

    public Transformer<Double, Double> getPriceInverseTransformer() {
        return priceInverseTransformer;
    }

    public double getLastUntrasformedQuantityTraded() {
        return market.getObservationRecordedThisDay(MarketDataType.VOLUME_TRADED, days.get(days.size() - 1));
    }

    public double getLastUntrasformedPrice() {
        return market.getObservationRecordedThisDay(MarketDataType.CLOSING_PRICE, days.get(days.size() - 1));
    }

    public float getDailyProbabilityOfObserving() {
        return dailyProbabilityOfObserving;
    }

    public void setDailyProbabilityOfObserving(float dailyProbabilityOfObserving) {
        this.dailyProbabilityOfObserving = dailyProbabilityOfObserving;
    }

    public boolean isExact() {
        return isExact;
    }

    public void setExact(boolean exact) {
        isExact = exact;
    }

}