Dukascopy Support Board
http://www.dukascopy.com/swiss/english/forex/jforex/forum/

ITesterClient run strategy from command line
http://www.dukascopy.com/swiss/english/forex/jforex/forum/viewtopic.php?f=65&t=48587
Page 1 of 1

Author:  Isak [ Sat 29 Dec, 2012, 15:22 ]
Post subject:  ITesterClient run strategy from command line

As part of the documentation on ITesterClient and associated API, it would be *very* useful to have a sample application such as the following:

A command-line tool for invoking a single back-test from the shell, where the following are specified by command-line arguments:
  • start date
  • end date
  • instrument
  • strategy file path
  • strategy parameter xml file path
  • etc.

Author:  API Support [ Mon 07 Jan, 2013, 09:27 ]
Post subject:  Re: ITesterClient sample application

Isak wrote:
strategy file path
strategy parameter xml file path
You can't run in JForex-SDK a strategy from a .jfx file, nor you can pass parameter xml file to it. Please use the JForex platform for such purposes.

Author:  Isak [ Mon 07 Jan, 2013, 12:05 ]
Post subject:  Re: ITesterClient sample application

Ok thanks, I see.

Suppose I have a given strategy, StrategyX implementing IStrategy. Now I want to invoke (from the OS command line e.g. batch file / bash script) an application that runs (without any user input) a backtest of this strategy.

For example, I could invoke the strategy from the OS like this:
BacktestStrategyX 120101 130101 EURUSD AllTicks

