├── build.xml
├── config
└── log4j.properties
├── data
└── nasdaq10.dat
├── lib
├── antlr.jar
├── aopalliance.jar
├── asg.cliche-020310.jar
├── asm-attrs.jar
├── asm.jar
├── com.springsource.net.sf.cglib-2.2.0.jar
├── commons-collections-3.1.jar
├── commons-collections.jar
├── commons-dbcp-1.2.2.jar
├── commons-logging-1.1.1.jar
├── commons-pool-1.5.3.jar
├── derby.jar
├── derbyclient.jar
├── derbynet.jar
├── dom4j-1.6.1.jar
├── dom4j.jar
├── ejb3-persistence.jar
├── groovy-all-1.7.10.jar
├── hibernate-annotations.jar
├── hibernate-commons-annotations.jar
├── hibernate-core.jar
├── hibernate-entitymanager.jar
├── iText-2.1.5.jar
├── javassist-3.9.0.GA.jar
├── javassist.jar
├── jcommon-1.0.16.jar
├── jfreechart-1.0.13.jar
├── jmxri.jar
├── jmxtools.jar
├── jta-1.1.jar
├── jta.jar
├── junit.jar
├── log4j.jar
├── mail.jar
├── org.springframework.aop-3.0.5.RELEASE.jar
├── org.springframework.asm-3.0.5.RELEASE.jar
├── org.springframework.aspects-3.0.5.RELEASE.jar
├── org.springframework.beans-3.0.5.RELEASE.jar
├── org.springframework.context-3.0.5.RELEASE.jar
├── org.springframework.context.support-3.0.5.RELEASE.jar
├── org.springframework.core-3.0.5.RELEASE.jar
├── org.springframework.expression-3.0.5.RELEASE.jar
├── org.springframework.instrument-3.0.5.RELEASE.jar
├── org.springframework.instrument.tomcat-3.0.5.RELEASE.jar
├── org.springframework.jdbc-3.0.5.RELEASE.jar
├── org.springframework.jms-3.0.5.RELEASE.jar
├── org.springframework.orm-3.0.5.RELEASE.jar
├── org.springframework.oxm-3.0.5.RELEASE.jar
├── org.springframework.spring-library-3.0.5.RELEASE.libd
├── org.springframework.test-3.0.5.RELEASE.jar
├── org.springframework.transaction-3.0.5.RELEASE.jar
├── org.springframework.web-3.0.5.RELEASE.jar
├── org.springframework.web.portlet-3.0.5.RELEASE.jar
├── org.springframework.web.servlet-3.0.5.RELEASE.jar
├── org.springframework.web.struts-3.0.5.RELEASE.jar
├── persistence-api-1.0.2.jar
├── slf4j-api.jar
├── slf4j-log4j12.jar
└── ta-lib-0.4.0.jar
├── openquant.bat
├── openquant.sh
├── resource
├── TestTradeSystem.xml
├── symbols-yahoo-500KVolume.xml
├── symbols-yahoo-SP500.xml
├── symbols-yahoo-highVolume.xml
└── symbols-yahoo-nasdaq10.xml
├── scripts
└── test.groovy
├── src
└── org
│ └── openquant
│ ├── backtest
│ ├── BackTestExecutor.java
│ ├── Candle.java
│ ├── CandleSeries.java
│ ├── CandleSeriesTestContext.java
│ ├── DateUtil.java
│ ├── EquityCalculator.java
│ ├── GroovyTradeSystem.java
│ ├── Order.java
│ ├── OrderManager.java
│ ├── OrderedSignalBackTestExecutor.java
│ ├── Position.java
│ ├── PositionProcessor.java
│ ├── QuantityCalculator.java
│ ├── Series.java
│ ├── SimpleEquityReport.java
│ ├── command
│ │ ├── OpenQuantCommand.java
│ │ └── SymbolSetSeries.java
│ ├── intraday
│ │ ├── AbstractIntradayTest.java
│ │ ├── BuyIntent.java
│ │ ├── CreateChart.java
│ │ ├── DipperIntradayTest.java
│ │ ├── IntradayBackTestExecutor.java
│ │ ├── IntradayCandle.java
│ │ ├── IntradayCandleSeries.java
│ │ ├── IntradayOrderManager.java
│ │ ├── IntradayQuoteDatasource.java
│ │ ├── IntradaySeriesDatasource.java
│ │ ├── LLHIntradayTest.java
│ │ ├── LowestLowIntra.java
│ │ ├── MyTest.java
│ │ ├── OrderCallback.java
│ │ ├── OrderTimeFrame.java
│ │ ├── SellIntent.java
│ │ ├── TimeBasedLimitSellIntent.java
│ │ ├── TimeBasedSellIntent.java
│ │ └── TimeBasedStopSellIntent.java
│ └── report
│ │ ├── AbstractReport.java
│ │ ├── CSVReport.java
│ │ ├── CSVReport2.java
│ │ ├── JFreeChartReport.java
│ │ ├── JFreeChartReportOriginal.java
│ │ ├── MultiStatJFreeChartReport.java
│ │ └── TradesReport.java
│ ├── data
│ ├── ObjectStreamSeriesDatasource.java
│ ├── SeriesCollection.java
│ ├── SeriesDatasource.java
│ ├── YahooSeriesDatasource.java
│ └── load
│ │ └── DataLoader.java
│ ├── quote
│ ├── QuoteDataSource.java
│ ├── QuoteListener.java
│ └── YahooQuoteDataSource.java
│ └── util
│ ├── Day.java
│ └── OpenMarketUtil.java
└── test
└── org
└── openquant
└── test
└── TestTradeSystem.java
/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/config/log4j.properties:
--------------------------------------------------------------------------------
1 | # Set root logger level to DEBUG and its only appender to A1.
2 | log4j.rootLogger=INFO, Console, File
3 |
4 | # A1 is set to be a ConsoleAppender.
5 | log4j.appender.Console=org.apache.log4j.ConsoleAppender
6 | log4j.appender.Console.layout=org.apache.log4j.PatternLayout
7 | log4j.appender.Console.layout.ConversionPattern=%m (%F:%L)%n
8 |
9 | log4j.appender.File=org.apache.log4j.RollingFileAppender
10 | log4j.appender.File.File=logs/openquant.log
11 | log4j.appender.File.MaxFileSize=5MB
12 | log4j.appender.File.MaxBackupIndex=1
13 | log4j.appender.File.layout=org.apache.log4j.PatternLayout
14 | log4j.appender.File.layout.ConversionPattern=%m%n
--------------------------------------------------------------------------------
/data/nasdaq10.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/data/nasdaq10.dat
--------------------------------------------------------------------------------
/lib/antlr.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/antlr.jar
--------------------------------------------------------------------------------
/lib/aopalliance.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/aopalliance.jar
--------------------------------------------------------------------------------
/lib/asg.cliche-020310.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/asg.cliche-020310.jar
--------------------------------------------------------------------------------
/lib/asm-attrs.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/asm-attrs.jar
--------------------------------------------------------------------------------
/lib/asm.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/asm.jar
--------------------------------------------------------------------------------
/lib/com.springsource.net.sf.cglib-2.2.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/com.springsource.net.sf.cglib-2.2.0.jar
--------------------------------------------------------------------------------
/lib/commons-collections-3.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/commons-collections-3.1.jar
--------------------------------------------------------------------------------
/lib/commons-collections.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/commons-collections.jar
--------------------------------------------------------------------------------
/lib/commons-dbcp-1.2.2.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/commons-dbcp-1.2.2.jar
--------------------------------------------------------------------------------
/lib/commons-logging-1.1.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/commons-logging-1.1.1.jar
--------------------------------------------------------------------------------
/lib/commons-pool-1.5.3.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/commons-pool-1.5.3.jar
--------------------------------------------------------------------------------
/lib/derby.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/derby.jar
--------------------------------------------------------------------------------
/lib/derbyclient.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/derbyclient.jar
--------------------------------------------------------------------------------
/lib/derbynet.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/derbynet.jar
--------------------------------------------------------------------------------
/lib/dom4j-1.6.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/dom4j-1.6.1.jar
--------------------------------------------------------------------------------
/lib/dom4j.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/dom4j.jar
--------------------------------------------------------------------------------
/lib/ejb3-persistence.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/ejb3-persistence.jar
--------------------------------------------------------------------------------
/lib/groovy-all-1.7.10.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/groovy-all-1.7.10.jar
--------------------------------------------------------------------------------
/lib/hibernate-annotations.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/hibernate-annotations.jar
--------------------------------------------------------------------------------
/lib/hibernate-commons-annotations.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/hibernate-commons-annotations.jar
--------------------------------------------------------------------------------
/lib/hibernate-core.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/hibernate-core.jar
--------------------------------------------------------------------------------
/lib/hibernate-entitymanager.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/hibernate-entitymanager.jar
--------------------------------------------------------------------------------
/lib/iText-2.1.5.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/iText-2.1.5.jar
--------------------------------------------------------------------------------
/lib/javassist-3.9.0.GA.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/javassist-3.9.0.GA.jar
--------------------------------------------------------------------------------
/lib/javassist.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/javassist.jar
--------------------------------------------------------------------------------
/lib/jcommon-1.0.16.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/jcommon-1.0.16.jar
--------------------------------------------------------------------------------
/lib/jfreechart-1.0.13.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/jfreechart-1.0.13.jar
--------------------------------------------------------------------------------
/lib/jmxri.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/jmxri.jar
--------------------------------------------------------------------------------
/lib/jmxtools.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/jmxtools.jar
--------------------------------------------------------------------------------
/lib/jta-1.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/jta-1.1.jar
--------------------------------------------------------------------------------
/lib/jta.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/jta.jar
--------------------------------------------------------------------------------
/lib/junit.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/junit.jar
--------------------------------------------------------------------------------
/lib/log4j.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/log4j.jar
--------------------------------------------------------------------------------
/lib/mail.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/mail.jar
--------------------------------------------------------------------------------
/lib/org.springframework.aop-3.0.5.RELEASE.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/org.springframework.aop-3.0.5.RELEASE.jar
--------------------------------------------------------------------------------
/lib/org.springframework.asm-3.0.5.RELEASE.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/org.springframework.asm-3.0.5.RELEASE.jar
--------------------------------------------------------------------------------
/lib/org.springframework.aspects-3.0.5.RELEASE.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/org.springframework.aspects-3.0.5.RELEASE.jar
--------------------------------------------------------------------------------
/lib/org.springframework.beans-3.0.5.RELEASE.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/org.springframework.beans-3.0.5.RELEASE.jar
--------------------------------------------------------------------------------
/lib/org.springframework.context-3.0.5.RELEASE.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/org.springframework.context-3.0.5.RELEASE.jar
--------------------------------------------------------------------------------
/lib/org.springframework.context.support-3.0.5.RELEASE.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/org.springframework.context.support-3.0.5.RELEASE.jar
--------------------------------------------------------------------------------
/lib/org.springframework.core-3.0.5.RELEASE.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/org.springframework.core-3.0.5.RELEASE.jar
--------------------------------------------------------------------------------
/lib/org.springframework.expression-3.0.5.RELEASE.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/org.springframework.expression-3.0.5.RELEASE.jar
--------------------------------------------------------------------------------
/lib/org.springframework.instrument-3.0.5.RELEASE.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/org.springframework.instrument-3.0.5.RELEASE.jar
--------------------------------------------------------------------------------
/lib/org.springframework.instrument.tomcat-3.0.5.RELEASE.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/org.springframework.instrument.tomcat-3.0.5.RELEASE.jar
--------------------------------------------------------------------------------
/lib/org.springframework.jdbc-3.0.5.RELEASE.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/org.springframework.jdbc-3.0.5.RELEASE.jar
--------------------------------------------------------------------------------
/lib/org.springframework.jms-3.0.5.RELEASE.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/org.springframework.jms-3.0.5.RELEASE.jar
--------------------------------------------------------------------------------
/lib/org.springframework.orm-3.0.5.RELEASE.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/org.springframework.orm-3.0.5.RELEASE.jar
--------------------------------------------------------------------------------
/lib/org.springframework.oxm-3.0.5.RELEASE.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/org.springframework.oxm-3.0.5.RELEASE.jar
--------------------------------------------------------------------------------
/lib/org.springframework.spring-library-3.0.5.RELEASE.libd:
--------------------------------------------------------------------------------
1 | Library-SymbolicName: org.springframework.spring
2 | Library-Version: 3.0.5.RELEASE
3 | Library-Name: Spring Framework
4 | Import-Bundle:
5 | org.springframework.aop;version="[3.0.5.RELEASE, 3.0.5.RELEASE]",
6 | org.springframework.asm;version="[3.0.5.RELEASE, 3.0.5.RELEASE]",
7 | org.springframework.aspects;version="[3.0.5.RELEASE, 3.0.5.RELEASE]",
8 | org.springframework.beans;version="[3.0.5.RELEASE, 3.0.5.RELEASE]",
9 | org.springframework.context;version="[3.0.5.RELEASE, 3.0.5.RELEASE]",
10 | org.springframework.context.support;version="[3.0.5.RELEASE, 3.0.5.RELEASE]",
11 | org.springframework.core;version="[3.0.5.RELEASE, 3.0.5.RELEASE]",
12 | org.springframework.expression;version="[3.0.5.RELEASE, 3.0.5.RELEASE]",
13 | org.springframework.jdbc;version="[3.0.5.RELEASE, 3.0.5.RELEASE]",
14 | org.springframework.jms;version="[3.0.5.RELEASE, 3.0.5.RELEASE]",
15 | org.springframework.orm;version="[3.0.5.RELEASE, 3.0.5.RELEASE]",
16 | org.springframework.oxm;version="[3.0.5.RELEASE, 3.0.5.RELEASE]",
17 | org.springframework.transaction;version="[3.0.5.RELEASE, 3.0.5.RELEASE]",
18 | org.springframework.web;version="[3.0.5.RELEASE, 3.0.5.RELEASE]",
19 | org.springframework.web.servlet;version="[3.0.5.RELEASE, 3.0.5.RELEASE]",
20 | org.springframework.web.portlet;version="[3.0.5.RELEASE, 3.0.5.RELEASE]",
21 | com.springsource.org.aopalliance;version="[1.0.0, 1.0.0]"
22 |
--------------------------------------------------------------------------------
/lib/org.springframework.test-3.0.5.RELEASE.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/org.springframework.test-3.0.5.RELEASE.jar
--------------------------------------------------------------------------------
/lib/org.springframework.transaction-3.0.5.RELEASE.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/org.springframework.transaction-3.0.5.RELEASE.jar
--------------------------------------------------------------------------------
/lib/org.springframework.web-3.0.5.RELEASE.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/org.springframework.web-3.0.5.RELEASE.jar
--------------------------------------------------------------------------------
/lib/org.springframework.web.portlet-3.0.5.RELEASE.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/org.springframework.web.portlet-3.0.5.RELEASE.jar
--------------------------------------------------------------------------------
/lib/org.springframework.web.servlet-3.0.5.RELEASE.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/org.springframework.web.servlet-3.0.5.RELEASE.jar
--------------------------------------------------------------------------------
/lib/org.springframework.web.struts-3.0.5.RELEASE.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/org.springframework.web.struts-3.0.5.RELEASE.jar
--------------------------------------------------------------------------------
/lib/persistence-api-1.0.2.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/persistence-api-1.0.2.jar
--------------------------------------------------------------------------------
/lib/slf4j-api.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/slf4j-api.jar
--------------------------------------------------------------------------------
/lib/slf4j-log4j12.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/slf4j-log4j12.jar
--------------------------------------------------------------------------------
/lib/ta-lib-0.4.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/x2q/openquant/9a5442f89a85b1c6efb7b7f0ad6fc511839c181f/lib/ta-lib-0.4.0.jar
--------------------------------------------------------------------------------
/openquant.bat:
--------------------------------------------------------------------------------
1 |
2 | java -cp "./lib/*;./scripts/;./config" -Xmx2048m -Xrs org.openquant.backtest.command.OpenQuantCommand
--------------------------------------------------------------------------------
/openquant.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | DIRNAME=`dirname $0`
4 |
5 | cd $DIRNAME
6 |
7 | # set Java
8 | if [ "x$JAVA" = "x" ]; then
9 | if [ -x "$TS_HOME/jre/bin/java" ]; then
10 | JAVA="$TS_HOME/jre/bin/java"
11 | elif [ "x$JAVA_HOME" != "x" ]; then
12 | JAVA="$JAVA_HOME/bin/java"
13 | else
14 | JAVA="java"
15 | fi
16 | fi
17 |
18 | # Memory Options
19 | JAVA_OPTS="-Xmx512m -Xrs"
20 |
21 | java -cp "./lib/*:./scripts/:./config" $JAVA_OPTS org.openquant.backtest.command.OpenQuantCommand
--------------------------------------------------------------------------------
/resource/TestTradeSystem.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/resource/symbols-yahoo-highVolume.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | XOM
10 | MSFT
11 | AAPL
12 | GE
13 | WMT
14 | PG
15 | BAC
16 | JNJ
17 | JPM
18 | WFC
19 | VALE
20 | CVX
21 | CSCO
22 | T
23 | PFE
24 | ORCL
25 | INTC
26 | C
27 | HPQ
28 | KO
29 | MRK
30 | ITUB
31 | SLB
32 | COP
33 | GS
34 | VZ
35 | DIS
36 | QCOM
37 | HD
38 | TSM
39 | CMCSA
40 | USB
41 | CVS
42 | F
43 | NOK
44 | KFT
45 | MO
46 | MS
47 | NWSA
48 | BMY
49 | EMC
50 | LOW
51 | ABX
52 | GILD
53 | DOW
54 | FCX
55 | DTV
56 | UNH
57 | TXN
58 | DELL
59 | GLW
60 | EBAY
61 | HAL
62 | XTO
63 | YHOO
64 | SCHW
65 | AMAT
66 | LVS
67 | MOT
68 | CHK
69 | ATVI
70 | AA
71 | MRVL
72 | SYMC
73 | WFT
74 | S
75 | CX
76 | FITB
77 | VLO
78 | BSX
79 | M
80 | RF
81 | DAL
82 | STX
83 | SNDK
84 | XRX
85 | MU
86 | NVDA
87 | NLY
88 | GNW
89 | Q
90 | X
91 | AUY
92 | KEY
93 | ALU
94 | MGM
95 | AMD
96 | AIG
97 | MI
98 | HBAN
99 | SIRI
100 | LSI
101 | ETFC
102 | UAUA
103 | ONNN
104 | THC
105 | BRCD
106 | CIM
107 | AMR
108 | BPOP
109 | LVLT
110 | EK
111 | AKS
112 | DRYS
113 | SNV
114 | HL
115 | FNM
116 | LCC
117 | FRE
118 | PALM
119 | DYN
120 | YRCW
121 | ABK
122 | ANX
123 |
124 |
--------------------------------------------------------------------------------
/resource/symbols-yahoo-nasdaq10.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | ATVI
10 | ADBE
11 | AKAM
12 | ALTR
13 | AMZN
14 | AMGN
15 | APOL
16 | AAPL
17 | AMAT
18 | ADSK
19 |
20 |
21 |
--------------------------------------------------------------------------------
/scripts/test.groovy:
--------------------------------------------------------------------------------
1 | import org.openquant.backtest.Position
2 | import org.openquant.backtest.Series
3 |
4 | def Close = context.closeSeries()
5 | def EMAClose = Close.EMA(12)
6 | def RSI = Close.RSI(25)
7 |
8 | for (int bar = 25; bar < context.barsCount() - 1; bar++) {
9 |
10 | println "Date[${context.date(bar)}], Close[${Close.getAt(bar)}], " +
11 | "EMA[${EMAClose.getAt(bar)}], RSI[${RSI.getAt(bar)}]"
12 |
13 | Position pos = context.getLastOpenPosition();
14 |
15 | if (RSI.getAt(bar - 1) > RSI.getAt(bar - 2)) {
16 | context.buyAtLimit(bar, context.close(bar), 1000, "ELL")
17 | }
18 | else{
19 | if (context.hasOpenPositions()) {
20 | context.sellAtLimit(bar, pos, pos.getEntryPrice() * 1.05, "XLL")
21 | }
22 |
23 | if (context.hasOpenPositions()) {
24 | context.sellAtStop(bar, pos, pos.getEntryPrice() * 0.95, "XLS")
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/src/org/openquant/backtest/BackTestExecutor.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest;
2 |
3 | /*
4 | Copyright (c) 2010, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import java.util.ArrayList;
23 | import java.util.Collections;
24 | import java.util.Comparator;
25 | import java.util.HashMap;
26 | import java.util.List;
27 | import java.util.Map;
28 |
29 | import org.apache.commons.logging.Log;
30 | import org.apache.commons.logging.LogFactory;
31 | import org.openquant.backtest.report.AbstractReport;
32 | import org.openquant.backtest.report.CSVReport;
33 | import org.openquant.backtest.report.JFreeChartReport;
34 | import org.openquant.backtest.report.TradesReport;
35 | import org.openquant.data.SeriesDatasource;
36 | import org.springframework.util.StopWatch;
37 |
38 | public class BackTestExecutor {
39 |
40 | private Log log = LogFactory.getLog(BackTestExecutor.class);
41 |
42 | private SeriesDatasource data;
43 |
44 | private List symbols;
45 |
46 | private CandleSeriesTestContext test;
47 |
48 | private double capital = 100000;
49 |
50 | private double commission = 9.99;
51 |
52 | private double slippage = 0.001;
53 |
54 | private List positions = new ArrayList();
55 |
56 | private String reportName;
57 |
58 | private int testCycles = 1;
59 |
60 | public int getTestCycles() {
61 | return testCycles;
62 | }
63 |
64 | public void setTestCycles(int testCycles) {
65 | this.testCycles = testCycles;
66 | }
67 |
68 | public BackTestExecutor(SeriesDatasource data, List symbols,
69 | CandleSeriesTestContext test, String reportName, double capital,
70 | double commission, double slippage) {
71 | super();
72 | this.data = data;
73 | this.symbols = symbols;
74 | this.test = test;
75 | this.capital = capital;
76 | this.commission = commission;
77 | this.slippage = slippage;
78 | this.reportName = reportName;
79 |
80 | }
81 |
82 | Map seriesCache = new HashMap();
83 |
84 | private void populateCache(){
85 | for (String symbol : symbols) {
86 |
87 | try {
88 | seriesCache.put(symbol, data.fetchSeries(symbol) );
89 | } catch (Exception e) {
90 | log.error(e.getMessage(), e);
91 | }
92 |
93 | }
94 | }
95 |
96 | public double run() {
97 |
98 | StopWatch watch = new StopWatch("-- DEBUGGING --");
99 | watch.start("execute tradesystem");
100 |
101 | Collections.shuffle(symbols);
102 |
103 | for (String symbol : symbols) {
104 |
105 | CandleSeries candleSeries;
106 | try {
107 | candleSeries = data.fetchSeries(symbol);
108 |
109 | test.reset();
110 | test.setSeries(candleSeries);
111 | test.run();
112 |
113 | positions.addAll(test.getOrderManager().getClosedPositions());
114 | } catch (Exception e) {
115 | log.error(e.getMessage(), e);
116 | }
117 |
118 | }
119 |
120 | // order all of the positions by entry date
121 | Collections.sort(positions, new Comparator() {
122 |
123 | @Override
124 | public int compare(Position positionOne, Position positionTwo) {
125 | return positionOne.getEntryDate().compareTo(
126 | positionTwo.getEntryDate());
127 | }
128 |
129 | });
130 |
131 | int posSize = test.getOrderManager().getOpenPositions().size();
132 |
133 | log.info("Open Position Size : " + posSize);
134 |
135 | // generate reports
136 | AbstractReport report = new JFreeChartReport(reportName + ".jpg",
137 | capital, commission, slippage, positions, test
138 | .getOrderManager().getOpenPositions());
139 | //double endingCapital = report.getTotalCapitalAndEquity();
140 | //log.debug(String.format("Ending capital is %12.2f", endingCapital));
141 | report.render();
142 |
143 | new CSVReport(capital, commission, slippage, positions, test
144 | .getOrderManager().getOpenPositions(), reportName + ".csv").render();
145 |
146 | new TradesReport(capital, commission, slippage, positions, test
147 | .getOrderManager().getOpenPositions(), reportName + "-trades.txt").render();
148 |
149 |
150 | watch.stop();
151 | log.debug(String.format("Time : %s seconds", watch.getTotalTimeSeconds()));
152 |
153 | return report.getTotalCapitalAndEquity();
154 | }
155 |
156 |
157 | }
--------------------------------------------------------------------------------
/src/org/openquant/backtest/Candle.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest;
2 |
3 | /*
4 | Copyright (c) 2009, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import java.io.Serializable;
23 | import java.util.Date;
24 |
25 | import javax.persistence.Entity;
26 | import javax.persistence.Id;
27 | import javax.persistence.Version;
28 |
29 | @Entity
30 | public class Candle implements Comparable, Serializable {
31 |
32 | private static final long serialVersionUID = 1L;
33 |
34 | @SuppressWarnings("unused")
35 | @Id
36 | private Integer id;
37 |
38 | @SuppressWarnings("unused")
39 | @Version
40 | private Object version;
41 |
42 | private String symbol;
43 |
44 | private Date date = new Date();
45 |
46 | private double openPrice;
47 |
48 | private double highPrice;
49 |
50 | private double lowPrice;
51 |
52 | private double closePrice;
53 |
54 | private double volume;
55 |
56 | public Candle() {
57 | }
58 |
59 | public Candle(String symbol, Date date, double open, double high, double low, double close, double volume) {
60 | super();
61 | this.symbol = symbol;
62 | this.date = date;
63 | this.openPrice = open;
64 | this.highPrice = high;
65 | this.lowPrice = low;
66 | this.closePrice = close;
67 | this.volume = volume;
68 | }
69 |
70 | public double getOpenPrice() {
71 | return openPrice;
72 | }
73 |
74 | public void setOpenPrice(double open) {
75 | this.openPrice = open;
76 | }
77 |
78 | public double getHighPrice() {
79 | return highPrice;
80 | }
81 |
82 | public void setHighPrice(double high) {
83 | this.highPrice = high;
84 | }
85 |
86 | public double getLowPrice() {
87 | return lowPrice;
88 | }
89 |
90 | public void setLowPrice(double low) {
91 | this.lowPrice = low;
92 | }
93 |
94 | public double getClosePrice() {
95 | return closePrice;
96 | }
97 |
98 | public void setClosePrice(double close) {
99 | this.closePrice = close;
100 | }
101 |
102 | public double getVolume() {
103 | return volume;
104 | }
105 |
106 | public void setVolume(double volume) {
107 | this.volume = volume;
108 | }
109 |
110 | public Date getDate() {
111 | return date;
112 | }
113 |
114 | public void setDate(Date time) {
115 | this.date = time;
116 | }
117 |
118 | public String getSymbol() {
119 | return symbol;
120 | }
121 |
122 | public void setSymbol(String symbol) {
123 | this.symbol = symbol;
124 | }
125 |
126 | @Override
127 | public String toString() {
128 | return String.format("%s [%.2f, %.2f, %.2f, %.2f] [%s]", symbol, openPrice, highPrice, lowPrice, closePrice, date);
129 | }
130 |
131 | @Override
132 | public int compareTo(Candle c) {
133 | return this.getDate().compareTo(c.getDate());
134 | }
135 |
136 | }
137 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/CandleSeries.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest;
2 |
3 | /*
4 | Copyright (c) 2010, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import java.io.Serializable;
23 | import java.util.ArrayList;
24 | import java.util.Date;
25 | import java.util.List;
26 |
27 | public class CandleSeries implements Serializable{
28 |
29 | private static final long serialVersionUID = 1L;
30 |
31 | protected List candles = new ArrayList();
32 |
33 | public List getCandles() {
34 | return candles;
35 | }
36 |
37 | public String getSymbol() {
38 | return candles.get(0).getSymbol();
39 | }
40 |
41 | public CandleSeries(List candles) {
42 | this.candles = candles;
43 | }
44 |
45 | public int size() {
46 | return candles.size();
47 | }
48 |
49 | public Candle get(int index) {
50 | return candles.get(index);
51 | }
52 |
53 | public Date getBeginDate(){
54 | return candles.get(0).getDate();
55 | }
56 |
57 | public Date getEndDate(){
58 | return candles.get(candles.size()-1).getDate();
59 | }
60 |
61 | public Series getOpenSeries() {
62 |
63 | double[] rArray = new double[candles.size()];
64 |
65 | int i = 0;
66 | for (Candle candle : candles) {
67 | rArray[i++] = candle.getOpenPrice();
68 | }
69 |
70 | return new Series(rArray);
71 | }
72 |
73 | public Series getCloseSeries() {
74 |
75 | double[] rArray = new double[candles.size()];
76 |
77 | int i = 0;
78 | for (Candle candle : candles) {
79 | rArray[i++] = candle.getClosePrice();
80 | }
81 |
82 | return new Series(rArray);
83 | }
84 |
85 | public Series getHighSeries() {
86 |
87 | double[] rArray = new double[candles.size()];
88 |
89 | int i = 0;
90 | for (Candle candle : candles) {
91 | rArray[i++] = candle.getHighPrice();
92 | }
93 |
94 | return new Series(rArray);
95 | }
96 |
97 | public Series getLowSeries() {
98 |
99 | double[] rArray = new double[candles.size()];
100 |
101 | int i = 0;
102 | for (Candle candle : candles) {
103 | rArray[i++] = candle.getLowPrice();
104 | }
105 |
106 | return new Series(rArray);
107 | }
108 |
109 | public Series getVolumeSeries() {
110 |
111 | double[] rArray = new double[candles.size()];
112 |
113 | int i = 0;
114 | for (Candle candle : candles) {
115 | rArray[i++] = candle.getVolume();
116 | }
117 |
118 | return new Series(rArray);
119 | }
120 |
121 | @Override
122 | public String toString() {
123 |
124 | StringBuffer buffer = new StringBuffer();
125 | for (Candle candle : candles) {
126 | buffer.append(candle.toString());
127 | buffer.append("\n");
128 | }
129 |
130 | return buffer.toString();
131 | }
132 |
133 | }
134 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/CandleSeriesTestContext.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest;
2 |
3 | /*
4 | Copyright (c) 2010, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import java.util.Date;
23 | import java.util.List;
24 |
25 | import org.apache.commons.logging.Log;
26 | import org.apache.commons.logging.LogFactory;
27 | import org.openquant.backtest.intraday.OrderCallback;
28 | import org.openquant.backtest.intraday.TimeBasedSellIntent;
29 |
30 | public abstract class CandleSeriesTestContext {
31 |
32 | protected Log log = LogFactory.getLog(CandleSeriesTestContext.class);
33 |
34 | private CandleSeries series;
35 |
36 | private OrderManager orderManager = new OrderManager();
37 |
38 | public CandleSeriesTestContext() {
39 | }
40 |
41 | public abstract void run();
42 |
43 | public List preFilterOrders(List positions){
44 | // default behavior is to just return all positions
45 | // override to filter out opening positions. This is ideal
46 | // for categorizing priority to a large list of potential
47 | // buy orders
48 | return positions;
49 | }
50 |
51 | public void finish(final double equity){
52 |
53 | }
54 |
55 | public void reset() {
56 | orderManager.getClosedPositions().clear();
57 | orderManager.getOpenPositions().clear();
58 | orderManager.setSymbol(null);
59 | }
60 |
61 | public OrderManager getOrderManager() {
62 | return orderManager;
63 | }
64 |
65 | public void setOrderManager(OrderManager orderManager) {
66 | this.orderManager = orderManager;
67 | }
68 |
69 | public CandleSeries getSeries() {
70 | return series;
71 | }
72 |
73 | public void setSeries(CandleSeries series) {
74 | this.series = series;
75 | orderManager.setSymbol(series.getSymbol());
76 | }
77 |
78 | public int barsCount() {
79 | return series.size();
80 | }
81 |
82 | public Series closeSeries() {
83 | return series.getCloseSeries();
84 | }
85 |
86 | public Series openSeries() {
87 | return series.getOpenSeries();
88 | }
89 |
90 | public Series highSeries() {
91 | return series.getHighSeries();
92 | }
93 |
94 | public Series volumeSeries(){
95 | return series.getVolumeSeries();
96 | }
97 |
98 | public Series lowSeries() {
99 | return series.getLowSeries();
100 | }
101 |
102 | public double volume(int barIndex) {
103 | return series.get(barIndex).getVolume();
104 | }
105 |
106 | public double close(int barIndex) {
107 | return series.get(barIndex).getClosePrice();
108 | }
109 |
110 | public double open(int barIndex) {
111 | return series.get(barIndex).getOpenPrice();
112 | }
113 |
114 | public double high(int barIndex) {
115 | return series.get(barIndex).getHighPrice();
116 | }
117 |
118 | public double low(int barIndex) {
119 | return series.get(barIndex).getLowPrice();
120 | }
121 |
122 | public Date date(int barIndex) {
123 | return series.get(barIndex).getDate();
124 | }
125 |
126 | public Position buyAtMarket(int barIndex, double marketPrice, int quantity, String comments) {
127 | return orderManager.buyAtMarket(barIndex, marketPrice, quantity, comments, series);
128 | }
129 |
130 | public Position buyAtMarket(int barIndex, double marketPrice, int quantity, String comments, double score) {
131 | Position pos = orderManager.buyAtMarket(barIndex, marketPrice, quantity, comments, series);
132 | if (pos != null){
133 | pos.setScore(score);
134 | }
135 | return pos;
136 | }
137 |
138 | public Position buyAtLimit(int barIndex, double limitPrice, int quantity, String comments) {
139 | return orderManager.buyAtLimit(barIndex, limitPrice, quantity, comments, series);
140 | }
141 |
142 | public Position buyAtLimit(int barIndex, double limitPrice, int quantity, double score, String comments) {
143 | Position pos = orderManager.buyAtLimit(barIndex, limitPrice, quantity, comments, series);
144 | if (pos != null){
145 | pos.setScore(score);
146 | }
147 | return pos;
148 | }
149 |
150 | public Position buyAtLimit(int barIndex, double limitPrice, String comments, QuantityCalculator calculator) {
151 | return orderManager.buyAtLimit(barIndex, limitPrice, 0, comments, series, calculator);
152 | }
153 |
154 | public Position buyAtLimit(int barIndex, double limitPrice, String comments, QuantityCalculator calculator, double score) {
155 | Position pos = orderManager.buyAtMarket(barIndex, limitPrice, 0, comments, series);
156 | if (pos != null){
157 | pos.setScore(score);
158 | }
159 | return pos;
160 | }
161 |
162 | public void sellAtLimit(int barIndex, Position position, double limitPrice, String comments) {
163 | orderManager.sellAtLimit(barIndex, position, limitPrice, comments, series);
164 | }
165 |
166 | public void sellAtStop(int barIndex, Position position, double stopPrice, String comments) {
167 | orderManager.sellAtStop(barIndex, position, stopPrice, comments, series);
168 | }
169 |
170 | public Position sellAtClose(int barIndex, Position position, int quantity, String comments){
171 | return orderManager.sellAtClose(barIndex, position, quantity, comments, series);
172 | }
173 |
174 | public Position getLastOpenPosition() {
175 | return orderManager.getLastOpenPosition();
176 | }
177 |
178 | public boolean hasOpenPositions() {
179 | return orderManager.hasOpenPositions();
180 | }
181 |
182 | public boolean containsOpenPosition(Position position){
183 | return orderManager.containsOpenPosition(position);
184 | }
185 |
186 | public void PrintLine(final String string) {
187 | System.out.println(string);
188 | }
189 |
190 | public void incrementBar(int bar){
191 |
192 | Candle candle = series.get(bar);
193 | orderManager.processCandle(candle);
194 | }
195 |
196 | public void timeBasedExitOnClose(int days, Position position){
197 | orderManager.timeBasedExitOnClose(days, position, null);
198 | }
199 |
200 | public void timeBasedExitOnClose(int days, Position position, OrderCallback callback){
201 | orderManager.timeBasedExitOnClose(days, position, callback);
202 | }
203 |
204 | public void buyAtLimit(double limitPrice, QuantityCalculator calculator, OrderCallback callback) {
205 | orderManager.buyAtLimit(limitPrice, calculator, callback);
206 | }
207 |
208 | public void sellAtLimit(double limitPrice, int quantity, Position position, OrderCallback callback){
209 | orderManager.sellAtLimit(limitPrice, quantity, position, callback);
210 | }
211 |
212 | public void sellAtLimit(double limitPrice, Position position){
213 | orderManager.sellAtLimit(limitPrice, position.getQuantity(), position, null);
214 | }
215 |
216 | }
217 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/DateUtil.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest;
2 |
3 | /*
4 | Copyright (c) 2010, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import java.util.ArrayList;
23 | import java.util.Calendar;
24 | import java.util.Date;
25 | import java.util.List;
26 |
27 | public class DateUtil {
28 |
29 | static List holidayList = new ArrayList();
30 |
31 | // holiday list
32 | static {
33 | holidayList.add(new Date("11/23/2010"));
34 | }
35 |
36 | public static void main(String args[]) {
37 |
38 | /*
39 | Date day1 = new Date("08/17/2009"); // start date
40 | Date day2 = new Date("08/27/2009"); // end date
41 | Date dayCounter = day1;
42 | int counter = 1;
43 |
44 | while (dayCounter.before(day2)) {
45 |
46 | // check for weekends and holiday list
47 | if (dayCounter.getDay() != Calendar.SATURDAY
48 | && dayCounter.getDay() != Calendar.SUNDAY
49 | && !holidayList.contains(dayCounter)) {
50 | counter++; // count weekdays
51 | }
52 |
53 | dayCounter.setDate(dayCounter.getDate() + 1);
54 | }
55 |
56 | System.out.println("Working days = " + counter);
57 |
58 | */
59 |
60 |
61 | Date date = calculateLookbackDate(1);
62 |
63 | System.out.println(date);
64 |
65 | }
66 |
67 | public static Date calculateLookbackDate(int lookback){
68 |
69 | // today
70 | Date dayCounter = new Date();
71 |
72 | int validDatesCounter = lookback;
73 | while(validDatesCounter > 0){
74 |
75 | // check for weekends and holiday list
76 | dayCounter.setDate(dayCounter.getDate() - 1);
77 |
78 | Calendar calendar = Calendar.getInstance();
79 | calendar.setTime(dayCounter);
80 |
81 | if (calendar.get(Calendar.DAY_OF_WEEK) != Calendar.SATURDAY
82 | && calendar.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY
83 | && !holidayList.contains(dayCounter)) {
84 | validDatesCounter--;
85 |
86 | }
87 | }
88 |
89 | return dayCounter;
90 |
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/EquityCalculator.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest;
2 |
3 | /*
4 | Copyright (c) 2010, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import org.apache.commons.logging.Log;
23 | import org.apache.commons.logging.LogFactory;
24 |
25 | class EquityCalculator implements PositionProcessor {
26 |
27 | private Log log = LogFactory.getLog(EquityCalculator.class);
28 |
29 | private double capital;
30 |
31 | private double slippage;
32 |
33 | private double commission;
34 |
35 | public EquityCalculator(double capital, double commission, double slippage) {
36 | super();
37 | this.capital = capital;
38 | this.slippage = slippage;
39 | this.commission = commission;
40 | this.capital = capital;
41 | }
42 |
43 | public double getCapital() {
44 | return capital;
45 | }
46 |
47 | @Override
48 | public void processClosePosition(Position position) {
49 |
50 | double profit = (position.getExitPrice() - position.getEntryPrice()) * position.getQuantity();
51 | profit = profit - (profit*slippage) - commission;
52 |
53 | capital += profit;
54 |
55 | }
56 |
57 | @Override
58 | public void processOpenPosition(Position position) {
59 |
60 | // calculate entry value and add to capital
61 | capital += position.getEntryPrice() * position.getQuantity();
62 |
63 | }
64 |
65 | @Override
66 | public void finish() {
67 | log.debug(String.format("Ending capital is %12.2f", capital));
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/GroovyTradeSystem.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest;
2 |
3 | /*
4 | Copyright (c) 2010, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import java.io.File;
23 | import java.io.FileReader;
24 | import java.io.IOException;
25 | import java.io.Reader;
26 | import java.util.ArrayList;
27 | import java.util.List;
28 |
29 | import javax.script.ScriptEngine;
30 | import javax.script.ScriptEngineManager;
31 | import javax.script.ScriptException;
32 |
33 | import org.openquant.data.SeriesDatasource;
34 | import org.openquant.data.YahooSeriesDatasource;
35 |
36 | public class GroovyTradeSystem extends CandleSeriesTestContext {
37 |
38 | private String scriptFile;
39 |
40 | public GroovyTradeSystem(String scriptFile) {
41 | super();
42 | if( !scriptFile.endsWith(".groovy") ){
43 | // automatically append the groovy ext
44 | scriptFile += ".groovy";
45 | }
46 | this.scriptFile = scriptFile;
47 | }
48 |
49 | @Override
50 | public void run() {
51 |
52 | ScriptEngineManager factory = new ScriptEngineManager();
53 |
54 | ScriptEngine engine = factory.getEngineByName("groovy");
55 |
56 |
57 | try {
58 | engine.put("context", this);
59 |
60 | engine.eval( readFully ( findScript("scripts") ));
61 |
62 | } catch (ScriptException e) {
63 | log.error(e.getMessage(), e);
64 | } catch (IOException e) {
65 | log.error(e.getMessage(), e);
66 | }
67 | }
68 |
69 | public static String readFully(File file) throws IOException {
70 | return readFully(new FileReader(file));
71 | }
72 |
73 | public static String readFully(Reader reader) throws IOException {
74 | char[] arr = new char[8 * 1024]; // 8K at a time
75 | StringBuffer buf = new StringBuffer();
76 | int numChars;
77 |
78 | while ((numChars = reader.read(arr, 0, arr.length)) > 0) {
79 | buf.append(arr, 0, numChars);
80 | }
81 |
82 | return buf.toString();
83 | }
84 |
85 | private File findScript(String... directories) {
86 | for (String directory : directories) {
87 | for (File pathname : new File(directory).listFiles()) {
88 | if (pathname.isFile()
89 | && pathname.toString().equals(directory + File.separator + scriptFile)) {
90 | return pathname;
91 | }
92 | }
93 | }
94 | return null;
95 | }
96 |
97 | public static void main(String... args) {
98 | // new ClassPathXmlApplicationContext("TestTradeSystem.xml");
99 |
100 | SeriesDatasource data = new YahooSeriesDatasource("2009-01-01");
101 |
102 | List symbols = new ArrayList();
103 | symbols.add("GOOG");
104 |
105 | BackTestExecutor executor = new BackTestExecutor(data, symbols,
106 | new GroovyTradeSystem("Tester.groovy"), "TesterReport", 100000, 9.99,
107 | 0.2);
108 | executor.run();
109 | }
110 |
111 | }
112 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/Order.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest;
2 |
3 | import java.util.Date;
4 |
5 | public class Order {
6 | private boolean entry;
7 |
8 | private Date date;
9 |
10 | private double value;
11 |
12 | private Position parentPosition;
13 |
14 | public Order(boolean entry, Date date, double value, Position position) {
15 | super();
16 | this.date = date;
17 | this.value = value;
18 | this.entry = entry;
19 | this.parentPosition = position;
20 | }
21 |
22 | public Date getDate() {
23 | return date;
24 | }
25 |
26 | public boolean isEntry() {
27 | return entry;
28 | }
29 |
30 | public double getValue() {
31 | return value;
32 | }
33 |
34 | public void setValue(double value) {
35 | this.value = value;
36 | }
37 |
38 | public Position getParentPosition() {
39 | return parentPosition;
40 | }
41 |
42 | @Override
43 | public String toString() {
44 | //return "Order [entry=" + entry + ", date=" + date + ", value="
45 | // + value + "]";
46 |
47 | return String.format( "Order[ id=%s, symbol=%s, entry=%s, date=%s, value=%.2f ]", getParentPosition().getId(), getParentPosition().getSymbol(), entry, date, value );
48 | }
49 |
50 | }
--------------------------------------------------------------------------------
/src/org/openquant/backtest/OrderedSignalBackTestExecutor.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest;
2 |
3 | /*
4 | Copyright (c) 2010, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import java.util.ArrayList;
23 | import java.util.Collections;
24 | import java.util.Comparator;
25 | import java.util.HashMap;
26 | import java.util.List;
27 | import java.util.Map;
28 |
29 | import org.apache.commons.logging.Log;
30 | import org.apache.commons.logging.LogFactory;
31 | import org.openquant.backtest.report.AbstractReport;
32 | import org.openquant.backtest.report.CSVReport;
33 | import org.openquant.backtest.report.JFreeChartReport;
34 | import org.openquant.backtest.report.TradesReport;
35 | import org.openquant.data.SeriesDatasource;
36 | import org.springframework.util.StopWatch;
37 |
38 | public class OrderedSignalBackTestExecutor {
39 |
40 | private Log log = LogFactory.getLog(OrderedSignalBackTestExecutor.class);
41 |
42 | private SeriesDatasource data;
43 |
44 | private List symbols;
45 |
46 | private CandleSeriesTestContext test;
47 |
48 | private double capital = 100000;
49 |
50 | private double commission = 9.99;
51 |
52 | private double slippage = 0.001;
53 |
54 | private List positions = new ArrayList();
55 |
56 | private String reportName;
57 |
58 | private int testCycles = 1;
59 |
60 | public int getTestCycles() {
61 | return testCycles;
62 | }
63 |
64 | public void setTestCycles(int testCycles) {
65 | this.testCycles = testCycles;
66 | }
67 |
68 | public OrderedSignalBackTestExecutor(SeriesDatasource data, List symbols,
69 | CandleSeriesTestContext test, String reportName, double capital,
70 | double commission, double slippage) {
71 | super();
72 | this.data = data;
73 | this.symbols = symbols;
74 | this.test = test;
75 | this.capital = capital;
76 | this.commission = commission;
77 | this.slippage = slippage;
78 | this.reportName = reportName;
79 |
80 | }
81 |
82 | Map seriesCache = new HashMap();
83 |
84 | private void populateCache(){
85 | for (String symbol : symbols) {
86 |
87 | try {
88 | seriesCache.put(symbol, data.fetchSeries(symbol) );
89 | } catch (Exception e) {
90 | log.error(e.getMessage(), e);
91 | }
92 |
93 | }
94 | }
95 |
96 | public double run() {
97 |
98 | StopWatch watch = new StopWatch("-- DEBUGGING --");
99 | watch.start("execute tradesystem");
100 |
101 | Collections.shuffle(symbols);
102 |
103 | for (String symbol : symbols) {
104 |
105 | CandleSeries candleSeries;
106 | try {
107 | candleSeries = data.fetchSeries(symbol);
108 |
109 | test.reset();
110 | test.setSeries(candleSeries);
111 | test.run();
112 |
113 | positions.addAll(test.getOrderManager().getClosedPositions());
114 | } catch (Exception e) {
115 | log.error(e.getMessage(), e);
116 | }
117 |
118 | }
119 |
120 | // order all of the positions by entry date
121 | Collections.sort(positions, new Comparator() {
122 |
123 | @Override
124 | public int compare(Position positionOne, Position positionTwo) {
125 | return positionOne.getEntryDate().compareTo(
126 | positionTwo.getEntryDate());
127 | }
128 |
129 | });
130 |
131 | int posSize = test.getOrderManager().getOpenPositions().size();
132 |
133 | log.info("Open Position Size : " + posSize);
134 |
135 | // generate reports
136 | AbstractReport report = new JFreeChartReport(reportName + ".jpg",
137 | capital, commission, slippage, positions, test
138 | .getOrderManager().getOpenPositions());
139 | //double endingCapital = report.getTotalCapitalAndEquity();
140 | //log.debug(String.format("Ending capital is %12.2f", endingCapital));
141 | report.render();
142 |
143 | new CSVReport(capital, commission, slippage, positions, test
144 | .getOrderManager().getOpenPositions(), reportName + ".csv").render();
145 |
146 | new TradesReport(capital, commission, slippage, positions, test
147 | .getOrderManager().getOpenPositions(), reportName + "-trades.txt").render();
148 |
149 |
150 | watch.stop();
151 | log.debug(String.format("Time : %s seconds", watch.getTotalTimeSeconds()));
152 |
153 | return report.getTotalCapitalAndEquity();
154 | }
155 |
156 |
157 | }
--------------------------------------------------------------------------------
/src/org/openquant/backtest/Position.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest;
2 |
3 | /*
4 | Copyright (c) 2010, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import java.util.Date;
23 |
24 | public class Position {
25 |
26 | private String symbol;
27 |
28 | private Date entryDate;
29 |
30 | private Date exitDate;
31 |
32 | private double entryPrice;
33 |
34 | private double exitPrice;
35 |
36 | private int quantity;
37 |
38 | private String comments;
39 |
40 | private double score = 0.0;
41 |
42 | private boolean successfullyFilled = true;
43 |
44 | private QuantityCalculator quantityCalculator;
45 |
46 | private Order entry;
47 |
48 | private Order exit;
49 |
50 | private int id;
51 |
52 | private static int idCounter = 0;
53 |
54 | public Position() {
55 | this.id = idCounter++;
56 | }
57 |
58 | public Position(String symbol, Date entryDate, Date exitDate,
59 | double entryPrice, double exitPrice, int quantity) {
60 | super();
61 | this.id = idCounter++;
62 | this.symbol = symbol;
63 | this.entryDate = entryDate;
64 | this.exitDate = exitDate;
65 | this.entryPrice = entryPrice;
66 | this.exitPrice = exitPrice;
67 | this.quantity = quantity;
68 | }
69 |
70 | public boolean isQuantityCalculated(){
71 | return quantityCalculator != null;
72 | }
73 |
74 | public int getId() {
75 | return id;
76 | }
77 |
78 | public void setId(int id) {
79 | this.id = id;
80 | }
81 |
82 | public String getSymbol() {
83 | return symbol;
84 | }
85 |
86 | public void setSymbol(String symbol) {
87 | this.symbol = symbol;
88 | }
89 |
90 | public Date getEntryDate() {
91 | return entryDate;
92 | }
93 |
94 | public void setEntryDate(Date entryDate) {
95 | this.entryDate = entryDate;
96 | }
97 |
98 | public Date getExitDate() {
99 | return exitDate;
100 | }
101 |
102 | public void setExitDate(Date exitDate) {
103 | this.exitDate = exitDate;
104 | }
105 |
106 | public int getQuantity() {
107 | return quantity;
108 | }
109 |
110 | public void setQuantity(int quantity) {
111 | this.quantity = quantity;
112 | }
113 |
114 | public double getEntryPrice() {
115 | return entryPrice;
116 | }
117 |
118 | public void setEntryPrice(double entryPrice) {
119 | this.entryPrice = entryPrice;
120 | }
121 |
122 | public double getExitPrice() {
123 | return exitPrice;
124 | }
125 |
126 | public void setExitPrice(double exitPrice) {
127 | this.exitPrice = exitPrice;
128 | }
129 |
130 | public void appendComment(String comment){
131 | this.comments += ", " + comment;
132 | }
133 |
134 | public void setComments(String comments) {
135 | this.comments = comments;
136 | }
137 |
138 | public String getComments() {
139 | return comments;
140 | }
141 |
142 | public void setScore(double score) {
143 | this.score = score;
144 | }
145 |
146 | public double getScore() {
147 | return score;
148 | }
149 |
150 | public boolean isSuccessfullyFilled() {
151 | return successfullyFilled;
152 | }
153 |
154 | public void setSuccessfullyFilled(boolean successfullyFilled) {
155 | this.successfullyFilled = successfullyFilled;
156 | }
157 |
158 | public QuantityCalculator getQuantityCalculator() {
159 | return quantityCalculator;
160 | }
161 |
162 | public void setQuantityCalculator(QuantityCalculator quantityCalculator) {
163 | this.quantityCalculator = quantityCalculator;
164 | }
165 |
166 | public Order getEntry() {
167 | return entry;
168 | }
169 |
170 | public void setEntry(Order entry) {
171 | this.entry = entry;
172 | }
173 |
174 | public Order getExit() {
175 | return exit;
176 | }
177 |
178 | public void setExit(Order exit) {
179 | this.exit = exit;
180 | }
181 |
182 | @Override
183 | public String toString() {
184 |
185 | return String.format(
186 | "{%1$s} entry[%2$td %2$tb %2$ty %2$tT] : %3$3.2f, exit[%4$td %4$tb %4$ty %4$tT]: %5$3.2f, quantity: %6$s comments: %7$s score: %8$s", symbol,
187 | entryDate, entryPrice, exitDate, exitPrice, quantity, comments, score);
188 | }
189 |
190 |
191 | }
192 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/PositionProcessor.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest;
2 |
3 | /*
4 | Copyright (c) 2011, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | public interface PositionProcessor {
23 |
24 | public void processClosePosition(final Position position);
25 |
26 | public void processOpenPosition(final Position position);
27 |
28 | public void finish();
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/QuantityCalculator.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest;
2 |
3 | public interface QuantityCalculator {
4 |
5 | public int execute(double totalEquity, double availableCash);
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/Series.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest;
2 |
3 | /*
4 | Copyright (c) 2010, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import java.util.Iterator;
23 | import java.util.List;
24 |
25 | import com.tictactec.ta.lib.Core;
26 | import com.tictactec.ta.lib.MInteger;
27 | import com.tictactec.ta.lib.RetCode;
28 |
29 | public class Series {
30 |
31 | private double values[];
32 |
33 | private Core lib = new Core();
34 | MInteger outBegIdx = new MInteger();
35 | MInteger outNbElement = new MInteger();
36 |
37 | public Series(){
38 | }
39 |
40 | public Series(double[] vals) {
41 | values = vals;
42 | }
43 |
44 | public Series(List vals ){
45 | values = convert(vals);
46 | }
47 |
48 | public void setValues(double[] values) {
49 | this.values = values;
50 | }
51 |
52 | public double[] getValues() {
53 | return values;
54 | }
55 |
56 | public double getAt(int i) {
57 | return values[i];
58 | }
59 |
60 | public int size(){
61 | return values.length;
62 | }
63 |
64 | public double lookBack(int i){
65 | return values[values.length-i];
66 | }
67 |
68 | public double getLast(){
69 | return values[values.length-1];
70 | }
71 |
72 | public static double[] convert(List doubles)
73 | {
74 | double[] ret = new double[doubles.size()];
75 | Iterator iterator = doubles.iterator();
76 | for (int i = 0; i < ret.length; i++)
77 | {
78 | ret[i] = iterator.next();
79 | }
80 | return ret;
81 | }
82 |
83 | private double[] adjust(double[] source, int lookback) {
84 |
85 | double rArray[] = new double[source.length];
86 | int i = 0, j = 0;
87 | for (double value : source) {
88 | if (i < lookback) {
89 | rArray[i] = 0.0;
90 | } else {
91 | rArray[i] = source[j++];
92 | }
93 | i++;
94 | }
95 |
96 | return rArray;
97 | }
98 |
99 | public Series Min(int lookback){
100 |
101 | double output[] = new double[values.length];
102 | lookback = lib.minLookback(lookback);
103 |
104 | RetCode retCode = lib.min(0, values.length - 1, values, lookback, outBegIdx, outNbElement, output);
105 |
106 | return new Series(adjust(output, lookback));
107 | }
108 |
109 | public Series EMA(int lookback) {
110 |
111 | double output[] = new double[values.length];
112 | lookback = lib.emaLookback(lookback);
113 | RetCode retCode = lib.ema(0, values.length - 1, values, lookback, outBegIdx, outNbElement, output);
114 |
115 | return new Series(adjust(output, lookback));
116 | }
117 |
118 | public Series SMA(int lookback){
119 |
120 | double output[] = new double[values.length];
121 |
122 | lookback = lib.smaLookback(lookback);
123 | RetCode retCode = lib.sma(0, values.length - 1, values, lookback, outBegIdx, outNbElement, output);
124 |
125 | return new Series(adjust(output, lookback));
126 | }
127 |
128 | public Series WMA(int lookback){
129 |
130 | double output[] = new double[values.length];
131 |
132 | lookback = lib.wmaLookback(lookback);
133 | RetCode retCode = lib.wma(0, values.length - 1, values, lookback, outBegIdx, outNbElement, output);
134 |
135 | return new Series(adjust(output, lookback));
136 | }
137 |
138 | public Series RSI(int lookback){
139 |
140 | double output[] = new double[values.length];
141 |
142 | lookback = lib.rsiLookback(lookback);
143 | RetCode retCode = lib.rsi(0, values.length - 1, values, lookback, outBegIdx, outNbElement, output);
144 |
145 | return new Series(adjust(output, lookback));
146 | }
147 |
148 | public Series CMO(int lookback){
149 |
150 | double output[] = new double[values.length];
151 |
152 | lookback = lib.cmoLookback(lookback);
153 | RetCode retCode = lib.cmo(0, values.length - 1, values, lookback, outBegIdx, outNbElement, output);
154 |
155 | return new Series(adjust(output, lookback));
156 | }
157 |
158 | public Series ROC(int lookback){
159 |
160 | double output[] = new double[values.length];
161 |
162 | lookback = lib.rocLookback(lookback);
163 | RetCode retCode = lib.roc(0, values.length - 1, values, lookback, outBegIdx, outNbElement, output);
164 |
165 | return new Series(adjust(output, lookback));
166 | }
167 |
168 | @Override
169 | public String toString() {
170 | StringBuffer buffer = new StringBuffer();
171 |
172 | for (double value : values) {
173 | buffer.append(value);
174 | buffer.append("\n");
175 | }
176 |
177 | return buffer.toString();
178 | }
179 |
180 | }
181 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/SimpleEquityReport.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest;
2 |
3 | /*
4 | Copyright (c) 2010, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import java.io.BufferedWriter;
23 | import java.io.File;
24 | import java.io.FileWriter;
25 | import java.io.IOException;
26 | import java.io.PrintWriter;
27 | import java.util.Collection;
28 |
29 | import org.apache.commons.logging.Log;
30 | import org.apache.commons.logging.LogFactory;
31 |
32 | public class SimpleEquityReport {
33 |
34 | private Log log = LogFactory.getLog(SimpleEquityReport.class);
35 |
36 | private String directoryname;
37 |
38 | private PrintWriter writer;
39 |
40 | public SimpleEquityReport(Collection positions) {
41 | initialize();
42 | }
43 |
44 | private void initialize() {
45 | try {
46 | String filename = directoryname + "/trades.csv";
47 | FileWriter fileWriter = new FileWriter(new File(filename));
48 | BufferedWriter bufWriter = new BufferedWriter(fileWriter);
49 | writer = new PrintWriter(bufWriter, true);
50 |
51 | createCSVHeader(writer);
52 |
53 | } catch (IOException e) {
54 | log.error(e, e);
55 | }
56 |
57 | }
58 |
59 | private void createCSVHeader(PrintWriter writer) {
60 | writer.println(String.format("%s,%s,%s,%s,%s,%s,%s,%s,%s", "Date", "Order Side", "Symbol", "Shares",
61 | "Share Price", "Commission", "Slippage", "Balance", "Value"));
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/command/OpenQuantCommand.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.command;
2 |
3 | /*
4 | Copyright (c) 2011, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import java.io.File;
23 | import java.io.IOException;
24 | import java.util.ArrayList;
25 | import java.util.Arrays;
26 | import java.util.HashMap;
27 | import java.util.HashSet;
28 | import java.util.Map;
29 |
30 | import org.openquant.backtest.BackTestExecutor;
31 | import org.openquant.backtest.GroovyTradeSystem;
32 | import org.openquant.data.YahooSeriesDatasource;
33 |
34 | import asg.cliche.Command;
35 | import asg.cliche.Param;
36 | import asg.cliche.ShellFactory;
37 |
38 | public class OpenQuantCommand {
39 |
40 | private double capital = 100000;
41 |
42 | private double commission = 9.99;
43 |
44 | private double slippage = 0.0002;
45 |
46 | private static Map symbolMap = new HashMap();
47 |
48 | static {
49 | // load up all persisted lists
50 | for (File pathname : new File("data").listFiles()) {
51 | if (pathname.isFile()) {
52 | StringBuffer output = new StringBuffer(pathname.getName());
53 | // remove the extension
54 | output.replace(output.length() - 4, output.length(), "");
55 | String name = output.toString();
56 | symbolMap.put(name,
57 | new SymbolSetSeries(name, pathname.getName()));
58 | }
59 | }
60 | }
61 |
62 | @Command(abbrev = "cap", description = "Get starting capital")
63 | public double getCapital() {
64 | return capital;
65 | }
66 |
67 | @Command(abbrev = "scap", description = "Set starting capital")
68 | public void setCapital(double capital) {
69 | this.capital = capital;
70 | }
71 |
72 | @Command(abbrev = "slip", description = "Get percent of 1% slippage per trade")
73 | public double getSlippage() {
74 | return slippage;
75 | }
76 |
77 | @Command(abbrev = "sslip", description = "Set percent of 1% slippage per trade")
78 | public void setSlippage(double slippage) {
79 | this.slippage = slippage;
80 | }
81 |
82 | @Command(abbrev = "com", description = "Get commission per trade")
83 | public double getCommission() {
84 | return commission;
85 | }
86 |
87 | @Command(abbrev = "scom", description = "Set commission per trade")
88 | public void setCommission(
89 | @Param(name = "amount", description = "Amount of commission per trade") double amount) {
90 | this.commission = amount;
91 | }
92 |
93 | @Command(abbrev = "clist", description = "Creates a new stock list. ie. clist mylist 2005-01-01 YHOO GOOG MSFT")
94 | public void createStockList(
95 | @Param(name = "name", description = "The stock list's name") String name,
96 | @Param(name = "startDate [" + YahooSeriesDatasource.DATE_FORMAT
97 | + "]", description = "The lookback start date as "
98 | + YahooSeriesDatasource.DATE_FORMAT) String startDate,
99 | @Param(name = "stocks", description = "Spaced-separated list of stock symbols") String... stocks) {
100 | symbolMap.put(name, new SymbolSetSeries(name, new HashSet(
101 | Arrays.asList(stocks)), startDate));
102 | }
103 |
104 | @Command(abbrev = "alist", description = "Add stocks to existing list")
105 | public void addToStockList(
106 | @Param(name = "name", description = "The stock list's name") String name,
107 | @Param(name = "stocks", description = "Spaced-separated list of stock symbols") String... stocks) {
108 |
109 | if (name == null) {
110 | System.out.println("Stocklist Name must be specified.");
111 | }
112 | symbolMap.get(name).getSymbols().addAll(Arrays.asList(stocks));
113 |
114 | }
115 |
116 | @Command(abbrev = "dlist", description = "Delete a specified stock list")
117 | public void deleteStockList(
118 | @Param(name = "name", description = "The stock list's name") String name) {
119 | SymbolSetSeries symbolSet = symbolMap.remove(name);
120 | symbolSet.delete();
121 | }
122 |
123 | @Command(abbrev = "ls", description = "List all stock lists")
124 | public void listAll() {
125 |
126 | for (String key : symbolMap.keySet()) {
127 | System.out.println(key + "\n");
128 | }
129 | }
130 |
131 | @Command(abbrev = "glist", description = "Prints out a specified stock list")
132 | public String getStockList(
133 | @Param(name = "name", description = "The stock list's name") String name) {
134 | StringBuffer output = new StringBuffer();
135 |
136 | if (name == null) {
137 | return output.append("Stocklist Name must be specified.")
138 | .toString();
139 | }
140 |
141 | SymbolSetSeries symbolSet = symbolMap.get(name);
142 | if (symbolSet == null) {
143 | return output.append(
144 | String.format("Symbol list [%s] not found.", name))
145 | .toString();
146 | }
147 |
148 | output.append(symbolSet);
149 |
150 | return output.toString();
151 | }
152 |
153 | @Command(abbrev = "test", description = "Backtest a strategy against a stock list")
154 | public void backTest(
155 | @Param(name = "stockList", description = "The stock list's name") String stockList,
156 | @Param(name = "tradeSystem", description = "The tradesystem script to test against") String tradeSystemScript) {
157 |
158 | SymbolSetSeries symbolSet = symbolMap.get(stockList);
159 | if (symbolSet == null) {
160 | System.out.println(String.format("Symbol list [%s] not found.",
161 | stockList));
162 | return;
163 | }
164 |
165 | BackTestExecutor executor = new BackTestExecutor(symbolSet,
166 | new ArrayList(symbolSet.getSymbols()),
167 | new GroovyTradeSystem(tradeSystemScript), tradeSystemScript,
168 | capital, commission, slippage);
169 |
170 | double cap = executor.run();
171 | System.out.println(String.format("Ending capital is $ %10.2f", cap));
172 | }
173 |
174 | public static void main(String[] args) throws IOException {
175 | ShellFactory
176 | .createConsoleShell(
177 | "openquant",
178 | "Welcome to the OpenQuant 1.0 command console.\nType ?list for a list of commands.",
179 | new OpenQuantCommand()).commandLoop();
180 | }
181 |
182 | }
183 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/command/SymbolSetSeries.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.command;
2 |
3 | /*
4 | Copyright (c) 2011, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import java.io.File;
23 | import java.util.Set;
24 |
25 | import org.openquant.backtest.CandleSeries;
26 | import org.openquant.data.ObjectStreamSeriesDatasource;
27 | import org.openquant.data.SeriesDatasource;
28 | import org.openquant.data.YahooSeriesDatasource;
29 |
30 | class SymbolSetSeries implements SeriesDatasource{
31 |
32 | private String name;
33 |
34 | private YahooSeriesDatasource datasource;
35 |
36 | private ObjectStreamSeriesDatasource localSeriesDatasource;
37 |
38 | private Set symbols;
39 |
40 | private String beginDate;
41 |
42 | private String endDate;
43 |
44 | public SymbolSetSeries(String name, Set symbols, String beginDate) {
45 | super();
46 | this.name = name;
47 | this.symbols = symbols;
48 | this.beginDate = beginDate;
49 | this.setDatasource(new YahooSeriesDatasource(beginDate));
50 | initialize();
51 | }
52 |
53 | public SymbolSetSeries(String name, Set symbols, String beginDate, String endDate) {
54 | super();
55 | this.name = name;
56 | this.symbols = symbols;
57 | this.beginDate = beginDate;
58 | this.setDatasource(new YahooSeriesDatasource(beginDate, endDate));
59 | initialize();
60 | }
61 |
62 | public SymbolSetSeries(String name, String file){
63 | super();
64 | this.name = name;
65 | this.localSeriesDatasource = new ObjectStreamSeriesDatasource( String.format("data/%s.dat", name));
66 | this.symbols = localSeriesDatasource.getSymbols();
67 | this.beginDate = localSeriesDatasource.getBeginDate().toString();
68 | this.endDate = localSeriesDatasource.getEndDate().toString();
69 | }
70 |
71 | public void delete(){
72 | new File(String.format("data/%s.dat", name)).delete();
73 | }
74 |
75 | private void initialize(){
76 |
77 | this.localSeriesDatasource = new ObjectStreamSeriesDatasource( String.format("data/%s.dat", name));
78 |
79 | if(!this.localSeriesDatasource.exists()){
80 | try{
81 |
82 | for (String symbol : symbols){
83 | try {
84 | CandleSeries series = datasource.fetchSeries(symbol);
85 | localSeriesDatasource.insertCandleSeries(series);
86 | } catch (Exception e) {
87 | // TODO Auto-generated catch block
88 | e.printStackTrace();
89 | }
90 | }
91 |
92 | }finally{
93 | localSeriesDatasource.commit();
94 | }
95 | }
96 |
97 |
98 | }
99 |
100 | public Set getSymbols() {
101 | return symbols;
102 | }
103 |
104 | public void setSymbols(Set symbols) {
105 | this.symbols = symbols;
106 | }
107 |
108 | @Override
109 | public String toString() {
110 | StringBuffer output = new StringBuffer();
111 | output.append(String.format("[%s, %s]",name, beginDate));
112 | output.append("\n");
113 |
114 | for (String symbol : symbols){
115 | output.append(symbol + "\n");
116 | }
117 |
118 | // remove the last newline
119 | output.replace(output.length()-1, output.length(), "");
120 |
121 | return output.toString();
122 | }
123 |
124 | public void setDatasource(YahooSeriesDatasource datasource) {
125 | this.datasource = datasource;
126 | }
127 |
128 | public YahooSeriesDatasource getDatasource() {
129 | return datasource;
130 | }
131 |
132 | @Override
133 | public CandleSeries fetchSeries(String symbol) throws Exception {
134 |
135 | return localSeriesDatasource.fetchSeries(symbol);
136 |
137 | }
138 |
139 | }
--------------------------------------------------------------------------------
/src/org/openquant/backtest/intraday/AbstractIntradayTest.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.intraday;
2 |
3 | import org.openquant.backtest.Candle;
4 | import org.openquant.backtest.Position;
5 | import org.openquant.backtest.QuantityCalculator;
6 | import org.openquant.backtest.Series;
7 |
8 |
9 | public abstract class AbstractIntradayTest {
10 |
11 | private IntradayCandleSeries candleSeries;
12 |
13 | private IntradayOrderManager orderManager = new IntradayOrderManager();
14 |
15 | public void reset() {
16 |
17 | orderManager.setSymbol(null);
18 | orderManager.reset();
19 | }
20 |
21 | public void setSymbol(String symbol) {
22 | orderManager.setSymbol(symbol);
23 | }
24 |
25 | public void processTick(Candle tick) {
26 | orderManager.processTick(tick);
27 | }
28 |
29 | public void processClose(Candle tick) {
30 | orderManager.processClose(tick);
31 | }
32 |
33 | public void buyAtLimit(double limitPrice, int quantity, OrderCallback callback) {
34 | orderManager.buyAtLimit(limitPrice, quantity, callback);
35 | }
36 |
37 | public void buyAtLimit(double limitPrice, QuantityCalculator quantityCalc, OrderCallback callback) {
38 | orderManager.buyAtLimit(limitPrice, quantityCalc, callback);
39 | }
40 |
41 | public void sellAtLimit(double limitPrice, int quantity, Position position, OrderCallback callback) {
42 | orderManager.sellAtLimit(limitPrice, quantity, position, callback);
43 | }
44 |
45 | public void sellAtStop(double stopPrice, int quantity, Position position, OrderCallback callback) {
46 | orderManager.sellAtStop(stopPrice, quantity, position, callback);
47 | }
48 |
49 | public void timeBasedExitOnClose(int days, Position position, OrderCallback callback){
50 | orderManager.timeBasedExitOnClose(days, position, callback);
51 | }
52 |
53 | public void setCandleSeries(IntradayCandleSeries candleSeries) {
54 | this.candleSeries = candleSeries;
55 | orderManager.setSymbol(candleSeries.getSymbol());
56 | }
57 |
58 | public IntradayCandleSeries getCandleSeries() {
59 | return candleSeries;
60 | }
61 |
62 | public void setOrderManager(IntradayOrderManager orderManager) {
63 | this.orderManager = orderManager;
64 | }
65 |
66 | public IntradayOrderManager getOrderManager() {
67 | return orderManager;
68 | }
69 |
70 | public abstract void run();
71 |
72 | public Series dayCloseSeries(){
73 | return candleSeries.getDayCloseSeries();
74 | }
75 |
76 | public Series dayOpenSeries(){
77 | return candleSeries.getDayOpenSeries();
78 | }
79 |
80 | public Series dayLowSeries(){
81 | return candleSeries.getDayLowSeries();
82 | }
83 |
84 | public Series dayHighSeries(){
85 | return candleSeries.getDayHighSeries();
86 | }
87 |
88 | public int barsCount() {
89 | return candleSeries.size();
90 | }
91 |
92 | public double getClose(int index){
93 | return candleSeries.getDayCloseSeries().getAt(index);
94 | }
95 |
96 | public boolean hasOpenPositions() {
97 | return orderManager.hasOpenPositions();
98 | }
99 |
100 | public int getOpenPositionCount(){
101 | return orderManager.getOpenPositionCount();
102 | }
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/intraday/BuyIntent.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.intraday;
2 |
3 | import org.openquant.backtest.QuantityCalculator;
4 |
5 |
6 | public class BuyIntent{
7 |
8 | private double price;
9 |
10 | private int quantity;
11 |
12 | private OrderTimeFrame timeframe = OrderTimeFrame.END_OF_DAY;
13 |
14 | private OrderCallback callback;
15 |
16 | private QuantityCalculator quantityCalc;
17 |
18 | public BuyIntent(double price, int quantity, OrderCallback callback){
19 | this.price = price;
20 | this.quantity = quantity;
21 | this.callback = callback;
22 | }
23 |
24 | public BuyIntent(double price, QuantityCalculator quantityCalc, OrderCallback callback){
25 | this.price = price;
26 | this.quantityCalc = quantityCalc;
27 | this.callback = callback;
28 | }
29 |
30 | public BuyIntent(double price, int quantity, OrderTimeFrame timeframe, OrderCallback callback) {
31 | super();
32 | this.price = price;
33 | this.quantity = quantity;
34 | this.timeframe = timeframe;
35 | this.callback = callback;
36 | }
37 |
38 | public boolean isQuantityCalculated(){
39 | return quantityCalc != null;
40 | }
41 | public double getPrice() {
42 | return price;
43 | }
44 |
45 | public OrderCallback getCallback() {
46 | return callback;
47 | }
48 |
49 | public int getQuantity() {
50 | return quantity;
51 | }
52 |
53 | public OrderTimeFrame getTimeframe() {
54 | return timeframe;
55 | }
56 |
57 | public void setTimeframe(OrderTimeFrame timeframe) {
58 | this.timeframe = timeframe;
59 | }
60 |
61 | public QuantityCalculator getQuantityCalc() {
62 | return quantityCalc;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/intraday/CreateChart.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.intraday;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.util.ArrayList;
6 | import java.util.Calendar;
7 | import java.util.Date;
8 | import java.util.List;
9 |
10 | import org.jfree.chart.ChartFactory;
11 | import org.jfree.chart.ChartUtilities;
12 | import org.jfree.chart.JFreeChart;
13 | import org.jfree.data.time.TimeSeriesCollection;
14 | import org.jfree.data.xy.DefaultHighLowDataset;
15 | import org.jfree.date.DayOfWeekInMonthRule;
16 | import org.openquant.backtest.Series;
17 | import org.openquant.data.SeriesDatasource;
18 |
19 | public class CreateChart extends AbstractIntradayTest {
20 |
21 | @Override
22 | public void run() {
23 |
24 | System.out.println("Beginning test...");
25 |
26 | createChart(createDataset());
27 |
28 | }
29 |
30 | private DefaultHighLowDataset createDataset() {
31 |
32 | int serice = getCandleSeries().getDayCount();
33 |
34 | Date[] date = new Date[serice];
35 | double[] high = new double[serice];
36 | double[] low = new double[serice];
37 | double[] open = new double[serice];
38 | double[] close = new double[serice];
39 | double[] volume = new double[serice];
40 |
41 | Series highs = dayHighSeries();
42 | Series lows = dayLowSeries();
43 | Series closes = dayCloseSeries();
44 | Series opens = dayOpenSeries();
45 |
46 | IntradayCandleSeries daySeries = getCandleSeries();
47 |
48 | for (int i = 0; i < getCandleSeries().getDayCount(); i++) {
49 |
50 | date[i] = daySeries.getDayCandles(i).get(0).getDate();
51 | high[i] = highs.getAt(i);
52 | low[i] = lows.getAt(i);
53 | open[i] = opens.getAt(i);
54 | close[i] = closes.getAt(i);
55 | volume[i] = 0;
56 |
57 | }
58 |
59 | DefaultHighLowDataset data = new DefaultHighLowDataset("", date, high, low, open, close, volume);
60 | return data;
61 | }
62 |
63 | private void createChart(final DefaultHighLowDataset dataset) {
64 | final JFreeChart chart = ChartFactory.createCandlestickChart("Candlestick Demo", "Time", "Price", dataset,
65 | false);
66 |
67 | try {
68 | ChartUtilities.saveChartAsJPEG(new File("MSFT-Candle"+".jpg"), chart, 1000, 600);
69 | } catch (IOException e) {
70 | // TODO Auto-generated catch block
71 | e.printStackTrace();
72 | }
73 | }
74 |
75 | void debugCandles(List cList) {
76 |
77 | for (IntradayCandle c : cList) {
78 | System.out.println(c);
79 | }
80 |
81 | }
82 |
83 | public static void main(String... args) {
84 |
85 | SeriesDatasource data = new IntradaySeriesDatasource("/home/jay/StockDatabase/Test");
86 | List symbols = new ArrayList();
87 |
88 | symbols.add("MSFT");
89 |
90 | IntradayBackTestExecutor executor = new IntradayBackTestExecutor(data, symbols, new CreateChart(), "ATest");
91 |
92 | executor.run();
93 |
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/intraday/DipperIntradayTest.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.intraday;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import org.apache.commons.logging.Log;
7 | import org.apache.commons.logging.LogFactory;
8 | import org.openquant.backtest.Position;
9 | import org.openquant.backtest.QuantityCalculator;
10 | import org.openquant.backtest.Series;
11 | import org.openquant.backtest.intraday.IntradayCandle.CANDLETYPE;
12 | import org.openquant.data.SeriesDatasource;
13 |
14 | public class DipperIntradayTest extends AbstractIntradayTest {
15 |
16 | private static Log log = LogFactory.getLog(DipperIntradayTest.class);
17 |
18 | private static final int EXIT_TIME_DAYS = 1;
19 |
20 | private static final double MAX_POSITION_AMOUNT = 100000;
21 |
22 | @Override
23 | public void run() {
24 |
25 | Series Closes = dayCloseSeries();
26 |
27 | IntradayCandleSeries daySeries = getCandleSeries();
28 |
29 | for (int bar = 2; bar < daySeries.getDayCount(); bar++) {
30 |
31 | //System.out.println( String.format( "bar : %s, date : %s, low : %s, Filter.getAt(bar) : %s", dayIndex, daySeries.getDayCandles(dayIndex).get(0).getDate(),
32 | // Lows.getAt(dayIndex), Filter.getAt(dayIndex) ));
33 |
34 | final int b = bar;
35 |
36 | List dayCandles = daySeries.getDayCandles(bar);
37 |
38 | //debugCandles(dayCandles);
39 |
40 | double todaysOpen = 0;
41 |
42 | for (IntradayCandle candle : dayCandles) {
43 |
44 | if (candle.getType().equals(CANDLETYPE.OPEN)) {
45 | todaysOpen = candle.getOpenPrice();
46 | }
47 |
48 | // tick processing for the order manager
49 | if (candle.getType().equals(CANDLETYPE.CLOSE)) {
50 | processClose(candle);
51 | } else {
52 | processTick(candle);
53 | }
54 |
55 | // do not open more than one position per stock at a time
56 | if (!hasOpenPositions()) {
57 |
58 | final double LIMITPRICE = Closes.getAt(bar-1) - ( todaysOpen * 0.08);
59 |
60 | buyAtLimit(LIMITPRICE, new QuantityCalculator() {
61 |
62 | @Override
63 | public int execute(double totalEquity, double availableCash) {
64 | int quantity = (int) (totalEquity/20 / LIMITPRICE);
65 |
66 | if (quantity * LIMITPRICE > MAX_POSITION_AMOUNT){
67 | quantity = (int) (MAX_POSITION_AMOUNT / LIMITPRICE);
68 | }
69 | return quantity;
70 | }
71 | }, new OrderCallback() {
72 |
73 | @Override
74 | public void success(Position position) {
75 |
76 | String candleString = String.format("Candle[%s, %s, %s, %s]", dayOpenSeries().getAt(b), dayHighSeries().getAt(b), dayLowSeries().getAt(b), dayCloseSeries().getAt(b));
77 |
78 | System.out.println("ENTER Position : " + candleString + " at limit Price: " + position.getEntryPrice());
79 |
80 | timeBasedExitOnClose(EXIT_TIME_DAYS, position, new OrderCallback() {
81 |
82 |
83 | @Override
84 | public void success(Position position) {
85 | }
86 |
87 | @Override
88 | public void expired() {
89 | // TODO Auto-generated method stub
90 |
91 | }
92 | });
93 |
94 | }
95 |
96 | @Override
97 | public void expired() {
98 | // TODO Auto-generated method stub
99 |
100 | }
101 | });
102 |
103 |
104 | }
105 | }
106 | }
107 |
108 | }
109 |
110 | void debugCandles(List cList){
111 |
112 | for(IntradayCandle c : cList){
113 | System.out.println(c);
114 | }
115 |
116 | }
117 |
118 | public static void main(String ... args){
119 |
120 | SeriesDatasource datasource = new IntradaySeriesDatasource("/home/jay/StockDatabase/Test");
121 | //IntradayCandleSeries series = (IntradayCandleSeries) datasource.fetchSeries("MSFT");
122 |
123 | List symbols = new ArrayList();
124 |
125 | symbols.add("MSFT");
126 |
127 | // nasdaq10
128 | /*
129 | symbols.add("ATVI");
130 | symbols.add("ADBE");
131 | symbols.add("AKAM");
132 | symbols.add("ALTR");
133 | symbols.add("AMZN");
134 | symbols.add("AMGN");
135 | symbols.add("APOL");
136 | symbols.add("AAPL");
137 | symbols.add("AMAT");
138 | symbols.add("ADSK");
139 | */
140 |
141 | //IntradayBackTestExecutor executor = new IntradayBackTestExecutor("/home/jay/StockDatabase/Test", new DipperIntradayTest(), "IntradayDipper-100");
142 |
143 | IntradayBackTestExecutor executor = new IntradayBackTestExecutor(datasource, symbols, new DipperIntradayTest(), "IntradayDipper-MSFT");
144 | executor.run();
145 |
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/intraday/IntradayBackTestExecutor.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.intraday;
2 |
3 | /*
4 | Copyright (c) 2011, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import java.io.File;
23 | import java.util.ArrayList;
24 | import java.util.Collections;
25 | import java.util.Comparator;
26 | import java.util.List;
27 |
28 | import org.apache.commons.logging.Log;
29 | import org.apache.commons.logging.LogFactory;
30 | import org.openquant.backtest.Position;
31 | import org.openquant.backtest.report.AbstractReport;
32 | import org.openquant.backtest.report.CSVReport;
33 | import org.openquant.backtest.report.JFreeChartReport;
34 | import org.openquant.backtest.report.TradesReport;
35 | import org.openquant.data.SeriesDatasource;
36 | import org.springframework.util.StopWatch;
37 |
38 | public class IntradayBackTestExecutor {
39 |
40 | private Log log = LogFactory.getLog(IntradayBackTestExecutor.class);
41 |
42 | private SeriesDatasource data;
43 |
44 | private List symbols;
45 |
46 | private AbstractIntradayTest test;
47 |
48 | private double capital = 100000;
49 |
50 | private double commission = 9.99;
51 |
52 | private double slippage = 0.001;
53 |
54 | private List positions = new ArrayList();
55 |
56 | private String reportName;
57 |
58 | public IntradayBackTestExecutor(SeriesDatasource data, List symbols,
59 | AbstractIntradayTest test, String reportName, double capital,
60 | double commission, double slippage) {
61 | super();
62 | this.data = data;
63 | this.symbols = symbols;
64 | this.test = test;
65 | this.capital = capital;
66 | this.commission = commission;
67 | this.slippage = slippage;
68 | this.reportName = reportName;
69 | }
70 |
71 | public IntradayBackTestExecutor(SeriesDatasource data, List symbols,
72 | AbstractIntradayTest test, String reportName){
73 | super();
74 | this.data = data;
75 | this.symbols = symbols;
76 | this.test = test;
77 | this.reportName = reportName;
78 | }
79 |
80 | public IntradayBackTestExecutor(String symbolsDirectory, AbstractIntradayTest test, String reportName){
81 | super();
82 | data = new IntradaySeriesDatasource(symbolsDirectory);
83 | populateSymbolsFromDirectory(symbolsDirectory);
84 | this.test = test;
85 | this.reportName = reportName;
86 | }
87 |
88 | private void populateSymbolsFromDirectory(String directoryName){
89 |
90 | symbols = new ArrayList();
91 |
92 | File dir = new File(directoryName);
93 |
94 | String[] children = dir.list();
95 | if (children == null) {
96 | // Either dir does not exist or is not a directory
97 | } else {
98 | for (int i=0; i() {
139 |
140 | @Override
141 | public int compare(Position positionOne, Position positionTwo) {
142 | return positionOne.getEntryDate().compareTo(
143 | positionTwo.getEntryDate());
144 | }
145 |
146 | });
147 |
148 | // generate reports
149 |
150 | AbstractReport report = new JFreeChartReport(reportName + ".jpg",
151 | capital, commission, slippage, positions, test
152 | .getOrderManager().getOpenPositions());
153 | double endingCapital = report.getTotalCapitalAndEquity();
154 | log.info(String.format("Ending capital is %12.2f", endingCapital));
155 | report.render();
156 |
157 |
158 | CSVReport report2 = new CSVReport(capital, commission, slippage, positions, test
159 | .getOrderManager().getOpenPositions(), reportName + ".csv");
160 | report2.render();
161 |
162 | new TradesReport(capital, commission, slippage, positions, test
163 | .getOrderManager().getOpenPositions(), reportName + "-trades.txt").render();
164 |
165 | watch.stop();
166 | log.debug(String.format("Time : %s seconds", watch.getTotalTimeSeconds()));
167 |
168 | return 0;
169 | }
170 |
171 |
172 | }
--------------------------------------------------------------------------------
/src/org/openquant/backtest/intraday/IntradayCandle.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.intraday;
2 |
3 | import java.util.Date;
4 |
5 | import org.openquant.backtest.Candle;
6 |
7 | public class IntradayCandle extends Candle {
8 |
9 | private static final long serialVersionUID = 3611441350748138120L;
10 |
11 | public enum CANDLETYPE{
12 | TICK,
13 | OPEN,
14 | HIGH,
15 | LOW,
16 | CLOSE
17 | }
18 |
19 | private CANDLETYPE type = CANDLETYPE.TICK;
20 |
21 | public IntradayCandle() {
22 | super();
23 | }
24 |
25 | public IntradayCandle(String symbol, Date date, double open, double high,
26 | double low, double close, double volume) {
27 | super(symbol, date, open, high, low, close, volume);
28 | }
29 |
30 | public void setType(CANDLETYPE type) {
31 | this.type = type;
32 | }
33 |
34 | public CANDLETYPE getType() {
35 | return type;
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/intraday/IntradayCandleSeries.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.intraday;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Date;
5 | import java.util.HashMap;
6 | import java.util.List;
7 | import java.util.Map;
8 |
9 | import org.openquant.backtest.Candle;
10 | import org.openquant.backtest.CandleSeries;
11 | import org.openquant.backtest.Series;
12 | import org.openquant.backtest.intraday.IntradayCandle.CANDLETYPE;
13 | import org.openquant.util.Day;
14 |
15 | public class IntradayCandleSeries extends CandleSeries {
16 |
17 | private static final long serialVersionUID = 1L;
18 |
19 | private Series dayCloseSeries;
20 |
21 | private Series dayHighSeries;
22 |
23 | private Series dayLowSeries;
24 |
25 | private Series dayOpenSeries;
26 |
27 | private List> dayCandles = new ArrayList>();
28 |
29 | public IntradayCandleSeries(List candles) {
30 | super(candles);
31 |
32 | processCandles();
33 | }
34 |
35 | public int getDayCount(){
36 | return dayCandles.size();
37 | }
38 |
39 | public List getDayCandles(int dayIndex){
40 | return dayCandles.get(dayIndex);
41 | }
42 |
43 | private void processCandles() {
44 |
45 | List closes = new ArrayList();
46 | List opens = new ArrayList();
47 |
48 | List highs = new ArrayList();
49 | List lows = new ArrayList();
50 |
51 | IntradayCandle previous = null;
52 |
53 | List day = new ArrayList();
54 |
55 | double dayLow = 0;
56 | double dayHigh = 0;
57 |
58 | for (Candle can : candles) {
59 |
60 | IntradayCandle candle = (IntradayCandle)can;
61 |
62 | if (previous == null) {
63 |
64 | dayLow = candle.getLowPrice();
65 | dayHigh = candle.getHighPrice();
66 |
67 | opens.add(candle.getOpenPrice());
68 | previous = candle;
69 | continue;
70 | }
71 |
72 | if (Day.compare(candle.getDate(), previous.getDate()) != 0) {
73 | // another day
74 | // mark the candles as open and close candles
75 |
76 | previous.setType(CANDLETYPE.CLOSE);
77 | candle.setType(CANDLETYPE.OPEN);
78 |
79 | closes.add(previous.getClosePrice());
80 | opens.add(candle.getOpenPrice());
81 | highs.add(dayHigh);
82 | lows.add(dayLow);
83 | dayLow = candle.getLowPrice();
84 | dayHigh = candle.getHighPrice();
85 |
86 |
87 | dayCandles.add(day);
88 | day = new ArrayList();
89 |
90 | }
91 |
92 | // check the highs/lows
93 | if (candle.getLowPrice() < dayLow){
94 | dayLow = candle.getLowPrice();
95 | }
96 | if (candle.getHighPrice() > dayHigh){
97 | dayHigh = candle.getHighPrice();
98 | }
99 |
100 | day.add(candle);
101 |
102 | previous = candle;
103 |
104 | }
105 |
106 | dayCloseSeries = new Series(closes);
107 | dayOpenSeries = new Series(opens);
108 | dayHighSeries = new Series(highs);
109 | dayLowSeries = new Series(lows);
110 | }
111 |
112 | public Series getDayHighSeries() {
113 | return dayHighSeries;
114 | }
115 |
116 | public Series getDayLowSeries() {
117 | return dayLowSeries;
118 | }
119 |
120 | public Series getDayOpenSeries() {
121 | return dayOpenSeries;
122 | }
123 |
124 | public Series getDayCloseSeries() {
125 | return dayCloseSeries;
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/intraday/IntradayOrderManager.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.intraday;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collection;
5 | import java.util.Iterator;
6 | import java.util.LinkedHashSet;
7 | import java.util.List;
8 | import java.util.Set;
9 |
10 | import org.apache.commons.logging.Log;
11 | import org.apache.commons.logging.LogFactory;
12 | import org.openquant.backtest.Candle;
13 | import org.openquant.backtest.Position;
14 | import org.openquant.backtest.QuantityCalculator;
15 |
16 | public class IntradayOrderManager {
17 |
18 | private Log log = LogFactory.getLog(IntradayOrderManager.class);
19 |
20 | private String symbol;
21 |
22 | private Set openPositions = new LinkedHashSet();
23 |
24 | private Set closedPositions = new LinkedHashSet();
25 |
26 | private List buyLimitIntents = new ArrayList();
27 |
28 | private List sellLimitIntents = new ArrayList();
29 |
30 | private List sellStopIntents = new ArrayList();
31 |
32 | private List timeBaseCloseIntents = new ArrayList();
33 |
34 | public String getSymbol() {
35 | return symbol;
36 | }
37 |
38 | public void setSymbol(String symbol) {
39 | this.symbol = symbol;
40 | }
41 |
42 | public Set getOpenPositions() {
43 | return openPositions;
44 | }
45 |
46 | public Collection getClosedPositions() {
47 | return closedPositions;
48 | }
49 |
50 | public void reset(){
51 | clearAllBuyIntents();
52 | clearAllSellIntents();
53 | closedPositions.clear();
54 | openPositions.clear();
55 | }
56 |
57 | private void clearAllBuyIntents(){
58 | buyLimitIntents.clear();
59 | }
60 |
61 | private void clearAllSellIntents(){
62 | sellLimitIntents.clear();
63 | sellStopIntents.clear();
64 | timeBaseCloseIntents.clear();
65 | }
66 |
67 | public void processTick(final Candle tick){
68 | //log.info("Tick " + tick);
69 |
70 | // limit buy orders
71 | for (Iterator iter = buyLimitIntents.iterator(); iter.hasNext();) {
72 | BuyIntent intent = iter.next();
73 | if (tick.getClosePrice() <= intent.getPrice()){
74 | // create our order
75 | Position pos = new Position();
76 | pos.setSymbol(getSymbol());
77 | pos.setEntryPrice(tick.getClosePrice());
78 | pos.setQuantity(intent.getQuantity());
79 | pos.setEntryDate(tick.getDate());
80 | if(intent.isQuantityCalculated()){
81 | pos.setQuantityCalculator(intent.getQuantityCalc());
82 | }
83 |
84 | openPosition(pos);
85 |
86 | intent.getCallback().success(pos);
87 | iter.remove();
88 |
89 | // since this is intended to be used by a single dataset at a time,
90 | // assume that multiple intraday orders of the same stock is not allowed
91 | // and clear out all other buy intents
92 | clearAllBuyIntents();
93 |
94 | break;
95 | }
96 | }
97 |
98 | // limit sell orders
99 | for (Iterator iter = sellLimitIntents.iterator(); iter.hasNext();) {
100 | SellIntent intent = iter.next();
101 |
102 | if (tick.getClosePrice() >= intent.getPrice() ){
103 |
104 | Position pos = intent.getPosition();
105 | pos.setExitPrice(tick.getClosePrice());
106 | pos.setQuantity(intent.getQuantity());
107 | pos.setExitDate(tick.getDate());
108 |
109 | pos.setComments("SELL LIMIT sell on " + tick.getDate());
110 |
111 | closePosition(pos);
112 | intent.getCallback().success(pos);
113 | iter.remove();
114 |
115 | clearAllSellIntents();
116 | break;
117 | }
118 |
119 | }
120 |
121 | for (Iterator iter = sellStopIntents.iterator(); iter.hasNext();) {
122 | SellIntent intent = iter.next();
123 |
124 | if (tick.getClosePrice() <= intent.getPrice() ){
125 |
126 | Position pos = intent.getPosition();
127 | pos.setExitPrice(tick.getClosePrice());
128 | pos.setQuantity(intent.getQuantity());
129 | pos.setExitDate(tick.getDate());
130 |
131 | closePosition(pos);
132 | intent.getCallback().success(pos);
133 | iter.remove();
134 |
135 | clearAllSellIntents();
136 | break;
137 | }
138 | }
139 |
140 | }
141 |
142 | public void processClose(final Candle tick){
143 |
144 | //log.info("CLOSE Tick " + tick);
145 |
146 | // limit buy orders
147 | for (Iterator iter = buyLimitIntents.iterator(); iter.hasNext();) {
148 | BuyIntent intent = iter.next();
149 |
150 | if (intent.getTimeframe().equals(OrderTimeFrame.END_OF_DAY)){
151 | intent.getCallback().expired();
152 | iter.remove();
153 | }
154 | }
155 | // limit sell orders
156 | for (Iterator iter = sellLimitIntents.iterator(); iter.hasNext();) {
157 | SellIntent intent = iter.next();
158 |
159 | if (intent.getTimeframe().equals(OrderTimeFrame.END_OF_DAY)){
160 | iter.remove();
161 | }
162 | }
163 |
164 | // limit sell orders
165 | for (Iterator iter = sellStopIntents.iterator(); iter.hasNext();) {
166 | SellIntent intent = iter.next();
167 |
168 | if (intent.getTimeframe().equals(OrderTimeFrame.END_OF_DAY)){
169 | iter.remove();
170 | }
171 | }
172 |
173 | for (Iterator iter = timeBaseCloseIntents.iterator(); iter.hasNext();) {
174 | TimeBasedSellIntent intent = iter.next();
175 |
176 | if (intent.decrement() == 0){
177 | Position pos = intent.getPosition();
178 | pos.setExitPrice(tick.getClosePrice());
179 | pos.setExitDate(tick.getDate());
180 |
181 | pos.setComments("Time base sell on " + tick.getDate());
182 |
183 | closePosition(pos);
184 | intent.getCallback().success(pos);
185 | iter.remove();
186 |
187 | clearAllSellIntents();
188 | }
189 |
190 | }
191 |
192 | }
193 |
194 | private void openPosition(Position pos) {
195 |
196 | //log.info("Opening position " + pos);
197 |
198 | pos.setSymbol(symbol);
199 | openPositions.add(pos);
200 | }
201 |
202 | private void closePosition(Position pos) {
203 |
204 | //log.info("Closing position " + pos);
205 |
206 | if (openPositions.remove(pos)) {
207 | closedPositions.add(pos);
208 | } else {
209 | log.error("Could not find open position" + pos);
210 | }
211 | }
212 |
213 | public boolean hasOpenPositions() {
214 | return !openPositions.isEmpty();
215 | }
216 |
217 | public int getOpenPositionCount(){
218 | return openPositions.size();
219 | }
220 |
221 | /**
222 | * An order to a broker to buy a specified quantity of a security at or
223 | * below a specified price (called the limit price).
224 | *
225 | * Read more:
226 | * http://www.investorwords.com/648/buy_limit_order.html#ixzz17LLcHmCc
227 | *
228 | */
229 | public void buyAtLimit(double limitPrice, int quantity, OrderCallback callback) {
230 | buyLimitIntents.add(new BuyIntent(limitPrice, quantity, callback));
231 | }
232 |
233 |
234 | public void buyAtLimit(double limitPrice, QuantityCalculator calculator, OrderCallback callback) {
235 | buyLimitIntents.add(new BuyIntent(limitPrice, calculator, callback));
236 | }
237 |
238 | /**
239 | * An order to a broker to sell a specified quantity of a security at or
240 | * above a specified price (called the limit price)
241 | *
242 | * Read more:
243 | * http://www.investorwords.com/4479/sell_limit_order.html#ixzz17LYIFrkY
244 | *
245 | */
246 | public void sellAtLimit(double limitPrice, int quantity, Position position, OrderCallback callback){
247 | sellLimitIntents.add(new SellIntent(limitPrice, quantity, position, callback));
248 | }
249 |
250 | public void timeBasedSellAtClose(int days, Position position, OrderCallback callback){
251 | //sellLimitIntents.add(new SellIntent(limitPrice, quantity, position, callback));
252 | timeBaseCloseIntents.add(new TimeBasedSellIntent(days, position, callback));
253 | }
254 |
255 | /**
256 | * A stop order for which the specified price is below the current market
257 | * price and the order is to sell.
258 | *
259 | * Read more: http://www.investorwords.com/4757/stop_loss.html#ixzz17LYl0YFK
260 | *
261 | */
262 | public void sellAtStop(double stopPrice, int quantity, Position position, OrderCallback callback){
263 | sellStopIntents.add(new SellIntent(stopPrice, quantity, position, callback));
264 | }
265 |
266 | public void timeBasedExitOnClose(int days, Position position, OrderCallback callback){
267 | timeBaseCloseIntents.add( new TimeBasedSellIntent(days, position, callback) );
268 | }
269 |
270 | }
271 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/intraday/IntradayQuoteDatasource.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.intraday;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.File;
5 | import java.io.FileReader;
6 | import java.util.ArrayList;
7 | import java.util.Collections;
8 | import java.util.HashSet;
9 | import java.util.List;
10 | import java.util.Set;
11 | import java.util.StringTokenizer;
12 |
13 | import org.apache.commons.logging.Log;
14 | import org.apache.commons.logging.LogFactory;
15 | import org.openquant.backtest.Candle;
16 | import org.openquant.quote.QuoteDataSource;
17 | import org.openquant.quote.YahooQuoteDataSource;
18 |
19 | public class IntradayQuoteDatasource extends QuoteDataSource {
20 |
21 | List orderedQuotes = new ArrayList();
22 |
23 | public IntradayQuoteDatasource(String directory, Set symbols,
24 | GlobalQuoteListener globalListener) {
25 | super(symbols, globalListener);
26 | initializeSymbolIterators(directory, symbols);
27 | }
28 |
29 | private void initializeSymbolIterators(String directory, Set symbols) {
30 |
31 | for (String symbol : symbols) {
32 |
33 | try {
34 |
35 | // csv file containing data
36 | String strFile = directory + File.separator + symbol + ".txt";
37 |
38 | // create BufferedReader to read csv file
39 | BufferedReader br = new BufferedReader(new FileReader(strFile));
40 | String strLine = "";
41 | StringTokenizer st = null;
42 | int lineNumber = 0, tokenNumber = 0;
43 |
44 | // read comma separated file line by line
45 | while ((strLine = br.readLine()) != null) {
46 | lineNumber++;
47 |
48 | // break comma separated line using ","
49 | st = new StringTokenizer(strLine, ",");
50 |
51 | while (st.hasMoreTokens()) {
52 | // display csv values
53 | tokenNumber++;
54 | System.out.println("Line # " + lineNumber
55 | + ", Token # " + tokenNumber + ", Token : "
56 | + st.nextToken());
57 | }
58 |
59 | // reset token number
60 | tokenNumber = 0;
61 |
62 | }
63 |
64 | } catch (Exception e) {
65 | System.out.println("Exception while reading csv file: " + e);
66 | }
67 |
68 | }
69 |
70 | Collections.sort(orderedQuotes);
71 |
72 | }
73 |
74 | @Override
75 | public Candle retrieveQuoteCandle(String symbol) throws Exception {
76 | // TODO Auto-generated method stub
77 | return null;
78 | }
79 |
80 | public static void main(String... args) {
81 |
82 | final Log log = LogFactory.getLog(YahooQuoteDataSource.class);
83 |
84 | Set symbols = new HashSet();
85 | symbols.add("MSFT");
86 | symbols.add("ORCL");
87 |
88 | QuoteDataSource quote = new IntradayQuoteDatasource(
89 | "/home/jay/StockDatabase/Test", symbols,
90 | new GlobalQuoteListener() {
91 |
92 | @Override
93 | public void onQuote(Candle quoteCandle) {
94 | log.info(String.format(
95 | "Got quote %s[%s, %s, %s, %s, %s, %s]",
96 | quoteCandle.getSymbol(),
97 | quoteCandle.getOpenPrice(),
98 | quoteCandle.getHighPrice(),
99 | quoteCandle.getLowPrice(),
100 | quoteCandle.getClosePrice(),
101 | quoteCandle.getVolume(), quoteCandle.getDate()));
102 |
103 | }
104 |
105 | @Override
106 | public void onOpenQuote(Candle quoteCandle) {
107 | log.info(String.format(
108 | "Got open quote %s[%s, %s, %s, %s, %s, %s]",
109 | quoteCandle.getSymbol(),
110 | quoteCandle.getOpenPrice(),
111 | quoteCandle.getHighPrice(),
112 | quoteCandle.getLowPrice(),
113 | quoteCandle.getClosePrice(),
114 | quoteCandle.getVolume(), quoteCandle.getDate()));
115 |
116 | }
117 | });
118 |
119 | quote.setLoopForever(true);
120 | quote.setInterval(1);
121 | quote.initialize();
122 |
123 | }
124 |
125 | }
126 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/intraday/IntradaySeriesDatasource.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.intraday;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.File;
5 | import java.io.FileReader;
6 | import java.text.DateFormat;
7 | import java.text.SimpleDateFormat;
8 | import java.util.ArrayList;
9 | import java.util.Date;
10 | import java.util.List;
11 | import java.util.StringTokenizer;
12 |
13 | import org.openquant.backtest.Candle;
14 | import org.openquant.backtest.CandleSeries;
15 | import org.openquant.data.SeriesDatasource;
16 |
17 | public class IntradaySeriesDatasource implements SeriesDatasource {
18 |
19 | public static final String DATE_TIME_FORMAT = "MM/dd/yyyyHHmm";
20 |
21 | private final static DateFormat sdf = new SimpleDateFormat(DATE_TIME_FORMAT);
22 |
23 | private String baseDirectory;
24 |
25 | public IntradaySeriesDatasource(String baseDirectory){
26 | this.baseDirectory = baseDirectory;
27 | }
28 |
29 | @Override
30 | public CandleSeries fetchSeries(String symbol) throws Exception {
31 |
32 | System.out.println( "Fetching series " + symbol );
33 |
34 | List candles = new ArrayList();
35 |
36 | String strFile = baseDirectory + File.separator + symbol + ".txt";
37 |
38 | // create BufferedReader to read csv file
39 | BufferedReader br = new BufferedReader(new FileReader(strFile));
40 | String strLine = "";
41 | StringTokenizer st = null;
42 |
43 | /*
44 | "Date","Time","Open","High","Low","Close","Volume"
45 | 12/30/2002,0931,23.85,23.89,23.81,23.88,511498
46 | */
47 |
48 | br.readLine();
49 | // read comma separated file line by line
50 | while ((strLine = br.readLine()) != null) {
51 |
52 | // break comma separated line using ","
53 | st = new StringTokenizer(strLine, ",");
54 |
55 | String dateString = st.nextToken();
56 | String timeString = st.nextToken();
57 |
58 | Date date = sdf.parse(dateString + timeString);
59 |
60 | double open = Double.parseDouble(st.nextToken());
61 | double high = Double.parseDouble(st.nextToken());
62 | double low = Double.parseDouble(st.nextToken());
63 | double close = Double.parseDouble(st.nextToken());
64 | double volume = Double.parseDouble(st.nextToken());
65 |
66 | candles.add(new IntradayCandle(symbol, date, open, high, low, close, volume));
67 |
68 | }
69 |
70 | //System.out.println( "finished." );
71 |
72 | return new IntradayCandleSeries(candles);
73 |
74 | }
75 |
76 | public static void main(String ... args) throws Exception{
77 | SeriesDatasource datasource = new IntradaySeriesDatasource("/home/jay/StockDatabase/Test");
78 | IntradayCandleSeries series = (IntradayCandleSeries) datasource.fetchSeries("MSFT");
79 |
80 |
81 | //System.out.print(series);
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/intraday/LLHIntradayTest.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.intraday;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import org.openquant.backtest.Position;
7 | import org.openquant.backtest.QuantityCalculator;
8 | import org.openquant.backtest.Series;
9 | import org.openquant.backtest.intraday.IntradayCandle.CANDLETYPE;
10 | import org.openquant.data.SeriesDatasource;
11 |
12 | public class LLHIntradayTest extends AbstractIntradayTest {
13 |
14 | private static final int EXIT_TIME_DAYS = 12;
15 |
16 | private static final double MAX_POSITION_AMOUNT = 100000;
17 |
18 | @Override
19 | public void run() {
20 |
21 |
22 | System.out.println("Beginning test...");
23 |
24 | int Len = 21;
25 | Series Lows = dayLowSeries();
26 |
27 | Series Filter = Lows.Min( Len );
28 |
29 | IntradayCandleSeries daySeries = getCandleSeries();
30 |
31 | for (int dayIndex = Len+2; dayIndex < daySeries.getDayCount(); dayIndex++) {
32 |
33 | //System.out.println( String.format( "bar : %s, date : %s, low : %s, Filter.getAt(bar) : %s", dayIndex, daySeries.getDayCandles(dayIndex).get(0).getDate(),
34 | // Lows.getAt(dayIndex), Filter.getAt(dayIndex) ));
35 |
36 |
37 | List dayCandles = daySeries.getDayCandles(dayIndex);
38 |
39 | //debugCandles(dayCandles);
40 |
41 | for (IntradayCandle candle : dayCandles) {
42 |
43 | // tick processing for the order manager
44 | if (candle.getType().equals(CANDLETYPE.CLOSE)) {
45 | processClose(candle);
46 | } else {
47 | processTick(candle);
48 | }
49 |
50 | // do not open more than one position per stock at a time
51 | if (!hasOpenPositions()) {
52 |
53 | final double LIMITPRICE = Filter.getAt(dayIndex);
54 |
55 | buyAtLimit(LIMITPRICE, new QuantityCalculator() {
56 |
57 | @Override
58 | public int execute(double totalEquity, double availableCash) {
59 | int quantity = (int) (totalEquity/20 / LIMITPRICE);
60 |
61 | if (quantity * LIMITPRICE > MAX_POSITION_AMOUNT){
62 | quantity = (int) (MAX_POSITION_AMOUNT / LIMITPRICE);
63 | }
64 | return quantity;
65 | }
66 | }, new OrderCallback() {
67 |
68 | @Override
69 | public void success(Position position) {
70 |
71 | sellAtLimit(position.getEntryPrice() * 1.025, position.getQuantity(), position, new OrderCallback() {
72 |
73 | @Override
74 | public void success(Position position) {
75 | //transactedToday = true;
76 |
77 | }
78 |
79 | @Override
80 | public void expired() {
81 | // TODO Auto-generated method stub
82 |
83 | }
84 | });
85 |
86 | timeBasedExitOnClose(EXIT_TIME_DAYS, position, new OrderCallback() {
87 |
88 | @Override
89 | public void success(Position position) {
90 | }
91 |
92 | @Override
93 | public void expired() {
94 | // TODO Auto-generated method stub
95 |
96 | }
97 | });
98 |
99 | }
100 |
101 | @Override
102 | public void expired() {
103 | // TODO Auto-generated method stub
104 |
105 | }
106 | });
107 |
108 |
109 | }
110 | }
111 | }
112 |
113 | }
114 |
115 | void debugCandles(List cList){
116 |
117 | for(IntradayCandle c : cList){
118 | System.out.println(c);
119 | }
120 |
121 | }
122 |
123 | public static void main(String ... args){
124 |
125 | SeriesDatasource datasource = new IntradaySeriesDatasource("/home/jay/StockDatabase/Test");
126 | //IntradayCandleSeries series = (IntradayCandleSeries) datasource.fetchSeries("MSFT");
127 |
128 | List symbols = new ArrayList();
129 |
130 | symbols.add("MSFT");
131 |
132 | // nasdaq10
133 | /*
134 | symbols.add("ATVI");
135 | symbols.add("ADBE");
136 | symbols.add("AKAM");
137 | symbols.add("ALTR");
138 | symbols.add("AMZN");
139 | symbols.add("AMGN");
140 | symbols.add("APOL");
141 | symbols.add("AAPL");
142 | symbols.add("AMAT");
143 | symbols.add("ADSK");
144 | */
145 |
146 | //IntradayBackTestExecutor executor = new IntradayBackTestExecutor("/home/jay/StockDatabase/Test", new LLHIntradayTest(), "Intraday-100");
147 |
148 | IntradayBackTestExecutor executor = new IntradayBackTestExecutor(datasource, symbols, new LLHIntradayTest(), "Intraday-MSFT");
149 | executor.run();
150 |
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/intraday/LowestLowIntra.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.intraday;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import org.apache.commons.logging.Log;
7 | import org.apache.commons.logging.LogFactory;
8 | import org.openquant.backtest.Position;
9 | import org.openquant.backtest.QuantityCalculator;
10 | import org.openquant.backtest.Series;
11 | import org.openquant.backtest.intraday.IntradayCandle.CANDLETYPE;
12 | import org.openquant.data.SeriesDatasource;
13 |
14 | public class LowestLowIntra extends AbstractIntradayTest {
15 |
16 | private Log log = LogFactory.getLog(LowestLowIntra.class);
17 |
18 | private static boolean transactedToday = false;
19 |
20 | private static final double MAX_POSITION_AMOUNT = 100000;
21 |
22 | @Override
23 | public void run() {
24 |
25 | //System.out.println("Beginning test...");
26 |
27 | int Len = 9;
28 | Series Filter = dayLowSeries().Min(Len);
29 | //Series Highs = dayHighSeries();
30 | //Series Closes = dayCloseSeries();
31 |
32 | IntradayCandleSeries daySeries = getCandleSeries();
33 |
34 | for (int dayIndex = Len; dayIndex < daySeries.getDayCount(); dayIndex++) {
35 |
36 | //System.out.println( String.format("%s Close : %.2f Filter[bar-1] : %.2f", daySeries.getDayCandles(dayIndex).get(0).getDate(), Closes.getAt(dayIndex), Filter.getAt(dayIndex - 1) * 1.21) );
37 |
38 | List dayCandles = daySeries.getDayCandles(dayIndex);
39 |
40 | // debugCandles(dayCandles);
41 |
42 | for (IntradayCandle candle : dayCandles) {
43 |
44 | // tick processing for the order manager
45 | if (candle.getType().equals(CANDLETYPE.CLOSE)) {
46 | processClose(candle);
47 | } else {
48 | processTick(candle);
49 | }
50 |
51 | // do not open more than one position per stock at a time
52 | //if ( getOpenPositionCount() < 20 ){
53 | if (!hasOpenPositions()) {
54 |
55 | //if (Filter.getAt(dayIndex - 1) > Filter.getAt(dayIndex - 2)) {
56 | //if (Highs.getAt(dayIndex - 1) > Filter.getAt(dayIndex - 2)) {
57 | //if (candle.getHighPrice() > Filter.getAt(dayIndex - 1)) {
58 |
59 | final double LIMITPRICE = Filter.getAt(dayIndex - 1) * 1.21;
60 |
61 | //final double LIMITPRICE = Filter.getAt(dayIndex - 1);
62 |
63 | buyAtLimit(LIMITPRICE, new QuantityCalculator() {
64 |
65 | @Override
66 | public int execute(double totalEquity, double availableCash) {
67 | int quantity = (int) (totalEquity/10 / LIMITPRICE);
68 |
69 | if (quantity * LIMITPRICE > MAX_POSITION_AMOUNT){
70 | quantity = (int) (MAX_POSITION_AMOUNT / LIMITPRICE);
71 | }
72 |
73 | return quantity;
74 | }
75 | },
76 | new OrderCallback() {
77 |
78 | @Override
79 | public void success(Position position) {
80 |
81 | //log.info("Entered Position : " + position);
82 |
83 | /*
84 | timeBasedSellAtStop(1, position.getEntryPrice() * 0.80, position.getQuantity(), position,
85 | new OrderCallback() {
86 |
87 | @Override
88 | public void success(Position position) {
89 | // transactedToday = true;
90 |
91 | }
92 |
93 | @Override
94 | public void expired() {
95 | // TODO Auto-generated method
96 | // stub
97 |
98 | }
99 | });
100 |
101 | */
102 |
103 | timeBasedExitOnClose(12, position, new OrderCallback() {
104 |
105 | @Override
106 | public void success(Position position) {
107 | //log.info("Exited Position[ time based ] : " + position);
108 |
109 | }
110 |
111 | @Override
112 | public void expired() {
113 | // TODO Auto-generated method stub
114 |
115 | }
116 | });
117 |
118 |
119 | }
120 |
121 | @Override
122 | public void expired() {
123 | // TODO Auto-generated method stub
124 |
125 | }
126 | });
127 |
128 | //}
129 |
130 | }
131 | }
132 | }
133 |
134 | }
135 |
136 | void debugCandles(List cList) {
137 |
138 | for (IntradayCandle c : cList) {
139 | System.out.println(c);
140 | }
141 |
142 | }
143 |
144 | public static void main(String... args) {
145 |
146 | SeriesDatasource data = new IntradaySeriesDatasource("/home/jay/StockDatabase/Test");
147 | // IntradayCandleSeries series = (IntradayCandleSeries)
148 | // datasource.fetchSeries("MSFT");
149 |
150 | List symbols = new ArrayList();
151 |
152 | //symbols.add("MSFT");
153 |
154 | // nasdaq10
155 |
156 | symbols.add("ATVI"); symbols.add("ADBE"); symbols.add("AKAM");
157 | symbols.add("ALTR"); symbols.add("AMZN"); symbols.add("AMGN");
158 | symbols.add("APOL"); symbols.add("AAPL"); symbols.add("AMAT");
159 | symbols.add("ADSK");
160 |
161 |
162 |
163 | //IntradayBackTestExecutor executor = new IntradayBackTestExecutor("/home/jay/StockDatabase/Test", new LowestLowIntra(),
164 | // "LL4-NAS-100-PosSize");
165 |
166 | IntradayBackTestExecutor executor = new IntradayBackTestExecutor(data, symbols, new LowestLowIntra(), "LL4-Nasdaq10-PosSize");
167 |
168 | executor.run();
169 |
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/intraday/MyTest.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.intraday;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import org.openquant.backtest.Position;
7 | import org.openquant.backtest.Series;
8 | import org.openquant.backtest.intraday.IntradayCandle.CANDLETYPE;
9 | import org.openquant.data.SeriesDatasource;
10 |
11 | public class MyTest extends AbstractIntradayTest {
12 |
13 | static boolean transactedToday = false;
14 |
15 | @Override
16 | public void run() {
17 |
18 | System.out.println("Beginning test...");
19 |
20 | int Len = 18;
21 | Series Close = dayCloseSeries();
22 | Series EMAClose = Close.EMA(Len);
23 | Series DayHighs = dayHighSeries();
24 |
25 | IntradayCandleSeries daySeries = getCandleSeries();
26 |
27 | for (int dayIndex = Len+2; dayIndex < daySeries.getDayCount(); dayIndex++) {
28 |
29 | List dayCandles = daySeries.getDayCandles(dayIndex);
30 |
31 | //debugCandles(dayCandles);
32 |
33 | transactedToday = false;
34 | for (IntradayCandle candle : dayCandles) {
35 |
36 | // if we buy a stock, hold overnight
37 | if (transactedToday){
38 | break;
39 | }
40 |
41 | // tick processing for the order manager
42 | if (candle.getType().equals(CANDLETYPE.CLOSE)) {
43 | processClose(candle);
44 | } else {
45 | processTick(candle);
46 | }
47 |
48 | // do not open more than one position per stock at a time
49 | if (!hasOpenPositions()) {
50 |
51 | //if (EMAClose.getAt(dayIndex - 1) > EMAClose.getAt(dayIndex - 2)) {
52 |
53 | //if(candle.getHighPrice() > EMAClose.getAt(dayIndex - 1)){
54 | if( DayHighs.getAt(dayIndex - 1) > EMAClose.getAt(dayIndex - 2)){
55 |
56 | double LIMITPRICE = getClose(dayIndex - 1)
57 | * (1 - 0.03 * EMAClose.getAt(dayIndex - 1) / 50);
58 |
59 | buyAtLimit(LIMITPRICE, 1000, new OrderCallback() {
60 |
61 | @Override
62 | public void success(Position position) {
63 |
64 | sellAtStop(position.getEntryPrice() * 0.99, position.getQuantity(), position, new OrderCallback() {
65 |
66 | @Override
67 | public void success(Position position) {
68 | //transactedToday = true;
69 |
70 | }
71 |
72 | @Override
73 | public void expired() {
74 | // TODO Auto-generated method stub
75 |
76 | }
77 | });
78 |
79 | sellAtLimit(position.getEntryPrice() * 1.025, position.getQuantity(), position, new OrderCallback() {
80 |
81 | @Override
82 | public void success(Position position) {
83 | //transactedToday = true;
84 |
85 | }
86 |
87 | @Override
88 | public void expired() {
89 | // TODO Auto-generated method stub
90 |
91 | }
92 | });
93 |
94 | transactedToday = true;
95 |
96 | }
97 |
98 | @Override
99 | public void expired() {
100 | // TODO Auto-generated method stub
101 |
102 | }
103 | });
104 |
105 | }
106 | }
107 | }
108 | }
109 |
110 | }
111 |
112 | void debugCandles(List cList){
113 |
114 | for(IntradayCandle c : cList){
115 | System.out.println(c);
116 | }
117 |
118 | }
119 |
120 | public static void main(String ... args){
121 |
122 | SeriesDatasource datasource = new IntradaySeriesDatasource("/home/jay/StockDatabase/Test");
123 | //IntradayCandleSeries series = (IntradayCandleSeries) datasource.fetchSeries("MSFT");
124 |
125 | List symbols = new ArrayList();
126 |
127 | //symbols.add("AAPL");
128 |
129 | // nasdaq10
130 |
131 | symbols.add("ATVI");
132 | symbols.add("ADBE");
133 | symbols.add("AKAM");
134 | symbols.add("ALTR");
135 | symbols.add("AMZN");
136 | symbols.add("AMGN");
137 | symbols.add("APOL");
138 | symbols.add("AAPL");
139 | symbols.add("AMAT");
140 | symbols.add("ADSK");
141 |
142 |
143 | /*
144 | symbols.add("AAPL");
145 | symbols.add("APOL");
146 | symbols.add("CERN");
147 | symbols.add("DLTR");
148 | symbols.add("FLEX");
149 | symbols.add("INTU");
150 | symbols.add("MICC");
151 | symbols.add("NWSA");
152 | symbols.add("SBUX");
153 | symbols.add("VMED");
154 | symbols.add("ADBE");
155 | symbols.add("ATVI");
156 | symbols.add("CHKP");
157 | symbols.add("DTV");
158 | symbols.add("FLIR");
159 | symbols.add("ISRG");
160 | symbols.add("MRVL");
161 | symbols.add("ORCL");
162 | symbols.add("SHLD");
163 | symbols.add("VOD");
164 | symbols.add("ADP");
165 | symbols.add("BBBY");
166 | symbols.add("CHRW");
167 | symbols.add("EBAY");
168 | symbols.add("FSLR");
169 | symbols.add("JOYG");
170 | symbols.add("MSFT");
171 | symbols.add("ORLY");
172 | symbols.add("SIAL");
173 | symbols.add("VRSN");
174 | symbols.add("ADSK");
175 | symbols.add("BIDU");
176 | symbols.add("CMCSA");
177 | symbols.add("ERTS");
178 | symbols.add("GILD");
179 | symbols.add("KLAC");
180 | symbols.add("MU");
181 | symbols.add("PAYX");
182 | symbols.add("SNDK");
183 | symbols.add("VRTX");
184 | symbols.add("AKAM");
185 | symbols.add("BIIB");
186 | symbols.add("COST");
187 | symbols.add("ESRX");
188 | symbols.add("GOOG");
189 | symbols.add("LIFE");
190 | symbols.add("MXIM");
191 | symbols.add("PCAR");
192 | symbols.add("SPLS");
193 | symbols.add("WCRX");
194 | symbols.add("ALTR");
195 | symbols.add("BMC");
196 | symbols.add("CSCO");
197 | symbols.add("EXPD");
198 | symbols.add("GRMN");
199 | symbols.add("LINTA");
200 | symbols.add("MYL");
201 | symbols.add("PCLN");
202 | symbols.add("SRCL");
203 | symbols.add("WFMI");
204 | symbols.add("ALXN");
205 | symbols.add("BRCM");
206 | symbols.add("CTRP");
207 | symbols.add("EXPE");
208 | symbols.add("HSIC");
209 | symbols.add("LLTC");
210 | symbols.add("NFLX");
211 | symbols.add("QCOM");
212 | symbols.add("STX");
213 | symbols.add("WYNN");
214 | symbols.add("AMAT");
215 | symbols.add("CA");
216 | symbols.add("CTSH");
217 | symbols.add("FAST");
218 | symbols.add("ILMN");
219 | symbols.add("LRCX");
220 | symbols.add("NIHD");
221 | symbols.add("QGEN");
222 | symbols.add("SYMC");
223 | symbols.add("XLNX");
224 | symbols.add("AMGN");
225 | symbols.add("CELG");
226 | symbols.add("CTXS");
227 | symbols.add("FFIV");
228 | symbols.add("INFY");
229 | symbols.add("MAT");
230 | symbols.add("NTAP");
231 | symbols.add("RIMM");
232 | symbols.add("TEVA");
233 | symbols.add("XRAY");
234 | symbols.add("AMZN");
235 | symbols.add("CEPH");
236 | symbols.add("DELL");
237 | symbols.add("FISV");
238 | symbols.add("INTC");
239 | symbols.add("MCHP");
240 | symbols.add("NVDA");
241 | symbols.add("ROST");
242 | symbols.add("URBN");
243 | symbols.add("YHOO");
244 | */
245 |
246 | IntradayBackTestExecutor executor = new IntradayBackTestExecutor(datasource, symbols, new MyTest(), "Intraday");
247 | executor.run();
248 |
249 | }
250 | }
251 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/intraday/OrderCallback.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.intraday;
2 |
3 | import org.openquant.backtest.Position;
4 |
5 | public interface OrderCallback{
6 |
7 | public void success(Position position);
8 |
9 | public void expired();
10 |
11 | }
--------------------------------------------------------------------------------
/src/org/openquant/backtest/intraday/OrderTimeFrame.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.intraday;
2 |
3 | public enum OrderTimeFrame {
4 |
5 | GOOD_TILL_CANCELLED,
6 | END_OF_DAY
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/intraday/SellIntent.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.intraday;
2 |
3 | import org.openquant.backtest.Position;
4 |
5 |
6 | public class SellIntent {
7 |
8 | private double price;
9 |
10 | private int quantity;
11 |
12 | private Position position;
13 |
14 | private OrderTimeFrame timeframe = OrderTimeFrame.GOOD_TILL_CANCELLED;
15 |
16 | private OrderCallback callback;
17 |
18 | public SellIntent(double price, int quantity, Position position, OrderTimeFrame timeframe, OrderCallback callback) {
19 | super();
20 | this.price = price;
21 | this.quantity = quantity;
22 | this.position = position;
23 | this.timeframe = timeframe;
24 | this.callback = callback;
25 | }
26 |
27 | public SellIntent(double price, int quantity, Position position, OrderCallback callback) {
28 | super();
29 | this.price = price;
30 | this.quantity = quantity;
31 | this.position = position;
32 | this.callback = callback;
33 | }
34 |
35 | public double getPrice() {
36 | return price;
37 | }
38 |
39 | public void setPrice(double price) {
40 | this.price = price;
41 | }
42 |
43 | public int getQuantity() {
44 | return quantity;
45 | }
46 |
47 | public void setQuantity(int quantity) {
48 | this.quantity = quantity;
49 | }
50 |
51 | public OrderTimeFrame getTimeframe() {
52 | return timeframe;
53 | }
54 |
55 | public void setTimeframe(OrderTimeFrame timeframe) {
56 | this.timeframe = timeframe;
57 | }
58 |
59 | public Position getPosition() {
60 | return position;
61 | }
62 |
63 | public OrderCallback getCallback() {
64 | return callback;
65 | }
66 | }
--------------------------------------------------------------------------------
/src/org/openquant/backtest/intraday/TimeBasedLimitSellIntent.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.intraday;
2 |
3 | import org.openquant.backtest.Position;
4 |
5 | public class TimeBasedLimitSellIntent extends SellIntent {
6 |
7 | private int decrementor;
8 |
9 | private int dayBarCount;
10 |
11 | public TimeBasedLimitSellIntent(int dayBarCount, double price, int quantity, Position position, OrderTimeFrame timeframe,
12 | OrderCallback callback) {
13 | super(price, quantity, position, timeframe, callback);
14 | this.dayBarCount = dayBarCount;
15 | this.decrementor = dayBarCount;
16 | }
17 |
18 | public TimeBasedLimitSellIntent(int dayBarCount, double price, int quantity, Position position, OrderCallback callback) {
19 | super(price, quantity, position, callback);
20 | this.dayBarCount = dayBarCount;
21 | this.decrementor = dayBarCount;
22 | }
23 |
24 | public boolean isActive(){
25 | return decrementor == 0;
26 | }
27 |
28 | public int getDayBarCount() {
29 | return dayBarCount;
30 | }
31 |
32 | public int decrement(){
33 | return decrementor--;
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/intraday/TimeBasedSellIntent.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.intraday;
2 |
3 | import org.openquant.backtest.Position;
4 |
5 |
6 | public class TimeBasedSellIntent {
7 |
8 | private int decrementor;
9 |
10 | private int dayBarCount;
11 |
12 | private Position position;
13 |
14 | private OrderCallback callback;
15 |
16 | public TimeBasedSellIntent(int dayBarCount, Position position, OrderCallback callback) {
17 | super();
18 | this.dayBarCount = dayBarCount;
19 | this.decrementor = dayBarCount;
20 | this.position = position;
21 | this.callback = callback;
22 | }
23 |
24 | public int getDayBarCount() {
25 | return dayBarCount;
26 | }
27 |
28 | public Position getPosition() {
29 | return position;
30 | }
31 |
32 | public OrderCallback getCallback() {
33 | return callback;
34 | }
35 |
36 | public int decrement(){
37 | return decrementor--;
38 | }
39 |
40 |
41 | }
--------------------------------------------------------------------------------
/src/org/openquant/backtest/intraday/TimeBasedStopSellIntent.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.intraday;
2 |
3 | import org.openquant.backtest.Position;
4 |
5 | public class TimeBasedStopSellIntent extends SellIntent {
6 |
7 | private int decrementor;
8 |
9 | private int dayBarCount;
10 |
11 | public TimeBasedStopSellIntent(int dayBarCount, double price, int quantity, Position position, OrderTimeFrame timeframe,
12 | OrderCallback callback) {
13 | super(price, quantity, position, timeframe, callback);
14 | this.dayBarCount = dayBarCount;
15 | this.decrementor = dayBarCount;
16 | }
17 |
18 | public TimeBasedStopSellIntent(int dayBarCount, double price, int quantity, Position position, OrderCallback callback) {
19 | super(price, quantity, position, callback);
20 | this.dayBarCount = dayBarCount;
21 | this.decrementor = dayBarCount;
22 | }
23 |
24 | public boolean isActive(){
25 | return decrementor == 0;
26 | }
27 |
28 | public int getDayBarCount() {
29 | return dayBarCount;
30 | }
31 |
32 | public int decrement(){
33 | return decrementor--;
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/report/AbstractReport.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.report;
2 |
3 | /*
4 | Copyright (c) 2011, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import java.util.ArrayList;
23 | import java.util.Collection;
24 | import java.util.Collections;
25 | import java.util.Comparator;
26 | import java.util.List;
27 |
28 | import org.apache.commons.logging.Log;
29 | import org.apache.commons.logging.LogFactory;
30 | import org.openquant.backtest.Order;
31 | import org.openquant.backtest.Position;
32 |
33 | public abstract class AbstractReport {
34 |
35 | private Log log = LogFactory.getLog(AbstractReport.class);
36 |
37 | private double totalCapitalAndEquity;
38 |
39 | private double slippage;
40 |
41 | private double commission;
42 |
43 | private double availableCash;
44 |
45 | private Collection closedPositions;
46 |
47 | private Collection openPositions;
48 |
49 | private Order extractEntryOrder(Position position) {
50 |
51 | return new Order(true, position.getEntryDate(),
52 | position.getEntryPrice() * position.getQuantity(), position);
53 | }
54 |
55 | private Order extractExitOrder(Position position) {
56 |
57 | return new Order(false, position.getExitDate(), position.getExitPrice()
58 | * position.getQuantity(), position);
59 | }
60 |
61 | public double getTotalCapitalAndEquity() {
62 | return totalCapitalAndEquity;
63 | }
64 |
65 | private int compareEntries(Order one, Order two){
66 |
67 | if ((one.isEntry() && two.isEntry()) || (!one.isEntry() && !two.isEntry()) ){
68 | return 0;
69 | }
70 | if (one.isEntry() && !two.isEntry()){
71 | return -1;
72 | }
73 | if ( !one.isEntry() && two.isEntry()){
74 | return 1;
75 | }
76 | return 0;
77 | }
78 |
79 | public void process() {
80 | List allOrders = new ArrayList();
81 |
82 | for (Position position : closedPositions) {
83 |
84 | Order exit = extractExitOrder(position);
85 | allOrders.add(exit);
86 | position.setExit(exit);
87 |
88 | Order entry = extractEntryOrder(position);
89 | allOrders.add(entry);
90 | position.setEntry(entry);
91 |
92 | }
93 |
94 | Collections.sort(allOrders, new Comparator() {
95 |
96 | /**
97 | * Compare and order by date and order entry.
98 | *
99 | * @param one
100 | * @param two
101 | * @return
102 | */
103 | @Override
104 | public int compare(Order one, Order two) {
105 | int i = one.getDate().compareTo(two.getDate());
106 | if (i != 0){
107 | return i;
108 | }
109 |
110 | i = compareEntries(one, two);
111 | if (i != 0){
112 | return i;
113 | }
114 |
115 | return 0;
116 | }
117 |
118 | });
119 |
120 | for (Order order : allOrders) {
121 |
122 | if (order.isEntry()) {
123 |
124 | log.debug( String.format("Order[%s]", order) );
125 |
126 | Position position = order.getParentPosition();
127 |
128 | if(position.isQuantityCalculated()){
129 | // calculate and set the order value based on the quantity calculator
130 | int quantity = position.getQuantityCalculator().execute(totalCapitalAndEquity, availableCash);
131 | position.setQuantity(quantity);
132 |
133 | double val = position.getEntryPrice() * position.getQuantity();
134 | order.setValue(val);
135 | //position.getEntry().setValue(val);
136 | //position.getExit().setValue(val);
137 | }
138 |
139 |
140 | if (order.getValue() > availableCash) {
141 | log.debug("Not enough available cash to purchase stock - "
142 | + position);
143 | // remove both order sides as position cannot be taken
144 | position.setSuccessfullyFilled(false);
145 |
146 | continue;
147 |
148 | } else {
149 |
150 | double entryCost = order.getValue()
151 | + (order.getValue() * this.slippage)
152 | + this.commission;
153 |
154 | availableCash -= entryCost;
155 |
156 | log.debug(order + " Available Cash : " + availableCash);
157 |
158 | onEnterOrder(order, totalCapitalAndEquity, availableCash);
159 | }
160 |
161 | } else {
162 | Position position = order.getParentPosition();
163 |
164 | if (position.isSuccessfullyFilled()) {
165 |
166 | if(position.isQuantityCalculated()){
167 | double val = position.getExitPrice() * position.getQuantity();
168 | order.setValue(val);
169 | }
170 |
171 | double profit = (position.getExitPrice() - position
172 | .getEntryPrice()) * position.getQuantity();
173 | profit -= (profit * this.slippage);
174 | profit -= this.commission;
175 |
176 | totalCapitalAndEquity += profit;
177 |
178 | double exitCost = order.getValue()
179 | - (order.getValue() * this.slippage)
180 | - this.commission;
181 |
182 | //availableCash = totalCapitalAndEquity;
183 | availableCash += exitCost;
184 |
185 | log.debug(order + " Available Cash : " + availableCash);
186 |
187 | onExitOrder(order, totalCapitalAndEquity, availableCash);
188 | }
189 | }
190 |
191 | }
192 |
193 | /*
194 | * for (Position position : openPositions) {
195 | * processOpenPosition(position); }
196 | */
197 |
198 | }
199 |
200 | public AbstractReport(double capital, double commission, double slippage,
201 | Collection closedPositions,
202 | Collection openPositions) {
203 | super();
204 | this.totalCapitalAndEquity = capital;
205 | this.availableCash = capital;
206 | this.commission = commission;
207 | this.slippage = slippage;
208 | this.closedPositions = closedPositions;
209 | this.openPositions = openPositions;
210 |
211 | }
212 |
213 | public abstract void onEnterOrder(Order order, double capital,
214 | double availableCash);
215 |
216 | public abstract void onExitOrder(Order order, double capital,
217 | double availableCash);
218 |
219 | public abstract void render();
220 |
221 | }
222 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/report/CSVReport.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.report;
2 |
3 | import java.io.FileWriter;
4 | import java.io.IOException;
5 | import java.io.PrintWriter;
6 | import java.util.Collection;
7 |
8 | import org.apache.commons.logging.Log;
9 | import org.apache.commons.logging.LogFactory;
10 | import org.openquant.backtest.Order;
11 | import org.openquant.backtest.Position;
12 |
13 | public class CSVReport extends AbstractReport {
14 |
15 | private Log log = LogFactory.getLog(CSVReport.class);
16 |
17 | private PrintWriter outputStream;
18 |
19 | public CSVReport(double capital, double commission, double slippage, Collection closedPositions,
20 | Collection openPositions, String filename) {
21 | super(capital, commission, slippage, closedPositions, openPositions);
22 |
23 | try {
24 | outputStream = new PrintWriter(new FileWriter(filename));
25 |
26 | outputStream.println( String.format("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s",
27 | "Symbol","Entry Date", "Entry Price", "Exit Date", "Exit Price", "Shares", "Available Cash", "Equity", "% Gain", "Comments" ) );
28 |
29 | } catch (IOException e) {
30 | // TODO Auto-generated catch block
31 | e.printStackTrace();
32 | }
33 |
34 | process();
35 | }
36 |
37 | @Override
38 | public void onEnterOrder(Order order, double capital, double availableCash) {
39 | /*
40 | Position position = order.getParentPosition();
41 | outputStream.println( String.format("%1$s,%2$tb %2$td %2$ty %2$tT,%3$6.2f,%4$tb %4$td %4$ty %4$tT,%5$6.2f,%6$s,%7$6.2f,%8$6.2f,%9$s", position.getSymbol(), position.getEntryDate(),
42 | position.getEntryPrice(), position.getExitDate(), position.getExitPrice(), position.getQuantity(), availableCash, capital, position.getComments() ) );
43 | */
44 | //log.info( String.format("Entering %s, capital: %.2f, availableCash: %.2f", order, capital, availableCash) );
45 |
46 | }
47 |
48 | @Override
49 | public void onExitOrder(Order order, double capital, double availableCash) {
50 | Position position = order.getParentPosition();
51 |
52 | double diff = (position.getExitPrice() - position.getEntryPrice())/ position.getEntryPrice() * 100;
53 |
54 | outputStream.println( String.format("%1$s,%2$tb %2$td %2$ty %2$tT,%3$6.2f,%4$tb %4$td %4$ty %4$tT,%5$6.2f,%6$s,%7$6.2f,%8$6.2f,%9$6.4f,%10$s", position.getSymbol(), position.getEntryDate(),
55 | position.getEntryPrice(), position.getExitDate(), position.getExitPrice(), position.getQuantity(), availableCash, capital, diff, position.getComments() ) );
56 |
57 | //log.info( String.format("Exiting %s, capital: %.2f, availableCash: %.2f", order, capital, availableCash) );
58 | }
59 |
60 | @Override
61 | public void render() {
62 | if (outputStream != null) {
63 | outputStream.close();
64 | }
65 |
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/report/CSVReport2.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.report;
2 |
3 | /*
4 | Copyright (c) 2011, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import java.io.FileWriter;
23 | import java.io.IOException;
24 | import java.io.PrintWriter;
25 |
26 | import org.apache.commons.logging.Log;
27 | import org.apache.commons.logging.LogFactory;
28 | import org.openquant.backtest.Position;
29 | import org.openquant.backtest.PositionProcessor;
30 |
31 | public class CSVReport2 implements PositionProcessor {
32 |
33 | private Log log = LogFactory.getLog(CSVReport2.class);
34 |
35 | private PrintWriter outputStream;
36 |
37 | private double capital;
38 |
39 | private double slippage;
40 |
41 | private double commission;
42 |
43 | public CSVReport2(String filename, double capital, double commission, double slippage) {
44 | super();
45 | this.capital = capital;
46 | this.slippage = slippage;
47 | this.commission = commission;
48 |
49 | try {
50 | outputStream = new PrintWriter(new FileWriter(filename));
51 |
52 | outputStream.println( String.format("%s,%s,%s,%s,%s,%s,%s,%s,%s",
53 | "Symbol","Entry Date", "Entry Price", "Exit Date", "Exit Price", "Shares", "Profit", "Equity", "Comments" ) );
54 |
55 | } catch (IOException e) {
56 | // TODO Auto-generated catch block
57 | e.printStackTrace();
58 | }
59 | }
60 |
61 |
62 | @Override
63 | public void processClosePosition(Position position) {
64 |
65 | double profit = (position.getExitPrice() - position.getEntryPrice()) * position.getQuantity();
66 | profit = profit - (profit*slippage) - commission;
67 |
68 | capital += profit;
69 |
70 | outputStream.println( String.format("%1$s,%2$tb %2$td %2$ty,%3$6.2f,%4$tb %4$td %4$ty,%5$6.2f,%6$s,%7$6.2f,%8$6.2f,%9$s", position.getSymbol(), position.getEntryDate(),
71 | position.getEntryPrice(), position.getExitDate(), position.getExitPrice(), position.getQuantity(), profit, capital, position.getComments() ) );
72 |
73 | }
74 |
75 | @Override
76 | public void processOpenPosition(Position position) {
77 | // calculate entry value and add to capital
78 | capital += position.getEntryPrice() * position.getQuantity();
79 |
80 | outputStream.println( String.format("%1$s,%2$tb %2$td %2$ty,%3$6.2f,%4$tb %4$td %4$ty,%5$6.2f,%6$s,%7$6.2f,%8$6.2f,%9$s", position.getSymbol(), position.getEntryDate(),
81 | position.getEntryPrice(), position.getExitDate(), position.getExitPrice(), position.getQuantity(), 0.0, capital, position.getComments() ) );
82 |
83 | }
84 |
85 | @Override
86 | public void finish() {
87 | if (outputStream != null) {
88 | outputStream.close();
89 | }
90 |
91 | log.debug(String.format("Ending capital is %12.2f", capital));
92 |
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/report/JFreeChartReport.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.report;
2 |
3 | /*
4 | Copyright (c) 2011, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import java.io.File;
23 | import java.io.IOException;
24 | import java.text.DateFormat;
25 | import java.text.SimpleDateFormat;
26 | import java.util.ArrayList;
27 | import java.util.Collection;
28 | import java.util.Random;
29 |
30 | import org.apache.commons.logging.Log;
31 | import org.apache.commons.logging.LogFactory;
32 | import org.jfree.chart.ChartFactory;
33 | import org.jfree.chart.ChartUtilities;
34 | import org.jfree.chart.JFreeChart;
35 | import org.jfree.data.time.Day;
36 | import org.jfree.data.time.TimeSeries;
37 | import org.jfree.data.time.TimeSeriesCollection;
38 | import org.openquant.backtest.Order;
39 | import org.openquant.backtest.Position;
40 |
41 | public class JFreeChartReport extends AbstractReport {
42 |
43 | private Log log = LogFactory.getLog(JFreeChartReport.class);
44 |
45 | private String filename;
46 |
47 | private TimeSeries equitySeries;
48 |
49 | private TimeSeries cashSeries;
50 |
51 | public JFreeChartReport(String filename, double capital, double commission,
52 | double slippage, Collection closedPositions,
53 | Collection openPositions) {
54 |
55 | super(capital, commission, slippage, closedPositions, openPositions);
56 |
57 | Random r = new Random();
58 |
59 | equitySeries = new TimeSeries("Equity" + r.nextInt());
60 |
61 | cashSeries = new TimeSeries("Cash" + r.nextInt());
62 |
63 | this.filename = filename;
64 |
65 | process();
66 | }
67 |
68 | @Override
69 | public void onEnterOrder(Order order, double capital, double availableCash) {
70 | cashSeries.addOrUpdate(new Day(order.getDate()), availableCash);
71 |
72 | }
73 |
74 | @Override
75 | public void onExitOrder(Order order, double capital, double availableCash) {
76 | equitySeries.addOrUpdate(new Day(order.getDate()), capital);
77 |
78 | cashSeries.addOrUpdate(new Day(order.getDate()), availableCash);
79 |
80 | }
81 |
82 | public void render() {
83 | try {
84 | // Add the series to your data set
85 | TimeSeriesCollection dataset = new TimeSeriesCollection();
86 | dataset.addSeries(equitySeries);
87 | dataset.addSeries(cashSeries);
88 |
89 | // Generate the graph
90 | JFreeChart chart = ChartFactory.createTimeSeriesChart(
91 | "Equity Curve", "Day", "Value", dataset, false, false,
92 | false);
93 |
94 | ChartUtilities.saveChartAsJPEG(new File(filename), chart, 1000, 600);
95 |
96 | } catch (IOException e) {
97 | log.error(e, e);
98 | }
99 |
100 | }
101 |
102 | public TimeSeries getEquitySeries() {
103 | return equitySeries;
104 | }
105 |
106 | public TimeSeries getCashSeries() {
107 | return cashSeries;
108 | }
109 |
110 | public static void main(String ... args) throws Exception{
111 |
112 | DateFormat format = new SimpleDateFormat("dd/MM/yy");
113 |
114 | Collection closedPositions = new ArrayList();
115 |
116 | closedPositions.add( new Position("TEST", format.parse("20/04/11"), format.parse("21/04/11"), 100, 110, 1000 ) );
117 | closedPositions.add( new Position("TEST", format.parse("21/04/11"), format.parse("22/04/11"), 100, 110, 1000 ) );
118 |
119 | closedPositions.add( new Position("TEST", format.parse("22/04/11"), format.parse("23/04/11"), 100, 110, 1000 ) );
120 | closedPositions.add( new Position("TEST", format.parse("22/04/11"), format.parse("23/04/11"), 100, 110, 1000 ) );
121 | closedPositions.add( new Position("TEST", format.parse("22/04/11"), format.parse("23/04/11"), 100, 110, 1000 ) );
122 |
123 | closedPositions.add( new Position("TEST", format.parse("23/04/11"), format.parse("24/04/11"), 100, 110, 1000 ) );
124 | closedPositions.add( new Position("TEST", format.parse("24/04/11"), format.parse("25/04/11"), 100, 110, 1000 ) );
125 | closedPositions.add( new Position("TEST", format.parse("25/04/11"), format.parse("26/04/11"), 100, 110, 1000 ) );
126 | closedPositions.add( new Position("TEST", format.parse("26/04/11"), format.parse("27/04/11"), 100, 110, 1000 ) );
127 |
128 | Collection openPositions = new ArrayList();
129 |
130 | new JFreeChartReport("mytest.jpg", 100000, 0, 0.0, closedPositions, openPositions).render();
131 | }
132 |
133 | }
134 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/report/JFreeChartReportOriginal.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.report;
2 |
3 | /*
4 | Copyright (c) 2011, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import java.io.File;
23 | import java.io.IOException;
24 | import java.util.Date;
25 |
26 | import org.apache.commons.logging.Log;
27 | import org.apache.commons.logging.LogFactory;
28 | import org.jfree.chart.ChartFactory;
29 | import org.jfree.chart.ChartUtilities;
30 | import org.jfree.chart.JFreeChart;
31 | import org.jfree.data.time.Day;
32 | import org.jfree.data.time.TimeSeries;
33 | import org.jfree.data.time.TimeSeriesCollection;
34 | import org.openquant.backtest.Position;
35 | import org.openquant.backtest.PositionProcessor;
36 |
37 | public class JFreeChartReportOriginal implements PositionProcessor {
38 |
39 | private Log log = LogFactory.getLog(JFreeChartReportOriginal.class);
40 |
41 | private double capital;
42 |
43 | private double slippage;
44 |
45 | private double commission;
46 |
47 | private double availableCash;
48 |
49 | private String filename;
50 |
51 | private TimeSeries series = new TimeSeries("Equity Curve");
52 |
53 | public JFreeChartReportOriginal(String filename, double capital, double commission, double slippage) {
54 | super();
55 | this.capital = capital;
56 | this.slippage = slippage;
57 | this.commission = commission;
58 | this.filename = filename;
59 |
60 | }
61 |
62 | @Override
63 | public void processClosePosition(Position position) {
64 |
65 |
66 | double profit = (position.getExitPrice() - position.getEntryPrice()) * position.getQuantity();
67 | profit = profit - (profit*slippage) - commission;
68 |
69 | capital += profit;
70 |
71 | series.addOrUpdate(new Day(position.getExitDate()), capital);
72 |
73 | }
74 |
75 | @Override
76 | public void processOpenPosition(Position position) {
77 |
78 | // calculate entry value and add to capital
79 | capital += position.getEntryPrice() * position.getQuantity();
80 |
81 | //double profit = (position.getExitPrice() - position.getEntryPrice()) * position.getQuantity();
82 | //profit = profit - (profit*slippage) - commission;
83 |
84 |
85 | series.addOrUpdate(new Day( new Date() ), capital);
86 |
87 |
88 | }
89 |
90 | @Override
91 | public void finish() {
92 | try {
93 | // Add the series to your data set
94 | TimeSeriesCollection dataset = new TimeSeriesCollection(series);
95 |
96 | // Generate the graph
97 | JFreeChart chart = ChartFactory.createTimeSeriesChart(
98 | "Equity Curve", "Day", "Value", dataset, false, false,
99 | false);
100 |
101 | ChartUtilities.saveChartAsJPEG(new File(filename), chart, 1000, 600);
102 |
103 | log.debug(String.format("Ending capital is %12.2f", capital));
104 |
105 | } catch (IOException e) {
106 | log.error(e, e);
107 | }
108 |
109 | }
110 |
111 | }
112 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/report/MultiStatJFreeChartReport.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.report;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.util.ArrayList;
6 | import java.util.Collection;
7 | import java.util.Collections;
8 | import java.util.Comparator;
9 | import java.util.List;
10 |
11 | import org.apache.commons.logging.Log;
12 | import org.apache.commons.logging.LogFactory;
13 | import org.jfree.chart.ChartFactory;
14 | import org.jfree.chart.ChartUtilities;
15 | import org.jfree.chart.JFreeChart;
16 | import org.jfree.data.time.TimeSeriesCollection;
17 | import org.openquant.backtest.Order;
18 | import org.openquant.backtest.Position;
19 |
20 | public class MultiStatJFreeChartReport extends AbstractReport {
21 |
22 | private Log log = LogFactory.getLog(MultiStatJFreeChartReport.class);
23 |
24 | private int iterations;
25 |
26 | private String filename;
27 |
28 | private List reports = new ArrayList();
29 |
30 | public MultiStatJFreeChartReport(String filename, double capital, double commission,
31 | double slippage, List closedPositions,
32 | Collection openPositions, int iterations) {
33 | super(capital, commission, slippage, closedPositions, openPositions);
34 | this.iterations = iterations;
35 | this.filename = filename;
36 |
37 | for (int i = 0 ; i < iterations; i++){
38 |
39 | ArrayList copy = new ArrayList(closedPositions);
40 | Collections.copy(copy, closedPositions);
41 |
42 | Collections.shuffle(copy);
43 | // order all of the positions by entry date
44 | Collections.sort(copy, new Comparator() {
45 |
46 | @Override
47 | public int compare(Position positionOne, Position positionTwo) {
48 | return positionOne.getEntryDate().compareTo(
49 | positionTwo.getEntryDate());
50 | }
51 |
52 | });
53 |
54 | System.out.println(" ===== ");
55 | for (Position pos : copy){
56 | System.out.println(pos);
57 | }
58 |
59 | JFreeChartReport report = new JFreeChartReport(filename + ".jpg",
60 | capital, commission, slippage, copy, openPositions);
61 |
62 | report.process();
63 | reports.add( report );
64 |
65 | }
66 |
67 |
68 | }
69 |
70 | @Override
71 | public void onEnterOrder(Order order, double capital, double availableCash) {
72 |
73 | }
74 |
75 | @Override
76 | public void onExitOrder(Order order, double capital, double availableCash) {
77 |
78 | }
79 |
80 | @Override
81 | public void render() {
82 | try {
83 | // Add the series to your data set
84 | TimeSeriesCollection dataset = new TimeSeriesCollection();
85 |
86 | //int i = 0;
87 | for(JFreeChartReport report : reports){
88 |
89 | //i++;
90 | //report.getEquitySeries().setKey(i);
91 | //report.getEquitySeries().setDescription("equity" + i);
92 | //report.getCashSeries().setDescription("cash" + i);
93 |
94 | dataset.addSeries(report.getEquitySeries());
95 | //dataset.addSeries(report.getCashSeries());
96 | }
97 |
98 | // Generate the graph
99 |
100 | JFreeChart chart = ChartFactory.createTimeSeriesChart(
101 | "Equity Curve", "Day", "Value", dataset, false, false,
102 | false);
103 |
104 | ChartUtilities.saveChartAsJPEG(new File(filename), chart, 1000, 600);
105 |
106 | } catch (IOException e) {
107 | log.error(e, e);
108 | }
109 |
110 | }
111 |
112 | }
113 |
--------------------------------------------------------------------------------
/src/org/openquant/backtest/report/TradesReport.java:
--------------------------------------------------------------------------------
1 | package org.openquant.backtest.report;
2 |
3 | import java.io.FileWriter;
4 | import java.io.IOException;
5 | import java.io.PrintWriter;
6 | import java.util.Collection;
7 |
8 | import org.apache.commons.logging.Log;
9 | import org.apache.commons.logging.LogFactory;
10 | import org.openquant.backtest.Order;
11 | import org.openquant.backtest.Position;
12 |
13 | public class TradesReport extends AbstractReport {
14 |
15 | private Log log = LogFactory.getLog(TradesReport.class);
16 |
17 | private PrintWriter outputStream;
18 |
19 | public TradesReport(double capital, double commission, double slippage, Collection closedPositions,
20 | Collection openPositions, String filename) {
21 | super(capital, commission, slippage, closedPositions, openPositions);
22 |
23 | try {
24 | outputStream = new PrintWriter(new FileWriter(filename));
25 |
26 | } catch (IOException e) {
27 | // TODO Auto-generated catch block
28 | e.printStackTrace();
29 | }
30 |
31 | process();
32 | }
33 |
34 | @Override
35 | public void onEnterOrder(Order order, double capital, double availableCash) {
36 | outputStream.println( String.format("Entering %s, capital: %.2f, availableCash: %.2f", order, capital, availableCash) );
37 |
38 | }
39 |
40 | @Override
41 | public void onExitOrder(Order order, double capital, double availableCash) {
42 | outputStream.println( String.format("Exiting %s, capital: %.2f, availableCash: %.2f", order, capital, availableCash) );
43 | }
44 |
45 | @Override
46 | public void render() {
47 |
48 | if (outputStream != null) {
49 | outputStream.close();
50 | }
51 |
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/org/openquant/data/ObjectStreamSeriesDatasource.java:
--------------------------------------------------------------------------------
1 | package org.openquant.data;
2 |
3 | /*
4 | Copyright (c) 2011, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import java.io.File;
23 | import java.io.FileInputStream;
24 | import java.io.FileOutputStream;
25 | import java.io.IOException;
26 | import java.io.ObjectInputStream;
27 | import java.io.ObjectOutputStream;
28 | import java.util.Date;
29 | import java.util.Set;
30 |
31 | import org.openquant.backtest.CandleSeries;
32 |
33 | public class ObjectStreamSeriesDatasource implements SeriesDatasource {
34 |
35 | private String filename;
36 |
37 | private SeriesCollection seriesCollection = new SeriesCollection();
38 |
39 | public ObjectStreamSeriesDatasource(final String filename) {
40 | this.filename = filename;
41 |
42 | if(exists()){
43 | // initialize if exists
44 | FileInputStream fis = null;
45 | ObjectInputStream in = null;
46 | try {
47 | fis = new FileInputStream(filename);
48 | in = new ObjectInputStream(fis);
49 | seriesCollection = (SeriesCollection) in.readObject();
50 | in.close();
51 | } catch (IOException ex) {
52 | ex.printStackTrace();
53 | } catch (ClassNotFoundException ex) {
54 | ex.printStackTrace();
55 | }
56 | }
57 | }
58 |
59 | public Date getBeginDate() {
60 | return seriesCollection.getBeginDate();
61 | }
62 |
63 | public Date getEndDate() {
64 | return seriesCollection.getEndDate();
65 | }
66 |
67 | public Set getSymbols(){
68 | return seriesCollection.getSymbols();
69 | }
70 |
71 | public boolean exists(){
72 | return new File(filename).exists();
73 | }
74 |
75 | public void insertCandleSeries(CandleSeries series) {
76 | seriesCollection.addSeries(series);
77 | }
78 |
79 | public void commit() {
80 |
81 | FileOutputStream fos = null;
82 | ObjectOutputStream out = null;
83 | try {
84 | fos = new FileOutputStream(filename);
85 | out = new ObjectOutputStream(fos);
86 | out.writeObject(seriesCollection);
87 | out.close();
88 | } catch (IOException ex) {
89 | ex.printStackTrace();
90 | }
91 |
92 | }
93 |
94 | @Override
95 | public CandleSeries fetchSeries(String symbol) throws Exception {
96 | return seriesCollection.getSeries(symbol);
97 | }
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/src/org/openquant/data/SeriesCollection.java:
--------------------------------------------------------------------------------
1 | package org.openquant.data;
2 |
3 | /*
4 | Copyright (c) 2011, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import java.io.Serializable;
23 | import java.util.Date;
24 | import java.util.HashSet;
25 | import java.util.Set;
26 |
27 | import org.openquant.backtest.CandleSeries;
28 |
29 | public class SeriesCollection implements Serializable {
30 |
31 | private static final long serialVersionUID = 1L;
32 |
33 | private Set seriesSet = new HashSet();
34 |
35 | public void addSeries(final CandleSeries series) {
36 | seriesSet.add(series);
37 | }
38 |
39 | public Set getSymbols(){
40 | Set rSet = new HashSet();
41 | for (CandleSeries series : seriesSet){
42 |
43 | rSet.add(series.getSymbol());
44 | }
45 | return rSet;
46 | }
47 |
48 | public Date getBeginDate(){
49 | return seriesSet.iterator().next().getBeginDate();
50 | }
51 |
52 | public Date getEndDate(){
53 | return seriesSet.iterator().next().getEndDate();
54 | }
55 |
56 | public CandleSeries getSeries(String symbol){
57 |
58 | for (CandleSeries series : seriesSet){
59 | if (series.getSymbol().equals(symbol)){
60 | return series;
61 | }
62 | }
63 | return null;
64 |
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/src/org/openquant/data/SeriesDatasource.java:
--------------------------------------------------------------------------------
1 | package org.openquant.data;
2 |
3 | /*
4 | Copyright (c) 2011, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import org.openquant.backtest.CandleSeries;
23 |
24 | public interface SeriesDatasource {
25 |
26 | public abstract CandleSeries fetchSeries(final String symbol) throws Exception;
27 |
28 | }
--------------------------------------------------------------------------------
/src/org/openquant/data/YahooSeriesDatasource.java:
--------------------------------------------------------------------------------
1 | package org.openquant.data;
2 |
3 | /*
4 | Copyright (c) 2010, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import java.io.BufferedReader;
23 | import java.io.InputStreamReader;
24 | import java.net.URL;
25 | import java.text.DateFormat;
26 | import java.text.MessageFormat;
27 | import java.text.ParseException;
28 | import java.text.SimpleDateFormat;
29 | import java.util.ArrayList;
30 | import java.util.Calendar;
31 | import java.util.Date;
32 | import java.util.GregorianCalendar;
33 | import java.util.List;
34 | import java.util.StringTokenizer;
35 | import java.util.TimeZone;
36 |
37 | import org.apache.commons.logging.Log;
38 | import org.apache.commons.logging.LogFactory;
39 | import org.openquant.backtest.Candle;
40 | import org.openquant.backtest.CandleSeries;
41 | import org.openquant.backtest.DateUtil;
42 |
43 | public class YahooSeriesDatasource implements SeriesDatasource {
44 |
45 | private Log log = LogFactory.getLog(YahooSeriesDatasource.class);
46 |
47 | private final static String YAHOO_URL = "http://table.finance.yahoo.com/table.csv?s={0}&a={1}&b={2}&c={3}&d={4}&e={5}&f={6}&g={7}&ignore=.csv";
48 |
49 | public static final String DATE_FORMAT = "yyyy-MM-dd";
50 |
51 | private final static DateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
52 |
53 | private boolean splitAdjust = true;
54 |
55 | private Date begin;
56 |
57 | private Date end;
58 |
59 |
60 | public YahooSeriesDatasource(String begin, String end) {
61 |
62 | try {
63 | this.begin = sdf.parse(begin);
64 | this.end = sdf.parse(end);
65 | } catch (ParseException e) {
66 | log.error(e.getMessage(), e);
67 | }
68 | }
69 |
70 | public YahooSeriesDatasource(String begin) {
71 |
72 | try {
73 | this.begin = sdf.parse(begin);
74 | } catch (ParseException e) {
75 | log.error(e.getMessage(), e);
76 | }
77 | }
78 |
79 | public YahooSeriesDatasource(final int lookback){
80 | this.begin = DateUtil.calculateLookbackDate(lookback);
81 | }
82 |
83 | public boolean isSplitAdjust() {
84 | return splitAdjust;
85 | }
86 |
87 | public void setSplitAdjust(boolean splitAdjust) {
88 | this.splitAdjust = splitAdjust;
89 | }
90 |
91 | public void setBeginDate(String beginDate){
92 | try {
93 | this.begin = sdf.parse(beginDate);
94 | } catch (ParseException e) {
95 | log.error(e.getMessage(), e);
96 | }
97 | }
98 |
99 | public void setEndDate(String endDate){
100 | try {
101 | this.end = sdf.parse(endDate);
102 | } catch (ParseException e) {
103 | log.error(e.getMessage(), e);
104 | }
105 | }
106 |
107 | /* (non-Javadoc)
108 | * @see org.openquant.backtest.SeriesDatasource#fetchSeries(java.lang.String)
109 | */
110 | public CandleSeries fetchSeries(final String symbol) throws Exception {
111 |
112 | Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
113 | cal.setTime(begin);
114 | String beginYear = String.valueOf(cal.get(Calendar.YEAR));
115 | String beginMonth = String.valueOf(cal.get(Calendar.MONTH));
116 | String beginDay = String.valueOf(cal.get(Calendar.DAY_OF_MONTH));
117 |
118 | // if end not set, assume yesterday's date
119 | if (end == null) {
120 | // get Calendar with current date
121 | GregorianCalendar gCal = new GregorianCalendar();
122 |
123 | // get yesterday's date
124 | gCal.add(Calendar.DATE, -1);
125 |
126 | end = gCal.getTime();
127 | }
128 |
129 | cal.setTime(end);
130 | String day = String.valueOf(cal.get(Calendar.DAY_OF_MONTH));
131 | String month = String.valueOf(cal.get(Calendar.MONTH));
132 | String year = String.valueOf(cal.get(Calendar.YEAR));
133 |
134 | String resolution = "d";
135 |
136 | // open url
137 | String urlStr = MessageFormat.format(YAHOO_URL, symbol, beginMonth, beginDay, beginYear, month, day, year,
138 | resolution);
139 | BufferedReader reader;
140 | String line;
141 |
142 | List lineList = new ArrayList();
143 |
144 | log.info("URL [" + urlStr + "]");
145 | URL url = new URL(urlStr);
146 | reader = new BufferedReader(new InputStreamReader(url.openStream()));
147 | line = reader.readLine();
148 | log.debug(line);
149 |
150 | while ((line = reader.readLine()) != null) {
151 | lineList.add(0, line);
152 | }
153 |
154 | List candles = new ArrayList();
155 |
156 | for (String currentLine : lineList) {
157 | log.debug(currentLine);
158 | StringTokenizer str = new StringTokenizer(currentLine, ",");
159 | String datestring = str.nextToken();
160 |
161 | double open = round(Double.parseDouble(str.nextToken()), 2);
162 | double high = Double.parseDouble(str.nextToken());
163 | double low = Double.parseDouble(str.nextToken());
164 | double close = Double.parseDouble(str.nextToken());
165 | Long volume = null;
166 | double adjclose = 0;
167 | if (str.hasMoreTokens()) {
168 | String volString = str.nextToken();
169 | volume = Long.parseLong(volString);
170 | log.trace( String.format("Parsed volume [%s] to %s", volString, volume));
171 |
172 | if (splitAdjust) {
173 | adjclose = Double.parseDouble(str.nextToken());
174 | }
175 | }
176 |
177 | Date date = sdf.parse(datestring);
178 |
179 | Candle candle = null;
180 | if (splitAdjust) {
181 | double adjustmentFactor = adjclose / close;
182 |
183 | candle = new Candle(symbol, date, round(open * adjustmentFactor, 2), round(high * adjustmentFactor, 2),
184 | round(low * adjustmentFactor, 2), adjclose, volume);
185 | } else {
186 | // new candle
187 | candle = new Candle(symbol, date, open, high, low, close, volume);
188 | }
189 |
190 | candles.add(candle);
191 |
192 | }
193 |
194 | return new CandleSeries(candles);
195 | }
196 |
197 | public static double round(double Rval, int Rpl) {
198 | double p = (double) Math.pow(10, Rpl);
199 | Rval = Rval * p;
200 | float tmp = Math.round(Rval);
201 | return (double) tmp / p;
202 | }
203 |
204 | }
205 |
--------------------------------------------------------------------------------
/src/org/openquant/data/load/DataLoader.java:
--------------------------------------------------------------------------------
1 | package org.openquant.data.load;
2 |
3 | /*
4 | Copyright (c) 2011, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import java.util.Set;
23 |
24 | import javax.annotation.Resource;
25 |
26 | import org.apache.commons.logging.Log;
27 | import org.apache.commons.logging.LogFactory;
28 | import org.openquant.backtest.CandleSeries;
29 | import org.openquant.data.ObjectStreamSeriesDatasource;
30 | import org.openquant.data.YahooSeriesDatasource;
31 | import org.springframework.beans.factory.annotation.Autowired;
32 | import org.springframework.context.annotation.AnnotationConfigApplicationContext;
33 | import org.springframework.context.annotation.Bean;
34 | import org.springframework.context.annotation.Configuration;
35 | import org.springframework.context.annotation.ImportResource;
36 |
37 | /**
38 | * Utility class for importing data from yahoo into a local
39 | * ObjectStreamSeriesDatasource. During backtesting the
40 | * ObjectStreamSeriesDatasource can replace the usual YahooSeriesDatasource so
41 | * the test does not have to go out and fetch the data from the Yahoo's servers
42 | * for each stock.
43 | *
44 | * This example will autowire the contents of symbols-yahoo-nasdaq10.xml into
45 | * the symbols variable and export the resulting Collection to the file specified
46 | * via the DATAFILE static var at a specific START_DATE to todays closing date.
47 | *
48 | * @author jay
49 | *
50 | */
51 | @Configuration
52 | @ImportResource("classpath:/symbols-yahoo-nasdaq10.xml")
53 | public class DataLoader {
54 |
55 | private static Log log = LogFactory.getLog(DataLoader.class);
56 |
57 | private static final String START_DATE = "2000-01-01";
58 |
59 | private static final String DATAFILE = "nasdaq10.dat";
60 |
61 | @Autowired
62 | @Resource(name = "symbols")
63 | private Set symbols;
64 |
65 | private ObjectStreamSeriesDatasource localSeriesDatasource = new ObjectStreamSeriesDatasource(DATAFILE);
66 |
67 | public class ExecuteBean {
68 |
69 | public void loadFromYahoo() {
70 |
71 | try {
72 |
73 | for (String symbol : symbols) {
74 | try {
75 | CandleSeries series = yahooSeriesDatasource().fetchSeries(symbol);
76 | localSeriesDatasource.insertCandleSeries(series);
77 | log.info("Loaded series " + symbol);
78 | } catch (Exception e) {
79 | log.error(e.getMessage(), e);
80 | }
81 | }
82 |
83 | } finally {
84 | localSeriesDatasource.commit();
85 | }
86 |
87 | }
88 | }
89 |
90 | @Bean
91 | public YahooSeriesDatasource yahooSeriesDatasource() {
92 | return new YahooSeriesDatasource(START_DATE);
93 | }
94 |
95 | @Bean(name = "executeBean", initMethod = "loadFromYahoo")
96 | public ExecuteBean executeBean() {
97 | return new ExecuteBean();
98 | }
99 |
100 | /**
101 | * @param args
102 | */
103 | public static void main(String[] args) {
104 | new AnnotationConfigApplicationContext(DataLoader.class);
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/src/org/openquant/quote/QuoteDataSource.java:
--------------------------------------------------------------------------------
1 | package org.openquant.quote;
2 |
3 | /*
4 | Copyright (c) 2011, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import java.util.HashSet;
23 | import java.util.Set;
24 | import java.util.concurrent.ScheduledFuture;
25 | import java.util.concurrent.ScheduledThreadPoolExecutor;
26 | import java.util.concurrent.TimeUnit;
27 |
28 | import org.apache.commons.logging.Log;
29 | import org.apache.commons.logging.LogFactory;
30 | import org.openquant.backtest.Candle;
31 |
32 | public abstract class QuoteDataSource {
33 |
34 | private Log log = LogFactory.getLog(QuoteDataSource.class);
35 |
36 | private Set symbols = new HashSet();
37 |
38 | private boolean loopForever = true;
39 |
40 | private int interval = 5;
41 |
42 | private ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(20);
43 |
44 | private ScheduledFuture> scheduledFuture;
45 |
46 | public interface GlobalQuoteListener{
47 | void onOpenQuote(Candle quote);
48 | void onQuote(Candle quote);
49 | }
50 |
51 | protected GlobalQuoteListener globalListener;
52 |
53 | public GlobalQuoteListener getGlobalListener() {
54 | return globalListener;
55 | }
56 |
57 | public void setGlobalListener(GlobalQuoteListener globalListener) {
58 | this.globalListener = globalListener;
59 | }
60 |
61 | public QuoteDataSource(Set symbols, GlobalQuoteListener globalListener){
62 | this.symbols = symbols;
63 | this.globalListener = globalListener;
64 | }
65 |
66 | public QuoteDataSource(){
67 |
68 | }
69 |
70 | private class QuoteRetriever implements Runnable {
71 |
72 | @Override
73 | public void run() {
74 |
75 | Set localSymbols = new HashSet(getSymbols());
76 |
77 | log.debug(">> BEGIN retrieve");
78 | for (String symbol : localSymbols) {
79 | try {
80 | Candle candle = retrieveQuoteCandle(symbol);
81 | if (candle != null){
82 | onGetQuote( candle );
83 | }
84 | } catch (Exception e) {
85 | log.error("Exception when retrieving [" + symbol + "]", e);
86 | }
87 | }
88 | log.debug(">> END retrieve");
89 |
90 |
91 | }
92 | }
93 |
94 | private void retrieveOpenQuotes(){
95 |
96 | Set localSymbols = new HashSet(getSymbols());
97 |
98 | log.debug(">> BEGIN retrieve");
99 | for (String symbol : localSymbols) {
100 | try {
101 | Candle candle = retrieveQuoteCandle(symbol);
102 | if (candle != null){
103 | onGetOpenQuote( candle );
104 | }
105 | } catch (Exception e) {
106 | log.error("Exception when retrieving [" + symbol + "]", e);
107 | }
108 | }
109 | log.debug(">> END retrieve");
110 | }
111 |
112 | public boolean isLoopForever() {
113 | return loopForever;
114 | }
115 |
116 | public void setLoopForever(boolean loopForever) {
117 | this.loopForever = loopForever;
118 | }
119 |
120 | public int getInterval() {
121 | return interval;
122 | }
123 |
124 | public void setInterval(int interval) {
125 | this.interval = interval;
126 | // reinitialize the schedule
127 | if (scheduledFuture != null){
128 | scheduledFuture.cancel(true);
129 | initialize();
130 | }
131 | }
132 |
133 | public Set getSymbols() {
134 | return symbols;
135 | }
136 |
137 | public void setSymbols(Set symbols) {
138 | this.symbols = symbols;
139 | }
140 |
141 | public void removeSymbol(String symbol){
142 | symbols.remove(symbol);
143 | }
144 |
145 | public void initialize() {
146 |
147 | // first get Open Quotes
148 | retrieveOpenQuotes();
149 |
150 | if (isLoopForever()) {
151 | scheduledFuture = executor.scheduleWithFixedDelay(new QuoteRetriever(), 0, getInterval(), TimeUnit.SECONDS);
152 |
153 | } else {
154 | scheduledFuture = executor.schedule(new QuoteRetriever(), getInterval(), TimeUnit.SECONDS);
155 | }
156 | }
157 |
158 | public void finalize() {
159 | scheduledFuture.cancel(false);
160 |
161 | }
162 |
163 |
164 | private void onGetQuote(final Candle quoteCandle) {
165 |
166 | if(log.isDebugEnabled()){
167 | log.debug(String.format("Got quote %s[%s, %s, %s, %s, %s, %s]", quoteCandle.getSymbol(),
168 | quoteCandle.getClosePrice(), quoteCandle.getOpenPrice(), quoteCandle.getHighPrice(),
169 | quoteCandle.getLowPrice(), quoteCandle.getVolume(), quoteCandle.getDate()));
170 | }
171 |
172 | this.globalListener.onQuote(quoteCandle);
173 |
174 | }
175 |
176 | private void onGetOpenQuote(final Candle quoteCandle) {
177 |
178 | if(log.isDebugEnabled()){
179 | log.debug(String.format("Got quote %s[%s, %s, %s, %s, %s, %s]", quoteCandle.getSymbol(),
180 | quoteCandle.getClosePrice(), quoteCandle.getOpenPrice(), quoteCandle.getHighPrice(),
181 | quoteCandle.getLowPrice(), quoteCandle.getVolume(), quoteCandle.getDate()));
182 | }
183 |
184 | this.globalListener.onOpenQuote(quoteCandle);
185 |
186 | }
187 |
188 |
189 | public abstract Candle retrieveQuoteCandle(String symbol) throws Exception;
190 |
191 | }
192 |
--------------------------------------------------------------------------------
/src/org/openquant/quote/QuoteListener.java:
--------------------------------------------------------------------------------
1 | package org.openquant.quote;
2 |
3 | /*
4 | Copyright (c) 2011, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import org.openquant.backtest.Candle;
23 |
24 | public abstract class QuoteListener{
25 |
26 | protected String symbol;
27 |
28 | public QuoteListener(){
29 | }
30 |
31 | public QuoteListener(String symbol){
32 | this.symbol = symbol;
33 | }
34 |
35 | public String getSymbol() {
36 | return symbol;
37 | }
38 |
39 | public void setSymbol(String symbol) {
40 | this.symbol = symbol;
41 | }
42 |
43 | public abstract void onGetQuote(Candle quoteCandle);
44 | }
45 |
--------------------------------------------------------------------------------
/src/org/openquant/quote/YahooQuoteDataSource.java:
--------------------------------------------------------------------------------
1 | package org.openquant.quote;
2 |
3 | /*
4 | Copyright (c) 2011, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import java.io.BufferedReader;
23 | import java.io.IOException;
24 | import java.io.InputStreamReader;
25 | import java.net.MalformedURLException;
26 | import java.net.URL;
27 | import java.text.DateFormat;
28 | import java.text.SimpleDateFormat;
29 | import java.util.Date;
30 | import java.util.HashSet;
31 | import java.util.Set;
32 | import java.util.StringTokenizer;
33 |
34 | import org.apache.commons.logging.Log;
35 | import org.apache.commons.logging.LogFactory;
36 | import org.openquant.backtest.Candle;
37 |
38 | public class YahooQuoteDataSource extends QuoteDataSource {
39 |
40 | private Log log = LogFactory.getLog(YahooQuoteDataSource.class);
41 |
42 | private static final String QUOTE_URL = "http://quote.yahoo.com/d/quotes.csv?s=%s&d=t&f=sl1d1t1c1ohgvj1pp2wern";
43 |
44 | private final static DateFormat DATE_FORMAT = new SimpleDateFormat("M/d/yyyy hh:mmaaa");
45 |
46 | public YahooQuoteDataSource(Set symbols, GlobalQuoteListener globalListener) {
47 | super(symbols, globalListener);
48 | }
49 |
50 | public YahooQuoteDataSource(Set symbols) {
51 | this.setSymbols(symbols);
52 | }
53 |
54 | @Override
55 | public Candle retrieveQuoteCandle(String symbol) throws Exception {
56 | String urlStr = String.format(QUOTE_URL, symbol);
57 | String line = readLine(urlStr);
58 |
59 | Candle returnCandle = null;
60 | // "MSFT",28.46,"5/7/2010","1:23pm",-0.52,28.90,28.94,27.32,94535576,249.4B,28.98,"-1.79%","19.01 - 31.58",1.93,15.02,"Microsoft Corpora"
61 |
62 | StringTokenizer str = new StringTokenizer(line, ",\"");
63 | // symbol is the next token
64 | str.nextToken();
65 | double quote = Double.parseDouble(str.nextToken());
66 | Date date;
67 | try {
68 | date = DATE_FORMAT.parse(str.nextToken() + " " + str.nextToken());
69 | } catch (Exception e) {
70 | log.warn("Exception when parsing date for " + symbol + " using system timestamp");
71 | date = new Date();
72 | }
73 | // price change is the next token
74 | str.nextToken();
75 | double open = Double.parseDouble(str.nextToken());
76 | double high = Double.parseDouble(str.nextToken());
77 | double low = Double.parseDouble(str.nextToken());
78 | double volume = Double.parseDouble(str.nextToken());
79 |
80 | returnCandle = new Candle(symbol, date, open, high, low, quote, volume);
81 |
82 | return returnCandle;
83 | }
84 |
85 | private String readLine(final String urlStr) {
86 | BufferedReader reader;
87 | String line = null;
88 |
89 | try {
90 | URL url = new URL(urlStr);
91 | reader = new BufferedReader(new InputStreamReader(url.openStream()));
92 | line = reader.readLine();
93 | } catch (MalformedURLException e) {
94 | log.error(e, e);
95 | } catch (IOException e) {
96 | log.error(e, e);
97 | }
98 |
99 | return line;
100 | }
101 |
102 | public static void main(String... args) {
103 |
104 | final Log log = LogFactory.getLog(YahooQuoteDataSource.class);
105 |
106 | Set symbols = new HashSet();
107 | symbols.add("MSFT");
108 | symbols.add("GOOG");
109 |
110 | QuoteDataSource quote = new YahooQuoteDataSource(symbols, new GlobalQuoteListener() {
111 |
112 | @Override
113 | public void onQuote(Candle quoteCandle) {
114 | log.info(String.format("Got quote %s[%s, %s, %s, %s, %s, %s]", quoteCandle.getSymbol(),
115 | quoteCandle.getOpenPrice(), quoteCandle.getHighPrice(), quoteCandle.getLowPrice(),
116 | quoteCandle.getClosePrice(), quoteCandle.getVolume(), quoteCandle.getDate()));
117 |
118 | }
119 |
120 | @Override
121 | public void onOpenQuote(Candle quoteCandle) {
122 | log.info(String.format("Got open quote %s[%s, %s, %s, %s, %s, %s]", quoteCandle.getSymbol(),
123 | quoteCandle.getOpenPrice(), quoteCandle.getHighPrice(), quoteCandle.getLowPrice(),
124 | quoteCandle.getClosePrice(), quoteCandle.getVolume(), quoteCandle.getDate()));
125 |
126 | }
127 | });
128 |
129 | quote.setLoopForever(true);
130 | quote.setInterval(1);
131 | quote.initialize();
132 |
133 | }
134 |
135 | }
136 |
--------------------------------------------------------------------------------
/src/org/openquant/util/OpenMarketUtil.java:
--------------------------------------------------------------------------------
1 | package org.openquant.util;
2 |
3 | /*
4 | Copyright (c) 2011, Jay Logelin
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8 | conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the distribution. Neither the name of the JQuant nor the names of its
13 | contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
15 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 | */
21 |
22 | import java.util.ArrayList;
23 | import java.util.Calendar;
24 | import java.util.Collections;
25 | import java.util.Date;
26 | import java.util.List;
27 |
28 |
29 | /**
30 | * source : http://www.isthemarketopen.com/
31 | *
32 | * @author jay
33 | *
34 | */
35 | public class OpenMarketUtil {
36 |
37 | private static List closed = new ArrayList();
38 |
39 | static{
40 | closed.add(new Day(2011, Calendar.JANUARY, 17));
41 | closed.add(new Day(2011, Calendar.FEBRUARY, 21));
42 | closed.add(new Day(2011, Calendar.APRIL, 22));
43 | closed.add(new Day(2011, Calendar.MAY, 30));
44 | closed.add(new Day(2011, Calendar.JULY, 4));
45 | closed.add(new Day(2011, Calendar.SEPTEMBER, 5));
46 | closed.add(new Day(2011, Calendar.NOVEMBER, 24));
47 | closed.add(new Day(2011, Calendar.DECEMBER, 26));
48 | }
49 |
50 | public static boolean isMarketOpenToday(){
51 | return isMarketOpen(new Day(new Date()));
52 | }
53 |
54 | public static boolean isMarketOpen(Day day){
55 |
56 | int position = Collections.binarySearch(closed, day);
57 |
58 | return position < 0;
59 |
60 | }
61 |
62 | public static void main(String ... args){
63 | if (OpenMarketUtil.isMarketOpenToday()){
64 | System.out.println("Market is open");
65 | }else{
66 | System.out.println("Market is closed");
67 | }
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/test/org/openquant/test/TestTradeSystem.java:
--------------------------------------------------------------------------------
1 | package org.openquant.test;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import org.openquant.backtest.BackTestExecutor;
7 | import org.openquant.backtest.CandleSeriesTestContext;
8 | import org.openquant.backtest.Position;
9 | import org.openquant.backtest.Series;
10 | import org.openquant.data.SeriesDatasource;
11 | import org.openquant.data.YahooSeriesDatasource;
12 |
13 | public class TestTradeSystem extends CandleSeriesTestContext{
14 |
15 | @Override
16 | public void run() {
17 |
18 | Series Close = closeSeries();
19 | Series RSI = Close.RSI(25);
20 |
21 | for (int bar = 25; bar < barsCount() - 1; bar++) {
22 |
23 | Position pos = getLastOpenPosition();
24 | if (hasOpenPositions()) {
25 | sellAtLimit(bar + 1, pos, pos.getEntryPrice() * 1.02, "XLL");
26 | }
27 |
28 | if (hasOpenPositions()) {
29 | sellAtStop(bar + 1, pos, pos.getEntryPrice() * 0.99, "XLS");
30 | }
31 |
32 | if (RSI.getAt(bar) > RSI.getAt(bar - 1)) {
33 | buyAtLimit(bar + 1, close(bar), 1000, "ELL");
34 | }
35 |
36 |
37 | }
38 |
39 | }
40 |
41 | public static void main(String... args) {
42 | //new ClassPathXmlApplicationContext("TestTradeSystem.xml");
43 |
44 | SeriesDatasource data = new YahooSeriesDatasource("2009-01-01");
45 |
46 | List symbols = new ArrayList();
47 | symbols.add("GOOG");
48 |
49 | BackTestExecutor executor = new BackTestExecutor(data, symbols, new TestTradeSystem(), "TestReport", 100000, 9.99, 0.2);
50 | executor.run();
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------