commit
fefe98e7cb
13 changed files with 441 additions and 0 deletions
@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
|
||||
# Changelog for exchange-simulator |
||||
|
||||
## Unreleased changes |
||||
@ -0,0 +1,30 @@
@@ -0,0 +1,30 @@
|
||||
Copyright Author name here (c) 2021 |
||||
|
||||
All rights reserved. |
||||
|
||||
Redistribution and use in source and binary forms, with or without |
||||
modification, are permitted provided that the following conditions are met: |
||||
|
||||
* Redistributions of source code must retain the above copyright |
||||
notice, this list of conditions and the following disclaimer. |
||||
|
||||
* Redistributions in binary form must reproduce the above |
||||
copyright notice, this list of conditions and the following |
||||
disclaimer in the documentation and/or other materials provided |
||||
with the distribution. |
||||
|
||||
* Neither the name of Author name here nor the names of other |
||||
contributors may be used to endorse or promote products derived |
||||
from this software without specific prior written permission. |
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
import Distribution.Simple |
||||
main = defaultMain |
||||
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
module Main where |
||||
|
||||
main :: IO () |
||||
main = error "Not implemented" |
||||
@ -0,0 +1,96 @@
@@ -0,0 +1,96 @@
|
||||
cabal-version: 1.12 |
||||
|
||||
-- This file has been generated from package.yaml by hpack version 0.34.4. |
||||
-- |
||||
-- see: https://github.com/sol/hpack |
||||
|
||||
name: exchange-simulator |
||||
version: 0.1.0.0 |
||||
description: Please see the README on GitHub at <https://github.com/githubuser/exchange-simulator#readme> |
||||
homepage: https://github.com/asakul/exchange-simulator#readme |
||||
bug-reports: https://github.com/asakul/exchange-simulator/issues |
||||
author: Denis Tereshkin |
||||
maintainer: denis@kasan.ws |
||||
copyright: 2021 Denis Tereshkin |
||||
license: BSD3 |
||||
license-file: LICENSE |
||||
build-type: Simple |
||||
extra-source-files: |
||||
README.md |
||||
ChangeLog.md |
||||
|
||||
source-repository head |
||||
type: git |
||||
location: https://github.com/asakul/exchange-simulator |
||||
|
||||
library |
||||
exposed-modules: |
||||
ATrade.ESim.Core |
||||
other-modules: |
||||
Paths_exchange_simulator |
||||
hs-source-dirs: |
||||
src |
||||
default-extensions: |
||||
OverloadedStrings |
||||
ImportQualifiedPost |
||||
FlexibleContexts |
||||
FlexibleInstances |
||||
MultiParamTypeClasses |
||||
RankNTypes |
||||
LambdaCase |
||||
build-depends: |
||||
base >=4.7 && <5 |
||||
, containers |
||||
, libatrade |
||||
default-language: Haskell2010 |
||||
|
||||
executable exchange-simulator-exe |
||||
main-is: Main.hs |
||||
other-modules: |
||||
Paths_exchange_simulator |
||||
hs-source-dirs: |
||||
app |
||||
default-extensions: |
||||
OverloadedStrings |
||||
ImportQualifiedPost |
||||
FlexibleContexts |
||||
FlexibleInstances |
||||
MultiParamTypeClasses |
||||
RankNTypes |
||||
LambdaCase |
||||
ghc-options: -threaded -rtsopts -with-rtsopts=-N |
||||
build-depends: |
||||
base >=4.7 && <5 |
||||
, containers |
||||
, exchange-simulator |
||||
, libatrade |
||||
default-language: Haskell2010 |
||||
|
||||
test-suite exchange-simulator-test |
||||
type: exitcode-stdio-1.0 |
||||
main-is: Spec.hs |
||||
other-modules: |
||||
TestLOB |
||||
Paths_exchange_simulator |
||||
hs-source-dirs: |
||||
test |
||||
default-extensions: |
||||
OverloadedStrings |
||||
ImportQualifiedPost |
||||
FlexibleContexts |
||||
FlexibleInstances |
||||
MultiParamTypeClasses |
||||
RankNTypes |
||||
LambdaCase |
||||
ghc-options: -threaded -rtsopts -with-rtsopts=-N |
||||
build-depends: |
||||
base >=4.7 && <5 |
||||
, containers |
||||
, exchange-simulator |
||||
, hedgehog |
||||
, libatrade |
||||
, tasty |
||||
, tasty-hedgehog |
||||
, tasty-hunit |
||||
, tasty-quickcheck |
||||
default-language: Haskell2010 |
||||
@ -0,0 +1,64 @@
@@ -0,0 +1,64 @@
|
||||
name: exchange-simulator |
||||
version: 0.1.0.0 |
||||
github: "asakul/exchange-simulator" |
||||
license: BSD3 |
||||
author: "Denis Tereshkin" |
||||
maintainer: "denis@kasan.ws" |
||||
copyright: "2021 Denis Tereshkin" |
||||
|
||||
extra-source-files: |
||||
- README.md |
||||
- ChangeLog.md |
||||
|
||||
# Metadata used when publishing your package |
||||
# synopsis: Short description of your package |
||||
# category: Web |
||||
|
||||
# To avoid duplicated efforts in documentation and dealing with the |
||||
# complications of embedding Haddock markup inside cabal files, it is |
||||
# common to point users to the README.md file. |
||||
description: Please see the README on GitHub at <https://github.com/githubuser/exchange-simulator#readme> |
||||
|
||||
dependencies: |
||||
- base >= 4.7 && < 5 |
||||
- libatrade |
||||
- containers |
||||
|
||||
library: |
||||
source-dirs: src |
||||
|
||||
default-extensions: |
||||
- OverloadedStrings |
||||
- ImportQualifiedPost |
||||
- FlexibleContexts |
||||
- FlexibleInstances |
||||
- MultiParamTypeClasses |
||||
- RankNTypes |
||||
- LambdaCase |
||||
|
||||
executables: |
||||
exchange-simulator-exe: |
||||
main: Main.hs |
||||
source-dirs: app |
||||
ghc-options: |
||||
- -threaded |
||||
- -rtsopts |
||||
- -with-rtsopts=-N |
||||
dependencies: |
||||
- exchange-simulator |
||||
|
||||
tests: |
||||
exchange-simulator-test: |
||||
main: Spec.hs |
||||
source-dirs: test |
||||
ghc-options: |
||||
- -threaded |
||||
- -rtsopts |
||||
- -with-rtsopts=-N |
||||
dependencies: |
||||
- exchange-simulator |
||||
- tasty |
||||
- tasty-quickcheck |
||||
- tasty-hunit |
||||
- tasty-hedgehog |
||||
- hedgehog |
||||
@ -0,0 +1,61 @@
@@ -0,0 +1,61 @@
|
||||
module ATrade.ESim.Core ( |
||||
addToLob, |
||||
LimitOrderBook, |
||||
PriceTick(..), |
||||
Volume(..), |
||||
MatchingAction(..), |
||||
emptyLob, |
||||
executeMatchingActions |
||||
) where |
||||
|
||||
import ATrade.Types |
||||
(Operation (Buy, Sell), Order (orderId, orderOperation, orderPrice, orderQuantity), OrderId, Price, TickerId) |
||||
import Data.Map.Strict qualified as M |
||||
import Data.Maybe (fromMaybe) |
||||
import Data.Sequence (Seq, empty, (|>)) |
||||
|
||||
-- | Represents price in tick size units |
||||
newtype PriceTick |
||||
= PriceTick { unPriceTick :: Int } |
||||
deriving (Eq, Ord, Show) |
||||
|
||||
newtype Volume |
||||
= Volume { unVolume :: Int } |
||||
deriving (Eq, Ord, Show) |
||||
|
||||
data LimitOrderBook |
||||
= LimitOrderBook |
||||
{ lobTickSize :: Price |
||||
, lobBids :: M.Map PriceTick (Seq (OrderId, Volume)) -- TODO I'll mix them up eventually, need some tags at type-level |
||||
, lobOffers :: M.Map PriceTick (Seq (OrderId, Volume)) |
||||
, lobTicker :: TickerId |
||||
, lobOrders :: M.Map OrderId Order |
||||
} |
||||
|
||||
data MatchingAction |
||||
= Enqueue OrderId PriceTick Volume Operation |
||||
| Match OrderId OrderId Operation PriceTick Volume |
||||
| Reject Order |
||||
deriving (Eq, Show) |
||||
|
||||
newtype Exchange |
||||
= Exchange { eLobs :: M.Map TickerId LimitOrderBook } |
||||
|
||||
addToLob :: Order -> LimitOrderBook -> [MatchingAction] |
||||
addToLob order lob = [] |
||||
|
||||
emptyLob :: TickerId -> Price -> LimitOrderBook |
||||
emptyLob tid tickSize = LimitOrderBook tickSize M.empty M.empty tid M.empty |
||||
|
||||
executeMatchingActions :: LimitOrderBook -> [MatchingAction] -> LimitOrderBook |
||||
executeMatchingActions = foldl applyAction |
||||
where |
||||
applyAction lob (Enqueue oid pricetick vol op) = |
||||
if op == Buy |
||||
then lob { lobBids = addOrder oid pricetick vol (lobBids lob) } |
||||
else lob { lobOffers = addOrder oid pricetick vol (lobOffers lob) } |
||||
|
||||
applyAction lob (Match subject object objectOperation pricetick vol) = error "Not implemented" |
||||
applyAction lob (Reject order) = error "Not implemented" |
||||
|
||||
addOrder oid pricetick vol = M.alter (\x -> Just (fromMaybe empty x |> (oid, vol)) ) pricetick |
||||
@ -0,0 +1,74 @@
@@ -0,0 +1,74 @@
|
||||
# This file was automatically generated by 'stack init' |
||||
# |
||||
# Some commonly used options have been documented as comments in this file. |
||||
# For advanced use and comprehensive documentation of the format, please see: |
||||
# https://docs.haskellstack.org/en/stable/yaml_configuration/ |
||||
|
||||
# Resolver to choose a 'specific' stackage snapshot or a compiler version. |
||||
# A snapshot resolver dictates the compiler version and the set of packages |
||||
# to be used for project dependencies. For example: |
||||
# |
||||
# resolver: lts-3.5 |
||||
# resolver: nightly-2015-09-21 |
||||
# resolver: ghc-7.10.2 |
||||
# |
||||
# The location of a snapshot can be provided as a file or url. Stack assumes |
||||
# a snapshot provided as a file might change, whereas a url resource does not. |
||||
# |
||||
# resolver: ./custom-snapshot.yaml |
||||
# resolver: https://example.com/snapshots/2018-01-01.yaml |
||||
resolver: |
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/18.yaml |
||||
|
||||
# User packages to be built. |
||||
# Various formats can be used as shown in the example below. |
||||
# |
||||
# packages: |
||||
# - some-directory |
||||
# - https://example.com/foo/bar/baz-0.0.2.tar.gz |
||||
# subdirs: |
||||
# - auto-update |
||||
# - wai |
||||
packages: |
||||
- . |
||||
- ../zeromq4-haskell-zap |
||||
- ../libatrade |
||||
|
||||
# Dependency packages to be pulled from upstream that are not in the resolver. |
||||
# These entries can reference officially published versions as well as |
||||
# forks / in-progress versions pinned to a git hash. For example: |
||||
# |
||||
# extra-deps: |
||||
# - acme-missiles-0.3 |
||||
# - git: https://github.com/commercialhaskell/stack.git |
||||
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a |
||||
# |
||||
extra-deps: |
||||
- co-log-0.4.0.1@sha256:3d4c17f37693c80d1aa2c41669bc3438fac3e89dc5f479e57d79bc3ddc4dfcc5,5087 |
||||
- datetime-0.3.1@sha256:7e275bd0ce7a2f66445bedfa0006abaf4d41af4c2204c3f8004c17eab5480e74,1534 |
||||
- ansi-terminal-0.10.3@sha256:e2fbcef5f980dc234c7ad8e2fa433b0e8109132c9e643bc40ea5608cd5697797,3226 |
||||
|
||||
|
||||
# Override default flag values for local packages and extra-deps |
||||
# flags: {} |
||||
|
||||
# Extra package databases containing global packages |
||||
# extra-package-dbs: [] |
||||
|
||||
# Control whether we use the GHC we find on the path |
||||
# system-ghc: true |
||||
# |
||||
# Require a specific version of stack, using version ranges |
||||
# require-stack-version: -any # Default |
||||
# require-stack-version: ">=2.7" |
||||
# |
||||
# Override the architecture used by stack, especially useful on Windows |
||||
# arch: i386 |
||||
# arch: x86_64 |
||||
# |
||||
# Extra directories used by stack for building |
||||
# extra-include-dirs: [/path/to/dir] |
||||
# extra-lib-dirs: [/path/to/dir] |
||||
# |
||||
# Allow a newer minor version of GHC than the snapshot specifies |
||||
# compiler-check: newer-minor |
||||
@ -0,0 +1,34 @@
@@ -0,0 +1,34 @@
|
||||
# This file was autogenerated by Stack. |
||||
# You should not edit this file by hand. |
||||
# For more information, please see the documentation at: |
||||
# https://docs.haskellstack.org/en/stable/lock_files |
||||
|
||||
packages: |
||||
- completed: |
||||
hackage: co-log-0.4.0.1@sha256:3d4c17f37693c80d1aa2c41669bc3438fac3e89dc5f479e57d79bc3ddc4dfcc5,5087 |
||||
pantry-tree: |
||||
size: 1126 |
||||
sha256: e73165ff8f744709428e2e87984c9d60ca1cec43d8455c413181c7c466e7497c |
||||
original: |
||||
hackage: co-log-0.4.0.1@sha256:3d4c17f37693c80d1aa2c41669bc3438fac3e89dc5f479e57d79bc3ddc4dfcc5,5087 |
||||
- completed: |
||||
hackage: datetime-0.3.1@sha256:7e275bd0ce7a2f66445bedfa0006abaf4d41af4c2204c3f8004c17eab5480e74,1534 |
||||
pantry-tree: |
||||
size: 334 |
||||
sha256: d41d182c143676464cb1774f0b7777e870ddeaf8b6cd5fee6ff0114997a1f504 |
||||
original: |
||||
hackage: datetime-0.3.1@sha256:7e275bd0ce7a2f66445bedfa0006abaf4d41af4c2204c3f8004c17eab5480e74,1534 |
||||
- completed: |
||||
hackage: ansi-terminal-0.10.3@sha256:e2fbcef5f980dc234c7ad8e2fa433b0e8109132c9e643bc40ea5608cd5697797,3226 |
||||
pantry-tree: |
||||
size: 1461 |
||||
sha256: 02f05d52be3ffcf36c78876629cbab80b63420672685371aea4fd10e1c4aabb6 |
||||
original: |
||||
hackage: ansi-terminal-0.10.3@sha256:e2fbcef5f980dc234c7ad8e2fa433b0e8109132c9e643bc40ea5608cd5697797,3226 |
||||
snapshots: |
||||
- completed: |
||||
size: 586296 |
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/18.yaml |
||||
sha256: 63539429076b7ebbab6daa7656cfb079393bf644971156dc349d7c0453694ac2 |
||||
original: |
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/18.yaml |
||||
@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
|
||||
|
||||
import Test.Tasty |
||||
|
||||
import TestLOB qualified |
||||
|
||||
main = defaultMain tests |
||||
|
||||
tests = testGroup "ESim tests" |
||||
[ |
||||
TestLOB.tests |
||||
] |
||||
@ -0,0 +1,59 @@
@@ -0,0 +1,59 @@
|
||||
|
||||
module TestLOB |
||||
( |
||||
tests |
||||
) where |
||||
|
||||
import ATrade.ESim.Core |
||||
(MatchingAction (..), PriceTick (PriceTick), Volume (..), addToLob, emptyLob, executeMatchingActions) |
||||
import ATrade.Types (Operation (..), Order (..), OrderPrice (..), OrderState (..), Price, SignalId (..)) |
||||
import Hedgehog |
||||
import Hedgehog.Gen qualified as Gen |
||||
import Hedgehog.Range qualified as Range |
||||
import Test.Tasty |
||||
import Test.Tasty.Hedgehog |
||||
|
||||
tests :: TestTree |
||||
tests = testGroup "LimitOrderBook" |
||||
[ |
||||
testProperty "add to empty LOB => enqueues order" prop_add_to_empty_lob, |
||||
testProperty "full match" prop_full_match |
||||
] |
||||
|
||||
prop_add_to_empty_lob :: Property |
||||
prop_add_to_empty_lob = property $ do |
||||
priceTick <- forAll $ Gen.integral (Range.linear 1 100000) |
||||
order <- forAll $ |
||||
Order <$> Gen.integral (Range.linear 1 100000) |
||||
<*> Gen.text (Range.linear 1 50) Gen.unicode |
||||
<*> Gen.text (Range.linear 1 50) Gen.unicode |
||||
<*> pure (Limit $ fromInteger priceTick * tickSize) |
||||
<*> Gen.integral (Range.linear 1 10000) |
||||
<*> pure 0 |
||||
<*> pure Buy |
||||
<*> pure Unsubmitted |
||||
<*> pure (SignalId "test" "foo" "") |
||||
addToLob order lob === [Enqueue (orderId order) (PriceTick . fromInteger $ priceTick) (Volume . fromIntegral $ orderQuantity order) (orderOperation order)] |
||||
where |
||||
lob = emptyLob "Test" tickSize |
||||
tickSize :: Price |
||||
tickSize = 0.1 |
||||
|
||||
prop_full_match :: Property |
||||
prop_full_match = property $ do |
||||
priceTick <- forAll $ Gen.integral (Range.linear 1 100000) |
||||
order <- forAll $ |
||||
Order <$> Gen.integral (Range.linear 1 100000) |
||||
<*> Gen.text (Range.linear 1 50) Gen.unicode |
||||
<*> Gen.text (Range.linear 1 50) Gen.unicode |
||||
<*> pure Market |
||||
<*> Gen.integral (Range.linear 1 10000) |
||||
<*> pure 0 |
||||
<*> pure Buy |
||||
<*> pure Unsubmitted |
||||
<*> pure (SignalId "test" "foo" "") |
||||
addToLob order (lob order priceTick) === [Match 1 (orderId order) Sell (PriceTick priceTick) (Volume . fromIntegral $ orderQuantity order)] |
||||
where |
||||
lob order priceTick = executeMatchingActions lob0 [Enqueue 1 (PriceTick priceTick) (Volume . fromIntegral $ orderQuantity order) Sell] |
||||
lob0 = emptyLob "Test" tickSize |
||||
tickSize = 0.1 |
||||
Loading…
Reference in new issue