├── .github └── workflows │ └── gradle.yml ├── .gitignore ├── .mvn └── wrapper │ ├── MavenWrapperDownloader.java │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── Dockerfile ├── HELP.md ├── Maxima_minima.jpg ├── MyChartFlag.jpg ├── MyTriangles.jpg ├── README-html.md ├── README.md ├── Trendlines.jpg ├── build.gradle ├── elliott ├── RELIANCE_OneHour.jpg └── SBIN_OneHour.jpg ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── hs_err_pid257906.log ├── settings.gradle ├── src ├── main │ ├── java │ │ └── com │ │ │ └── dtech │ │ │ ├── algo │ │ │ ├── backtest │ │ │ │ ├── BackTestingHandlerJson.java │ │ │ │ ├── BacktestInput.java │ │ │ │ ├── BacktestResult.java │ │ │ │ ├── OrderRecord.java │ │ │ │ └── TradeRecord.java │ │ │ ├── config │ │ │ │ ├── AlgoTradeMapperConfig.java │ │ │ │ └── CachingConfig.java │ │ │ ├── controller │ │ │ │ ├── BacktestController.java │ │ │ │ └── MetadataController.java │ │ │ ├── entities │ │ │ │ └── StrategyEntity.java │ │ │ ├── exception │ │ │ │ └── StrategyException.java │ │ │ ├── indicators │ │ │ │ ├── IndicatorConstructor.java │ │ │ │ ├── IndicatorInfo.java │ │ │ │ └── IndicatorRegistry.java │ │ │ ├── mapper │ │ │ │ ├── GenericMapper.java │ │ │ │ └── KiteTickToDataTickMapper.java │ │ │ ├── registry │ │ │ │ └── common │ │ │ │ │ ├── BaseRegistry.java │ │ │ │ │ └── ConstructorArgs.java │ │ │ ├── rules │ │ │ │ ├── RuleConstructor.java │ │ │ │ ├── RuleInfo.java │ │ │ │ └── RuleRegistry.java │ │ │ ├── runner │ │ │ │ └── candle │ │ │ │ │ ├── DataTick.java │ │ │ │ │ ├── LatestBarSeriesProvider.java │ │ │ │ │ └── UpdatableBarSeriesLoader.java │ │ │ ├── series │ │ │ │ ├── Exchange.java │ │ │ │ ├── ExtendedBarSeries.java │ │ │ │ ├── InstrumentType.java │ │ │ │ ├── Interval.java │ │ │ │ ├── IntervalBarSeries.java │ │ │ │ └── SeriesType.java │ │ │ ├── service │ │ │ │ └── StrategyService.java │ │ │ └── strategy │ │ │ │ ├── TradeStrategy.java │ │ │ │ ├── TradeStrategyImpl.java │ │ │ │ ├── builder │ │ │ │ ├── FinalStrategyBuilder.java │ │ │ │ ├── StrategyBuilderIfc.java │ │ │ │ ├── cache │ │ │ │ │ ├── BarSeriesCache.java │ │ │ │ │ ├── ConstantsCache.java │ │ │ │ │ ├── IndicatorCache.java │ │ │ │ │ ├── RuleCache.java │ │ │ │ │ ├── ThreadLocalCache.java │ │ │ │ │ └── TradingRecordCache.java │ │ │ │ └── ifc │ │ │ │ │ ├── BarSeriesLoader.java │ │ │ │ │ ├── ConstantsLoader.java │ │ │ │ │ ├── IndicatorBuilder.java │ │ │ │ │ └── RuleBuilder.java │ │ │ │ ├── config │ │ │ │ ├── BarSeriesConfig.java │ │ │ │ ├── FollowUpRuleConfig.java │ │ │ │ ├── FollowUpRuleType.java │ │ │ │ ├── IndicatorConfig.java │ │ │ │ ├── IndicatorInput.java │ │ │ │ ├── IndicatorInputType.java │ │ │ │ ├── RuleConfig.java │ │ │ │ ├── RuleInput.java │ │ │ │ ├── RuleInputType.java │ │ │ │ ├── RunnerConfig.java │ │ │ │ └── StrategyConfig.java │ │ │ │ ├── sync │ │ │ │ ├── CandleSyncExecutor.java │ │ │ │ ├── CandleSyncJob.java │ │ │ │ └── CandleSyncToken.java │ │ │ │ └── units │ │ │ │ ├── AbstractObjectBuilder.java │ │ │ │ ├── CachedIndicatorBuilder.java │ │ │ │ ├── CachedRuleBuilder.java │ │ │ │ └── RdbmsBarSeriesLoader.java │ │ │ ├── kitecon │ │ │ ├── KiteconApplication.java │ │ │ ├── config │ │ │ │ ├── HistoricalDateLimit.java │ │ │ │ └── KiteConnectConfig.java │ │ │ ├── controller │ │ │ │ ├── BarSeriesHelper.java │ │ │ │ ├── ConfigController.java │ │ │ │ ├── DataFetchController.java │ │ │ │ ├── ElliottVisualizerController.java │ │ │ │ ├── ExecutionController.java │ │ │ │ ├── ImageController.java │ │ │ │ ├── OrderController.java │ │ │ │ ├── StrategyController.java │ │ │ │ └── TrendlineController.java │ │ │ ├── data │ │ │ │ ├── Candle.java │ │ │ │ ├── IndexSymbol.java │ │ │ │ ├── Instrument.java │ │ │ │ ├── LocalDateTimeAttributeConverter.java │ │ │ │ ├── StrategyParameters.java │ │ │ │ └── TrendLine.java │ │ │ ├── historical │ │ │ │ └── limits │ │ │ │ │ └── LimitsKey.java │ │ │ ├── loader │ │ │ │ └── DataLoader.java │ │ │ ├── market │ │ │ │ ├── Provider.java │ │ │ │ ├── fetch │ │ │ │ │ ├── DataFetchException.java │ │ │ │ │ ├── MarketDataFetch.java │ │ │ │ │ ├── MarketDataFetchFactory.java │ │ │ │ │ ├── MarketDataFetchImpl.java │ │ │ │ │ └── ZerodhaDataFetch.java │ │ │ │ └── orders │ │ │ │ │ ├── OrderException.java │ │ │ │ │ ├── OrderManager.java │ │ │ │ │ └── ZerodhaOrderManager.java │ │ │ ├── misc │ │ │ │ └── StrategyEnvironment.java │ │ │ ├── repository │ │ │ │ ├── CandleRepository.java │ │ │ │ ├── CandleRepositoryOld.java │ │ │ │ ├── IndexSymbolRepository.java │ │ │ │ ├── InstrumentRepository.java │ │ │ │ └── StrategyParametersRepository.java │ │ │ ├── service │ │ │ │ ├── CandleFacade.java │ │ │ │ ├── DataDownloadRequest.java │ │ │ │ ├── DataDownloader.java │ │ │ │ ├── DataFetchService.java │ │ │ │ ├── DateRange.java │ │ │ │ ├── ExecutionService.java │ │ │ │ ├── IndexSymbolUpdaterService.java │ │ │ │ ├── StrategyService.java │ │ │ │ └── TradeInfo.java │ │ │ └── strategy │ │ │ │ ├── TradeDirection.java │ │ │ │ ├── TradingStrategy.java │ │ │ │ ├── backtest │ │ │ │ ├── BackTestingHandler.java │ │ │ │ ├── BacktestResult.java │ │ │ │ ├── BacktestSummary.java │ │ │ │ ├── OrderRecord.java │ │ │ │ └── TradeRecord.java │ │ │ │ ├── builder │ │ │ │ ├── BaseStrategyBuilder.java │ │ │ │ ├── MACDDiversionStrategy.java │ │ │ │ ├── OpeningRangeBreakoutStrategyBuiderModified.java │ │ │ │ ├── OpeningRangeBreakoutStrategyBuilder.java │ │ │ │ ├── PivotPointReversalStrategyBuider.java │ │ │ │ ├── SimpleMovingAverageStrategyBuider.java │ │ │ │ ├── StrategyBuilder.java │ │ │ │ └── StrategyConfig.java │ │ │ │ ├── dataloader │ │ │ │ ├── BarsLoader.java │ │ │ │ └── InstrumentDataLoader.java │ │ │ │ ├── exec │ │ │ │ ├── AlgoTradingRecord.java │ │ │ │ ├── HybridDataLoader.java │ │ │ │ ├── ProductionHandler.java │ │ │ │ ├── ProductionSeriesManager.java │ │ │ │ ├── ProductionStrategyRunner.java │ │ │ │ └── ProductionTradingRecord.java │ │ │ │ └── sets │ │ │ │ └── StrategySet.java │ │ │ ├── swagger │ │ │ ├── SwaggerDocumentationConfig.java │ │ │ └── SwaggerUiConfiguration.java │ │ │ ├── ta │ │ │ ├── BarTuple.java │ │ │ ├── OHLC.java │ │ │ ├── OHLCAnalyzer.java │ │ │ ├── TrendAnalysis.java │ │ │ ├── TrendLineCalculated.java │ │ │ ├── TrendLineCanculator.java │ │ │ ├── TrendLineUpside.java │ │ │ ├── TrendlineReliability.java │ │ │ ├── divergences │ │ │ │ ├── Divergence.java │ │ │ │ ├── DivergenceAnalyzer.java │ │ │ │ ├── DivergenceDetector.java │ │ │ │ ├── DivergenceDirection.java │ │ │ │ ├── DivergenceType.java │ │ │ │ ├── IndicatorType.java │ │ │ │ ├── MACDDivergenceDetector.java │ │ │ │ ├── RSIDivergenceDetector.java │ │ │ │ └── StochasticDivergenceDetector.java │ │ │ ├── elliott │ │ │ │ ├── AdvancedElliottWaveAnalyzer.java │ │ │ │ ├── ElliottWaveAnalyzer.java │ │ │ │ ├── RefinedLocalExtremesDetector.java │ │ │ │ ├── Wave.java │ │ │ │ ├── pattern │ │ │ │ │ ├── EndingDiagonalDetector.java │ │ │ │ │ ├── ExpandedFlatPatternDetector.java │ │ │ │ │ ├── FlatPatternDetector.java │ │ │ │ │ ├── LeadingDiagonalDetector.java │ │ │ │ │ ├── PatternDetectorBase.java │ │ │ │ │ └── TrianglePatternDetector.java │ │ │ │ ├── priceaction │ │ │ │ │ ├── PriceAction.java │ │ │ │ │ └── PriceActionAnalyzer.java │ │ │ │ └── wave │ │ │ │ │ ├── Wave1Detector.java │ │ │ │ │ ├── Wave2Detector.java │ │ │ │ │ ├── Wave3Detector.java │ │ │ │ │ ├── Wave4Detector.java │ │ │ │ │ ├── Wave5Detector.java │ │ │ │ │ └── WaveDetectorBase.java │ │ │ ├── patterns │ │ │ │ ├── DoubleBottomDetector.java │ │ │ │ ├── DoubleBottomPattern.java │ │ │ │ ├── FlagPattern.java │ │ │ │ ├── FlagPatternDetector.java │ │ │ │ ├── IndicatorCalculator.java │ │ │ │ ├── TriangleDetector.java │ │ │ │ └── TrianglePattern.java │ │ │ ├── trendline │ │ │ │ ├── ActiveTrendlineAnalysis.java │ │ │ │ ├── TrendLineAnalysis.java │ │ │ │ ├── TrendLineDetection.java │ │ │ │ ├── TrendlineAnalyser.java │ │ │ │ ├── TrendlineTAConfirmation.java │ │ │ │ ├── TrendlineType.java │ │ │ │ └── Util.java │ │ │ └── visualize │ │ │ │ ├── MACDVisualizer.java │ │ │ │ ├── RSIVisualizer.java │ │ │ │ ├── StochasticVisualizer.java │ │ │ │ ├── TrendlineVisualizer.java │ │ │ │ └── VisualizerHelper.java │ │ │ └── trade │ │ │ ├── ActiveOrderManager.java │ │ │ ├── instrument │ │ │ └── InstrumentBarSeriesManager.java │ │ │ ├── model │ │ │ ├── Order.java │ │ │ └── OrderStatus.java │ │ │ ├── order │ │ │ ├── OrderManager.java │ │ │ └── RealTradeOrder.java │ │ │ ├── repository │ │ │ └── OrderRepository.java │ │ │ └── zerodha │ │ │ ├── KiteOrderManager.java │ │ │ └── ZerodhaOrder.java │ └── resources │ │ ├── application.properties │ │ └── static │ │ ├── index.html │ │ └── js │ │ └── image-browser.js └── test │ ├── java │ └── com │ │ └── dtech │ │ ├── algo │ │ ├── backtest │ │ │ └── BackTestingHandlerJsonTest.java │ │ ├── controller │ │ │ ├── BacktestControllerTest.java │ │ │ └── MetadataControllerTest.java │ │ ├── indicators │ │ │ └── IndicatorRegistryTest.java │ │ ├── rules │ │ │ └── RuleRegistryTest.java │ │ ├── runner │ │ │ └── candle │ │ │ │ ├── LatestBarSeriesProviderFromCacheTest.java │ │ │ │ └── LatestBarSeriesProviderTest.java │ │ ├── series │ │ │ └── ExtendedBarSeriesTest.java │ │ └── strategy │ │ │ ├── TestHelper.java │ │ │ ├── builder │ │ │ └── FinalStrategyBuilderTest.java │ │ │ ├── cache │ │ │ └── ThreadLocalCacheTest.java │ │ │ ├── config │ │ │ └── BarSeriesConfigTest.java │ │ │ ├── helper │ │ │ └── ComponentHelper.java │ │ │ ├── sync │ │ │ ├── CandleSyncExecutorTest.java │ │ │ └── CandleSyncJobTest.java │ │ │ └── units │ │ │ ├── CachedIndicatorBuilderTest.java │ │ │ ├── CachedRuleBuilderTest.java │ │ │ └── RdbmsBarSeriesLoaderTest.java │ │ ├── chart │ │ ├── ChartCreator.java │ │ ├── FlagVisualizer.java │ │ └── TriangleVisualizer.java │ │ ├── kitecon │ │ └── service │ │ │ ├── DateRangeTest.java │ │ │ └── PnLCalculator.java │ │ ├── ta │ │ ├── OHLCAnalyzerTest.java │ │ ├── patterns │ │ │ ├── DoubleBottomDetectorTest.java │ │ │ ├── FlagPatternDetectorTest.java │ │ │ ├── PatternTestHelper.java │ │ │ └── TriangleVisualizationTest.java │ │ └── trendline │ │ │ └── TrendLineDetectionTest.java │ │ └── trade │ │ ├── ActiveOrderManagerTest.java │ │ ├── instrument │ │ └── InstrumentBarSeriesManagerTest.java │ │ └── zerodha │ │ └── KiteOrderManagerTest.java │ └── resources │ ├── application.properties │ ├── bitstamp_trades_from_20131125_usd.csv │ ├── com │ └── dtech │ │ └── ta │ │ └── processed_ohlc_data.csv │ └── mockito-extensions │ └── org.mockito.plugins.MockMaker ├── start.sh ├── test_data ├── NSE_ROSSARI_1D.csv ├── processed_ohlc_data.csv └── rossari_60.csv └── tlbo ├── ASHOKLEY_Day.jpg ├── CUMMINSIND_Day.jpg ├── DIVISLAB_Day.jpg ├── DMART_Day.jpg ├── EMAMILTD_Day.jpg ├── GODREJCP_Day.jpg ├── GUJGASLTD_Day.jpg ├── IEX_Day.jpg ├── JINDALSTEL_Day.jpg ├── KAJARIACER_Day.jpg ├── MARICO_Day.jpg ├── SUPREMEIND_Day.jpg └── VGUARD_Day.jpg /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Gradle 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle 3 | 4 | name: Java CI with Gradle 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up JDK 1.8 20 | uses: actions/setup-java@v1 21 | with: 22 | java-version: 11 23 | - name: Grant execute permission for gradlew 24 | run: chmod +x gradlew 25 | - name: Build with Gradle 26 | run: ./gradlew build 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | 30 | ### VS Code ### 31 | .vscode/ 32 | .gradle/ 33 | *.class 34 | out/ 35 | /tlbo/ 36 | /elliott/ 37 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProScriptSlinger/Zerodha-Algo-Trading/0efe9b3ca2d524a66cf0d07ce789618933b510bf/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gradle:jdk11 2 | 3 | ARG DEBIAN_FRONTEND=noninteractive 4 | RUN apt-get update && apt-get -y upgrade 5 | RUN apt-get -y install git wget 6 | 7 | USER root 8 | 9 | RUN mkdir code 10 | COPY . /code 11 | #RUN mkdir -p /root/.gradle/wrapper/dists/gradle-6.6.1-bin 12 | #ADD ./gradle-6.6.1-bin /root/.gradle/wrapper/dists/gradle-6.6.1-bin 13 | RUN cd /code && ./gradlew bootjar 14 | 15 | CMD /code/start.sh -------------------------------------------------------------------------------- /HELP.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ### Reference Documentation 4 | For further reference, please consider the following sections: 5 | 6 | * [Official Apache Maven documentation](https://maven.apache.org/guides/index.html) 7 | * [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.1.9.RELEASE/maven-plugin/) 8 | * [Spring Configuration Processor](https://docs.spring.io/spring-boot/docs/2.1.9.RELEASE/reference/htmlsingle/#configuration-metadata-annotation-processor) 9 | * [Spring Web](https://docs.spring.io/spring-boot/docs/2.1.9.RELEASE/reference/htmlsingle/#boot-features-developing-web-applications) 10 | * [Spring Boot DevTools](https://docs.spring.io/spring-boot/docs/2.1.9.RELEASE/reference/htmlsingle/#using-boot-devtools) 11 | 12 | ### Guides 13 | The following guides illustrate how to use some features concretely: 14 | 15 | * [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/) 16 | * [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/) 17 | * [Building REST services with Spring](https://spring.io/guides/tutorials/bookmarks/) 18 | * [Accessing data with MySQL](https://spring.io/guides/gs/accessing-data-mysql/) 19 | 20 | -------------------------------------------------------------------------------- /Maxima_minima.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProScriptSlinger/Zerodha-Algo-Trading/0efe9b3ca2d524a66cf0d07ce789618933b510bf/Maxima_minima.jpg -------------------------------------------------------------------------------- /MyChartFlag.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProScriptSlinger/Zerodha-Algo-Trading/0efe9b3ca2d524a66cf0d07ce789618933b510bf/MyChartFlag.jpg -------------------------------------------------------------------------------- /MyTriangles.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProScriptSlinger/Zerodha-Algo-Trading/0efe9b3ca2d524a66cf0d07ce789618933b510bf/MyTriangles.jpg -------------------------------------------------------------------------------- /README-html.md: -------------------------------------------------------------------------------- 1 | # zerodha-algo-trading 2 | A codebase to connect zerodha connect along with algo trading library to use power of algo trading with zerodha 3 | 4 |
5 | Any new code additions should go to package com.dtech.algo. This package is supposed to have 100% code coverage production ready. 6 |
7 | 8 | We started with simple MVP for fetching data from Zerodha and putting trades. When it was successful, we went ahead with 9 | building a fully working algo trading app. 10 | 11 | ### What do we want to build? 12 | 13 | We want to build a solution where users can build a strategies that can 14 | together work on different segments and make a comparison analysis in Equity, Derivatives before 15 | Putting trade. E.g. I want to buy SBIN Fut, but the best criteria to make that call is 16 | analysing open interest in the nearest In the money Put. We want to build that level of mechanism, which no one provides as of now. 17 | 18 | ### What do we have as of now? 19 | 20 | After first MVP, now we have built a server side architecture to make fully configurable strategies using 21 | different bar-series for technical analysis and different bar-series for trades. So now you can make a trade in 22 | SBIN Fut Or SBI Cash by analysing Open interest in SBIN call and puts, or PE ratio, 23 | 24 | ### What is pending? 25 | 26 | We have a huge backlog, because we want to build a market ready product. Some of them are 27 | 1. Build a usable web/mobile app that can be used by our users to build strategy 28 | 2. Integrate with zerodha websockets for now for putting trades in realtime. 29 | 3. Take the library to next level with integrating with different brokers. 30 | These are few things from the top of my mind, and the list is ever-growing!! 31 | 32 | ### Do we follow any development practices? 33 | Yes, we follow TDD, or at least we expect reasonable coverage on the code that we write 34 | We have CI setup with github actions, and the code health is tracked on it. 35 | 36 | 37 | I am looking for React/ Flutter developers to build a frontend app for this project 38 | 39 | 40 | > We welcome all sort of contributions, including your time and money!! 41 | 42 | 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zerodha-algo-trading 2 | A codebase to connect zerodha connect along with algo trading library to use power of algo trading with zerodha 3 | 4 | **IMP: Any new code additions should go to package `com.dtech.algo`. This package is supposed to have 100% code coverage production ready.** 5 | 6 | **I am looking for React/ Flutter developers to build a frontend app for this project** 7 | 8 | We started with simple MVP for fetching data from Zerodha and putting trades. When it was successful, we went ahead with 9 | building a fully working algo trading app. 10 | 11 | ### What do we want to build? 12 | 13 | We want to build a solution where users can build a strategies that can 14 | together work on different segments and make a comparison analysis in Equity, Derivatives before 15 | Putting trade. E.g. I want to buy SBIN Fut, but the best criteria to make that call is 16 | analysing open interest in the nearest In the money Put. We want to build that level of mechanism, which no one provides as of now. 17 | 18 | ### What do we have as of now? 19 | 20 | After first MVP, now we have built a server side architecture to make fully configurable strategies using 21 | different bar-series for technical analysis and different bar-series for trades. So now you can make a trade in 22 | SBIN Fut Or SBI Cash by analysing Open interest in SBIN call and puts, or PE ratio, 23 | 24 | ### What is pending? 25 | 26 | We have a huge backlog, because we want to build a market ready product. Some of them are 27 | 1. Build a usable web/mobile app that can be used by our users to build strategy 28 | 2. Integrate with zerodha websockets for now for putting trades in realtime. 29 | 3. Take the library to next level with integrating with different brokers. 30 | These are few things from the top of my mind, and the list is ever-growing!! 31 | 32 | ### Do we follow any development practices? 33 | Yes, we follow TDD, or at least we expect reasonable coverage on the code that we write 34 | We have CI setup with github actions, and the code health is tracked on it. 35 | 36 | > We welcome all sort of contributions, including your time and money!! 37 | 38 | -------------------------------------------------------------------------------- /Trendlines.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProScriptSlinger/Zerodha-Algo-Trading/0efe9b3ca2d524a66cf0d07ce789618933b510bf/Trendlines.jpg -------------------------------------------------------------------------------- /elliott/RELIANCE_OneHour.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProScriptSlinger/Zerodha-Algo-Trading/0efe9b3ca2d524a66cf0d07ce789618933b510bf/elliott/RELIANCE_OneHour.jpg -------------------------------------------------------------------------------- /elliott/SBIN_OneHour.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProScriptSlinger/Zerodha-Algo-Trading/0efe9b3ca2d524a66cf0d07ce789618933b510bf/elliott/SBIN_OneHour.jpg -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProScriptSlinger/Zerodha-Algo-Trading/0efe9b3ca2d524a66cf0d07ce789618933b510bf/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | */ 4 | pluginManagement { 5 | repositories { 6 | mavenLocal() 7 | maven { url 'https://repo.spring.io/milestone' } 8 | maven { url 'https://repo.spring.io/snapshot' } 9 | gradlePluginPortal() 10 | maven { url 'https://jitpack.io' } 11 | } 12 | // resolutionStrategy { 13 | // eachPlugin { 14 | // if (requested.id.id == 'org.springframework.boot') { 15 | // useModule("org.springframework.boot:spring-boot-gradle-plugin:${requested.version}") 16 | // } 17 | // } 18 | // } 19 | } 20 | rootProject.name = 'kitecon' 21 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/backtest/BacktestInput.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.backtest; 2 | 3 | import com.dtech.algo.strategy.config.BarSeriesConfig; 4 | import com.dtech.algo.strategy.config.StrategyConfig; 5 | import lombok.*; 6 | 7 | import java.util.List; 8 | 9 | @Data 10 | @Builder 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | public class BacktestInput { 14 | 15 | private List barSeriesConfigs; 16 | private String barSeriesName; 17 | private StrategyConfig strategyConfig; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/backtest/BacktestResult.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.backtest; 2 | 3 | import lombok.Builder; 4 | import lombok.Value; 5 | import org.ta4j.core.Position; 6 | 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | @Value 11 | @Builder 12 | public class BacktestResult { 13 | 14 | Map aggregatesResults; 15 | List tradingRecord; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/backtest/OrderRecord.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.backtest; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import org.ta4j.core.Trade.TradeType; 7 | import org.ta4j.core.Trade; 8 | 9 | import java.time.ZonedDateTime; 10 | 11 | @Data 12 | @AllArgsConstructor 13 | @Builder 14 | public class OrderRecord { 15 | 16 | /** 17 | * Type of the order 18 | */ 19 | private TradeType type; 20 | 21 | /** 22 | * The index the order was executed 23 | */ 24 | private int index; 25 | 26 | /** 27 | * The pricePerAsset for the order 28 | */ 29 | private Double pricePerAsset; 30 | 31 | /** 32 | * The net price for the order, net transaction costs 33 | */ 34 | private Double netPrice; 35 | 36 | /** 37 | * The amount to be (or that was) ordered 38 | */ 39 | private Double amount; 40 | 41 | /** 42 | * Cost of executing the order 43 | */ 44 | private Double cost; 45 | 46 | private ZonedDateTime dateTime; 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/backtest/TradeRecord.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.backtest; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | 7 | @Builder 8 | @AllArgsConstructor 9 | @Data 10 | public class TradeRecord { 11 | 12 | /** 13 | * The type of the entry order 14 | */ 15 | private Double profit; 16 | 17 | /** 18 | * The entry order 19 | */ 20 | private OrderRecord entry; 21 | 22 | /** 23 | * The exit order 24 | */ 25 | private OrderRecord exit; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/config/AlgoTradeMapperConfig.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.config; 2 | 3 | import org.mapstruct.MapperConfig; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | @MapperConfig(componentModel = "spring") 7 | @Configuration 8 | public class AlgoTradeMapperConfig { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/config/CachingConfig.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.config; 2 | 3 | import org.springframework.cache.CacheManager; 4 | import org.springframework.cache.annotation.EnableCaching; 5 | import org.springframework.cache.concurrent.ConcurrentMapCacheManager; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | @Configuration 10 | @EnableCaching 11 | public class CachingConfig { 12 | 13 | @Bean 14 | public CacheManager cacheManager() { 15 | return new ConcurrentMapCacheManager("barSeries"); 16 | } 17 | } -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/controller/BacktestController.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.controller; 2 | 3 | import com.dtech.algo.backtest.BackTestingHandlerJson; 4 | import com.dtech.algo.backtest.BacktestInput; 5 | import com.dtech.algo.backtest.BacktestResult; 6 | import com.dtech.algo.exception.StrategyException; 7 | import lombok.RequiredArgsConstructor; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.web.bind.annotation.PostMapping; 10 | import org.springframework.web.bind.annotation.RequestBody; 11 | import org.springframework.web.bind.annotation.RestController; 12 | 13 | @RestController 14 | @RequiredArgsConstructor(onConstructor = @__(@Autowired)) 15 | public class BacktestController { 16 | 17 | private final BackTestingHandlerJson backTestingHandlerJson; 18 | 19 | @PostMapping("/backtest") 20 | public BacktestResult runBacktest(@RequestBody BacktestInput backtestInput) throws StrategyException { 21 | return backTestingHandlerJson.execute(backtestInput); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/controller/MetadataController.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.controller; 2 | 3 | import com.dtech.algo.backtest.BackTestingHandlerJson; 4 | import com.dtech.algo.backtest.BacktestInput; 5 | import com.dtech.algo.backtest.BacktestResult; 6 | import com.dtech.algo.exception.StrategyException; 7 | import com.dtech.algo.indicators.IndicatorInfo; 8 | import com.dtech.algo.indicators.IndicatorRegistry; 9 | import com.dtech.algo.rules.RuleInfo; 10 | import com.dtech.algo.rules.RuleRegistry; 11 | import com.dtech.algo.strategy.config.IndicatorConfig; 12 | import lombok.RequiredArgsConstructor; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.web.bind.annotation.GetMapping; 15 | import org.springframework.web.bind.annotation.PostMapping; 16 | import org.springframework.web.bind.annotation.RequestBody; 17 | import org.springframework.web.bind.annotation.RestController; 18 | 19 | import java.util.Map; 20 | import java.util.stream.Collectors; 21 | 22 | @RestController 23 | @RequiredArgsConstructor(onConstructor = @__(@Autowired)) 24 | public class MetadataController { 25 | 26 | private final IndicatorRegistry indicatorRegistry; 27 | private final RuleRegistry ruleRegistry; 28 | 29 | @GetMapping("/meta/indicator-detail") 30 | public Map getIndicatorDetails() throws StrategyException { 31 | return indicatorRegistry.getAllObjectNames().stream() 32 | .collect(Collectors.toMap(name -> name, name -> indicatorRegistry.getObjectInfo(name))); 33 | } 34 | 35 | @GetMapping("/meta/rule-detail") 36 | public Map getRuleDetails() throws StrategyException { 37 | return ruleRegistry.getAllObjectNames().stream() 38 | .collect(Collectors.toMap(name -> name, name -> ruleRegistry.getObjectInfo(name))); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/entities/StrategyEntity.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.entities; 2 | 3 | import com.dtech.kitecon.data.Instrument; 4 | 5 | import jakarta.persistence.*; 6 | 7 | @Entity(name = "strategy") 8 | public class StrategyEntity { 9 | @Id 10 | @GeneratedValue(strategy = GenerationType.AUTO) 11 | private Integer id; 12 | 13 | @Column 14 | private String criteria; 15 | 16 | @Column 17 | private String barSeriesConfig; 18 | 19 | @Column(name = "order_id") 20 | private String order; 21 | 22 | @JoinColumn 23 | private String barSeriesName; 24 | 25 | @Column 26 | private Boolean enabled; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/exception/StrategyException.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.exception; 2 | 3 | public class StrategyException extends Exception { 4 | 5 | public StrategyException() { 6 | super(); 7 | } 8 | 9 | public StrategyException(String message) { 10 | super(message); 11 | } 12 | 13 | public StrategyException(String message, Throwable cause) { 14 | super(message, cause); 15 | } 16 | 17 | public StrategyException(Throwable cause) { 18 | super(cause); 19 | } 20 | 21 | protected StrategyException(String message, Throwable cause, boolean enableSuppression, 22 | boolean writableStackTrace) { 23 | super(message, cause, enableSuppression, writableStackTrace); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/indicators/IndicatorConstructor.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.indicators; 2 | 3 | import com.dtech.algo.registry.common.ConstructorArgs; 4 | import java.util.List; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | import lombok.NoArgsConstructor; 10 | import lombok.ToString; 11 | 12 | @Builder 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | @EqualsAndHashCode 16 | @Data 17 | @ToString 18 | public class IndicatorConstructor { 19 | 20 | private List args; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/indicators/IndicatorInfo.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.indicators; 2 | 3 | import java.util.List; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.ToString; 8 | 9 | @Data 10 | @AllArgsConstructor 11 | @Builder 12 | @ToString 13 | public class IndicatorInfo { 14 | private String name; 15 | private List constructors; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/indicators/IndicatorRegistry.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.indicators; 2 | 3 | import com.dtech.algo.registry.common.BaseRegistry; 4 | import java.lang.reflect.Constructor; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | import java.util.Set; 8 | import java.util.stream.Collectors; 9 | 10 | import org.reflections.Reflections; 11 | import org.reflections.scanners.SubTypesScanner; 12 | import org.springframework.stereotype.Service; 13 | import org.ta4j.core.Indicator; 14 | import org.ta4j.core.indicators.AbstractIndicator; 15 | import org.ta4j.core.indicators.RSIIndicator; 16 | import org.ta4j.core.indicators.SMAIndicator; 17 | import org.ta4j.core.indicators.candles.DojiIndicator; 18 | import org.ta4j.core.indicators.helpers.ClosePriceIndicator; 19 | import org.ta4j.core.indicators.helpers.ConstantIndicator; 20 | import org.ta4j.core.indicators.range.OpeningRangeLow; 21 | 22 | import jakarta.annotation.PostConstruct; 23 | 24 | 25 | @Service 26 | public class IndicatorRegistry extends BaseRegistry { 27 | 28 | public static Set> getClassesFromPackage(String packageName) { 29 | Reflections reflections = new Reflections(packageName, new SubTypesScanner(false)); 30 | return reflections.getSubTypesOf(AbstractIndicator.class); 31 | } 32 | 33 | @PostConstruct 34 | public void initialize() { 35 | Set> indicators = getClassesFromPackage("org.ta4j.core.indicators"); 36 | indicators.stream().forEach(this::add); 37 | } 38 | 39 | public Class getIndicatorClass(String name) { 40 | return registryMap.get(name); 41 | } 42 | 43 | public IndicatorInfo getObjectInfo(String name) { 44 | Class aClass = getIndicatorClass(name); 45 | String className = camelToLower(aClass.getSimpleName()); 46 | Constructor[] constructors = aClass.getConstructors(); 47 | List indicatorConstructors = Arrays.stream(constructors) 48 | .map(constructor -> IndicatorConstructor.builder() 49 | .args(mapConstructorArgs(constructor)) 50 | .build()) 51 | .collect(Collectors.toList()); 52 | return IndicatorInfo.builder() 53 | .constructors(indicatorConstructors) 54 | .name(className) 55 | .build(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/mapper/GenericMapper.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.mapper; 2 | 3 | public interface GenericMapper{ 4 | S reverse(T source); 5 | T map(S destination); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/mapper/KiteTickToDataTickMapper.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.mapper; 2 | 3 | import com.dtech.algo.runner.candle.DataTick; 4 | import com.zerodhatech.models.Tick; 5 | import org.mapstruct.Mapper; 6 | 7 | @Mapper 8 | public interface KiteTickToDataTickMapper extends GenericMapper { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/registry/common/BaseRegistry.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.registry.common; 2 | 3 | import com.dtech.algo.indicators.IndicatorInfo; 4 | import com.google.common.base.CaseFormat; 5 | 6 | import java.lang.reflect.Constructor; 7 | import java.lang.reflect.Parameter; 8 | import java.util.*; 9 | import java.util.stream.Collectors; 10 | 11 | import org.jetbrains.annotations.Nullable; 12 | import org.ta4j.core.num.DoubleNum; 13 | import org.ta4j.core.num.Num; 14 | 15 | public abstract class BaseRegistry { 16 | 17 | protected final Map> registryMap = new HashMap<>(); 18 | 19 | public abstract I getObjectInfo(String name); 20 | 21 | @Nullable 22 | protected static String camelToLower(String simpleName) { 23 | return CaseFormat.UPPER_CAMEL.converterTo(CaseFormat.LOWER_HYPHEN) 24 | .convert(simpleName); 25 | } 26 | 27 | protected String getTypeName(Parameter parameter) { 28 | Class type = parameter.getType(); 29 | if (type.isPrimitive()) { 30 | return type.getName(); 31 | } else if (type.isAssignableFrom(DoubleNum.class)) { 32 | return BaseRegistry.camelToLower(Num.class.getSimpleName()); 33 | } 34 | { 35 | return BaseRegistry.camelToLower(type.getSimpleName()); 36 | } 37 | } 38 | 39 | protected List getValues(Parameter parameter) { 40 | Class type = parameter.getType(); 41 | if (type.isEnum()) { 42 | Enum[] enumConstants = (Enum[]) type.getEnumConstants(); 43 | return Arrays.stream(enumConstants).map(Enum::name) 44 | .collect(Collectors.toList()); 45 | } 46 | return Collections.emptyList(); 47 | } 48 | 49 | protected List mapConstructorArgs(Constructor constructor) { 50 | return Arrays.stream(constructor.getParameters()) 51 | .map(parameter -> ConstructorArgs.builder() 52 | .type(getTypeName(parameter)) 53 | .name(parameter.getName()) 54 | .values(getValues(parameter)) 55 | .build()) 56 | .collect(Collectors.toList()); 57 | } 58 | 59 | protected void add(Class aClass) { 60 | String simpleName = aClass.getSimpleName(); 61 | String key = camelToLower(simpleName); 62 | registryMap.put(key, aClass); 63 | } 64 | 65 | public Collection getAllObjectNames() { 66 | return registryMap.keySet(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/registry/common/ConstructorArgs.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.registry.common; 2 | 3 | import java.util.List; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.Getter; 8 | import lombok.Setter; 9 | import lombok.ToString; 10 | 11 | @AllArgsConstructor 12 | @Getter 13 | @Setter 14 | @EqualsAndHashCode(exclude = "name") 15 | @Builder 16 | @ToString 17 | public class ConstructorArgs { 18 | private String type; 19 | private String name; 20 | private List values; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/rules/RuleConstructor.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.rules; 2 | 3 | import com.dtech.algo.registry.common.ConstructorArgs; 4 | import java.util.List; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | import lombok.NoArgsConstructor; 10 | import lombok.ToString; 11 | 12 | @Builder 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | @EqualsAndHashCode 16 | @Data 17 | @ToString 18 | public class RuleConstructor { 19 | 20 | private List args; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/rules/RuleInfo.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.rules; 2 | 3 | import com.dtech.algo.indicators.IndicatorConstructor; 4 | import java.util.List; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.ToString; 9 | 10 | @Data 11 | @AllArgsConstructor 12 | @Builder 13 | @ToString 14 | public class RuleInfo { 15 | private String name; 16 | private List constructors; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/rules/RuleRegistry.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.rules; 2 | 3 | import com.dtech.algo.registry.common.BaseRegistry; 4 | import org.reflections.Reflections; 5 | import org.reflections.scanners.SubTypesScanner; 6 | import org.springframework.stereotype.Service; 7 | import org.ta4j.core.Rule; 8 | import org.ta4j.core.indicators.AbstractIndicator; 9 | import org.ta4j.core.rules.*; 10 | 11 | import jakarta.annotation.PostConstruct; 12 | import java.lang.reflect.Constructor; 13 | import java.util.Arrays; 14 | import java.util.List; 15 | import java.util.Set; 16 | import java.util.stream.Collectors; 17 | 18 | 19 | @Service 20 | public class RuleRegistry extends BaseRegistry { 21 | 22 | public static Set> getClassesFromPackage(String packageName) { 23 | Reflections reflections = new Reflections(packageName, new SubTypesScanner(false)); 24 | return reflections.getSubTypesOf(AbstractRule.class); 25 | } 26 | 27 | @PostConstruct 28 | public void initialise() { 29 | Set> indicators = getClassesFromPackage("org.ta4j.core.rules"); 30 | indicators.stream().forEach(this::add); 31 | } 32 | 33 | public Class getRuleClass(String name) { 34 | return registryMap.get(name); 35 | } 36 | 37 | public RuleInfo getObjectInfo(String name) { 38 | Class aClass = getRuleClass(name); 39 | String className = camelToLower(aClass.getSimpleName()); 40 | Constructor[] constructors = aClass.getConstructors(); 41 | List indicatorConstructors = Arrays.stream(constructors) 42 | .map(constructor -> RuleConstructor.builder() 43 | .args(mapConstructorArgs(constructor)) 44 | .build()) 45 | .collect(Collectors.toList()); 46 | return RuleInfo.builder() 47 | .constructors(indicatorConstructors) 48 | .name(className) 49 | .build(); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/runner/candle/DataTick.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.runner.candle; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import com.zerodhatech.models.Depth; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Date; 10 | import java.util.Map; 11 | 12 | @Getter 13 | @Setter 14 | public class DataTick { 15 | 16 | private String mode; 17 | private boolean tradable; 18 | private long instrumentToken; 19 | private double lastTradedPrice; 20 | private double highPrice; 21 | private double lowPrice; 22 | private double openPrice; 23 | private double closePrice; 24 | private double change; 25 | private double lastTradedQuantity; 26 | private double averageTradePrice; 27 | private double volumeTradedToday; 28 | private double totalBuyQuantity; 29 | private double totalSellQuantity; 30 | private Date lastTradedTime; 31 | private double oi; 32 | private double oiDayHigh; 33 | private double oiDayLow; 34 | private Date tickTimestamp; 35 | private Map> depth; 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/runner/candle/LatestBarSeriesProvider.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.runner.candle; 2 | 3 | 4 | import com.dtech.algo.exception.StrategyException; 5 | import com.dtech.algo.series.IntervalBarSeries; 6 | import com.dtech.algo.strategy.builder.ifc.BarSeriesLoader; 7 | import com.dtech.algo.strategy.config.BarSeriesConfig; 8 | import lombok.RequiredArgsConstructor; 9 | import org.springframework.cache.annotation.Cacheable; 10 | import org.springframework.stereotype.Component; 11 | 12 | import java.time.LocalDate; 13 | import java.time.Period; 14 | 15 | /** 16 | * Expected to cache bar series from the request, The request will be similar to that comes for strategy. 17 | */ 18 | @RequiredArgsConstructor 19 | @Component 20 | public class LatestBarSeriesProvider implements UpdatableBarSeriesLoader { 21 | 22 | private final BarSeriesLoader delegate; 23 | 24 | @Cacheable(cacheNames = "barSeries") 25 | public IntervalBarSeries loadBarSeries(BarSeriesConfig barSeriesConfig) throws StrategyException { 26 | try { 27 | BarSeriesConfig refSeries = barSeriesConfig.clone(); 28 | refSeries.setStartDate(refSeries.getStartDate()); 29 | refSeries.setEndDate(refSeries.getEndDate()); 30 | return delegate.loadBarSeries(refSeries); 31 | } catch (CloneNotSupportedException e) { 32 | throw new StrategyException(e); 33 | } 34 | } 35 | 36 | 37 | @Override 38 | public void updateBarSeries(DataTick tick) { 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/runner/candle/UpdatableBarSeriesLoader.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.runner.candle; 2 | 3 | import com.dtech.algo.strategy.builder.ifc.BarSeriesLoader; 4 | 5 | public interface UpdatableBarSeriesLoader extends BarSeriesLoader { 6 | void updateBarSeries(DataTick tick); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/series/Exchange.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.series; 2 | 3 | public enum Exchange { 4 | NSE, 5 | BSE, 6 | NFO, 7 | CDS, 8 | BCD, 9 | MCX 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/series/InstrumentType.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.series; 2 | 3 | public enum InstrumentType { 4 | EQ, 5 | FUT, 6 | CE, 7 | PE 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/series/Interval.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.series; 2 | 3 | import lombok.Getter; 4 | import lombok.RequiredArgsConstructor; 5 | 6 | @RequiredArgsConstructor 7 | @Getter 8 | public enum Interval { 9 | OneMinute("minute", 60), 10 | ThreeMinute("3minute", 3*60), 11 | FiveMinute("5minute", 5*60), 12 | FifteenMinute("15minute", 15*60), 13 | ThirtyMinute("30minute", 30 * 60), 14 | OneHour("60minute", 60 * 60), 15 | FourHours("4hour", 4 * 60 * 60), 16 | Day("day", 24 * 60 * 60), 17 | Week("week", 7 * 24 * 60 * 60); 18 | // Month("month", 30 * 24 * 60 * 60); 19 | 20 | private final String kiteKey; 21 | private final int offset; 22 | 23 | public Interval getParent() { 24 | if( this == OneMinute || this == ThreeMinute || this == FiveMinute) { 25 | return FifteenMinute; 26 | } else if (this == FifteenMinute) { 27 | return OneHour; 28 | } else if(this == OneHour) { 29 | return Day; 30 | } else return Week; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/series/IntervalBarSeries.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.series; 2 | 3 | import org.ta4j.core.BarSeries; 4 | 5 | import java.time.ZonedDateTime; 6 | 7 | public interface IntervalBarSeries extends BarSeries { 8 | Interval getInterval(); 9 | SeriesType getSeriesType(); 10 | String getInstrument(); 11 | void addBarWithTimeValidation(ZonedDateTime endTime, Number openPrice, Number highPrice, Number lowPrice, 12 | Number closePrice, Number volume); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/series/SeriesType.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.series; 2 | 3 | public enum SeriesType { 4 | EQUITY, 5 | Future, 6 | PutPlus1, 7 | PutPlus2, 8 | PutPlus3, 9 | PutMinus1, 10 | PutMinus2, 11 | PutMinus3, 12 | CallPlus1, 13 | CallPlus2, 14 | CallPlus3, 15 | CallMinus1, 16 | CallMinus2, 17 | CallMinus3; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/service/StrategyService.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.service; 2 | 3 | public class StrategyService { 4 | 5 | 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/TradeStrategy.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy; 2 | 3 | import com.dtech.kitecon.strategy.TradeDirection; 4 | import org.ta4j.core.Strategy; 5 | 6 | public interface TradeStrategy extends Strategy { 7 | public TradeDirection getDirection(); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/TradeStrategyImpl.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy; 2 | 3 | import com.dtech.kitecon.strategy.TradeDirection; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | import lombok.experimental.Delegate; 9 | import org.ta4j.core.Strategy; 10 | 11 | @Data 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | @Builder 15 | public class TradeStrategyImpl implements TradeStrategy { 16 | 17 | @Delegate 18 | private Strategy delegate; 19 | 20 | private TradeDirection direction; 21 | 22 | private String strategyName; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/builder/StrategyBuilderIfc.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.builder; 2 | 3 | import com.dtech.algo.exception.StrategyException; 4 | import com.dtech.algo.strategy.TradeStrategy; 5 | import com.dtech.algo.strategy.config.StrategyConfig; 6 | 7 | public interface StrategyBuilderIfc { 8 | TradeStrategy buildStrategy(StrategyConfig strategyConfig) throws StrategyException; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/builder/cache/BarSeriesCache.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.builder.cache; 2 | 3 | import com.dtech.algo.series.IntervalBarSeries; 4 | import org.springframework.stereotype.Component; 5 | import org.ta4j.core.BarSeries; 6 | 7 | @Component 8 | public class BarSeriesCache extends ThreadLocalCache { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/builder/cache/ConstantsCache.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.builder.cache; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | @Component 6 | public class ConstantsCache extends ThreadLocalCache { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/builder/cache/IndicatorCache.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.builder.cache; 2 | 3 | import org.springframework.stereotype.Component; 4 | import org.ta4j.core.Indicator; 5 | 6 | @Component 7 | public class IndicatorCache extends ThreadLocalCache { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/builder/cache/RuleCache.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.builder.cache; 2 | 3 | import org.springframework.stereotype.Component; 4 | import org.ta4j.core.Indicator; 5 | import org.ta4j.core.Rule; 6 | 7 | @Component 8 | public class RuleCache extends ThreadLocalCache { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/builder/cache/ThreadLocalCache.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.builder.cache; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import org.springframework.stereotype.Component; 6 | 7 | @Component 8 | public class ThreadLocalCache { 9 | 10 | private ThreadLocal> threadLocal = new ThreadLocal<>(); 11 | 12 | private Map getThreadLocal() { 13 | if(threadLocal.get() == null) { 14 | threadLocal.set(new HashMap<>()); 15 | } 16 | return (Map) threadLocal.get(); 17 | } 18 | 19 | public void reset() { 20 | threadLocal.remove(); 21 | } 22 | 23 | public V get(K key) { 24 | return getThreadLocal().get(key); 25 | } 26 | 27 | public void put(K key, V value) { 28 | getThreadLocal().put(key, value); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/builder/cache/TradingRecordCache.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.builder.cache; 2 | 3 | import org.springframework.stereotype.Component; 4 | import org.ta4j.core.Rule; 5 | 6 | @Component 7 | public class TradingRecordCache extends ThreadLocalCache { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/builder/ifc/BarSeriesLoader.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.builder.ifc; 2 | 3 | import com.dtech.algo.exception.StrategyException; 4 | import com.dtech.algo.series.IntervalBarSeries; 5 | import com.dtech.algo.strategy.config.BarSeriesConfig; 6 | 7 | public interface BarSeriesLoader { 8 | IntervalBarSeries loadBarSeries(BarSeriesConfig barSeriesConfig) throws StrategyException; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/builder/ifc/ConstantsLoader.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.builder.ifc; 2 | 3 | public interface ConstantsLoader { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/builder/ifc/IndicatorBuilder.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.builder.ifc; 2 | 3 | import com.dtech.algo.exception.StrategyException; 4 | import com.dtech.algo.strategy.builder.cache.ConstantsCache; 5 | import com.dtech.algo.strategy.builder.cache.IndicatorCache; 6 | import com.dtech.algo.strategy.config.IndicatorConfig; 7 | import java.lang.reflect.InvocationTargetException; 8 | import org.ta4j.core.Indicator; 9 | 10 | public interface IndicatorBuilder { 11 | Indicator getIndicator(IndicatorConfig config) throws StrategyException; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/builder/ifc/RuleBuilder.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.builder.ifc; 2 | 3 | import com.dtech.algo.exception.StrategyException; 4 | import com.dtech.algo.strategy.builder.cache.ConstantsCache; 5 | import com.dtech.algo.strategy.config.RuleConfig; 6 | import org.ta4j.core.Rule; 7 | 8 | public interface RuleBuilder { 9 | Rule getRule(RuleConfig config) throws StrategyException; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/config/BarSeriesConfig.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.config; 2 | 3 | import com.dtech.algo.series.Exchange; 4 | import com.dtech.algo.series.InstrumentType; 5 | import com.dtech.algo.series.Interval; 6 | import com.dtech.algo.series.SeriesType; 7 | import lombok.*; 8 | 9 | import java.time.LocalDate; 10 | 11 | @Data 12 | @AllArgsConstructor 13 | @Builder 14 | @ToString 15 | @NoArgsConstructor 16 | public class BarSeriesConfig implements Cloneable { 17 | @EqualsAndHashCode.Include 18 | private Interval interval; 19 | private SeriesType seriesType; 20 | private InstrumentType instrumentType; 21 | private Exchange exchange; 22 | @EqualsAndHashCode.Include 23 | private String instrument; 24 | private String name; 25 | private LocalDate startDate; 26 | private LocalDate endDate; 27 | 28 | public BarSeriesConfig clone() throws CloneNotSupportedException { 29 | return (BarSeriesConfig) super.clone(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/config/FollowUpRuleConfig.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.config; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Builder.Default; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | import lombok.ToString; 9 | 10 | 11 | @Data 12 | @AllArgsConstructor 13 | @Builder 14 | @ToString 15 | public class FollowUpRuleConfig { 16 | @Default 17 | private FollowUpRuleType followUpRuleType = FollowUpRuleType.None; 18 | @Default 19 | private RuleConfig followUpRule = null; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/config/FollowUpRuleType.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.config; 2 | 3 | public enum FollowUpRuleType { 4 | And, 5 | Or, 6 | Xor, 7 | None 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/config/IndicatorConfig.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.config; 2 | 3 | import java.util.List; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | import lombok.ToString; 9 | 10 | @Data 11 | @AllArgsConstructor 12 | @Builder 13 | @NoArgsConstructor 14 | @ToString 15 | public class IndicatorConfig { 16 | private String key; 17 | private String indicatorName; 18 | private List inputs; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/config/IndicatorInput.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.config; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import lombok.ToString; 8 | 9 | @Data 10 | @AllArgsConstructor 11 | @Builder 12 | @NoArgsConstructor 13 | @ToString 14 | public class IndicatorInput { 15 | private String name; 16 | private IndicatorInputType type; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/config/IndicatorInputType.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.config; 2 | 3 | public enum IndicatorInputType { 4 | BarSeries, 5 | Indicator, 6 | Number, 7 | TimeLevel, 8 | Integer; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/config/RuleConfig.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.config; 2 | 3 | import java.util.List; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | import lombok.ToString; 9 | 10 | @Data 11 | @AllArgsConstructor 12 | @Builder 13 | @NoArgsConstructor 14 | @ToString 15 | public class RuleConfig { 16 | private String key; 17 | private String ruleName; 18 | private List inputs; 19 | private List followUpRules; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/config/RuleInput.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.config; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import lombok.ToString; 8 | 9 | @Data 10 | @AllArgsConstructor 11 | @Builder 12 | @NoArgsConstructor 13 | @ToString 14 | public class RuleInput { 15 | private String name; 16 | private RuleInputType type; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/config/RuleInputType.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.config; 2 | 3 | import org.ta4j.core.Indicator; 4 | 5 | public enum RuleInputType { 6 | Indicator, 7 | Rule, 8 | Number, 9 | TradingRecord 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/config/RunnerConfig.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.config; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.util.List; 9 | 10 | @Data 11 | @Builder 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | public class RunnerConfig { 15 | private List barSeriesConfigs; 16 | private String barSeriesName; 17 | private StrategyConfig strategyConfig; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/config/StrategyConfig.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.config; 2 | 3 | import com.dtech.kitecon.strategy.TradeDirection; 4 | import java.util.List; 5 | import java.util.Map; 6 | 7 | import lombok.AllArgsConstructor; 8 | import lombok.Builder; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | import lombok.ToString; 12 | 13 | @Data 14 | @AllArgsConstructor 15 | @Builder 16 | @ToString 17 | @NoArgsConstructor 18 | public class StrategyConfig { 19 | // Strategy name 20 | private String strategyName; 21 | 22 | // Trade direction - buy/ sell 23 | private TradeDirection direction; 24 | 25 | private Map constants; 26 | 27 | private List indicators; 28 | 29 | private List rules; 30 | 31 | private List entry; 32 | private List exit; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/sync/CandleSyncExecutor.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.sync; 2 | 3 | import com.dtech.kitecon.repository.CandleRepository; 4 | import com.dtech.kitecon.service.CandleFacade; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.stereotype.Service; 7 | 8 | import jakarta.annotation.PostConstruct; 9 | import jakarta.annotation.PreDestroy; 10 | import java.util.concurrent.ExecutorService; 11 | import java.util.concurrent.Executors; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | @Service 15 | @RequiredArgsConstructor 16 | public class CandleSyncExecutor { 17 | 18 | private final CandleRepository candleRepository; 19 | private final CandleFacade candleFacade; 20 | 21 | private ExecutorService executorService; 22 | 23 | @PostConstruct 24 | public void initialize() { 25 | executorService = Executors.newFixedThreadPool(1); 26 | } 27 | 28 | public void submit(CandleSyncToken job) { 29 | executorService.submit(getSyncJob(job)); 30 | } 31 | 32 | protected CandleSyncJob getSyncJob(CandleSyncToken job) { 33 | return new CandleSyncJob(candleRepository, candleFacade, job); 34 | } 35 | 36 | @PreDestroy 37 | public void shutdown() throws InterruptedException { 38 | executorService.shutdown(); 39 | executorService.awaitTermination(5, TimeUnit.MINUTES); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/sync/CandleSyncJob.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.sync; 2 | 3 | import com.dtech.algo.series.Interval; 4 | import com.dtech.kitecon.data.Candle; 5 | import com.dtech.kitecon.data.Instrument; 6 | import com.dtech.kitecon.repository.CandleRepository; 7 | import com.dtech.kitecon.service.CandleFacade; 8 | import lombok.RequiredArgsConstructor; 9 | import org.ta4j.core.BaseBar; 10 | 11 | import java.util.Collections; 12 | 13 | @RequiredArgsConstructor 14 | public class CandleSyncJob implements Runnable { 15 | 16 | private final CandleRepository candleRepository; 17 | private final CandleFacade candleFacade; 18 | 19 | private final CandleSyncToken syncToken; 20 | 21 | @Override 22 | public void run() { 23 | Long instrument = Long.valueOf(syncToken.getInstrument()); 24 | insertNewCandle(instrument, syncToken.getBaseBar(), syncToken.getInterval()); 25 | } 26 | 27 | protected void insertNewCandle(Long instrument, BaseBar baseBar, Interval interval) { 28 | Instrument ins = new Instrument(); 29 | ins.setInstrumentToken(instrument); 30 | Candle candle = candleFacade.buildCandle(ins, baseBar, interval); 31 | candleRepository.saveAll(Collections.singletonList(candle)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/sync/CandleSyncToken.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.sync; 2 | 3 | import com.dtech.algo.series.Interval; 4 | import lombok.Getter; 5 | import lombok.RequiredArgsConstructor; 6 | import org.ta4j.core.BaseBar; 7 | 8 | @RequiredArgsConstructor 9 | @Getter 10 | public class CandleSyncToken { 11 | private final BaseBar baseBar; 12 | private final String instrument; 13 | private final Interval interval; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/algo/strategy/units/AbstractObjectBuilder.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.units; 2 | 3 | import com.dtech.algo.exception.StrategyException; 4 | import com.dtech.algo.strategy.builder.cache.ThreadLocalCache; 5 | import com.dtech.algo.strategy.config.IndicatorConfig; 6 | 7 | import java.lang.reflect.InvocationTargetException; 8 | import java.util.List; 9 | import java.util.function.Function; 10 | 11 | public abstract class AbstractObjectBuilder { 12 | protected T[] resolveParameters(List inputs, Function function) { 13 | T[] params = (T[]) new Object[inputs.size()]; 14 | for (int i = 0, inputsSize = inputs.size(); i < inputsSize; i++) { 15 | I input = inputs.get(i); 16 | params[i] = function.apply(input); 17 | } 18 | return params; 19 | } 20 | 21 | protected Class[] resolveClasses(List inputs, Function function) { 22 | Class[] params = new Class[inputs.size()]; 23 | for (int i = 0, inputsSize = inputs.size(); i < inputsSize; i++) { 24 | I input = inputs.get(i); 25 | params[i] = function.apply(input); 26 | } 27 | return params; 28 | } 29 | 30 | 31 | protected T getObjectInternal(String key, C config, ThreadLocalCache objectCache) throws StrategyException { 32 | T cachedIndicator = objectCache.get(key); 33 | if (cachedIndicator != null) { 34 | return cachedIndicator; 35 | } 36 | try { 37 | T value = buildObject(config); 38 | objectCache.put(key, value); 39 | return value; 40 | } catch (Exception ex) { 41 | throw new StrategyException("Error occurred while constructing an indicator", ex); 42 | } 43 | } 44 | 45 | protected abstract T buildObject(C config) throws StrategyException; 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/KiteconApplication.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.autoconfigure.domain.EntityScan; 6 | import org.springframework.context.annotation.ComponentScan; 7 | import org.springframework.context.annotation.ComponentScan.Filter; 8 | import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 9 | import org.springframework.scheduling.annotation.EnableAsync; 10 | 11 | @SpringBootApplication 12 | @ComponentScan(basePackages = {"com.dtech"}) 13 | @EnableJpaRepositories(basePackages = {"com.dtech"}) 14 | @EntityScan(basePackages = {"com.dtech"}) 15 | public class KiteconApplication { 16 | 17 | public static void main(String[] args) { 18 | SpringApplication.run(KiteconApplication.class, args); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/config/HistoricalDateLimit.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.config; 2 | 3 | import com.dtech.algo.series.Interval; 4 | import com.dtech.kitecon.historical.limits.LimitsKey; 5 | import java.time.Duration; 6 | import java.time.temporal.ChronoUnit; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | import jakarta.annotation.PostConstruct; 10 | import org.springframework.context.annotation.Configuration; 11 | 12 | @Configuration 13 | public class HistoricalDateLimit { 14 | 15 | public int getDuration(String exchange, Interval interval) { 16 | int offset = interval.getOffset(); 17 | if(offset <= 60) { 18 | return durationAsInt(60); 19 | } else if (offset < 60*15) { 20 | return durationAsInt(90); 21 | } else if (offset < 60*60) { 22 | return durationAsInt(180); 23 | } else if (offset < 24 * 60 * 60) { 24 | return durationAsInt(365); 25 | } else { 26 | return durationAsInt(2000); 27 | } 28 | } 29 | 30 | private static int durationAsInt(int twoHundred) { 31 | return Long.valueOf(Duration.of(twoHundred, ChronoUnit.DAYS).toDays()).intValue(); 32 | } 33 | 34 | public int getTotalAvailableDuration(String exchange, Interval interval) { 35 | int offset = interval.getOffset(); 36 | if(offset <= 60) { 37 | return durationAsInt(60); 38 | } else if (offset < 60*15) { 39 | return durationAsInt(90); 40 | } else if (offset < 60*60) { 41 | return durationAsInt(180); 42 | } else if (offset < 24 * 60 * 60) { 43 | return durationAsInt(365); 44 | } else { 45 | return durationAsInt(2000); 46 | } 47 | } 48 | 49 | public int getScreenerDuration(String exchange, Interval interval) { 50 | int offset = interval.getOffset(); 51 | if(offset <= 60) { 52 | return durationAsInt(15); 53 | } else if (offset < 60*15) { 54 | return durationAsInt(30); 55 | } else if (offset < 60*60) { 56 | return durationAsInt(60); 57 | } else if (offset < 24 * 60 * 60) { 58 | return durationAsInt(365); 59 | } else { 60 | return durationAsInt(2000); 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/config/KiteConnectConfig.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.config; 2 | 3 | import com.zerodhatech.kiteconnect.KiteConnect; 4 | import com.zerodhatech.kiteconnect.kitehttp.exceptions.KiteException; 5 | import com.zerodhatech.models.User; 6 | import java.io.IOException; 7 | import lombok.Getter; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.stereotype.Component; 11 | 12 | @Component 13 | @Getter 14 | public class KiteConnectConfig { 15 | 16 | @Value("${kite.api.key}") 17 | private String apiKey; 18 | 19 | @Value("${kite.api.user}") 20 | private String userId; 21 | 22 | @Value("${kite.api.secret}") 23 | private String secret; 24 | 25 | private KiteConnect kiteConnect; 26 | 27 | public final void initialize(String requestToken) throws KiteException, IOException { 28 | this.kiteConnect = new KiteConnect(apiKey); 29 | 30 | //If you wish to enable debug logs send true in the constructor, this will log request and response. 31 | //KiteConnect kiteConnect = new KiteConnect("xxxxyyyyzzzz", true); 32 | 33 | // If you wish to set proxy then pass proxy as a second parameter in the constructor with api_key. syntax:- new KiteConnect("xxxxxxyyyyyzzz", proxy). 34 | //KiteConnect kiteConnect = new KiteConnect("xxxxyyyyzzzz", userProxy, false); 35 | 36 | // Set userId 37 | kiteConnect.setUserId(userId); 38 | 39 | // Get login url 40 | String url = kiteConnect.getLoginURL(); 41 | 42 | User user = kiteConnect.generateSession(requestToken, secret); 43 | kiteConnect.setAccessToken(user.accessToken); 44 | kiteConnect.setPublicToken(user.publicToken); 45 | } 46 | 47 | @Bean 48 | public KiteConnect getKiteConnect() { 49 | return kiteConnect; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/controller/BarSeriesHelper.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.controller; 2 | 3 | import com.dtech.algo.exception.StrategyException; 4 | import com.dtech.algo.runner.candle.LatestBarSeriesProvider; 5 | import com.dtech.algo.series.Exchange; 6 | import com.dtech.algo.series.InstrumentType; 7 | import com.dtech.algo.series.Interval; 8 | import com.dtech.algo.series.IntervalBarSeries; 9 | import com.dtech.algo.strategy.config.BarSeriesConfig; 10 | import com.dtech.kitecon.config.HistoricalDateLimit; 11 | import lombok.RequiredArgsConstructor; 12 | import org.springframework.stereotype.Service; 13 | 14 | import java.time.LocalDate; 15 | 16 | @Service 17 | @RequiredArgsConstructor 18 | public class BarSeriesHelper { 19 | private final HistoricalDateLimit historicalDateLimit; 20 | private final LatestBarSeriesProvider barSeriesLoader; 21 | 22 | BarSeriesConfig createBarSeriesConfig(String symbol, String timeframe) { 23 | Interval interval = Interval.valueOf(timeframe); // Assuming you have an Interval enum 24 | int duration = historicalDateLimit.getScreenerDuration("NSE", interval); 25 | 26 | return BarSeriesConfig.builder() 27 | .interval(interval) 28 | .exchange(Exchange.NSE) 29 | .instrument(symbol) 30 | .instrumentType(InstrumentType.EQ) 31 | .startDate(LocalDate.now().minusDays(duration)) 32 | .endDate(LocalDate.now()) 33 | .name(symbol + "_" + timeframe) 34 | .build(); 35 | } 36 | 37 | IntervalBarSeries getIntervalBarSeries(String stock, String tf) { 38 | BarSeriesConfig config = createBarSeriesConfig(stock, tf); 39 | IntervalBarSeries barSeries = null; 40 | try { 41 | barSeries = barSeriesLoader.loadBarSeries(config); 42 | } catch (StrategyException e) { 43 | throw new RuntimeException(e); 44 | } 45 | return barSeries; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/controller/ConfigController.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.controller; 2 | 3 | import com.dtech.kitecon.config.KiteConnectConfig; 4 | import com.dtech.kitecon.service.IndexSymbolUpdaterService; 5 | import com.zerodhatech.kiteconnect.kitehttp.exceptions.KiteException; 6 | import java.io.IOException; 7 | import lombok.RequiredArgsConstructor; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Controller; 10 | import org.springframework.web.bind.annotation.GetMapping; 11 | import org.springframework.web.bind.annotation.RequestParam; 12 | import org.springframework.web.bind.annotation.ResponseBody; 13 | 14 | @Controller 15 | @RequiredArgsConstructor(onConstructor = @__(@Autowired)) 16 | public class ConfigController { 17 | 18 | private final KiteConnectConfig kiteConnectConfig; 19 | private final IndexSymbolUpdaterService updaterService; 20 | 21 | @GetMapping("/app") 22 | @ResponseBody 23 | public String fetchData(@RequestParam("request_token") String token) 24 | throws IOException, KiteException, InterruptedException { 25 | kiteConnectConfig.initialize(token); 26 | // updaterService.updateSymbols(); 27 | return "success"; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/controller/DataFetchController.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.controller; 2 | 3 | import com.dtech.algo.series.Interval; 4 | import com.dtech.kitecon.service.DataFetchService; 5 | import com.zerodhatech.kiteconnect.kitehttp.exceptions.KiteException; 6 | import java.io.IOException; 7 | import java.util.Arrays; 8 | import java.util.List; 9 | import lombok.RequiredArgsConstructor; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.web.bind.annotation.GetMapping; 12 | import org.springframework.web.bind.annotation.PathVariable; 13 | import org.springframework.web.bind.annotation.RestController; 14 | 15 | @RestController 16 | @RequiredArgsConstructor(onConstructor = @__(@Autowired)) 17 | public class DataFetchController { 18 | 19 | private final DataFetchService dataFetchService; 20 | 21 | @GetMapping("/profile") 22 | public String getProfile() throws IOException, KiteException { 23 | return dataFetchService.getProfile(); 24 | } 25 | 26 | @GetMapping("/fetch/{instrument}/{exchanges}") 27 | public void fetchData(@PathVariable String instrument, @PathVariable(required = false) String[] exchanges) { 28 | final String[] exchangeList = (exchanges == null || exchanges.length == 0) ? new String[]{"NSE", "NFO"} : exchanges; 29 | List intervals = Arrays.stream(Interval.values()).toList(); 30 | intervals.forEach(interval -> dataFetchService.downloadCandleData(instrument, interval, exchangeList)); 31 | } 32 | 33 | @GetMapping("/fetch-interval/{instrument}/{interval}/{exchanges}") 34 | public void fetchDataInterval(@PathVariable String instrument, @PathVariable Interval interval, @PathVariable(required = false) String[] exchanges) { 35 | final String[] exchangeList = (exchanges == null || exchanges.length == 0) ? new String[]{"NSE", "NFO"} : exchanges; 36 | dataFetchService.downloadCandleData(instrument, interval, exchangeList); 37 | } 38 | 39 | @GetMapping("/update-interval/{instrument}/{interval}/{exchanges}") 40 | public void updateCandleDataToLatest(@PathVariable String instrument, @PathVariable Interval interval, @PathVariable(required = false) String[] exchanges) { 41 | final String[] exchangeList = (exchanges == null || exchanges.length == 0) ? new String[]{"NSE", "NFO"} : exchanges; 42 | dataFetchService.updateInstrumentToLatest(instrument, interval, exchangeList); 43 | } 44 | 45 | @GetMapping("/fetch/instruments/all") 46 | public void fetchAllInstruments() throws IOException, KiteException { 47 | dataFetchService.downloadAllInstruments(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/controller/ElliottVisualizerController.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.controller; 2 | 3 | import com.dtech.algo.series.IntervalBarSeries; 4 | import com.dtech.ta.BarTuple; 5 | import com.dtech.ta.TrendLineCalculated; 6 | import com.dtech.ta.elliott.RefinedLocalExtremesDetector; 7 | import com.dtech.ta.elliott.Wave; 8 | import com.dtech.ta.elliott.priceaction.PriceAction; 9 | import com.dtech.ta.elliott.priceaction.PriceActionAnalyzer; 10 | import com.dtech.ta.elliott.wave.Wave1Detector; 11 | import com.dtech.ta.visualize.TrendlineVisualizer; 12 | import lombok.RequiredArgsConstructor; 13 | import org.springframework.web.bind.annotation.GetMapping; 14 | import org.springframework.web.bind.annotation.PathVariable; 15 | import org.springframework.web.bind.annotation.RestController; 16 | 17 | import java.io.IOException; 18 | import java.util.Arrays; 19 | import java.util.Collections; 20 | import java.util.List; 21 | import java.util.stream.Collectors; 22 | 23 | @RestController 24 | @RequiredArgsConstructor 25 | public class ElliottVisualizerController { 26 | 27 | private final BarSeriesHelper barSeriesHelper; 28 | 29 | // Endpoint to generate and return the visualization as a JPG 30 | @GetMapping(value = "/price-action/{stock}/{tf}") 31 | public String visualizePriceAction(@PathVariable String stock, @PathVariable String tf) throws IOException { 32 | // Dummy BarTuple and PriceAction data (replace with real data) 33 | IntervalBarSeries barSeries = barSeriesHelper.getIntervalBarSeries(stock, tf); 34 | RefinedLocalExtremesDetector refinedLocalExtremesDetector = new RefinedLocalExtremesDetector(barSeries, 20, 5); 35 | List extremes = refinedLocalExtremesDetector.detectLocalExtremes(); 36 | List priceActions = new PriceActionAnalyzer(barSeries).analyzePriceActions(extremes); 37 | List trendLines = priceActions.stream().map(p -> { 38 | return new TrendLineCalculated(barSeries, 0.0, 0.0, 39 | Arrays.asList(p.getStart(), p.getEnd()), true); 40 | }).toList(); 41 | 42 | // File path to save the JPG 43 | String filePath = stock + "_" + tf; 44 | Wave1Detector detector = new Wave1Detector(barSeries, priceActions); 45 | // Generate and save the chart as JPG 46 | List wave1 = detector.detectPotentialWave1s() 47 | .stream().map(pa -> new Wave(1, pa)).collect(Collectors.toList()); 48 | TrendlineVisualizer tlvisualizer = new TrendlineVisualizer(filePath, barSeries, trendLines, extremes, Collections.emptyList(), false, wave1); 49 | tlvisualizer.saveChartAsJpg( filePath, "elliott/"); 50 | 51 | 52 | // Return the image as a response 53 | return filePath; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/controller/ExecutionController.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.controller; 2 | 3 | import com.dtech.kitecon.service.ExecutionService; 4 | import lombok.RequiredArgsConstructor; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.PathVariable; 7 | import org.springframework.web.bind.annotation.ResponseBody; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | @RequiredArgsConstructor 11 | @RestController 12 | public class ExecutionController { 13 | 14 | private final ExecutionService executionService; 15 | 16 | 17 | @GetMapping("/start/{strategyName}/{instrument}/{direction}") 18 | @ResponseBody 19 | public String startStrategy(@PathVariable String strategyName, 20 | @PathVariable String instrument, @PathVariable String direction) throws InterruptedException { 21 | return executionService.startStrategy(strategyName, instrument, direction); 22 | } 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/controller/ImageController.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.controller; 2 | 3 | import org.springframework.web.bind.annotation.GetMapping; 4 | import org.springframework.web.bind.annotation.RequestParam; 5 | import org.springframework.web.bind.annotation.RestController; 6 | import org.springframework.core.io.Resource; 7 | import org.springframework.core.io.UrlResource; 8 | 9 | import java.io.IOException; 10 | import java.nio.file.*; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | @RestController 15 | public class ImageController { 16 | 17 | @GetMapping("/images") 18 | public List getImages(@RequestParam String directory) throws IOException { 19 | List imageFiles = new ArrayList<>(); 20 | Path imageDir = Paths.get(directory); 21 | 22 | try (DirectoryStream stream = Files.newDirectoryStream(imageDir, "*.jpg")) { 23 | for (Path file : stream) { 24 | imageFiles.add(file.getFileName().toString()); 25 | } 26 | } catch (IOException e) { 27 | throw new IOException("Unable to read image directory", e); 28 | } 29 | 30 | return imageFiles; 31 | } 32 | 33 | @GetMapping("/images/view") 34 | public Resource getImage(@RequestParam String directory, @RequestParam String filename) throws IOException { 35 | Path file = Paths.get(directory).resolve(filename); 36 | Resource resource = new UrlResource(file.toUri()); 37 | 38 | if (!resource.exists() || !resource.isReadable()) { 39 | throw new IOException("Could not read file: " + filename); 40 | } 41 | 42 | return resource; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/controller/OrderController.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.controller; 2 | 3 | import com.dtech.kitecon.data.Instrument; 4 | import com.dtech.kitecon.market.orders.OrderException; 5 | import com.dtech.kitecon.market.orders.OrderManager; 6 | import com.dtech.kitecon.repository.InstrumentRepository; 7 | import lombok.RequiredArgsConstructor; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.PathVariable; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | @RestController 13 | @RequiredArgsConstructor 14 | public class OrderController { 15 | 16 | private String[] exchanges = new String[]{"NSE"}; 17 | 18 | private final InstrumentRepository instrumentRepository; 19 | private final OrderManager orderManager; 20 | 21 | @GetMapping("/order/{instrument}/{direction}/{price}") 22 | public String placeOrder(@PathVariable Double price, 23 | @PathVariable String instrument, @PathVariable String direction) throws OrderException { 24 | Instrument tradingIdentity = instrumentRepository 25 | .findByTradingsymbolAndExchangeIn(instrument, exchanges); 26 | return orderManager.placeMISOrder(price, 200, tradingIdentity, direction); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/controller/StrategyController.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.controller; 2 | 3 | import com.dtech.algo.series.Interval; 4 | import com.dtech.kitecon.service.StrategyService; 5 | import com.dtech.kitecon.strategy.backtest.BacktestSummary; 6 | import lombok.RequiredArgsConstructor; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.PathVariable; 10 | import org.springframework.web.bind.annotation.ResponseBody; 11 | import org.springframework.web.bind.annotation.RestController; 12 | 13 | @RestController 14 | @RequiredArgsConstructor(onConstructor = @__(@Autowired)) 15 | public class StrategyController { 16 | 17 | private final StrategyService strategyService; 18 | 19 | @GetMapping("/test/{strategyName}/{instrument}") 20 | @ResponseBody 21 | public BacktestSummary backtestStrategy(@PathVariable String strategyName, 22 | @PathVariable String instrument) throws InterruptedException { 23 | return strategyService.testStrategy(instrument, strategyName, Interval.FifteenMinute); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/data/Candle.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.data; 2 | 3 | import java.time.LocalDateTime; 4 | 5 | import com.dtech.algo.series.Interval; 6 | import jakarta.persistence.*; 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | import lombok.NoArgsConstructor; 10 | import org.hibernate.annotations.PartitionKey; 11 | 12 | @Entity 13 | @Data 14 | @NoArgsConstructor 15 | @Table(uniqueConstraints = @UniqueConstraint(columnNames = {"timeframe", "timestamp", "instrument_instrument_token"}), 16 | indexes = @Index(columnList = "instrument_instrument_token, timeframe")) 17 | public class Candle { 18 | 19 | @Column 20 | protected Double open; 21 | @Column 22 | protected Double high; 23 | @Column 24 | protected Double low; 25 | @Column 26 | protected Double close; 27 | @Column 28 | protected Long volume; 29 | @Column 30 | protected Long oi; 31 | @Column 32 | 33 | @EqualsAndHashCode.Include 34 | protected LocalDateTime timestamp; 35 | @ManyToOne(targetEntity = Instrument.class) 36 | 37 | @EqualsAndHashCode.Include 38 | @PartitionKey 39 | protected Instrument instrument; 40 | 41 | @Id 42 | @GeneratedValue(strategy = GenerationType.SEQUENCE) 43 | private long id; 44 | 45 | @Column 46 | @Enumerated(EnumType.STRING) 47 | @EqualsAndHashCode.Include 48 | private Interval timeframe; 49 | 50 | //private String timeFrame 51 | 52 | 53 | public Candle(Double open, Double high, Double low, Double close, Long volume, Long oi, 54 | LocalDateTime timestamp, Instrument instrument, Interval interval) { 55 | this.open = open; 56 | this.high = high; 57 | this.low = low; 58 | this.close = close; 59 | this.volume = volume; 60 | this.oi = oi; 61 | this.timestamp = timestamp; 62 | this.instrument = instrument; 63 | this.timeframe = interval; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/data/IndexSymbol.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.data; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | import jakarta.persistence.*; 7 | import java.time.LocalDateTime; 8 | 9 | @Entity 10 | @Table(name = "index_symbols") 11 | @Getter 12 | @Setter 13 | public class IndexSymbol { 14 | 15 | @Id 16 | @GeneratedValue(strategy = GenerationType.IDENTITY) 17 | private Long id; 18 | 19 | @Column(name = "exchange_symbol", nullable = false) 20 | private String exchangeSymbol; 21 | 22 | @Column(name = "instrument_name", nullable = false) 23 | private String instrumentName; 24 | 25 | @Column(name = "index_name", nullable = false) 26 | private String indexName; 27 | 28 | @Column(name = "created_at", nullable = false) 29 | private LocalDateTime createdAt; 30 | 31 | @Column(name = "updated_at", nullable = false) 32 | private LocalDateTime updatedAt; 33 | 34 | @PrePersist 35 | protected void onCreate() { 36 | this.createdAt = LocalDateTime.now(); 37 | this.updatedAt = LocalDateTime.now(); 38 | } 39 | 40 | @PreUpdate 41 | protected void onUpdate() { 42 | this.updatedAt = LocalDateTime.now(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/data/Instrument.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.data; 2 | 3 | import java.time.LocalDateTime; 4 | import jakarta.persistence.Column; 5 | import jakarta.persistence.Entity; 6 | import jakarta.persistence.Id; 7 | 8 | import lombok.*; 9 | 10 | @Entity 11 | @Data 12 | @Builder 13 | @AllArgsConstructor 14 | @EqualsAndHashCode 15 | @NoArgsConstructor 16 | public class Instrument { 17 | 18 | @Id 19 | private Long instrumentToken; 20 | @Column 21 | private Long exchangeToken; 22 | @Column 23 | private String tradingsymbol; 24 | @Column 25 | private String name; 26 | @Column 27 | private Double lastPrice; 28 | @Column 29 | private LocalDateTime expiry; 30 | @Column 31 | private String strike; 32 | @Column 33 | private Double tickSize; 34 | @Column 35 | private Integer lotSize; 36 | @Column 37 | private String instrumentType; 38 | @Column 39 | private String segment; 40 | @Column 41 | private String exchange; 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/data/LocalDateTimeAttributeConverter.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.data; 2 | 3 | import java.sql.Timestamp; 4 | import java.time.LocalDateTime; 5 | import java.time.ZoneId; 6 | import java.util.Date; 7 | import jakarta.persistence.AttributeConverter; 8 | import jakarta.persistence.Converter; 9 | 10 | @Converter(autoApply = true) 11 | public class LocalDateTimeAttributeConverter implements AttributeConverter { 12 | 13 | @Override 14 | public Date convertToDatabaseColumn(LocalDateTime locDateTime) { 15 | return locDateTime == null ? null : Timestamp.valueOf(locDateTime); 16 | } 17 | 18 | @Override 19 | public LocalDateTime convertToEntityAttribute(Date sqlTimestamp) { 20 | return sqlTimestamp == null ? 21 | null : LocalDateTime.ofInstant(sqlTimestamp.toInstant(), ZoneId.systemDefault()); 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/data/StrategyParameters.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.data; 2 | 3 | import com.dtech.kitecon.misc.StrategyEnvironment; 4 | import com.dtech.kitecon.strategy.TradeDirection; 5 | import jakarta.persistence.Column; 6 | import jakarta.persistence.Entity; 7 | import jakarta.persistence.EnumType; 8 | import jakarta.persistence.Enumerated; 9 | import jakarta.persistence.GeneratedValue; 10 | import jakarta.persistence.GenerationType; 11 | import jakarta.persistence.Id; 12 | import jakarta.persistence.Index; 13 | import jakarta.persistence.Table; 14 | import lombok.AllArgsConstructor; 15 | import lombok.Builder; 16 | import lombok.Data; 17 | import lombok.NoArgsConstructor; 18 | 19 | @Entity 20 | @Data 21 | @Builder 22 | @AllArgsConstructor 23 | @NoArgsConstructor 24 | @Table(indexes = @Index(columnList = "strategyName,instrumentName,environment")) 25 | public class StrategyParameters { 26 | 27 | @Id @GeneratedValue(strategy = GenerationType.IDENTITY) 28 | private Integer id; 29 | 30 | @Column(length = 20) 31 | private String strategyName; 32 | 33 | @Column(length = 50) 34 | private String instrumentName; 35 | 36 | @Column 37 | @Enumerated 38 | private StrategyEnvironment environment; 39 | 40 | @Column 41 | private String strategyType; 42 | 43 | @Column(length = 50) 44 | private String configKey; 45 | @Column(length = 10) 46 | private String value; 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/data/TrendLine.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.data; 2 | 3 | import com.dtech.algo.series.Interval; 4 | import com.dtech.ta.OHLC; 5 | import jakarta.persistence.*; 6 | import lombok.Data; 7 | 8 | import java.time.LocalDateTime; 9 | 10 | @Entity 11 | @Data 12 | @Table(uniqueConstraints = @UniqueConstraint(columnNames = {"instrument", "startTime", "endTime", "startOhlc", "endOhlc"})) 13 | public class TrendLine { 14 | @Id 15 | @GeneratedValue 16 | private Long id; 17 | 18 | @Column 19 | private String instrument; 20 | @Column 21 | private LocalDateTime startTime; 22 | @Column 23 | private LocalDateTime endTime; 24 | @Column 25 | @Enumerated(EnumType.STRING) 26 | private OHLC startOhlc; 27 | @Column 28 | @Enumerated(EnumType.STRING) 29 | private OHLC endOhlc; 30 | @Column 31 | @Enumerated(EnumType.STRING) 32 | private Interval timeframe; 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/historical/limits/LimitsKey.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.historical.limits; 2 | 3 | import com.dtech.algo.series.Interval; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | 8 | @AllArgsConstructor 9 | @Data 10 | @Builder 11 | public class LimitsKey { 12 | 13 | private String exchange; 14 | private String interval; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/loader/DataLoader.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.loader; 2 | 3 | public class DataLoader { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/market/Provider.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.market; 2 | 3 | public enum Provider { 4 | ZERODHA 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/market/fetch/DataFetchException.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.market.fetch; 2 | 3 | public class DataFetchException extends Exception { 4 | 5 | public DataFetchException(Throwable cause) { 6 | super(cause); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/market/fetch/MarketDataFetch.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.market.fetch; 2 | 3 | import com.dtech.algo.series.Interval; 4 | import com.dtech.kitecon.data.Candle; 5 | import com.dtech.kitecon.data.Instrument; 6 | import com.dtech.kitecon.service.DateRange; 7 | import com.zerodhatech.kiteconnect.kitehttp.exceptions.KiteException; 8 | import java.io.IOException; 9 | import java.util.List; 10 | 11 | public interface MarketDataFetch { 12 | 13 | 14 | 15 | String getProfile() throws IOException, KiteException; 16 | 17 | void fetch(DateRange dateRange, String instrumentToken, Interval interval) 18 | throws DataFetchException; 19 | 20 | List fetchTodaysData(Instrument instrument, Interval interval) 21 | throws DataFetchException; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/market/fetch/MarketDataFetchFactory.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.market.fetch; 2 | 3 | public interface MarketDataFetchFactory { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/market/fetch/MarketDataFetchImpl.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.market.fetch; 2 | 3 | public interface MarketDataFetchImpl { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/market/orders/OrderException.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.market.orders; 2 | 3 | public class OrderException extends Exception { 4 | 5 | public OrderException(Throwable cause) { 6 | super(cause); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/market/orders/OrderManager.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.market.orders; 2 | 3 | import com.dtech.kitecon.data.Instrument; 4 | 5 | public interface OrderManager { 6 | 7 | String placeMISOrder(Double price, int amount, Instrument instrument, String orderType) 8 | throws OrderException; 9 | 10 | Integer getActualOrderStatus(String orderId) throws OrderException; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/market/orders/ZerodhaOrderManager.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.market.orders; 2 | 3 | import com.dtech.kitecon.config.KiteConnectConfig; 4 | import com.dtech.kitecon.data.Instrument; 5 | import com.zerodhatech.kiteconnect.KiteConnect; 6 | import com.zerodhatech.models.Order; 7 | import com.zerodhatech.models.OrderParams; 8 | import com.zerodhatech.models.Trade; 9 | import java.util.List; 10 | import java.util.UUID; 11 | import java.util.stream.Collectors; 12 | import lombok.RequiredArgsConstructor; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.beans.factory.annotation.Qualifier; 15 | import org.springframework.stereotype.Component; 16 | 17 | @RequiredArgsConstructor(onConstructor = @__(@Autowired)) 18 | @Component 19 | @Qualifier("zerodhaOrderManagerOld") 20 | public class ZerodhaOrderManager implements OrderManager { 21 | private final KiteConnectConfig kiteConnectConfig; 22 | 23 | @Override 24 | public String placeMISOrder(Double price, int amount, Instrument instrument, String orderType) 25 | throws OrderException { 26 | KiteConnect connect = kiteConnectConfig.getKiteConnect(); 27 | OrderParams params = new OrderParams(); 28 | params.exchange = "NSE"; 29 | params.tradingsymbol = instrument.getTradingsymbol(); 30 | params.transactionType = orderType.toUpperCase(); 31 | params.quantity = amount; 32 | params.price = price; 33 | params.product = "MIS"; 34 | params.orderType = "LIMIT"; 35 | params.validity = "DAY"; 36 | params.disclosedQuantity = amount; 37 | params.parentOrderId = UUID.randomUUID().toString(); 38 | try { 39 | Order order = connect.placeOrder(params, "regular"); 40 | return order.orderId; 41 | } catch (Throwable e) { 42 | throw new OrderException(e); 43 | } 44 | } 45 | 46 | @Override 47 | public Integer getActualOrderStatus(String orderId) throws OrderException { 48 | try { 49 | List orderTrades = kiteConnectConfig.getKiteConnect().getOrderTrades(orderId); 50 | return orderTrades.stream() 51 | .collect(Collectors.summingInt(trade -> Integer.parseInt(trade.quantity))); 52 | } catch (Throwable th) { 53 | throw new OrderException(th); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/misc/StrategyEnvironment.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.misc; 2 | 3 | public enum StrategyEnvironment { 4 | DEV, 5 | PROD; 6 | 7 | public boolean isProd() { 8 | return this == PROD; 9 | } 10 | 11 | public boolean isDev() { 12 | return this == DEV; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/repository/CandleRepository.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.repository; 2 | 3 | import com.dtech.algo.series.Interval; 4 | import com.dtech.kitecon.data.Candle; 5 | import com.dtech.kitecon.data.Instrument; 6 | import java.time.LocalDateTime; 7 | import java.util.List; 8 | 9 | import org.springframework.data.jpa.repository.JpaRepository; 10 | import org.springframework.stereotype.Repository; 11 | 12 | @Repository 13 | public interface CandleRepository extends JpaRepository { 14 | 15 | List findAllByInstrumentAndTimeframe(Instrument instrument, Interval interval); 16 | 17 | List findAllByInstrumentAndTimeframeAndTimestampBetween(Instrument instrument, Interval interval, LocalDateTime startDate, LocalDateTime endDate); 18 | 19 | Candle findFirstByInstrumentAndTimeframeOrderByTimestampDesc(Instrument instrument, Interval interval); 20 | 21 | Candle findFirstByInstrumentAndTimeframeOrderByTimestamp(Instrument instrument, Interval interval); 22 | 23 | void deleteByInstrumentAndTimeframe(Instrument instrument, Interval interval); 24 | 25 | void deleteByInstrumentAndTimeframeAndTimestampBetween(Instrument instrument, Interval interval, 26 | LocalDateTime startDate, LocalDateTime endDate); 27 | 28 | } 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/repository/CandleRepositoryOld.java: -------------------------------------------------------------------------------- 1 | //package com.dtech.kitecon.repository; 2 | // 3 | //import com.dtech.kitecon.data.Candle; 4 | //import com.dtech.kitecon.data.Instrument; 5 | //import lombok.RequiredArgsConstructor; 6 | //import org.springframework.beans.factory.annotation.Autowired; 7 | //import org.springframework.stereotype.Service; 8 | // 9 | //import jakarta.annotation.PostConstruct; 10 | //import java.time.LocalDateTime; 11 | //import java.util.List; 12 | //import java.util.Map; 13 | //import java.util.stream.Collectors; 14 | // 15 | //@SuppressWarnings("ALL") 16 | //@Service 17 | //@RequiredArgsConstructor(onConstructor = @__(@Autowired)) 18 | //public class CandleRepository { 19 | // 20 | // private final List repositoryList; 21 | // private Map repositories; 22 | // 23 | // @PostConstruct 24 | // public void initialise() { 25 | // repositories = repositoryList.stream() 26 | // .collect(Collectors.toMap(BaseCandleRepository::getInterval, t -> t)); 27 | // } 28 | // 29 | // public List getAllIntervals() { 30 | // return repositories.keySet().stream().collect(Collectors.toList()); 31 | // } 32 | // 33 | // private BaseCandleRepository getDelegate(Interval interval) { 34 | // return repositories.get(interval); 35 | // } 36 | // 37 | // public List findAllByInstrument(Interval interval, Instrument instrument) { 38 | // return getDelegate(interval).findAllByInstrument(instrument); 39 | // } 40 | // 41 | // public Candle findFirstByInstrumentOrderByTimestampDesc(Interval interval, 42 | // Instrument instrument) { 43 | // return getDelegate(interval).findFirstByInstrumentOrderByTimestampDesc(instrument); 44 | // } 45 | // 46 | // public List findAllByInstrumentAndTimestampBetween(Interval interval, Instrument instrument, 47 | // LocalDateTime startDate, LocalDateTime endDate) { 48 | // return getDelegate(interval).findAllByInstrumentAndTimestampBetween(instrument, startDate, endDate); 49 | // } 50 | // 51 | // 52 | // public void deleteByInstrument(Interval interval, Instrument instrument) { 53 | // getDelegate(interval).deleteByInstrument(instrument); 54 | // } 55 | // 56 | // public void deleteByInstrumentAndTimestampBetween(Interval interval, Instrument instrument, 57 | // LocalDateTime startDate, LocalDateTime endDate) { 58 | // getDelegate(interval).deleteByInstrumentAndTimestampBetween(instrument, 59 | // startDate, endDate); 60 | // } 61 | // 62 | // public void saveAll(Interval interval, List databaseCandles) { 63 | // getDelegate(interval).saveAll(databaseCandles); 64 | // } 65 | //} 66 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/repository/IndexSymbolRepository.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.repository; 2 | 3 | import com.dtech.kitecon.data.IndexSymbol; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.jpa.repository.Query; 6 | import org.springframework.stereotype.Repository; 7 | 8 | import java.util.List; 9 | 10 | @Repository 11 | public interface IndexSymbolRepository extends JpaRepository { 12 | 13 | /** 14 | * Finds all symbols (exchangeSymbol) for the IndexSymbol entities. 15 | */ 16 | @Query("SELECT s.exchangeSymbol FROM IndexSymbol s") 17 | List findAllSymbols(); 18 | 19 | /** 20 | * Finds all symbols (exchangeSymbol) for the given indexName. 21 | */ 22 | @Query("SELECT s.exchangeSymbol FROM IndexSymbol s WHERE s.indexName = :indexName") 23 | List findAllSymbolsByIndexName(String indexName); 24 | 25 | /** 26 | * Checks if a given index name exists. 27 | */ 28 | boolean existsByIndexName(String indexName); 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/repository/InstrumentRepository.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.repository; 2 | 3 | import com.dtech.algo.series.InstrumentType; 4 | import com.dtech.kitecon.data.Instrument; 5 | import java.time.LocalDateTime; 6 | import java.util.List; 7 | import org.springframework.data.jpa.repository.JpaRepository; 8 | import org.springframework.stereotype.Repository; 9 | 10 | @Repository 11 | public interface InstrumentRepository extends JpaRepository { 12 | 13 | List findAllByTradingsymbolStartingWithAndExpiryBetweenAndExchangeIn(String symbol, 14 | LocalDateTime expiryStart, LocalDateTime expiryEnd, String[] exchanges); 15 | 16 | List findAllByTradingsymbolStartingWithAndExpiryIsNullAndExchangeIn(String symbol, 17 | String[] exchanges); 18 | 19 | List findAllByTradingsymbolStartingWithAndExchangeIn(String symbol, 20 | String[] exchanges); 21 | 22 | List findAllByExchangeAndInstrumentTypeAndTradingsymbolStartingWith(String exchange, 23 | String instrumentType, String tradingSymbol); 24 | 25 | Instrument findByTradingsymbolAndExchangeIn(String symbol, String[] exchanges); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/repository/StrategyParametersRepository.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.repository; 2 | 3 | import com.dtech.kitecon.data.StrategyParameters; 4 | import com.dtech.kitecon.misc.StrategyEnvironment; 5 | import java.util.List; 6 | import org.springframework.data.jpa.repository.JpaRepository; 7 | import org.springframework.stereotype.Repository; 8 | 9 | @Repository 10 | public interface StrategyParametersRepository extends JpaRepository { 11 | 12 | List findByStrategyNameAndInstrumentNameAndEnvironment(String strategyName, 13 | String InstrumentName, StrategyEnvironment env); 14 | 15 | } 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/service/DataDownloadRequest.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.service; 2 | 3 | import com.dtech.algo.series.Interval; 4 | import com.dtech.kitecon.data.Instrument; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | 9 | @Data 10 | @AllArgsConstructor 11 | @Builder 12 | public class DataDownloadRequest { 13 | 14 | private Instrument instrument; 15 | private DateRange dateRange; 16 | private Interval interval; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/service/DateRange.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.service; 2 | 3 | import java.time.ZonedDateTime; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Builder; 8 | import lombok.Data; 9 | 10 | @AllArgsConstructor 11 | @Builder 12 | @Data 13 | public class DateRange { 14 | 15 | private ZonedDateTime startDate; 16 | private ZonedDateTime endDate; 17 | 18 | public List split(int days) { 19 | List result = new ArrayList<>(); 20 | ZonedDateTime startRef = startDate; 21 | ZonedDateTime endRef = startDate.plusDays(days); 22 | while (endRef.isBefore(endDate)) { 23 | result.add(DateRange.builder() 24 | .endDate(endRef) 25 | .startDate(startRef).build()); 26 | startRef = endRef; 27 | endRef = startRef.plusDays(days); 28 | } 29 | result.add(DateRange.builder() 30 | .endDate(endDate) 31 | .startDate(startRef).build()); 32 | return result; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/service/ExecutionService.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.service; 2 | 3 | import com.dtech.kitecon.market.orders.OrderManager; 4 | import com.dtech.kitecon.repository.InstrumentRepository; 5 | import com.dtech.kitecon.repository.StrategyParametersRepository; 6 | import com.dtech.kitecon.strategy.builder.StrategyBuilder; 7 | import com.dtech.kitecon.strategy.dataloader.InstrumentDataLoader; 8 | import com.dtech.kitecon.strategy.exec.ProductionHandler; 9 | import com.dtech.kitecon.strategy.exec.ProductionSeriesManager; 10 | import com.dtech.kitecon.strategy.sets.StrategySet; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | import java.util.UUID; 14 | import lombok.RequiredArgsConstructor; 15 | import org.springframework.stereotype.Service; 16 | 17 | @Service 18 | @RequiredArgsConstructor 19 | public class ExecutionService { 20 | 21 | private final StrategySet strategySet; 22 | private final InstrumentRepository instrumentRepository; 23 | private final InstrumentDataLoader instrumentDataLoader; 24 | private final OrderManager ordermanager; 25 | private final ProductionSeriesManager productionSeriesManager; 26 | private final StrategyParametersRepository strategyParametersRepository; 27 | 28 | private Map runners = new HashMap(); 29 | private Map tradeHandlers = new HashMap(); 30 | 31 | public String startStrategy(String strategyName, String instrumentName, String direction) { 32 | String uuid = UUID.randomUUID().toString(); 33 | StrategyBuilder strategy = strategySet.getStrategy(strategyName); 34 | TradeInfo key = TradeInfo.builder().direction(direction).symbol(instrumentName).build(); 35 | ProductionHandler handler = tradeHandlers.computeIfAbsent(key, 36 | tradeInfo -> getProductionHandler(uuid, instrumentName, direction)); 37 | handler.startStrategy(instrumentName, strategy, direction); 38 | return uuid; 39 | } 40 | 41 | private ProductionHandler getProductionHandler(String uuid, String instrumentName, 42 | String direction) { 43 | ProductionHandler handler = new ProductionHandler(instrumentRepository, instrumentDataLoader, 44 | ordermanager, productionSeriesManager, strategyParametersRepository); 45 | runners.put(uuid, handler); 46 | handler.initialise(instrumentName, direction); 47 | return handler; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/service/IndexSymbolUpdaterService.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.service; 2 | 3 | import com.dtech.algo.series.Interval; 4 | import com.dtech.kitecon.repository.IndexSymbolRepository; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.scheduling.annotation.Async; 8 | import org.springframework.scheduling.annotation.EnableAsync; 9 | import org.springframework.stereotype.Service; 10 | 11 | import jakarta.transaction.Transactional; 12 | import java.util.List; 13 | import java.util.concurrent.ExecutorService; 14 | import java.util.concurrent.Executors; 15 | import java.util.concurrent.TimeUnit; 16 | 17 | @Service 18 | @EnableAsync 19 | public class IndexSymbolUpdaterService { 20 | 21 | @Autowired 22 | private DataFetchService dataFetchService; 23 | 24 | // @Value("${symbol.update.worker.thread.pool.size:10}") 25 | @Autowired 26 | private IndexSymbolRepository indexSymbolRepository; 27 | 28 | 29 | /** 30 | * Updates symbols for all intervals using worker threads. Each symbol is processed in a separate worker thread. 31 | */ 32 | 33 | @Async 34 | public void updateSymbols() throws InterruptedException { 35 | List allSymbols = indexSymbolRepository.findAllSymbols(); 36 | 37 | // For each symbol, assign a worker thread to handle the updates 38 | for (String symbol : allSymbols) { 39 | updateSymbolForAllIntervals(symbol); 40 | } 41 | 42 | // Shutdown the executor service after submission 43 | // executorService.shutdown(); 44 | // executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); 45 | } 46 | 47 | /** 48 | * Updates all intervals for the given symbol within a transaction. 49 | */ 50 | @Transactional 51 | public void updateSymbolForAllIntervals(String symbol) { 52 | String[] exchanges = {"NSE"}; // Assuming we're updating for NSE 53 | 54 | for (Interval interval : Interval.values()) { 55 | dataFetchService.updateInstrumentToLatest(symbol, interval, exchanges); // Updated method signature 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/service/StrategyService.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.service; 2 | 3 | import com.dtech.algo.series.Interval; 4 | import com.dtech.kitecon.strategy.backtest.BackTestingHandler; 5 | import com.dtech.kitecon.strategy.backtest.BacktestSummary; 6 | import com.dtech.kitecon.strategy.sets.StrategySet; 7 | import lombok.RequiredArgsConstructor; 8 | import org.springframework.stereotype.Service; 9 | 10 | @Service 11 | @RequiredArgsConstructor 12 | public class StrategyService { 13 | 14 | private final BackTestingHandler backTestingHandler; 15 | private final StrategySet strategySet; 16 | 17 | public BacktestSummary testStrategy(String instrument, String strategyName, Interval interval) { 18 | return backTestingHandler.execute(instrument, strategySet.getStrategy(strategyName), interval); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/service/TradeInfo.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.service; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | 7 | @Data 8 | @AllArgsConstructor 9 | @Builder 10 | public class TradeInfo { 11 | 12 | private String symbol; 13 | private String direction; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/strategy/TradeDirection.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.strategy; 2 | 3 | public enum TradeDirection { 4 | Buy, 5 | Sell, 6 | Both; 7 | 8 | public boolean isBuy() { 9 | return this == Buy || this == Both; 10 | } 11 | 12 | public boolean isSell() { 13 | return this == Sell || this == Both; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/strategy/TradingStrategy.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.strategy; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | import org.ta4j.core.Strategy; 6 | 7 | @Data 8 | @Builder 9 | public class TradingStrategy { 10 | 11 | private String name; 12 | private Strategy buyStrategy; 13 | private Strategy sellStrategy; 14 | private TradeDirection tradeDirection; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/strategy/backtest/BacktestResult.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.strategy.backtest; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import lombok.Value; 6 | import org.ta4j.core.Position; 7 | 8 | @Value 9 | public class BacktestResult { 10 | 11 | private Map aggregatesResults; 12 | private List tradingRecord; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/strategy/backtest/BacktestSummary.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.strategy.backtest; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | 9 | @Data 10 | @AllArgsConstructor 11 | @Builder 12 | public class BacktestSummary { 13 | 14 | Map> summary; 15 | List results; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/strategy/backtest/OrderRecord.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.strategy.backtest; 2 | 3 | import java.time.ZonedDateTime; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import org.ta4j.core.Trade.TradeType; 8 | 9 | @Data 10 | @AllArgsConstructor 11 | @Builder 12 | public class OrderRecord { 13 | 14 | /** 15 | * Type of the order 16 | */ 17 | private TradeType type; 18 | 19 | /** 20 | * The index the order was executed 21 | */ 22 | private int index; 23 | 24 | /** 25 | * The pricePerAsset for the order 26 | */ 27 | private Double pricePerAsset; 28 | 29 | /** 30 | * The net price for the order, net transaction costs 31 | */ 32 | private Double netPrice; 33 | 34 | /** 35 | * The amount to be (or that was) ordered 36 | */ 37 | private Double amount; 38 | 39 | /** 40 | * Cost of executing the order 41 | */ 42 | private Double cost; 43 | 44 | private ZonedDateTime dateTime; 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/strategy/backtest/TradeRecord.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.strategy.backtest; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | 7 | @Builder 8 | @AllArgsConstructor 9 | @Data 10 | public class TradeRecord { 11 | 12 | /** 13 | * The type of the entry order 14 | */ 15 | private Double profit; 16 | 17 | /** 18 | * The entry order 19 | */ 20 | private OrderRecord entry; 21 | 22 | /** 23 | * The exit order 24 | */ 25 | private OrderRecord exit; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/strategy/builder/BaseStrategyBuilder.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.strategy.builder; 2 | 3 | import com.dtech.kitecon.data.Instrument; 4 | import com.dtech.kitecon.strategy.TradingStrategy; 5 | import java.util.Map; 6 | import org.ta4j.core.BarSeries; 7 | import org.ta4j.core.Strategy; 8 | 9 | public abstract class BaseStrategyBuilder implements StrategyBuilder { 10 | 11 | @Override 12 | public TradingStrategy build(Instrument tradingIdentity, 13 | Map barSeriesMap, StrategyConfig config) { 14 | return TradingStrategy.builder() 15 | .buyStrategy(getBuyStrategy(tradingIdentity, barSeriesMap, config.getConfig("Buy"))) 16 | .sellStrategy(getSellStrategy(tradingIdentity, barSeriesMap, config.getConfig("Sell"))) 17 | .tradeDirection(getTradeDirection()) 18 | .name(getName()) 19 | .build(); 20 | } 21 | 22 | protected abstract Strategy getSellStrategy(Instrument tradingIdentity, 23 | Map barSeriesMap, 24 | Map sell); 25 | 26 | protected abstract Strategy getBuyStrategy(Instrument tradingIdentity, 27 | Map barSeriesMap, 28 | Map config); 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/strategy/builder/MACDDiversionStrategy.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.strategy.builder; 2 | 3 | import com.dtech.kitecon.data.Instrument; 4 | import com.dtech.kitecon.strategy.TradeDirection; 5 | import java.util.Map; 6 | import org.springframework.stereotype.Component; 7 | import org.ta4j.core.BarSeries; 8 | import org.ta4j.core.BaseStrategy; 9 | import org.ta4j.core.Rule; 10 | import org.ta4j.core.Strategy; 11 | import org.ta4j.core.indicators.MACDIndicator; 12 | import org.ta4j.core.indicators.RSIIndicator; 13 | import org.ta4j.core.indicators.helpers.ClosePriceIndicator; 14 | import org.ta4j.core.num.DecimalNum; 15 | import org.ta4j.core.rules.OverIndicatorRule; 16 | import org.ta4j.core.rules.StopGainRule; 17 | import org.ta4j.core.rules.StopLossRule; 18 | import org.ta4j.core.rules.TrailingStopLossRule; 19 | import org.ta4j.core.rules.UnderIndicatorRule; 20 | 21 | @Component 22 | public class MACDDiversionStrategy extends BaseStrategyBuilder { 23 | 24 | private static Strategy create3DaySmaUnderStrategy(BarSeries series) { 25 | ClosePriceIndicator closePrice = new ClosePriceIndicator(series); 26 | 27 | MACDIndicator macdIndicator = new MACDIndicator(closePrice); 28 | Rule entryRule = new OverIndicatorRule(macdIndicator, 1) 29 | .and(new UnderIndicatorRule(new RSIIndicator(closePrice, 14), 55)) 30 | .and(new OverIndicatorRule(new RSIIndicator(closePrice, 14), 30)); 31 | 32 | Rule exitRule = new StopGainRule(closePrice, 5) 33 | .or(new StopLossRule(closePrice, 2)) 34 | .or(new TrailingStopLossRule(closePrice, DecimalNum.valueOf(1))); 35 | 36 | return new BaseStrategy(entryRule, exitRule); 37 | } 38 | 39 | @Override 40 | protected Strategy getSellStrategy(Instrument tradingIdentity, 41 | Map barSeriesMap, 42 | Map sell) { 43 | return null; 44 | } 45 | 46 | @Override 47 | public Strategy getBuyStrategy(Instrument tradingIdentity, 48 | Map BarSeriesMap, 49 | Map config) { 50 | return create3DaySmaUnderStrategy(BarSeriesMap.get(tradingIdentity)); 51 | } 52 | 53 | @Override 54 | public TradeDirection getTradeDirection() { 55 | return TradeDirection.Buy; 56 | } 57 | 58 | @Override 59 | public String getName() { 60 | return "MACDDivergence"; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/strategy/builder/SimpleMovingAverageStrategyBuider.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.strategy.builder; 2 | 3 | import com.dtech.kitecon.data.Instrument; 4 | import com.dtech.kitecon.strategy.TradeDirection; 5 | import java.util.Map; 6 | import org.springframework.stereotype.Component; 7 | import org.ta4j.core.BarSeries; 8 | import org.ta4j.core.BaseStrategy; 9 | import org.ta4j.core.Strategy; 10 | import org.ta4j.core.indicators.SMAIndicator; 11 | import org.ta4j.core.indicators.helpers.ClosePriceIndicator; 12 | import org.ta4j.core.rules.OverIndicatorRule; 13 | import org.ta4j.core.rules.UnderIndicatorRule; 14 | 15 | @Component 16 | public class SimpleMovingAverageStrategyBuider extends BaseStrategyBuilder { 17 | 18 | private static Strategy create3DaySmaUnderStrategy(BarSeries series) { 19 | ClosePriceIndicator closePrice = new ClosePriceIndicator(series); 20 | SMAIndicator sma = new SMAIndicator(closePrice, 3); 21 | return new BaseStrategy( 22 | new UnderIndicatorRule(sma, closePrice), 23 | new OverIndicatorRule(sma, closePrice) 24 | ); 25 | } 26 | 27 | @Override 28 | protected Strategy getSellStrategy(Instrument tradingIdentity, 29 | Map barSeriesMap, 30 | Map sell) { 31 | return null; 32 | } 33 | 34 | @Override 35 | public Strategy getBuyStrategy(Instrument tradingIdentity, 36 | Map BarSeriesMap, 37 | Map config) { 38 | return create3DaySmaUnderStrategy(BarSeriesMap.get(tradingIdentity)); 39 | } 40 | 41 | @Override 42 | public TradeDirection getTradeDirection() { 43 | return TradeDirection.Buy; 44 | } 45 | 46 | @Override 47 | public String getName() { 48 | return "SimpleMovingAverage"; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/strategy/builder/StrategyBuilder.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.strategy.builder; 2 | 3 | import com.dtech.kitecon.data.Instrument; 4 | import com.dtech.kitecon.strategy.TradeDirection; 5 | import com.dtech.kitecon.strategy.TradingStrategy; 6 | import java.util.Map; 7 | import org.ta4j.core.BarSeries; 8 | 9 | public interface StrategyBuilder { 10 | 11 | TradeDirection getTradeDirection(); 12 | 13 | String getName(); 14 | 15 | TradingStrategy build(Instrument tradingIdentity, 16 | Map barSeriesMap, StrategyConfig config); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/strategy/builder/StrategyConfig.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.strategy.builder; 2 | 3 | import com.dtech.kitecon.data.StrategyParameters; 4 | import com.dtech.kitecon.strategy.TradeDirection; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.stream.Collectors; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Builder; 10 | import lombok.Data; 11 | import lombok.NoArgsConstructor; 12 | 13 | @Builder 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | public class StrategyConfig { 17 | private List params; 18 | 19 | public Map getConfig(String key) { 20 | return params.stream().filter(strategyParameters -> strategyParameters.getStrategyType().equals(key)) 21 | .collect(Collectors.toMap(StrategyParameters::getConfigKey, StrategyParameters::getValue)); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/strategy/dataloader/InstrumentDataLoader.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.strategy.dataloader; 2 | 3 | import com.dtech.algo.series.Interval; 4 | import com.dtech.kitecon.data.Instrument; 5 | import com.dtech.kitecon.market.fetch.DataFetchException; 6 | import com.dtech.kitecon.repository.InstrumentRepository; 7 | import com.dtech.kitecon.strategy.exec.HybridDataLoader; 8 | import java.time.ZonedDateTime; 9 | import java.time.temporal.ChronoUnit; 10 | import java.util.Collections; 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.stream.Collectors; 14 | import lombok.RequiredArgsConstructor; 15 | import lombok.extern.log4j.Log4j2; 16 | import org.springframework.stereotype.Component; 17 | import org.ta4j.core.BarSeries; 18 | import org.ta4j.core.BaseBarSeries; 19 | 20 | @RequiredArgsConstructor 21 | @Component 22 | @Log4j2 23 | public class InstrumentDataLoader { 24 | 25 | private final InstrumentRepository instrumentRepository; 26 | private final HybridDataLoader barsLoader; 27 | 28 | public Map loadData(String instrumentName, Interval interval) { 29 | String[] exchanges = new String[]{"NSE", "NFO"}; 30 | List instruments = getRelaventInstruments(instrumentName, exchanges); 31 | return instruments.stream() 32 | .collect(Collectors.toMap(instrument -> 33 | instrument, instrument -> { 34 | try { 35 | return barsLoader.loadInstrumentSeries(instrument, interval); 36 | } catch (DataFetchException e) { 37 | log.catching(e); 38 | return new BaseBarSeries(); 39 | } 40 | })); 41 | } 42 | 43 | public Map loadHybridData(Instrument mappedInstrument, Interval interval) { 44 | String[] exchanges = new String[]{"NSE", "NFO"}; 45 | ZonedDateTime startDate = ZonedDateTime.now().minus(30, ChronoUnit.DAYS); 46 | List instruments = Collections.singletonList(mappedInstrument); 47 | return instruments.stream() 48 | .collect(Collectors.toMap(instrument -> 49 | instrument, instrument -> { 50 | try { 51 | return barsLoader.loadInstrumentSeriesWithLiveData(instrument, startDate, interval); 52 | } catch (DataFetchException e) { 53 | log.catching(e); 54 | return new BaseBarSeries(); 55 | } 56 | })); 57 | } 58 | 59 | 60 | 61 | 62 | private List getRelaventInstruments(String instrument, String[] exchanges) { 63 | return instrumentRepository 64 | .findAllByTradingsymbolStartingWithAndExchangeIn(instrument, exchanges); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/strategy/exec/AlgoTradingRecord.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.strategy.exec; 2 | 3 | import com.dtech.kitecon.market.orders.OrderException; 4 | import org.ta4j.core.TradingRecord; 5 | 6 | public interface AlgoTradingRecord extends TradingRecord { 7 | void updateOrderStatus() throws OrderException; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/strategy/exec/HybridDataLoader.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.strategy.exec; 2 | 3 | import com.dtech.algo.series.Interval; 4 | import com.dtech.kitecon.config.KiteConnectConfig; 5 | import com.dtech.kitecon.data.Candle; 6 | import com.dtech.kitecon.data.Instrument; 7 | import com.dtech.kitecon.market.fetch.DataFetchException; 8 | import com.dtech.kitecon.market.fetch.ZerodhaDataFetch; 9 | import com.dtech.kitecon.repository.CandleRepository; 10 | import com.dtech.kitecon.strategy.dataloader.BarsLoader; 11 | import java.time.ZonedDateTime; 12 | import java.util.List; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.beans.factory.annotation.Qualifier; 15 | import org.springframework.stereotype.Component; 16 | import org.ta4j.core.BarSeries; 17 | 18 | @Qualifier("hybridDataLoader") 19 | @Component 20 | public class HybridDataLoader extends BarsLoader { 21 | 22 | private final KiteConnectConfig connectConfig; 23 | private final ZerodhaDataFetch zerodhaDataFetch; 24 | 25 | @Autowired 26 | public HybridDataLoader( 27 | CandleRepository fifteenMinuteCandleRepository, KiteConnectConfig connectConfig, 28 | ZerodhaDataFetch zerodhaDataFetch) { 29 | super(fifteenMinuteCandleRepository); 30 | this.connectConfig = connectConfig; 31 | this.zerodhaDataFetch = zerodhaDataFetch; 32 | } 33 | 34 | public BarSeries loadInstrumentSeriesWithLiveData(Instrument instrument, ZonedDateTime startDate, 35 | Interval interval) 36 | throws DataFetchException { 37 | BarSeries barSeries = super.loadInstrumentSeries(instrument, startDate, interval); 38 | List todaysFeed = zerodhaDataFetch 39 | .fetchTodaysData(instrument, interval); 40 | if (barSeries.isEmpty()) { 41 | return barSeries; 42 | } 43 | ZonedDateTime endTime = barSeries.getLastBar().getEndTime(); 44 | todaysFeed.stream() 45 | .filter(baseCandle -> baseCandle.getTimestamp().isAfter(endTime.toLocalDateTime())) 46 | .forEach(candle -> super.addBarToSeries(barSeries, candle)); 47 | return barSeries; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/strategy/exec/ProductionSeriesManager.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.strategy.exec; 2 | 3 | import lombok.extern.log4j.Log4j2; 4 | import org.springframework.stereotype.Service; 5 | import org.ta4j.core.BarSeries; 6 | import org.ta4j.core.Strategy; 7 | import org.ta4j.core.TradingRecord; 8 | import org.ta4j.core.num.DecimalNum; 9 | 10 | @Log4j2 11 | @Service 12 | public class ProductionSeriesManager { 13 | 14 | public TradingRecord run(BarSeries barSeries, Strategy strategy, 15 | TradingRecord tradingRecord, Integer quantity) { 16 | int index = barSeries.getEndIndex(); 17 | log.trace("Running strategy (indexes: {} -> {}): {} (starting with {})", 18 | index, index, strategy, 19 | tradingRecord.getCurrentPosition()); 20 | 21 | boolean shouldOperate = strategy.shouldOperate(index, tradingRecord); 22 | if (shouldOperate) { 23 | tradingRecord.operate(index, barSeries.getBar(index).getClosePrice(), 24 | DecimalNum.valueOf(quantity)); 25 | } 26 | return tradingRecord; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/strategy/exec/ProductionStrategyRunner.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.strategy.exec; 2 | 3 | import com.dtech.kitecon.strategy.TradeDirection; 4 | import com.dtech.kitecon.strategy.TradingStrategy; 5 | import java.util.Timer; 6 | import java.util.TimerTask; 7 | import java.util.concurrent.atomic.AtomicBoolean; 8 | import lombok.RequiredArgsConstructor; 9 | import lombok.extern.log4j.Log4j2; 10 | import org.ta4j.core.BarSeries; 11 | import org.ta4j.core.Strategy; 12 | 13 | @Log4j2 14 | @RequiredArgsConstructor 15 | public class ProductionStrategyRunner { 16 | 17 | private final BarSeries barSeries; 18 | private final TradingStrategy tradingStrategy; 19 | private final AlgoTradingRecord record; 20 | private final ProductionSeriesManager productionSeriesManager; 21 | private final TradeDirection tradeDirection; 22 | 23 | private AtomicBoolean isTradeActive = new AtomicBoolean(false); 24 | 25 | public void exec(BarSeries barSeries, TradingStrategy tradingStrategy) { 26 | Strategy strategy = getStrategy(tradingStrategy); 27 | log.info("Running strategy for" + this.record); 28 | productionSeriesManager.run(barSeries, strategy, this.record, 2); 29 | } 30 | 31 | private Strategy getStrategy(TradingStrategy tradingStrategy) { 32 | if (tradeDirection.isBuy()) { 33 | return tradingStrategy.getBuyStrategy(); 34 | } else { 35 | return tradingStrategy.getSellStrategy(); 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/strategy/exec/ProductionTradingRecord.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.strategy.exec; 2 | 3 | import com.dtech.kitecon.data.Instrument; 4 | import com.dtech.kitecon.market.orders.OrderException; 5 | import com.dtech.kitecon.market.orders.OrderManager; 6 | import lombok.extern.log4j.Log4j2; 7 | import org.ta4j.core.BaseTradingRecord; 8 | import org.ta4j.core.Trade; 9 | import org.ta4j.core.num.Num; 10 | 11 | @Log4j2 12 | public class ProductionTradingRecord extends BaseTradingRecord implements AlgoTradingRecord { 13 | 14 | private final OrderManager ordermanager; 15 | private final Instrument instrument; 16 | private int actualQuantity = 0; 17 | private String orderId = null; 18 | private Trade.TradeType orderType = null; 19 | 20 | public ProductionTradingRecord(Trade.TradeType orderType, 21 | OrderManager ordermanager, Instrument instrument) { 22 | super(orderType); 23 | this.ordermanager = ordermanager; 24 | this.instrument = instrument; 25 | this.orderType = orderType; 26 | } 27 | 28 | public void operate(int index, Num price, Num amount) { 29 | super.operate(index, price, amount); 30 | Trade.TradeType type = getOrderType(); 31 | try { 32 | this.orderId = ordermanager.placeMISOrder(price.doubleValue(), 33 | amount.intValue(), instrument, type.name()); 34 | } catch (OrderException e) { 35 | log.catching(e); 36 | } 37 | } 38 | 39 | private Trade.TradeType getOrderType() { 40 | if (getCurrentPosition().isOpened()) { 41 | return orderType; 42 | } else { 43 | return orderType.complementType(); 44 | } 45 | } 46 | 47 | @Override 48 | public void updateOrderStatus() throws OrderException { 49 | this.actualQuantity = ordermanager.getActualOrderStatus(orderId); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/kitecon/strategy/sets/StrategySet.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.strategy.sets; 2 | 3 | import com.dtech.kitecon.strategy.builder.StrategyBuilder; 4 | import java.util.Map; 5 | import java.util.Set; 6 | import java.util.stream.Collectors; 7 | import jakarta.annotation.PostConstruct; 8 | import lombok.RequiredArgsConstructor; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Component; 11 | 12 | @Component 13 | @RequiredArgsConstructor(onConstructor = @__(@Autowired)) 14 | public class StrategySet { 15 | private final Set strategyBuilders; 16 | private Map strategyBuilderMap; 17 | 18 | @PostConstruct 19 | public void buildStrategyBuilderMap() { 20 | strategyBuilderMap = strategyBuilders.stream() 21 | .collect(Collectors.toMap(s -> s.getName(), s -> s)); 22 | } 23 | 24 | public StrategyBuilder getStrategy(String name) { 25 | return strategyBuilderMap.get(name); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/swagger/SwaggerDocumentationConfig.java: -------------------------------------------------------------------------------- 1 | package com.dtech.swagger; 2 | 3 | import io.swagger.v3.oas.models.ExternalDocumentation; 4 | import io.swagger.v3.oas.models.OpenAPI; 5 | import io.swagger.v3.oas.models.info.Info; 6 | import io.swagger.v3.oas.models.info.License; 7 | import org.springdoc.core.models.GroupedOpenApi; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | 11 | @jakarta.annotation.Generated(value = "io.com.dtech.swagger.codegen.v3.generators.java.SpringCodegen", date = "2020-12-24T18:29:06.738Z[GMT]") 12 | @Configuration 13 | public class SwaggerDocumentationConfig { 14 | 15 | @Bean 16 | public GroupedOpenApi publicApi() { 17 | return GroupedOpenApi.builder() 18 | .group("springshop-public") 19 | .pathsToMatch("/stocks/**") 20 | .build(); 21 | } 22 | @Bean 23 | public GroupedOpenApi adminApi() { 24 | return GroupedOpenApi.builder() 25 | .group("springshop-admin") 26 | .pathsToMatch("/admin/**") 27 | .build(); 28 | } 29 | 30 | @Bean 31 | public OpenAPI springShopOpenAPI() { 32 | return new OpenAPI() 33 | .info(new Info().title("SpringShop API") 34 | .description("Spring shop sample application") 35 | .version("v0.0.1") 36 | .license(new License().name("Apache 2.0").url("http://springdoc.org"))) 37 | .externalDocs(new ExternalDocumentation() 38 | .description("SpringShop Wiki Documentation") 39 | .url("/v3/api-docs")); 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /src/main/java/com/dtech/swagger/SwaggerUiConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.dtech.swagger; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 5 | import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; 6 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 7 | 8 | @jakarta.annotation.Generated(value = "io.com.dtech.swagger.codegen.v3.generators.java.SpringCodegen", date = "2020-12-24T18:29:06.738Z[GMT]") 9 | @Configuration 10 | public class SwaggerUiConfiguration implements WebMvcConfigurer { 11 | @Override 12 | public void addResourceHandlers(ResourceHandlerRegistry registry) { 13 | registry. 14 | addResourceHandler("/com.dtech.swagger-ui/**") 15 | .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-com.dtech.swagger-ui/") 16 | .resourceChain(false); 17 | } 18 | 19 | @Override 20 | public void addViewControllers(ViewControllerRegistry registry) { 21 | registry.addViewController("/com.dtech.swagger-ui/").setViewName("forward:/com.dtech.swagger-ui/index.html?configUrl=/v3/api-docs"); 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/BarTuple.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta; 2 | 3 | import lombok.EqualsAndHashCode; 4 | import lombok.Getter; 5 | import lombok.ToString; 6 | import org.ta4j.core.Bar; 7 | 8 | @EqualsAndHashCode(of = {"index", "ohlcType"}) 9 | public class BarTuple { 10 | private int index; 11 | private Bar bar; 12 | @Getter 13 | private OHLC ohlcType; 14 | 15 | public int getIndex() { 16 | return this.index; 17 | } 18 | 19 | public Bar getBar() { 20 | return this.bar; 21 | } 22 | 23 | public BarTuple(int index, Bar bar) { 24 | this.index = index; 25 | this.bar = bar; 26 | ohlcType = OHLC.C; 27 | } 28 | 29 | public BarTuple(int index, Bar bar, OHLC ohlcType) { 30 | this.index = index; 31 | this.bar = bar; 32 | this.ohlcType = ohlcType; 33 | } 34 | 35 | public double getValue() { 36 | switch (ohlcType) { 37 | case O: 38 | return bar.getOpenPrice().doubleValue(); 39 | case H: 40 | return bar.getHighPrice().doubleValue(); 41 | case L: 42 | return bar.getLowPrice().doubleValue(); 43 | case C: 44 | return bar.getClosePrice().doubleValue(); 45 | default: 46 | throw new IllegalStateException("Unexpected OHLC type: " + ohlcType); 47 | } 48 | } 49 | 50 | public String toString() { 51 | return index + " " + bar.getEndTime().toString() + " " + getValue(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/OHLC.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta; 2 | 3 | public enum OHLC { 4 | O, 5 | H, 6 | L, 7 | C 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/TrendLineCalculated.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta; 2 | 3 | import lombok.Data; 4 | import org.ta4j.core.BarSeries; 5 | 6 | import java.util.List; 7 | 8 | @Data 9 | public class TrendLineCalculated { 10 | private final BarSeries barSeries; 11 | private final double slope; 12 | private final double intercept; 13 | private final List points; 14 | private final boolean isSupport; 15 | private double reliabilityScore; 16 | 17 | private double calculateSlope(int startIndex, int endIndex, boolean useHighs) { 18 | if (startIndex >= 0 && endIndex < barSeries.getBarCount()) { // Ensure indices are valid 19 | double y1 = useHighs ? barSeries.getBar(startIndex).getHighPrice().doubleValue() : barSeries.getBar(startIndex).getLowPrice().doubleValue(); 20 | double y2 = useHighs ? barSeries.getBar(endIndex).getHighPrice().doubleValue() : barSeries.getBar(endIndex).getLowPrice().doubleValue(); 21 | return (y2 - y1) / (endIndex - startIndex); 22 | } 23 | return 0; // Return a default slope if indices are out of bounds 24 | } 25 | 26 | private double calculateIntercept(int startIndex, double slope, boolean useHighs) { 27 | double y1 = useHighs ? barSeries.getBar(startIndex).getHighPrice().doubleValue() : barSeries.getBar(startIndex).getLowPrice().doubleValue(); 28 | return y1 - slope * startIndex; 29 | } 30 | 31 | public double getPriceAt(int index) { 32 | return (1 + (slope * (index - points.get(0).getIndex()))) * points.get(0).getValue(); 33 | } 34 | public double calculateYValue(double y) { 35 | // double lastVal = points.getLast().getValue(); 36 | // return (y - points.getLast().getIndex()) * slope * lastVal + lastVal; 37 | return getPriceAt(Double.valueOf(y).intValue()); 38 | } 39 | 40 | public int getStartIndex() { 41 | return points.getFirst().getIndex(); 42 | } 43 | 44 | public int getEndIndex() { 45 | return points.getLast().getIndex(); 46 | } 47 | 48 | public String toString() { 49 | return points.toString(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/divergences/Divergence.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.divergences; 2 | 3 | import com.dtech.ta.BarTuple; 4 | import lombok.Data; 5 | import lombok.RequiredArgsConstructor; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | @RequiredArgsConstructor 11 | @Data 12 | public class Divergence { 13 | private final DivergenceType type; 14 | private final IndicatorType indicator; 15 | private final DivergenceDirection direction; 16 | private final List candles = new ArrayList<>(); 17 | 18 | // Method to add candles 19 | public void addCandle(BarTuple candle) { 20 | this.candles.add(candle); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/divergences/DivergenceAnalyzer.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.divergences; 2 | 3 | import com.dtech.ta.trendline.ActiveTrendlineAnalysis; 4 | import org.springframework.stereotype.Service; 5 | import org.ta4j.core.BarSeries; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | @Service 11 | public class DivergenceAnalyzer { 12 | 13 | // Change the return type to List to return all detected divergences 14 | public List detectTripleDivergences(BarSeries series) { 15 | List detectors = new ArrayList<>(); 16 | detectors.add(new MACDDivergenceDetector(series, new ActiveTrendlineAnalysis())); 17 | detectors.add(new RSIDivergenceDetector(series)); 18 | detectors.add(new StochasticDivergenceDetector(series)); 19 | 20 | List allDivergences = new ArrayList<>(); 21 | for (DivergenceDetector detector : detectors) { 22 | List divergences = detector.detectDivergences(); 23 | allDivergences.addAll(divergences); 24 | } 25 | return allDivergences; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/divergences/DivergenceDetector.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.divergences; 2 | 3 | import org.springframework.stereotype.Service; 4 | import org.ta4j.core.BarSeries; 5 | import java.util.List; 6 | 7 | public abstract class DivergenceDetector { 8 | protected final BarSeries series; 9 | 10 | public DivergenceDetector(BarSeries series) { 11 | this.series = series; 12 | } 13 | 14 | // Abstract method to detect divergences and return a list 15 | public abstract List detectDivergences(); 16 | 17 | protected boolean isPricePeak(int index) { 18 | return series.getBar(index).getHighPrice().isGreaterThan(series.getBar(index - 1).getHighPrice()) 19 | && series.getBar(index).getHighPrice().isGreaterThan(series.getBar(index + 1).getHighPrice()); 20 | } 21 | 22 | protected boolean isPriceTrough(int index) { 23 | return series.getBar(index).getLowPrice().isLessThan(series.getBar(index - 1).getLowPrice()) 24 | && series.getBar(index).getLowPrice().isLessThan(series.getBar(index + 1).getLowPrice()); 25 | } 26 | 27 | protected boolean isConfirmed(int startIndex, int confirmationPeriod, boolean isPrice) { 28 | // Ensure no reverse crossover/movement in confirmation period 29 | for (int i = startIndex; i < startIndex + confirmationPeriod; i++) { 30 | if (isPrice) { 31 | if (isPricePeak(i) || isPriceTrough(i)) { 32 | return false; 33 | } 34 | } else { 35 | if (series.getBar(i).getClosePrice().isGreaterThan(series.getBar(i - 1).getClosePrice())) { 36 | return false; 37 | } 38 | } 39 | } 40 | return true; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/divergences/DivergenceDirection.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.divergences; 2 | 3 | public enum DivergenceDirection { 4 | Bullish, 5 | Bearish 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/divergences/DivergenceType.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.divergences; 2 | 3 | public enum DivergenceType { 4 | Double, 5 | Triple; 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/divergences/IndicatorType.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.divergences; 2 | 3 | public enum IndicatorType { 4 | MACD, 5 | RSI, 6 | STOCH 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/elliott/ElliottWaveAnalyzer.java: -------------------------------------------------------------------------------- 1 | //package com.dtech.ta.elliott; 2 | // 3 | //import com.dtech.ta.elliott.pattern.*; 4 | //import com.dtech.ta.elliott.wave.*; 5 | //import org.ta4j.core.BarSeries; 6 | // 7 | //import java.util.List; 8 | // 9 | //public class ElliottWaveAnalyzer { 10 | // 11 | // private Wave1Detector wave1Detector; 12 | // private Wave2Detector wave2Detector; 13 | // private Wave3Detector wave3Detector; 14 | // private Wave4Detector wave4Detector; 15 | // private Wave5Detector wave5Detector; 16 | // private FlatPatternDetector flatPatternDetector; 17 | // private ExpandedFlatPatternDetector expandedFlatPatternDetector; 18 | // private TrianglePatternDetector trianglePatternDetector; 19 | // private LeadingDiagonalDetector leadingDiagonalDetector; 20 | // private EndingDiagonalDetector endingDiagonalDetector; 21 | // 22 | // public ElliottWaveAnalyzer(BarSeries series) { 23 | // this.wave1Detector = new Wave1Detector(series); 24 | // this.wave3Detector = new Wave3Detector(series); 25 | // } 26 | // 27 | // public void analyzeWaves() { 28 | // // Detect Wave 1 29 | // int wave1Index = wave1Detector.detectWave(); 30 | // 31 | // // Detect Wave 3 32 | // int wave3Index = wave3Detector.detectWave(); 33 | // 34 | //// // Detect Wave 2 (needs Wave 1 and Wave 3) 35 | //// this.wave2Detector = new Wave2Detector(wave1Index, wave3Index); 36 | //// int wave2Index = wave2Detector.detectWave(); 37 | //// 38 | //// // Detect Wave 4 (needs Wave 3) 39 | //// this.wave4Detector = new Wave4Detector(series, wave3Index); 40 | //// int wave4Index = wave4Detector.detectWave(); 41 | //// 42 | //// // Detect Wave 5 (needs Wave 4) 43 | //// this.wave5Detector = new Wave5Detector(series, wave4Index); 44 | //// int wave5Index = wave5Detector.detectWave(); 45 | //// 46 | //// // Detect patterns (example for flat pattern detection) 47 | //// this.flatPatternDetector = new FlatPatternDetector(series, wave4Index); 48 | //// boolean isFlatPattern = flatPatternDetector.detectPattern(); 49 | // 50 | // // Other pattern detection logic here 51 | // } 52 | //} 53 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/elliott/Wave.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.elliott; 2 | 3 | import com.dtech.ta.BarTuple; 4 | import com.dtech.ta.elliott.priceaction.PriceAction; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | import lombok.experimental.Delegate; 9 | 10 | @Data 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | public class Wave { 14 | 15 | private int waveNumber; // 1 to 5, or A, B, C for corrective waves 16 | @Delegate 17 | private PriceAction pa; // Average volume during the wave 18 | 19 | // Convenience methods 20 | public boolean isUpward() { 21 | return pa.isUpward(); 22 | } 23 | 24 | public boolean isDownward() { 25 | return !pa.isUpward(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/elliott/pattern/EndingDiagonalDetector.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.elliott.pattern; 2 | 3 | import org.ta4j.core.BarSeries; 4 | 5 | public class EndingDiagonalDetector extends PatternDetectorBase { 6 | 7 | private int wave5Index; 8 | 9 | public EndingDiagonalDetector(BarSeries series, int wave5Index) { 10 | super(series); 11 | this.wave5Index = wave5Index; 12 | } 13 | 14 | @Override 15 | public boolean detectPattern() { 16 | // Logic to detect Ending Diagonal pattern in Wave 5 17 | return false; // Placeholder logic 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/elliott/pattern/ExpandedFlatPatternDetector.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.elliott.pattern; 2 | 3 | import org.ta4j.core.BarSeries; 4 | 5 | public class ExpandedFlatPatternDetector extends PatternDetectorBase { 6 | 7 | private int wave4Index; 8 | 9 | public ExpandedFlatPatternDetector(BarSeries series, int wave4Index) { 10 | super(series); 11 | this.wave4Index = wave4Index; 12 | } 13 | 14 | @Override 15 | public boolean detectPattern() { 16 | // Logic to detect Expanded Flat Pattern (Wave B exceeds Wave A, Wave C extends beyond A) 17 | return false; // Placeholder logic 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/elliott/pattern/FlatPatternDetector.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.elliott.pattern; 2 | 3 | import org.ta4j.core.BarSeries; 4 | 5 | public class FlatPatternDetector extends PatternDetectorBase { 6 | 7 | private int wave4Index; 8 | 9 | public FlatPatternDetector(BarSeries series, int wave4Index) { 10 | super(series); 11 | this.wave4Index = wave4Index; 12 | } 13 | 14 | @Override 15 | public boolean detectPattern() { 16 | // Logic to detect Flat Pattern in Wave 4 (Wave B retracing fully, Wave C equal to Wave A) 17 | return false; // Placeholder logic 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/elliott/pattern/LeadingDiagonalDetector.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.elliott.pattern; 2 | 3 | import org.ta4j.core.BarSeries; 4 | 5 | public class LeadingDiagonalDetector extends PatternDetectorBase { 6 | 7 | private int wave1Index; 8 | 9 | public LeadingDiagonalDetector(BarSeries series, int wave1Index) { 10 | super(series); 11 | this.wave1Index = wave1Index; 12 | } 13 | 14 | @Override 15 | public boolean detectPattern() { 16 | // Logic to detect Leading Diagonal pattern in Wave 1 17 | return false; // Placeholder logic 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/elliott/pattern/PatternDetectorBase.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.elliott.pattern; 2 | 3 | import org.ta4j.core.BarSeries; 4 | 5 | public abstract class PatternDetectorBase { 6 | protected BarSeries series; 7 | 8 | public PatternDetectorBase(BarSeries series) { 9 | this.series = series; 10 | } 11 | 12 | public abstract boolean detectPattern(); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/elliott/pattern/TrianglePatternDetector.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.elliott.pattern; 2 | 3 | import org.ta4j.core.BarSeries; 4 | 5 | public class TrianglePatternDetector extends PatternDetectorBase { 6 | 7 | private int wave4Index; 8 | 9 | public TrianglePatternDetector(BarSeries series, int wave4Index) { 10 | super(series); 11 | this.wave4Index = wave4Index; 12 | } 13 | 14 | @Override 15 | public boolean detectPattern() { 16 | // Logic to detect Triangle patterns (e.g., contracting or expanding triangles) 17 | return false; // Placeholder logic 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/elliott/priceaction/PriceAction.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.elliott.priceaction; 2 | 3 | import com.dtech.ta.BarTuple; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import org.ta4j.core.BarSeries; 8 | 9 | @Data 10 | @AllArgsConstructor 11 | @NoArgsConstructor 12 | public class PriceAction { 13 | 14 | private String direction; // "up" or "down" 15 | private BarTuple start; 16 | private BarTuple end; 17 | private int startIndex; 18 | private int endIndex; 19 | private double relevantRSI; // Highest for uptrend, lowest for downtrend 20 | private double relevantMACD; // Highest for uptrend, lowest for downtrend 21 | private boolean isRetracement; // True if the movement is corrective, false if impulsive 22 | 23 | public boolean isUpward() { 24 | return direction.equals("up"); 25 | } 26 | 27 | // Example method to calculate total volume 28 | public double getTotalVolume(BarSeries series) { 29 | double totalVolume = 0; 30 | for (int i = startIndex; i <= endIndex; i++) { 31 | totalVolume += series.getBar(i).getVolume().doubleValue(); 32 | } 33 | return totalVolume; 34 | } 35 | 36 | // Example method to calculate average volume 37 | public double getAverageVolume(BarSeries series) { 38 | double totalVolume = getTotalVolume(series); 39 | return totalVolume / (endIndex - startIndex + 1); 40 | } 41 | 42 | // Example method to calculate peak volume 43 | public double getPeakVolume(BarSeries series) { 44 | double peakVolume = 0; 45 | for (int i = startIndex; i <= endIndex; i++) { 46 | double currentVolume = series.getBar(i).getVolume().doubleValue(); 47 | if (currentVolume > peakVolume) { 48 | peakVolume = currentVolume; 49 | } 50 | } 51 | return peakVolume; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/elliott/wave/Wave1Detector.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.elliott.wave; 2 | 3 | import com.dtech.ta.elliott.priceaction.PriceAction; 4 | import org.ta4j.core.BarSeries; 5 | import org.ta4j.core.num.Num; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class Wave1Detector { 11 | 12 | private final BarSeries series; 13 | private final List priceActions; 14 | 15 | public Wave1Detector(BarSeries series, List priceActions) { 16 | this.series = series; 17 | this.priceActions = priceActions; 18 | } 19 | 20 | public List detectPotentialWave1s() { 21 | List potentialWave1s = new ArrayList<>(); 22 | 23 | for (PriceAction pa : priceActions) { 24 | // Check for an upward price action as a candidate for Wave 1 25 | if (pa.isUpward()) { 26 | // Check MACD and RSI for bullish divergence or strong momentum 27 | double macdValue = pa.getRelevantMACD(); 28 | double rsiValue = pa.getRelevantRSI(); 29 | 30 | if (isBullishDivergenceOrMomentum(macdValue, rsiValue)) { 31 | // Check volume (if it's significant compared to the previous PriceActions) 32 | if (isVolumeIncreasing(pa)) { 33 | // Add to the list of potential Wave 1s 34 | potentialWave1s.add(pa); 35 | } 36 | } 37 | } 38 | } 39 | 40 | return potentialWave1s; 41 | } 42 | 43 | private boolean isBullishDivergenceOrMomentum(double macdValue, double rsiValue) { 44 | // Basic checks for MACD and RSI values, can be adjusted for more complex logic 45 | return macdValue > 0 && rsiValue > 30; 46 | } 47 | 48 | private boolean isVolumeIncreasing(PriceAction pa) { 49 | // Check if the volume is increasing compared to previous actions 50 | // This is a simplified example, and could be more complex depending on your data structure 51 | return pa.getAverageVolume(series) > getPreviousVolume(pa); 52 | } 53 | 54 | private double getPreviousVolume(PriceAction pa) { 55 | int index = priceActions.indexOf(pa); 56 | if (index > 0) { 57 | PriceAction previousAction = priceActions.get(index - 1); 58 | return previousAction.getAverageVolume(series); // Use average volume of the previous PriceAction 59 | } 60 | return 0; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/elliott/wave/Wave2Detector.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.elliott.wave; 2 | 3 | import org.ta4j.core.BarSeries; 4 | import org.ta4j.core.num.Num; 5 | 6 | public class Wave2Detector extends WaveDetectorBase { 7 | 8 | private int wave1Index; 9 | private int wave3Index; 10 | 11 | public Wave2Detector(BarSeries series, int wave1Index, int wave3Index) { 12 | super(series); 13 | this.wave1Index = wave1Index; 14 | this.wave3Index = wave3Index; 15 | } 16 | 17 | @Override 18 | public int detectWave() { 19 | Num wave1Price = closePriceIndicator.getValue(wave1Index); 20 | Num wave3Price = closePriceIndicator.getValue(wave3Index); 21 | Num fibonacciRetracementLevel = wave3Price.minus(wave3Price.minus(wave1Price).multipliedBy(series.numOf(0.618))); 22 | 23 | for (int i = wave1Index + 1; i < wave3Index; i++) { 24 | Num currentPrice = closePriceIndicator.getValue(i); 25 | if (currentPrice.isLessThanOrEqual(fibonacciRetracementLevel)) { 26 | return i; // Detected Wave 2 27 | } 28 | } 29 | return wave1Index + 1; // Default if not found 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/elliott/wave/Wave3Detector.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.elliott.wave; 2 | 3 | import org.ta4j.core.BarSeries; 4 | import org.ta4j.core.num.Num; 5 | 6 | public class Wave3Detector extends WaveDetectorBase { 7 | 8 | public Wave3Detector(BarSeries series) { 9 | super(series); 10 | } 11 | 12 | @Override 13 | public int detectWave() { 14 | int wave3Index = 0; 15 | Num highestMacdValue = macdIndicator.getValue(0); 16 | 17 | for (int i = 1; i < series.getBarCount(); i++) { 18 | Num currentMacdValue = macdIndicator.getValue(i); 19 | if (currentMacdValue.isGreaterThan(highestMacdValue)) { 20 | highestMacdValue = currentMacdValue; 21 | wave3Index = i; 22 | } 23 | } 24 | return wave3Index; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/elliott/wave/Wave4Detector.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.elliott.wave; 2 | 3 | import org.ta4j.core.BarSeries; 4 | import org.ta4j.core.num.Num; 5 | 6 | public class Wave4Detector extends WaveDetectorBase { 7 | 8 | private int wave3Index; 9 | 10 | public Wave4Detector(BarSeries series, int wave3Index) { 11 | super(series); 12 | this.wave3Index = wave3Index; 13 | } 14 | 15 | @Override 16 | public int detectWave() { 17 | Num wave3Price = closePriceIndicator.getValue(wave3Index); 18 | Num wave4RetracementLevel = wave3Price.minus(wave3Price.multipliedBy(series.numOf(0.382))); // 38.2% retracement 19 | 20 | for (int i = wave3Index + 1; i < series.getBarCount(); i++) { 21 | Num currentPrice = closePriceIndicator.getValue(i); 22 | if (currentPrice.isLessThanOrEqual(wave4RetracementLevel)) { 23 | return i; // Detected Wave 4 24 | } 25 | } 26 | return wave3Index + 1; // Default if not found 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/elliott/wave/Wave5Detector.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.elliott.wave; 2 | 3 | import org.ta4j.core.BarSeries; 4 | import org.ta4j.core.num.Num; 5 | 6 | public class Wave5Detector extends WaveDetectorBase { 7 | 8 | private int wave4Index; 9 | 10 | public Wave5Detector(BarSeries series, int wave4Index) { 11 | super(series); 12 | this.wave4Index = wave4Index; 13 | } 14 | 15 | @Override 16 | public int detectWave() { 17 | Num wave4Price = closePriceIndicator.getValue(wave4Index); 18 | int wave5Index = wave4Index; 19 | 20 | for (int i = wave4Index + 1; i < series.getBarCount(); i++) { 21 | Num currentPrice = closePriceIndicator.getValue(i); 22 | if (currentPrice.isGreaterThan(wave4Price)) { 23 | wave5Index = i; 24 | } 25 | } 26 | return wave5Index; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/elliott/wave/WaveDetectorBase.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.elliott.wave; 2 | 3 | import org.ta4j.core.BarSeries; 4 | import org.ta4j.core.indicators.MACDIndicator; 5 | import org.ta4j.core.indicators.helpers.ClosePriceIndicator; 6 | 7 | public abstract class WaveDetectorBase { 8 | protected BarSeries series; 9 | protected MACDIndicator macdIndicator; 10 | protected ClosePriceIndicator closePriceIndicator; 11 | 12 | public WaveDetectorBase(BarSeries series) { 13 | this.series = series; 14 | this.closePriceIndicator = new ClosePriceIndicator(series); 15 | this.macdIndicator = new MACDIndicator(closePriceIndicator, 12, 26); 16 | } 17 | 18 | public abstract int detectWave(); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/patterns/DoubleBottomPattern.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.patterns; 2 | 3 | import com.dtech.ta.TrendLineCalculated; 4 | 5 | // Define the DoubleBottomPattern class to hold the pattern details 6 | public class DoubleBottomPattern { 7 | private final int firstLowIndex; 8 | private final int secondLowIndex; 9 | private final int retraceHighIndex; 10 | private final TrendLineCalculated trendline; 11 | 12 | public DoubleBottomPattern(int firstLowIndex, int secondLowIndex, int retraceHighIndex, TrendLineCalculated trendline) { 13 | this.firstLowIndex = firstLowIndex; 14 | this.secondLowIndex = secondLowIndex; 15 | this.retraceHighIndex = retraceHighIndex; 16 | this.trendline = trendline; 17 | } 18 | 19 | public int getFirstLowIndex() { 20 | return firstLowIndex; 21 | } 22 | 23 | public int getSecondLowIndex() { 24 | return secondLowIndex; 25 | } 26 | 27 | public int getRetraceHighIndex() { 28 | return retraceHighIndex; 29 | } 30 | 31 | public TrendLineCalculated getTrendline() { 32 | return trendline; 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/patterns/FlagPattern.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.patterns; 2 | 3 | import com.dtech.ta.TrendLineCalculated; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | 7 | @Data 8 | @AllArgsConstructor 9 | public class FlagPattern { 10 | private final int startIndex; 11 | private final int endIndex; 12 | private final TrendLineCalculated highTrendline; 13 | private final TrendLineCalculated lowTrendline; 14 | 15 | public String toString() { 16 | return highTrendline.getPoints().getFirst().getBar().getDateName() + " " 17 | + highTrendline.getPoints().getLast().getBar().getDateName(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/patterns/IndicatorCalculator.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.patterns; 2 | 3 | import org.ta4j.core.BarSeries; 4 | import org.ta4j.core.indicators.EMAIndicator; 5 | import org.ta4j.core.indicators.RSIIndicator; 6 | import org.ta4j.core.indicators.StochasticOscillatorKIndicator; 7 | import org.ta4j.core.indicators.adx.ADXIndicator; 8 | import org.ta4j.core.indicators.helpers.ClosePriceIndicator; 9 | 10 | public class IndicatorCalculator { 11 | 12 | private final BarSeries series; 13 | private final int emaPeriod; 14 | private RSIIndicator rsi; 15 | private StochasticOscillatorKIndicator stochastic; 16 | private ADXIndicator adx; 17 | private EMAIndicator rsiEma; 18 | private EMAIndicator stochasticEma; 19 | private EMAIndicator adxEma; 20 | 21 | public IndicatorCalculator(BarSeries series, int emaPeriod) { 22 | this.series = series; 23 | this.emaPeriod = emaPeriod; 24 | ClosePriceIndicator closePrice = new ClosePriceIndicator(series); 25 | rsi = new RSIIndicator(closePrice, 14); 26 | stochastic = new StochasticOscillatorKIndicator(series, 14); 27 | adx = new ADXIndicator(series, 14); 28 | 29 | rsiEma = new EMAIndicator(rsi, emaPeriod); 30 | stochasticEma = new EMAIndicator(stochastic, emaPeriod); 31 | adxEma = new EMAIndicator(adx, emaPeriod); 32 | } 33 | 34 | public double getIndicatorAverage(int index) { 35 | return (rsiEma.getValue(index).doubleValue() + stochasticEma.getValue(index).doubleValue() + adxEma.getValue(index).doubleValue()) / 3.0; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/patterns/TrianglePattern.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.patterns; 2 | 3 | import com.dtech.ta.TrendLineCalculated; 4 | 5 | public class TrianglePattern { 6 | private final TrendLineCalculated highTrendline; 7 | private final TrendLineCalculated lowTrendline; 8 | 9 | public TrianglePattern(TrendLineCalculated highTrendline, TrendLineCalculated lowTrendline) { 10 | this.highTrendline = highTrendline; 11 | this.lowTrendline = lowTrendline; 12 | } 13 | 14 | public TrendLineCalculated getHighTrendline() { 15 | return highTrendline; 16 | } 17 | 18 | public TrendLineCalculated getLowTrendline() { 19 | return lowTrendline; 20 | } 21 | 22 | public String toString() { 23 | return lowTrendline.toString(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/trendline/TrendlineAnalyser.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.trendline; 2 | 3 | import com.dtech.algo.series.IntervalBarSeries; 4 | import com.dtech.ta.BarTuple; 5 | import com.dtech.ta.TrendLineCalculated; 6 | import org.ta4j.core.BarSeries; 7 | import java.util.List; 8 | 9 | public interface TrendlineAnalyser { 10 | List analyze(IntervalBarSeries series, boolean validateActive); 11 | 12 | // Combine high/lows using convex hull and sliding window 13 | List getCombinedHighLows(BarSeries series); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/trendline/TrendlineType.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.trendline; 2 | 3 | public enum TrendlineType { 4 | SUPPORT, 5 | RESISTANCE, 6 | BOTH 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/trendline/Util.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.trendline; 2 | 3 | import com.dtech.ta.OHLC; 4 | import org.ta4j.core.Bar; 5 | import org.ta4j.core.BarSeries; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class Util { 11 | 12 | // Find the average distance between High and Low price in a set of candles 13 | public static double avgCandleRange(BarSeries barSeries) { 14 | return Math.max(barSeries.getBarData().stream() 15 | .mapToDouble(bar -> bar.getHighPrice().doubleValue() - bar.getLowPrice().doubleValue()) 16 | .average().orElse(0.0), 0.01); 17 | } 18 | 19 | // Find mean in a list of doubles 20 | public static double mean(List values) { 21 | return values.stream().mapToDouble(Double::doubleValue).average().orElse(0.0); 22 | } 23 | 24 | // Find the set of maximums or minimums in a series with tolerance 25 | public static List findMaxsOrMinsInSeries(BarSeries barSeries, OHLC ohlcType, String kind, double toleranceThreshold) { 26 | List isolatedGlobals = new ArrayList<>(); 27 | double globalMaxOrMin; 28 | 29 | // Determine if we are looking for max or min 30 | if (kind.equals("max")) { 31 | globalMaxOrMin = barSeries.getBarData().stream() 32 | .mapToDouble(bar -> getValueByOHLC(bar, ohlcType)) 33 | .max().orElseThrow(); 34 | } else { 35 | globalMaxOrMin = barSeries.getBarData().stream() 36 | .mapToDouble(bar -> getValueByOHLC(bar, ohlcType)) 37 | .min().orElseThrow(); 38 | } 39 | 40 | // Use a for-loop to iterate over the bars and find matches 41 | for (int i = 0; i < barSeries.getBarCount(); i++) { 42 | Bar bar = barSeries.getBar(i); 43 | double value = getValueByOHLC(bar, ohlcType); 44 | 45 | if (value == globalMaxOrMin || Math.abs(globalMaxOrMin - value) < toleranceThreshold) { 46 | isolatedGlobals.add(i); // Add the index directly 47 | } 48 | } 49 | 50 | return isolatedGlobals; 51 | } 52 | 53 | // Get value by OHLC 54 | private static double getValueByOHLC(Bar bar, OHLC ohlcType) { 55 | switch (ohlcType) { 56 | case O: 57 | return bar.getOpenPrice().doubleValue(); 58 | case H: 59 | return bar.getHighPrice().doubleValue(); 60 | case L: 61 | return bar.getLowPrice().doubleValue(); 62 | case C: 63 | return bar.getClosePrice().doubleValue(); 64 | default: 65 | throw new IllegalStateException("Unexpected OHLC type: " + ohlcType); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/visualize/RSIVisualizer.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.visualize; 2 | 3 | import com.dtech.ta.divergences.Divergence; 4 | import com.dtech.ta.divergences.IndicatorType; 5 | import org.jfree.chart.axis.NumberAxis; 6 | import org.jfree.chart.plot.XYPlot; 7 | import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; 8 | import org.jfree.data.xy.XYSeries; 9 | import org.jfree.data.xy.XYSeriesCollection; 10 | import org.ta4j.core.BarSeries; 11 | import org.ta4j.core.indicators.RSIIndicator; 12 | import org.ta4j.core.indicators.helpers.ClosePriceIndicator; 13 | 14 | import java.util.List; 15 | 16 | import static com.dtech.ta.visualize.VisualizerHelper.plotDivergences; 17 | 18 | public class RSIVisualizer { 19 | public static XYPlot createRSIPlot(BarSeries series, List divergences) { 20 | XYPlot plot = new XYPlot(); 21 | 22 | // Create RSI dataset and renderer 23 | XYSeries rsiSeries = new XYSeries("RSI"); 24 | RSIIndicator rsiIndicator = new RSIIndicator(new ClosePriceIndicator(series), 14); 25 | for (int i = 0; i < series.getBarCount(); i++) { 26 | rsiSeries.add(series.getBar(i).getEndTime().toInstant().toEpochMilli(), rsiIndicator.getValue(i).doubleValue()); 27 | } 28 | XYSeriesCollection dataset = new XYSeriesCollection(rsiSeries); 29 | plot.setDataset(dataset); 30 | XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(true, false); 31 | plot.setRenderer(renderer); 32 | 33 | // Plot divergences on RSI chart 34 | plotDivergences(plot, divergences, IndicatorType.RSI, series); 35 | 36 | // Initialize the range axis and set range for RSI 37 | NumberAxis rangeAxis = new NumberAxis("RSI"); 38 | rangeAxis.setRange(0, 100); 39 | plot.setRangeAxis(rangeAxis); 40 | 41 | return plot; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/visualize/StochasticVisualizer.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.visualize; 2 | 3 | import com.dtech.ta.divergences.Divergence; 4 | import com.dtech.ta.divergences.IndicatorType; 5 | import org.jfree.chart.axis.NumberAxis; 6 | import org.jfree.chart.plot.XYPlot; 7 | import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; 8 | import org.jfree.data.xy.XYSeries; 9 | import org.jfree.data.xy.XYSeriesCollection; 10 | import org.ta4j.core.BarSeries; 11 | import org.ta4j.core.indicators.StochasticOscillatorKIndicator; 12 | 13 | import java.util.List; 14 | 15 | import static com.dtech.ta.visualize.VisualizerHelper.plotDivergences; 16 | 17 | public class StochasticVisualizer { 18 | 19 | public static XYPlot createStochasticPlot(BarSeries series, List divergences) { 20 | XYPlot plot = new XYPlot(); 21 | 22 | // Create Stochastic dataset and renderer 23 | XYSeries stochasticSeries = new XYSeries("Stochastic"); 24 | StochasticOscillatorKIndicator stochasticIndicator = new StochasticOscillatorKIndicator(series, 14); 25 | for (int i = 0; i < series.getBarCount(); i++) { 26 | stochasticSeries.add(series.getBar(i).getEndTime().toInstant().toEpochMilli(), stochasticIndicator.getValue(i).doubleValue()); 27 | } 28 | XYSeriesCollection dataset = new XYSeriesCollection(stochasticSeries); 29 | plot.setDataset(dataset); 30 | XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(true, false); 31 | plot.setRenderer(renderer); 32 | 33 | // Plot divergences on Stochastic chart 34 | plotDivergences(plot, divergences, IndicatorType.STOCH, series); 35 | 36 | // Initialize the range axis and set range for Stochastic 37 | NumberAxis rangeAxis = new NumberAxis("Stochastic"); 38 | rangeAxis.setRange(0, 100); 39 | plot.setRangeAxis(rangeAxis); 40 | 41 | return plot; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/ta/visualize/VisualizerHelper.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.visualize; 2 | 3 | import com.dtech.ta.BarTuple; 4 | import com.dtech.ta.divergences.Divergence; 5 | import com.dtech.ta.divergences.DivergenceDirection; 6 | import com.dtech.ta.divergences.IndicatorType; 7 | import org.jfree.chart.plot.XYPlot; 8 | import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; 9 | import org.jfree.data.xy.XYSeries; 10 | import org.jfree.data.xy.XYSeriesCollection; 11 | import org.ta4j.core.BarSeries; 12 | 13 | import java.awt.*; 14 | import java.util.List; 15 | 16 | public class VisualizerHelper { 17 | 18 | // Method to plot divergences on their respective subplots 19 | public static void plotDivergences(XYPlot plot, List divergences, IndicatorType indicatorType, BarSeries series) { 20 | XYSeriesCollection divergenceDataset = new XYSeriesCollection(); 21 | XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(true, true); // Enable lines and shapes 22 | 23 | XYSeries bullishSeries = new XYSeries("Bullish Divergence"); 24 | XYSeries bearishSeries = new XYSeries("Bearish Divergence"); 25 | 26 | for (Divergence divergence : divergences) { 27 | if (divergence.getIndicator() == indicatorType) { 28 | for (BarTuple candle : divergence.getCandles()) { 29 | double x = series.getBar(candle.getIndex()).getEndTime().toInstant().toEpochMilli(); 30 | double y = candle.getValue(); 31 | 32 | // Add the points for bullish and bearish divergences 33 | if (divergence.getDirection() == DivergenceDirection.Bullish) { 34 | bullishSeries.add(x, y); // Add points to the bullish series 35 | } else { 36 | bearishSeries.add(x, y); // Add points to the bearish series 37 | } 38 | } 39 | } 40 | } 41 | 42 | // Add the series to the dataset 43 | divergenceDataset.addSeries(bullishSeries); 44 | divergenceDataset.addSeries(bearishSeries); 45 | 46 | // Set the dataset and the renderer 47 | plot.setDataset(1, divergenceDataset); 48 | 49 | // Customize the renderer for colors 50 | renderer.setSeriesPaint(0, Color.GREEN); // Green for bullish divergence 51 | renderer.setSeriesPaint(1, Color.RED); // Red for bearish divergence 52 | 53 | // Add renderer to the plot 54 | plot.setRenderer(1, renderer); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/trade/ActiveOrderManager.java: -------------------------------------------------------------------------------- 1 | package com.dtech.trade; 2 | 3 | import com.dtech.kitecon.market.Provider; 4 | import com.dtech.trade.model.Order; 5 | import com.dtech.trade.order.OrderManager; 6 | import com.dtech.trade.repository.OrderRepository; 7 | import lombok.RequiredArgsConstructor; 8 | import org.springframework.stereotype.Service; 9 | 10 | import jakarta.annotation.PostConstruct; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | @RequiredArgsConstructor 16 | @Service 17 | public class ActiveOrderManager { 18 | 19 | protected final OrderRepository repository; 20 | protected final List orderManagerList; 21 | 22 | protected Map orderManagers = new HashMap<>(); 23 | 24 | @PostConstruct 25 | public void initialize() { 26 | orderManagerList.forEach(manager -> orderManagers.put(manager.getProvider(), manager)); 27 | } 28 | 29 | public void placeOrder(Order order, Provider provider) { 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/trade/model/Order.java: -------------------------------------------------------------------------------- 1 | package com.dtech.trade.model; 2 | 3 | import com.dtech.kitecon.data.Instrument; 4 | import com.dtech.trade.order.RealTradeOrder; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | import org.hibernate.annotations.Type; 9 | 10 | import jakarta.persistence.*; 11 | import org.hibernate.usertype.UserType; 12 | 13 | import java.time.ZonedDateTime; 14 | 15 | @Getter 16 | @Setter 17 | @NoArgsConstructor 18 | @Entity(name = "trade_order") 19 | public class Order implements RealTradeOrder { 20 | @Id 21 | @GeneratedValue(strategy = GenerationType.UUID) 22 | @Column(length = 36) 23 | private String parentOrderId; 24 | 25 | @Column 26 | private String exchangeOrderId; 27 | 28 | @Column 29 | private String status; 30 | 31 | @Column 32 | private String statusMessage; 33 | 34 | @Column 35 | private double averagePrice; 36 | 37 | @Column 38 | private Integer fulfilledQuantity; 39 | 40 | @Column 41 | private ZonedDateTime timestamp; 42 | 43 | @Column 44 | private Integer disclosedQuantity; 45 | @Column 46 | private String validity; 47 | 48 | @ManyToOne 49 | private Instrument instrument; 50 | 51 | @Column 52 | private String orderVariety; 53 | @Column 54 | private String userId; 55 | @Column 56 | private String orderType; 57 | @Column 58 | private double triggerPrice; 59 | @Column 60 | private Double price; 61 | @Column 62 | private String product; 63 | @Column 64 | private String accountId; 65 | @Column 66 | private String exchange; 67 | @Column 68 | private String orderId; 69 | @Column 70 | private String symbol; 71 | @Column 72 | private Integer pendingQuantity; 73 | @Column 74 | private ZonedDateTime orderTimestamp; 75 | @Column 76 | private ZonedDateTime exchangeTimestamp; 77 | @Column 78 | private ZonedDateTime exchangeUpdateTimestamp; 79 | 80 | @Column 81 | private Integer quantity; 82 | @Column 83 | private String transactionType; 84 | 85 | @Column 86 | @Enumerated(EnumType.STRING) 87 | private OrderStatus orderStatus; 88 | 89 | } 90 | 91 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/trade/model/OrderStatus.java: -------------------------------------------------------------------------------- 1 | package com.dtech.trade.model; 2 | 3 | public enum OrderStatus { 4 | CREATED, 5 | SENT_TO_EXCHANGE, 6 | PARTIAL_COMPLETE, 7 | COMPLETE, 8 | FAILED, 9 | EXITED 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/trade/order/OrderManager.java: -------------------------------------------------------------------------------- 1 | package com.dtech.trade.order; 2 | 3 | import com.dtech.kitecon.data.Instrument; 4 | import com.dtech.kitecon.market.Provider; 5 | import com.dtech.kitecon.market.orders.OrderException; 6 | import com.dtech.trade.model.Order; 7 | 8 | public interface OrderManager { 9 | 10 | public Provider getProvider(); 11 | 12 | RealTradeOrder placeIntradayLimitsOrder(RealTradeOrder order) 13 | throws OrderException; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/trade/order/RealTradeOrder.java: -------------------------------------------------------------------------------- 1 | package com.dtech.trade.order; 2 | 3 | import com.dtech.kitecon.data.Instrument; 4 | 5 | import java.time.ZonedDateTime; 6 | 7 | public interface RealTradeOrder { 8 | 9 | String getExchangeOrderId(); 10 | 11 | String getStatus(); 12 | 13 | String getStatusMessage(); 14 | 15 | double getAveragePrice(); 16 | 17 | Integer getFulfilledQuantity(); 18 | 19 | ZonedDateTime getTimestamp(); 20 | 21 | 22 | Integer getDisclosedQuantity(); 23 | String getValidity(); 24 | Instrument getInstrument(); 25 | 26 | String getOrderVariety(); 27 | String getUserId(); 28 | String getOrderType(); 29 | double getTriggerPrice(); 30 | Double getPrice(); 31 | String getProduct(); 32 | String getAccountId(); 33 | String getExchange(); 34 | String getOrderId(); 35 | String getSymbol(); 36 | Integer getPendingQuantity(); 37 | ZonedDateTime getOrderTimestamp(); 38 | ZonedDateTime getExchangeTimestamp(); 39 | ZonedDateTime getExchangeUpdateTimestamp(); 40 | String getTransactionType(); 41 | 42 | Integer getQuantity(); 43 | 44 | String getParentOrderId(); 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/trade/repository/OrderRepository.java: -------------------------------------------------------------------------------- 1 | package com.dtech.trade.repository; 2 | 3 | import com.dtech.trade.model.Order; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface OrderRepository extends JpaRepository { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/dtech/trade/zerodha/KiteOrderManager.java: -------------------------------------------------------------------------------- 1 | package com.dtech.trade.zerodha; 2 | 3 | import com.dtech.kitecon.config.KiteConnectConfig; 4 | import com.dtech.kitecon.market.Provider; 5 | import com.dtech.kitecon.market.orders.OrderException; 6 | import com.dtech.trade.order.OrderManager; 7 | import com.dtech.trade.order.RealTradeOrder; 8 | import com.zerodhatech.kiteconnect.KiteConnect; 9 | import com.zerodhatech.models.Order; 10 | import com.zerodhatech.models.OrderParams; 11 | import lombok.RequiredArgsConstructor; 12 | import org.springframework.context.annotation.Primary; 13 | import org.springframework.stereotype.Service; 14 | 15 | @RequiredArgsConstructor 16 | @Service 17 | @Primary 18 | public class KiteOrderManager implements OrderManager { 19 | 20 | private final KiteConnectConfig connectConfig; 21 | 22 | @Override 23 | public Provider getProvider() { 24 | return Provider.ZERODHA; 25 | } 26 | 27 | @Override 28 | public RealTradeOrder placeIntradayLimitsOrder(RealTradeOrder order) throws OrderException { 29 | OrderParams params = new OrderParams(); 30 | params.exchange = "NSE"; 31 | params.tradingsymbol = order.getInstrument().getTradingsymbol(); 32 | params.transactionType = order.getOrderType().toUpperCase(); 33 | params.quantity = order.getQuantity(); 34 | params.price = order.getPrice(); 35 | params.product = "MIS"; 36 | params.orderType = "LIMIT"; 37 | params.validity = "DAY"; 38 | params.disclosedQuantity = order.getDisclosedQuantity(); 39 | params.parentOrderId = order.getParentOrderId(); 40 | try { 41 | Order exchangeOrder = connectConfig.getKiteConnect().placeOrder(params, "regular"); 42 | return new ZerodhaOrder(exchangeOrder, order.getInstrument()); 43 | } catch (Throwable e) { 44 | throw new OrderException(e); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.driverClassName=com.mysql.jdbc.Driver 2 | spring.datasource.url=jdbc:mysql://localhost:3306/algotrading?autoreconnect=true 3 | spring.datasource.username=anand 4 | spring.datasource.password=password 5 | spring.jpa.hibernate.ddl-auto=update 6 | 7 | hibernate.dialect.storage_engine=innodb 8 | server.port=8080 9 | #spring.jpa.show-sql=true 10 | #spring.datasource.testOnBorrow=true 11 | #spring.datasource.testWhileIdle=true 12 | #spring.datasource.timeBetweenEvictionRunsMillis=60000 13 | #spring.datasource.minEvictableIdleTimeMillis=30000 14 | #spring.datasource.validationQuery=SELECT 1 15 | #spring.datasource.max-active=15 16 | #spring.datasource.max-idle=10 17 | #spring.datasource.max-wait=8000 18 | 19 | kite.api.key=1g19o5ohebi8fj3l 20 | kite.api.user=ZQ5356 21 | kite.api.secret=47hfn5uwrb138506whg0lk26w6pxiadi 22 | 23 | spring.application.admin.enabled=true 24 | 25 | spring.jackson.serialization.FAIL_ON_EMPTY_BEANS=false -------------------------------------------------------------------------------- /src/main/resources/static/js/image-browser.js: -------------------------------------------------------------------------------- 1 | // image-browser.js 2 | function fetchImages() { 3 | const directory = document.getElementById("directoryInput").value; 4 | if (!directory) { 5 | alert("Please enter a directory path"); 6 | return; 7 | } 8 | 9 | // Fetch the list of image filenames from the server 10 | fetch(`/images?directory=${encodeURIComponent(directory)}`) 11 | .then(response => response.json()) 12 | .then(imageFiles => { 13 | const imageSelector = document.getElementById("imageSelector"); 14 | imageSelector.innerHTML = ""; // Clear previous options 15 | 16 | // Populate the select dropdown with image filenames 17 | imageFiles.forEach((file) => { 18 | const option = document.createElement("option"); 19 | option.value = file; 20 | option.textContent = file; 21 | imageSelector.appendChild(option); 22 | }); 23 | }) 24 | .catch(error => console.error('Error fetching image list:', error)); 25 | } 26 | 27 | function loadImage() { 28 | const directory = document.getElementById("directoryInput").value; 29 | const selectedImage = document.getElementById("imageSelector").value; 30 | const displayedImage = document.getElementById("displayedImage"); 31 | 32 | if (!selectedImage) { 33 | alert("Please select an image"); 34 | return; 35 | } 36 | 37 | displayedImage.src = `/images/view?directory=${encodeURIComponent(directory)}&filename=${encodeURIComponent(selectedImage)}`; 38 | displayedImage.alt = selectedImage; 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/com/dtech/algo/controller/MetadataControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.controller; 2 | 3 | import com.dtech.algo.exception.StrategyException; 4 | import com.dtech.algo.indicators.IndicatorInfo; 5 | import com.dtech.algo.indicators.IndicatorRegistry; 6 | import com.dtech.algo.rules.RuleInfo; 7 | import com.dtech.algo.rules.RuleRegistry; 8 | import com.dtech.algo.strategy.helper.ComponentHelper; 9 | import org.junit.jupiter.api.Test; 10 | import org.junit.jupiter.api.extension.ExtendWith; 11 | import org.mockito.InjectMocks; 12 | import org.mockito.Mock; 13 | import org.mockito.Mockito; 14 | import org.mockito.junit.jupiter.MockitoExtension; 15 | 16 | import java.util.Collections; 17 | import java.util.Map; 18 | 19 | import static org.junit.jupiter.api.Assertions.*; 20 | 21 | @ExtendWith(MockitoExtension.class) 22 | class MetadataControllerTest { 23 | 24 | @Mock 25 | private IndicatorRegistry indicatorRegistry; 26 | 27 | @Mock 28 | private RuleRegistry ruleRegistry; 29 | 30 | @InjectMocks 31 | private MetadataController metadataController; 32 | 33 | private ComponentHelper componentHelper = new ComponentHelper(null, null, null, null); 34 | 35 | @Test 36 | void getRuleDetails() throws StrategyException { 37 | String name = "constant-indicator"; 38 | Mockito.doReturn(Collections.singletonList(name)) 39 | .when(indicatorRegistry).getAllObjectNames(); 40 | Mockito.doReturn(componentHelper.getConstantIndicatorInfo("a", "b", "c")) 41 | .when(indicatorRegistry).getObjectInfo(name); 42 | Map indicatorDetails = metadataController.getIndicatorDetails(); 43 | assertEquals(indicatorDetails.get(name).getName(), "c"); 44 | } 45 | 46 | @Test 47 | void getIndicatorDetails() throws StrategyException { 48 | String name = "and-rule"; 49 | Mockito.doReturn(Collections.singletonList(name)) 50 | .when(ruleRegistry).getAllObjectNames(); 51 | Mockito.doReturn(componentHelper.getGenericRuleInfo(name)) 52 | .when(ruleRegistry).getObjectInfo(name); 53 | Map indicatorDetails = metadataController.getRuleDetails(); 54 | assertEquals(indicatorDetails.get(name).getName(), name); 55 | } 56 | } -------------------------------------------------------------------------------- /src/test/java/com/dtech/algo/runner/candle/LatestBarSeriesProviderFromCacheTest.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.runner.candle; 2 | 3 | import com.dtech.algo.exception.StrategyException; 4 | import com.dtech.algo.series.*; 5 | import com.dtech.algo.strategy.builder.ifc.BarSeriesLoader; 6 | import com.dtech.algo.strategy.config.BarSeriesConfig; 7 | import com.dtech.kitecon.KiteconApplication; 8 | import org.junit.jupiter.api.Test; 9 | import org.junit.jupiter.api.extension.ExtendWith; 10 | import org.mockito.ArgumentMatchers; 11 | import org.mockito.Mockito; 12 | import org.mockito.junit.jupiter.MockitoExtension; 13 | import org.springframework.boot.test.context.SpringBootTest; 14 | import org.springframework.boot.test.mock.mockito.MockBean; 15 | import org.springframework.boot.test.mock.mockito.SpyBean; 16 | import org.springframework.test.annotation.DirtiesContext; 17 | 18 | import java.time.LocalDate; 19 | 20 | import static org.junit.jupiter.api.Assertions.assertEquals; 21 | import static org.mockito.Mockito.atLeast; 22 | import static org.mockito.Mockito.times; 23 | 24 | @SpringBootTest(classes = KiteconApplication.class) 25 | class LatestBarSeriesProviderFromCacheTest { 26 | 27 | @MockBean 28 | private BarSeriesLoader delegate; 29 | 30 | @SpyBean 31 | private LatestBarSeriesProvider barSeriesProvider; 32 | 33 | @Test 34 | @DirtiesContext 35 | void loadBarSeriesFromCache() throws StrategyException { 36 | LocalDate date = LocalDate.now().minusDays(10); 37 | ExtendedBarSeries barSeries = new ExtendedBarSeries(); 38 | Mockito.doReturn(barSeries) 39 | .when(delegate).loadBarSeries(Mockito.any(BarSeriesConfig.class)); 40 | 41 | IntervalBarSeries intervalBarSeries = barSeriesProvider.loadBarSeries(getBarSeriesConfigSbinCash15Min(date, date)); 42 | IntervalBarSeries intervalBarSeries2 = barSeriesProvider.loadBarSeries(getBarSeriesConfigSbinCash15Min(date, date)); 43 | assertEquals(barSeries, intervalBarSeries2); 44 | assertEquals(intervalBarSeries, intervalBarSeries2); 45 | } 46 | 47 | private BarSeriesConfig getBarSeriesConfigSbinCash15Min(LocalDate endDate, LocalDate startDate) { 48 | return BarSeriesConfig.builder() 49 | .seriesType(SeriesType.EQUITY) 50 | .exchange(Exchange.NSE) 51 | .instrument("SBIN") 52 | .instrumentType(InstrumentType.EQ) 53 | .interval(Interval.FifteenMinute) 54 | .name("sbin15min") 55 | .endDate(endDate) 56 | .startDate(startDate) 57 | .build(); 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /src/test/java/com/dtech/algo/runner/candle/LatestBarSeriesProviderTest.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.runner.candle; 2 | 3 | import com.dtech.algo.exception.StrategyException; 4 | import com.dtech.algo.series.*; 5 | import com.dtech.algo.strategy.builder.ifc.BarSeriesLoader; 6 | import com.dtech.algo.strategy.config.BarSeriesConfig; 7 | import com.dtech.kitecon.KiteconApplication; 8 | import org.junit.jupiter.api.Test; 9 | import org.junit.jupiter.api.extension.ExtendWith; 10 | import org.mockito.ArgumentMatchers; 11 | import org.mockito.Mockito; 12 | import org.mockito.junit.jupiter.MockitoExtension; 13 | import org.springframework.boot.test.context.SpringBootTest; 14 | import org.springframework.boot.test.mock.mockito.MockBean; 15 | import org.springframework.boot.test.mock.mockito.SpyBean; 16 | 17 | import java.time.LocalDate; 18 | 19 | import static org.junit.jupiter.api.Assertions.assertEquals; 20 | import static org.mockito.Mockito.times; 21 | 22 | @ExtendWith(MockitoExtension.class) 23 | @SpringBootTest(classes = KiteconApplication.class) 24 | class LatestBarSeriesProviderTest { 25 | 26 | @MockBean 27 | private BarSeriesLoader delegate; 28 | 29 | @SpyBean 30 | private LatestBarSeriesProvider barSeriesProvider; 31 | 32 | @Test 33 | void loadBarSeries() throws StrategyException { 34 | LocalDate date = LocalDate.now().minusDays(10); 35 | ExtendedBarSeries barSeries = new ExtendedBarSeries(); 36 | Mockito.doReturn(barSeries) 37 | .when(delegate).loadBarSeries(Mockito.any(BarSeriesConfig.class)); 38 | 39 | IntervalBarSeries intervalBarSeries = barSeriesProvider.loadBarSeries(getBarSeriesConfigSbinCash15Min(date, date)); 40 | Mockito.verify(delegate, times(1)) 41 | .loadBarSeries(ArgumentMatchers.argThat(argument -> 42 | argument.getEndDate().equals(LocalDate.now()))); 43 | assertEquals(barSeries, intervalBarSeries); 44 | 45 | } 46 | 47 | private BarSeriesConfig getBarSeriesConfigSbinCash15Min(LocalDate endDate, LocalDate startDate) { 48 | return BarSeriesConfig.builder() 49 | .seriesType(SeriesType.EQUITY) 50 | .exchange(Exchange.NSE) 51 | .instrument("SBIN") 52 | .instrumentType(InstrumentType.EQ) 53 | .interval(Interval.FifteenMinute) 54 | .name("sbin15min") 55 | .endDate(endDate) 56 | .startDate(startDate) 57 | .build(); 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /src/test/java/com/dtech/algo/strategy/TestHelper.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy; 2 | 3 | public class TestHelper { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/test/java/com/dtech/algo/strategy/builder/FinalStrategyBuilderTest.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.builder; 2 | 3 | import com.dtech.algo.exception.StrategyException; 4 | import com.dtech.algo.strategy.TradeStrategy; 5 | import com.dtech.algo.strategy.builder.cache.BarSeriesCache; 6 | import com.dtech.algo.strategy.builder.ifc.BarSeriesLoader; 7 | import com.dtech.algo.strategy.config.*; 8 | import com.dtech.algo.strategy.helper.ComponentHelper; 9 | import com.dtech.algo.strategy.units.CachedIndicatorBuilder; 10 | import com.dtech.algo.strategy.units.CachedRuleBuilder; 11 | import com.dtech.kitecon.KiteconApplication; 12 | 13 | import java.util.*; 14 | 15 | import org.junit.jupiter.api.Assertions; 16 | import org.junit.jupiter.api.Test; 17 | import org.springframework.beans.factory.annotation.Autowired; 18 | import org.springframework.boot.test.context.SpringBootTest; 19 | import org.springframework.boot.test.mock.mockito.MockBean; 20 | import org.ta4j.core.BarSeriesManager; 21 | import org.ta4j.core.TradingRecord; 22 | import org.ta4j.core.analysis.criteria.pnl.GrossProfitCriterion; 23 | import org.ta4j.core.num.Num; 24 | 25 | @SpringBootTest(classes = {KiteconApplication.class}) 26 | class FinalStrategyBuilderTest { 27 | 28 | private List indicatorConfigs = new ArrayList<>(); 29 | 30 | @MockBean 31 | private BarSeriesLoader barSeriesLoader; 32 | 33 | @Autowired 34 | private BarSeriesCache barSeriesCache; 35 | 36 | @Autowired 37 | private CachedIndicatorBuilder cachedIndicatorBuilder; 38 | 39 | @Autowired 40 | private CachedRuleBuilder cachedRuleBuilder; 41 | 42 | @Autowired 43 | private StrategyBuilderIfc strategyBuilderIfc; 44 | 45 | @Autowired 46 | private ComponentHelper componentHelper; 47 | 48 | @Test 49 | void buildStrategy() throws StrategyException { 50 | //Constants 51 | StrategyConfig config = componentHelper.buildSimpleSmaStrategy(); 52 | 53 | TradeStrategy tradeStrategy = strategyBuilderIfc.buildStrategy(config); 54 | BarSeriesManager seriesManager = new BarSeriesManager(componentHelper.getBarSeries()); 55 | TradingRecord tradingRecord = seriesManager.run(tradeStrategy); 56 | 57 | Num profit = new GrossProfitCriterion().calculate(componentHelper.getBarSeries(), tradingRecord); 58 | Assertions.assertEquals(tradingRecord.getPositionCount(), 5); 59 | Assertions.assertEquals(profit.doubleValue(), 281.73, 0.001); 60 | 61 | System.out.println("Number of trades for the strategy: " + tradingRecord.getPositionCount()); 62 | // Analysis 63 | System.out.println( 64 | "Total profit for the strategy: " + profit); 65 | } 66 | 67 | } -------------------------------------------------------------------------------- /src/test/java/com/dtech/algo/strategy/cache/ThreadLocalCacheTest.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.cache; 2 | 3 | import static org.junit.jupiter.api.Assertions.*; 4 | 5 | import com.dtech.algo.strategy.builder.cache.ThreadLocalCache; 6 | import java.util.concurrent.ExecutionException; 7 | import java.util.concurrent.ExecutorService; 8 | import java.util.concurrent.Executors; 9 | import java.util.concurrent.TimeUnit; 10 | import org.junit.jupiter.api.Test; 11 | import org.junit.jupiter.api.extension.ExtendWith; 12 | import org.mockito.InjectMocks; 13 | import org.mockito.junit.jupiter.MockitoExtension; 14 | 15 | @ExtendWith(MockitoExtension.class) 16 | class ThreadLocalCacheTest { 17 | 18 | @InjectMocks 19 | private ThreadLocalCache constantsCache; 20 | 21 | @Test 22 | public void getConstant() { 23 | constantsCache.put("a", "value"); 24 | assertEquals(constantsCache.get("a"), "value"); 25 | } 26 | 27 | @Test 28 | public void reset() { 29 | getConstant(); 30 | constantsCache.reset(); 31 | assertEquals(constantsCache.get("a"), null); 32 | } 33 | 34 | @Test 35 | public void threadSafety() throws ExecutionException, InterruptedException { 36 | getConstant(); 37 | ExecutorService exec = Executors.newSingleThreadExecutor(); 38 | assertNull(exec.submit(() -> constantsCache.get("a")).get()); 39 | exec.shutdown(); 40 | exec.awaitTermination(1, TimeUnit.SECONDS); 41 | } 42 | 43 | 44 | } -------------------------------------------------------------------------------- /src/test/java/com/dtech/algo/strategy/config/BarSeriesConfigTest.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.config; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 6 | import org.junit.jupiter.api.Test; 7 | import org.junit.jupiter.api.extension.ExtendWith; 8 | import org.mockito.junit.jupiter.MockitoExtension; 9 | 10 | class BarSeriesConfigTest { 11 | 12 | private static String jsonValue = "{\n" + 13 | " \"interval\" : \"FifteenMinute\",\n" + 14 | " \"seriesType\" : \"EQUITY\",\n" + 15 | " \"instrumentType\" : \"EQ\",\n" + 16 | " \"exchange\" : \"NSE\",\n" + 17 | " \"instrument\" : \"SBIN\",\n" + 18 | " \"name\" : \"sbin15min\",\n" + 19 | " \"startDate\" : \"2020-06-13\",\n" + 20 | " \"endDate\" : \"2020-09-13\"\n" + 21 | " }"; 22 | 23 | @Test 24 | public void testJson() throws JsonProcessingException { 25 | ObjectMapper objectMapper = new ObjectMapper(); 26 | objectMapper.registerModule(new JavaTimeModule()); 27 | BarSeriesConfig config = objectMapper.readValue(jsonValue, BarSeriesConfig.class); 28 | System.out.println(config); 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /src/test/java/com/dtech/algo/strategy/sync/CandleSyncExecutorTest.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.sync; 2 | 3 | import com.dtech.algo.series.Interval; 4 | import com.dtech.kitecon.repository.CandleRepository; 5 | import com.dtech.kitecon.service.CandleFacade; 6 | import org.junit.jupiter.api.Test; 7 | import org.junit.jupiter.api.extension.ExtendWith; 8 | import org.mockito.InjectMocks; 9 | import org.mockito.Mock; 10 | import org.mockito.Mockito; 11 | import org.mockito.Spy; 12 | import org.mockito.junit.jupiter.MockitoExtension; 13 | import org.ta4j.core.BaseBar; 14 | 15 | import static org.mockito.ArgumentMatchers.any; 16 | import static org.mockito.ArgumentMatchers.eq; 17 | 18 | @ExtendWith(MockitoExtension.class) 19 | class CandleSyncExecutorTest { 20 | 21 | @Mock 22 | private BaseBar baseBar1; 23 | 24 | @Mock 25 | private CandleFacade candleFacade; 26 | 27 | @Mock 28 | private CandleRepository candleRepository; 29 | 30 | @InjectMocks 31 | @Spy 32 | private CandleSyncExecutor candleSyncExecutor; 33 | 34 | @Test 35 | void queueNewCandle() throws InterruptedException { 36 | Long instrument = 1L; 37 | CandleSyncToken syncToken = new CandleSyncToken(baseBar1, instrument.toString(), Interval.FifteenMinute); 38 | CandleSyncJob syncJob = new CandleSyncJob(candleRepository, candleFacade, 39 | syncToken); 40 | CandleSyncJob job = Mockito.spy(syncJob); 41 | Mockito.doReturn(job).when(candleSyncExecutor).getSyncJob(any()); 42 | candleSyncExecutor.initialize(); 43 | candleSyncExecutor.submit(syncToken); 44 | candleSyncExecutor.shutdown(); 45 | Mockito.verify(job, Mockito.times(1)).insertNewCandle(eq(instrument), eq(baseBar1), 46 | eq(Interval.FifteenMinute)); 47 | } 48 | } -------------------------------------------------------------------------------- /src/test/java/com/dtech/algo/strategy/sync/CandleSyncJobTest.java: -------------------------------------------------------------------------------- 1 | package com.dtech.algo.strategy.sync; 2 | 3 | import com.dtech.algo.series.Interval; 4 | import com.dtech.kitecon.repository.CandleRepository; 5 | import com.dtech.kitecon.service.CandleFacade; 6 | import org.junit.jupiter.api.BeforeEach; 7 | import org.junit.jupiter.api.Test; 8 | import org.junit.jupiter.api.extension.ExtendWith; 9 | import org.mockito.Mock; 10 | import org.mockito.Mockito; 11 | import org.mockito.junit.jupiter.MockitoExtension; 12 | import org.ta4j.core.BaseBar; 13 | 14 | import java.util.concurrent.ExecutorService; 15 | import java.util.concurrent.Executors; 16 | 17 | import static org.mockito.ArgumentMatchers.any; 18 | import static org.mockito.ArgumentMatchers.eq; 19 | 20 | @ExtendWith(MockitoExtension.class) 21 | class CandleSyncJobTest { 22 | 23 | @Mock 24 | private BaseBar baseBar1; 25 | 26 | @Mock 27 | private CandleFacade candleFacade; 28 | 29 | @Mock 30 | private CandleRepository candleRepository; 31 | 32 | @BeforeEach 33 | public void setup() { 34 | Mockito.doNothing().when(candleRepository).saveAll(any()); 35 | } 36 | 37 | @Test 38 | void queueNewCandle() throws InterruptedException { 39 | Long instrument = 1L; 40 | CandleSyncToken syncToken = new CandleSyncToken(baseBar1, instrument.toString(), Interval.FifteenMinute); 41 | CandleSyncJob syncJob = new CandleSyncJob(candleRepository, candleFacade, syncToken); 42 | ExecutorService service = Executors.newFixedThreadPool(1); 43 | CandleSyncJob job = Mockito.spy(syncJob); 44 | job.run(); 45 | Mockito.verify(job, Mockito.times(1)).insertNewCandle(instrument, baseBar1, Interval.FifteenMinute); 46 | Mockito.verify(candleRepository, Mockito.times(1)).saveAll( Mockito.any()); 47 | } 48 | } 49 | 50 | //Satyam Negi - 8859642930 51 | //Suraj Prakash - 9954245119 -------------------------------------------------------------------------------- /src/test/java/com/dtech/chart/ChartCreator.java: -------------------------------------------------------------------------------- 1 | package com.dtech.chart; 2 | 3 | import com.dtech.chart.TriangleVisualizer; 4 | import com.dtech.ta.BarTuple; 5 | import org.jfree.chart.ChartFactory; 6 | import org.jfree.chart.JFreeChart; 7 | import org.jfree.chart.plot.PlotOrientation; 8 | import org.jfree.chart.plot.XYPlot; 9 | 10 | import org.jfree.data.xy.XYSeries; 11 | import org.jfree.data.xy.XYSeriesCollection; 12 | import org.ta4j.core.Bar; 13 | import org.ta4j.core.BarSeries; 14 | 15 | import java.io.File; 16 | import java.io.IOException; 17 | import java.util.List; 18 | 19 | public class ChartCreator { 20 | 21 | public JFreeChart createChart(BarSeries series, List maxima, List minima) { 22 | XYSeriesCollection dataset = new XYSeriesCollection(); 23 | 24 | XYSeries highSeries = new XYSeries("High"); 25 | XYSeries lowSeries = new XYSeries("Low"); 26 | XYSeries maxSeries = new XYSeries("Significant Maxima"); 27 | XYSeries minSeries = new XYSeries("Significant Minima"); 28 | 29 | for (int i = 0; i < series.getBarCount(); i++) { 30 | Bar bar = series.getBar(i); 31 | highSeries.add(i, bar.getHighPrice().doubleValue()); 32 | lowSeries.add(i, bar.getLowPrice().doubleValue()); 33 | } 34 | 35 | for (BarTuple tuple : maxima) { 36 | Bar bar = tuple.getBar(); 37 | int index = tuple.getIndex(); 38 | maxSeries.add(index, bar.getHighPrice().doubleValue()); 39 | } 40 | 41 | for (BarTuple tuple : minima) { 42 | Bar bar = tuple.getBar(); 43 | int index = tuple.getIndex(); 44 | minSeries.add(index, bar.getLowPrice().doubleValue()); 45 | } 46 | 47 | dataset.addSeries(highSeries); 48 | dataset.addSeries(lowSeries); 49 | dataset.addSeries(maxSeries); 50 | dataset.addSeries(minSeries); 51 | 52 | JFreeChart chart = ChartFactory.createXYLineChart( 53 | "OHLC Chart", "Index", "Price", 54 | dataset, PlotOrientation.VERTICAL, 55 | true, true, false 56 | ); 57 | 58 | XYPlot plot = chart.getXYPlot(); 59 | plot.setDomainPannable(true); 60 | plot.setRangePannable(true); 61 | 62 | TriangleVisualizer.saveChartAsJPEG("Maxima_minima", chart); 63 | 64 | return chart; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/test/java/com/dtech/kitecon/service/DateRangeTest.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.service; 2 | 3 | import static org.junit.jupiter.api.Assertions.*; 4 | 5 | import java.time.ZonedDateTime; 6 | import java.util.List; 7 | import org.junit.jupiter.api.Test; 8 | 9 | class DateRangeTest { 10 | 11 | @Test 12 | public void testDateRangeSplit() { 13 | ZonedDateTime endDate = ZonedDateTime.now(); 14 | ZonedDateTime startDate = endDate.minusYears(1); 15 | DateRange dateRange = DateRange.builder() 16 | .endDate(endDate) 17 | .startDate(startDate) 18 | .build(); 19 | List splits = dateRange.split(30); 20 | assertEquals(splits.size(), 13); 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /src/test/java/com/dtech/kitecon/service/PnLCalculator.java: -------------------------------------------------------------------------------- 1 | package com.dtech.kitecon.service; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | public class PnLCalculator { 6 | 7 | @Test 8 | void calculatePnL() { 9 | double amount = 50000.0; 10 | double tentativePrice = 240; 11 | double marginPercentage = 21.0; 12 | double buyProfit = 250.0; 13 | double sellProfit = 118.0; 14 | double brokerage = 138.0; 15 | 16 | int numberTrades = 298 + 275; 17 | 18 | System.out.println(); 19 | 20 | double availableMargin = amount * 100 / marginPercentage; 21 | System.out.println("available margin = " + Math.floor(availableMargin)); 22 | 23 | double volume = availableMargin / tentativePrice; 24 | System.out.println("recommended volume = " + Math.floor(volume)); 25 | 26 | double totalBrokerage = numberTrades * brokerage; 27 | System.out.println("total brokerage = " + Math.floor(totalBrokerage)); 28 | 29 | double ledgerProfit = ( buyProfit + sellProfit) * volume; 30 | System.out.println("ledger profit = " + Math.floor(ledgerProfit)); 31 | 32 | double actualProfit = ledgerProfit - totalBrokerage; 33 | System.out.println("actual profit = " + Math.floor(actualProfit)); 34 | 35 | double profitPercentage = actualProfit * 100 / amount; 36 | System.out.println("profit percentage = " + Math.floor(profitPercentage)); 37 | 38 | System.out.println(); 39 | 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/com/dtech/ta/patterns/DoubleBottomDetectorTest.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.patterns; 2 | 3 | import com.dtech.ta.TrendLineCalculated; 4 | import org.junit.jupiter.api.Test; 5 | import org.ta4j.core.BarSeries; 6 | 7 | import java.util.List; 8 | 9 | class DoubleBottomDetectorTest { 10 | 11 | @Test 12 | void detectDoubleBottoms() { 13 | BarSeries series = PatternTestHelper.createBarSeriesFromCSV("test_data/NSE_ROSSARI_1D.csv"); 14 | 15 | DoubleBottomDetector detector = new DoubleBottomDetector(series); 16 | 17 | List patterns = detector.detectDoubleBottoms(150); 18 | patterns.forEach(System.out::println); 19 | } 20 | } -------------------------------------------------------------------------------- /src/test/java/com/dtech/ta/patterns/FlagPatternDetectorTest.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.patterns; 2 | 3 | import com.dtech.chart.FlagVisualizer; 4 | import org.jfree.chart.ui.UIUtils; 5 | import org.junit.jupiter.api.Test; 6 | import org.ta4j.core.BarSeries; 7 | 8 | import java.util.List; 9 | 10 | class FlagPatternDetectorTest { 11 | 12 | @Test 13 | void detectFlags() { 14 | BarSeries series = PatternTestHelper.createBarSeriesFromCSV("test_data/rossari_60.csv"); 15 | FlagPatternDetector detector = new FlagPatternDetector(series, new IndicatorCalculator(series, 14)); 16 | List flags = detector.detectFlags(10, 0.02); 17 | System.out.println(flags); 18 | FlagVisualizer chart = new FlagVisualizer("My Chart", series, flags); 19 | chart.pack(); 20 | UIUtils.centerFrameOnScreen(chart); 21 | chart.setVisible(true); 22 | chart.saveChartAsJPEG("MyChartFlag"); 23 | } 24 | } -------------------------------------------------------------------------------- /src/test/java/com/dtech/ta/patterns/PatternTestHelper.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.patterns; 2 | 3 | import org.ta4j.core.Bar; 4 | import org.ta4j.core.BarSeries; 5 | import org.ta4j.core.BaseBar; 6 | import org.ta4j.core.BaseBarSeries; 7 | 8 | import java.io.BufferedReader; 9 | import java.io.FileReader; 10 | import java.io.IOException; 11 | import java.math.BigDecimal; 12 | import java.time.Duration; 13 | import java.time.LocalDate; 14 | import java.time.LocalDateTime; 15 | import java.time.ZoneOffset; 16 | import java.time.format.DateTimeFormatter; 17 | 18 | public class PatternTestHelper { 19 | public static BarSeries createBarSeriesFromCSV(String csvFile) { 20 | BarSeries series = new BaseBarSeries(); 21 | DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); 22 | // DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX"); 23 | try (BufferedReader br = new BufferedReader(new FileReader(csvFile))) { 24 | String line; 25 | while ((line = br.readLine()) != null) { 26 | String[] values = line.split(","); 27 | LocalDate date = LocalDate.parse(values[0], formatter); 28 | LocalDateTime dateTime = date.atStartOfDay(); 29 | // LocalDateTime dateTime = LocalDateTime.parse(values[0], formatter); 30 | double open = Double.parseDouble(values[1]); 31 | double high = Double.parseDouble(values[2]); 32 | double low = Double.parseDouble(values[3]); 33 | double close = Double.parseDouble(values[4]); 34 | Bar bar = new BaseBar(Duration.ofDays(1), dateTime.atZone(ZoneOffset.UTC), BigDecimal.valueOf(open), 35 | BigDecimal.valueOf(high), BigDecimal.valueOf(low), BigDecimal.valueOf(close), BigDecimal.valueOf(0)); 36 | series.addBar(bar); 37 | } 38 | } catch (IOException e) { 39 | e.printStackTrace(); 40 | } 41 | return series; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/com/dtech/ta/patterns/TriangleVisualizationTest.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.patterns; 2 | 3 | import com.dtech.algo.series.ExtendedBarSeries; 4 | import com.dtech.algo.series.Interval; 5 | import com.dtech.algo.series.IntervalBarSeries; 6 | import com.dtech.algo.series.SeriesType; 7 | import com.dtech.chart.TriangleVisualizer; 8 | import com.dtech.kitecon.data.Instrument; 9 | import org.junit.jupiter.api.Test; 10 | import org.ta4j.core.*; 11 | 12 | import java.io.BufferedReader; 13 | import java.io.FileReader; 14 | import java.io.IOException; 15 | import java.math.BigDecimal; 16 | import java.time.Duration; 17 | import java.time.LocalDateTime; 18 | import java.time.ZoneOffset; 19 | import java.time.format.DateTimeFormatter; 20 | import java.util.List; 21 | 22 | public class TriangleVisualizationTest { 23 | 24 | public static IntervalBarSeries createBarSeriesFromCSV(String csvFile) { 25 | BarSeries refSeries = new BaseBarSeries(); 26 | IntervalBarSeries series = new ExtendedBarSeries(refSeries, Interval.OneHour, SeriesType.EQUITY, "rossari"); 27 | DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX"); 28 | try (BufferedReader br = new BufferedReader(new FileReader(csvFile))) { 29 | String line; 30 | while ((line = br.readLine()) != null) { 31 | String[] values = line.split(","); 32 | LocalDateTime dateTime = LocalDateTime.parse(values[0], formatter); 33 | double open = Double.parseDouble(values[1]); 34 | double high = Double.parseDouble(values[2]); 35 | double low = Double.parseDouble(values[3]); 36 | double close = Double.parseDouble(values[4]); 37 | Bar bar = new BaseBar(Duration.ofDays(1), dateTime.atZone(ZoneOffset.UTC), BigDecimal.valueOf(open), 38 | BigDecimal.valueOf(high), BigDecimal.valueOf(low), BigDecimal.valueOf(close), BigDecimal.valueOf(0)); 39 | series.addBar(bar); 40 | } 41 | } catch (IOException e) { 42 | e.printStackTrace(); 43 | } 44 | return series; 45 | } 46 | 47 | @Test 48 | public void testTriangle() { 49 | // Create a BarSeries and populate it with your data 50 | BarSeries series = createBarSeriesFromCSV("test_data/rossari_60.csv"); 51 | // Populate series with your data... 52 | 53 | // Detect triangles 54 | TriangleDetector detector = new TriangleDetector(series); 55 | List triangles = detector.detectTriangles(14); 56 | 57 | // Visualize triangles 58 | TriangleVisualizer visualizer = new TriangleVisualizer(series); 59 | visualizer.visualizeTriangles(triangles); 60 | 61 | triangles.forEach(System.out::println); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/com/dtech/ta/trendline/TrendLineDetectionTest.java: -------------------------------------------------------------------------------- 1 | package com.dtech.ta.trendline; 2 | 3 | import com.dtech.algo.series.IntervalBarSeries; 4 | import com.dtech.ta.visualize.TrendlineVisualizer; 5 | import com.dtech.ta.BarTuple; 6 | import com.dtech.ta.TrendLineCalculated; 7 | import com.dtech.ta.patterns.TriangleVisualizationTest; 8 | import org.jfree.chart.ui.UIUtils; 9 | import org.junit.jupiter.api.Test; 10 | 11 | import java.util.Collections; 12 | import java.util.List; 13 | 14 | class TrendLineDetectionTest { 15 | 16 | 17 | @Test 18 | void detectTrendlines() { 19 | // BarSeries series = PatternTestHelper.createBarSeriesFromCSV("test_data/NSE_ROSSARI_1D.csv"); 20 | IntervalBarSeries series = TriangleVisualizationTest.createBarSeriesFromCSV("test_data/rossari_60.csv"); 21 | 22 | ActiveTrendlineAnalysis detection = new ActiveTrendlineAnalysis(); 23 | // Detect trendlines for support 24 | List supportTrendlines = detection.analyze(series, false); 25 | List maxima = detection.getCombinedHighLows(series); 26 | // List minima = detection.getCombinedHighLows(series); 27 | 28 | // List resistanceTrendlines = detection.detectTrendlines(TrendlineType.RESISTANCE, false); 29 | 30 | for (TrendLineCalculated trendline : supportTrendlines) { 31 | System.out.println(trendline); 32 | } 33 | // for (TrendLineCalculated trendline : resistanceTrendlines) { 34 | // System.out.println(trendline); 35 | // } 36 | 37 | // List allTrendLines = supportTrendlines.stream().collect(Collectors.toList()); 38 | // allTrendLines.addAll(resistanceTrendlines); 39 | 40 | TrendlineVisualizer chart = new TrendlineVisualizer("TrendLines", series, supportTrendlines, maxima, Collections.emptyList()); 41 | // chart.pack(); 42 | // UIUtils.centerFrameOnScreen(chart); 43 | // chart.setVisible(true); 44 | chart.saveChartAsJPEG("Trendlines"); 45 | 46 | } 47 | } -------------------------------------------------------------------------------- /src/test/java/com/dtech/trade/ActiveOrderManagerTest.java: -------------------------------------------------------------------------------- 1 | package com.dtech.trade; 2 | 3 | import com.dtech.kitecon.KiteconApplication; 4 | import com.dtech.kitecon.config.KiteConnectConfig; 5 | import com.dtech.kitecon.market.Provider; 6 | import com.dtech.trade.model.Order; 7 | import com.dtech.trade.order.OrderManager; 8 | import com.dtech.trade.repository.OrderRepository; 9 | import com.dtech.trade.zerodha.KiteOrderManager; 10 | import com.zerodhatech.kiteconnect.KiteConnect; 11 | import org.junit.jupiter.api.Assertions; 12 | import org.junit.jupiter.api.Test; 13 | import org.junit.jupiter.api.extension.ExtendWith; 14 | import org.mockito.InjectMocks; 15 | import org.mockito.Mock; 16 | import org.mockito.Mockito; 17 | import org.mockito.Spy; 18 | import org.mockito.junit.jupiter.MockitoExtension; 19 | import org.springframework.beans.factory.annotation.Autowired; 20 | import org.springframework.boot.test.context.SpringBootTest; 21 | 22 | import java.util.Collections; 23 | import java.util.List; 24 | import java.util.Map; 25 | 26 | 27 | @ExtendWith(MockitoExtension.class) 28 | @SpringBootTest(classes = KiteconApplication.class) 29 | class ActiveOrderManagerTest { 30 | 31 | // @Mock 32 | // private KiteConnect kiteConnect; 33 | // 34 | // @Mock 35 | // private KiteConnectConfig kiteConnectConfig; 36 | // 37 | // @Mock 38 | // private OrderRepository orderRepository; 39 | // 40 | // @Spy 41 | // private List orderManagers; 42 | // 43 | // @Mock 44 | // private KiteOrderManager kiteOrderManager = new KiteOrderManager(kiteConnectConfig); 45 | // { 46 | // orderManagers = Collections.singletonList(kiteOrderManager); 47 | // } 48 | 49 | @Autowired 50 | private ActiveOrderManager activeOrderManager; 51 | 52 | @Test 53 | void initialize() { 54 | Assertions.assertTrue(activeOrderManager.orderManagers.containsKey(Provider.ZERODHA)); 55 | } 56 | 57 | @Test 58 | void placeOrder() { 59 | Order order = new Order(); 60 | // Mockito.when(kiteConnectConfig.getKiteConnect()).thenReturn(kiteConnect); 61 | // Mockito.when(kiteConnect.getKiteConnect()).thenReturn(kiteConnect); 62 | 63 | } 64 | } -------------------------------------------------------------------------------- /src/test/java/com/dtech/trade/instrument/InstrumentBarSeriesManagerTest.java: -------------------------------------------------------------------------------- 1 | package com.dtech.trade.instrument; 2 | 3 | import com.dtech.algo.series.Interval; 4 | import com.dtech.kitecon.data.Candle; 5 | import com.dtech.kitecon.data.Instrument; 6 | import com.dtech.kitecon.repository.CandleRepository; 7 | import org.junit.jupiter.api.Test; 8 | import org.junit.jupiter.api.extension.ExtendWith; 9 | import org.mockito.InjectMocks; 10 | import org.mockito.Mock; 11 | import org.mockito.Mockito; 12 | import org.mockito.junit.jupiter.MockitoExtension; 13 | 14 | import java.time.LocalDateTime; 15 | import java.util.ArrayList; 16 | import java.util.Arrays; 17 | import java.util.List; 18 | 19 | import static org.junit.jupiter.api.Assertions.*; 20 | 21 | @ExtendWith(MockitoExtension.class) 22 | class InstrumentBarSeriesManagerTest { 23 | 24 | @Mock 25 | private Instrument instrument; 26 | @Mock 27 | private CandleRepository candleRepository; 28 | 29 | @InjectMocks 30 | private InstrumentBarSeriesManager instrumentBarSeriesManager; 31 | 32 | // @Test 33 | // void initialize() { 34 | // List singletonList = Arrays.asList( 35 | // new Candle(1.0, 1.0, 1.0, 1.0, 10L, 10L, LocalDateTime.now(), instrument, Interval.OneMinute), 36 | // new Candle(1.0, 1.0, 1.0, 1.0, 10L, 10L, LocalDateTime.now(), instrument, Interval.OneMinute)); 37 | // Mockito.when(candleRepository.findAllByInstrument(instrument, Interval.OneMinute)).thenAnswer(invocation -> singletonList); 38 | // Mockito.when(candleRepository.findAllByInstrument(instrument, Interval.OneMinute)).thenReturn(new ArrayList<>()); 39 | // Mockito.when(candleRepository.getAllIntervals()).thenReturn(Arrays.asList("1minute", "15minute")); 40 | // instrumentBarSeriesManager.initialize(); 41 | // Mockito.verify(candleRepository, Mockito.times(1)).getAllIntervals(); 42 | // Mockito.verify(candleRepository, Mockito.times(1)).findAllByInstrument("1minute", instrument); 43 | // Mockito.verify(candleRepository, Mockito.times(1)).findAllByInstrument("15minute", instrument); 44 | // assertEquals(instrumentBarSeriesManager.candleMap.get("1minute").size(), 2); 45 | // assertEquals(instrumentBarSeriesManager.barSeriesMap.get("1minute").getBarCount(), 2); 46 | // } 47 | } -------------------------------------------------------------------------------- /src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.url=jdbc:h2:mem:testdb 2 | spring.datasource.driverClassName=org.h2.Driver 3 | spring.datasource.username=sa 4 | spring.datasource.password=password 5 | spring.jpa.database-platform=org.hibernate.dialect.H2Dialect 6 | 7 | 8 | kite.api.key=abc 9 | kite.api.user=def 10 | kite.api.secret=ghi -------------------------------------------------------------------------------- /src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | java -jar /code/build/libs/kitecon-0.0.1-SNAPSHOT.jar -------------------------------------------------------------------------------- /tlbo/ASHOKLEY_Day.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProScriptSlinger/Zerodha-Algo-Trading/0efe9b3ca2d524a66cf0d07ce789618933b510bf/tlbo/ASHOKLEY_Day.jpg -------------------------------------------------------------------------------- /tlbo/CUMMINSIND_Day.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProScriptSlinger/Zerodha-Algo-Trading/0efe9b3ca2d524a66cf0d07ce789618933b510bf/tlbo/CUMMINSIND_Day.jpg -------------------------------------------------------------------------------- /tlbo/DIVISLAB_Day.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProScriptSlinger/Zerodha-Algo-Trading/0efe9b3ca2d524a66cf0d07ce789618933b510bf/tlbo/DIVISLAB_Day.jpg -------------------------------------------------------------------------------- /tlbo/DMART_Day.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProScriptSlinger/Zerodha-Algo-Trading/0efe9b3ca2d524a66cf0d07ce789618933b510bf/tlbo/DMART_Day.jpg -------------------------------------------------------------------------------- /tlbo/EMAMILTD_Day.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProScriptSlinger/Zerodha-Algo-Trading/0efe9b3ca2d524a66cf0d07ce789618933b510bf/tlbo/EMAMILTD_Day.jpg -------------------------------------------------------------------------------- /tlbo/GODREJCP_Day.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProScriptSlinger/Zerodha-Algo-Trading/0efe9b3ca2d524a66cf0d07ce789618933b510bf/tlbo/GODREJCP_Day.jpg -------------------------------------------------------------------------------- /tlbo/GUJGASLTD_Day.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProScriptSlinger/Zerodha-Algo-Trading/0efe9b3ca2d524a66cf0d07ce789618933b510bf/tlbo/GUJGASLTD_Day.jpg -------------------------------------------------------------------------------- /tlbo/IEX_Day.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProScriptSlinger/Zerodha-Algo-Trading/0efe9b3ca2d524a66cf0d07ce789618933b510bf/tlbo/IEX_Day.jpg -------------------------------------------------------------------------------- /tlbo/JINDALSTEL_Day.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProScriptSlinger/Zerodha-Algo-Trading/0efe9b3ca2d524a66cf0d07ce789618933b510bf/tlbo/JINDALSTEL_Day.jpg -------------------------------------------------------------------------------- /tlbo/KAJARIACER_Day.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProScriptSlinger/Zerodha-Algo-Trading/0efe9b3ca2d524a66cf0d07ce789618933b510bf/tlbo/KAJARIACER_Day.jpg -------------------------------------------------------------------------------- /tlbo/MARICO_Day.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProScriptSlinger/Zerodha-Algo-Trading/0efe9b3ca2d524a66cf0d07ce789618933b510bf/tlbo/MARICO_Day.jpg -------------------------------------------------------------------------------- /tlbo/SUPREMEIND_Day.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProScriptSlinger/Zerodha-Algo-Trading/0efe9b3ca2d524a66cf0d07ce789618933b510bf/tlbo/SUPREMEIND_Day.jpg -------------------------------------------------------------------------------- /tlbo/VGUARD_Day.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProScriptSlinger/Zerodha-Algo-Trading/0efe9b3ca2d524a66cf0d07ce789618933b510bf/tlbo/VGUARD_Day.jpg --------------------------------------------------------------------------------