/*
 * 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 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.time.SQTime;
import com.strategyquant.lib.utils.SQUtils;

public class StagnationValues extends StatValue {
	private SQOrder veryFirstOrder = null;
	private SQOrder veryLastOrder = null;
	private SQOrder bestStagnationFirstOrder = null;
	private SQOrder bestStagnationLastOrder = null;
	
	private long bestStagnationPeriod = 0;
	private long stagnationPeriod = 0;
	
	private double accountBalance = 0;
	private double latestProfitHigh = 0;
	private SQOrder peakOrder = null;
	
	private boolean stagnationActive = false;
	private int totalDays = 0;
	
	private SQTime dt = new SQTime();
	
	@Override
	public void initCompute(SQStats stats, StatsTypeCombination combination, SQOrderList ordersList, SQSettings settings, SQStats statsLong, SQStats statsShort) throws Exception {
		this.veryFirstOrder = null;
		this.veryLastOrder = null;
		this.bestStagnationFirstOrder = null;
		this.bestStagnationLastOrder = null;
		
		this.bestStagnationPeriod = 0;
		this.stagnationPeriod = 0;
		
		this.accountBalance = 0;
		this.latestProfitHigh = Double.MAX_VALUE * -1;
		this.peakOrder = ordersList.size() > 0 ? ordersList.get(0) : null;
		
		this.stagnationActive = false;
		
		if(!ordersList.isEmpty()) {
			long veryFirstOrderOpen = ordersList.get(0).OpenTime;		
			long veryLastOrderClose = findVeryLastOrderCloseTime(ordersList);
			
			this.totalDays = dt.getDaysBetween(veryFirstOrderOpen, veryLastOrderClose);
		}
	}

	private long findVeryLastOrderCloseTime(SQOrderList ordersList) throws Exception {
		long veryLastOrderTime = 0;
		
		for(SQOrder order : ordersList) {
			if(order.CloseTime > veryLastOrderTime) {
				veryLastOrderTime = order.CloseTime;
			}
		}
		
		return veryLastOrderTime;
	}
		
	@Override
	public void computeForOrder(SQStats stats, StatsTypeCombination combination, SQOrder order, SQSettings settings, SQStats statsLong, SQStats statsShort) throws Exception {
		double pl = getPLByStatsType(order, combination);
		
		accountBalance += pl;
		
		if(accountBalance > latestProfitHigh) {
			if(stagnationActive && peakOrder != null) {
				// there was drawdown and now we are going up
         		stagnationPeriod = order.OpenTime - peakOrder.CloseTime;
         		if(stagnationPeriod > bestStagnationPeriod) {
         			bestStagnationFirstOrder = peakOrder;
         			bestStagnationLastOrder = order;
         			bestStagnationPeriod = stagnationPeriod;
         		}
         	}
         	
         	peakOrder = order;
         	latestProfitHigh = accountBalance;
         	stagnationActive = false;
         } else {
         	stagnationActive = true;
         }
		
		 veryLastOrder = order;
	}

	@Override
	public void endCompute(SQStats stats, StatsTypeCombination combination, SQOrderList ordersList, SQSettings settings, SQStats statsLong, SQStats statsShort) throws Exception {
        long stagnationFrom=0;
        long stagnationTo=0;
        double stagnationPeriodPct=0;
		        
		if(stagnationActive && veryLastOrder != null && peakOrder != null) {
        	stagnationPeriod = veryLastOrder.OpenTime - peakOrder.CloseTime;
        	
        	if(stagnationPeriod > bestStagnationPeriod) {
        		bestStagnationFirstOrder = peakOrder;
        		bestStagnationLastOrder = veryLastOrder;
        		bestStagnationPeriod = stagnationPeriod;
        	}
        }

        if(bestStagnationFirstOrder == null) {
        	bestStagnationFirstOrder = veryFirstOrder;
        }
        if(bestStagnationLastOrder == null) {
        	bestStagnationLastOrder = veryLastOrder;
        }
        
        if(bestStagnationFirstOrder != null) {
        	stagnationFrom = bestStagnationFirstOrder.OpenTime;
        } else {
        	stagnationFrom = 0;
        }

        if(bestStagnationLastOrder != null) {
        	stagnationTo = bestStagnationLastOrder.CloseTime;
        } else {
        	stagnationTo = 0;
        }
        
        if(stagnationFrom != 0 && stagnationTo != 0) {
        	stagnationPeriod = dt.getDaysBetween(stagnationFrom, stagnationTo);
        } else {
        	stagnationPeriod = 0;
        }  
		
		stats.set(StatsConst.STAGNATION_FROM, stagnationFrom);
		stats.set(StatsConst.STAGNATION_TO, stagnationTo);
		stats.set(StatsConst.STAGNATION_PERIOD, (int)stagnationPeriod);
		
		stagnationPeriodPct = SQUtils.safeDivide(stagnationPeriod, totalDays) * 100d;
		stats.set(StatsConst.STAGNATION_PERIOD_PCT, SQUtils.round2(stagnationPeriodPct));
	}

}