Not logged in
Viewing 6 posts - 1 through 6 (of 6 total)

Forums>StrategyQuant>General Discussion>Possible new way to randomize data with %change instead of %ATR

  • #267915 |
    Customer
    68 Posts

    As per here: https://roadmap.strategyquant.com/tasks/sq4_7233
    and here:  https://roadmap.strategyquant.com/tasks/sq4_7350
    The current way is not perfect and some strategies can exploit the randomize history data algo while others suffer from the bias of this test. I believe this is because of the way ATR is used in the algo combined with the fact that the new randomized position is always based on the  next original data tick (which will have an extra revert-to-the-mean effect and higher x% of ATR will increase volatility.)

    In this new method, I randomize the % of price change instead % of ATR and allow price to set a new path so each tick is based on the last tick of the randomized data (allowing price to slowly wander far away from the original data, eliminating the excessive reversion to the mean effect.) This should maintain volatility while randomizing data as long as the % change is reasonable.

    I open this thread for the discussion and testing. It is a work in progress at this time. I am quite unfamiliar with java so I could use a hand with the code and with testing.

    I had time to work on this today and here’s what I have so far but you may wish to check for new versions later in this thread as we progress and post new versions.


     

     

     

     

     

    #267956
    Customer
    68 Posts

    OK looks like I fixed that first tick bug properly this time

     

    /*
     * Copyright (c) 2017-2018, 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:
     * https://roadmap.strategyquant.com
     *
     * 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 SQ.MonteCarlo.Retest;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import com.strategyquant.lib.*;
    import com.strategyquant.datalib.*;
    import com.strategyquant.datalib.consts.Precisions;
    import com.strategyquant.tradinglib.*;
    
    @ClassConfig(name="Randomize history data", display="Randomize history data v2.0, with max price change #MaxChange# % of PRICE")
    @Help("<b>Note!</b>If you choose this option then robustness tests will be tested with Selected Timeframe precision, while original strategy will be tested with configured precision.<br/>This might cause difference in results between original strategy and simulations because of different precision.<br/>To avoid this, use Selected Timeframe precision in Setings : Data.")
    public class RandomizeHistoryData2 extends MonteCarloRetest {
    	public static final Logger Log = LoggerFactory.getLogger(RandomizeHistoryData2.class);
    	
    	//@Parameter(name="Probability change price", defaultValue="20", minValue=1, maxValue=100, step=1)
    	//public int Probability;
    	
    	@Parameter(name="Max change price", defaultValue="10", minValue=1, maxValue=100, step=1)
    	public int MaxChange;
    	
    	private int barsProcessed = 0;
    	private double atrValue = 0;
    	private int atrPeriod = 14;
    	
    	private int relativeMaxChange = -1;
    	
    	
    	//bendedit: we need some persistant variables so we can track price and allow it to wander away from original data
    	private double lastbid=0;
    	private double lastorgbid=0;
    	private long lastticktime=0;
    	
    	
    	//------------------------------------------------------------------------
    	//------------------------------------------------------------------------
    	//------------------------------------------------------------------------
    
    	public RandomizeHistoryData2() {
    		super(MonteCarloTestTypes.ModifyData);
    	}
    	
    	//------------------------------------------------------------------------
    
    	public void modifyData(IRandomGenerator rng, TickEvent tickEvent, double globalATR) {
    		//double dblProbability = ((double) Probability/ 100.0d);
    		
    			
    		//if(rng.probability(dblProbability)) {
    			// bendedit: I don't see a point in probability of change, lets just always change every tick, it will be much smoother now that we turfed ATR from the equation.
    
    			// we should change this price
    
    			double ask = tickEvent.getAsk();
    			double bid = tickEvent.getBid();
    			double spread = ask - bid;
    			double tickorgchange=0;
    			long currentticktime = tickEvent.getTime();
    			
    			//bendedit: check if first tick, if not then set original tick change.
    			if (lastticktime <= currentticktime && lastticktime!=0) {
    				//bendedit: calculate the price change from last original tick to the current tick from original data
    				tickorgchange=bid-lastorgbid;
    				//debug("MC price randomizer", "tickorgchange is: "+tickorgchange);
    				//debug("MC price randomizer", "time is: "+currentticktime);
    				//debug("MC price randomizer", "last time is: "+lastticktime);
    				//debug("MC price randomizer", "EOL");
    			} else { //bendedit: in case of first tick there is no change.
    				lastbid=bid;
    				tickorgchange=0;
    			}
    			
    			//bendedit:set last original data bid in to variable before we change bid, we only need it to calculate the price change next time.
    			lastorgbid=bid;
    			
    			//bendedit:store the ticktime.
    			lastticktime = currentticktime;
    
    			
    			int change;
    			
    			if(relativeMaxChange <= 0) {
    				change = rng.nextInt(MaxChange);
    			} else {
    				change = rng.nextInt(relativeMaxChange);
    			}
    			
    			double dblChange = ((double) change)/ 10000.0d; 
    			//bendedit: Modding every tick and allowing price to move far away from original data therefore we need to use much smaller adjustments.
    			//bendedit: I chose a percent of a percent
    
    			//bendedit: changed from using ATR to just use tick price change of original data.
    			double priceChange = tickorgchange * dblChange;
    			
    			
    			//bendedit: set the working bid relative to the last bid instead of itself
    			//bid = (rng.nextInt(2) == 0 ? bid + priceChange : bid - priceChange);
    			bid = (rng.nextInt(2) == 0 ? lastbid + tickorgchange + priceChange : lastbid + tickorgchange - priceChange);
    			
    			tickEvent.setBid(bid);
    			tickEvent.setAsk(bid + spread);
    			
    			//bendedit:set last bid in to variable for next time.
    			lastbid = bid; 
    
    			
    		//}
    	}
    
    	//------------------------------------------------------------------------
    	
    	public void initSettings(SettingsMap settings) {
    		super.initSettings(settings);
    		
    		// compute relative Max Change according to chart TF and precision
    		ChartSetup chartSetup = (ChartSetup) settings.get(SettingsKeys.BacktestChart);
    		if(chartSetup != null) {
    			int testPrecision = chartSetup.getTestPrecision();
    			
    			if(testPrecision == Precisions.SelectedTF) {
    				// do nothing for selected TF precision
    				relativeMaxChange = MaxChange;
    				return;
    			}
    			
    			// it is higher precision, set MaxChange to 1/3 of the value
    			// because the change is applied o every minute bar / tick we have to lower the MaxChange 
    			// so that the resulting full bar in target timeframe (for example H1) isn't changed too much
    			
    			//bendedit: Hopefully we don't need this adjustment anymore since we aren't using globalATR which is inflated by higher timeframes. deviding by 3 arbitrarily was suspect anyways. If we still need to adjust, I'll hopefully find a better way.
    			//bendedit: Now that we are doing change % instead of atr %, we may not need any further adjustments relative to precision or TF. Turned off this equalizer for now.
    			//relativeMaxChange = MaxChange / 3;
    			relativeMaxChange = MaxChange;
    			if(relativeMaxChange <= 0) {
    				relativeMaxChange = 1;
    			}
    			
    		}
    	}	
    
    }

     

    #267958
    Customer
    68 Posts

    This one works on b130

    /*
     * Copyright (c) 2017-2018, 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:
     * https://roadmap.strategyquant.com
     *
     * 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 SQ.MonteCarlo.Retest;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import com.strategyquant.lib.*;
    import com.strategyquant.datalib.*;
    import com.strategyquant.datalib.consts.Precisions;
    import com.strategyquant.tradinglib.*;
    
    @ClassConfig(name="Randomize history data", display="Randomize history data v2.0, with max change #MaxChange# % of TICK PRICE CHANGES")
    @Help("<b>Note!</b>If you choose this option then robustness tests will be tested with Selected Timeframe precision, while original strategy will be tested with configured precision.<br/>This might cause difference in results between original strategy and simulations because of different precision.<br/>To avoid this, use Selected Timeframe precision in Setings : Data.")
    public class RandomizeHistoryData2 extends MonteCarloRetest {
    	public static final Logger Log = LoggerFactory.getLogger(RandomizeHistoryData2.class);
    	
    	//@Parameter(name="Probability change price", defaultValue="20", minValue=1, maxValue=100, step=1)
    	//public int Probability;
    	
    	@Parameter(name="Max change price", defaultValue="10", minValue=1, maxValue=100, step=1)
    	public int MaxChange;
    	
    	private int barsProcessed = 0;
    	private double atrValue = 0;
    	private int atrPeriod = 14;
    	
    	private int relativeMaxChange = -1;
    	
    	
    	//bendedit: we need some persistant variables so we can track price and allow it to wander away from original data
    	private double lastbid=0;
    	private double lastorgbid=0;
    	private long lastticktime=0;
    	
    	
    	//------------------------------------------------------------------------
    	//------------------------------------------------------------------------
    	//------------------------------------------------------------------------
    
    	public RandomizeHistoryData2() {
    		super(MonteCarloTestTypes.ModifyData);
    	}
    	
    	//------------------------------------------------------------------------
    
    	public void modifyData(IRandomGenerator rng, TickEvent tickEvent, double globalATR) {
    		//double dblProbability = ((double) Probability/ 100.0d);
    		
    			
    		//if(rng.probability(dblProbability)) {
    			// bendedit: I don't see a point in probability of change, lets just always change every tick, it will be much smoother now that we turfed ATR from the equation.
    
    			// we should change this price
    
    			double ask = tickEvent.getAsk();
    			double bid = tickEvent.getBid();
    			double spread = ask - bid;
    			double tickorgchange=0;
    			long currentticktime = tickEvent.getTime();
    			
    			//bendedit: check if first tick, if not then set original tick change.
    			if (lastticktime <= currentticktime && lastticktime!=0) {
    				//bendedit: calculate the price change from last original tick to the current tick from original data
    				tickorgchange=bid-lastorgbid;
    				//debug("MC price randomizer", "tickorgchange is: "+tickorgchange);
    				//debug("MC price randomizer", "time is: "+currentticktime);
    				//debug("MC price randomizer", "last time is: "+lastticktime);
    				//debug("MC price randomizer", "EOL");
    			} else { //bendedit: in case of first tick there is no change.
    				lastbid=bid;
    				tickorgchange=0;
    			}
    			
    			//bendedit:set last original data bid in to variable before we change bid, we only need it to calculate the price change next time.
    			lastorgbid=bid;
    			
    			//bendedit:store the ticktime.
    			lastticktime = currentticktime;
    
    			
    			int change;
    			
    			if(relativeMaxChange <= 0) {
    				change = rng.nextInt(MaxChange);
    			} else {
    				change = rng.nextInt(relativeMaxChange);
    			}
    			
    			double dblChange = ((double) change)/ 10000.0d; 
    			//bendedit: Modding every tick and allowing price to move far away from original data therefore we need to use much smaller adjustments.
    			//bendedit: I chose a percent of a percent
    
    			//bendedit: changed from using ATR to just use tick price change of original data.
    			double priceChange = tickorgchange * dblChange;
    			
    			
    			//bendedit: set the working bid relative to the last bid instead of itself
    			//bid = (rng.nextInt(2) == 0 ? bid + priceChange : bid - priceChange);
    			bid = (rng.nextInt(2) == 0 ? lastbid + tickorgchange + priceChange : lastbid + tickorgchange - priceChange);
    			
    			tickEvent.setBid(bid);
    			tickEvent.setAsk(bid + spread);
    			
    			//bendedit:set last bid in to variable for next time.
    			lastbid = bid; 
    
    			
    		//}
    	}
    
    	//------------------------------------------------------------------------
    	
    	public void initSettings(SettingsMap settings) {
    		super.initSettings(settings);
    		
    		// compute relative Max Change according to chart TF and precision
    		ChartSetup chartSetup = (ChartSetup) settings.get(SettingsKeys.BacktestChart);
    		if(chartSetup != null) {
    			int testPrecision = chartSetup.getTestPrecision();
    			
    			if(testPrecision == Precisions.SelectedTF) {
    				// do nothing for selected TF precision
    				relativeMaxChange = MaxChange;
    				return;
    			}
    			
    			// it is higher precision, set MaxChange to 1/3 of the value
    			// because the change is applied o every minute bar / tick we have to lower the MaxChange 
    			// so that the resulting full bar in target timeframe (for example H1) isn't changed too much
    			
    			//bendedit: Hopefully we don't need this adjustment anymore since we aren't using globalATR which is inflated by higher timeframes. deviding by 3 arbitrarily was suspect anyways. If we still need to adjust, I'll hopefully find a better way.
    			//bendedit: Now that we are doing change % instead of atr %, we may not need any further adjustments relative to precision or TF. Turned off this equalizer for now.
    			//relativeMaxChange = MaxChange / 3;
    			relativeMaxChange = MaxChange;
    			if(relativeMaxChange <= 0) {
    				relativeMaxChange = 1;
    			}
    			
    		}
    	}
    	//------------------------------------------------------------------------
    	
    	/**
    	 * Clones this MC retest object
    	 *
    	 * @return the clone
    	 * @throws Exception the exception
    	 */
    	@Override
    	public RandomizeHistoryData2 getClone() throws Exception {
    		RandomizeHistoryData2 mc = new RandomizeHistoryData2();
    		mc.MaxChange = this.MaxChange;
    		
    		return mc;
    		
    	}	
    
    }
    #267976
    Customer
    256 Posts

    Hi Bendex. We will test it on few thousand strategies and post here results. Thanks for this snippet,

    #267979
    Customer
    68 Posts

    Great thanks, I am concerned about potential differences on different markets maybe from rounding. Also effects of different timeframes and precisions.

    #268127
    Customer
    68 Posts

    New version is here:
    https://roadmap.strategyquant.com/tasks/sq4_7233
    -potential rounding errors fixed
    -default setting to 40%

    Any help testing is appreciated, please post any problem configs/strats to that ticket.

Viewing 6 posts - 1 through 6 (of 6 total)

You must be logged in to reply to this topic.