Skip to content

Commit

Permalink
fixes #26
Browse files Browse the repository at this point in the history
  • Loading branch information
timkpaine committed Jul 2, 2019
1 parent ea25dba commit 8fa062f
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 114 deletions.
76 changes: 74 additions & 2 deletions aat/callback.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from abc import ABCMeta, abstractmethod
from .enums import Side
from .structs import MarketData, TradeResponse
from .logging import log

Expand Down Expand Up @@ -37,9 +38,80 @@ def onExit(self):
'''onExit'''
pass

def onAnalyze(self, data):
def onAnalyze(self, engine):
'''onAnalyze'''
pass
import pandas
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import seaborn as sns
matplotlib.rc('font', **{'size': 5})

# extract data from trading engine
portfolio_value = engine.portfolio_value()
requests = engine.query().query_tradereqs()
responses = engine.query().query_traderesps()
trades = pandas.DataFrame([{'time': x.time, 'price': x.price} for x in engine.query().query_trades(instrument=requests[0].instrument, page=None)])
trades.set_index(['time'], inplace=True)

# format into pandas
pd = pandas.DataFrame(portfolio_value, columns=['time', 'value', 'pnl'])
pd.set_index(['time'], inplace=True)

# setup charting
sns.set_style('darkgrid')
fig = plt.figure()
ax1 = fig.add_subplot(311)
ax2 = fig.add_subplot(312)
ax3 = fig.add_subplot(313)

# plot algo performance
pd.plot(ax=ax1, y=['value'], legend=False, fontsize=5, rot=0)

# plot up/down chart
pd['pos'] = pd['pnl']
pd['neg'] = pd['pnl']
pd['pos'][pd['pos'] <= 0] = np.nan
pd['neg'][pd['neg'] > 0] = np.nan
pd.plot(ax=ax2, y=['pos', 'neg'], kind='area', stacked=False, color=['green', 'red'], legend=False, linewidth=0, fontsize=5, rot=0)

# annotate with key data
ax1.set_title('Performance')
ax1.set_ylabel('Portfolio value($)')
for xy in [portfolio_value[0][:2]] + [portfolio_value[-1][:2]]:
ax1.annotate('$%s' % xy[1], xy=xy, textcoords='data')
ax3.annotate('$%s' % xy[1], xy=xy, textcoords='data')

# plot trade intent/trade action
ax3.set_ylabel('Intent/Action')
ax3.set_xlabel('Date')

ax3.plot(trades)
ax3.plot([x.time for x in requests if x.side == Side.BUY],
[x.price for x in requests if x.side == Side.BUY],
'2', color='y')
ax3.plot([x.time for x in requests if x.side == Side.SELL],
[x.price for x in requests if x.side == Side.SELL],
'1', color='y')
ax3.plot([x.time for x in responses if x.side == Side.BUY], # FIXME
[x.price for x in responses if x.side == Side.BUY],
'^', color='g')
ax3.plot([x.time for x in responses if x.side == Side.SELL], # FIXME
[x.price for x in responses if x.side == Side.SELL],
'v', color='r')

# set same limits
y_bot, y_top = ax1.get_ylim()
x_bot, x_top = ax1.get_xlim()
ax3.set_ylim(y_bot, y_top)
ax1.set_xlim(x_bot, x_top)
ax2.set_xlim(x_bot, x_top)
ax3.set_xlim(x_bot, x_top)
dif = (x_top-x_bot)*.01
ax1.set_xlim(x_bot-dif, x_top+dif)
ax2.set_xlim(x_bot-dif, x_top+dif)
ax3.set_xlim(x_bot-dif, x_top+dif)
plt.show()

def onHalt(self, data):
'''onHalt'''
Expand Down
3 changes: 2 additions & 1 deletion aat/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ def _recalculate_value(self, data: MarketData) -> None:
# start from first tick of data
# TODO risk bounds?
self._portfolio_value = [[data.time, volume*data.price, volume*(data.price-purchase_price)]]
self._portfolio_value.append([data.time, volume*data.price, volume*(data.price-purchase_price)])
else:
self._portfolio_value.append([data.time, volume*data.price, volume*(data.price-purchase_price)])

