Browse Source

Work (yes)

master
Denis Tereshkin 4 years ago
parent
commit
1673e6f693
  1. 2
      cuda/__init__.py
  2. 72
      cuda/signalcalc.py
  3. 228
      data/signal.py
  4. 64
      execution/executor.py
  5. 110
      solver/cudasolver.py
  6. 42
      solver/solver.py
  7. 28
      ui/mainwindow.py
  8. 34
      ui/mainwindow.ui
  9. 18
      ui/ui_mainwindow.py

2
cuda/__init__.py

@ -0,0 +1,2 @@
'''
'''

72
cuda/signalcalc.py

@ -0,0 +1,72 @@
'''
'''
import pycuda.driver as cuda
import pycuda.autoinit
from pycuda.compiler import SourceModule
class SignalCalculator():
'''
'''
def __init__(self):
'''
'''
self.generators = []
def add_signal_generator(self, gen):
self.generators.append(gen)
def make_cuda_kernel(self):
src = """
#include <curand_kernel.h>
const int nstates = %(NGENERATORS)s;
__device__ curandState_t* states[nstates];
__global__ void initkernel(int seed)
{
int tidx = threadIdx.x + blockIdx.x * blockDim.x;
if (tidx < nstates) {
curandState_t* s = new curandState_t;
if (s != 0) {
curand_init(seed, tidx, 0, s);
}
states[tidx] = s;
}
}
__global__ void calc_signal(float* open, float* high, float* low, float* close, size_t points, int8_t* signals, char* sig_names, float* scratch)
{
int tidx = threadIdx.x + blockIdx.x * blockDim.x;
curandState_t s = *states[tidx];
int signals = 5 * curand_uniform(&s);
float* tScratch = &scratch[tidx * points];
int8_t* tSignals = &signals[tidx * points];
char* tSigName = &signames[tidx * 256];
for(int i = 0; i < signals; i++)
{
int sigtype = curand_uniform(&s);
switch(sigtype)
{
"""
i = 0
for gen in self.generators:
src += gen.make_signal_kernel(i)
i += 1
src += """
}
}
}
"""

228
data/signal.py

