/*
 * Copyright (c) 2015, StrategyQuant - All rights reserved.
 *
 * Code in this file was made in a good faith that it is correct and does what it should. 
 * If you found a bug in this code OR you have an improvement suggestion OR you want to include 
 * your own code snippet into our standard library please contact us at:
 * http://tasks.strategyquant.com/projects/snippets/
 *
 * This code can be used only within StrategyQuant products. 
 * Every owner of valid (free, trial or commercial) license of any StrategyQuant product 
 * is allowed to freely use, copy, modify or make derivative work of this code without limitations,
 * to be used in all StrategyQuant products and share his/her modifications or derivative work 
 * with the StrategyQuant community.
 *  
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
 * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES 
 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *  
 */
package com.strategyquant.extend.StatValues;

import java.util.ArrayList;

import com.strategyquant.lib.results.SQOrder;
import com.strategyquant.lib.results.SQOrderList;
import com.strategyquant.lib.results.SQStats;
import com.strategyquant.lib.results.StatValue;
import com.strategyquant.lib.results.StatsConst;
import com.strategyquant.lib.results.StatsTypeCombination;
import com.strategyquant.lib.settings.SQSettings;
import com.strategyquant.lib.utils.SQUtils;

public class ZScoreValues extends StatValue {
	private ArrayList<Double> tradesResults = new ArrayList<Double>();
	
	@Override
	public String dependsOn() {
		return StatsConst.AVG_TRADE;
	}
		
	@Override
	public void initCompute(SQStats stats, StatsTypeCombination combination, SQOrderList ordersList, SQSettings settings, SQStats statsLong, SQStats statsShort) throws Exception {
		tradesResults.clear();
	}

	@Override
	public void computeForOrder(SQStats stats, StatsTypeCombination combination, SQOrder order, SQSettings settings, SQStats statsLong, SQStats statsShort) throws Exception {
		tradesResults.add(order.PL);
	}

	@Override
	public void endCompute(SQStats stats, StatsTypeCombination combination, SQOrderList ordersList, SQSettings settings, SQStats statsLong, SQStats statsShort) throws Exception {
		double avgTrade = stats.getDouble(StatsConst.AVG_TRADE);
		
		double zScore = computeZIndex(tradesResults, avgTrade);		
		stats.set(StatsConst.Z_SCORE, SQUtils.round2(zScore));
		
		tradesResults.clear();
	}

	private double sign(double val) {
		if (val<0) return -1;
		return 1;
	}
	
    private double computeZIndex(ArrayList<Double> tradesResults, double avgTrade) {
    	if(tradesResults.size() <= 0) return 0;
    	
    	int W = 0, L = 0, i, size = tradesResults.size();
    	double Z,x,r,w,l,n;
    	double Sn = 0;
    	int Seria[] = new int[size];
    	 
    	n=size;
    	double pl, plPrev;
    	
    	// Skewness
    	for (i=0;i<size;i++) {
    		pl = tradesResults.get(i);
    		if(i == 0) {
    			plPrev = 0;
    		} else {
    			plPrev = tradesResults.get(i-1);
    		}
    		
    		Sn = Sn + Math.pow(pl-avgTrade,2f);
    		if (i==0) Seria[i]=1;
    		
    		if(i > 0) {
    			if (sign(pl)*sign(plPrev)<0) {
    				Seria[i]=Seria[i-1]+1;
    			} else {
    				Seria[i]=Seria[i-1];
    			}
    		} else {
    			Seria[i] = 0;
    		}
    	    if (pl>=0) W++;
    	    if (pl<0) L++;
    	}
    	
    	if (n>1) {
    		Sn= Math.sqrt(Sn/(n-1f));
    	}
    	 
    	r=Seria[size-1];
    	w=W;
    	l=L;
    	
    	if (l>0 && w>0) {   
    		x = 2f * w*l;

    		Z = (n*(r-0.5f)-x)/(Math.sqrt((x*(x-n))/(n-1f)));
    	} else {
    		if (l==0) {
    			Z=100000;
    		} else {
    			Z=-100000;
    		}
    	}

    	return(Z);   
	}
 
}