Source code for flashcrashed.market.gen

import os
import random
from collections import deque

import numpy as np
import pandas as pd


[docs]def market_maker(): directory = '/'.join([i for i in os.getcwd().replace('\\', '/').split('/') if i not in {'flashcrashed', 'market', 'tests'}] + ['flashcrashed', 'flashcrashed', 'market']) + '/' # Generated crashes from real market data with open(directory + 'prices.csv') as f: data = pd.read_csv(f, index_col=0) markets = [] symbols = data['symbol'].unique() for symbol in symbols: _data = data[data['symbol'] == symbol] markets.append(_data['last_price']) # Real crashes that happened crashes = [] for f in os.listdir(directory + 'real_crashes/'): with open(directory + 'real_crashes/' + f) as rf: data = pd.read_csv(rf, index_col=0) crashes.append(list(data['price'])) cs = iter(crashes) def factory(real=False): nonlocal cs if real: try: return next(cs) except StopIteration: cs = iter(crashes) return next(cs) else: return iter(random.choice(markets)) return factory
[docs]class FlashedMarket: def __init__(self, market, real=False): self.real = real self._market = market self.market = self._market self.last_price = 0 self.position = 0 self.crashes = [] self.crashed = False self.drops = [] self.rises = [] self.crash_start = 0 self.flash_ratio = 0 self.drop_duration = 0 self.rise_duration = 0 self.min_price = 1e9 self.return_count = 0 self.returned_after = 25 self.reset()
[docs] def reset(self): self.crashed = False self.min_price = 1e9 self.last_price = 0 if not self.real: end = min(max(5000 - self.position, 0), 1300) if 5000 - self.position > 1100: self.crash_start = self.position + random.randint(500, end) self.flash_ratio = random.randint(5, 10) if random.random() < 0.5: self.flash_ratio = random.randint(10, 100) self.drop_duration = random.randint(3, 5) if random.random() < 0.5: self.drop_duration = random.randint(5, 15) self.rise_duration = random.randint(2, 5) if random.random() < 0.5: self.rise_duration = random.randint(5, 50) self.drops = iter(self.random_offset( self.drop_duration, 0.8, order='exp')) self.rises = iter(self.random_offset( self.rise_duration, 0.6, reverse=True)) self.market = self._market else: self.min_price = min(self._market) crashed = False prices = deque(maxlen=500) for i, price in enumerate(self._market): prices.append(price) if sum(prices) / len(prices) / price >= 1.1 and not crashed: crashed = True self.crash_start = i self.drop_duration = self._market.index( self.min_price) - self.crash_start + 1 elif price / (sum(prices) / len(prices)) >= 0.85 and crashed: self.rise_duration = ( i - self.crash_start + self.drop_duration - 1 ) break self.flash_ratio = sum(self._market[:10]) / 10 / self.min_price self.market = iter(self._market)
[docs] @staticmethod def random_offset(duration, volatility=0.5, reverse=False, order='linear'): rv = int(reverse) sp = np.linspace if order == 'linear' else np.geomspace base_space = sp(0 + rv + 0.0001, 1 - rv + 0.0001, duration) r = np.full([duration], 0.5) - np.random.rand(duration) space = base_space + base_space * r * volatility space[rv - 1] = min(space[rv - 1], 1) space[-rv] = max(space[-rv], 0) return space
@property def crash_mid(self): return self.crash_start + self.drop_duration @property def crash_end(self): return self.crash_start + self.drop_duration + self.rise_duration @property def crashing(self): return self.crash_start < self.position <= self.crash_mid @property def rising(self): return self.crash_mid < self.position <= self.crash_end @property def returned(self): return self.crashed and (not self.crashing and not self.rising) def __iter__(self): return self def __next__(self): self.position += 1 price = next(self.market) self.last_price = price if not self.real: if self.crashing: self.crashed = True price /= max(next(self.drops) * self.flash_ratio, 1) elif self.rising: price /= max(next(self.rises) * self.flash_ratio, 1) elif self.returned: self.return_count += 1 if self.return_count >= self.returned_after: crash = { 'start': self.crash_start, 'drop_duration': self.drop_duration, 'rise_duration': self.rise_duration, 'ratio': self.flash_ratio, 'min_price': self.min_price } if ( not self.crashes or crash['start'] != self.crashes[-1]['start'] ): self.crashes.append(crash) self.reset() if price < self.min_price: self.min_price = price else: if self.crashing: self.crashed = True elif self.returned: crash = { 'start': self.crash_start, 'drop_duration': self.drop_duration, 'rise_duration': self.rise_duration, 'ratio': self.flash_ratio, 'min_price': self.min_price } if ( not self.crashes or crash['start'] != self.crashes[-1]['start'] ): self.crashes.append(crash) self.crash_start = 0 self.crashed = False return price