def update_holdings(self, resp: TradeResponse) -> None:
# TODO move to risk
Expand Down
89 changes: 5 additions & 84 deletions aat/strategies/buy_and_hold.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@


class BuyAndHoldStrategy(TradingStrategy):
def __init__(self) -> None:
super(BuyAndHoldStrategy, self).__init__()
def __init__(self, *args, **kwargs) -> None:
super(BuyAndHoldStrategy, self).__init__(*args, **kwargs)
self.bought = None

def onFill(self, res: TradeResponse) -> None:
self.bought = res
log.info('d->g:bought %.2f @ %.2f' % (res.volume, res.price))
log.info('bought %.2f @ %.2f' % (res.volume, res.price))

def onTrade(self, data: MarketData) -> bool:
if self.bought is None:
Expand All @@ -23,97 +23,18 @@ def onTrade(self, data: MarketData) -> bool:
price=data.price,
time=data.time)
log.info("requesting buy : %s", req)
self.requestBuy(req)
self.request(req)
self.bought = 'pending'

def onError(self, e) -> None:
log.critical(e)

def onAnalyze(self, engine) -> None:
import pandas
import numpy as np
import matplotlib, matplotlib.pyplot as plt # noqa: E401
import seaborn as sns
matplotlib.rc('font', **{'size': 5})

# extract data from trading engine
portfolio_value = engine.portfolio_value()
requests = engine.query().query_tradereqs()
responses = engine.query().query_traderesps()
trades = pandas.DataFrame([{'time': x.time, 'price': x.price} for x in engine.query().query_trades(instrument=requests[0].instrument, page=None)])
trades.set_index(['time'], inplace=True)

# format into pandas
pd = pandas.DataFrame(portfolio_value, columns=['time', 'value', 'pnl'])
pd.set_index(['time'], inplace=True)

# setup charting
sns.set_style('darkgrid')
fig = plt.figure()
ax1 = fig.add_subplot(311)
ax2 = fig.add_subplot(312)
ax3 = fig.add_subplot(313)

# plot algo performance
pd.plot(ax=ax1, y=['value'], legend=False, fontsize=5, rot=0)

# plot up/down chart
pd['pos'] = pd['pnl']
pd['neg'] = pd['pnl']
pd['pos'][pd['pos'] <= 0] = np.nan
pd['neg'][pd['neg'] > 0] = np.nan
pd.plot(ax=ax2, y=['pos', 'neg'], kind='area', stacked=False, color=['green', 'red'], legend=False, linewidth=0, fontsize=5, rot=0)

# annotate with key data
ax1.set_title('Performance')
ax1.set_ylabel('Portfolio value($)')
for xy in [portfolio_value[0][:2]] + [portfolio_value[-1][:2]]:
ax1.annotate('$%s' % xy[1], xy=xy, textcoords='data')
ax3.annotate('$%s' % xy[1], xy=xy, textcoords='data')

# plot trade intent/trade action
ax3.set_ylabel('Intent/Action')
ax3.set_xlabel('Date')

ax3.plot(trades)
ax3.plot([x.time for x in requests if x.side == Side.BUY],
[x.price for x in requests if x.side == Side.BUY],
'2', color='y')
ax3.plot([x.time for x in requests if x.side == Side.SELL],
[x.price for x in requests if x.side == Side.SELL],
'1', color='y')
ax3.plot([x.time for x in responses if x.side == Side.BUY], # FIXME
[x.price for x in responses if x.side == Side.BUY],
'^', color='g')
ax3.plot([x.time for x in responses if x.side == Side.SELL], # FIXME
[x.price for x in responses if x.side == Side.SELL],
'v', color='r')

