Browse Source

Some updates

master
Denis Tereshkin 5 years ago
parent
commit
145e059e3c
  1. 44
      src/naiback/analyzers/statsanalyzer.py
  2. 14
      src/naiback/broker/broker.py
  3. 31
      src/naiback/broker/position.py
  4. 35
      src/naiback/data/feeds/yahoofeed.py
  5. 4
      src/naiback/indicators/__init__.py
  6. 33
      src/naiback/indicators/adx.py
  7. 24
      src/naiback/indicators/atr.py
  8. 3
      src/naiback/indicators/bollinger.py
  9. 2
      src/naiback/indicators/ema.py
  10. 13
      src/naiback/indicators/smma.py

44
src/naiback/analyzers/statsanalyzer.py

@ -16,6 +16,25 @@ def render_ratio(a, b):
else: else:
return 0 return 0
def calculate_kelly(pos):
winning = list(filter(lambda x: x.pnl() >= 0, pos))
losing = list(filter(lambda x: x.pnl() < 0, pos))
won = len(winning)
lost = len(losing)
if won == 0:
return 0
elif lost == 0:
return 1
avg_winning = sum(map(lambda x: x.pnl(), winning)) / won
avg_losing = sum(map(lambda x: x.pnl(), losing)) / lost
p = float(won) / (won + lost)
return p + (1 - p)/(avg_winning / avg_losing)
class StatsAnalyzer(Analyzer): class StatsAnalyzer(Analyzer):
def __init__(self, strategy): def __init__(self, strategy):
@ -57,6 +76,10 @@ class StatsAnalyzer(Analyzer):
result['long']['net_profit'] = sum([pos.pnl() for pos in longs]) result['long']['net_profit'] = sum([pos.pnl() for pos in longs])
result['short']['net_profit'] = sum([pos.pnl() for pos in shorts]) result['short']['net_profit'] = sum([pos.pnl() for pos in shorts])
result['all']['total_commission'] = sum([pos.total_commission() for pos in positions])
result['long']['total_commission'] = sum([pos.total_commission() for pos in longs])
result['short']['total_commission'] = sum([pos.total_commission() for pos in shorts])
result['all']['bars_in_trade'] = sum([pos.bars_in_trade() for pos in positions]) result['all']['bars_in_trade'] = sum([pos.bars_in_trade() for pos in positions])
result['long']['bars_in_trade'] = sum([pos.bars_in_trade() for pos in longs]) result['long']['bars_in_trade'] = sum([pos.bars_in_trade() for pos in longs])
result['short']['bars_in_trade'] = sum([pos.bars_in_trade() for pos in shorts]) result['short']['bars_in_trade'] = sum([pos.bars_in_trade() for pos in shorts])
@ -103,24 +126,27 @@ class StatsAnalyzer(Analyzer):
mean = np.mean(list(map(lambda x: x.pnl(), positions))) mean = np.mean(list(map(lambda x: x.pnl(), positions)))
stddev = np.std(list(map(lambda x: x.pnl(), positions))) stddev = np.std(list(map(lambda x: x.pnl(), positions)))
sharpe = mean / stddev z_score = mean / stddev
tstat = sharpe * math.sqrt(len(positions)) tstat = z_score * math.sqrt(len(positions))
result['all']['sharpe_ratio'] = sharpe result['all']['z_score'] = z_score
result['all']['t_stat'] = tstat result['all']['t_stat'] = tstat
result['all']['kelly'] = calculate_kelly(positions)
mean = np.mean(list(map(lambda x: x.pnl(), longs))) mean = np.mean(list(map(lambda x: x.pnl(), longs)))
stddev = np.std(list(map(lambda x: x.pnl(), longs))) stddev = np.std(list(map(lambda x: x.pnl(), longs)))
sharpe = mean / stddev z_score = mean / stddev
tstat = sharpe * math.sqrt(len(longs)) tstat = z_score * math.sqrt(len(longs))
result['long']['sharpe_ratio'] = sharpe result['long']['z_score'] = z_score
result['long']['t_stat'] = tstat result['long']['t_stat'] = tstat
result['long']['kelly'] = calculate_kelly(longs)
mean = np.mean(list(map(lambda x: x.pnl(), shorts))) mean = np.mean(list(map(lambda x: x.pnl(), shorts)))
stddev = np.std(list(map(lambda x: x.pnl(), shorts))) stddev = np.std(list(map(lambda x: x.pnl(), shorts)))
sharpe = mean / stddev z_score = mean / stddev
tstat = sharpe * math.sqrt(len(shorts)) tstat = z_score * math.sqrt(len(shorts))
result['short']['sharpe_ratio'] = sharpe result['short']['z_score'] = z_score
result['short']['t_stat'] = tstat result['short']['t_stat'] = tstat
result['short']['kelly'] = calculate_kelly(shorts)
return result return result