@ -5,7 +5,6 @@ import random
import numpy import numpy
import talib import talib
from scipy.integrate.tests.test_odeint_jac import rhs
class Signal: class Signal:
@ -21,6 +20,9 @@ class Signal:
def mutate(self, factor): def mutate(self, factor):
return copy.deepcopy(self) return copy.deepcopy(self)
def cuda_kernel(self):
pass
class PriceComparisonSignalGenerator: class PriceComparisonSignalGenerator:
def __init__(self): def __init__(self):
@ -36,6 +38,54 @@ class PriceComparisonSignalGenerator:
def id(self): def id(self):
return 'Price' return 'Price'
def make_signal_kernel(self, ix):
return ("""
case %(IX):
{
int lhs = curand_uniform(&s) * 4;
int rhs = curand_uniform(&s) * 4;
int lhs_shift = curand_uniform(&s) * 10;
int rhs_shift = curand_uniform(&s) * 10;
float* lx = open;
if(lhs == 1)
{
lx = high;
}
else if(lhs == 2)
{
lx = low;
}
else if(lhs == 3)
{
lx = close;
}
float* rx = open;
if(rhs == 1)
{
rx = high;
}
else if(rhs == 2)
{
rx = low;
}
else if(rhs == 3)
{
rx = close;
}
for(int x = 0; x < points; x++)
{
if((x < lhs_shift) || (x < rhs_shift))
{
tSignals[x] = 0;
}
else
{
tSignals[x] &= (lx[x - lhs_shift] < rx[x - rhs_shift]);
}
}
}
""")
class PriceComparisonSignal(Signal): class PriceComparisonSignal(Signal):
OPEN = 0 OPEN = 0
@ -50,9 +100,9 @@ class PriceComparisonSignal(Signal):
self.rhs_shift = rhs_shift self.rhs_shift = rhs_shift
def calculate(self, series): def calculate(self, series):
result = [] result = numpy.zeros(series.length())
for i in range(0, series.length()): for i in range(0, series.length()):
result.append(self.calculate_at_index(series, i)) result[i] = self.calculate_at_index(series, i)
return result return result
@ -111,8 +161,6 @@ class PriceComparisonSignal(Signal):
elif mutation_type == 3: elif mutation_type == 3:
self.rhs_shift = max(0, self.rhs_shift + random.randint(-int(10 * factor + 1), int(10 * factor + 1))) self.rhs_shift = max(0, self.rhs_shift + random.randint(-int(10 * factor + 1), int(10 * factor + 1)))
class RsiSignalGenerator: class RsiSignalGenerator:
def __init__(self): def __init__(self):
@ -148,11 +196,13 @@ class RsiSignal(Signal):
rsi = talib.RSI(closes, self.period) rsi = talib.RSI(closes, self.period)
result = [self.calc_signal(v) for v in rsi] result = numpy.zeros(len(rsi))
if self.shift == 0: pos = 0
return result for i in range(self.shift, len(result)):
else: result[i] = self.calc_signal(rsi[pos])
return [False] * self.shift + result[:-self.shift] pos += 1
return result
def calc_signal(self, value): def calc_signal(self, value):
if self.inequality_type == RsiSignal.LT: if self.inequality_type == RsiSignal.LT:
@ -205,9 +255,11 @@ class AtrSignal(Signal):
highs = numpy.array([series.get_high(i) for i in range(0, series.length())]) highs = numpy.array([series.get_high(i) for i in range(0, series.length())])
lows = numpy.array([series.get_low(i) for i in range(0, series.length())]) lows = numpy.array([series.get_low(i) for i in range(0, series.length())])
atr = talib.ATR(highs, lows, closes, self.period) atr = numpy.nan_to_num(talib.ATR(highs, lows, closes, self.period))
result = [self.calc_signal(v, c) for (v, c) in zip(atr, closes)] result = numpy.zeros(len(atr))
for i in range(0, len(atr)):
result[i] = self.calc_signal(atr[i], closes[i])
return result return result
def calc_signal(self, value, close): def calc_signal(self, value, close):
@ -271,13 +323,12 @@ class AtrDeltaSignal(Signal):
highs = numpy.array([series.get_high(i) for i in range(0, series.length())]) highs = numpy.array([series.get_high(i) for i in range(0, series.length())])
lows = numpy.array([series.get_low(i) for i in range(0, series.length())]) lows = numpy.array([series.get_low(i) for i in range(0, series.length())])
atr = talib.ATR(highs, lows, closes, self.period) atr = numpy.nan_to_num(talib.ATR(highs, lows, closes, self.period))
result = [False] result = numpy.zeros(len(atr))
for i in range(1, len(closes)): for i in range(1, len(closes)):
result.append(self.calc_signal(atr[i], closes[i], closes[i - 1])) result[i] = self.calc_signal(atr[i], closes[i], closes[i - 1])
return result return result
def calc_signal(self, value, c1, c2): def calc_signal(self, value, c1, c2):
@ -324,9 +375,9 @@ class DayOfWeekSignal(Signal):
self.day_of_week = day_of_week self.day_of_week = day_of_week
def calculate(self, series): def calculate(self, series):
result = [] result = numpy.zeros(series.length())
for i in range(0, series.length()): for i in range(0, series.length()):
result.append(series.get_dt(i).date().weekday() == self.day_of_week) result[i] = series.get_dt(i).date().weekday() == self.day_of_week
return result return result
@ -367,12 +418,12 @@ class DayOfMonthSignal(Signal):
self.inequality_sign_str = '>' self.inequality_sign_str = '>'
def calculate(self, series): def calculate(self, series):
result = [] result = numpy.zeros(series.length())
for i in range(0, series.length()): for i in range(0, series.length()):
if self.inequality_type == DayOfMonthSignal.LT: if self.inequality_type == DayOfMonthSignal.LT:
result.append(series.get_dt(i).date().day < self.day_of_month) result[i] = series.get_dt(i).date().day < self.day_of_month
else: else:
result.append(series.get_dt(i).date().day > self.day_of_month) result[i] = series.get_dt(i).date().day > self.day_of_month
return result return result
@ -412,7 +463,7 @@ class CrtdrSignal(Signal):
self.inequality_sign_str = '>' self.inequality_sign_str = '>'
def calculate(self, series): def calculate(self, series):
result = [] result = numpy.zeros(series.length())
for i in range(0, series.length()): for i in range(0, series.length()):
try: try:
h = series.get_high(i - self.shift) h = series.get_high(i - self.shift)
@ -421,13 +472,13 @@ class CrtdrSignal(Signal):
if h > l: if h > l:
if self.inequality_type == CrtdrSignal.LT: if self.inequality_type == CrtdrSignal.LT:
result.append((c - l) / (h - l) < self.threshold) result[i] = (c - l) / (h - l) < self.threshold
else: else:
result.append((c - l) / (h - l) > self.threshold) result[i] = (c - l) / (h - l) > self.threshold
else: else:
result.append(False) result[i] = False
except IndexError: except IndexError:
result.append(False) result[i] = False
return result return result
@ -498,9 +549,11 @@ class SmaSignal(Signal):
elif self.rhs == SmaSignal.CLOSE: elif self.rhs == SmaSignal.CLOSE:
rhs = numpy.array([series.get_close(i) for i in range(0, series.length())]) rhs = numpy.array([series.get_close(i) for i in range(0, series.length())])
rhs_sma = talib.SMA(rhs, self.period) rhs_sma = numpy.nan_to_num(talib.SMA(rhs, self.period))
result = [self.calc_signal(l, r) for (l, r) in zip(lhs, rhs_sma)] result = numpy.zeros(series.length())
for i in range(0, len(result)):
result[i] = self.calc_signal(lhs[i], rhs_sma[i])
return result return result
def calc_signal(self, l, r): def calc_signal(self, l, r):
@ -560,14 +613,15 @@ class CciSignal(Signal):
highs = numpy.array([series.get_high(i) for i in range(0, series.length())]) highs = numpy.array([series.get_high(i) for i in range(0, series.length())])
lows = numpy.array([series.get_low(i) for i in range(0, series.length())]) lows = numpy.array([series.get_low(i) for i in range(0, series.length())])
cci = talib.CCI(highs, lows, closes, self.period) cci = numpy.nan_to_num(talib.CCI(highs, lows, closes, self.period))
result = [self.calc_signal(v) for v in cci] result = numpy.zeros(len(cci))
pos = 0
for i in range(self.shift, len(result)):
result[i] = cci[pos]
pos += 1
if self.shift == 0: return result
return result
else:
return [False] * self.shift + result[:-self.shift]
def calc_signal(self, value): def calc_signal(self, value):
if self.inequality_type == CciSignal.LT: if self.inequality_type == CciSignal.LT:
@ -655,16 +709,16 @@ class BbandsSignal(Signal):
elif self.rhs == BbandsSignal.CLOSE: elif self.rhs == BbandsSignal.CLOSE:
rhs = numpy.array([series.get_close(i) for i in range(0, series.length())]) rhs = numpy.array([series.get_close(i) for i in range(0, series.length())])
(upband, _, downband) = talib.BBANDS(rhs, self.period, self.dev, self.dev) (downband, _, upband) = talib.BBANDS(rhs, self.period, self.dev, self.dev)
if self.band_type == BbandsSignal.UP: if self.band_type == BbandsSignal.UP:
band = upband band = numpy.nan_to_num(upband)
else: else:
band = downband band = numpy.nan_to_num(downband)
result = [self.calc_signal(l, r) for (l, r) in zip(lhs, band)] result = numpy.zeros(len(band))
for i in range(0, len(result)):
result[i] = self.calc_signal(lhs[i], band[i])
return result return result
def calc_signal(self, l, r): def calc_signal(self, l, r):
@ -727,14 +781,12 @@ class PivotPointsSignal(Signal):
self.rhs_shift = rhs_shift self.rhs_shift = rhs_shift
def calculate(self, series): def calculate(self, series):
result = [] result = numpy.zeros(series.length())
for i in range(0, series.length()): for i in range(0, series.length()):
lhs = self.calculate_signal(series, self.lhs, i - self.lhs_shift) lhs = self.calculate_signal(series, self.lhs, i - self.lhs_shift)
rhs = self.calculate_signal(series, self.rhs, i - self.rhs_shift) rhs = self.calculate_signal(series, self.rhs, i - self.rhs_shift)
if i - self.lhs_shift >= 0 and i - self.rhs_shift >= 0: if i - self.lhs_shift >= 0 and i - self.rhs_shift >= 0:
result.append(lhs < rhs) result[i] = lhs < rhs
else:
result.append(False)
return result return result
@ -836,11 +888,17 @@ class StochasticSignal(Signal):
stoch_k, stoch_d = talib.STOCHF(highs, lows, closes, self.period, self.period2) stoch_k, stoch_d = talib.STOCHF(highs, lows, closes, self.period, self.period2)
result = [self.calc_signal(v) for v in stoch_k] stoch_k = numpy.nan_to_num(stoch_k)
if self.shift == 0:
return result result = numpy.zeros(len(stoch_k))
else: pos = 0
return [False] * self.shift + result[:-self.shift] for i in range(self.shift, len(result)):
result[i] = self.calc_signal(stoch_k[pos])
pos += 1
return result
def calc_signal(self, value): def calc_signal(self, value):
if self.inequality_type == StochasticSignal.LT: if self.inequality_type == StochasticSignal.LT:
@ -849,7 +907,7 @@ class StochasticSignal(Signal):
return value > self.threshold return value > self.threshold
def get_text(self): def get_text(self):
return "stoch(c, {:d})[{:d}] {:s} {:d}".format(self.period, self.shift, self.inequality_sign_str, int(self.threshold)) return "stoch(c, {:d}, {:d})[{:d}] {:s} {:d}".format(self.period, self.period2, self.shift, self.inequality_sign_str, int(self.threshold))
class IntradayBarNumberSignalGenerator: class IntradayBarNumberSignalGenerator:
@ -882,7 +940,7 @@ class IntradayBarNumberSignal(Signal):
self.inequality_sign_str = '==' self.inequality_sign_str = '=='
def calculate(self, series): def calculate(self, series):
ibn = [] ibn = numpy.zeros(series.length())
cur_date = None cur_date = None
for i in range(0, series.length()): for i in range(0, series.length()):
if series.get_dt(i).date() != cur_date: if series.get_dt(i).date() != cur_date:
@ -890,11 +948,11 @@ class IntradayBarNumberSignal(Signal):
ctr = 0 ctr = 0
else: else:
ctr += 1 ctr += 1
ibn.append(ctr) ibn[i] = ctr
result = [] result = numpy.zeros(len(ibn))
for i in ibn: for i in range(0, len(result)):
result.append(self.calc_signal(i)) result[i] = self.calc_signal(ibn[i])
return result return result
def calc_signal(self, ibn): def calc_signal(self, ibn):
@ -908,3 +966,63 @@ class IntradayBarNumberSignal(Signal):
def get_text(self): def get_text(self):
return "ibn {:s} {:d}".format(self.inequality_sign_str, self.ibn) return "ibn {:s} {:d}".format(self.inequality_sign_str, self.ibn)
class GainPercentrankSignalGenerator:
def __init__(self):
pass
def generate(self):
period = random.randint(2, 120)
threshold = random.choice([1, 5, 10, 20, 25, 50, 75, 80, 90, 95, 99])
ineq_type = random.randint(GainPercentrankSignal.LT, GainPercentrankSignal.GT)
return GainPercentrankSignal(period, threshold, ineq_type)
def id(self):
return 'IBN'
class GainPercentrankSignal(Signal):
LT = 0
GT = 1
def __init__(self, period, threshold, inequality_type):
self.period = period
self.threshold = threshold
self.inequality_type = inequality_type
if inequality_type == GainPercentrankSignal.LT:
self.inequality_sign_str = '<'
elif inequality_type == GainPercentrankSignal.GT:
self.inequality_sign_str = '>'
else:
self.inequality_sign_str = '=='
def calculate(self, series):
gains = numpy.zeros(series.length())
for i in range(0, len(gains)):
gains[i] = series.get_close(i) / series.get_open(i)
result = numpy.zeros(series.length())
for i in range(0, len(result)):
result[i] = self.calc_signal(gains[i-self.period+1:i+1])
return result
def calc_signal(self, gains):
if len(gains) == 0:
return False
current_gain = gains[-1]
less = 1
for g in gains:
if g < current_gain:
less += 1
rank = 100 * (float(less) / len(gains))
if self.inequality_type == GainPercentrankSignal.LT:
return rank < self.threshold
elif self.inequality_type == GainPercentrankSignal.GT:
return rank > self.threshold
def get_text(self):
return "%rank(return, {:d}) {:s} {:d}".format(self.period, self.inequality_sign_str, self.threshold)

