commit
8969a3d9ea
14 changed files with 613 additions and 0 deletions
@ -0,0 +1,53 @@ |
|||||||
|
''' |
||||||
|
''' |
||||||
|
import csv |
||||||
|
import datetime |
||||||
|
|
||||||
|
class Series: |
||||||
|
|
||||||
|
def __init__(self): |
||||||
|
self.data = [] |
||||||
|
|
||||||
|
def load_from_finam_csv(self, filepath): |
||||||
|
with open(filepath) as fp: |
||||||
|
self.ticker_ = None |
||||||
|
reader = csv.reader(fp, delimiter=',') |
||||||
|
next(reader) |
||||||
|
for row in reader: |
||||||
|
try: |
||||||
|
self.ticker_ = row[0] |
||||||
|
open_ = float(row[4]) |
||||||
|
high = float(row[5]) |
||||||
|
low = float(row[6]) |
||||||
|
close = float(row[7]) |
||||||
|
volume = int(row[8]) |
||||||
|
date = row[2] |
||||||
|
time = row[3] |
||||||
|
dt = datetime.datetime.strptime(date + "_" + time, "%Y%m%d_%H%M%S") |
||||||
|
self.append_bar(dt, open_, high, low, close, volume) |
||||||
|
except IndexError: |
||||||
|
pass |
||||||
|
|
||||||
|
def append_bar(self, dt, open_, high, low, close, volume): |
||||||
|
self.data.append((dt, open_, high, low, close, volume)) |
||||||
|
|
||||||
|
def get_dt(self, index): |
||||||
|
return self.data[index][0] |
||||||
|
|
||||||
|
def get_open(self, index): |
||||||
|
return self.data[index][1] |
||||||
|
|
||||||
|
def get_high(self, index): |
||||||
|
return self.data[index][2] |
||||||
|
|
||||||
|
def get_low(self, index): |
||||||
|
return self.data[index][3] |
||||||
|
|
||||||
|
def get_close(self, index): |
||||||
|
return self.data[index][4] |
||||||
|
|
||||||
|
def get_volume(self, index): |
||||||
|
return self.data[index][5] |
||||||
|
|
||||||
|
def length(self): |
||||||
|
return len(self.data) |
||||||
@ -0,0 +1,186 @@ |
|||||||
|
''' |
||||||
|
''' |
||||||
|
import random |
||||||
|
|
||||||
|
import talib |
||||||
|
import numpy |
||||||
|
|
||||||
|
class Signal: |
||||||
|
|
||||||
|
def __init__(self): |
||||||
|
pass |
||||||
|
|
||||||
|
def calculate(self, series): |
||||||
|
pass |
||||||
|
|
||||||
|
def get_text(self): |
||||||
|
pass |
||||||
|
|
||||||
|
class PriceComparisonSignalGenerator: |
||||||
|
|
||||||
|
def __init__(self): |
||||||
|
pass |
||||||
|
|
||||||
|
def generate(self): |
||||||
|
lhs = random.randint(PriceComparisonSignal.OPEN, PriceComparisonSignal.CLOSE) |
||||||
|
lhs_shift = random.randint(0, 10) |
||||||
|
rhs = random.randint(PriceComparisonSignal.OPEN, PriceComparisonSignal.CLOSE) |
||||||
|
rhs_shift = random.randint(0, 10) |
||||||
|
return PriceComparisonSignal(lhs, lhs_shift, rhs, rhs_shift) |
||||||
|
|
||||||
|
class PriceComparisonSignal(Signal): |
||||||
|
|
||||||
|
OPEN = 0 |
||||||
|
HIGH = 1 |
||||||
|
LOW = 2 |
||||||
|
CLOSE = 3 |
||||||
|
|
||||||
|
def __init__(self, lhs, lhs_shift, rhs, rhs_shift): |
||||||
|
self.lhs = lhs |
||||||
|
self.lhs_shift = lhs_shift |
||||||
|
self.rhs = rhs |
||||||
|
self.rhs_shift = rhs_shift |
||||||
|
|
||||||
|
def calculate(self, series): |
||||||
|
result = [] |
||||||
|
for i in range(0, series.length()): |
||||||
|
result.append(self.calculate_at_index(series, i)) |
||||||
|
|
||||||
|
return result |
||||||
|
|
||||||
|
def calculate_at_index(self, series, index): |
||||||
|
try: |
||||||
|
if self.lhs == PriceComparisonSignal.OPEN: |
||||||
|
lhs = series.get_open(index - self.lhs_shift) |
||||||
|
elif self.lhs == PriceComparisonSignal.HIGH: |
||||||
|
lhs = series.get_high(index - self.lhs_shift) |
||||||
|
elif self.lhs == PriceComparisonSignal.LOW: |
||||||
|
lhs = series.get_low(index - self.lhs_shift) |
||||||
|
elif self.lhs == PriceComparisonSignal.CLOSE: |
||||||
|
lhs = series.get_close(index - self.lhs_shift) |
||||||
|
else: |
||||||
|
raise Exception('Invalid lhs type') |
||||||
|
|
||||||
|
if self.rhs == PriceComparisonSignal.OPEN: |
||||||
|
rhs = series.get_open(index - self.rhs_shift) |
||||||
|
elif self.rhs == PriceComparisonSignal.HIGH: |
||||||
|
rhs = series.get_high(index - self.rhs_shift) |
||||||
|
elif self.rhs == PriceComparisonSignal.LOW: |
||||||
|
rhs = series.get_low(index - self.rhs_shift) |
||||||
|
elif self.rhs == PriceComparisonSignal.CLOSE: |
||||||
|
rhs = series.get_close(index - self.rhs_shift) |
||||||
|
else: |
||||||
|
raise Exception('Invalid lhs type') |
||||||
|
|
||||||
|
return lhs < rhs |
||||||
|
|
||||||
|
except IndexError: |
||||||
|
return False |
||||||
|
|
||||||
|
def get_text(self): |
||||||
|
return self.component_to_str(self.lhs) + '[' + str(self.lhs_shift) + '] < ' + self.component_to_str(self.rhs) + '[' + str(self.rhs_shift) + ']' |
||||||
|
|
||||||
|
def component_to_str(self, component): |
||||||
|
if component == PriceComparisonSignal.OPEN: |
||||||
|
return "open" |
||||||
|
elif component == PriceComparisonSignal.HIGH: |
||||||
|
return "high" |
||||||
|
elif component == PriceComparisonSignal.LOW: |
||||||
|
return "low" |
||||||
|
elif component == PriceComparisonSignal.CLOSE: |
||||||
|
return "close" |
||||||
|
else: |
||||||
|
return "??" |
||||||
|
|
||||||
|
|
||||||
|
class RsiSignalGenerator: |
||||||
|
|
||||||
|
def __init__(self): |
||||||
|
pass |
||||||
|
|
||||||
|
def generate(self): |
||||||
|
period = random.randint(2, 30) |
||||||
|
threshold = random.randrange(1, 9) * 10 |
||||||
|
ineq_type = random.randint(RsiSignal.LT, RsiSignal.GT) |
||||||
|
return RsiSignal(period, threshold, ineq_type) |
||||||
|
|
||||||
|
class RsiSignal(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 == RsiSignal.LT: |
||||||
|
self.inequality_sign_str = '<' |
||||||
|
else: |
||||||
|
self.inequality_sign_str = '>' |
||||||
|
|
||||||
|
def calculate(self, series): |
||||||
|
closes = numpy.array([series.get_close(i) for i in range(0, series.length())]) |
||||||
|
|
||||||
|
rsi = talib.RSI(closes, self.period) |
||||||
|
|
||||||
|
result = [self.calc_signal(v) for v in rsi] |
||||||
|
return result |
||||||
|
|
||||||
|
def calc_signal(self, value): |
||||||
|
if self.inequality_type == RsiSignal.LT: |
||||||
|
return value < self.threshold |
||||||
|
else: |
||||||
|
return value > self.threshold |
||||||
|
|
||||||
|
def get_text(self): |
||||||
|
return "rsi(c, " + str(self.period) + ') ' + self.inequality_sign_str + ' ' + str(self.threshold) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class AtrSignalGenerator: |
||||||
|
|
||||||
|
def __init__(self): |
||||||
|
pass |
||||||
|
|
||||||
|
def generate(self): |
||||||
|
period = random.randint(2, 30) |
||||||
|
threshold = random.randint(1, 30) * 0.001 |
||||||
|
ineq_type = random.randint(AtrSignal.LT, AtrSignal.GT) |
||||||
|
return AtrSignal(period, threshold, ineq_type) |
||||||
|
|
||||||
|
class AtrSignal(Signal): |
||||||
|
|
||||||
|
LT = 0 |
||||||
|
GT = 1 |
||||||
|
|
||||||
|
def __init__(self, period, threshold_factor, inequality_type): |
||||||
|
self.period = period |
||||||
|
self.threshold_factor = threshold_factor |
||||||
|
self.inequality_type = inequality_type |
||||||
|
if inequality_type == RsiSignal.LT: |
||||||
|
self.inequality_sign_str = '<' |
||||||
|
else: |
||||||
|
self.inequality_sign_str = '>' |
||||||
|
|
||||||
|
def calculate(self, series): |
||||||
|
closes = numpy.array([series.get_close(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())]) |
||||||
|
|
||||||
|
atr = talib.ATR(highs, lows, closes, self.period) |
||||||
|
|
||||||
|
result = [self.calc_signal(v, c) for (v, c) in zip(atr, closes)] |
||||||
|
return result |
||||||
|
|
||||||
|
def calc_signal(self, value, close): |
||||||
|
if self.inequality_type == AtrSignal.LT: |
||||||
|
return value < self.threshold_factor * close |
||||||
|
else: |
||||||
|
return value > self.threshold_factor * close |
||||||
|
|
||||||
|
def get_text(self): |
||||||
|
return "atr(" + str(self.period) + ') ' + self.inequality_sign_str + ' close[0] * ' + "{:.3f}".format(self.threshold_factor) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,17 @@ |
|||||||
|
''' |
||||||
|
|
||||||
|
''' |
||||||
|
|
||||||
|
import sys |
||||||
|
|
||||||
|
from PyQt5.QtWidgets import QApplication |
||||||
|
|
||||||
|
from ui.mainwindow import MainWindow |
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
app = QApplication(sys.argv) |
||||||
|
|
||||||
|
wnd = MainWindow() |
||||||
|
wnd.show() |
||||||
|
|
||||||
|
app.exec_() |
||||||
@ -0,0 +1,52 @@ |
|||||||
|
''' |
||||||
|
''' |
||||||
|
|
||||||
|
from .trade import Trade |
||||||
|
|
||||||
|
class Executor(object): |
||||||
|
''' |
||||||
|
''' |
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, series): |
||||||
|
''' |
||||||
|
Constructor |
||||||
|
''' |
||||||
|
self.series = series |
||||||
|
self.max_hold_bars = 1 |
||||||
|
|
||||||
|
def execute(self, signals): |
||||||
|
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 |
||||||
|
current_entry_price = None |
||||||
|
bar_counter = 0 |
||||||
|
|
||||||
|
for i in range(0, vec_length): |
||||||
|
if not in_trade: |
||||||
|
has_signal = True |
||||||
|
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: |
||||||
|
in_trade = True |
||||||
|
current_entry_price = self.series.get_open(i + 1) |
||||||
|
bar_counter = 0 |
||||||
|
else: |
||||||
|
bar_counter += 1 |
||||||
|
if bar_counter >= self.max_hold_bars: |
||||||
|
in_trade = False |
||||||
|
self.trades.append(Trade(current_entry_price, self.series.get_close(i))) |
||||||
|
|
||||||
|
return self.trades |
||||||
@ -0,0 +1,31 @@ |
|||||||
|
''' |
||||||
|
''' |
||||||
|
|
||||||
|
class Trade: |
||||||
|
''' |
||||||
|
''' |
||||||
|
|
||||||
|
LONG = 1 |
||||||
|
SHORT = 2 |
||||||
|
|
||||||
|
def __init__(self, entry_price, exit_price, direction = LONG): |
||||||
|
''' |
||||||
|
Constructor |
||||||
|
''' |
||||||
|
|
||||||
|
self.direction = direction |
||||||
|
self.entry_price = entry_price |
||||||
|
self.exit_price = exit_price |
||||||
|
|
||||||
|
|
||||||
|
def pnl(self): |
||||||
|
if self.direction == Trade.LONG: |
||||||
|
return self.exit_price - self.entry_price |
||||||
|
else: |
||||||
|
return self.entry_price - self.exit_price |
||||||
|
|
||||||
|
def pnl_percentage(self): |
||||||
|
if self.direction == Trade.LONG: |
||||||
|
return (self.exit_price - self.entry_price) / self.entry_price * 100 |
||||||
|
else: |
||||||
|
return (self.entry_price - self.exit_price) / self.entry_price * 100 |
||||||
@ -0,0 +1,79 @@ |
|||||||
|
''' |
||||||
|
''' |
||||||
|
from data import series |
||||||
|
from execution.executor import Executor |
||||||
|
|
||||||
|
import random |
||||||
|
from math import inf |
||||||
|
import numpy |
||||||
|
|
||||||
|
class Solver(): |
||||||
|
''' |
||||||
|
|
||||||
|
''' |
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, series): |
||||||
|
''' |
||||||
|
Constructor |
||||||
|
''' |
||||||
|
|
||||||
|
self.series = series |
||||||
|
self.executor = Executor(series) |
||||||
|
self.generators = [] |
||||||
|
|
||||||
|
def add_generator(self, generator): |
||||||
|
self.generators.append(generator) |
||||||
|
|
||||||
|
def solve(self): |
||||||
|
max_signals = 3 |
||||||
|
max_strategies = 1000 |
||||||
|
self.results = [] |
||||||
|
|
||||||
|
for x in range(0, 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) |
||||||
|
result = self.evaluate_trades(trades) |
||||||
|
result['display_name'] = ' && '.join([signal.get_text() for signal in strategy]) |
||||||
|
self.results.append(result) |
||||||
|
|
||||||
|
return self.results |
||||||
|
|
||||||
|
def evaluate_trades(self, trades): |
||||||
|
result = {} |
||||||
|
|
||||||
|
profits = [x.pnl() for x in trades] |
||||||
|
|
||||||
|
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 |
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,43 @@ |
|||||||
|
''' |
||||||
|
''' |
||||||
|
|
||||||
|
from PyQt5.QtWidgets import QMainWindow, QTreeWidgetItem |
||||||
|
|
||||||
|
from .ui_mainwindow import Ui_MainWindow |
||||||
|
from solver.solver import Solver |
||||||
|
from data.series import Series |
||||||
|
from data.signal import PriceComparisonSignalGenerator, RsiSignalGenerator,\ |
||||||
|
AtrSignalGenerator |
||||||
|
from PyQt5.Qt import Qt |
||||||
|
|
||||||
|
class MainWindow(QMainWindow, Ui_MainWindow): |
||||||
|
''' |
||||||
|
''' |
||||||
|
|
||||||
|
def __init__(self, parent=None): |
||||||
|
''' |
||||||
|
Constructor |
||||||
|
''' |
||||||
|
super().__init__(parent) |
||||||
|
self.setupUi(self) |
||||||
|
|
||||||
|
self.series = Series() |
||||||
|
self.series.load_from_finam_csv('/home/asakul/tmp/daily/RTSI_20000101_20171231_daily.csv') |
||||||
|
self.solver = Solver(self.series) |
||||||
|
self.solver.add_generator(PriceComparisonSignalGenerator()) |
||||||
|
self.solver.add_generator(RsiSignalGenerator()) |
||||||
|
self.solver.add_generator(AtrSignalGenerator()) |
||||||
|
results = self.solver.solve() |
||||||
|
|
||||||
|
for result in results: |
||||||
|
item = QTreeWidgetItem(self.tw_strategies) |
||||||
|
item.setText(0, result['display_name']) |
||||||
|
item.setText(1, str(result['trades_number'])) |
||||||
|
item.setText(2, str(result['total_pnl'])) |
||||||
|
item.setText(3, "{:.2f}".format(result['profit_factor'])) |
||||||
|
item.setText(4, "{:.2f}".format(result['sharpe'])) |
||||||
|
item.setText(5, "{:.2f}%".format(result['avg_percentage'])) |
||||||
|
item.setData(0, Qt.UserRole + 1, result) |
||||||
|
|
||||||
|
def strategyClicked(self, item, column): |
||||||
|
result = item.getData(0, Qt.UserRole + 1) |
||||||
@ -0,0 +1,103 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<ui version="4.0"> |
||||||
|
<class>MainWindow</class> |
||||||
|
<widget class="QMainWindow" name="MainWindow"> |
||||||
|
<property name="geometry"> |
||||||
|
<rect> |
||||||
|
<x>0</x> |
||||||
|
<y>0</y> |
||||||
|
<width>800</width> |
||||||
|
<height>600</height> |
||||||
|
</rect> |
||||||
|
</property> |
||||||
|
<property name="windowTitle"> |
||||||
|
<string>MainWindow</string> |
||||||
|
</property> |
||||||
|
<widget class="QWidget" name="centralwidget"> |
||||||
|
<layout class="QGridLayout" name="gridLayout"> |
||||||
|
<property name="leftMargin"> |
||||||
|
<number>2</number> |
||||||
|
</property> |
||||||
|
<property name="topMargin"> |
||||||
|
<number>2</number> |
||||||
|
</property> |
||||||
|
<property name="rightMargin"> |
||||||
|
<number>2</number> |
||||||
|
</property> |
||||||
|
<property name="bottomMargin"> |
||||||
|
<number>2</number> |
||||||
|
</property> |
||||||
|
<item row="0" column="0"> |
||||||
|
<widget class="QTreeWidget" name="tw_strategies"> |
||||||
|
<property name="sortingEnabled"> |
||||||
|
<bool>true</bool> |
||||||
|
</property> |
||||||
|
<column> |
||||||
|
<property name="text"> |
||||||
|
<string>Strategy</string> |
||||||
|
</property> |
||||||
|
</column> |
||||||
|
<column> |
||||||
|
<property name="text"> |
||||||
|
<string>Trades #</string> |
||||||
|
</property> |
||||||
|
</column> |
||||||
|
<column> |
||||||
|
<property name="text"> |
||||||
|
<string>Total PnL</string> |
||||||
|
</property> |
||||||
|
</column> |
||||||
|
<column> |
||||||
|
<property name="text"> |
||||||
|
<string>PF</string> |
||||||
|
</property> |
||||||
|
</column> |
||||||
|
<column> |
||||||
|
<property name="text"> |
||||||
|
<string>Sharpe</string> |
||||||
|
</property> |
||||||
|
</column> |
||||||
|
<column> |
||||||
|
<property name="text"> |
||||||
|
<string>Avg. %</string> |
||||||
|
</property> |
||||||
|
</column> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
</layout> |
||||||
|
</widget> |
||||||
|
<widget class="QMenuBar" name="menubar"> |
||||||
|
<property name="geometry"> |
||||||
|
<rect> |
||||||
|
<x>0</x> |
||||||
|
<y>0</y> |
||||||
|
<width>800</width> |
||||||
|
<height>27</height> |
||||||
|
</rect> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
<widget class="QStatusBar" name="statusbar"/> |
||||||
|
</widget> |
||||||
|
<resources/> |
||||||
|
<connections> |
||||||
|
<connection> |
||||||
|
<sender>tw_strategies</sender> |
||||||
|
<signal>itemClicked(QTreeWidgetItem*,int)</signal> |
||||||
|
<receiver>MainWindow</receiver> |
||||||
|
<slot>strategyClicked(QTreeWidgetItem*,int)</slot> |
||||||
|
<hints> |
||||||
|
<hint type="sourcelabel"> |
||||||
|
<x>106</x> |
||||||
|
<y>103</y> |
||||||
|
</hint> |
||||||
|
<hint type="destinationlabel"> |
||||||
|
<x>800</x> |
||||||
|
<y>33</y> |
||||||
|
</hint> |
||||||
|
</hints> |
||||||
|
</connection> |
||||||
|
</connections> |
||||||
|
<slots> |
||||||
|
<slot>strategyClicked(QTreeWidgetItem*,int)</slot> |
||||||
|
</slots> |
||||||
|
</ui> |
||||||
@ -0,0 +1,46 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
|
||||||
|
# Form implementation generated from reading ui file 'mainwindow.ui' |
||||||
|
# |
||||||
|
# Created by: PyQt5 UI code generator 5.5.1 |
||||||
|
# |
||||||
|
# WARNING! All changes made in this file will be lost! |
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets |
||||||
|
|
||||||
|
class Ui_MainWindow(object): |
||||||
|
def setupUi(self, MainWindow): |
||||||
|
MainWindow.setObjectName("MainWindow") |
||||||
|
MainWindow.resize(800, 600) |
||||||
|
self.centralwidget = QtWidgets.QWidget(MainWindow) |
||||||
|
self.centralwidget.setObjectName("centralwidget") |
||||||
|
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget) |
||||||
|
self.gridLayout.setContentsMargins(2, 2, 2, 2) |
||||||
|
self.gridLayout.setObjectName("gridLayout") |
||||||
|
self.tw_strategies = QtWidgets.QTreeWidget(self.centralwidget) |
||||||
|
self.tw_strategies.setObjectName("tw_strategies") |
||||||
|
self.gridLayout.addWidget(self.tw_strategies, 0, 0, 1, 1) |
||||||
|
MainWindow.setCentralWidget(self.centralwidget) |
||||||
|
self.menubar = QtWidgets.QMenuBar(MainWindow) |
||||||
|
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 27)) |
||||||
|
self.menubar.setObjectName("menubar") |
||||||
|
MainWindow.setMenuBar(self.menubar) |
||||||
|
self.statusbar = QtWidgets.QStatusBar(MainWindow) |
||||||
|
self.statusbar.setObjectName("statusbar") |
||||||
|
MainWindow.setStatusBar(self.statusbar) |
||||||
|
|
||||||
|
self.retranslateUi(MainWindow) |
||||||
|
self.tw_strategies.itemClicked['QTreeWidgetItem*','int'].connect(MainWindow.strategyClicked) |
||||||
|
QtCore.QMetaObject.connectSlotsByName(MainWindow) |
||||||
|
|
||||||
|
def retranslateUi(self, MainWindow): |
||||||
|
_translate = QtCore.QCoreApplication.translate |
||||||
|
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) |
||||||
|
self.tw_strategies.setSortingEnabled(True) |
||||||
|
self.tw_strategies.headerItem().setText(0, _translate("MainWindow", "Strategy")) |
||||||
|
self.tw_strategies.headerItem().setText(1, _translate("MainWindow", "Trades #")) |
||||||
|
self.tw_strategies.headerItem().setText(2, _translate("MainWindow", "Total PnL")) |
||||||
|
self.tw_strategies.headerItem().setText(3, _translate("MainWindow", "PF")) |
||||||
|
self.tw_strategies.headerItem().setText(4, _translate("MainWindow", "Sharpe")) |
||||||
|
self.tw_strategies.headerItem().setText(5, _translate("MainWindow", "Avg. %")) |
||||||
|
|
||||||
Loading…
Reference in new issue