14
src/naiback/broker/broker.py

@ -15,6 +15,7 @@ class Broker:
self.positions = [] self.positions = []
self.retired_positions_ = [] self.retired_positions_ = []
self.commission_percentage = 0 self.commission_percentage = 0
self.commission_fixed = 0
self.timestamp = None self.timestamp = None
def cash(self): def cash(self):
@ -28,27 +29,30 @@ class Broker:
#if amount > 0: #if amount > 0:
# if volume * (1 + 0.01 * self.commission_percentage) > self.cash_: # if volume * (1 + 0.01 * self.commission_percentage) > self.cash_:
# return None # return None
commission = volume * 0.01 * self.commission_percentage + abs(amount) * self.commission_fixed
pos = Position(ticker) pos = Position(ticker)
pos.enter(price, amount, bar=bar_index, timestamp=self.timestamp) pos.enter(price, amount, bar=bar_index, timestamp=self.timestamp, commission=commission)
self.cash_ -= price * amount self.cash_ -= price * amount
self.cash_ -= volume * 0.01 * self.commission_percentage self.cash_ -= commission
self.positions.append(pos) self.positions.append(pos)
return pos return pos
def close_position(self, pos, price, bar_index): def close_position(self, pos, price, bar_index):
volume = abs(price * pos.size()) volume = abs(price * pos.size())
size = pos.size() size = pos.size()
pos.exit(price, bar=bar_index, timestamp=self.timestamp) commission = volume * 0.01 * self.commission_percentage + abs(size) * self.commission_fixed
pos.exit(price, bar=bar_index, timestamp=self.timestamp, commission=commission)
self.retired_positions_.append(pos) self.retired_positions_.append(pos)
self.positions.remove(pos) self.positions.remove(pos)
self.cash_ += price * size self.cash_ += price * size
self.cash_ -= volume * 0.01 * self.commission_percentage self.cash_ -= commission
return True return True
def set_commission(self, percentage): def set_commission(self, percentage, fixed):
self.commission_percentage = percentage self.commission_percentage = percentage
self.commission_fixed = fixed
def last_position(self): def last_position(self):
return self.positions[-1] return self.positions[-1]

31
src/naiback/broker/position.py

@ -30,7 +30,10 @@ class Position:
return self.original_size_ < 0 return self.original_size_ < 0
def entry_commission(self): def entry_commission(self):
return self.entry_metadata['commission'] try:
return self.entry_metadata['commission']
except KeyError:
return 0
def entry_bar(self): def entry_bar(self):
return self.entry_metadata['bar'] return self.entry_metadata['bar']
@ -44,17 +47,35 @@ class Position:
def exit_time(self): def exit_time(self):
return self.exit_metadata['timestamp'] return self.exit_metadata['timestamp']
def exit_commission(self):
try:
return self.exit_metadata['commission']
except KeyError:
return 0
def bars_in_trade(self): def bars_in_trade(self):
return self.exit_bar() - self.entry_bar() return self.exit_bar() - self.entry_bar()
def total_commission(self):
commission = 0
try:
commission += self.entry_commission()
except KeyError:
pass
try:
commission += self.exit_commission()
except KeyError:
pass
return commission
def pnl(self): def pnl(self):
return self.total_pnl return self.total_pnl - self.total_commission()
def profit_percentage(self): def profit_percentage(self):
if self.is_long(): if self.is_long():
return (self.exit_price() / self.entry_price() - 1) * 100. return (self.exit_price() / self.entry_price() - 1 - (self.total_commission()) / self.entry_price()) * 100.
else: else:
return -(self.exit_price() / self.entry_price() - 1) * 100. return (1 - self.exit_price() / self.entry_price() - (self.total_commission()) / self.entry_price()) * 100.
def enter(self, price, amount, **kwargs): def enter(self, price, amount, **kwargs):
self.entry_price_ = price self.entry_price_ = price
@ -66,7 +87,7 @@ class Position:
def exit(self, price, **kwargs): def exit(self, price, **kwargs):
self.exit_price_ = price self.exit_price_ = price
self.total_pnl += (self.exit_price() - self.entry_price()) * self.size() self.total_pnl += (self.exit_price() - self.entry_price()) * self.size() - self.total_commission()
self.size_ = 0 self.size_ = 0
for k, v in kwargs.items(): for k, v in kwargs.items():

35
src/naiback/data/feeds/yahoofeed.py