64
execution/executor.py

@ -2,6 +2,7 @@
''' '''
from .trade import Trade from .trade import Trade
import numpy
class Executor(object): class Executor(object):
''' '''
@ -15,44 +16,59 @@ class Executor(object):
self.series = series self.series = series
self.max_hold_bars = max_hold_bars self.max_hold_bars = max_hold_bars
def execute(self, signals, long=True): def execute(self, sig_vectors, long=True, stop=None, tp=None):
self.trades = [] self.trades = []
sig_vectors = []
vec_length = 0
for signal in signals:
vec = signal.calculate(self.series)
sig_vectors.append(vec)
if vec_length == 0:
vec_length = len(vec)
else:
assert(vec_length == len(vec))
in_trade = False in_trade = False
current_entry_price = None current_entry_price = None
bar_counter = 0 bar_counter = 0
entry_bar = 0 entry_bar = 0
stop_price = 0
tp_price = 0
for i in range(0, vec_length): for i in range(0, self.series.length()):
if not in_trade: if not in_trade:
has_signal = True has_signal = sig_vectors[i]
for vec in sig_vectors:
if vec[i] is None or vec[i] == False:
has_signal = False
break
if has_signal and i + 1 < vec_length: if has_signal and i + 1 < self.series.length():
in_trade = True in_trade = True
current_entry_price = self.series.get_open(i + 1) current_entry_price = self.series.get_open(i + 1)
if stop is not None:
if long:
stop_price = current_entry_price * (1 - stop)
else:
stop_price = current_entry_price * (1 + stop)
if tp is not None:
if long:
tp_price = current_entry_price * (1 + tp)
else:
tp_price = current_entry_price * (1 - tp)
entry_bar = i + 1 entry_bar = i + 1
bar_counter = 0 bar_counter = 0
else: else:
bar_counter += 1 bar_counter += 1
if bar_counter >= self.max_hold_bars: if long:
in_trade = False if stop is not None and self.series.get_low(i) < stop_price:
if long: self.trades.append(Trade(current_entry_price, stop_price, entry_bar, i, Trade.LONG))
trade_dir = Trade.LONG in_trade = False
else: elif tp is not None and self.series.get_high(i) > tp_price:
trade_dir = Trade.SHORT self.trades.append(Trade(current_entry_price, tp_price, entry_bar, i, Trade.LONG))
self.trades.append(Trade(current_entry_price, self.series.get_close(i), entry_bar, i, trade_dir)) in_trade = False
else:
if stop is not None and self.series.get_high(i) > stop_price:
self.trades.append(Trade(current_entry_price, stop_price, entry_bar, i, Trade.SHORT))
in_trade = False
elif tp is not None and self.series.get_low(i) < tp_price:
self.trades.append(Trade(current_entry_price, tp_price, entry_bar, i, Trade.SHORT))
in_trade = False
if in_trade:
if bar_counter >= self.max_hold_bars:
in_trade = False
if long:
trade_dir = Trade.LONG
else:
trade_dir = Trade.SHORT
self.trades.append(Trade(current_entry_price, self.series.get_close(i), entry_bar, i, trade_dir))
return self.trades return self.trades

