├── .gitignore ├── README.md ├── example └── example.py ├── ordergenerator ├── __init__.py ├── agent.py ├── market.py ├── order.py ├── strategy.py └── transaction.py ├── pictures ├── depthOrderbook.png ├── example.png ├── examplePriceDevelopment.png ├── manyAgents.png ├── marketMakerWithPositionLimits.png ├── marketMakerWithPositionLimitsBuyProb65.png ├── plot.png ├── plotPositions.png ├── plotProfits.png ├── showOrderBook.png ├── simpleArbitrage.png └── summary2.png └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/python 3 | # Edit at https://www.gitignore.io/?templates=python 4 | 5 | ### Python ### 6 | # Byte-compiled / optimized / DLL files 7 | __pycache__/ 8 | *.py[cod] 9 | *$py.class 10 | 11 | # C extensions 12 | *.so 13 | 14 | # Distribution / packaging 15 | .Python 16 | build/ 17 | develop-eggs/ 18 | dist/ 19 | downloads/ 20 | eggs/ 21 | .eggs/ 22 | lib/ 23 | lib64/ 24 | parts/ 25 | sdist/ 26 | var/ 27 | wheels/ 28 | pip-wheel-metadata/ 29 | share/python-wheels/ 30 | *.egg-info/ 31 | .installed.cfg 32 | *.egg 33 | MANIFEST 34 | 35 | # PyInstaller 36 | # Usually these files are written by a python script from a template 37 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 38 | *.manifest 39 | *.spec 40 | 41 | # Installer logs 42 | pip-log.txt 43 | pip-delete-this-directory.txt 44 | 45 | # Unit test / coverage reports 46 | htmlcov/ 47 | .tox/ 48 | .nox/ 49 | .coverage 50 | .coverage.* 51 | .cache 52 | nosetests.xml 53 | coverage.xml 54 | *.cover 55 | .hypothesis/ 56 | .pytest_cache/ 57 | 58 | # Translations 59 | *.mo 60 | *.pot 61 | 62 | # Scrapy stuff: 63 | .scrapy 64 | 65 | # Sphinx documentation 66 | docs/_build/ 67 | 68 | # PyBuilder 69 | target/ 70 | 71 | # pyenv 72 | .python-version 73 | 74 | # pipenv 75 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 76 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 77 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 78 | # install all needed dependencies. 79 | #Pipfile.lock 80 | 81 | # celery beat schedule file 82 | celerybeat-schedule 83 | 84 | # SageMath parsed files 85 | *.sage.py 86 | 87 | # Spyder project settings 88 | .spyderproject 89 | .spyproject 90 | 91 | # Rope project settings 92 | .ropeproject 93 | 94 | # Mr Developer 95 | .mr.developer.cfg 96 | .project 97 | .pydevproject 98 | 99 | # mkdocs documentation 100 | /site 101 | 102 | # mypy 103 | .mypy_cache/ 104 | .dmypy.json 105 | dmypy.json 106 | 107 | # Pyre type checker 108 | .pyre/ 109 | 110 | # End of https://www.gitignore.io/api/python 111 | 112 | .idea/* 113 | venv/* 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Market simulator 2 | ## This module allows you to simulate financial markets. 3 | ## You can easily initiate order-based markets, add agents with various strategies, and evaluate the actions of agents on measures such as volatility, profits and more. 4 | 5 | ## Introduction 6 | 7 | It all starts with initiating one or more markets, and adding agents to the market(s). 8 | 9 | When the orderGenerator is started, agents start sending orders to the market (according to their strategy), which are in turn processed by a matching engine. If the order matches an order/orders of the opposite side (e.g. a bid price exceeds a sell price), a transaction will occur. Otherwise, the order will be added to the orderbook. 10 | 11 | By initiating markets with different setups (different ticksizes, for example) and different agents (many/few random agents, many/no market makers, parties arbritrating between markets, etc.) one can evaluate the impact on market quality (=price development, volatility, etc.) and agents positions and profits. 12 | 13 | One can see - for example - that adding a market maker to a market, will decrease volatility: 14 | 15 | ![examplePriceDevelopment](pictures/examplePriceDevelopment.png) 16 | 17 | Adding a market maker with a position limit (= closes position when limit is reached), can lead to sudden price spikes. 18 | 19 | Compare the price development one the green market (which includes market makers with position limits) to the one in blue (without positions limits): 20 | 21 | ![marketMakerWithPositionLimits](pictures/marketMakerWithPositionLimits.png) 22 | 23 | The difference between price developments appears to increase whenever the price is more trending. If leave this one for you to investigate ;). 24 | 25 | ## Why the project is useful 26 | This project can be useful for at least three groups of people: trading firms, regulators and trading venues. 27 | 28 | ### Trading firms 29 | For the first, it can be used to test algorithmic trading strategies. 30 | 31 | Although there is plenty of transaction data around to backtest strategies, transaction data by itself doesn't provide an accurate picture of any market. 32 | 33 | In reality, orders will have market-impact, directly matching other orders (hence changing the price) or cause other market participants to change their behaviour. 34 | 35 | In order to fully appreciate the effects of any trading strategy, one has to take into account this feedback. A dynamic order generation system is one way to do so. 36 | 37 | Furthermore, the market simulator can allow parties to test their strategies under a wide range of conditions. One can set the market (the ticksize, for example), and test the results of strategies given various sets of agents. 38 | 39 | One can also stress-test algorithms, making sure they don't have any negative side effects on the market, give any particular condition. 40 | 41 | ### Regulators 42 | Regulators can test the effects of various combinations of algoritmic strategies on the financial markets. While two trading strategies might - one used without the other - have "good" results for the markets (i.e., decrease volatitliy, increase market depth, etc.), the combination of both might have unintentional (and negative) consequences. 43 | 44 | Simulations can help regulators in testing under what condtions dangers might emerge, and how to mitigate them (for example: changing the proporitions of various trading strategies). 45 | 46 | ### Trading venues 47 | Trading venues can use simulations to test the effects of features of their market (ticksize, number of agents, for example) on variables of interest (number of transactions, for example). There might turn out to be conditions under which the market is not as stable as expected. What would - for example - happen to liquidity if we add a whole bunch of trend following algorithms? Would they trigger agents with stop losses? And if so, what would be the net effect on the market? What are the worst case scenarious you have to prepare for? 48 | 49 | Questions such as these could be investigated using this simulator. 50 | 51 | ## How users can get started with the project 52 | Using the libary is simple. 53 | 54 | First: install ordergenerator: 55 | 56 | ```shell script 57 | pip install https://github.com/bobbakov/orderGenerator/archive/master.zip 58 | ``` 59 | 60 | Secondly: Import the libary. 61 | 62 | ``` 63 | from ordergenerator import * 64 | ``` 65 | 66 | Step 3: Create a market: 67 | 68 | ### Market 69 | 70 | `a = market()` 71 | 72 | Adding no parameters initializes a market with default parameters (= minimum allowed price = 1, maximum price = 100, 73 | ticksize = 0.05, minimal quantity of any order = 1 and maximum quantity = 10). 74 | 75 | You can change any of these parameters by putting any of them between brackets: 76 | 77 | `a = market(minprice = 100, maxprice = 1000, ticksize = 1, minquantity = 10, maxquantity = 1000)` 78 | 79 | Now you can start adding your agents: 80 | 81 | For now, you can pick any of the following strategies (see Agent section for more details): 82 | - randomUniform 83 | - randomNormal 84 | - randomLogNormal 85 | - marketMaker 86 | - simpleArbitrage 87 | 88 | Each strategy has parameters you can customize. 89 | 90 | In time you will be able to make your own strategies, using simple supporting functions. You can already do so by changing the code, of course. 91 | 92 | Now: create some agents 93 | ``` 94 | agent1 = agent("randomLogNormal") # initialize agent1 with strategy "randomLogNormal" 95 | agent2 = agent("randomLogNormal", buy_probability = 0.6) # initialize agent with strategy "randomLogNormal" that buys with probability 0.6 (and sells with probability 0.4). This causes an upwards tendency in the price. 96 | agent3 = agent("bestBidOffer") # initialize agent2 with strategy "bestBidOffer" (= market maker) 97 | agent4 = agent("bestBidOffer", position_limit = 100) # a market maker with position limit = 100 98 | 99 | a.addAgents([agent1, agent2, agent3, agent4]) 100 | ``` 101 | 102 | You could add many hundreds of agents (although it might slow down the generator): 103 | 104 | ![manyAgents](pictures/manyAgents.png) 105 | 106 | Now, having a market with agents, you can start the market/have the various agents send in orders: 107 | 108 | `b.orderGenerator()` 109 | 110 | ![showOrderbook()](pictures/showOrderBook.png) 111 | 112 | This functions loops through the market, through the agents, and have each agent execute its strategy. Once finished, it will repeat. It will do so for (by default) 1000 times. You can of course adjsut this number (and other parameters): 113 | 114 | `b.orderGenerator(n = 100, clearAt = 10, printOrderbook = True, sleeptime = 5)` 115 | 116 | Once the loop is finished, we can see the results: 117 | 118 | `a.summary()` 119 | 120 | ![summary()](pictures/summary2.png) 121 | 122 | This shows four graphs: 123 | - Price over time 124 | - Volatility over time 125 | - Positions of each agent over time 126 | - Realized profit of each agent over time 127 | 128 | You can also just check the price by: 129 | 130 | `a.plotPrice()` 131 | 132 | ![plot()](pictures/plot.png) 133 | 134 | Positions over time by: 135 | 136 | `a.plotPositions()` 137 | 138 | ![plotPositions()](pictures/plotPositions.png) 139 | 140 | (Realized) profits over time: 141 | 142 | `à.plotProfits()` 143 | 144 | ![plotProfits()](pictures/plotProfits.png) 145 | 146 | You can see the orderbook by typing: 147 | 148 | `a.showOrderbook()` 149 | 150 | ![showOrderbook()](pictures/showOrderBook.png) 151 | 152 | Or (viusally) by typing: 153 | 154 | `a.showOrderbookH()` 155 | 156 | ![showOrderbookH()](pictures/depthOrderbook.png) 157 | 158 | ### Agents 159 | At this point in time, the following strategies are available: 160 | 161 | #### randomUniform: 162 | - Agent randomly chooses to send in Buy or Sell Order 163 | - Agent chooses randomly from a **uniform distribution** a price between minprice of market and maxprice market 164 | - Agent chooses randomly from a uniform distribution a quantity between minquantity of market and maxquantity market 165 | 166 | #### randomNormal: 167 | - Agent randomly chooses to send in Buy or Sell Order 168 | - Agent chooses randomly from a **normal distribution** with mean = last price and standard deviation = 0.1 169 | - Agent chooses randomly from a uniform distribution a quantity between minquantity of market and maxquantity market 170 | 171 | #### randomLogNormal 172 | - Agent randomly chooses to send in Buy or Sell Order 173 | - Agent chooses randomly from a **lognormal distribution with underlying distribution having mean = 0 and standard deviation = 0.2**. It multiplies this value with the last price traded. Value gets rounded to nearest multiple of ticksize. 174 | - Agent chooses randomly (from a uniform distribution) a quantity between minquantity of market and maxquantity market 175 | 176 | #### marketMaker 177 | The market maker always checks if he is best bid and best offer in the orderbook. If he is not, he will improve best bid by 1 tick or best offer by 1 tick (or both). 178 | 179 | He will choose randomly (from a uniform distribution) a quantity between minquantity of market and maxquantity market 180 | 181 | You can set various parameters at the marketMaker, such as position limits, meaning: if his accumulated position exceeds its limit, he will close its entire position at market. The opposite goes for a short position. 182 | 183 | #### simpleArbitrage 184 | You can have an agent arbitrating between two markets. Meaning: the agent will buy the best offer on marketA if its price is smaller than the price of best bid on marketB. He will - simultenously - sell at marketB. 185 | 186 | This way the prices on marketA and marketB will stay quite similair. 187 | 188 | In to order to use this agent, you need two markets: 189 | 190 | ``` 191 | a = market() 192 | b = market() 193 | 194 | agent1 = agent("randomLogNormal") 195 | agent2 = agent("randomLogNormal") 196 | agent3 = agent("simpleArbitrage") # agent3 arbitrates between market a and b 197 | 198 | # Add the agents 199 | a.addAgents([agent1, agent3]) 200 | b.addAgents([agent2, agent3]) 201 | 202 | # Start the markets 203 | a.orderGenerator(1000) 204 | ``` 205 | 206 | You can see that the prices follow a similar trajectory: 207 | 208 | ![simpleArbitrage()](pictures/simpleArbitrage.png) 209 | 210 | In the near future you can make your own agents by using simple sets of instrunctions. 211 | 212 | In the near future you can also change more parameters to the agents strategies. 213 | 214 | ### Orders 215 | If an agents sends in an order, the order will go through the matching engine. This follows the standard rules of a limit order book. Briefly: if the price of a bid order is higher than the price of best offer in the orderbook, there will be a transaction. If there is a remaining quantity left (= agent didn't buy all units he wanted), the engine will check for the next lowest offer. If there is, there will be a transaction. This will go on untill either the buyer bought all units he wanted, or there are no offers matching the bid price. In case of the later, the reimaing bid will be send to the orderbook. 216 | 217 | The opposite goes for offers. 218 | 219 | ### Transaction 220 | If an order matching an order of the opposite site a transaciton will occur. 221 | 222 | ## Where users can get help with your project 223 | - You can add strategies 224 | - You can write supporting functions that will allow users to create their own strategies 225 | - Add your own cool ideas :) 226 | -------------------------------------------------------------------------------- /example/example.py: -------------------------------------------------------------------------------- 1 | import ordergenerator as og 2 | 3 | # initialize a market 4 | a = og.market(minprice = 100, maxprice = 1000, ticksize = 1, minquantity = 10, maxquantity = 1000) 5 | 6 | 7 | # initialize agents 8 | agent1 = og.agent("randomLogNormal") # initialize agent1 with strategy "randomLogNormal" 9 | agent2 = og.agent("randomLogNormal", buy_probability = 0.6) # initialize agent with strategy "randomLogNormal" that buys with probability 0.6 (and sells with probability 0.4). This causes an upwards tendency in the price. 10 | agent3 = og.agent("bestBidOffer") # initialize agent2 with strategy "bestBidOffer" (= market maker) 11 | agent4 = og.agent("bestBidOffer", position_limit = 100) # a market maker with position limit = 100 12 | 13 | # add agents 14 | a.addAgents([agent1, agent2, agent3, agent4]) 15 | 16 | # run the market for 1000 ticks 17 | a.orderGenerator() 18 | 19 | # summarize 20 | a.summary() 21 | 22 | -------------------------------------------------------------------------------- /ordergenerator/__init__.py: -------------------------------------------------------------------------------- 1 | from .agent import agent 2 | from .market import market 3 | from .order import order 4 | from .strategy import strategies 5 | from .transaction import transaction -------------------------------------------------------------------------------- /ordergenerator/agent.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | 3 | from .transaction import transaction 4 | 5 | class agent(): 6 | counter = itertools.count() 7 | agents = [] 8 | 9 | # Initialize agent 10 | def __init__(self, strategy, **params): 11 | self.name = next(agent.counter) 12 | self.strategy = strategy 13 | self.params = params 14 | 15 | # STARTS WITH EMPTY VALUES 16 | self.position = {} 17 | self.runningProfit = {} 18 | 19 | self.valueBought = {} 20 | self.quantityBought = {} 21 | self.valueSold = {} 22 | self.quantitySold = {} 23 | 24 | self.stop = {} 25 | 26 | agent.agents.append(self) 27 | 28 | # If price has been printed in market --> return last price. Else --> return mid price 29 | def getLastPriceOrElse(market): 30 | if market.id in transaction.history.keys(): 31 | p = transaction.history[market.id][-1].price 32 | else: 33 | p = (market.maxprice - market.minprice) / 2 34 | return p 35 | -------------------------------------------------------------------------------- /ordergenerator/market.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | import random 3 | import numpy as np 4 | import operator 5 | from matplotlib import pyplot as plt 6 | import pandas as pd 7 | import time 8 | 9 | from .agent import agent 10 | from .order import order 11 | from .strategy import strategies 12 | from .transaction import transaction 13 | 14 | class market(): 15 | counter = itertools.count() 16 | markets = [] 17 | 18 | # Initialize market 19 | def __init__(self, minprice = 1, maxprice = 100, ticksize = 0.05, minquantity = 1, maxquantity = 10): 20 | self.id = next(market.counter) 21 | self.transaction_counter = 0 22 | self.minprice = minprice 23 | self.maxprice = maxprice 24 | self.ticksize = ticksize 25 | self.minquantity = minquantity 26 | self.maxquantity = maxquantity 27 | 28 | market.markets.append(self) 29 | 30 | # Initialize agents 31 | self.agents = [] 32 | 33 | # Make sure simulations start from same feed --> that way they can be easily compared 34 | random.seed(100) 35 | np.random.seed(100) 36 | 37 | # Start agents sending in orders to market(s) 38 | def orderGenerator(self, n = 1000, clearAt = 10000, printOrderbook = False, sleeptime = 0, all_markets = False): 39 | c = 1 40 | 41 | # For each iteration 42 | for o in range(int(n)): 43 | # For each market 44 | if all_markets: 45 | for m in market.markets: 46 | # For each agent 47 | for a in m.agents: 48 | # Take strategy agent 49 | strategyAgent = strategies.strategiesDict[a.strategy]["strategy"] 50 | 51 | # Keep track of last transaction printed 52 | if self.id in transaction.history.keys(): 53 | lastTransactionId = transaction.history[self.id][-1].id 54 | else: 55 | lastTransactionId = -1 56 | 57 | # If strategy == simpleArbitrage --> number of markets should be 2. 58 | if (a.strategy == "simpleArbitrage"): 59 | if (len(market.markets) == 2): 60 | m1, m2 = market.markets 61 | strategyAgent(a, m1, m2) 62 | else: 63 | strategyAgent(a, m) 64 | 65 | if printOrderbook: 66 | self.printOrderbook(lastTransactionId) 67 | 68 | # Slow down printing orderbook 69 | time.sleep(float(sleeptime)) 70 | 71 | # If we only generate orders for this market 72 | else: 73 | # For each agent 74 | for a in self.agents: 75 | # Take strategy agent 76 | strategyAgent = strategies.strategiesDict[a.strategy]["strategy"] 77 | 78 | # Keep track of last transaction printed 79 | if self.id in transaction.history.keys(): 80 | lastTransactionId = transaction.history[self.id][-1].id 81 | else: 82 | lastTransactionId = -1 83 | 84 | # If strategy == simpleArbitrage --> number of markets should be 2. 85 | if (a.strategy == "simpleArbitrage"): 86 | if (len(market.markets) == 2): 87 | m1, m2 = market.markets 88 | strategyAgent(a, m1, m2) 89 | else: 90 | strategyAgent(a, self) 91 | 92 | if printOrderbook: 93 | self.printOrderbook(lastTransactionId) 94 | 95 | # Slow down printing orderbook 96 | time.sleep(float(sleeptime)) 97 | 98 | c += 1 99 | 100 | # Clear orderbook at fixed intervals 101 | if (c % clearAt == 0): 102 | self.clear() 103 | 104 | def test(self, n = 1000): 105 | a1 = agent("randomLogNormal", buy_probability = 0.5) 106 | a2 = agent("bestBidOffer") 107 | # a3 = agent("randomLogNormal") 108 | # a4 = agent("bestBidOffer") 109 | # a5 = agent("bestBidOffer") 110 | # agents = [a1, a2, a3, a4, a5] 111 | agents = [a1, a2] 112 | self.addAgents(agents) 113 | self.orderGenerator(n, printOrderbook = False, sleeptime = 0) 114 | 115 | # Initiate a healthy or normal market 116 | def healthy(self): 117 | a1 = agent("randomLogNormal", buy_probability = 0.50) 118 | a2 = agent("bestBidOffer") 119 | a3 = agent("randomLogNormal") 120 | a4 = agent("bestBidOffer") 121 | a5 = agent("bestBidOffer") 122 | agents = [a1, a2, a3, a4, a5] 123 | self.addAgents(agents) 124 | 125 | # Initiate a stressed market (= high volatility) 126 | def stressed(self): 127 | a1 = agent("randomLogNormal") 128 | a2 = agent("randomLogNormal") 129 | a3 = agent("randomLogNormal") 130 | a4 = agent("randomLogNormal") 131 | a5 = agent("randomLogNormal") 132 | agents = [a1, a2, a3, a4, a5] 133 | self.addAgents(agents) 134 | 135 | # Initiate a trending (up) market 136 | def trendUp(self): 137 | a1 = agent("randomLogNormal", buy_probability = 0.55) 138 | a2 = agent("bestBidOffer") 139 | a3 = agent("randomLogNormal") 140 | a4 = agent("bestBidOffer") 141 | a5 = agent("bestBidOffer") 142 | agents = [a1, a2, a3, a4, a5] 143 | self.addAgents(agents) 144 | 145 | # Initiate a trending (down) market 146 | def trendDown(self): 147 | a1 = agent("randomLogNormal", buy_probability = 0.45) 148 | a2 = agent("bestBidOffer") 149 | a3 = agent("randomLogNormal") 150 | a4 = agent("bestBidOffer") 151 | a5 = agent("bestBidOffer") 152 | agents = [a1, a2, a3, a4, a5] 153 | self.addAgents(agents) 154 | 155 | ### METHODS 156 | def __str__(self): 157 | return "{}".format(self.id) 158 | 159 | # Add your agents to the market 160 | 161 | def addAgents(self, agents): 162 | for a in agents: 163 | self.agents.append(a) 164 | 165 | # Empty active orders 166 | 167 | def clear(self): 168 | order.activeBuyOrders[self.id] = [] 169 | order.activeSellOrders[self.id] = [] 170 | 171 | # Get last order 172 | def getLastOrder(self): 173 | # If market has been activated --> get last order 174 | if self.id in order.historyIntialOrder.keys(): 175 | lastOrderDictId = max(order.historyIntialOrder[self.id].keys()) 176 | lastOrder = order.historyIntialOrder[self.id][lastOrderDictId] 177 | # Else --> return 0 178 | else: 179 | lastOrder = 0 180 | 181 | return lastOrder 182 | 183 | # Print last transactions 184 | def printLastTransactions(self, lastTransactionId, number_transactions = 5): 185 | if self.id in transaction.history.keys(): 186 | for t in transaction.history[self.id][-number_transactions:]: 187 | if t.id > lastTransactionId: 188 | print("--> Trade at price {} (quantity = {})".format(t.price, t.quantity)) 189 | # last_transaction_id = x.id 190 | 191 | def printOrderbook(self, lastTransactionId = -1): 192 | last_order = market.getLastOrder(self) 193 | 194 | # Print last order 195 | print("Last order: \n{} with price {} (quantity = {})".format(last_order["side"], last_order["price"], 196 | last_order["quantity"])) 197 | 198 | # Print last transactions 199 | market.printLastTransactions(self, lastTransactionId) 200 | 201 | # Print orderbook 202 | self.showOrderbook() 203 | 204 | # Plot price over time 205 | 206 | def plotPrice(self, skip_transactions = 20, first = 0, last = 999999): 207 | df = pd.DataFrame(transaction.historyList[self.id], columns = ["id", "time", "price"]) 208 | df = df[["id", "price"]] 209 | df = df[(df["id"] > first) & (df["id"] < last)] 210 | df = df[df["id"] > skip_transactions] 211 | df = df.set_index("id") 212 | 213 | return df.plot() 214 | 215 | # Plot price over time on all markets in one graph 216 | 217 | def plotPriceAllMarkets(self, skip_transactions = 20, first = 0, last = 999999): 218 | for m in market.markets: 219 | df = pd.DataFrame(transaction.historyList[m.id], columns = ["id", "time", "price"]) 220 | df = df[["id", "price"]] 221 | df = df[(df["id"] > first) & (df["id"] < last)] 222 | df = df[df["id"] > skip_transactions] 223 | df = df.set_index("id") 224 | plt.plot(df, label = "{}".format(m.id)) 225 | plt.legend() 226 | 227 | return plt.show() 228 | 229 | # Plot position (+ profits) all agents 230 | def plotPositions(self, agents = []): 231 | if len(agents) == 0: 232 | agents = [a.name for a in self.agents] 233 | for a in self.agents: 234 | if a.name in agents: 235 | df = pd.DataFrame(transaction.historyMarketAgent[self.id, a.name], 236 | columns = ["id", "runningPosition", "runningProfit"]) 237 | df = df.set_index("id") 238 | rPosition = df["runningPosition"] 239 | plt.plot(rPosition, label = "{} ({})".format(str(a.name), a.strategy), 240 | linestyle = strategies.strategiesDict[a.strategy]["linestyle"]) 241 | plt.plot() 242 | plt.legend() 243 | 244 | return plt.show() 245 | 246 | # Plot profitsall agents 247 | def plotProfits(self, agents = []): 248 | if len(agents) == 0: 249 | agents = [a.name for a in self.agents] 250 | for a in self.agents: 251 | if a.name in agents: 252 | df = pd.DataFrame(transaction.historyMarketAgent[self.id, a.name], 253 | columns = ["id", "runningPosition", "runningProfit"]) 254 | df = df.set_index("id") 255 | rProfit = df["runningProfit"] 256 | plt.plot(rProfit, label = "{} ({}) runningProfit".format(str(a.name), a.strategy), 257 | linestyle = strategies.strategiesDict[a.strategy]["linestyle"]) 258 | plt.plot() 259 | plt.legend() 260 | return plt.show() 261 | 262 | def plotPricePositions(self, agents = []): 263 | if len(agents) == 0: 264 | agents = [a.name for a in self.agents] 265 | # Plot price 266 | fig, ax1 = plt.subplots() 267 | ax1.set_xlabel('transctionNumber') 268 | ax1.set_ylabel('Price') 269 | df = pd.DataFrame(transaction.historyList[self.id], columns = ["id", "time", "price"]) 270 | df[["id", "price"]] 271 | df = df.set_index("id") 272 | ax1.plot(df.index, df["price"]) 273 | ax1.tick_params(axis = 'y') 274 | 275 | ax2 = ax1.twinx() # instantiate a second axes that shares the same x-axis 276 | ax2.set_ylabel('Position') # we already handled the x-label with ax1 277 | for a in self.agents: 278 | if a.name in agents: 279 | df = pd.DataFrame(transaction.MarketAgent[self.id, a.name], 280 | columns = ["id", "runningPosition", "runningProfit"]) 281 | df = df.set_index("id") 282 | ax2.plot(df.index, df["runningPosition"], 283 | linestyle = strategies.strategiesDict[a.strategy]["linestyle"]) 284 | ax2.tick_params(axis = 'y') 285 | 286 | fig.tight_layout() # otherwise the right y-label is slightly clipped 287 | return plt.show() 288 | 289 | def plotOrdersTrades(self): 290 | # Times of order entry 291 | orderTimes = [o.datetime for o in order.history[self.id]] 292 | orderTimes = pd.DataFrame(orderTimes) 293 | 294 | # Times of transaction 295 | tradeTimes = [[t.datetime, t.price] for t in transaction.history[self.id]] 296 | tradeTimes = pd.DataFrame(tradeTimes) 297 | 298 | # combined 299 | combined = pd.merge(orderTimes, tradeTimes, on = 0, how = "left") 300 | combined = combined.set_index(0) 301 | 302 | return combined.plot() 303 | 304 | # Plot price, volatility, positions and profits 305 | 306 | def summary(self): 307 | df = pd.DataFrame(transaction.historyList[self.id], columns = ["id", "time", "price"]) 308 | df["volatility"] = df["price"].rolling(7).std() 309 | df["volatilityTrend"] = df["volatility"].rolling(100).mean() 310 | df = df[["id", "price", "volatility", "volatilityTrend"]] 311 | df = df.set_index("id") 312 | 313 | dfPrice = df["price"] 314 | dfVolatility = df[["volatility", "volatilityTrend"]] 315 | 316 | # PLOT PRICE 317 | fig, axs = plt.subplots(2, 2) 318 | axs[0, 0].plot(dfPrice, label = "price") 319 | axs[0, 0].set_title("Price") 320 | axs[0, 0].legend() 321 | 322 | # PLOT VOLATILITY 323 | axs[0, 1].plot(dfVolatility) 324 | axs[0, 1].set_title("Volatility") 325 | axs[0, 1].legend() 326 | 327 | # PLOT RUNNING POSITIONS + RUNNING PROFITS AGENTS 328 | for a in self.agents: 329 | right = pd.DataFrame(transaction.historyMarketAgent[self.id, a.name], 330 | columns = ["id", a.name, str(a.name) + "RunningProfit"]) 331 | right = right.set_index("id") 332 | right = right[a.name] 333 | axs[1, 0].plot(right, label = str(a.name)) 334 | axs[1, 0].set_title("Positions") 335 | 336 | if len(self.agents) < 20: 337 | axs[1, 0].legend() 338 | 339 | for a in self.agents: 340 | right = pd.DataFrame(transaction.historyMarketAgent[self.id, a.name], 341 | columns = ["id", a.name, str(a.name) + "RunningProfit"]) 342 | right = right.set_index("id") 343 | right = right[str(a.name) + "RunningProfit"] 344 | axs[1, 1].plot(right, label = str(a.name) + "RunningProfit") 345 | 346 | axs[1, 1].set_title("Running profit") 347 | 348 | return plt.show() 349 | 350 | # SHOW ORDERBOOK RAW VERSION 351 | 352 | def showOrderbook(self, show_depth = 10): 353 | widthOrderbook = len("0 Bert Buy 33 5") 354 | print(widthOrderbook * 2 * "*") 355 | 356 | if self.id in order.activeSellOrders.keys(): 357 | for sellOrder in sorted(order.activeSellOrders[self.id], key = operator.attrgetter("price"), 358 | reverse = True)[:show_depth]: 359 | print(widthOrderbook * "." + " " + str(sellOrder)) 360 | if self.id in order.activeBuyOrders.keys(): 361 | for buyOrder in sorted(order.activeBuyOrders[self.id], key = operator.attrgetter("price"), reverse = True)[ 362 | :show_depth]: 363 | print(str(buyOrder) + " " + widthOrderbook * ".") 364 | 365 | print(widthOrderbook * 2 * "*") 366 | print(" ") 367 | 368 | # SHOW ORDERBOOK GRAPHICALLY 369 | def showOrderbookH(self): 370 | buyOrders = [] 371 | for b in order.activeBuyOrders[self.id]: 372 | for o in range(b.quantity): 373 | buyOrders.append(b.price) 374 | 375 | sellOrders = [] 376 | for of in order.activeSellOrders[self.id]: 377 | for o in range(of.quantity): 378 | sellOrders.append(of.price) 379 | 380 | plt.hist(buyOrders, bins = 100, color = "green") 381 | plt.hist(sellOrders, bins = 100, color = "red") 382 | 383 | return plt.show() -------------------------------------------------------------------------------- /ordergenerator/order.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | from datetime import datetime, timedelta 3 | import operator 4 | 5 | from .transaction import transaction 6 | 7 | class order(): 8 | counter = itertools.count() 9 | datetimeCounter = datetime(2019, 1, 1, 9, 0, 0) 10 | history = {} 11 | activeOrders = {} 12 | activeBuyOrders = {} 13 | activeSellOrders = {} 14 | historyIntialOrder = {} 15 | 16 | # Initialize order 17 | def __init__(self, market, agent, side, price, quantity): 18 | self.id = next(order.counter) 19 | order.datetimeCounter += timedelta(seconds = 1) 20 | self.datetime = order.datetimeCounter 21 | self.market = market 22 | self.agent = agent 23 | self.side = side 24 | price = round(price / market.ticksize) * market.ticksize 25 | price = float("%.2f" % (price)) 26 | self.price = price 27 | self.quantity = quantity 28 | 29 | # If order is first order in a market --> create orderbook 30 | if not market.id in order.history.keys(): 31 | order.history[market.id] = [] 32 | order.activeOrders[market.id] = [] 33 | order.activeBuyOrders[market.id] = [] 34 | order.activeSellOrders[market.id] = [] 35 | order.historyIntialOrder[market.id] = {} 36 | 37 | # Add order to order history 38 | order.history[market.id].append(self) 39 | 40 | # Add hardcoded values to dictionaries (If you add self --> values get overwritten) 41 | order.historyIntialOrder[market.id][self.id] = {"id" : self.id, "market": market, "side": side, 42 | "price": price, "quantity": quantity} 43 | 44 | # Check if order results in transaction 45 | # If order is buy order 46 | if self.side == "Buy": 47 | # start loop 48 | remainingQuantity = self.quantity 49 | while True: 50 | # If there are active sell orders --> continue 51 | if not not order.activeSellOrders[market.id]: 52 | sellOrders = sorted(order.activeSellOrders[market.id], key = operator.attrgetter("price")) 53 | bestOffer = sellOrders[0] 54 | 55 | # If limit price buy order >= price best offer --> transaction 56 | if self.price >= bestOffer.price: 57 | transactionPrice = bestOffer.price 58 | 59 | # If quantity buy order larger quantity than best offer --> remove best offer from active orders 60 | if remainingQuantity > bestOffer.quantity: 61 | transactionQuantity = bestOffer.quantity 62 | 63 | # Register transaction 64 | transaction(self, bestOffer, market, transactionPrice, transactionQuantity) 65 | 66 | # Remove offer from orderbook 67 | order.removeOffer(bestOffer, market) 68 | 69 | # Reduce remaining quantity order 70 | remainingQuantity -= transactionQuantity 71 | 72 | # If quantity buy order equals quantity best offer 73 | elif remainingQuantity == bestOffer.quantity: 74 | transactionQuantity = bestOffer.quantity 75 | 76 | # Register transaction 77 | transaction(self, bestOffer, market, transactionPrice, transactionQuantity) 78 | 79 | # Remove offer from orderbook 80 | order.removeOffer(bestOffer, market) 81 | 82 | # Buy order is executed --> break loop 83 | break 84 | 85 | # if quantity buy order is small than quantity best offer --> reduce quantity best offer 86 | else: 87 | transactionQuantity = remainingQuantity 88 | 89 | # Register transaction 90 | transaction(self, bestOffer, market, transactionPrice, transactionQuantity) 91 | 92 | # Reduce quantity offer 93 | order.reduceOffer(bestOffer, transactionQuantity, market) 94 | 95 | # Buy order is executed --> break loop 96 | break 97 | 98 | # If bid price < best offer --> no transaction 99 | else: 100 | self.quantity = remainingQuantity 101 | 102 | # Send order to orderbook 103 | order.activeOrders[market.id].append(self) 104 | order.activeBuyOrders[market.id].append(self) 105 | break 106 | 107 | # If there are NO active sell orders --> add order to active orders 108 | else: 109 | self.quantity = remainingQuantity 110 | order.activeOrders[market.id].append(self) 111 | order.activeBuyOrders[market.id].append(self) 112 | break 113 | 114 | # If order is sell order 115 | else: 116 | # start loop 117 | remainingQuantity = self.quantity 118 | while True: 119 | # if there are active buy orders --> continue 120 | if not not order.activeBuyOrders[market.id]: 121 | buyOrders = sorted(order.activeBuyOrders[market.id], key = operator.attrgetter("price"), 122 | reverse = True) 123 | bestBid = buyOrders[0] 124 | 125 | # If price sell order <= price best bid --> transaction 126 | if bestBid.price >= self.price: 127 | transactionPrice = bestBid.price 128 | 129 | # If quantity offer larger than quantity best bid 130 | if remainingQuantity > bestBid.quantity: 131 | transactionQuantity = bestBid.quantity 132 | 133 | # Register transaction 134 | transaction(bestBid, self, market, transactionPrice, transactionQuantity) 135 | 136 | # Remove bid from orderbook 137 | order.removeBid(bestBid, market) 138 | 139 | remainingQuantity -= transactionQuantity 140 | # If quantity order equals quantity best offer 141 | elif remainingQuantity == bestBid.quantity: 142 | transactionQuantity = bestBid.quantity 143 | 144 | # Register transaction 145 | transaction(bestBid, self, market, transactionPrice, transactionQuantity) 146 | 147 | # Remove best bid from orderbook 148 | order.removeBid(bestBid, market) 149 | 150 | # Order is executed --> break loop 151 | break 152 | 153 | # If quantity sell order is smaller than quantity best bid 154 | else: 155 | transactionQuantity = remainingQuantity 156 | 157 | # Register transaction 158 | transaction(bestBid, self, market, transactionPrice, transactionQuantity) 159 | 160 | # Reduce quantity best bid 161 | order.reduceBid(bestBid, transactionQuantity, market) 162 | break 163 | 164 | # If best offer price > best bid price --> no transaction 165 | else: 166 | self.quantity = remainingQuantity 167 | order.activeOrders[market.id].append(self) 168 | order.activeSellOrders[market.id].append(self) 169 | break 170 | 171 | # If there are no active buy orders --> add order to active orders 172 | else: 173 | self.quantity = remainingQuantity 174 | order.activeOrders[market.id].append(self) 175 | order.activeSellOrders[market.id].append(self) 176 | break 177 | 178 | ### METHODS 179 | def __str__(self): 180 | return "{} \t {} \t {} \t {} \t {}".format(self.market, self.agent.name, self.side, self.price, self.quantity) 181 | 182 | # Remove offer from orderbook 183 | def removeOffer(offer, market): 184 | a = order.activeSellOrders[market.id] 185 | for c, o in enumerate(a): 186 | if o.id == offer.id: 187 | del a[c] 188 | break 189 | 190 | # Reduce quantity offer in orderbook 191 | def reduceOffer(offer, transactionQuantity, market): 192 | a = order.activeSellOrders[market.id] 193 | for c, o in enumerate(a): 194 | if o.id == offer.id: 195 | if a[c].quantity == transactionQuantity: 196 | order.removeOffer(offer, market) 197 | else: 198 | a[c].quantity -= transactionQuantity 199 | break 200 | 201 | # Remove bid from orderbook 202 | def removeBid(bid, market): 203 | a = order.activeBuyOrders[market.id] 204 | for c, o in enumerate(a): 205 | if o.id == bid.id: 206 | del a[c] 207 | break 208 | 209 | # Reduce quantity bid in orderbook 210 | 211 | def reduceBid(bid, transactionQuantity, market): 212 | a = order.activeBuyOrders[market.id] 213 | for c, o in enumerate(a): 214 | if o.id == bid.id: 215 | if a[c].quantity == transactionQuantity: 216 | order.removeBid(bid, market) 217 | else: 218 | a[c].quantity -= transactionQuantity 219 | break 220 | -------------------------------------------------------------------------------- /ordergenerator/strategy.py: -------------------------------------------------------------------------------- 1 | import random 2 | import numpy as np 3 | import operator 4 | 5 | from .agent import agent 6 | from .order import order 7 | 8 | class strategies(): 9 | def randomUniform(agentId, market): 10 | side = random.choice(["Buy", "Sell"]) 11 | price = np.arange(market.minprice, market.maxprice, market.ticksize) 12 | price = np.random.choice(price) 13 | quantity = random.randint(market.minquantity, market.maxquantity) 14 | 15 | # Send order to market 16 | order(market, agentId, side, price, quantity) 17 | 18 | # Randomly choose Buy/Sell order with price from normal distribution 19 | def randomNormal(agentId, market): 20 | side = random.choice(["Buy", "Sell"]) 21 | lastPrice = agent.getLastPriceOrElse(market) 22 | std = 0.1 * lastPrice 23 | price = np.random.normal(lastPrice, std) 24 | quantity = random.randint(market.minquantity, market.maxquantity) 25 | 26 | # Send order to market 27 | order(market, agentId, side, price, quantity) 28 | 29 | # Randomly choose Buy/Sell order with price from lognormal distribution 30 | 31 | def randomLogNormal(agentId, market, buy_probability = 0.5): 32 | 33 | if "buy_probability" in agentId.params.keys(): 34 | buy_probability = agentId.params["buy_probability"] 35 | 36 | sell_probability = 1 - buy_probability 37 | side = np.random.choice(["Buy", "Sell"], p = [buy_probability, sell_probability]) 38 | lastPrice = agent.getLastPriceOrElse(market) 39 | price = lastPrice * np.random.lognormal(0, 0.2) 40 | quantity = random.randint(market.minquantity, market.maxquantity) 41 | 42 | # Send order to market 43 | order(market, agentId, side, price, quantity) 44 | 45 | ############################################################################### 46 | # AGENT: MARKET MAKER 47 | ############################################################################### 48 | # Agents always tries to be best bid and best offer at market. If he is not --> he improves price by 1 tick. 49 | # If position limit is exceeded --> agent closes position at market (in case position > 0) or buys back all shorts (in case position < 0) 50 | def bestBidOffer(agentId, market): 51 | quantity = random.randint(market.minquantity, market.maxquantity) 52 | 53 | # If agent has position_limit --> check if limit is exceeded 54 | if "position_limit" in agentId.params.keys(): 55 | position_limit = agentId.params["position_limit"] 56 | if market.id in agentId.position.keys(): 57 | if agentId.position[market.id] > position_limit: 58 | order(market, agentId, "Sell", market.minprice, abs(agentId.position[market.id])) 59 | elif agentId.position[market.id] < -1 * position_limit: 60 | order(market, agentId, "Buy", agent.getLastPriceOrElse(market) * 2, 61 | abs(agentId.position[market.id])) 62 | 63 | # If there are active buy orders --> improve best bid by one tick 64 | if market.id in order.activeBuyOrders.keys(): 65 | if not not order.activeBuyOrders[market.id]: 66 | buyOrders = sorted(order.activeBuyOrders[market.id], key = operator.attrgetter("price"), reverse = True) 67 | bestBid = buyOrders[0] 68 | 69 | # If trader is not best bid --> improve best bid 70 | if not bestBid.agent.name == agentId.name: 71 | order(market, agentId, "Buy", bestBid.price + market.ticksize, quantity) 72 | else: 73 | pass 74 | # Else --> create best bid 75 | else: 76 | order(market, agentId, "Buy", market.minprice, quantity) 77 | # If no buy orders active in market --> start the market 78 | else: 79 | order(market, agentId, "Sell", market.maxprice, quantity) 80 | 81 | # If there are active sell orders --> improve best offer by one tick 82 | if market.id in order.activeSellOrders.keys(): 83 | if not not order.activeSellOrders[market.id]: 84 | sellOrders = sorted(order.activeSellOrders[market.id], key = operator.attrgetter("price")) 85 | bestOffer = sellOrders[0] 86 | 87 | # If trader is not best offer --> improve best offer 88 | if not bestOffer.agent.name == agentId.name: 89 | order(market, agentId, "Sell", bestOffer.price - market.ticksize, quantity) 90 | else: 91 | pass 92 | # Else --> create best bid 93 | else: 94 | order(market, agentId, "Sell", market.maxprice, quantity) 95 | else: 96 | # If no sell orders active in market --> start the market 97 | order(market, agentId, "Sell", market.maxprice, quantity) 98 | 99 | ############################################################################### 100 | # AGENT: ARBITRAGE (BETWEEN MARKETS) 101 | ############################################################################### 102 | # If price at marketB is lower than bestBid at marketA --> agents buys bestOffer at MarketB and sells to bestBid at marketA. 103 | # The same goes the other way around. 104 | def simpleArbitrage(agentId, marketA, marketB): 105 | # If marketA is initiated 106 | if marketA.id in order.activeBuyOrders.keys(): 107 | # If there are active buy orders 108 | if not not order.activeBuyOrders[marketA.id]: 109 | # If marketB is initiated 110 | if marketB.id in order.activeSellOrders.keys(): 111 | # If there are active sell orders 112 | if not not order.activeSellOrders[marketB.id]: 113 | buyOrdersMarketA = sorted(order.activeBuyOrders[marketA.id], key = operator.attrgetter("price"), 114 | reverse = True) 115 | bestBidMarketA = buyOrdersMarketA[0] 116 | 117 | sellOrdersMarketB = sorted(order.activeSellOrders[marketB.id], 118 | key = operator.attrgetter("price"), reverse = False) 119 | bestOfferMarketB = sellOrdersMarketB[0] 120 | 121 | if bestBidMarketA.price > bestOfferMarketB.price: 122 | order(marketB, agentId, "Buy", bestOfferMarketB.price, bestOfferMarketB.quantity) 123 | order(marketA, agentId, "Sell", bestBidMarketA.price, bestBidMarketA.quantity) 124 | # The other way around 125 | if marketA.id in order.activeSellOrders.keys(): 126 | if not not order.activeSellOrders[marketA.id]: 127 | if marketB.id in order.activeBuyOrders.keys(): 128 | if not not order.activeBuyOrders[marketB.id]: 129 | sellOrdersMaketA = sorted(order.activeSellOrders[marketA.id], 130 | key = operator.attrgetter("price"), reverse = False) 131 | bestOfferMarketA = sellOrdersMaketA[0] 132 | 133 | buyOrdersMarketB = sorted(order.activeBuyOrders[marketB.id], key = operator.attrgetter("price"), 134 | reverse = True) 135 | bestBidMarketB = buyOrdersMarketB[0] 136 | 137 | if bestBidMarketB.price > bestOfferMarketA.price: 138 | order(marketA, agentId, "Buy", bestOfferMarketA.price, bestOfferMarketA.quantity) 139 | order(marketB, agentId, "Sell", bestBidMarketB.price, bestBidMarketB.quantity) 140 | 141 | ############################################################################### 142 | # AGENT: ONE BUY ORDER WITH STOP LOSS 143 | ############################################################################### 144 | def stopLoss(agentId, market): 145 | ''' 146 | if market.id in self.lastOrder.keys(): 147 | if (self.lastOrder[market.id].side == "Buy"): 148 | self.stop[market.id] = self.lastOrder[market.id].price * (1 + stop_loss) 149 | 150 | lastPrice = agent.getLastPriceOrElse(market) 151 | 152 | # If agent doesn't have position in market 153 | if (not market.id in agent.position.keys()) or (agent.position[market.id] == 0): 154 | # Agent buys at market (= for any price). This is +- equivalent to buying at price = lastPrice * 2 155 | price = market.maxprice 156 | # Correct for ticksize market 157 | quantity = random.randint(market.minquantity, market.maxquantity) 158 | 159 | # Send order to market 160 | order(market, self, "Buy", price, quantity) 161 | 162 | # Initiate stop 163 | if "stop_loss" in self.params.keys(): 164 | stop_loss = self.params["stop_loss"] 165 | self.stop[market.id] = price * (1 + stop_loss) 166 | else: 167 | if lastPrice < self.stop[market.id]: 168 | # Sell position at market 169 | order(market, self, "Sell", market.minprice, self.position[market.id]) 170 | ''' 171 | return 0 172 | 173 | ############################################################################### 174 | # AGENT: ADD strategies TO ARRAY 175 | ############################################################################### 176 | strategies = [randomUniform, randomNormal, randomLogNormal, bestBidOffer, simpleArbitrage, stopLoss] 177 | strategiesName = ["randomUniform", "randomNormal", "randomLogNormal", "bestBidOffer", "simpleArbitrage", "stopLoss"] 178 | linestyles = ["solid", "dotted", "solid", "dashed", "dashdot", "dashed"] 179 | strategiesDict = {} 180 | 181 | for i, x in enumerate(strategiesName): 182 | # print(i) 183 | if (i % 2 == 0): 184 | strategiesDict[x] = {"strategy": strategies[i], "linestyle": "dotted"} 185 | else: 186 | strategiesDict[x] = {"strategy": strategies[i], "linestyle": "solid"} 187 | -------------------------------------------------------------------------------- /ordergenerator/transaction.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | 3 | class transaction(): 4 | counter = itertools.count() 5 | history = {} 6 | historyList = {} 7 | historyMarketAgent = {} 8 | 9 | # Initialize transaction 10 | def __init__(self, buyOrder, sellOrder, market, price, quantity): 11 | market.transaction_counter += 1 12 | self.id = market.transaction_counter 13 | 14 | # If transaction is buyer initiated --> take buyOrder.datetime == trade time. 15 | # Else --> take sellOrder.datetime 16 | self.datetime = max(buyOrder.datetime, sellOrder.datetime) 17 | self.market = market 18 | self.buyOrder = buyOrder 19 | self.sellOrder = sellOrder 20 | self.price = price 21 | self.quantity = quantity 22 | 23 | # If first transaction by agent at market --> initialize values 24 | if not market.id in buyOrder.agent.position.keys(): 25 | buyOrder.agent.position[market.id] = 0 26 | buyOrder.agent.valueBought[market.id] = 0 27 | buyOrder.agent.quantityBought[market.id] = 0 28 | buyOrder.agent.valueSold[market.id] = 0 29 | buyOrder.agent.quantitySold[market.id] = 0 30 | 31 | if not market.id in sellOrder.agent.position.keys(): 32 | sellOrder.agent.position[market.id] = 0 33 | sellOrder.agent.valueBought[market.id] = 0 34 | sellOrder.agent.quantityBought[market.id] = 0 35 | sellOrder.agent.valueSold[market.id] = 0 36 | sellOrder.agent.quantitySold[market.id] = 0 37 | 38 | if not market.id in transaction.history.keys(): 39 | transaction.history[market.id] = [] 40 | transaction.historyList[market.id] = [] 41 | 42 | if not (market.id, buyOrder.agent.name) in transaction.historyMarketAgent.keys(): 43 | transaction.historyMarketAgent[market.id, buyOrder.agent.name] = [] 44 | 45 | if not (market.id, sellOrder.agent.name) in transaction.historyMarketAgent.keys(): 46 | transaction.historyMarketAgent[market.id, sellOrder.agent.name] = [] 47 | 48 | # Update values agents at market 49 | buyOrder.agent.position[market.id] += quantity 50 | sellOrder.agent.position[market.id] -= quantity 51 | buyOrder.agent.valueBought[market.id] += price * quantity 52 | buyOrder.agent.quantityBought[market.id] += quantity 53 | sellOrder.agent.valueSold[market.id] += price * quantity 54 | sellOrder.agent.quantitySold[market.id] += quantity 55 | 56 | # Add to transaction history 57 | transaction.history[market.id].append(self) 58 | transaction.historyList[market.id].append([self.id, self.datetime.time(), self.price]) 59 | 60 | # Add to history agent at market 61 | transaction.historyMarketAgent[market.id, buyOrder.agent.name].append([self.id, 62 | buyOrder.agent.position[market.id], 63 | transaction.calculateRprofit( 64 | buyOrder.agent, market)]) 65 | transaction.historyMarketAgent[market.id, sellOrder.agent.name].append([self.id, 66 | sellOrder.agent.position[market.id], 67 | transaction.calculateRprofit( 68 | sellOrder.agent, market)]) 69 | 70 | ### METHODS 71 | # Display transaction 72 | def __str__(self): 73 | return "{} \t {} \t {} \t {} \t {} \t {} \t {}".format(self.id, self.datetime.time(), self.market, 74 | self.buyOrder.agent.name, self.sellOrder.agent.name, 75 | self.price, self.quantity) 76 | 77 | # Calculate realized profit agent at market 78 | 79 | def calculateRprofit(agent, market): 80 | if agent.quantitySold[market.id] > 0: 81 | askVwap = agent.valueSold[market.id] / agent.quantitySold[market.id] 82 | else: 83 | askVwap = 0 84 | if agent.quantityBought[market.id] > 0: 85 | bidVwap = agent.valueBought[market.id] / agent.quantityBought[market.id] 86 | else: 87 | bidVwap = 0 88 | 89 | q = min(agent.quantitySold[market.id], agent.quantityBought[market.id]) 90 | rp = q * (askVwap - bidVwap) 91 | return rp 92 | 93 | def transactionDescription(bid, offer, market, price, quantity): 94 | return print( 95 | "At market {} - Best bid: {} ({}) Best offer: {} ({}) --> Transaction at: {} ({})".format(market.id, 96 | bid.price, 97 | bid.quantity, 98 | offer.price, 99 | offer.quantity, 100 | price, quantity)) 101 | -------------------------------------------------------------------------------- /pictures/depthOrderbook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bobbakov/orderGenerator/33091401aab86013ed3a682aa6f1e0698692f39e/pictures/depthOrderbook.png -------------------------------------------------------------------------------- /pictures/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bobbakov/orderGenerator/33091401aab86013ed3a682aa6f1e0698692f39e/pictures/example.png -------------------------------------------------------------------------------- /pictures/examplePriceDevelopment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bobbakov/orderGenerator/33091401aab86013ed3a682aa6f1e0698692f39e/pictures/examplePriceDevelopment.png -------------------------------------------------------------------------------- /pictures/manyAgents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bobbakov/orderGenerator/33091401aab86013ed3a682aa6f1e0698692f39e/pictures/manyAgents.png -------------------------------------------------------------------------------- /pictures/marketMakerWithPositionLimits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bobbakov/orderGenerator/33091401aab86013ed3a682aa6f1e0698692f39e/pictures/marketMakerWithPositionLimits.png -------------------------------------------------------------------------------- /pictures/marketMakerWithPositionLimitsBuyProb65.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bobbakov/orderGenerator/33091401aab86013ed3a682aa6f1e0698692f39e/pictures/marketMakerWithPositionLimitsBuyProb65.png -------------------------------------------------------------------------------- /pictures/plot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bobbakov/orderGenerator/33091401aab86013ed3a682aa6f1e0698692f39e/pictures/plot.png -------------------------------------------------------------------------------- /pictures/plotPositions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bobbakov/orderGenerator/33091401aab86013ed3a682aa6f1e0698692f39e/pictures/plotPositions.png -------------------------------------------------------------------------------- /pictures/plotProfits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bobbakov/orderGenerator/33091401aab86013ed3a682aa6f1e0698692f39e/pictures/plotProfits.png -------------------------------------------------------------------------------- /pictures/showOrderBook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bobbakov/orderGenerator/33091401aab86013ed3a682aa6f1e0698692f39e/pictures/showOrderBook.png -------------------------------------------------------------------------------- /pictures/simpleArbitrage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bobbakov/orderGenerator/33091401aab86013ed3a682aa6f1e0698692f39e/pictures/simpleArbitrage.png -------------------------------------------------------------------------------- /pictures/summary2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bobbakov/orderGenerator/33091401aab86013ed3a682aa6f1e0698692f39e/pictures/summary2.png -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name = "ordergenerator", 5 | version = "0.1", 6 | description = "Agent-based modeling of markets", 7 | url = "http://github.com/bobbakov/orderGenerator", 8 | author = "main author", 9 | author_email = "main.author@email.com", 10 | license = "GPL3", 11 | packages = ["ordergenerator"], 12 | install_requires = ["seaborn", "matplotlib", "numpy", ], 13 | zip_safe = False 14 | ) --------------------------------------------------------------------------------