@ -0,0 +1,35 @@
from naiback.data.feed import Feed, Bar
import csv
import datetime
import itertools
class YahooCSVFeed(Feed):
def __init__(self, fp):
self.bars = []
self.ticker_ = None
reader = csv.reader(fp, delimiter=',')
next(reader)
for row in reader:
try:
self.ticker_ = row[0]
open_ = float(row[1])
high = float(row[2])
low = float(row[3])
close = float(row[4])
volume = int(row[6])
date = row[0]
dt = datetime.datetime.strptime(date, "%Y-%m-%d")
self.bars.append(Bar(open_, high, low, close, volume, dt))
except IndexError:
pass
def type(self):
return 'bars'
def items(self):
return self.bars
def ticker(self):
return self.ticker_

4
src/naiback/indicators/__init__.py

@ -5,5 +5,7 @@ from .rsi import RSI
from .intradaybarnumber import IntradayBarNumber from .intradaybarnumber import IntradayBarNumber
from .highest import Highest,HighestValue from .highest import Highest,HighestValue
from .lowest import Lowest,LowestValue from .lowest import Lowest,LowestValue
from .atr import ATR from .atr import ATR, ATR_minus, ATR_plus
from .bollinger import BollingerBands from .bollinger import BollingerBands
from .adx import ADX
from .smma import SMMA

33
src/naiback/indicators/adx.py

@ -0,0 +1,33 @@
import numpy as np
from .atr import ATR
from .ema import EMA
from .smma import SMMA
def ADX(bars, period):
dm_minus = np.zeros(len(bars.close))
dm_plus = np.zeros(len(bars.close))
adx = np.zeros(len(bars.close))
atr = ATR(bars, period)
if len(bars.close) == 0:
return np.array([])
for i in range(1, len(bars.close)):
plus = bars.high[i] - bars.high[i - 1]
minus = -bars.low[i] + bars.low[i - 1]
if plus > 0 and plus > minus:
dm_plus[i] = plus
if minus > 0 and minus > plus:
dm_minus[i] = minus
di_plus = np.asarray(SMMA(dm_plus, period)) * 100 / atr
di_minus = np.asarray(SMMA(dm_minus, period)) * 100 / atr
preprocessed_adx = np.abs(np.nan_to_num(np.divide(di_plus - di_minus, di_plus + di_minus)))
adx = 100 * np.asarray(SMMA(preprocessed_adx, period))
return (adx, di_plus, di_minus)

24
src/naiback/indicators/atr.py

@ -18,3 +18,27 @@ def ATR(bars, period):
for i in range(period, len(bars.close)): for i in range(period, len(bars.close)):
atr[i] = (atr[i - 1] * (period - 1) + tr[i]) / period atr[i] = (atr[i - 1] * (period - 1) + tr[i]) / period
return atr return atr
def ATR_filtered(bars, period, f):
tr = np.zeros(len(bars.close))
if len(bars.close) == 0:
return np.array([])
tr[0] = bars.high[0] - bars.low[0]
for i in range(1, len(bars.close)):
tr[i] = max(bars.high[i] - bars.low[i], abs(bars.high[i] - bars.close[i - 1]), abs(bars.low[i] - bars.close[i - 1]))
atr = np.zeros(len(bars.close))
if len(bars.close) <= period:
return atr
atr[period - 1] = sum(filter(f, tr[0:period])) / period
for i in range(period, len(bars.close)):
atr[i] = (atr[i - 1] * (period - 1) + tr[i]) / period
return atr
def ATR_plus(bars, period):
return ATR_filtered(bars, period, lambda x : x > 0)
def ATR_minus(bars, period):
return ATR_filtered(bars, period, lambda x : x < 0)

3
src/naiback/indicators/bollinger.py

@ -9,7 +9,8 @@ def BollingerBands(values, period, stddevs):
ma = SMA(values, period) ma = SMA(values, period)
diffs = ma - np.array(values) diffs = ma - np.array(values)
for i in range(period, len(values)): for i in range(period, len(values)):
sigma = np.std(diffs[i-period+1:i+1]) #sigma = np.std(diffs[i-period+1:i+1])
sigma = np.std(values[i-period+1:i+1])
lower[i] = ma[i] - stddevs * sigma lower[i] = ma[i] - stddevs * sigma
higher[i] = ma[i] + stddevs * sigma higher[i] = ma[i] + stddevs * sigma

2
src/naiback/indicators/ema.py

@ -6,7 +6,7 @@ def EMA(data, period, alpha=None):
if alpha is None: if alpha is None:
alpha = 2. / (period + 1) alpha = 2. / (period + 1)
result = [] result = []
v = None v = 0
for d in data: for d in data:
if d is None: if d is None:
result.append(None) result.append(None)

13
src/naiback/indicators/smma.py

@ -0,0 +1,13 @@
import numpy as np
def SMMA(values, period):
smma = np.zeros(len(values))
alpha = (period - 1) / period
smma[0] = values[0]
for i in range(1, len(values)):
smma[i] = smma[i - 1] * alpha + values[i] * (1 - alpha)
return smma
Loading…
Cancel
Save