110
solver/cudasolver.py

@ -0,0 +1,110 @@
'''
'''
from PyQt5.Qt import pyqtSignal
from PyQt5 import QtCore
import random
class CudaSolver():
'''
'''
progress = pyqtSignal(int, int, name='progress')
done = pyqtSignal(list, name='done')
def __init__(self, calc, series):
'''
Constructor
'''
super().__init__(None)
self.calc = calc
self.series = series
self.generators = []
self.counter = 0
self.total_counter = 0
def add_generator(self, generator):
self.generators.append(generator)
@QtCore.pyqtSlot(dict)
def solve(self, params):
max_signals = 5
max_hold_bars = params.get('max_hold_bars', 1)
#self.executor = Executor(self.series, max_hold_bars)
max_strategies = params.get('num_strategies', 1000)
results = []
min_trades = params.get('min_trades', 0)
min_win_rate = params.get('min_win_rate', 0)
min_sharpe = params.get('min_sharpe', 0)
stop = params.get('stop_loss', None)
tp = params.get('take_profit', None)
is_long = params.get('direction', 'long') == 'long'
while len(results) < max_strategies:
sig_num = random.randint(1, max_signals)
strategy = []
for i in range(0, sig_num):
strategy.append(random.choice(self.generators).generate())
trades = self.executor.execute(strategy, is_long, stop, tp)
if len(trades) >= min_trades:
result = self.evaluate_trades(trades)
if result['win_percentage'] > min_win_rate and result['sharpe'] > min_sharpe:
result['strategy'] = strategy
result['display_name'] = ' && '.join([signal.get_text() for signal in strategy])
result['trades'] = trades
results.append(result)
self.progress.emit(len(results), max_strategies)
self.done.emit([result])
self.counter += 1
self.total_counter += 1
def evaluate_trades(self, trades):
result = {}
profits = [x.pnl() for x in trades]
total_won = len(list(filter(lambda x: x.pnl() > 0, trades)))
if len(trades) > 0:
result['win_percentage'] = total_won / len(trades) * 100
else:
result['win_percentage'] = 0
result['trades_number'] = len(trades)
result['total_pnl'] = sum(profits)
if len(trades) > 0:
result['avg_percentage'] = sum([trade.pnl_percentage() for trade in trades]) / len(trades)
else:
result['avg_percentage'] = 0
gross_profit = sum([max(0, x.pnl()) for x in trades])
gross_loss = sum([min(0, x.pnl()) for x in trades])
if gross_loss != 0:
result['profit_factor'] = gross_profit / (-gross_loss)
else:
result['profit_factor'] = inf
if len(profits) > 0:
mean = numpy.mean(profits)
stddev = numpy.std(profits)
if stddev != 0:
result['sharpe'] = mean / stddev
else:
result['sharpe'] = 0
else:
result['sharpe'] = 0
return result

