Added:
trunk/profit/lib/session/requestthread.py (contents, props changed)
trunk/profit/lib/widgets/extendedshell.py (contents, props changed)
trunk/profit/lib/widgets/ui_extendedshell.ui (contents, props changed)
trunk/profit/lib/widgets/ui_marketdatacontract.ui (contents, props changed)
Modified:
trunk/README
trunk/bin/hist_downloader
trunk/bin/login_helper
trunk/examples/accountsummary.py
trunk/profit/lib/__init__.py
trunk/profit/lib/scripttools.py
trunk/profit/lib/session/__init__.py
trunk/profit/lib/session/collection.py
trunk/profit/lib/session/savethread.py
trunk/profit/lib/strategy/builder.py
trunk/profit/lib/widgets/shell.py
trunk/profit/lib/widgets/tickfieldselect.py
trunk/profit/neuralnetdesigner/network_test.py
trunk/profit/neuralnetdesigner/test.py
trunk/profit/neuralnetdesigner/train_test.py
trunk/profit/strategydesigner/main.py
trunk/profit/workbench/accountdisplay.py
trunk/profit/workbench/centraltabs.py
trunk/profit/workbench/connectiondisplay.py
trunk/profit/workbench/executionsdisplay.py
trunk/profit/workbench/historicaldatadisplay.py
trunk/profit/workbench/main.py
trunk/profit/workbench/messagedisplay.py
trunk/profit/workbench/orderdisplay.py
trunk/profit/workbench/portfoliodisplay.py
trunk/profit/workbench/sessiontree.py
trunk/profit/workbench/strategydisplay.py
trunk/profit/workbench/tickerdisplay.py
Log:
Improvements to ticker display and other widgets. Adding an extended
shell and an unused requestthread module. Other fixes.
Modified: trunk/README
==============================================================================
--- trunk/README (original)
+++ trunk/README Thu Jul 31 00:06:40 2008
@@ -1,13 +1,18 @@
== To Do for 0.2 ==
-1. add buffer editor to shell panel. perhaps update shell w/ code
from eric4
+micro:
-2. add ticker field select widget to ticker display; make splitter like
- the message display
+2. clean up signals and slots where possible
3. fix method request/tickerId/contract handling. allow for display
of tickerIds with symbol. extend tickers display with contract
details.
+
+4. un-nest profit/lib/ packages (leave widgets)
+
+macro:
+
+1. add buffer editor to shell panel. perhaps update shell w/ code
from eric4
4. add viewable historical data requests and plots
Modified: trunk/bin/hist_downloader
==============================================================================
--- trunk/bin/hist_downloader (original)
+++ trunk/bin/hist_downloader Thu Jul 31 00:06:40 2008
@@ -149,7 +149,7 @@
sess = Session()
sess.histdataQueue.put(('AAPL', 100))
- sess.connect(sess, Signals.sessionStatus, logging.debug)
+ sess.connect(sess, Signals.session.status, logging.debug)
sess.filename = options.output
sess.connectTWS(
options.host, options.port, options.clientid)
Modified: trunk/bin/login_helper
==============================================================================
--- trunk/bin/login_helper (original)
+++ trunk/bin/login_helper Thu Jul 31 00:06:40 2008
@@ -141,6 +141,7 @@
else:
logging.debug('input focus set successfully.')
for key in keys:
+ time.sleep(0.125)
if not guitest.PressReleaseKey(key):
logging.debug('could not press key "%s".', key)
result = False
Modified: trunk/examples/accountsummary.py
==============================================================================
--- trunk/examples/accountsummary.py (original)
+++ trunk/examples/accountsummary.py Thu Jul 31 00:06:40 2008
@@ -123,7 +123,7 @@
self.valueCache = {}
self.setHorizontalHeaderLabels(['Item', 'Currency', 'Value'])
if parent:
- self.connect(parent.window(), Signals.sessionCreated,
+ self.connect(parent.window(), Signals.session.created,
self.setSession)
def setAccountKeyDisplay(self, key, currency, show, view):
Modified: trunk/profit/lib/__init__.py
==============================================================================
--- trunk/profit/lib/__init__.py (original)
+++ trunk/profit/lib/__init__.py Thu Jul 31 00:06:40 2008
@@ -64,7 +64,7 @@
"""
names = name.split('.')
modname, itemname = names[0:-1], names[-1]
- mod = importName(str.join('.', modname), reloaded=True)
+ mod = importName(str.join('.', modname), reloaded=reloaded)
return getattr(mod, itemname)
@@ -72,33 +72,24 @@
""" Contains SIGNAL attributes for easy and consistent reference.
"""
- activated = SIGNAL('activated(QSystemTrayIcon::ActivationReason)')
clicked = SIGNAL('clicked()')
- connectedTWS = SIGNAL('connectedTWS')
createdAccountData = SIGNAL('createdAccountData')
createdSeries = SIGNAL('createdSeries')
createdTicker = SIGNAL('createdTicker')
- currentChanged = SIGNAL('currentChanged(int)')
currentIndexChanged = SIGNAL('currentIndexChanged(int)')
- customContextMenuRequested = SIGNAL(
- 'customContextMenuRequested(const QPoint &)')
- dataChanged = SIGNAL(
- 'dataChanged(const QModelIndex &, const QModelIndex &)')
+ dataChanged = SIGNAL('dataChanged(const QModelIndex &, const
QModelIndex &)')
dialogFinished = SIGNAL('finished(int)')
- disconnectedTWS = SIGNAL('disconnectedTWS')
doubleValueChanged = SIGNAL('valueChanged(double)')
editingFinished = SIGNAL('editingFinished()')
enableCurve = SIGNAL('enableCurve')
+ highlightSelections = SIGNAL('highlightSelections')
finished = SIGNAL('finished()')
headerDataChanged = SIGNAL('headerDataChanged(Qt::Orientation,
int, int)')
- highlightSelections = SIGNAL('highlightSelections')
- historicalDataStart = SIGNAL('historicalDataStart')
- historicalDataFinish = SIGNAL('historicalDataFinish')
-
- iconChanged = SIGNAL('iconChanged()')
intValueChanged = SIGNAL('valueChanged(int)')
+ itemActivated = SIGNAL('itemActivated (const QModelIndex &)')
itemChanged = SIGNAL('itemChanged(QStandardItem *)')
itemDoubleClicked = SIGNAL('itemDoubleClicked(QTreeWidgetItem *, int)')
+ itemSelected = SIGNAL('itemSelected (const QModelIndex &)')
lastWindowClosed = SIGNAL('lastWindowClosed()')
layoutChanged = SIGNAL('layoutChanged()')
loadFinished = SIGNAL('loadFinished(bool)')
@@ -107,40 +98,43 @@
modelReset = SIGNAL('modelReset()')
modified = SIGNAL('modified')
openUrl = SIGNAL('openUrl(PyQt_PyObject)')
- processFinished = SIGNAL('finished(int, QProcess::ExitStatus)')
- requestedHistoricalData = SIGNAL('requestedHistoricalData')
rowsInserted = SIGNAL('rowsInserted(const QModelIndex &, int, int)')
- selectionChanged = SIGNAL(
- 'selectionChanged(const QItemSelection &, const QItemSelection &)')
- sessionCreated = SIGNAL('sessionCreated(PyQt_PyObject)')
- sessionItemSelected = SIGNAL('itemSelected (const QModelIndex &)')
- sessionItemActivated = SIGNAL('itemActivated (const QModelIndex &)')
- sessionReference = SIGNAL('sessionReference(PyQt_PyObject)')
- sessionRequest = SIGNAL('sessionRequest')
- sessionStatus = SIGNAL('sessionStatus')
+ selectionChanged = SIGNAL('selectionChanged(const QItemSelection
&, const QItemSelection &)')
settingsChanged = SIGNAL('settingsChanged')
splitterMoved = SIGNAL('splitterMoved(int, int)')
standardItemChanged = SIGNAL('itemChanged(QStandardItem *)')
-
- strategyRequestActivate = SIGNAL('strategyActivated(PyQt_PyObject, bool)')
-
- strategyLoaded = SIGNAL('strategyLoaded(PyQt_PyObject)')
- strategyLoadFailed = SIGNAL('strategyLoadFaield(PyQt_PyObject)')
- strategyFileUpdated = SIGNAL('strategyFileUpdated(PyQt_PyObject)')
terminated = SIGNAL('terminated()')
textChanged = SIGNAL('textChanged(const QString &)')
textChangedEditor = SIGNAL('textChanged()')
tickerClicked = SIGNAL('tickerClicked')
timeout = SIGNAL('timeout()')
+ trayIconActivated = SIGNAL('activated(QSystemTrayIcon::ActivationReason)')
triggered = SIGNAL('triggered()')
triggeredBool = SIGNAL('triggered(bool)')
zoomed = SIGNAL('zoomed(const QwtDoubleRect &)')
- neuralNetworkCreated = SIGNAL('neuralNetworkCreated')
- threadStarted = SIGNAL('started()')
- threadRunning = SIGNAL('running')
- threadFinished = SIGNAL('finished()')
- collectorActivate = SIGNAL('collectorActivate(PyQt_PyObject)')
+ class contract:
+ added = SIGNAL('contractAdded(int, PyQt_PyObject)')
+
+ class histdata:
+ start = SIGNAL('historicalDataStart')
+ finish = SIGNAL('historicalDataFinish')
+
+ class session:
+ created = SIGNAL('sessionCreated(PyQt_PyObject)')
+ reference = SIGNAL('sessionReference(PyQt_PyObject)')
+ request = SIGNAL('sessionRequest')
+ status = SIGNAL('sessionStatus')
+
+ class strategy:
+ loaded = SIGNAL('strategyLoaded(PyQt_PyObject)')
+ loadFailed = SIGNAL('strategyLoadFaield(PyQt_PyObject)')
+ fileUpdated = SIGNAL('strategyFileUpdated(PyQt_PyObject)')
+ requestActivate = SIGNAL('strategyActivated(PyQt_PyObject, bool)')
+
+ class tws:
+ connected = SIGNAL('connectedTWS')
+ disconnected = SIGNAL('disconnectedTWS')
class Slots:
@@ -280,7 +274,7 @@
@return None
"""
self.disconnect(
- instance(), Signals.sessionReference, self.existingSession)
+ instance(), Signals.session.reference, self.existingSession)
if session is not self.session:
self.setSession(session)
@@ -291,10 +285,10 @@
"""
app = instance()
connect = self.connect
- connect(app, Signals.sessionCreated, self.setSession)
- connect(app, Signals.sessionReference, self.existingSession)
- connect(self, Signals.sessionRequest, app, Signals.sessionRequest)
- self.emit(Signals.sessionRequest)
+ connect(app, Signals.session.created, self.setSession)
+ connect(app, Signals.session.reference, self.existingSession)
+ connect(self, Signals.session.request, app, Signals.session.request)
+ self.emit(Signals.session.request)
def setSession(self, session):
""" Default implementation sets session as attribute.
Modified: trunk/profit/lib/scripttools.py
==============================================================================
--- trunk/profit/lib/scripttools.py (original)
+++ trunk/profit/lib/scripttools.py Thu Jul 31 00:06:40 2008
@@ -79,7 +79,7 @@
self.session = session = Session(strategy=False)
session.filename = options.output
- self.connect(session, Signals.sessionStatus, logging.debug)
+ self.connect(session, Signals.session.status, logging.debug)
session.connectTWS(
options.host, options.port, options.clientid)
Modified: trunk/profit/lib/session/__init__.py
==============================================================================
--- trunk/profit/lib/session/__init__.py (original)
+++ trunk/profit/lib/session/__init__.py Thu Jul 31 00:06:40 2008
@@ -14,12 +14,12 @@
from ib.opt import ibConnection
from ib.opt.message import messageTypeNames
-from ib.ext.ExecutionFilter import ExecutionFilter
from profit.lib import logging
from profit.lib import Signals
from profit.lib.session import collection
from profit.lib.session.savethread import SaveThread
+from profit.lib.session.requestthread import RequestThread
from profit.lib.strategy.builder import SessionStrategyBuilder
@@ -30,6 +30,7 @@
self.ticker = collection.TickerCollection(session)
self.error = collection.ErrorDataCollection(session)
self.order = collection.OrderDataCollection(session)
+ self.contract = collection.ContractDataCollection(session)
class Session(QObject):
@@ -38,13 +39,15 @@
"""
def __init__(self, strategy=None):
QObject.__init__(self)
+ self.requestThread = requestThread = RequestThread(self)
+ requestThread.start()
self.strategy = strategy if strategy else SessionStrategyBuilder(self)
self.connection = self.filename = None
self.messages = []
self.messagesBare = []
self.messagesTyped = {}
self.savedLength = 0
- self.dataMaps = DataMaps(self)
+ self.maps = DataMaps(self)
def __str__(self):
""" x.__str__() <==> str(x)
@@ -167,7 +170,7 @@
con.enableLogging(enableLogging)
con.connect()
con.registerAll(self.receiveMessage)
- self.emit(Signals.connectedTWS)
+ self.emit(Signals.tws.connected)
def disconnectTWS(self):
""" Disconnects this instance from TWS.
@@ -176,7 +179,13 @@
"""
if self.isConnected():
self.connection.disconnect()
- self.emit(Signals.disconnectedTWS)
+ self.emit(Signals.tws.disconnected)
+
+ def receiveObject(self, object):
+ """ Recieve an unknown object, usually during session load/import.
+
+ """
+ pass
def receiveMessage(self, message, mtime=time):
""" Receive a message from TWS and propagate it as a Qt signal.
@@ -208,7 +217,6 @@
for tickerId, contract in self.strategy.makeContracts():
connection.reqMktData(tickerId, contract, '', False)
connection.reqMktDepth(tickerId, contract, 1)
- ## else queue for later?
def requestAccount(self):
""" Request account data.
@@ -226,14 +234,12 @@
"""
connection = self.connection
if connection and connection.isConnected():
- filt = ExecutionFilter()
-# connection.reqExecutions(filt)
connection.reqAllOpenOrders()
connection.reqOpenOrders()
def requestHistoricalData(self, params):
## we should msg the object instead
- self.historicalDataCollection.begin(params)
+ self.maps.historical.begin(params)
def saveFinished(self):
""" Slot that updates this instance after a save thread has completed.
@@ -246,10 +252,13 @@
msg = 'Session file saved. Wrote %s messages.' % count
else:
msg = 'Error saving file.'
- self.emit(Signals.sessionStatus, msg)
+ self.emit(Signals.session.status, msg)
+
+ def extraObjects(self):
+ return []
def exportFinished(self):
- """ Slot that updates this instance after an export thread has completed.
+ """ Updates this instance after an export thread has completed.
@return None
"""
@@ -258,21 +267,21 @@
msg = 'Session exported. Wrote %s messages.' % count
else:
msg = 'Error exporting messages.'
- self.emit(Signals.sessionStatus, msg)
+ self.emit(Signals.session.status, msg)
def saveTerminated(self):
""" Slot for handling a canceled save thread.
@return None
"""
- self.emit(Signals.sessionStatus, 'Session file save terminated.')
+ self.emit(Signals.session.status, 'Session file save terminated.')
def exportTerminated(self):
""" Slot for handling a canceled export thread.
@return None
"""
- self.emit(Signals.sessionStatus, 'Session export terminated.')
+ self.emit(Signals.session.status, 'Session export terminated.')
def saveInProgress(self):
""" Returns True if this instance has a running save thread
@@ -296,7 +305,7 @@
self.connect(thread, Signals.finished, self.saveFinished)
self.connect(thread, Signals.terminated, self.saveTerminated)
thread.start()
- self.emit(Signals.sessionStatus, 'Started session file save.')
+ self.emit(Signals.session.status, 'Started session file save.')
def load(self, filename):
""" Restores session messages from file.
@@ -318,9 +327,14 @@
try:
messages = load(handle)
yield len(messages)
- for index, (mtime, message) in enumerate(messages):
- self.receiveMessage(message, mtime)
- yield index
+ for obj in enumerate(messages):
+ try:
+ index, (mtime, message) = obj
+ except (TypeError, ValueError, ):
+ self.receiveObject(obj)
+ else:
+ self.receiveMessage(message, mtime)
+ yield index
except (UnpicklingError, ):
pass
finally:
@@ -384,7 +398,7 @@
self.connect(thread, Signals.finished, self.exportFinished)
self.connect(thread, Signals.terminated, self.exportTerminated)
thread.start()
- self.emit(Signals.sessionStatus, 'Started session export.')
+ self.emit(Signals.session.status, 'Started session export.')
def iterMessageTypes(self, *types):
for key in types:
Modified: trunk/profit/lib/session/collection.py
==============================================================================
--- trunk/profit/lib/session/collection.py (original)
+++ trunk/profit/lib/session/collection.py Thu Jul 31 00:06:40 2008
@@ -1,6 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
+
# Copyright 2007 Troy Melhase, Yichun Wei
# Distributed under the terms of the GNU General Public License v2
# Author: Troy Melhase <tr...@gci.net>
@@ -67,6 +68,21 @@
self.last[key] = iv
+class ContractDataCollection(DataCollection):
+ sessionResendSignals = [Signals.contract.added, ]
+
+ def __setitem__(self, tickerId, contract):
+ ## maybe enforce types?
+ DataCollection.__setitem__(self, tickerId, contract)
+ self.emit(Signals.contract.added, tickerId, contract)
+
+ def on_session_TickPrice_TickSize(self, message):
+ tickerId = message.tickerId
+ if tickerId not in self:
+ contract = self[tickerId] = self.session.strategy.makeContract(symbol='')
+ self.emit(Signals.contract.added, tickerId, contract)
+
+
class TickerCollection(DataCollection):
sessionResendSignals = [Signals.createdSeries,
Signals.createdTicker, ]
@@ -100,8 +116,8 @@
class HistoricalDataCollection(DataCollection):
- sessionResendSignals = [Signals.historicalDataStart,
- Signals.historicalDataFinish]
+ sessionResendSignals = [Signals.histdata.start,
+ Signals.histdata.finish]
def __init__(self, session):
DataCollection.__init__(self, session)
@@ -112,13 +128,13 @@
reqData = self.setdefault(reqId, {})
histMsgs = self.session.messagesTyped['HistoricalData']
reqData['messages'] = self.historyMessages(reqId, histMsgs)
- self.emit(Signals.historicalDataFinish, reqId)
+ self.emit(Signals.histdata.finish, reqId)
def begin(self, params):
reqId = params['tickerId']
reqData = self.setdefault(reqId, {})
reqData.update(params)
- self.emit(Signals.historicalDataStart, reqId, reqData)
+ self.emit(Signals.histdata.start, reqId, reqData)
self.session.connection.reqHistoricalData(**reqData)
@staticmethod
@@ -139,4 +155,3 @@
class ErrorDataCollection(DataCollection):
def on_session_Error(self, message):
logging.debug(str(message))
-
Added: trunk/profit/lib/session/requestthread.py
==============================================================================
--- (empty file)
+++ trunk/profit/lib/session/requestthread.py Thu Jul 31 00:06:40 2008
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Copyright 2007 Troy Melhase, Yichun Wei
+# Distributed under the terms of the GNU General Public License v2
+# Author: Troy Melhase <tr...@gci.net>
+# Yichun Wei <yichu...@gmail.com>
+
+
+from PyQt4.QtCore import QMutex, QThread
+from profit.lib import logging
+
+class RequestThread(QThread):
+ """ RequestThread -> queues requests until connection is active.
+
+
+ """
+ def __init__(self, parent):
+ """ Initializer.
+
+ @param parent parent of this object; should be a Session instance
+ @return None
+ """
+ QThread.__init__(self, parent)
+ self.queue = []
+ self.mutex = QMutex()
+
+ def run(self):
+ """ Send queued requests.
+
+ @return None
+ """
+ session = self.parent()
+ ## could connect to session Signals.tws.connected
+ while True:
+ self.msleep(500)
+ if self.queue and session.isConnected():
+ request, args, kwds = self.nextRequest()
+ try:
+ request(*args, **kwds)
+ except (Exception, ), exc:
+ logging.exception('Could not call request: %s', request)
+
+ def addRequest(self, request, *args, **kwds):
+ self.mutex.lock()
+ self.queue.append((request, args, kwds))
+ self.mutex.unlock()
+
+ def nextRequest(self):
+ self.mutex.lock()
+ request = self.queue.pop(0)
+ self.mutex.unlock()
+ return request
Modified: trunk/profit/lib/session/savethread.py
==============================================================================
--- trunk/profit/lib/session/savethread.py (original)
+++ trunk/profit/lib/session/savethread.py Thu Jul 31 00:06:40 2008
@@ -40,7 +40,8 @@
pass
else:
last = len(session.messages)
- messages = session.messages[0:last]
+ extras = session.extraObjects()
+ messages = session.messages[0:last] + extras
types = self.types
if types:
def messageFilter((mtime, message)):
Modified: trunk/profit/lib/strategy/builder.py
==============================================================================
--- trunk/profit/lib/strategy/builder.py (original)
+++ trunk/profit/lib/strategy/builder.py Thu Jul 31 00:06:40 2008
@@ -41,11 +41,12 @@
self.threads = []
self.tickers = []
app = instance()
- connect = self.connect
- connect(app, Signals.strategyFileUpdated,
- self.externalFileUpdated)
- connect(app, Signals.strategyRequestActivate,
- self.requestActivation)
+ if app:
+ connect = self.connect
+ connect(app, Signals.strategy.fileUpdated,
+ self.externalFileUpdated)
+ connect(app, Signals.strategy.requestActivate,
+ self.requestActivation)
@classmethod
def paramsHistoricalData(cls, **kwds):
@@ -133,10 +134,10 @@
call = getattr(self, 'from%s' % origintype.title())
okay, message = call(**params)
self.loadMessage = message
- signal = Signals.strategyLoaded if okay else \
- Signals.strategyLoadFailed
+ signal = Signals.strategy.loaded if okay else \
+ Signals.strategy.loadFailed
self.emit(signal, message)
if okay and params.get('reload', False):
self.emit(signal, 'Strategy reloaded')
except (Exception, ), ex:
- self.emit(Signals.strategyLoadFailed, str(ex))
+ self.emit(Signals.strategy.loadFailed, str(ex))
Added: trunk/profit/lib/widgets/extendedshell.py
==============================================================================
--- (empty file)
+++ trunk/profit/lib/widgets/extendedshell.py Thu Jul 31 00:06:40 2008
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Copyright 2007 Troy Melhase
+# Distributed under the terms of the GNU General Public License v2
+# Author: Troy Melhase <tr...@gci.net>
+
+from PyQt4.QtCore import Qt, pyqtSignature
+from PyQt4.QtGui import QFrame, QKeyEvent
+from profit.lib.widgets.ui_extendedshell import Ui_ExtendedShell
+
+## save/load splitter state
+
+class ExtendedPythonShell(QFrame, Ui_ExtendedShell):
+ def __init__(self, parent=None):
+ QFrame.__init__(self, parent)
+ self.setupUi(self)
+
+ @pyqtSignature('')
+ def on_execButton_clicked(self):
+ shell = self.shellWidget
+ #shell.runLines(str(self.editorWidget.text()).split('\n'))
+ shell.insertPlainText(self.editorWidget.text())
+ e = QKeyEvent(QKeyEvent.KeyPress,
+ Qt.Key_Return, Qt.NoModifier)
+ shell.keyPressEvent(e)
+
Modified: trunk/profit/lib/widgets/shell.py
==============================================================================
--- trunk/profit/lib/widgets/shell.py (original)
+++ trunk/profit/lib/widgets/shell.py Thu Jul 31 00:06:40 2008
@@ -22,7 +22,7 @@
from PyQt4.QtCore import Qt, QString
from PyQt4.QtGui import QApplication, QBrush, QColor, QFont,
QTextCursor, QTextEdit, QTextCharFormat
-from profit.lib import Settings, Signals, SessionHandler
+from profit.lib import Settings, Signals, BasicHandler
# disable the help function because it reads directly from stdin and
@@ -68,7 +68,7 @@
def update(self, **kwds):
self.locals.update(kwds)
-class PythonShell(QTextEdit, SessionHandler):
+class PythonShell(QTextEdit, BasicHandler):
""" PythonShell(...) -> python shell widget
"""
@@ -82,12 +82,12 @@
ps1 = '>>> '
ps2 = '... '
- def __init__(self, parent, stdout, stderr):
- QTextEdit.__init__(self, parent)
-
+ def setStdOutErr(self, stdout, stderr):
sys.stdout.extend([stdout, self])
sys.stderr.extend([stderr, self])
+ def __init__(self, parent):
+ QTextEdit.__init__(self, parent)
self.line = QString()
self.lines = []
self.history = []
@@ -208,6 +208,11 @@
self.lines = []
self.clearLine()
+ def runLines(self, lines):
+ interp = self.interp
+ for line in lines:
+ interp.runsource(line)
+
def clearLine(self):
self.point = 0
self.line.truncate(0)
@@ -244,10 +249,7 @@
return
elif key in (Qt.Key_Return, Qt.Key_Enter):
self.write('\n')
- if self.reading:
- self.reading = 0
- else:
- self.run()
+ self.run()
elif key==Qt.Key_Tab:
self.insertPlainText(text)
elif key==Qt.Key_Backspace and self.point:
@@ -359,6 +361,7 @@
if __name__ == '__main__':
app = QApplication(sys.argv)
- window = PythonShell(parent=None, stdout=sys.__stdout__, stderr=sys.__stderr__)
+ window = PythonShell(parent=None)
+ window.setStdOutErr(stdout=sys.__stdout__, stderr=sys.__stderr__)
window.show()
sys.exit(app.exec_())
Modified: trunk/profit/lib/widgets/tickfieldselect.py
==============================================================================
--- trunk/profit/lib/widgets/tickfieldselect.py (original)
+++ trunk/profit/lib/widgets/tickfieldselect.py Thu Jul 31 00:06:40 2008
@@ -14,31 +14,36 @@
from profit.lib.widgets.ui_tickfieldselect import Ui_TickFieldSelect
-def itemTickField(item):
- """ Returns the tick field from the item's data
+class ExField(object):
+ """ Namespace for our 'extra' fields, i.e., fields not in TickType.
+ The extra fields are all negative so as to not conflict with those
+ in TickType.
"""
- return item.data(DataRoles.tickerField).toInt()[0]
+ tid, sym, pos, val = enum = range(-4, 0)
+ labels = ['id', 'symbol', 'position', 'value']
+ all = zip(enum, labels)
-def setItemTickField(item, field):
- """ Sets the tick field role on the item.
+def extraFieldSpecs():
+ """ Generates sequence of dictionaries that describe our extra fields.
"""
- item.setData(DataRoles.tickerField, QVariant(field))
+ for field, label in ExField.all:
+ yield dict(value=field, sort=field, name=label, title=label.title())
def fieldIds():
- """ Generates sequence of tick field identifiers. Refer to the
- TickType class for concrete list.
+ """ Generates sequence of tick field identifiers.
+ Refer to the TickType class for actual attributes and values.
"""
for field in fieldSpecs():
yield field['value']
def fieldSpecs():
- """ Yields one description dict for every TickType field.
+ """ Yields one description dictionary for every TickType field.
"""
values = [getattr(TickType, k) for k in dir(TickType)]
@@ -48,6 +53,20 @@
yield dict(sort=value, value=value, name=name, title=title)
+def itemTickField(item):
+ """ Returns the tick field from the item's data. May be invalid.
+
+ """
+ return item.data(DataRoles.tickerField).toInt()[0]
+
+
+def setItemTickField(item, field):
+ """ Sets the tick field role on the item.
+
+ """
+ item.setData(DataRoles.tickerField, QVariant(field))
+
+
def tickFieldTitle(name):
""" Make title from name, aka UnCapCase.
@@ -59,18 +78,10 @@
words = rxsplit('([a-z]+)', name)
## title case each word in the word list if the word isn't already
## all upper case.
- words = [(word.title() if not word.upper()==word else word)
- for word in words if word]
+ words = [(w if w.upper()==w else w.title()) for w in words if w]
return str.join(' ', words)
-def extraFieldSpecs():
- yield dict(sort=-4, value=-4, name='id', title='Id')
- yield dict(sort=-3, value=-3, name='symbol', title='Symbol')
- yield dict(sort=-2, value=-2, name='position', title='Position')
- yield dict(sort=-1, value=-1, name='value', title='Value')
-
-
class TickFieldSelect(QFrame, Ui_TickFieldSelect):
""" TickFieldSelect -> widget for selecting various tick fields.
@@ -90,10 +101,9 @@
"""
fieldsList = self.fieldsList
fieldsList.clear()
- allFields = sorted(list(extraFieldSpecs()) + list(fieldSpecs()),
- key=lambda d:d['sort'])
+ allFields = list(extraFieldSpecs()) + list(fieldSpecs())
+ allFields = sorted(allFields, key=lambda d:d['sort'])
self.allTickFields = allFields
-
for rowId, fieldDesc in enumerate(allFields):
fieldsList.addItem(fieldDesc['title'])
item = fieldsList.item(rowId)
Added: trunk/profit/lib/widgets/ui_extendedshell.ui
==============================================================================
--- (empty file)
+++ trunk/profit/lib/widgets/ui_extendedshell.ui Thu Jul 31 00:06:40 2008
@@ -0,0 +1,102 @@
+<ui version="4.0" >
+ <class>ExtendedShell</class>
+ <widget class="QWidget" name="ExtendedShell" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>471</width>
+ <height>388</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2" >
+ <property name="topMargin" >
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QSplitter" name="splitter" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <widget class="QFrame" name="shellFrame" >
+ <layout class="QVBoxLayout" name="verticalLayout_3" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="PythonShell" native="1" name="shellWidget" />
+ </item>
+ </layout>
+ </widget>
+ <widget class="QFrame" name="editorFrame" >
+ <layout class="QVBoxLayout" name="verticalLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="SourceEditor" native="1" name="editorWidget" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
+ <horstretch>0</horstretch>
+ <verstretch>1</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout" >
+ <item>
+ <spacer name="horizontalSpacer" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="execButton" >
+ <property name="text" >
+ <string>Execute</string>
+ </property>
+ <property name="icon" >
+ <iconset resource="profit.qrc" >
+ <normaloff>:/images/icons/misc.png</normaloff>:/images/icons/misc.png</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ <zorder>editorWidget</zorder>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>PythonShell</class>
+ <extends>QWidget</extends>
+ <header location="global" >profit.lib.widgets.shell.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
+ <class>SourceEditor</class>
+ <extends>QWidget</extends>
+ <header location="global" >profit.lib.widgets.sourceeditor.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources>
+ <include location="profit.qrc" />
+ </resources>
+ <connections/>
+</ui>
Added: trunk/profit/lib/widgets/ui_marketdatacontract.ui
==============================================================================
--- (empty file)
+++ trunk/profit/lib/widgets/ui_marketdatacontract.ui Thu Jul 31
00:06:40 2008
@@ -0,0 +1,235 @@
+<ui version="4.0" >
+ <class>Form</class>
+ <widget class="QWidget" name="Form" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>360</width>
+ <height>426</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout" >
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout" >
+ <item>
+ <widget class="QLabel" name="label" >
+ <property name="text" >
+ <string>TIcker Id:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSpinBox" name="tickerId" />
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="contract" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
+ <horstretch>0</horstretch>
+ <verstretch>2</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title" >
+ <string>Contract Description</string>
+ </property>
+ <property name="flat" >
+ <bool>false</bool>
+ </property>
+ <property name="checkable" >
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout" >
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label_3" >
+ <property name="text" >
+ <string>Symbol:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2" >
+ <widget class="QLineEdit" name="symbol" >
+ <property name="text" >
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label_8" >
+ <property name="text" >
+ <string>Local Symbol:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2" >
+ <widget class="QLineEdit" name="localSymbol" >
+ <property name="text" >
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="label_6" >
+ <property name="text" >
+ <string>Exchange:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2" >
+ <widget class="QLineEdit" name="exchange" >
+ <property name="text" >
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" colspan="2" >
+ <widget class="QLabel" name="label_7" >
+ <property name="text" >
+ <string>Primary Exchange:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="2" >
+ <widget class="QLineEdit" name="primaryExchange" >
+ <property name="text" >
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0" >
+ <widget class="QLabel" name="label_9" >
+ <property name="text" >
+ <string>Currency:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="2" >
+ <widget class="QComboBox" name="comboBox" >
+ <item>
+ <property name="text" >
+ <string>USD</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>EUR</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="5" column="0" >
+ <widget class="QLabel" name="label_2" >
+ <property name="text" >
+ <string>Security Type:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="2" >
+ <widget class="QComboBox" name="secType" >
+ <item>
+ <property name="text" >
+ <string>STK</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>OPT</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>FUT</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="6" column="0" >
+ <widget class="QLabel" name="label_11" >
+ <property name="text" >
+ <string>Multiplier:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="2" >
+ <widget class="QSpinBox" name="multiplier" />
+ </item>
+ <item row="7" column="0" >
+ <widget class="QLabel" name="label_4" >
+ <property name="text" >
+ <string>Expiry:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="2" >
+ <widget class="QLineEdit" name="expiry" >
+ <property name="text" >
+ <string>YYYYMM</string>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="0" >
+ <widget class="QLabel" name="label_5" >
+ <property name="text" >
+ <string>Strike:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="2" >
+ <widget class="QDoubleSpinBox" name="strike" />
+ </item>
+ <item row="9" column="2" >
+ <widget class="QComboBox" name="comboBox_2" >
+ <item>
+ <property name="text" >
+ <string>CALL</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>PUT</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="9" column="0" >
+ <widget class="QLabel" name="label_10" >
+ <property name="text" >
+ <string>Right:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ <zorder>secType</zorder>
+ <zorder>label_2</zorder>
+ <zorder>label_3</zorder>
+ <zorder>symbol</zorder>
+ <zorder>label_4</zorder>
+ <zorder>expiry</zorder>
+ <zorder>label_5</zorder>
+ <zorder>strike</zorder>
+ <zorder>label_6</zorder>
+ <zorder>exchange</zorder>
+ <zorder>primaryExchange</zorder>
+ <zorder>label_8</zorder>
+ <zorder>primaryExchange_2</zorder>
+ <zorder>comboBox</zorder>
+ <zorder>strike</zorder>
+ <zorder>multiplier</zorder>
+ <zorder>label_11</zorder>
+ <zorder>label_9</zorder>
+ <zorder>label_7</zorder>
+ <zorder>localSymbol</zorder>
+ <zorder>comboBox_2</zorder>
+ <zorder>label_10</zorder>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
Modified: trunk/profit/neuralnetdesigner/network_test.py
==============================================================================
--- trunk/profit/neuralnetdesigner/network_test.py (original)
+++ trunk/profit/neuralnetdesigner/network_test.py Thu Jul 31 00:06:40 2008
@@ -11,7 +11,7 @@
from PyQt4.QtGui import (QApplication, QFrame, QIcon,
QStandardItem, QStandardItemModel, QToolBar)
-from profit.lib import SessionHandler
+from profit.lib import BasicHandler
from profit.lib import Signals, tickerIdRole
from profit.lib.widgets.ui_breadfan_network import Ui_NetworkControlFrame
Modified: trunk/profit/neuralnetdesigner/test.py
==============================================================================
--- trunk/profit/neuralnetdesigner/test.py (original)
+++ trunk/profit/neuralnetdesigner/test.py Thu Jul 31 00:06:40 2008
@@ -11,7 +11,7 @@
from PyQt4.QtGui import (QApplication, QFrame, QIcon,
QStandardItem, QStandardItemModel)
-from profit.lib import SessionHandler
+from profit.lib import BasicHandler
from profit.lib import Signals, tickerIdRole
from profit.neuralnetdesigner.widgets.ui_test import Ui_TestTree
@@ -117,9 +117,9 @@
tree.header().hide()
tree.setAnimated(True)
app = QApplication.instance()
- connect(tree, Signals.modelClicked, app, Signals.sessionItemSelected)
+ connect(tree, Signals.modelClicked, app, Signals.session.itemSelected)
connect(tree, Signals.modelDoubleClicked,
- app, Signals.sessionItemActivated)
+ app, Signals.session.itemActivated)
def setSession(self, session):
Modified: trunk/profit/neuralnetdesigner/train_test.py
==============================================================================
--- trunk/profit/neuralnetdesigner/train_test.py (original)
+++ trunk/profit/neuralnetdesigner/train_test.py Thu Jul 31 00:06:40 2008
@@ -11,7 +11,7 @@
from PyQt4.QtGui import (QApplication, QFrame, QIcon,
QStandardItem, QStandardItemModel)
-from profit.lib import SessionHandler
+from profit.lib import BasicHandler
from profit.lib import Signals, tickerIdRole
from profit.lib.widgets.ui_breadfan_train import Ui_BreadFanTrainTree
Modified: trunk/profit/strategydesigner/main.py
==============================================================================
--- trunk/profit/strategydesigner/main.py (original)
+++ trunk/profit/strategydesigner/main.py Thu Jul 31 00:06:40 2008
@@ -102,8 +102,8 @@
else:
self.resetWindowTitle()
self.connect(
- self, Signals.strategyFileUpdated,
- QApplication.instance(), Signals.strategyFileUpdated)
+ self, Signals.strategy.fileUpdated,
+ QApplication.instance(), Signals.strategy.fileUpdated)
# index parameter and documentation group methods
@@ -1061,7 +1061,7 @@
self, 'Error', 'Unable to save schema file.')
else:
self.setWindowModified(False)
- self.emit(Signals.strategyFileUpdated, self.strategyFile)
+ self.emit(Signals.strategy.fileUpdated, self.strategyFile)
finally:
handle.close()
Modified: trunk/profit/workbench/accountdisplay.py
==============================================================================
--- trunk/profit/workbench/accountdisplay.py (original)
+++ trunk/profit/workbench/accountdisplay.py Thu Jul 31 00:06:40 2008
@@ -7,7 +7,7 @@
from PyQt4.QtCore import QAbstractTableModel, QSize, QVariant, Qt
from PyQt4.QtGui import QFrame, QStandardItemModel, QStandardItem
-from profit.lib import SessionHandler, Signals, valueAlign
+from profit.lib import BasicHandler, Signals, valueAlign
from profit.lib.gui import colorIcon, complementColor
from profit.lib.series import Series
from profit.lib.widgets.plot import PlotCurve, ControlTreeValueItem
@@ -53,7 +53,7 @@
items[2].setText(message.value)
-class AccountDisplay(QFrame, Ui_AccountDisplay, SessionHandler):
+class AccountDisplay(QFrame, Ui_AccountDisplay, BasicHandler):
""" AccountDisplay -> displays account data and associated plot controls.
"""
@@ -77,12 +77,12 @@
self.dataModel = model = AccountTableModel(session, self)
plot = self.plot
plot.plotButton.setVisible(False)
- plot.setSessionPlot(session, session.dataMaps.account, 'account')
+ plot.setSessionPlot(session, session.maps.account, 'account')
plot.controlsTreeModel = model
plot.controlsTree.setModel(model)
plot.controlsTree.header().show()
- for key, series in session.dataMaps.account.items():
- value = session.dataMaps.account.last.get(key, None)
+ for key, series in session.maps.account.items():
+ value = session.maps.account.last.get(key, None)
self.newPlotSeries(key, series, value)
connect = self.connect
connect(session, Signals.createdAccountData, self.newPlotSeries)
Modified: trunk/profit/workbench/centraltabs.py
==============================================================================
--- trunk/profit/workbench/centraltabs.py (original)
+++ trunk/profit/workbench/centraltabs.py Thu Jul 31 00:06:40 2008
@@ -12,14 +12,14 @@
from PyQt4.QtGui import QIcon, QTabWidget, QStandardItem
from profit.lib import importItem, logging
-from profit.lib import SessionHandler, Signals, DataRoles, instance
+from profit.lib import BasicHandler, Signals, DataRoles, instance
from profit.lib.gui import addCloseAction, makeUrlItem
from profit.lib.widgets.buttons import CloseTabButton, DetachTabButton
from profit.lib.widgets.webbrowser import WebBrowserDisplay
from profit.workbench.tickerplotdisplay import TickerPlotDisplay
-class CentralTabs(QTabWidget, SessionHandler):
+class CentralTabs(QTabWidget, BasicHandler):
""" CentralTabs -> tab widget with special powers
"""
@@ -39,7 +39,7 @@
self.setCornerWidget(self.closeTabButton, Qt.TopRightCorner)
self.setCornerWidget(self.detachTabButton, Qt.TopLeftCorner)
app, connect = instance(), self.connect
- connect(app, Signals.sessionItemActivated, self.createTab)
+ connect(app, Signals.itemActivated, self.createTab)
connect(app, Signals.openUrl, self.createTab)
connect(app, Signals.tickerClicked, self.createTab)
connect(self.closeTabButton, Signals.clicked, self.closeTab)
@@ -94,7 +94,7 @@
if tickerIdValid:
widget = TickerPlotDisplay(self)
session = self.session
- widget.setSessionPlot(session, session.dataMaps.ticker, tickerId)
+ widget.setSessionPlot(session, session.maps.ticker, tickerId)
index = self.addTab(widget, symbol)
icon = QIcon(item.data(Qt.DecorationRole))
self.setTextIconCurrentTab(index, symbol, icon)
Modified: trunk/profit/workbench/connectiondisplay.py
==============================================================================
--- trunk/profit/workbench/connectiondisplay.py (original)
+++ trunk/profit/workbench/connectiondisplay.py Thu Jul 31 00:06:40 2008
@@ -18,8 +18,7 @@
from PyQt4 import QtCore, QtGui
from PyQt4.Qwt5 import QwtThermo; QtGui.QwtThermo = QwtThermo
-from profit.lib import defaults, logging
-from profit.lib import SessionHandler, SettingsHandler, Signals
+from profit.lib import BasicHandler, Signals, defaults, logging
from profit.workbench.widgets.ui_connectionwidget import Ui_ConnectionWidget
@@ -48,7 +47,7 @@
return keyCmd, brokerCmd
-class ConnectionDisplay(QFrame, Ui_ConnectionWidget, SessionHandler, SettingsHandler):
+class ConnectionDisplay(QFrame, Ui_ConnectionWidget, BasicHandler):
""" ConnectionDisplay -> widgets for managing broker connection.
"""
@@ -87,7 +86,7 @@
"""
session = self.session
session.deregisterAll(self.updateLastMessage)
- self.disconnect(session, Signals.connectedTWS, self.on_connectedTWS)
+ self.disconnect(session, Signals.tws.connected, self.on_connectedTWS)
def on_session_ConnectionClosed(self, message):
""" Resets various widgets after a connection closed message.
Modified: trunk/profit/workbench/executionsdisplay.py
==============================================================================
--- trunk/profit/workbench/executionsdisplay.py (original)
+++ trunk/profit/workbench/executionsdisplay.py Thu Jul 31 00:06:40 2008
@@ -9,7 +9,7 @@
from PyQt4.QtCore import QAbstractTableModel, QVariant, Qt
from PyQt4.QtGui import QFrame
-from profit.lib import SessionHandler, Signals, Slots, makeCheckNames, valueAlign
+from profit.lib import BasicHandler, Signals, Slots, makeCheckNames, valueAlign
from profit.lib.gui import symbolIcon
from profit.workbench.widgets.ui_executionsdisplay import Ui_ExecutionsDisplay
@@ -159,7 +159,7 @@
return len(self.columnTitles)
-class ExecutionsDisplay(QFrame, Ui_ExecutionsDisplay, SessionHandler):
+class ExecutionsDisplay(QFrame, Ui_ExecutionsDisplay, BasicHandler):
""" Combines a filter bar and an exec details table.
"""
Modified: trunk/profit/workbench/historicaldatadisplay.py
==============================================================================
--- trunk/profit/workbench/historicaldatadisplay.py (original)
+++ trunk/profit/workbench/historicaldatadisplay.py Thu Jul 31 00:06:40 2008
@@ -9,7 +9,7 @@
from PyQt4.QtCore import Qt
from PyQt4.QtGui import QFrame, QIcon
-from profit.lib import SessionHandler, makeCheckNames
+from profit.lib import BasicHandler, makeCheckNames
from profit.lib.gui import ValueTableItem
from profit.workbench.widgets.ui_historicaldatadisplay import Ui_HistoricalDataDisplay
@@ -22,7 +22,7 @@
callback(message)
-class HistoricalDataDisplay(QFrame, Ui_HistoricalDataDisplay, SessionHandler):
+class HistoricalDataDisplay(QFrame, Ui_HistoricalDataDisplay, BasicHandler):
def __init__(self, parent=None):
QFrame.__init__(self, parent)
self.setupUi(self)
Modified: trunk/profit/workbench/main.py
==============================================================================
--- trunk/profit/workbench/main.py (original)
+++ trunk/profit/workbench/main.py Thu Jul 31 00:06:40 2008
@@ -31,6 +31,8 @@
from profit.lib.widgets.propertyeditor import PropertyEditor
from profit.lib.widgets.shell import PythonShell
+from profit.lib.widgets.extendedshell import ExtendedPythonShell
+
from profit.workbench.widgets.ui_main import Ui_ProfitWorkbenchWindow
from profit.workbench.sessiontree import SessionTree
@@ -61,12 +63,12 @@
self.readSettings()
self.setWindowTitle('%s (0.2 alpha)' % applicationName())
app = instance()
- sessreq = lambda :app.emit(Signals.sessionReference, self.session)
+ sessreq = lambda :app.emit(Signals.session.reference, self.session)
connect = self.connect
- connect(app, Signals.sessionRequest, sessreq)
+ connect(app, Signals.session.request, sessreq)
connect(app, Signals.lastWindowClosed, self.writeSettings)
connect(self, Signals.openUrl, app, Signals.openUrl)
- connect(self, Signals.sessionCreated, app, Signals.sessionCreated)
+ connect(self, Signals.session.created, app, Signals.session.created)
connect(self, Signals.settingsChanged, self.setupColors)
connect(self, Signals.settingsChanged, self.setupSysTray)
self.createSession()
@@ -138,9 +140,9 @@
def createSession(self):
self.session = session = Session()
app = instance()
- app.emit(Signals.sessionCreated, session)
+ app.emit(Signals.session.created, session)
bar = self.statusBar()
- self.connect(session, Signals.sessionStatus, bar.showMessage)
+ self.connect(session, Signals.session.status, bar.showMessage)
@pyqtSignature('')
def on_actionAboutProfitWorkbench_triggered(self):
@@ -413,10 +415,11 @@
tabify(self.propertyEditor, self.sessionDock)
self.stdoutDock = Dock('Standard Output', self, OutputWidget, bottom)
self.stderrDock = Dock('Standard Error', self, OutputWidget, bottom)
- makeShell = partial(
- PythonShell,
- stdout=self.stdoutDock.widget(),
- stderr=self.stderrDock.widget())
+ def makeShell(p):
+ shell = ExtendedPythonShell(p)
+ shell.shellWidget.setStdOutErr(stdout=self.stdoutDock.widget(),
+ stderr=self.stderrDock.widget())
+ return shell
self.shellDock = Dock('Python Shell', self, makeShell, bottom)
tabify(self.shellDock, self.stdoutDock)
tabify(self.stdoutDock, self.stderrDock)
@@ -465,7 +468,7 @@
for action in self.menuFile.actions():
trayMenu.addAction(action)
trayIcon.setContextMenu(trayMenu)
- self.connect(trayIcon, Signals.activated,
+ self.connect(trayIcon, Signals.trayIconActivated,
self.on_trayIcon_activated)
trayIcon.show()
else:
Modified: trunk/profit/workbench/messagedisplay.py
==============================================================================
--- trunk/profit/workbench/messagedisplay.py (original)
+++ trunk/profit/workbench/messagedisplay.py Thu Jul 31 00:06:40 2008
@@ -12,7 +12,7 @@
from ib.opt.message import messageTypeNames
-from profit.lib import SessionHandler, SettingsHandler, Slots, defaults
+from profit.lib import BasicHandler, Slots, defaults
from profit.lib.gui import colorIcon
from profit.lib.models.messages import MessagesTableModel
from profit.workbench.widgets.ui_messagedisplay import Ui_MessageDisplay
@@ -84,7 +84,7 @@
self.reset()
-class MessageDisplay(QFrame, Ui_MessageDisplay, SessionHandler, SettingsHandler):
+class MessageDisplay(QFrame, Ui_MessageDisplay, BasicHandler):
""" MessageDisplay -> table view of session messages with nifty controls
"""
Modified: trunk/profit/workbench/orderdisplay.py
==============================================================================
--- trunk/profit/workbench/orderdisplay.py (original)
+++ trunk/profit/workbench/orderdisplay.py Thu Jul 31 00:06:40 2008
@@ -9,7 +9,7 @@
from PyQt4.QtCore import Qt
from PyQt4.QtGui import QFrame
-from profit.lib import SessionHandler, makeCheckNames
+from profit.lib import BasicHandler, makeCheckNames
from profit.workbench.widgets.ui_orderdisplay import Ui_OrderDisplay
## TODO: orders should be displayed in a parent/child relationship,
@@ -29,7 +29,7 @@
calls[message.typeName](message)
-class OrderDisplay(QFrame, Ui_OrderDisplay, SessionHandler):
+class OrderDisplay(QFrame, Ui_OrderDisplay, BasicHandler):
""" OrderDisplay -> table of orders
"""
Modified: trunk/profit/workbench/portfoliodisplay.py
==============================================================================
--- trunk/profit/workbench/portfoliodisplay.py (original)
+++ trunk/profit/workbench/portfoliodisplay.py Thu Jul 31 00:06:40 2008
@@ -9,7 +9,7 @@
from PyQt4.QtCore import Qt
from PyQt4.QtGui import QFrame, QIcon
-from profit.lib import SessionHandler, makeCheckNames
+from profit.lib import BasicHandler, makeCheckNames
from profit.lib.gui import ValueTableItem
from profit.workbench.widgets.ui_portfoliodisplay import Ui_PortfolioDisplay
@@ -25,7 +25,7 @@
break
-class PortfolioDisplay(QFrame, Ui_PortfolioDisplay, SessionHandler):
+class PortfolioDisplay(QFrame, Ui_PortfolioDisplay, BasicHandler):
def __init__(self, parent=None):
QFrame.__init__(self, parent)
self.setupUi(self)
Modified: trunk/profit/workbench/sessiontree.py
==============================================================================
--- trunk/profit/workbench/sessiontree.py (original)
+++ trunk/profit/workbench/sessiontree.py Thu Jul 31 00:06:40 2008
@@ -15,8 +15,7 @@
from profit.lib import defaults, logging
from profit.lib.gui import UrlRequestor, makeUrlAction
-from profit.lib import SessionHandler, SettingsHandler
-from profit.lib import Settings, Signals, DataRoles
+from profit.lib import BasicHandler, Settings, Signals, DataRoles
from profit.workbench.widgets.ui_sessiontree import Ui_SessionTree
@@ -203,8 +202,7 @@
formatHistDataError = mkHistDataFormatter('request %s (%s/%s) (error)')
-class SessionTree(QFrame, Ui_SessionTree, SessionHandler,
- SettingsHandler, UrlRequestor):
+class SessionTree(QFrame, Ui_SessionTree, BasicHandler, UrlRequestor):
""" Tree view of a Session object.
"""
@@ -222,14 +220,13 @@
app = QApplication.instance()
connect = self.connect
connect(self, Signals.openUrl, app, Signals.openUrl)
- connect(self, Signals.sessionItemActivated,
- app, Signals.sessionItemActivated)
+ connect(self, Signals.itemActivated, app, Signals.itemActivated)
self.requestSession()
def on_treeView_doubleClicked(self, index):
#print '### index:', index
## set more data
- self.emit(Signals.sessionItemActivated, index)
+ self.emit(Signals.itemActivated, index)
def histDataItem(self):
""" returns the 'historical data' item or None
@@ -290,6 +287,10 @@
view = self.treeView
view.setModel(model)
session.registerMeta(self)
+ self.connect(session, Signals.histdata.start,
+ self.on_session_historicalDataStart)
+ self.connect(session, Signals.histdata.finish,
+ self.on_session_historicalDataFinish)
if not session.messages:
settings = self.settings
settings.beginGroup(settings.keys.main)
Modified: trunk/profit/workbench/strategydisplay.py
==============================================================================
--- trunk/profit/workbench/strategydisplay.py (original)
+++ trunk/profit/workbench/strategydisplay.py Thu Jul 31 00:06:40 2008
@@ -13,7 +13,7 @@
QStandardItemModel, QFileDialog, )
from profit.lib import defaults, logging
-from profit.lib import SessionHandler, SettingsHandler, Signals,
DataRoles, instance
+from profit.lib import BasicHandler, Signals, DataRoles, instance
from profit.lib.gui import StandardItem
from profit.workbench.widgets.ui_strategydisplay import Ui_StrategyDisplay
@@ -77,8 +77,7 @@
]
-class StrategyDisplay(QFrame, Ui_StrategyDisplay, SessionHandler,
- SettingsHandler):
+class StrategyDisplay(QFrame, Ui_StrategyDisplay, BasicHandler):
"""
"""
@@ -117,8 +116,8 @@
self.on_strategyTable_itemChanged)
connect(view.selectionModel(), Signals.selectionChanged,
self.on_strategyTable_selectionChanged)
- connect(self, Signals.strategyRequestActivate,
- instance(), Signals.strategyRequestActivate)
+ connect(self, Signals.strategy.requestActivate,
+ instance(), Signals.strategy.requestActivate)
def on_strategyTable_doubleClicked(self, index):
"""
@@ -157,7 +156,7 @@
return
## emit the signal for all activate/deactivate changes
rowdict = self.strategyModel.rowToDict(item.row())
- self.emit(Signals.strategyRequestActivate, rowdict, bool(checked))
+ self.emit(Signals.strategy.requestActivate, rowdict, bool(checked))
## house keeping common for all activate/deactivate
other = self.strategyModel.item(item.row(), 1)
other.setIcon(self.activeIcon if checked else self.inactiveIcon)
Modified: trunk/profit/workbench/tickerdisplay.py
==============================================================================
--- trunk/profit/workbench/tickerdisplay.py (original)
+++ trunk/profit/workbench/tickerdisplay.py Thu Jul 31 00:06:40 2008
@@ -4,9 +4,6 @@
# Copyright 2007 Troy Melhase <tr...@gci.net>
# Distributed under the terms of the GNU General Public License v2
-# TODO: cache previous values on column drop and reuse on add.
-# TODO: support id, symbol, position and value fields
-
from functools import partial
from itertools import ifilter
from string import Template
@@ -16,14 +13,12 @@
from ib.opt.message import TickPrice
-from profit.lib import (BasicHandler, DataRoles, Signals, defaults,
- instance, makeCheckNames, )
-
-from profit.lib.gui import (UrlRequestor, ValueTableItem, separator,
- makeUrlAction, )
-
-from profit.lib.widgets.tickfieldselect import (fieldIds, itemTickField,
- setItemTickField, )
+from profit.lib import (
+ BasicHandler, DataRoles, Signals, defaults, instance,
makeCheckNames, )
+from profit.lib.gui import (
+ UrlRequestor, ValueTableItem, separator, makeUrlAction, )
+from profit.lib.widgets.tickfieldselect import (
+ ExField, fieldIds, itemTickField, setItemTickField, )
from profit.workbench.portfoliodisplay import replayPortfolio
from profit.workbench.widgets.ui_tickerdisplay import Ui_TickerDisplay
@@ -43,7 +38,7 @@
def pred((t, m)):
return isMsg(m) and m.field==field and m.tickerId==tickerId
for time, message in ifilter(pred, reversed(messages)):
- callback(message)
+ callback(message, replay=True)
break
@@ -58,6 +53,9 @@
yield tick(field=field)
+valueCache = {}
+
+
class TickerDisplay(QFrame, Ui_TickerDisplay, BasicHandler, UrlRequestor):
""" TickerDisplay -> shows ticker data in a nice table.
@@ -70,8 +68,13 @@
"""
QFrame.__init__(self, parent)
self.setupUi(self)
- self.headerItemColumnMap = {}
self.tickerIds = {}
+ self.extraFieldItemSetups = {
+ ExField.tid : self.setIdItem,
+ ExField.sym : self.setSymbolItem,
+ ExField.pos : self.setPositionItem,
+ ExField.val : self.setPositionValueItem,
+ }
self.setupWidgets()
self.requestSession()
@@ -94,18 +97,6 @@
connect(self, Signals.tickerClicked, app, Signals.tickerClicked)
self.tickerTable.verticalHeader().hide()
- def on_splitter_splitterMoved(self, pos, index):
- """ Signal handler for splitter move; saves state to user settings.
-
- @param pos ignored
- @param index ignored
- @return None
- """
- settings = self.settings
- settings.beginGroup(self.__class__.__name__)
- settings.setValue(settings.keys.splitstate, self.splitter.saveState())
- settings.endGroup()
-
def setSession(self, session):
""" Configures this instance for a session.
@@ -114,14 +105,14 @@
"""
self.session = session
symbols = session.strategy.symbols()
- replayTickerMessages(session.messages, symbols,
- self.on_session_TickPrice_TickSize)
- replayPortfolio(session.messages, self.on_session_UpdatePortfolio)
+ #replayTickerMessages(session.messages, symbols,
+ # self.on_session_TickPrice_TickSize)
+ #replayPortfolio(session.messages, self.on_session_UpdatePortfolio)
session.registerMeta(self)
- if not session.messages:
- for tickerId in symbols.values():
- for msg in fakeTickerMessages(tickerId):
- self.on_session_TickPrice_TickSize(msg)
+# if not session.messages:
+# for tickerId in symbols.values():
+# for msg in fakeTickerMessages(tickerId):
+# self.on_session_TickPrice_TickSize(msg)
def basicActions(self, index):
""" Creates action and separator list suitable for a context menu.
@@ -150,27 +141,6 @@
"""
print '## close position order dialog'
- def urlActions(self, symbol):
- """
-
- """
- actions = []
- settings = self.settings
- settings.beginGroup(self.settings.keys.urls)
- urls = settings.value(settings.keys.tickerurls, defaults.tickerUrls())
- settings.endGroup()
- urls = [str(s) for s in defaults.tickerUrls()]
- for url in urls:
- try:
- name, url = str(url).split(':', 1)
- url = Template(url).substitute(symbol=symbol)
- except (KeyError, ValueError, ):
- continue
- act = makeUrlAction(name, url, toolTip='%s %s' % (symbol, name))
- self.connect(act, Signals.triggered,
partial(self.requestUrl, action=act))
- actions.append(act)
- return actions
-
def closePositionAction(self, row):
""" Creates an action for closing a position.
@@ -189,6 +159,90 @@
self.connect(act, Signals.triggered, self.closePosition)
return act
+ def fieldColumn(self, field, default=None):
+ """ Returns the ticker table column number for the given field.
+
+ This method could move to a TickerTable(QTableWidget) class.
+
+ @param field TickType field
+ @param default=None value returned if column not found
+ """
+ table = self.tickerTable
+ for col in range(table.columnCount()):
+ if field == itemTickField(table.horizontalHeaderItem(col)):
+ return col
+ return default
+
+ def makeTickerColumn(self, field, label):
+ """ Constructs a new column and a header for the ticker table.
+
+ @param field TickType field
+ @param label header label
+ @return new column number
+ """
+ table = self.tickerTable
+ column = table.columnCount()
+ table.insertColumn(column)
+ header = ValueTableItem()
+ header.setText(label)
+ setItemTickField(header, field)
+ table.setHorizontalHeaderItem(column, header)
+ return column
+
+ def makeTickerColumnItems(self, column):
+ """ Creates ticker table items for (new) column.
+
+ @param column table column
+ @return None
+ """
+ table = self.tickerTable
+ for row in range(table.rowCount()):
+ item = ValueTableItem()
+ item.setValueAlign()
+ table.setItem(row, column, item)
+
+ def setupFieldColumn(self, field, column):
+ """ Configures column items as much as possible.
+
+ This method maps existing ticker fields to items at the given
+ column. We don't mix this behavior with the column
+ construction (makeTickerColumnItems) because that would muddle the
+ behavior.
+
+ @param field TickType field
+ @param column table column
+ @return None
+ """
+ extraFieldItemSetups = self.extraFieldItemSetups
+ tickerTable = self.tickerTable
+ for tickerId, row in self.tickerIds.items():
+ item = tickerTable.item(row, column)
+ if item:
+ if field in extraFieldItemSetups:
+ extraFieldItemSetups[field](item, tickerId)
+ else:
+ value = valueCache.get(tickerId, {}).get(field, '')
+ item.setValue(value)
+
+ def makeTickerRow(self, tickerId):
+ """ Creates a ticker table row for the given tickerId.
+
+ @param tickerId yes, that
+ @return id of new row
+
+ """
+ table = self.tickerTable
+ items = table.newItemsRow()
+ extraFieldItemSetups = self.extraFieldItemSetups
+ for col, item in enumerate(items):
+ item.setValueAlign()
+ field = itemTickField(table.horizontalHeaderItem(col))
+ if field in extraFieldItemSetups:
+ extraFieldItemSetups[field](item, tickerId)
+ table.resizeColumnsToContents()
+ table.resizeRowsToContents()
+ return table.rowCount()
+
@pyqtSignature('')
def on_actionChart_triggered(self):
""" Emits a signal for a ticker chart.
@@ -211,46 +265,79 @@
"""
print '## order for ', self.actionOrder.data().toString()
- def tickerTableColumns(self):
- t = self.tickerTable
- return [(c, t.horizontalHeaderItem(c))
- for c in range(t.columnCount())]
-
- def tickerTableColumnField(self, field):
- t = self.tickerTable
- for c in range(t.columnCount()):
- i = t.horizontalHeaderItem(c)
- if field == itemTickField(i):
- return c
-
def on_fieldsList_itemChanged(self, item):
""" Add/drop a column when a field is checked/unchecked.
"""
- headerItemColumnMap = self.headerItemColumnMap
- tickerTable = self.tickerTable
- userItems = self.tickFieldSelect.checkedItems()
+ table = self.tickerTable
field = itemTickField(item)
-
if item.checkState():
- col = tickerTable.columnCount()
- tickerTable.insertColumn(col)
- header = headerItemColumnMap[field] = ValueTableItem()
- header.setText(item.text())
- setItemTickField(header, field)
- tickerTable.setHorizontalHeaderItem(col, header)
- for r in range(tickerTable.rowCount()):
- tickerTable.setItem(r, col, TickerTableItem())
+ col = self.makeTickerColumn(field, item.text())
+ self.makeTickerColumnItems(col)
+ self.setupFieldColumn(field, col)
+ table.resizeColumnToContents(col)
else:
- headers = self.tickerTableColumns()
- col = [c for c, i in headers if itemTickField(i)==field][0]
- tickerTable.removeColumn(col)
- del(headerItemColumnMap[field])
+ table.removeColumn(self.fieldColumn(field))
+ self.saveFieldSelections()
+
+ def on_session_UpdatePortfolio(self, message):
+ """ Updates the position and market value columns in the
ticker table.
+
+ """
+
+ ## TODO: fix references, i.e., make contract lookup precise,
+ ## and also locate column by contract (or
+ ## by message.contract symbol+secType+expiry+etc)
+
+ sym = message.contract.m_symbol
+ symbols = self.session.strategy.symbols()
+ try:
+ tid = symbols[sym]
+ row = self.tickerIds[tid]
+ except (KeyError, ):
+ return
+ items = ((ExField.val, message.marketValue),
+ (ExField.pos, message.position),
+ )
+ table = self.tickerTable
+ for field, value in items:
+ col = self.fieldColumn(field)
+ if col is not None:
+ item = table.item(row, col)
+ if item:
+ item.setValue(value)
+
+ def on_session_TickPrice_TickSize(self, message):
+ """ Updates size and price columns in the ticker table.
+ Creates rows as needed.
+
+ """
+ field = message.field
+ value = (message.price if hasattr(message, 'price') else message.size)
+ tickerTable = self.tickerTable
+ tickerId = message.tickerId
+ valueCache.setdefault(tickerId, {})[field] = value
+ col = self.fieldColumn(field)
+ if col is None:
+ return
+ try:
+ row = self.tickerIds[tickerId]
+ except (KeyError, ):
+ row = self.tickerIds[tickerId] = self.makeTickerRow(tickerId)
+ item = tickerTable.item(row, col)
+ if item:
+ item.setValue(value)
+ def on_splitter_splitterMoved(self, pos, index):
+ """ Signal handler for splitter move; saves state to user settings.
+
+ @param pos ignored
+ @param index ignored
+ @return None
+ """
settings = self.settings
settings.beginGroup(self.__class__.__name__)
- saveFields = [itemTickField(i) for i in userItems]
- settings.setValueDump('selectedFields', saveFields)
+ settings.setValue(settings.keys.splitstate, self.splitter.saveState())
settings.endGroup()
def on_tickerTable_customContextMenuRequested(self, pos):
@@ -292,86 +379,67 @@
elif (2 < col < 9):
self.emit(Signals.tickerClicked, item, col)
- def on_session_UpdatePortfolio(self, message):
- """ Updates the position and market value columns in the
ticker table.
+ def saveFieldSelections(self):
+ """ Saves the selected fields.
"""
- sym = message.contract.m_symbol
- symbols = self.session.strategy.symbols()
- try:
- tid = symbols[sym]
- items = self.tickerIds[tid]
- except (KeyError, ):
- pass
- else:
- items[1].setValue(message.position)
- items[2].setValue(message.marketValue)
+ settings = self.settings
+ settings.beginGroup(self.__class__.__name__)
+ userItems = self.tickFieldSelect.checkedItems()
+ saveFields = [itemTickField(i) for i in userItems]
+ settings.setValueDump('selectedFields', saveFields)
+ settings.endGroup()
- def on_session_TickPrice_TickSize(self, message):
- """ Updates size and price columns in the ticker table.
- Creates rows as needed.
+ def urlActions(self, symbol):
+ """ Returns a list of actions for the given symbol.
"""
- table = self.tickerTable
- if not table.columnCount():
- return
- tickerId = message.tickerId
- value = (message.price if hasattr(message, 'price') else message.size)
- try:
- row = self.tickerIds[tickerId]
- except (KeyError, ):
- items = table.newItemsRow()
- #for item in items:
- # item.setValueAlign()
- row = self.tickerIds[tickerId] = items[0].row()
- col = self.tickerTableColumnField(message.field)
- if col is None:
- return
- item = table.item(row, col)
- if not hasattr(item, 'setValue'):
- return
- if item:
- item.setValue(value)
-
-
-class TickerTableItem(ValueTableItem):
- """ Automatically aligned value table items.
-
- """
- def __init__(self):
- ValueTableItem.__init__(self)
- self.setValueAlign()
-
-
-
+ actions = []
+ settings = self.settings
+ settings.beginGroup(self.settings.keys.urls)
+ urls = settings.value(settings.keys.tickerurls, defaults.tickerUrls())
+ settings.endGroup()
+ urls = [str(s) for s in defaults.tickerUrls()]
+ for url in urls:
+ try:
+ name, url = str(url).split(':', 1)
+ url = Template(url).substitute(symbol=symbol)
+ except (KeyError, ValueError, ):
+ continue
+ act = makeUrlAction(name, url, toolTip='%s %s' % (symbol, name))
+ request = partial(self.requestUrl, action=act)
+ self.connect(act, Signals.triggered, request)
+ actions.append(act)
+ return actions
+ ## table item setter-uppers
+ def setIdItem(self, item, tickerId):
+ """ Configures an item for the 'Id' column.
- #table.resizeColumnsToContents()
- #table.resizeRowsToContents()
+ """
+ item.setText(tickerId)
+ item.setValueAlign(Qt.AlignLeft|Qt.AlignVCenter)
-if 0:
- if 0:
- if 0:
- symbols = self.session.strategy.symbols()
- try:
- sym = dict([(b, a) for a, b in symbols.items()])[tickerId]
- except (KeyError, ):
- ## something wrong -- we don't have data for the
- ## ticker symbol. this can happen if the connection
- ## sends tick messages and we don't have a strategy
- ## loaded with the symbol (tickerId) defined. how can
- ## this be fixed?
- return
+ def setSymbolItem(self, item, tickerId):
+ """ Configures an item for the 'Symbol' column.
+ """
+ symbols = self.session.strategy.symbols()
+ try:
+ sym = dict([(b, a) for a, b in symbols.items()])[tickerId]
+ except (KeyError, ):
+ pass
+ else:
+ item.setSymbol(sym)
+ item.setValueAlign(Qt.AlignLeft|Qt.AlignVCenter)
+ def setPositionItem(self, item, tickerId):
+ """ Configures an item for the 'Position' column.
+ """
-# items[0].setSymbol(sym)
-# items[0].tickerId = tid
-# for item in items[1:]:
-# item.setValueAlign()
-# table.sortItems(0)
-# table.resizeColumnsToContents()
-# table.resizeRowsToContents()
+ def setPositionValueItem(self, item, tickerId):
+ """ Configures an item for the 'Value' column.
+ """