├── .gitignore ├── iextrading4j-book-api ├── src │ ├── main │ │ └── java │ │ │ └── pl │ │ │ └── zankowski │ │ │ └── iextrading4j │ │ │ └── book │ │ │ └── api │ │ │ ├── OrderBook.java │ │ │ └── PriceLevel.java │ └── test │ │ └── java │ │ └── pl │ │ └── zankowski │ │ └── iextrading4j │ │ └── book │ │ └── api │ │ ├── PriceLevelDataBuilder.java │ │ └── PriceLevelTest.java └── pom.xml ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── main.yml ├── iextrading4j-book-sample ├── src │ └── main │ │ └── java │ │ └── pl │ │ └── zankowski │ │ └── iextrading4j │ │ └── book │ │ └── sample │ │ ├── OrderBookStore.java │ │ └── OrderBookSample.java └── pom.xml ├── PULL_REQUEST_TEMPLATE.md ├── iextrading4j-book-core ├── src │ ├── main │ │ └── java │ │ │ └── pl │ │ │ └── zankowski │ │ │ └── iextrading4j │ │ │ └── book │ │ │ ├── IEXOrderBook.java │ │ │ ├── BookSide.java │ │ │ └── OrderBookPrinter.java │ └── test │ │ └── java │ │ └── pl │ │ └── zankowski │ │ └── iextrading4j │ │ └── book │ │ ├── builder │ │ └── IEXPriceLevelUpdateMessageDataBuilder.java │ │ ├── IEXOrderBookTestCase.java │ │ └── IEXOrderBookTest.java └── pom.xml ├── CONTRIBUTING.md ├── CODE_OF_CONDUCT.md ├── README.md ├── pom.xml └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | /*.iml 2 | /*.ipr 3 | /*.iws 4 | /.idea 5 | /.classpath 6 | /.project 7 | /.settings 8 | */*.iml -------------------------------------------------------------------------------- /iextrading4j-book-api/src/main/java/pl/zankowski/iextrading4j/book/api/OrderBook.java: -------------------------------------------------------------------------------- 1 | package pl.zankowski.iextrading4j.book.api; 2 | 3 | import java.util.List; 4 | 5 | public interface OrderBook { 6 | 7 | String getSymbol(); 8 | 9 | List getBidLevels(); 10 | 11 | List getAskLevels(); 12 | 13 | PriceLevel getBestAskOffer(); 14 | 15 | PriceLevel getBestBidOffer(); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Additional context** 24 | Add any other context about the problem here. 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /iextrading4j-book-sample/src/main/java/pl/zankowski/iextrading4j/book/sample/OrderBookStore.java: -------------------------------------------------------------------------------- 1 | package pl.zankowski.iextrading4j.book.sample; 2 | 3 | import pl.zankowski.iextrading4j.book.IEXOrderBook; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import java.util.Optional; 8 | 9 | public class OrderBookStore { 10 | 11 | private final Map orderBookMap = new HashMap<>(); 12 | 13 | public Optional getOrderBook(String symbol) { 14 | return Optional.ofNullable(orderBookMap.get(symbol)); 15 | } 16 | 17 | public void updateOrderBook(IEXOrderBook iexOrderBook) { 18 | orderBookMap.put(iexOrderBook.getSymbol(), iexOrderBook); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven 2 | 3 | name: Main workflow 4 | on: 5 | push: 6 | branches: [ master ] 7 | pull_request: 8 | branches: [ master ] 9 | schedule: 10 | - cron: 0 0 * * * 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up JDK 11 20 | uses: actions/setup-java@v1 21 | with: 22 | java-version: 11 23 | - name: Build with Maven 24 | run: mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package --file pom.xml 25 | - name: Analyze with SonarCloud 26 | run: mvn -B verify sonar:sonar -Dsonar.projectKey=pl.zankowski:iextrading4j-book -Dsonar.organization=wojciechzankowski-github -Dsonar.host.url=https://sonarcloud.io -Dsonar.login=$SONAR_TOKEN 27 | env: 28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 30 | - name: Code coverage 31 | run: source <(sudo curl -s https://codecov.io/bash) 32 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. 4 | 5 | Fixes # (issue) 6 | 7 | ## Type of change 8 | 9 | Please delete options that are not relevant. 10 | 11 | - [ ] Bug fix (non-breaking change which fixes an issue) 12 | - [ ] New feature (non-breaking change which adds functionality) 13 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 14 | - [ ] This change requires a documentation update 15 | 16 | # Checklist: 17 | 18 | - [ ] My code follows the style guidelines of this project 19 | - [ ] I have performed a self-review of my own code 20 | - [ ] I have commented my code, particularly in hard-to-understand areas 21 | - [ ] I have made corresponding changes to the documentation 22 | - [ ] My changes generate no new warnings 23 | - [ ] I have added tests that prove my fix is effective or that my feature works 24 | - [ ] New and existing unit tests pass locally with my changes 25 | - [ ] Any dependent changes have been merged and published in downstream modules 26 | -------------------------------------------------------------------------------- /iextrading4j-book-api/src/test/java/pl/zankowski/iextrading4j/book/api/PriceLevelDataBuilder.java: -------------------------------------------------------------------------------- 1 | package pl.zankowski.iextrading4j.book.api; 2 | 3 | import pl.zankowski.iextrading4j.hist.api.field.IEXPrice; 4 | 5 | public class PriceLevelDataBuilder { 6 | 7 | private String symbol = "IBM"; 8 | private long timestamp = 123456789L; 9 | private IEXPrice price = new IEXPrice(12345); 10 | private int size = 100; 11 | 12 | public static PriceLevel defaultPriceLevel() { 13 | return aPriceLevel().build(); 14 | } 15 | 16 | public static PriceLevelDataBuilder aPriceLevel() { 17 | return new PriceLevelDataBuilder(); 18 | } 19 | 20 | public PriceLevelDataBuilder withSymbol(String symbol) { 21 | this.symbol = symbol; 22 | return this; 23 | } 24 | 25 | public PriceLevelDataBuilder withPrice(IEXPrice iexPrice) { 26 | this.price = iexPrice; 27 | return this; 28 | } 29 | 30 | public PriceLevelDataBuilder withTimestamp(long timestamp) { 31 | this.timestamp = timestamp; 32 | return this; 33 | } 34 | 35 | public PriceLevelDataBuilder withSize(int size) { 36 | this.size = size; 37 | return this; 38 | } 39 | 40 | public PriceLevel build() { 41 | return new PriceLevel(symbol, timestamp, price, size); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /iextrading4j-book-api/src/test/java/pl/zankowski/iextrading4j/book/api/PriceLevelTest.java: -------------------------------------------------------------------------------- 1 | package pl.zankowski.iextrading4j.book.api; 2 | 3 | import org.junit.Test; 4 | import pl.zankowski.iextrading4j.hist.api.field.IEXPrice; 5 | 6 | import static org.assertj.core.api.Assertions.assertThat; 7 | import static pl.zankowski.iextrading4j.book.api.PriceLevelDataBuilder.defaultPriceLevel; 8 | 9 | public class PriceLevelTest { 10 | 11 | @Test 12 | public void shouldSuccessfullyCreatePriceLevelInstance() { 13 | final String symbol = "IBM"; 14 | final long timestamp = 123456789L; 15 | final IEXPrice price = new IEXPrice(12345); 16 | final int size = 100; 17 | 18 | PriceLevel priceLevel = new PriceLevel(symbol, timestamp, price, size); 19 | 20 | assertThat(priceLevel.getPrice()).isEqualTo(price); 21 | assertThat(priceLevel.getSize()).isEqualTo(size); 22 | assertThat(priceLevel.getSymbol()).isEqualTo(symbol); 23 | assertThat(priceLevel.getTimestamp()).isEqualTo(timestamp); 24 | } 25 | 26 | @Test 27 | public void shouldTwoInstancesWithSameValuesBeEqual() { 28 | PriceLevel priceLevel_1 = defaultPriceLevel(); 29 | PriceLevel priceLevel_2 = defaultPriceLevel(); 30 | 31 | assertThat(priceLevel_1).isEqualTo(priceLevel_2); 32 | assertThat(priceLevel_1.hashCode()).isEqualTo(priceLevel_2.hashCode()); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /iextrading4j-book-api/src/main/java/pl/zankowski/iextrading4j/book/api/PriceLevel.java: -------------------------------------------------------------------------------- 1 | package pl.zankowski.iextrading4j.book.api; 2 | 3 | import pl.zankowski.iextrading4j.hist.api.field.IEXPrice; 4 | 5 | import java.util.Objects; 6 | 7 | public class PriceLevel { 8 | 9 | private final String symbol; 10 | private final long timestamp; 11 | private final IEXPrice price; 12 | private final int size; 13 | 14 | public PriceLevel( 15 | final String symbol, 16 | final long timestamp, 17 | final IEXPrice price, 18 | final int size) { 19 | this.symbol = symbol; 20 | this.timestamp = timestamp; 21 | this.price = price; 22 | this.size = size; 23 | } 24 | 25 | public String getSymbol() { 26 | return symbol; 27 | } 28 | 29 | public long getTimestamp() { 30 | return timestamp; 31 | } 32 | 33 | public IEXPrice getPrice() { 34 | return price; 35 | } 36 | 37 | public int getSize() { 38 | return size; 39 | } 40 | 41 | @Override 42 | public boolean equals(Object o) { 43 | if (this == o) return true; 44 | if (o == null || getClass() != o.getClass()) return false; 45 | PriceLevel that = (PriceLevel) o; 46 | return timestamp == that.timestamp && 47 | size == that.size && 48 | Objects.equals(symbol, that.symbol) && 49 | Objects.equals(price, that.price); 50 | } 51 | 52 | @Override 53 | public int hashCode() { 54 | return Objects.hash(symbol, timestamp, price, size); 55 | } 56 | 57 | @Override 58 | public String toString() { 59 | return "PriceLevel{" + 60 | "symbol='" + symbol + '\'' + 61 | ", timestamp=" + timestamp + 62 | ", price=" + price + 63 | ", size=" + size + 64 | '}'; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /iextrading4j-book-core/src/main/java/pl/zankowski/iextrading4j/book/IEXOrderBook.java: -------------------------------------------------------------------------------- 1 | package pl.zankowski.iextrading4j.book; 2 | 3 | import pl.zankowski.iextrading4j.book.api.OrderBook; 4 | import pl.zankowski.iextrading4j.book.api.PriceLevel; 5 | import pl.zankowski.iextrading4j.hist.deep.trading.message.IEXPriceLevelUpdateMessage; 6 | 7 | import java.util.Comparator; 8 | import java.util.List; 9 | 10 | public class IEXOrderBook implements OrderBook { 11 | 12 | private final String symbol; 13 | 14 | private final BookSide bidSide; 15 | private final BookSide askSide; 16 | 17 | public IEXOrderBook(final String symbol) { 18 | this.symbol = symbol; 19 | this.bidSide = new BookSide(Comparator.reverseOrder()); 20 | this.askSide = new BookSide(Comparator.naturalOrder()); 21 | } 22 | 23 | public void priceLevelUpdate(final IEXPriceLevelUpdateMessage iexPriceLevelUpdateMessage) { 24 | switch (iexPriceLevelUpdateMessage.getIexMessageType()) { 25 | case PRICE_LEVEL_UPDATE_SELL: 26 | askSide.priceLevelUpdate(iexPriceLevelUpdateMessage); 27 | break; 28 | case PRICE_LEVEL_UPDATE_BUY: 29 | bidSide.priceLevelUpdate(iexPriceLevelUpdateMessage); 30 | break; 31 | default: 32 | throw new IllegalArgumentException("Unknown Price Level Update type. Cannot process."); 33 | } 34 | } 35 | 36 | @Override 37 | public String getSymbol() { 38 | return symbol; 39 | } 40 | 41 | @Override 42 | public List getBidLevels() { 43 | return bidSide.getLevels(); 44 | } 45 | 46 | @Override 47 | public List getAskLevels() { 48 | return askSide.getLevels(); 49 | } 50 | 51 | @Override 52 | public PriceLevel getBestAskOffer() { 53 | return askSide.getBestOffer(); 54 | } 55 | 56 | @Override 57 | public PriceLevel getBestBidOffer() { 58 | return bidSide.getBestOffer(); 59 | } 60 | 61 | @Override 62 | public String toString() { 63 | return OrderBookPrinter.printBook(this); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /iextrading4j-book-core/src/test/java/pl/zankowski/iextrading4j/book/builder/IEXPriceLevelUpdateMessageDataBuilder.java: -------------------------------------------------------------------------------- 1 | package pl.zankowski.iextrading4j.book.builder; 2 | 3 | import pl.zankowski.iextrading4j.hist.api.IEXMessageType; 4 | import pl.zankowski.iextrading4j.hist.api.field.IEXPrice; 5 | import pl.zankowski.iextrading4j.hist.deep.trading.field.IEXEventFlag; 6 | import pl.zankowski.iextrading4j.hist.deep.trading.message.IEXPriceLevelUpdateMessage; 7 | 8 | public class IEXPriceLevelUpdateMessageDataBuilder { 9 | 10 | private IEXMessageType iexMessageType = IEXMessageType.PRICE_LEVEL_UPDATE_BUY; 11 | private IEXEventFlag iexEventFlag = IEXEventFlag.EVENT_PROCESSING_COMPLETE; 12 | private long timestamp = 14875433212L; 13 | private String symbol = "AAPL"; 14 | private int size = 100; 15 | private IEXPrice iexPrice = new IEXPrice(17532L); 16 | 17 | public static IEXPriceLevelUpdateMessage defaultIEXPriceLevelUpdateMessage() { 18 | return anIEXPriceLevelUpdateMessage().build(); 19 | } 20 | 21 | public static IEXPriceLevelUpdateMessageDataBuilder anIEXPriceLevelUpdateMessage() { 22 | return new IEXPriceLevelUpdateMessageDataBuilder(); 23 | } 24 | 25 | public IEXPriceLevelUpdateMessageDataBuilder withIEXMessageType(final IEXMessageType iexMessageType) { 26 | this.iexMessageType = iexMessageType; 27 | return this; 28 | } 29 | 30 | public IEXPriceLevelUpdateMessageDataBuilder withIEXEventFlag(final IEXEventFlag iexEventFlag) { 31 | this.iexEventFlag = iexEventFlag; 32 | return this; 33 | } 34 | 35 | public IEXPriceLevelUpdateMessageDataBuilder withTimestamp(final long timestamp) { 36 | this.timestamp = timestamp; 37 | return this; 38 | } 39 | 40 | public IEXPriceLevelUpdateMessageDataBuilder withSize(final int size) { 41 | this.size = size; 42 | return this; 43 | } 44 | 45 | public IEXPriceLevelUpdateMessageDataBuilder withSymbol(final String symbol) { 46 | this.symbol = symbol; 47 | return this; 48 | } 49 | 50 | public IEXPriceLevelUpdateMessageDataBuilder withIEXPrice(final IEXPrice iexPrice) { 51 | this.iexPrice = iexPrice; 52 | return this; 53 | } 54 | 55 | public IEXPriceLevelUpdateMessage build() { 56 | return new IEXPriceLevelUpdateMessage(iexMessageType, iexEventFlag, timestamp, symbol, size, iexPrice); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /iextrading4j-book-sample/src/main/java/pl/zankowski/iextrading4j/book/sample/OrderBookSample.java: -------------------------------------------------------------------------------- 1 | package pl.zankowski.iextrading4j.book.sample; 2 | 3 | import org.pcap4j.core.NotOpenException; 4 | import org.pcap4j.core.PacketListener; 5 | import org.pcap4j.core.PcapHandle; 6 | import org.pcap4j.core.PcapNativeException; 7 | import org.pcap4j.core.Pcaps; 8 | import org.pcap4j.packet.Packet; 9 | import pl.zankowski.iextrading4j.book.IEXOrderBook; 10 | import pl.zankowski.iextrading4j.hist.api.message.IEXMessage; 11 | import pl.zankowski.iextrading4j.hist.api.message.IEXSegment; 12 | import pl.zankowski.iextrading4j.hist.deep.IEXDEEPMessageBlock; 13 | import pl.zankowski.iextrading4j.hist.deep.trading.message.IEXPriceLevelUpdateMessage; 14 | 15 | import java.io.IOException; 16 | 17 | public class OrderBookSample { 18 | 19 | private final OrderBookStore orderBookStore = new OrderBookStore(); 20 | 21 | public static void main(String[] args) throws IOException, PcapNativeException, InterruptedException, NotOpenException { 22 | final OrderBookSample orderBookSample = new OrderBookSample(); 23 | orderBookSample.readDEEPSample(); 24 | } 25 | 26 | private void readDEEPSample() throws PcapNativeException, NotOpenException, InterruptedException { 27 | final PcapHandle handle = Pcaps.openOffline("path_to_pcap", PcapHandle.TimestampPrecision.NANO); 28 | 29 | handle.loop(-1, new PacketListener() { 30 | @Override 31 | public void gotPacket(Packet packet) { 32 | final byte[] data = packet.getPayload().getPayload().getPayload().getRawData(); 33 | final IEXSegment block = IEXDEEPMessageBlock.createIEXSegment(data); 34 | 35 | for (final IEXMessage iexMessage : block.getMessages()) { 36 | if (iexMessage instanceof IEXPriceLevelUpdateMessage) { 37 | final IEXPriceLevelUpdateMessage iexPriceLevelUpdateMessage = (IEXPriceLevelUpdateMessage) iexMessage; 38 | final IEXOrderBook iexOrderBook = orderBookStore.getOrderBook(iexPriceLevelUpdateMessage.getSymbol()) 39 | .orElseGet(() -> new IEXOrderBook(iexPriceLevelUpdateMessage.getSymbol())); 40 | iexOrderBook.priceLevelUpdate(iexPriceLevelUpdateMessage); 41 | orderBookStore.updateOrderBook(iexOrderBook); 42 | 43 | if (iexOrderBook.getSymbol().equals("AAPL")) { 44 | System.out.println(iexOrderBook); 45 | } 46 | } 47 | } 48 | } 49 | }); 50 | 51 | 52 | handle.close(); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | I like to encourage you to contribute to the repository. 4 | This should be as easy as possible for you but there are a few things to consider when contributing. 5 | The following guidelines for contribution should be followed if you want to submit a pull request. 6 | 7 | ## How to prepare 8 | 9 | * You need a [GitHub account](https://github.com/signup/free) 10 | * Submit an [issue ticket](https://github.com/WojciechZankowski/iextrading4j/issues) for your issue if there is no one yet. 11 | * Describe the issue and include steps to reproduce if it's a bug. 12 | * Ensure to mention the earliest version that you know is affected. 13 | * If you are able and want to fix this, fork the repository on GitHub 14 | 15 | ## Make Changes 16 | 17 | * In your forked repository, create a topic branch for your upcoming patch. (e.g. `feature--autoplay` or `bugfix--ios-crash`) 18 | * Usually this is based on the master branch. 19 | * Create a branch based on master; `git branch 20 | fix/master/my_contribution master` then checkout the new branch with `git 21 | checkout fix/master/my_contribution`. Please avoid working directly on the `master` branch. 22 | * Make sure you stick to the coding style that is used already. 23 | * Make use of the `.editorconfig`-file if provided with the repository. 24 | * Make commits of logical units and describe them properly. 25 | * Check for unnecessary whitespace with `git diff --check` before committing. 26 | 27 | * If possible, submit tests to your patch / new feature so it can be tested easily. 28 | * Assure nothing is broken by running all the tests. 29 | 30 | ## Submit Changes 31 | 32 | * Push your changes to a topic branch in your fork of the repository. 33 | * Open a pull request to the original repository and choose the right original branch you want to patch. 34 | _Advanced users may install the `hub` gem and use the [`hub pull-request` command](https://hub.github.com/hub.1.html)._ 35 | * If not done in commit messages (which you really should do) please reference and update your issue with the code changes. But _please do not close the issue yourself_. 36 | _Notice: You can [turn your previously filed issues into a pull-request here](http://issue2pr.herokuapp.com/)._ 37 | * Even if you have write access to the repository, do not directly push or merge pull-requests. Let another team member review your pull request and approve. 38 | 39 | # Additional Resources 40 | 41 | * [General GitHub documentation](http://help.github.com/) 42 | * [GitHub pull request documentation](https://help.github.com/articles/about-pull-requests/) 43 | * [Read the Issue Guidelines by @necolas](https://github.com/necolas/issue-guidelines/blob/master/CONTRIBUTING.md) for more details 44 | -------------------------------------------------------------------------------- /iextrading4j-book-core/src/main/java/pl/zankowski/iextrading4j/book/BookSide.java: -------------------------------------------------------------------------------- 1 | package pl.zankowski.iextrading4j.book; 2 | 3 | import pl.zankowski.iextrading4j.book.api.PriceLevel; 4 | import pl.zankowski.iextrading4j.hist.api.field.IEXPrice; 5 | import pl.zankowski.iextrading4j.hist.deep.trading.field.IEXEventFlag; 6 | import pl.zankowski.iextrading4j.hist.deep.trading.message.IEXPriceLevelUpdateMessage; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Comparator; 10 | import java.util.LinkedList; 11 | import java.util.List; 12 | import java.util.Queue; 13 | import java.util.TreeMap; 14 | 15 | class BookSide { 16 | 17 | private final TreeMap offers; 18 | private final Queue queue = new LinkedList<>(); 19 | 20 | BookSide(final Comparator comparator) { 21 | this.offers = new TreeMap<>(comparator); 22 | } 23 | 24 | void priceLevelUpdate(final IEXPriceLevelUpdateMessage iexPriceLevelUpdateMessage) { 25 | if (iexPriceLevelUpdateMessage.getIexEventFlag() == IEXEventFlag.EVENT_PROCESSING_COMPLETE) { 26 | drainPriceLevelQueue(); 27 | processPriceLevelToOffers(toPriceLevel(iexPriceLevelUpdateMessage)); 28 | } else if (iexPriceLevelUpdateMessage.getIexEventFlag() == IEXEventFlag.ORDER_BOOK_IS_PROCESSING_EVENT) { 29 | addEventToQueue(iexPriceLevelUpdateMessage); 30 | } else { 31 | throw new IllegalArgumentException("Unknown Event Flag. Cannot process price level update"); 32 | } 33 | } 34 | 35 | List getLevels() { 36 | return new ArrayList<>(offers.values()); 37 | } 38 | 39 | PriceLevel getBestOffer() { 40 | return offers.firstEntry().getValue(); 41 | } 42 | 43 | 44 | private void drainPriceLevelQueue() { 45 | while (!queue.isEmpty()) { 46 | final PriceLevel priceLevel = queue.poll(); 47 | processPriceLevelToOffers(priceLevel); 48 | } 49 | } 50 | 51 | private void processPriceLevelToOffers(final PriceLevel priceLevel) { 52 | if (priceLevel.getSize() == 0) { 53 | offers.remove(priceLevel.getPrice()); 54 | } else { 55 | offers.put(priceLevel.getPrice(), priceLevel); 56 | } 57 | } 58 | 59 | private void addEventToQueue(final IEXPriceLevelUpdateMessage iexPriceLevelUpdateMessage) { 60 | queue.add(toPriceLevel(iexPriceLevelUpdateMessage)); 61 | } 62 | 63 | private PriceLevel toPriceLevel(final IEXPriceLevelUpdateMessage iexPriceLevelUpdateMessage) { 64 | return new PriceLevel( 65 | iexPriceLevelUpdateMessage.getSymbol(), 66 | iexPriceLevelUpdateMessage.getTimestamp(), 67 | iexPriceLevelUpdateMessage.getIexPrice(), 68 | iexPriceLevelUpdateMessage.getSize()); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at info@zankowski.pl. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /iextrading4j-book-api/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | iextrading4j-book 7 | pl.zankowski 8 | 1.1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | iextrading4j-book-api 13 | 14 | 15 | 16 | 17 | 18 | 19 | pl.zankowski 20 | iextrading4j-hist-api 21 | 22 | 23 | 24 | 25 | 26 | junit 27 | junit 28 | test 29 | 30 | 31 | 32 | org.mockito 33 | mockito-core 34 | test 35 | 36 | 37 | 38 | org.assertj 39 | assertj-core 40 | test 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | org.apache.maven.plugins 49 | maven-clean-plugin 50 | 51 | 52 | 53 | org.apache.maven.plugins 54 | maven-jar-plugin 55 | 56 | 57 | 58 | test-jar 59 | 60 | 61 | 62 | 63 | 64 | 65 | org.jacoco 66 | jacoco-maven-plugin 67 | ${jacoco.maven.plugin} 68 | 69 | 70 | 71 | 72 | 73 | 74 | release 75 | 76 | false 77 | 78 | 79 | 80 | 81 | org.apache.maven.plugins 82 | maven-source-plugin 83 | 84 | 85 | 86 | org.apache.maven.plugins 87 | maven-javadoc-plugin 88 | 89 | 90 | 91 | org.apache.maven.plugins 92 | maven-gpg-plugin 93 | 94 | 95 | 96 | org.sonatype.plugins 97 | nexus-staging-maven-plugin 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /iextrading4j-book-core/src/main/java/pl/zankowski/iextrading4j/book/OrderBookPrinter.java: -------------------------------------------------------------------------------- 1 | package pl.zankowski.iextrading4j.book; 2 | 3 | import pl.zankowski.iextrading4j.book.api.OrderBook; 4 | import pl.zankowski.iextrading4j.book.api.PriceLevel; 5 | import pl.zankowski.iextrading4j.hist.api.field.IEXPrice; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Iterator; 9 | import java.util.List; 10 | 11 | class OrderBookPrinter { 12 | 13 | private static final String BID_HEADER = "Bid"; 14 | private static final String ASK_HEADER = "Ask"; 15 | 16 | static String printBook(final OrderBook orderBook) { 17 | final Iterator bidPriceLevels = orderBook.getBidLevels().iterator(); 18 | final Iterator askPriceLevels = orderBook.getAskLevels().iterator(); 19 | final List bids = new ArrayList<>(); 20 | final List asks = new ArrayList<>(); 21 | 22 | int maxSize = 10; 23 | 24 | while (bidPriceLevels.hasNext() || askPriceLevels.hasNext()) { 25 | if (bidPriceLevels.hasNext()) { 26 | final String line = formatBidLevel(bidPriceLevels.next()); 27 | maxSize = Math.max(maxSize, line.length()); 28 | bids.add(line); 29 | } 30 | if (askPriceLevels.hasNext()) { 31 | final String line = formatAskLevel(askPriceLevels.next()); 32 | maxSize = Math.max(maxSize, line.length()); 33 | asks.add(line); 34 | } 35 | } 36 | return prepareBook(maxSize, orderBook.getSymbol(), bids.iterator(), asks.iterator()); 37 | } 38 | 39 | private static String prepareBook(final int maxSize, final String symbol, final Iterator bidStringIterator, 40 | final Iterator askStringIterator) { 41 | final StringBuilder finalBook = new StringBuilder() 42 | .append("Order Book: " + symbol + " \n") 43 | .append(BID_HEADER) 44 | .append(fillWith("", maxSize - BID_HEADER.length(), ' ')) 45 | .append("| ").append(ASK_HEADER).append("\n") 46 | .append(fillWith("", maxSize, '-')) 47 | .append("+") 48 | .append(fillWith("", maxSize, '-')) 49 | .append("\n"); 50 | 51 | while (bidStringIterator.hasNext() || askStringIterator.hasNext()) { 52 | if (bidStringIterator.hasNext()) { 53 | finalBook.append(fillWith(bidStringIterator.next(), maxSize, ' ')); 54 | } else { 55 | finalBook.append(fillWith("", maxSize, ' ')); 56 | } 57 | finalBook.append("|"); 58 | if (askStringIterator.hasNext()) { 59 | finalBook.append(" ").append(askStringIterator.next()); 60 | } 61 | finalBook.append("\n"); 62 | } 63 | return finalBook.toString(); 64 | } 65 | 66 | private static String formatBidLevel(final PriceLevel priceLevel) { 67 | return priceLevel.getSize() + " " + printPrice(priceLevel.getPrice()) + " "; 68 | } 69 | 70 | private static String formatAskLevel(final PriceLevel priceLevel) { 71 | return printPrice(priceLevel.getPrice()) + " " + priceLevel.getSize(); 72 | } 73 | 74 | private static String fillWith(final String stringToFill, final int size, final char fillChar) { 75 | final StringBuilder output = new StringBuilder(stringToFill); 76 | 77 | for (int i = output.length(); i < size; i++) { 78 | output.append(fillChar); 79 | } 80 | 81 | return output.toString(); 82 | } 83 | 84 | private static String printPrice(final IEXPrice iexPrice) { 85 | return iexPrice.toString(); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /iextrading4j-book-sample/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | iextrading4j-book 7 | pl.zankowski 8 | 1.1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | iextrading4j-book-sample 13 | 14 | 15 | 16 | pl.zankowski 17 | iextrading4j-book-core 18 | 19 | 20 | 21 | org.pcap4j 22 | pcap4j-core 23 | 1.7.1 24 | 25 | 26 | 27 | org.pcap4j 28 | pcap4j-packetfactory-static 29 | 1.7.1 30 | 31 | 32 | 33 | ch.qos.logback 34 | logback-classic 35 | 1.2.3 36 | 37 | 38 | 39 | 40 | 41 | 42 | org.apache.maven.plugins 43 | maven-clean-plugin 44 | 45 | 46 | 47 | org.apache.maven.plugins 48 | maven-jar-plugin 49 | 50 | 51 | 52 | test-jar 53 | 54 | 55 | 56 | 57 | 58 | 59 | org.jacoco 60 | jacoco-maven-plugin 61 | ${jacoco.maven.plugin} 62 | 63 | 64 | 65 | 66 | 67 | 68 | release 69 | 70 | false 71 | 72 | 73 | 74 | 75 | org.apache.maven.plugins 76 | maven-source-plugin 77 | 78 | 79 | 80 | org.apache.maven.plugins 81 | maven-javadoc-plugin 82 | 83 | 84 | 85 | org.apache.maven.plugins 86 | maven-gpg-plugin 87 | 88 | 89 | 90 | org.sonatype.plugins 91 | nexus-staging-maven-plugin 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | clojars 101 | http://clojars.org/repo/ 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /iextrading4j-book-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | iextrading4j-book 7 | pl.zankowski 8 | 1.1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | iextrading4j-book-core 13 | 14 | 15 | 16 | 17 | 18 | 19 | pl.zankowski 20 | iextrading4j-book-api 21 | 22 | 23 | 24 | pl.zankowski 25 | iextrading4j-hist-deep 26 | 27 | 28 | 29 | 30 | 31 | junit 32 | junit 33 | test 34 | 35 | 36 | 37 | org.mockito 38 | mockito-core 39 | test 40 | 41 | 42 | 43 | org.assertj 44 | assertj-core 45 | test 46 | 47 | 48 | 49 | 50 | 51 | 52 | org.apache.maven.plugins 53 | maven-clean-plugin 54 | 55 | 56 | 57 | org.apache.maven.plugins 58 | maven-jar-plugin 59 | 60 | 61 | 62 | test-jar 63 | 64 | 65 | 66 | 67 | 68 | 69 | org.jacoco 70 | jacoco-maven-plugin 71 | ${jacoco.maven.plugin} 72 | 73 | 74 | 75 | 76 | 77 | 78 | release 79 | 80 | false 81 | 82 | 83 | 84 | 85 | org.apache.maven.plugins 86 | maven-source-plugin 87 | 88 | 89 | 90 | org.apache.maven.plugins 91 | maven-javadoc-plugin 92 | 93 | 94 | 95 | org.apache.maven.plugins 96 | maven-gpg-plugin 97 | 98 | 99 | 100 | org.sonatype.plugins 101 | nexus-staging-maven-plugin 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IEXTrading4j Book 2 | 3 | [![Build Status](https://github.com/WojciechZankowski/iextrading4j-book/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/WojciechZankowski/iextrading4j-book/actions/workflows/main.yml?query=branch%3Amaster) 4 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/pl.zankowski/iextrading4j-book-core/badge.svg)](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22pl.zankowski%22%20AND%20a%3A%22iextrading4j-book-core%22) 5 | [![codecov](https://codecov.io/gh/WojciechZankowski/iextrading4j-book/branch/master/graph/badge.svg)](https://codecov.io/gh/WojciechZankowski/iextrading4j-book) 6 | [![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=pl.zankowski%3Aiextrading4j-book&metric=alert_status)](https://sonarcloud.io/dashboard/index/pl.zankowski:iextrading4j-book) 7 | 8 | IEX Trading open source incremental Order Book implementation 9 | 10 | * [Quick Start](#quick-start) 11 | * [Description](#description) 12 | * [IEX Trading](#iex-trading) 13 | * [Roadmap](#roadmap) 14 | * [License](#license) 15 | 16 | ## Quick Start 17 | 18 | Java SE 8 is required to use IEXTrading4j Book library. 19 | 20 | ``` 21 | 22 | pl.zankowski 23 | iextrading4j-book-core 24 | 1.0.0 25 | 26 | ``` 27 | 28 | Library is up to: 29 | 30 | * DEEP version 1.04 - August 1, 2017 31 | 32 | ## Description 33 | 34 | IEXTrading4j Book library allows to build incremental Order Book based on PriceLevelUpdateMessages from DEEP market data stream. OrderBook implementation follows description from specification. Description down bellow: 35 | 36 | Each symbol should be considered to have an independent Order Book. The following bullet-point statements are to be 37 | read as "within a given symbol," unless otherwise specified. Therefore, when an Order Book is said to be "transitioning" 38 | it is the Order Book of a particular symbol and not all Order Books across all symbols. 39 | * Given a singular update to an Order Book (i.e., an event) (e.g., an aggressively priced order entering the Order 40 | Book), the System may need to affect multiple price levels atomically (i.e., at once). The Order Book, along with 41 | the IEX BBO in such symbol, should be considered to have atomically transitioned from the state immediately 42 | prior to the Order Book transaction ("atomic update") to the state immediately subsequent to the transaction, 43 | but not to every state in between. 44 | * Atomic updates to a given symbol s Order Book are described in DEEP by a series of zero or more Price Level 45 | Update Messages with the Event Flags OFF (i.e., 0x0), followed by a Price Level Update Message ( PLU ) with 46 | the Event Flags ON (i.e., 0x1) (e.g., PLU 0x0, PLU 0x0, . . ., PLU 0x1). 47 | * When there are updates to multiple price levels that must occur as part of a single transaction to a given 48 | symbol's Order Book (e.g., multiple price levels' shares being exhausted via a single active order's execution), 49 | each PLU transmitted, except for the last one, will have Event Flags OFF, and Event Flags will be ON in the final 50 | PLU of the transaction. 51 | * For each update to an Order Book, there will be exactly one final PLU with the Event Flags ON, but there is no 52 | guarantee to have received preceding PLUs with an Event Flags OFF. In the case, where only a single price level 53 | is being updated atomically, there will be a single PLU transmitted with Event Flags ON, without any preceding 54 | PLUs transmitted with Event Flags OFF (i.e., PLUs with an Event Flags OFF are only transmitted when multiple 55 | price levels must be affected atomically). 56 | * A PLU with Event Flags OFF begins an atomic event (note that one or more Trade Report Message may 57 | proceed a PLU that is part of the same atomic event). A PLU with Event Flags ON ends a transaction. If no 58 | transaction was ongoing when the PLU with Event Flags ON arrived, then such PLU both begins and ends a 59 | transaction. 60 | * The Order Book should be considered to retain its BBO from prior to the transaction during the processing of 61 | the transaction. Once the transaction is complete, then the IEX BBO should be derived. This ensures that the 62 | transactional update has taken place atomically (i.e., that an in-transition IEX BBO, which never truly existed, is 63 | not seen/acted upon). 64 | Example 65 | 66 | 1. Order Book for symbol ZIEXT 67 | * Sell 100 @ 25.30 68 | * Sell 100 @ 25.20 69 | * Sell 100 @ 25.10 70 | * Buy 100 @ 25.00 71 | * Buy 100 @ 24.90 72 | 73 | IEX BBO: Buy 100 @ 25.00 x Sell 100 @ 25.10 74 | 75 | 2. Price Level Update on the Sell Side received: Event Flags: 0x0, Price: 25.10, Shares: 0 76 | * Sell 100 @ 25.30 77 | * Sell 100 @ 25.20 78 | * Buy 100 @ 25.00 79 | * Buy 100 @ 24.90 80 | 81 | IEX BBO: Buy 100 @ 25.00 x Sell 100 @ 25.10 (Reason: ZIEXT Order Book is in transition) 82 | 83 | 3. Price Level Update on the Sell Side received: Event Flags: 0x1, Price: 25.20, Shares: 0 84 | * Sell 100 @ 25.30 85 | * Buy 100 @ 25.00 86 | * Buy 100 @ 24.90 87 | 88 | IEX BBO: Buy 100 @ 25.00 x Sell 100 @ 25.30 (Reason: ZIEXT Order Book transition complete) 89 | 90 | ## IEX Trading 91 | 92 | IEX A Fair, Simple, Transparent Exchange. 93 | 94 | IEX is a stock exchange based in the United States. Started by Brad Katsuyama, it opened for trading on October 25, 2013. The company’s offices are located at 4 World Trade Center in New York City. The matching engine is located across the Hudson River in Weehawken, New Jersey, and the initial point of presence is located in a data center in Secaucus, New Jersey. IEX's main innovation is a 38-mile coil of optical fiber placed in front of its trading engine. This 350 microsecond delay adds a round-trip delay of 0.0007 seconds and is designed to negate the certain speed advantages utilized by some high-frequency traders. 95 | 96 | IEX was created in response to questionable trading practices that had become widely used across traditional public Wall Street exchanges as well as dark pools and other alternative trading systems. The IEX exchange aims to attract investors by promising to "play fair" by operating in a transparent and straightforward manner, while also helping to level the playing field for traders. Strategies to achieve those goals include: 97 | 98 | * Publishing the matching rules used in the exchanges's computerized order matching engine. 99 | * Offering a limited number of simple and familiar order types. 100 | * Charging fixed fees on most orders (or a flat percentage rate on small orders). 101 | * Ensuring market pricing data arrives at external points of presence simultaneously. 102 | * Slightly delaying market pricing data to all customers (no colocation). 103 | * Refusing to pay for order flow. 104 | 105 | Check out their beautiful site: [IEX Trading](https://iextrading.com/) 106 | 107 | ## Roadmap 108 | 109 | * Improve performance and implementation 110 | 111 | ## License 112 | 113 | Code and documentation released under the Apache License, Version 2.0 114 | 115 | Data provided for free by [IEX](https://iextrading.com/developer). 116 | 117 | IEX Trading API Exhibit A: [Exhibit A](https://iextrading.com/api-exhibit-a) 118 | 119 | ## Donations 120 | 121 | To support this repository: bc1qv6cz6t302qvamxlhyr3m7mdwrnc7anqc35wp3t 122 | -------------------------------------------------------------------------------- /iextrading4j-book-core/src/test/java/pl/zankowski/iextrading4j/book/IEXOrderBookTestCase.java: -------------------------------------------------------------------------------- 1 | package pl.zankowski.iextrading4j.book; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | import pl.zankowski.iextrading4j.book.api.PriceLevel; 6 | import pl.zankowski.iextrading4j.book.builder.IEXPriceLevelUpdateMessageDataBuilder; 7 | import pl.zankowski.iextrading4j.hist.api.IEXMessageType; 8 | import pl.zankowski.iextrading4j.hist.api.field.IEXPrice; 9 | import pl.zankowski.iextrading4j.hist.deep.trading.field.IEXEventFlag; 10 | import pl.zankowski.iextrading4j.hist.deep.trading.message.IEXPriceLevelUpdateMessage; 11 | 12 | import static org.assertj.core.api.Assertions.assertThat; 13 | 14 | /** 15 | *
 16 |  * 1. Order Book for symbol ZIEXT
 17 |  * Sell 100 @ 25.30
 18 |  * Sell 100 @ 25.20
 19 |  * Sell 100 @ 25.10
 20 |  * Buy 100 @ 25.00
 21 |  * Buy 100 @ 24.90
 22 |  * IEX BBO: Buy 100 @ 25.00 x Sell 100 @ 25.10
 23 |  *
 24 |  * 2. Price Level Update on the Sell Side received: Event Flags: 0x0, Price: 25.10, Shares: 0
 25 |  * Sell 100 @ 25.30
 26 |  * Sell 100 @ 25.20
 27 |  * Buy 100 @ 25.00
 28 |  * Buy 100 @ 24.90
 29 |  * IEX BBO: Buy 100 @ 25.00 x Sell 100 @ 25.10 (Reason: ZIEXT Order Book is in transition)
 30 |  *
 31 |  * 3. Price Level Update on the Sell Side received: Event Flags: 0x1, Price: 25.20, Shares: 0
 32 |  * Sell 100 @ 25.30
 33 |  * Buy 100 @ 25.00
 34 |  * Buy 100 @ 24.90
 35 |  * IEX BBO: Buy 100 @ 25.00 x Sell 100 @ 25.30 (Reason: ZIEXT Order Book transition complete)
 36 |  * 
37 | */ 38 | public class IEXOrderBookTestCase { 39 | 40 | private final String SYMBOL = "ZIEXT"; 41 | private IEXOrderBook orderBook; 42 | 43 | @Before 44 | public void setUp() { 45 | orderBook = new IEXOrderBook(SYMBOL); 46 | } 47 | 48 | @Test 49 | public void testCase_01() { 50 | // 1 Step 51 | System.out.println("==FIRST STEP"); 52 | prepareInitialOrderBook(); 53 | printOrderBook(); 54 | assertFirstStep(); 55 | 56 | // 2 Step 57 | System.out.println("==SECOND STEP"); 58 | updateOrderBookWithProcessingEvent(); 59 | printOrderBook(); 60 | assertSecondStep(); 61 | 62 | // 3 Step 63 | System.out.println("==THIRD STEP"); 64 | updateOrderBookWithProcessingComplete(); 65 | printOrderBook(); 66 | assertThirdStep(); 67 | } 68 | 69 | private void prepareInitialOrderBook() { 70 | final IEXPriceLevelUpdateMessage firstAsk = new IEXPriceLevelUpdateMessageDataBuilder() 71 | .withIEXMessageType(IEXMessageType.PRICE_LEVEL_UPDATE_SELL) 72 | .withIEXEventFlag(IEXEventFlag.EVENT_PROCESSING_COMPLETE) 73 | .withSymbol(SYMBOL) 74 | .withIEXPrice(new IEXPrice(253000)) 75 | .withSize(100) 76 | .build(); 77 | final IEXPriceLevelUpdateMessage secondAsk = new IEXPriceLevelUpdateMessageDataBuilder() 78 | .withIEXMessageType(IEXMessageType.PRICE_LEVEL_UPDATE_SELL) 79 | .withIEXEventFlag(IEXEventFlag.EVENT_PROCESSING_COMPLETE) 80 | .withSymbol(SYMBOL) 81 | .withIEXPrice(new IEXPrice(252000)) 82 | .withSize(100) 83 | .build(); 84 | final IEXPriceLevelUpdateMessage thirdAsk = new IEXPriceLevelUpdateMessageDataBuilder() 85 | .withIEXMessageType(IEXMessageType.PRICE_LEVEL_UPDATE_SELL) 86 | .withIEXEventFlag(IEXEventFlag.EVENT_PROCESSING_COMPLETE) 87 | .withSymbol(SYMBOL) 88 | .withIEXPrice(new IEXPrice(251000)) 89 | .withSize(100) 90 | .build(); 91 | orderBook.priceLevelUpdate(firstAsk); 92 | orderBook.priceLevelUpdate(secondAsk); 93 | orderBook.priceLevelUpdate(thirdAsk); 94 | 95 | final IEXPriceLevelUpdateMessage firstBid = new IEXPriceLevelUpdateMessageDataBuilder() 96 | .withIEXMessageType(IEXMessageType.PRICE_LEVEL_UPDATE_BUY) 97 | .withIEXEventFlag(IEXEventFlag.EVENT_PROCESSING_COMPLETE) 98 | .withSymbol(SYMBOL) 99 | .withIEXPrice(new IEXPrice(250000)) 100 | .withSize(100) 101 | .build(); 102 | final IEXPriceLevelUpdateMessage secondBid = new IEXPriceLevelUpdateMessageDataBuilder() 103 | .withIEXMessageType(IEXMessageType.PRICE_LEVEL_UPDATE_BUY) 104 | .withIEXEventFlag(IEXEventFlag.EVENT_PROCESSING_COMPLETE) 105 | .withSymbol(SYMBOL) 106 | .withIEXPrice(new IEXPrice(249000)) 107 | .withSize(100) 108 | .build(); 109 | orderBook.priceLevelUpdate(firstBid); 110 | orderBook.priceLevelUpdate(secondBid); 111 | } 112 | 113 | private void assertFirstStep() { 114 | final PriceLevel bestAskOffer = orderBook.getBestAskOffer(); 115 | assertThat(bestAskOffer.getPrice()).isEqualTo(new IEXPrice(251000)); 116 | assertThat(bestAskOffer.getSize()).isEqualTo(100); 117 | 118 | final PriceLevel bestBidOffer = orderBook.getBestBidOffer(); 119 | assertThat(bestBidOffer.getPrice()).isEqualTo(new IEXPrice(250000)); 120 | assertThat(bestBidOffer.getSize()).isEqualTo(100); 121 | } 122 | 123 | private void updateOrderBookWithProcessingEvent() { 124 | final IEXPriceLevelUpdateMessage update = new IEXPriceLevelUpdateMessageDataBuilder() 125 | .withIEXMessageType(IEXMessageType.PRICE_LEVEL_UPDATE_SELL) 126 | .withIEXEventFlag(IEXEventFlag.ORDER_BOOK_IS_PROCESSING_EVENT) 127 | .withSymbol(SYMBOL) 128 | .withIEXPrice(new IEXPrice(251000)) 129 | .withSize(0) 130 | .build(); 131 | orderBook.priceLevelUpdate(update); 132 | } 133 | 134 | private void assertSecondStep() { 135 | final PriceLevel bestAskOffer = orderBook.getBestAskOffer(); 136 | assertThat(bestAskOffer.getPrice()).isEqualTo(new IEXPrice(251000)); 137 | assertThat(bestAskOffer.getSize()).isEqualTo(100); 138 | 139 | final PriceLevel bestBidOffer = orderBook.getBestBidOffer(); 140 | assertThat(bestBidOffer.getPrice()).isEqualTo(new IEXPrice(250000)); 141 | assertThat(bestBidOffer.getSize()).isEqualTo(100); 142 | } 143 | 144 | private void updateOrderBookWithProcessingComplete() { 145 | final IEXPriceLevelUpdateMessage update = new IEXPriceLevelUpdateMessageDataBuilder() 146 | .withIEXMessageType(IEXMessageType.PRICE_LEVEL_UPDATE_SELL) 147 | .withIEXEventFlag(IEXEventFlag.EVENT_PROCESSING_COMPLETE) 148 | .withSymbol(SYMBOL) 149 | .withIEXPrice(new IEXPrice(252000)) 150 | .withSize(0) 151 | .build(); 152 | orderBook.priceLevelUpdate(update); 153 | } 154 | 155 | private void assertThirdStep() { 156 | final PriceLevel bestAskOffer = orderBook.getBestAskOffer(); 157 | assertThat(bestAskOffer.getPrice()).isEqualTo(new IEXPrice(253000)); 158 | assertThat(bestAskOffer.getSize()).isEqualTo(100); 159 | 160 | final PriceLevel bestBidOffer = orderBook.getBestBidOffer(); 161 | assertThat(bestBidOffer.getPrice()).isEqualTo(new IEXPrice(250000)); 162 | assertThat(bestBidOffer.getSize()).isEqualTo(100); 163 | } 164 | 165 | private void printOrderBook() { 166 | System.out.println(orderBook.toString()); 167 | } 168 | 169 | } 170 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | pl.zankowski 8 | iextrading4j-book 9 | pom 10 | 1.1.0-SNAPSHOT 11 | 12 | IEXTrading4j Book 13 | IEX Trading open source incremental Order Book implementation 14 | https://github.com/WojciechZankowski/iextrading4j-book 15 | 16 | 17 | 18 | The Apache Software License, Version 2.0 19 | http://www.apache.org/licenses/LICENSE-2.0.txt 20 | 21 | 22 | 23 | 24 | 25 | WojciechZankowski 26 | Wojciech Zankowski 27 | 28 | 29 | 30 | 31 | scm:git:git://github.com/WojciechZankowski/iextrading4j-book.git 32 | scm:git:git@github.com:WojciechZankowski/iextrading4j-book.git 33 | https://github.com/WojciechZankowski/iextrading4j-book 34 | master 35 | 36 | 37 | 38 | iextrading4j-book-core 39 | iextrading4j-book-sample 40 | iextrading4j-book-api 41 | 42 | 43 | 44 | 1.8 45 | 1.8 46 | 47 | 3.6.1 48 | 1.6 49 | 3.0.1 50 | 3.0.2 51 | 2.10.4 52 | 1.6.8 53 | 3.0.0 54 | 0.8.2 55 | 4.13.2 56 | 3.19.0 57 | 58 | 59 | 60 | 61 | 62 | pl.zankowski 63 | iextrading4j-hist-api 64 | 1.0.0 65 | 66 | 67 | 68 | pl.zankowski 69 | iextrading4j-hist-deep 70 | 1.0.0 71 | 72 | 73 | 74 | pl.zankowski 75 | iextrading4j-book-api 76 | 1.1.0-SNAPSHOT 77 | 78 | 79 | 80 | pl.zankowski 81 | iextrading4j-book-core 82 | 1.1.0-SNAPSHOT 83 | 84 | 85 | 86 | junit 87 | junit 88 | ${junit.version} 89 | test 90 | 91 | 92 | 93 | org.mockito 94 | mockito-core 95 | 2.7.17 96 | test 97 | 98 | 99 | 100 | org.assertj 101 | assertj-core 102 | ${assertj.version} 103 | test 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | org.apache.maven.plugins 113 | maven-compiler-plugin 114 | ${maven.compiler.plugin} 115 | 116 | ${maven.compiler.source} 117 | ${maven.compiler.target} 118 | 119 | 120 | 121 | 122 | org.apache.maven.plugins 123 | maven-jar-plugin 124 | ${maven.jar.plugin} 125 | 126 | 127 | 128 | org.apache.maven.plugins 129 | maven-clean-plugin 130 | ${maven.clean.plugin} 131 | 132 | 133 | 134 | org.apache.maven.plugins 135 | maven-source-plugin 136 | ${maven.source.plugin} 137 | 138 | 139 | attach-sources 140 | 141 | jar-no-fork 142 | 143 | 144 | 145 | 146 | 147 | 148 | org.apache.maven.plugins 149 | maven-javadoc-plugin 150 | ${maven.javadoc.plugin} 151 | 152 | 153 | attach-javadocs 154 | package 155 | 156 | jar 157 | 158 | 159 | 160 | 161 | 162 | 163 | org.apache.maven.plugins 164 | maven-gpg-plugin 165 | 166 | 167 | sign-artifacts 168 | verify 169 | 170 | sign 171 | 172 | 173 | 174 | 175 | 176 | 177 | org.sonatype.plugins 178 | nexus-staging-maven-plugin 179 | true 180 | 181 | ossrh 182 | https://oss.sonatype.org/ 183 | true 184 | 185 | 186 | 187 | 188 | org.jacoco 189 | jacoco-maven-plugin 190 | ${jacoco.maven.plugin} 191 | 192 | 193 | 194 | prepare-agent 195 | 196 | 197 | 198 | report 199 | test 200 | 201 | report 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | release 213 | 214 | false 215 | 216 | 217 | 218 | 219 | org.apache.maven.plugins 220 | maven-source-plugin 221 | 222 | 223 | 224 | org.apache.maven.plugins 225 | maven-javadoc-plugin 226 | 227 | 228 | 229 | org.apache.maven.plugins 230 | maven-gpg-plugin 231 | 232 | 233 | 234 | org.sonatype.plugins 235 | nexus-staging-maven-plugin 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | ossrh 245 | https://oss.sonatype.org/content/repositories/snapshots 246 | 247 | 248 | 249 | 250 | 251 | -------------------------------------------------------------------------------- /iextrading4j-book-core/src/test/java/pl/zankowski/iextrading4j/book/IEXOrderBookTest.java: -------------------------------------------------------------------------------- 1 | package pl.zankowski.iextrading4j.book; 2 | 3 | import org.junit.Test; 4 | import pl.zankowski.iextrading4j.book.api.PriceLevel; 5 | import pl.zankowski.iextrading4j.hist.api.IEXMessageType; 6 | import pl.zankowski.iextrading4j.hist.api.field.IEXPrice; 7 | import pl.zankowski.iextrading4j.hist.deep.trading.field.IEXEventFlag; 8 | import pl.zankowski.iextrading4j.hist.deep.trading.message.IEXPriceLevelUpdateMessage; 9 | 10 | import java.util.List; 11 | 12 | import static org.assertj.core.api.Assertions.assertThat; 13 | import static pl.zankowski.iextrading4j.book.builder.IEXPriceLevelUpdateMessageDataBuilder.anIEXPriceLevelUpdateMessage; 14 | import static pl.zankowski.iextrading4j.book.builder.IEXPriceLevelUpdateMessageDataBuilder.defaultIEXPriceLevelUpdateMessage; 15 | 16 | public class IEXOrderBookTest { 17 | 18 | private final String TEST_SYMBOL = "AAPL"; 19 | 20 | @Test 21 | public void shouldSuccessfullyAddBidQuote() { 22 | final IEXPriceLevelUpdateMessage iexPriceLevelUpdateMessage = defaultIEXPriceLevelUpdateMessage(); 23 | final IEXOrderBook orderBook = new IEXOrderBook(TEST_SYMBOL); 24 | 25 | orderBook.priceLevelUpdate(iexPriceLevelUpdateMessage); 26 | 27 | final List bidPriceLevels = orderBook.getBidLevels(); 28 | assertThat(bidPriceLevels).hasSize(1); 29 | 30 | final PriceLevel priceLevel = bidPriceLevels.get(0); 31 | assertThat(priceLevel.getTimestamp()).isEqualTo(iexPriceLevelUpdateMessage.getTimestamp()); 32 | assertThat(priceLevel.getSize()).isEqualTo(iexPriceLevelUpdateMessage.getSize()); 33 | assertThat(priceLevel.getPrice()).isEqualTo(iexPriceLevelUpdateMessage.getIexPrice()); 34 | assertThat(orderBook.getAskLevels()).isEmpty(); 35 | } 36 | 37 | @Test 38 | public void shouldSuccessfullyAddAskQuote() { 39 | final IEXPriceLevelUpdateMessage iexPriceLevelUpdateMessage = anIEXPriceLevelUpdateMessage() 40 | .withIEXMessageType(IEXMessageType.PRICE_LEVEL_UPDATE_SELL).build(); 41 | final IEXOrderBook orderBook = new IEXOrderBook(TEST_SYMBOL); 42 | 43 | orderBook.priceLevelUpdate(iexPriceLevelUpdateMessage); 44 | 45 | final List askPriceLevels = orderBook.getAskLevels(); 46 | assertThat(askPriceLevels).hasSize(1); 47 | 48 | final PriceLevel priceLevel = askPriceLevels.get(0); 49 | assertThat(priceLevel.getTimestamp()).isEqualTo(iexPriceLevelUpdateMessage.getTimestamp()); 50 | assertThat(priceLevel.getSize()).isEqualTo(iexPriceLevelUpdateMessage.getSize()); 51 | assertThat(priceLevel.getPrice()).isEqualTo(iexPriceLevelUpdateMessage.getIexPrice()); 52 | assertThat(orderBook.getBidLevels()).isEmpty(); 53 | } 54 | 55 | @Test 56 | public void shouldAddBidPriceLevelToQueueAndProcessItLater() { 57 | final IEXPriceLevelUpdateMessage iexPriceLevelUpdateMessage_1 = anIEXPriceLevelUpdateMessage() 58 | .withIEXPrice(new IEXPrice(17534L)).withIEXEventFlag(IEXEventFlag.ORDER_BOOK_IS_PROCESSING_EVENT).build(); 59 | final IEXPriceLevelUpdateMessage iexPriceLevelUpdateMessage_2 = defaultIEXPriceLevelUpdateMessage(); 60 | final IEXOrderBook orderBook = new IEXOrderBook(TEST_SYMBOL); 61 | 62 | orderBook.priceLevelUpdate(iexPriceLevelUpdateMessage_1); 63 | assertThat(orderBook.getBidLevels()).isEmpty(); 64 | 65 | orderBook.priceLevelUpdate(iexPriceLevelUpdateMessage_2); 66 | assertThat(orderBook.getBidLevels()).hasSize(2); 67 | 68 | final List bidPriceLevels = orderBook.getBidLevels(); 69 | assertThat(bidPriceLevels.get(0).getPrice()).isEqualTo(iexPriceLevelUpdateMessage_1.getIexPrice()); 70 | assertThat(bidPriceLevels.get(1).getPrice()).isEqualTo(iexPriceLevelUpdateMessage_2.getIexPrice()); 71 | } 72 | 73 | @Test 74 | public void shouldAddAskPriceLevelToQueueAndProcessItLater() { 75 | final IEXPriceLevelUpdateMessage iexPriceLevelUpdateMessage_1 = anIEXPriceLevelUpdateMessage() 76 | .withIEXMessageType(IEXMessageType.PRICE_LEVEL_UPDATE_SELL).withIEXPrice(new IEXPrice(17534L)) 77 | .withIEXEventFlag(IEXEventFlag.ORDER_BOOK_IS_PROCESSING_EVENT).build(); 78 | final IEXPriceLevelUpdateMessage iexPriceLevelUpdateMessage_2 = anIEXPriceLevelUpdateMessage() 79 | .withIEXMessageType(IEXMessageType.PRICE_LEVEL_UPDATE_SELL).build(); 80 | final IEXOrderBook orderBook = new IEXOrderBook(TEST_SYMBOL); 81 | 82 | orderBook.priceLevelUpdate(iexPriceLevelUpdateMessage_1); 83 | assertThat(orderBook.getAskLevels()).isEmpty(); 84 | 85 | orderBook.priceLevelUpdate(iexPriceLevelUpdateMessage_2); 86 | assertThat(orderBook.getAskLevels()).hasSize(2); 87 | 88 | final List askPriceLevels = orderBook.getAskLevels(); 89 | assertThat(askPriceLevels.get(0).getPrice()).isEqualTo(iexPriceLevelUpdateMessage_2.getIexPrice()); 90 | assertThat(askPriceLevels.get(1).getPrice()).isEqualTo(iexPriceLevelUpdateMessage_1.getIexPrice()); 91 | } 92 | 93 | @Test 94 | public void shouldUpdateBidPriceLevel() { 95 | final IEXPriceLevelUpdateMessage iexPriceLevelUpdateMessage_1 = anIEXPriceLevelUpdateMessage().withSize(200).build(); 96 | final IEXPriceLevelUpdateMessage iexPriceLevelUpdateMessage_2 = defaultIEXPriceLevelUpdateMessage(); 97 | final IEXOrderBook orderBook = new IEXOrderBook(TEST_SYMBOL); 98 | 99 | orderBook.priceLevelUpdate(iexPriceLevelUpdateMessage_1); 100 | assertThat(orderBook.getBidLevels().get(0).getSize()).isEqualTo(iexPriceLevelUpdateMessage_1.getSize()); 101 | 102 | orderBook.priceLevelUpdate(iexPriceLevelUpdateMessage_2); 103 | assertThat(orderBook.getBidLevels().get(0).getSize()).isEqualTo(iexPriceLevelUpdateMessage_2.getSize()); 104 | } 105 | 106 | @Test 107 | public void shouldUpdateAskPriceLevel() { 108 | final IEXPriceLevelUpdateMessage iexPriceLevelUpdateMessage_1 = anIEXPriceLevelUpdateMessage() 109 | .withIEXMessageType(IEXMessageType.PRICE_LEVEL_UPDATE_SELL).withSize(200).build(); 110 | final IEXPriceLevelUpdateMessage iexPriceLevelUpdateMessage_2 = anIEXPriceLevelUpdateMessage() 111 | .withIEXMessageType(IEXMessageType.PRICE_LEVEL_UPDATE_SELL).build(); 112 | final IEXOrderBook orderBook = new IEXOrderBook(TEST_SYMBOL); 113 | 114 | orderBook.priceLevelUpdate(iexPriceLevelUpdateMessage_1); 115 | assertThat(orderBook.getAskLevels().get(0).getSize()).isEqualTo(iexPriceLevelUpdateMessage_1.getSize()); 116 | 117 | orderBook.priceLevelUpdate(iexPriceLevelUpdateMessage_2); 118 | assertThat(orderBook.getAskLevels().get(0).getSize()).isEqualTo(iexPriceLevelUpdateMessage_2.getSize()); 119 | } 120 | 121 | @Test 122 | public void shouldRemoveBidPriceLevel() { 123 | final IEXPriceLevelUpdateMessage iexPriceLevelUpdateMessage_1 = anIEXPriceLevelUpdateMessage().withSize(200).build(); 124 | final IEXPriceLevelUpdateMessage iexPriceLevelUpdateMessage_2 = anIEXPriceLevelUpdateMessage().withSize(0).build(); 125 | final IEXOrderBook orderBook = new IEXOrderBook(TEST_SYMBOL); 126 | 127 | orderBook.priceLevelUpdate(iexPriceLevelUpdateMessage_1); 128 | assertThat(orderBook.getBidLevels().get(0).getSize()).isEqualTo(iexPriceLevelUpdateMessage_1.getSize()); 129 | 130 | orderBook.priceLevelUpdate(iexPriceLevelUpdateMessage_2); 131 | assertThat(orderBook.getBidLevels()).isEmpty(); 132 | } 133 | 134 | @Test 135 | public void shouldRemoveAskPriceLevel() { 136 | final IEXPriceLevelUpdateMessage iexPriceLevelUpdateMessage_1 = anIEXPriceLevelUpdateMessage() 137 | .withIEXMessageType(IEXMessageType.PRICE_LEVEL_UPDATE_SELL).withSize(200).build(); 138 | final IEXPriceLevelUpdateMessage iexPriceLevelUpdateMessage_2 = anIEXPriceLevelUpdateMessage() 139 | .withIEXMessageType(IEXMessageType.PRICE_LEVEL_UPDATE_SELL).withSize(0).build(); 140 | final IEXOrderBook orderBook = new IEXOrderBook(TEST_SYMBOL); 141 | 142 | orderBook.priceLevelUpdate(iexPriceLevelUpdateMessage_1); 143 | assertThat(orderBook.getAskLevels().get(0).getSize()).isEqualTo(iexPriceLevelUpdateMessage_1.getSize()); 144 | 145 | orderBook.priceLevelUpdate(iexPriceLevelUpdateMessage_2); 146 | assertThat(orderBook.getAskLevels()).isEmpty(); 147 | } 148 | 149 | @Test 150 | public void shouldSuccessfullyReturnBestAsk() { 151 | final IEXPriceLevelUpdateMessage iexPriceLevelUpdateMessage = anIEXPriceLevelUpdateMessage() 152 | .withIEXMessageType(IEXMessageType.PRICE_LEVEL_UPDATE_SELL).build(); 153 | final IEXOrderBook orderBook = new IEXOrderBook(TEST_SYMBOL); 154 | 155 | orderBook.priceLevelUpdate(iexPriceLevelUpdateMessage); 156 | 157 | final PriceLevel priceLevel = orderBook.getBestAskOffer(); 158 | assertThat(priceLevel.getTimestamp()).isEqualTo(iexPriceLevelUpdateMessage.getTimestamp()); 159 | assertThat(priceLevel.getSize()).isEqualTo(iexPriceLevelUpdateMessage.getSize()); 160 | assertThat(priceLevel.getPrice()).isEqualTo(iexPriceLevelUpdateMessage.getIexPrice()); 161 | assertThat(orderBook.getBidLevels()).isEmpty(); 162 | } 163 | 164 | @Test 165 | public void shouldSuccessfullyReturnBestBid() { 166 | final IEXPriceLevelUpdateMessage iexPriceLevelUpdateMessage = defaultIEXPriceLevelUpdateMessage(); 167 | final IEXOrderBook orderBook = new IEXOrderBook(TEST_SYMBOL); 168 | 169 | orderBook.priceLevelUpdate(iexPriceLevelUpdateMessage); 170 | 171 | final PriceLevel priceLevel = orderBook.getBestBidOffer(); 172 | assertThat(priceLevel.getTimestamp()).isEqualTo(iexPriceLevelUpdateMessage.getTimestamp()); 173 | assertThat(priceLevel.getSize()).isEqualTo(iexPriceLevelUpdateMessage.getSize()); 174 | assertThat(priceLevel.getPrice()).isEqualTo(iexPriceLevelUpdateMessage.getIexPrice()); 175 | assertThat(orderBook.getAskLevels()).isEmpty(); 176 | } 177 | 178 | @Test(expected = IllegalArgumentException.class) 179 | public void shouldThrowExceptionForUnknownMessageType() { 180 | final IEXPriceLevelUpdateMessage iexPriceLevelUpdateMessage = anIEXPriceLevelUpdateMessage() 181 | .withIEXMessageType(IEXMessageType.OPERATIONAL_HALT_STATUS) 182 | .build(); 183 | final IEXOrderBook orderBook = new IEXOrderBook(TEST_SYMBOL); 184 | 185 | orderBook.priceLevelUpdate(iexPriceLevelUpdateMessage); 186 | } 187 | 188 | @Test(expected = IllegalArgumentException.class) 189 | public void shouldThrowAnExceptionForUnknownEventFlag() { 190 | final IEXPriceLevelUpdateMessage iexPriceLevelUpdateMessage = anIEXPriceLevelUpdateMessage() 191 | .withIEXEventFlag(null) 192 | .build(); 193 | final IEXOrderBook orderBook = new IEXOrderBook(TEST_SYMBOL); 194 | 195 | orderBook.priceLevelUpdate(iexPriceLevelUpdateMessage); 196 | } 197 | 198 | } 199 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------