42
solver/solver.py

@ -9,6 +9,9 @@ import numpy
from PyQt5.Qt import pyqtSignal, QObject from PyQt5.Qt import pyqtSignal, QObject
from PyQt5 import QtCore from PyQt5 import QtCore
import talib import talib
from data.signal import PriceComparisonSignal, DayOfMonthSignal, DayOfWeekSignal,\
StochasticSignal, IntradayBarNumberSignal, BbandsSignal,\
GainPercentrankSignal
class Solver(QObject): class Solver(QObject):
''' '''
@ -26,12 +29,15 @@ class Solver(QObject):
self.series = series self.series = series
self.generators = [] self.generators = []
self.counter = 0
self.total_counter = 0
def add_generator(self, generator): def add_generator(self, generator):
self.generators.append(generator) self.generators.append(generator)
@QtCore.pyqtSlot(dict) @QtCore.pyqtSlot(dict)
def solve(self, params): def solve(self, params):
max_signals = 5 max_signals = 5
max_hold_bars = params.get('max_hold_bars', 1) max_hold_bars = params.get('max_hold_bars', 1)
self.executor = Executor(self.series, max_hold_bars) self.executor = Executor(self.series, max_hold_bars)
@ -42,15 +48,33 @@ class Solver(QObject):
min_win_rate = params.get('min_win_rate', 0) min_win_rate = params.get('min_win_rate', 0)
min_sharpe = params.get('min_sharpe', 0) min_sharpe = params.get('min_sharpe', 0)
stop = params.get('stop_loss', None)
tp = params.get('take_profit', None)
is_long = params.get('direction', 'long') == 'long' is_long = params.get('direction', 'long') == 'long'
print(is_long)
strategy = [GainPercentrankSignal(107, 1, GainPercentrankSignal.LT)]
signals = self.make_signals(strategy)
trades = self.executor.execute(signals, is_long, stop, tp)
result = self.evaluate_trades(trades)
result['strategy'] = strategy
result['display_name'] = ' && '.join([signal.get_text() for signal in strategy])
result['trades'] = trades
results.append(result)
self.progress.emit(len(results), max_strategies)
self.done.emit([result])
while len(results) < max_strategies: while len(results) < max_strategies:
sig_num = random.randint(1, max_signals) sig_num = random.randint(1, max_signals)
strategy = [] strategy = []
for i in range(0, sig_num): for i in range(0, sig_num):
strategy.append(random.choice(self.generators).generate()) strategy.append(random.choice(self.generators).generate())
trades = self.executor.execute(strategy, is_long) signals = self.make_signals(strategy)
trades = self.executor.execute(signals, is_long, stop, tp)
if len(trades) >= min_trades: if len(trades) >= min_trades:
result = self.evaluate_trades(trades) result = self.evaluate_trades(trades)
if result['win_percentage'] > min_win_rate and result['sharpe'] > min_sharpe: if result['win_percentage'] > min_win_rate and result['sharpe'] > min_sharpe:
@ -60,7 +84,19 @@ class Solver(QObject):
results.append(result) results.append(result)
self.progress.emit(len(results), max_strategies) self.progress.emit(len(results), max_strategies)
self.done.emit(results) self.done.emit([result])
self.counter += 1
self.total_counter += 1
def make_signals(self, strategy):
sig_vectors = numpy.ones(self.series.length(), dtype=int)
for signal in strategy:
vec = signal.calculate(self.series)
assert(len(vec) == self.series.length())
sig_vectors = numpy.logical_and(sig_vectors, vec)
return sig_vectors
def evaluate_trades(self, trades): def evaluate_trades(self, trades):
result = {} result = {}