(In my case I don't really care about the report or results or anything, since my strategies document themselves by writing to custom log files etc... but this may be different for others)

Now, in order to develop BacktestStrategyX, I would need:
1. StrategyX.java
2. The JForex API

Can you help me on how to go about developing BacktestStrategyX.java ? What I was trying to suggest in my original post is that such a sample application should be included in the wiki, as part of documentation on ITesterClient.

Author:  API Support [ Tue 08 Jan, 2013, 18:04 ]
Post subject:  Re: ITesterClient sample application

Add the following program to you JForex-SDK project:
package jforex.sdk;

import com.dukascopy.api.*;
import com.dukascopy.api.system.ISystemListener;
import com.dukascopy.api.system.ITesterClient;
import com.dukascopy.api.system.ITesterClient.DataLoadingMethod;
import com.dukascopy.api.system.TesterFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.Future;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;


/**
 * This example program demonstrates how to compile and run a strategy from a java file
 */
@RequiresFullAccess
public class StrategyRunner {
   private static final Logger LOGGER = LoggerFactory.getLogger(StrategyRunner.class);

    private static String jnlpUrl = "https://www.dukascopy.com/client/demo/jclient/jforex.jnlp";
    private static String userName;
    private static String password; 

   public static void main(String[] args) throws Exception {
      
      if(args.length < 5){
         LOGGER.error("Insufficient parameter count " + args.length + ". Expected 5 arguments." );
         return;
      }
      
      userName = args[0];
      password = args[1];
      String strategyPath = args[2];
      String fromStr = args[3];
      String toStr = args[4];
      
      JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
      StandardJavaFileManager sjfm = jc.getStandardFileManager(null, null, null);
      File javaFile = new File(strategyPath);
      String qualifiedClassName = getQualifiedName(javaFile.getAbsolutePath());

      jc.getTask(null, null, null, null, null, sjfm.getJavaFileObjects(javaFile)).call();

      sjfm.close();
      LOGGER.info("Class has been successfully compiled");

      URL[] urls = new URL[] { new URL("file://.") };
      URLClassLoader ucl = new URLClassLoader(urls);
      Class targetClass = ucl.loadClass(qualifiedClassName);
      IStrategy strategy = (IStrategy) targetClass.newInstance();
      
      // get the instance of the IClient interface
      final ITesterClient client = TesterFactory.getDefaultInstance();
      // set the listener that will receive system events
      client.setSystemListener(new ISystemListener() {
         @Override
         public void onStart(long processId) {
            LOGGER.info("Strategy started: " + processId);
         }

         @Override
         public void onStop(long processId) {
            LOGGER.info("Strategy stopped: " + processId);
            File reportFile = new File("C:\\temp\\report.html");
            try {
               client.createReport(processId, reportFile);
            } catch (Exception e) {
               LOGGER.error(e.getMessage(), e);
            }
            if (client.getStartedStrategies().size() == 0) {
               System.exit(0);
            }
         }

         @Override
         public void onConnect() {
            LOGGER.info("Connected");
         }

         @Override
         public void onDisconnect() {
            // tester doesn't disconnect
         }
      });

      LOGGER.info("Connecting...");
      // connect to the server using jnlp, user name and password
      // connection is needed for data downloading
      client.connect(jnlpUrl, userName, password);

      // wait for it to connect
      int i = 10; // wait max ten seconds
      while (i > 0 && !client.isConnected()) {
         Thread.sleep(1000);
         i--;
      }
      if (!client.isConnected()) {
         LOGGER.error("Failed to connect Dukascopy servers");
         System.exit(1);
      }

      final SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
      dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));

      Date dateFrom = dateFormat.parse(fromStr);
      Date dateTo = dateFormat.parse(toStr);

      client.setDataInterval(DataLoadingMethod.ALL_TICKS, dateFrom.getTime(), dateTo.getTime());

      // set instruments that will be used in testing
      Set<Instrument> instruments = new HashSet<Instrument>();
      instruments.add(Instrument.EURUSD);
      LOGGER.info("Subscribing instruments...");
      client.setSubscribedInstruments(instruments);
      // setting initial deposit
      client.setInitialDeposit(Instrument.EURUSD.getSecondaryCurrency(), 50000);
      //client.setCacheDirectory(new File("C:/temp/cacheTemp"));
      // load data
      LOGGER.info("Downloading data");
      Future<?> future = client.downloadData(null);
      // wait for downloading to complete
      future.get();
      // start the strategy
      LOGGER.info("Starting strategy"); 

      client.startStrategy(strategy);
      // now it's running

   }
   
   
   private static String getQualifiedName(String path) throws Exception{      
      String fileContents = readFileContents(path);
      String className = findRegex(fileContents, "class ([\\p{Alnum}.]+) ");
      String packageName = findRegex(fileContents, "package ([\\p{Alnum}.]+);");
      if(packageName.isEmpty()){
         throw new RuntimeException("Please define the package of your strategy");
      }
      return packageName + "." + className;
   }
   
   private static String readFileContents(String path) throws IOException {
      FileInputStream stream = new FileInputStream(new File(path));
      try {
         FileChannel fc = stream.getChannel();
         MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
         return Charset.defaultCharset().decode(bb).toString();
      } finally {
         stream.close();
      }
   }
   
   private static String findRegex(String src, String regexpString) {
      Pattern pattern = Pattern.compile(regexpString, Pattern.DOTALL);
      Matcher matcher = pattern.matcher(src);
      return matcher.find() ? matcher.group(1) : "";
   }

}
Then export the program to a runnable jar file and run it in the following manner:
java -jar JarFileName.jar username password ./YourStrategy.java "05/01/2012 00:00:00" "05/01/2012 00:10:00"
If you don't want the strategy to get recompiled every time you run the program, consider passing the strategy's .class file to the program and remove the compilation logic.
Isak wrote:
What I was trying to suggest in my original post is that such a sample application should be included in the wiki, as part of documentation on ITesterClient.
We don't plan to post this example in our wiki, since this is a Java SE problem, not a JForex-API problem. In general wiki's purpose is to show users how to use the JForex-API functionality, not Java SE in whole (which itself has countless online tutorials and forums).

Attachments:
StrategyRunner.java [5.66 KiB]
Downloaded 1940 times

Author:  tcsabina [ Wed 09 Jan, 2013, 10:58 ]
Post subject:  Re: ITesterClient sample application

This is awesome!!!

Author:  Isak [ Thu 10 Jan, 2013, 14:24 ]
Post subject:  Re: ITesterClient sample application

Thank you very much! Exactly what I needed!

Author:  mcquak [ Fri 15 Jun, 2018, 21:40 ]
Post subject:  Re: ITesterClient run strategy from command line

It's actually quite easy. Try to read this Building Custom Strategy Trader on Jforex Api, hopefully it makes sense.
I tried to describe how to do exactly what you need. Hopefully it helps.

  Page 1 of 1