Skip to content

Commit

Permalink
synthetic backtest looking good
Browse files Browse the repository at this point in the history
  • Loading branch information
timkpaine committed Jun 16, 2019
1 parent 5f649aa commit 63ad129
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 40 deletions.
3 changes: 3 additions & 0 deletions aat/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ class SyntheticExchangeConfig(ExchangeConfig):
exchange_type = Instance(ExchangeType)
adversaries = List(default_value=[])

direction = Float(default_value=2.0)
volatility = Float(default_value=50)


class BacktestConfig(HasTraits):
pass
Expand Down
99 changes: 82 additions & 17 deletions aat/exchanges/synthetic.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import asyncio
import ccxt
import json
import pandas as pd
import time
from datetime import datetime
from functools import lru_cache
from numpy.random import normal, laplace
from random import randint, random, choice
from typing import List
from ..config import SyntheticExchangeConfig
from ..enums import ExchangeType, TickType, PairType, OrderType, CurrencyType
from ..enums import ExchangeType, TickType, Side, PairType, OrderType, CurrencyType, TradingType, ExchangeType_to_string
from ..exchange import Exchange
from ..order_book import OrderBook
from ..structs import MarketData, TradeRequest, TradeResponse, Instrument, Account
Expand All @@ -17,15 +22,61 @@ def __init__(self,
query_engine=None) -> None:
super(SyntheticExchange, self).__init__(exchange_type=exchange_type, options=options, query_engine=query_engine)
self._book = OrderBook(options.instruments)
self._instruments = options.instruments
self._trading_type = options.trading_type

# random walk generated by laplace
# generated order book will skew in this direction
self._direction = options.direction

# generated order book will skew with this volatility
self._volatility = options.volatility

self._starting_price = 100

self._underlying_exchange = options.exchange_type
self._advesaries = []

def generateMsg(self):
raise NotImplementedError()
price = 1000
while True:
# take a random walk

# endpoint
next_ = normal(self._direction, self._volatility)

# walk between
# substeps = normal(self._direction/10, self._volatility/10, randint(1, 10))

# scale to fill next
# substeps = substeps*(next_/substeps.cumsum()[-1])
# for step in substeps:
# price += step
# price = abs(price)
price = abs(price + next_)
data = MarketData(time=datetime.now(),
volume=random()*10,
price=price,
type=TickType.TRADE,
instrument=self._instruments[0],
side=choice([Side.BUY, Side.SELL]),
exchange=self._underlying_exchange,
remaining=random()*10,
sequence=-1,
order_type=OrderType.NONE)
if self._trading_type != TradingType.BACKTEST:
time.sleep(random()*3)
else:
time.sleep(random()/100)
yield data

async def run(self, trading) -> None:
while True:
await self.receive()

async def receive(self) -> None:
async for msg in self.generateMsg():
self.callback_data(json.loads(msg.data))
for msg in self.generateMsg():
self.callback_data(msg)

@lru_cache(None)
def oe_client(self):
Expand Down Expand Up @@ -104,20 +155,34 @@ def heartbeat(self):
def tickToData(self, jsn: dict) -> MarketData:
pass

def strToTradeType(self, s: str, reason: str = '') -> TickType:
pass
def historical(self):
data = []
i = 0
for msg in self.generateMsg():
# msgs = asyncio.get_event_loop().run_until_complete(asyncio.ensure_future(self.generateMsg()))
data.append(msg)

i += 1
if i > 1000:
break

data = [{'timestamp': d.time,
'open': d.price,
'high': d.price,
'low': d.price,
'close': d.price,
'volume': d.volume} for d in data]

dfs = [{'pair': str(symbol),
'exchange': ExchangeType_to_string(self._underlying_exchange),
'data': data}
for symbol in self.options().currency_pairs]
df = pd.io.json.json_normalize(dfs, 'data', ['pair', 'exchange'])
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
df.set_index(['timestamp', 'pair'], inplace=True)
df.sort_index(inplace=True)
return df

def tradeReqToParams(self, req) -> dict:
pass

def currencyPairToString(self, cur: PairType) -> str:
pass

def orderTypeToString(self, typ: OrderType) -> str:
pass