28
ui/mainwindow.py

@ -10,11 +10,14 @@ from data.signal import PriceComparisonSignalGenerator, RsiSignalGenerator,\
AtrSignalGenerator, DayOfWeekSignalGenerator, CrtdrSignalGenerator,\ AtrSignalGenerator, DayOfWeekSignalGenerator, CrtdrSignalGenerator,\
AtrDeltaSignalGenerator, SmaSignalGenerator, DayOfMonthSignalGenerator,\ AtrDeltaSignalGenerator, SmaSignalGenerator, DayOfMonthSignalGenerator,\
CciSignalGenerator, BbandsSignalGenerator, PivotPointsSignalGenerator,\ CciSignalGenerator, BbandsSignalGenerator, PivotPointsSignalGenerator,\
StochasticSignalGenerator, IntradayBarNumberSignalGenerator StochasticSignalGenerator, IntradayBarNumberSignalGenerator,\
GainPercentrankSignalGenerator
from PyQt5.Qt import Qt, QFileDialog, QThread, Q_ARG, QMetaObject from PyQt5.Qt import Qt, QFileDialog, QThread, Q_ARG, QMetaObject
from PyQt5 import QtCore
import pyqtgraph import pyqtgraph
import numpy import numpy
from backtrader import strategies
class MainWindow(QMainWindow, Ui_MainWindow): class MainWindow(QMainWindow, Ui_MainWindow):
''' '''
@ -29,6 +32,10 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.work_thread = QThread() self.work_thread = QThread()
self.work_thread.start() self.work_thread.start()
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.speed)
def browse(self): def browse(self):
fname = QFileDialog.getOpenFileName(self, 'Open file') fname = QFileDialog.getOpenFileName(self, 'Open file')
if fname[0] != '': if fname[0] != '':
@ -51,7 +58,10 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.solver.add_generator(BbandsSignalGenerator()) self.solver.add_generator(BbandsSignalGenerator())
self.solver.add_generator(PivotPointsSignalGenerator()) self.solver.add_generator(PivotPointsSignalGenerator())
self.solver.add_generator(StochasticSignalGenerator()) self.solver.add_generator(StochasticSignalGenerator())
self.solver.add_generator(IntradayBarNumberSignalGenerator(14)) self.solver.add_generator(IntradayBarNumberSignalGenerator(60))
self.solver.add_generator(GainPercentrankSignalGenerator())
self.timer.start(10000.0)
params = { 'num_strategies' : self.sb_strategiesNum.value(), params = { 'num_strategies' : self.sb_strategiesNum.value(),
'max_hold_bars' : self.sb_maxHoldBars.value() } 'max_hold_bars' : self.sb_maxHoldBars.value() }
@ -68,6 +78,11 @@ class MainWindow(QMainWindow, Ui_MainWindow):
else: else:
params['direction'] = 'short' params['direction'] = 'short'
if self.cb_stopLoss.isChecked():
params['stop_loss'] = self.sb_stopLoss.value() / 100.;
if self.cb_takeProfit.isChecked():
params['take_profit'] = self.sb_takeProfit.value() / 100.;
self.solver.done.connect(self.done) self.solver.done.connect(self.done)
self.solver.progress.connect(self.progress) self.solver.progress.connect(self.progress)
@ -96,6 +111,11 @@ class MainWindow(QMainWindow, Ui_MainWindow):
else: else:
self.pb_progress.setValue(100) self.pb_progress.setValue(100)
def speed(self):
print(self.solver.counter)
self.statusbar.showMessage("{:.2f} sps, {:d} total ({:.2f}% yield)".format(self.solver.counter / 10., self.solver.total_counter, 100. * float(self.tw_strategies.topLevelItemCount()) / self.solver.total_counter))
self.solver.counter = 0
def strategyClicked(self, item, column): def strategyClicked(self, item, column):
result = item.data(0, Qt.UserRole + 1) result = item.data(0, Qt.UserRole + 1)
@ -103,6 +123,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
xs = [trade.entry_bar for trade in result['trades']] xs = [trade.entry_bar for trade in result['trades']]
pyqtgraph.plot(xs, pnl) pyqtgraph.plot(xs, pnl)
for trade in result['trades']: #for trade in result['trades']:
print(trade.entry_bar, trade.entry_price, trade.exit_price) # print(trade.entry_bar, trade.entry_price, trade.exit_price)

