/*
 * 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.EquityControl;

import com.strategyquant.lib.language.L;
import com.strategyquant.lib.snippets.Trade;
import com.strategyquant.lib.snippets.EquityControlSim.EquityControl;

public class BBLow extends EquityControl {

	public BBLow() {
		setName(L.t("BB Low"));
		
		addIntParameter("Period", L.t("Period"), 20, 2, 1000, 1);
		addDoubleParameter("Deviation", L.t("Deviation"), 2d, 0d, 10d, 2d);
	}

	/**
	 * computes control line that will be drawn on chart and used to compute new equity
	 * 
	 * If you need to check value of something for debugging purpose you can use function logToFile(fileName, text).
	 * Example: logToFile("debug_file", trades[1].openTime)
	 *
	 *           
	 * Trade structure:
	 *   .openTime - opening time of trade in seconds from 1.1.1970
	 *   .balanceOnOpen - actual account balance on trade open
	 *   .pl - trade resulting Profit/Loss in %
	 *   .size - trade size            
	 *                         
	 * @param originalTrades - list of original trades
	 * @return - return an array of double values that represent computed equity control line 
	 * that will be used to control the equity.
	 */
	@Override
	public double[] computeBalanceControlLine(Trade[] originalTrades) throws Exception {
		double[] controlLine = new double[originalTrades.length];
		
		int period = getIntParameterValue("Period");
		
		// computes Simple Moving Average of trades equity (balance)
		for(int i=0; i<originalTrades.length; i++) {
			if( i<= period) {
				controlLine[i] = VALUE_UNDEFINED;
			} else {
				controlLine[i] = computeLowerBBand(originalTrades, i, period);
			}
		}
		
		return controlLine;
	}

	private double computeLowerBBand(Trade[] originalTrades, int shift,	int period) throws Exception {
		double avg = 0; // for avg computed from acc. balance
		Trade trade;

		// first compute MA
		for(int i = shift-1; i > shift - 1 - period; i--) {
			trade = originalTrades[i];

			avg += originalTrades[i].balanceOnOpen;
		}

		avg = avg / (period);
		
		double diff = 0;
		double sum = 0;
		
		double bbDeviation = getDoubleParameterValue("Deviation");
		
		// now compute deviation and lower BB
		for(int i = shift-1; i > shift - 1 - period; i--) {
			trade = originalTrades[i];
	         
			diff = originalTrades[i].balanceOnOpen - avg;

			sum += diff * diff;
		}			
		
		sum /= (period);
		double dev = bbDeviation * Math.sqrt(sum);
		
		return avg - dev;
	}
	
	/**
	 * Function receives list of trades sorted by open time and computed control line.
	 * It computes new size for every trade.
	 * 
	 * The standard approach is that is balance at open of trade is bigger than control line then take the trade (keep size unchanged)
	 * otherwise skip the trade (set trade size = 0).           
	 * You can also modify the original trade size to use more advanced money management scheme - 
	 *  for example if balance is below equity don't skip the trade, but trade with only half of the size.  
	 * 
	 * If you need to check value of something for debugging purpose you can use function logToFile(fileName, text).
	 * Example: logToFile("debug_file", trades[1].openTime)
	 * 
	 *                    
	 * Trade structure:
	 *   .openTime - opening time of trade in seconds from 1.1.1970
	 *   .balanceOnOpen - actual account balance on trade open
	 *   .pl - trade resulting Profit/Loss in %
	 *   .size - trade size
	 *                    
	 * @param trades - list of original trades. Each trade has account balance on open accessible through .balanceOnOpen property
	 * @param balanceControlLine - controlline computed using function computeControlLine()    
	 */    
	@Override
	public void computeNewTradeSizes(Trade[] trades, double[] balanceControlLine) {
    	for(int i=0; i<trades.length; i++) {
                		
            if(balanceControlLine[i] == VALUE_UNDEFINED || trades[i].balanceOnOpen >= balanceControlLine[i]) {
                // control line is either undefined
    			// or equity is above control line
    			// do nothing, take the trade without any change      
    		} else {
                // equity is below control line, miss the trade
    			trades[i].size = 0f;
    		}
    	}
    }

}