def reasonToTradeType(self, s: str) -> TickType:
pass


class Adversary(object):
Expand Down
20 changes: 0 additions & 20 deletions aat/market_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,23 +77,3 @@ async def run(self, engine) -> None:
@abstractmethod
def tickToData(self, jsn: dict) -> MarketData:
pass

@abstractmethod
def strToTradeType(self, s: str) -> TickType:
pass

@abstractmethod
def tradeReqToParams(self, req) -> dict:
pass

@abstractmethod
def currencyPairToString(self, cur: PairType) -> str:
pass

@abstractmethod
def orderTypeToString(self, typ: OrderType) -> str:
pass

@abstractmethod
def reasonToTradeType(self, s: str) -> TickType:
pass
2 changes: 1 addition & 1 deletion aat/order_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def ticker(self,
def historical(self, timeframe='1m', since=None, limit=None):
'''get historical data (for backtesting)'''
client = self.oe_client()
dfs = [{'pair': str(symbol), 'exchange': self.exchange().value, 'data': client.fetch_ohlcv(symbol=str(symbol), timeframe=timeframe, since=since, limit=limit)}
dfs = [{'pair': str(symbol), 'exchange': ExchangeType_to_string(self.exchange()), 'data': client.fetch_ohlcv(symbol=str(symbol), timeframe=timeframe, since=since, limit=limit)}
for symbol in self.options().currency_pairs]
df = pd.io.json.json_normalize(dfs, 'data', ['pair', 'exchange'])
df.columns = ['timestamp', 'open', 'high', 'low', 'close', 'volume', 'pair', 'exchange']
Expand Down
27 changes: 26 additions & 1 deletion aat/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import os.path
from configparser import ConfigParser
from pydoc import locate
from .config import TradingEngineConfig, BacktestConfig, StrategyConfig
from .config import TradingEngineConfig, BacktestConfig, StrategyConfig, SyntheticExchangeConfig
from .enums import TradingType, InstrumentType, ExchangeType
from .exceptions import ConfigException
from .structs import Instrument
Expand Down Expand Up @@ -141,6 +141,17 @@ def _parse_options(argv, config: TradingEngineConfig) -> None:
if config.type == TradingType.LIVE and exchange == ExchangeType.SYNTHETIC:
raise ConfigException('Cannot run synthetic exchange in live mode!')
elif exchange == ExchangeType.SYNTHETIC:
new_config = SyntheticExchangeConfig()
new_config.exchange_types = config.exchange_options.exchange_types
new_config.trading_type = config.exchange_options.trading_type
new_config.currency_pairs = config.exchange_options.currency_pairs
new_config.instruments = config.exchange_options.instruments

if argv.get('direction'):
new_config.direction = argv.get('direction')
if argv.get('volatility'):
new_config.volatility = argv.get('volatility')
config.exchange_options = new_config
config.exchange_options.exchange_type = ExchangeType.COINBASE
else:
raise ConfigException('No exchange set!')
Expand Down Expand Up @@ -178,6 +189,20 @@ def _parse_backtest_options(argv, config) -> None:

if argv.get('exchanges'):
config.exchange_options.exchange_types = [str_to_exchange(x) for x in argv['exchanges'].split() if x]
for exchange in config.exchange_options.exchange_types:
if exchange == ExchangeType.SYNTHETIC:
new_config = SyntheticExchangeConfig()
new_config.exchange_types = config.exchange_options.exchange_types
new_config.trading_type = config.exchange_options.trading_type
new_config.currency_pairs = config.exchange_options.currency_pairs
new_config.instruments = config.exchange_options.instruments

if argv.get('direction'):
new_config.direction = argv.get('direction')
if argv.get('volatility'):
new_config.volatility = argv.get('volatility')
config.exchange_options = new_config
config.exchange_options.exchange_type = ExchangeType.COINBASE
else:
raise ConfigException('No exchange set!')

Expand Down
2 changes: 1 addition & 1 deletion config/synthetic.cfg
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[general]
verbose=1
print=0
TradingType=simulation
TradingType=backtest

[exchange]
exchanges=
Expand Down

0 comments on commit 63ad129

Please sign in to comment.