34
ui/mainwindow.ui

@ -211,6 +211,40 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="4">
<widget class="QCheckBox" name="cb_takeProfit">
<property name="text">
<string>Take profit</string>
</property>
</widget>
</item>
<item row="2" column="5">
<widget class="QDoubleSpinBox" name="sb_takeProfit">
<property name="suffix">
<string> %</string>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item row="3" column="4">
<widget class="QCheckBox" name="cb_stopLoss">
<property name="text">
<string>Stop loss</string>
</property>
</widget>
</item>
<item row="3" column="5">
<widget class="QDoubleSpinBox" name="sb_stopLoss">
<property name="suffix">
<string> %</string>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QMenuBar" name="menubar"> <widget class="QMenuBar" name="menubar">

18
ui/ui_mainwindow.py

@ -84,6 +84,20 @@ class Ui_MainWindow(object):
self.sb_maxHoldBars.setMinimum(1) self.sb_maxHoldBars.setMinimum(1)
self.sb_maxHoldBars.setObjectName("sb_maxHoldBars") self.sb_maxHoldBars.setObjectName("sb_maxHoldBars")
self.gridLayout.addWidget(self.sb_maxHoldBars, 2, 3, 1, 1) self.gridLayout.addWidget(self.sb_maxHoldBars, 2, 3, 1, 1)
self.cb_takeProfit = QtWidgets.QCheckBox(self.centralwidget)
self.cb_takeProfit.setObjectName("cb_takeProfit")
self.gridLayout.addWidget(self.cb_takeProfit, 2, 4, 1, 1)
self.sb_takeProfit = QtWidgets.QDoubleSpinBox(self.centralwidget)
self.sb_takeProfit.setProperty("value", 1.0)
self.sb_takeProfit.setObjectName("sb_takeProfit")
self.gridLayout.addWidget(self.sb_takeProfit, 2, 5, 1, 1)
self.cb_stopLoss = QtWidgets.QCheckBox(self.centralwidget)
self.cb_stopLoss.setObjectName("cb_stopLoss")
self.gridLayout.addWidget(self.cb_stopLoss, 3, 4, 1, 1)
self.sb_stopLoss = QtWidgets.QDoubleSpinBox(self.centralwidget)
self.sb_stopLoss.setProperty("value", 1.0)
self.sb_stopLoss.setObjectName("sb_stopLoss")
self.gridLayout.addWidget(self.sb_stopLoss, 3, 5, 1, 1)
MainWindow.setCentralWidget(self.centralwidget) MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1041, 27)) self.menubar.setGeometry(QtCore.QRect(0, 0, 1041, 27))
@ -120,4 +134,8 @@ class Ui_MainWindow(object):
self.cb_minWinRate.setText(_translate("MainWindow", "Min. win rate")) self.cb_minWinRate.setText(_translate("MainWindow", "Min. win rate"))
self.rb_short.setText(_translate("MainWindow", "Short")) self.rb_short.setText(_translate("MainWindow", "Short"))
self.label_3.setText(_translate("MainWindow", "Max hold bars")) self.label_3.setText(_translate("MainWindow", "Max hold bars"))
self.cb_takeProfit.setText(_translate("MainWindow", "Take profit"))
self.sb_takeProfit.setSuffix(_translate("MainWindow", " %"))
self.cb_stopLoss.setText(_translate("MainWindow", "Stop loss"))
self.sb_stopLoss.setSuffix(_translate("MainWindow", " %"))

Loading…
Cancel
Save