# set same limits
y_bot, y_top = ax1.get_ylim()
x_bot, x_top = ax1.get_xlim()
ax3.set_ylim(y_bot, y_top)
ax1.set_xlim(x_bot, x_top)
ax2.set_xlim(x_bot, x_top)
ax3.set_xlim(x_bot, x_top)
dif = (x_top-x_bot)*.01
ax1.set_xlim(x_bot-dif, x_top+dif)
ax2.set_xlim(x_bot-dif, x_top+dif)
ax3.set_xlim(x_bot-dif, x_top+dif)
plt.show()

def onChange(self, data: MarketData) -> None:
pass

def onContinue(self, data: MarketData) -> None:
pass

def onCancel(self, data: MarketData) -> None:
pass

def onHalt(self, data: MarketData) -> None:
pass

def onOpen(self, data: MarketData) -> None:
pass

Expand Down Expand Up @@ -161,7 +82,7 @@ def onTrade(self, data: MarketData) -> bool:
price=data.price,
time=data.time)
log.info("requesting sell : %s", req)
self.requestSell(req)
self.request(req)
self.sold = 'pending'

def onError(self, e) -> None:
Expand Down
4 changes: 2 additions & 2 deletions aat/strategies/data_capture.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@


class DataCaptureStrategy(TradingStrategy):
def __init__(self, filename) -> None:
super(DataCaptureStrategy, self).__init__()
def __init__(self, filename, *args, **kwargs) -> None:
super(DataCaptureStrategy, self).__init__(*args, **kwargs)
self.filename = filename

def onTrade(self, data: MarketData) -> bool:
Expand Down
23 changes: 8 additions & 15 deletions aat/strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,22 @@

class Strategy(metaclass=ABCMeta):
'''Strategy interface'''
def __init__(self, *args, **kwargs) -> None:
pass
def __init__(self, query=None, exchanges=None, *args, **kwargs) -> None:
self.query = query
self.exchanges = exchanges

def setEngine(self, engine) -> None:
self._te = engine

@abstractmethod
def requestBuy(self, req: TradeRequest):
'''requestBuy'''

@abstractmethod
def requestSell(self, req: TradeRequest):
'''requestSell'''
def request(self, req: TradeRequest):
'''request'''


class TradingStrategy(Strategy, Callback):
def requestBuy(self, req: TradeRequest) -> None:
'''attempt to buy'''
return self._te.requestBuy(req, self)

def requestSell(self, req: TradeRequest) -> None:
'''attempt to sell'''
return self._te.requestSell(req, self)
def request(self, req: TradeRequest) -> None:
'''attempt to buy/sell'''
return self._te.request(req, self)

def cancel(self, resp: TradeResponse) -> None:
'''cancel order'''
Expand Down
17 changes: 7 additions & 10 deletions aat/trading.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ def __init__(self, options: TradingEngineConfig, ui_handlers: list = None, ui_se
# register strategies from config
for x in options.strategy_options:
log.critical('Registering strategy: %s', str(x.clazz))
x.kwargs['query'] = self.query()
x.kwargs['exchanges'] = self.exchanges()
self.registerStrategy(x.clazz(*x.args, **x.kwargs))

# actively trading or halted?
Expand Down Expand Up @@ -171,7 +173,7 @@ async def _run():

elif self._backtest:
# trigger starts
for strat in self._strats:
for strat in self.query().strategies():
strat.onStart()

# let backtester run
Expand Down Expand Up @@ -268,15 +270,10 @@ def _request(self,
self.query().update_holdings(resp)
return resp

def requestBuy(self, req: TradeRequest, strat=None):
self._request(side=Side.BUY,
req=req,
strat=strat)

def requestSell(self, req: TradeRequest, strat=None):
self._request(side=Side.SELL,
req=req,
strat=strat)
def request(self, req: TradeRequest, strat=None):
return self._request(side=req.side,
req=req,
strat=strat)

def cancel(self, resp: TradeResponse, strat=None):
resp = self._ec.cancel(resp)
Expand Down

0 comments on commit 8fa062f

Please sign in to comment.