[profitpy commit] r303 - in trunk: bin profit/lib profit/lib/models profit/lib/session profit/lib/widgets profit/wo...

1 view
Skip to first unread message

codesite...@google.com

unread,
Jul 22, 2008, 8:48:23 PM7/22/08
to profitp...@googlegroups.com
Author: troy.melhase
Date: Tue Jul 22 17:47:38 2008
New Revision: 303

Modified:
trunk/bin/hist_downloader
trunk/profit/lib/models/__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/widgets/plot.py
trunk/profit/workbench/connectiondisplay.py
trunk/profit/workbench/main.py

Log:
Crap crap crap.

Modified: trunk/bin/hist_downloader
==============================================================================
--- trunk/bin/hist_downloader (original)
+++ trunk/bin/hist_downloader Tue Jul 22 17:47:38 2008
@@ -150,11 +150,11 @@
sess.histdataQueue.put(('AAPL', 100))

sess.connect(sess, Signals.sessionStatus, logging.debug)
- sess.sessionFile = options.output
+ sess.filename = options.output
sess.connectTWS(
options.host, options.port, options.clientid)

- if not sess.isConnected:
+ if not sess.isConnected():
logging.error('Could not connect to %s:%s.',
options.host, options.port)
logging.error('Aborting.')

Modified: trunk/profit/lib/models/__init__.py
==============================================================================
--- trunk/profit/lib/models/__init__.py (original)
+++ trunk/profit/lib/models/__init__.py Tue Jul 22 17:47:38 2008
@@ -5,3 +5,61 @@
# Distributed under the terms of the GNU General Public License v2
# Author: Troy Melhase <tr...@gci.net>

+from PyQt4.QtCore import Qt, QModelIndex, QObject,
QAbstractTableModel, QVariant, QAbstractItemModel
+from profit.lib.core import Signals
+
+
+class ListStorage(QObject):
+ def __init__(self, parent):
+ QObject.__init__(self, parent)
+ self.storage = []
+
+ def __contains__(self, item):
+ return item in self.storage
+
+ def __getitem__(self, index):
+ return self.storage[index]
+
+ def __setitem__(self, index, value):
+ self.storage[index] = value
+
+ def __str__(self):
+ return str(self.storage)
+
+ def __len__(self):
+ return len(self.storage)
+
+ def append(self, item):
+ self.storage.append(item)
+
+
+class BasicModelMixin(object):
+ horizontalLabels = []
+ verticalLabels = []
+ sessionResendSignals = []
+
+ def headerData(self, section, orientation, role=Qt.DisplayRole):
+ if (orientation == Qt.Horizontal) and (role == Qt.DisplayRole):
+ return QVariant(self.horizontalLabels[section])
+ elif (orientation == Qt.Vertical) and (role == Qt.DisplayRole):
+ return QVariant(self.verticallabels[section])
+ return QVariant()
+
+ def columnCount(self, parent=QModelIndex()):
+ return len(self.horizontalLabels)
+
+ def rowCount(self, parent=QModelIndex()):
+ if parent.isValid():
+ return 0
+ return len(self)
+
+
+class BasicModel(QAbstractItemModel, BasicModelMixin):
+ def __init__(self, parent=None):
+ QAbstractItemModel.__init__(self, parent)
+
+
+class BasicTableModel(QAbstractTableModel, BasicModelMixin):
+ def __init__(self, parent=None):
+ QAbstractTableModel.__init__(self, parent)
+

Modified: trunk/profit/lib/scripttools.py
==============================================================================
--- trunk/profit/lib/scripttools.py (original)
+++ trunk/profit/lib/scripttools.py Tue Jul 22 17:47:38 2008
@@ -78,12 +78,12 @@
interval = options.interval * 60

self.session = session = Session(strategy=False)
- session.sessionFile = options.output
+ session.filename = options.output
self.connect(session, Signals.sessionStatus, logging.debug)

session.connectTWS(
options.host, options.port, options.clientid)
- if not session.isConnected:
+ if not session.isConnected():
logging.error('Could not connect to %s:%s.',
options.host, options.port)
logging.error('Aborting.')

Modified: trunk/profit/lib/session/__init__.py
==============================================================================
--- trunk/profit/lib/session/__init__.py (original)
+++ trunk/profit/lib/session/__init__.py Tue Jul 22 17:47:38 2008
@@ -6,38 +6,28 @@
# Author: Troy Melhase <tr...@gci.net>
# Yichun Wei <yichu...@gmail.com>

-import os
-import logging
-
-from cPickle import PicklingError, UnpicklingError, dump, load
-from itertools import ifilter
+from cPickle import UnpicklingError, load
from random import randint
-from time import time, strftime
-from Queue import Queue
+from time import time

-from PyQt4.QtCore import QObject, QMutex, QThread, SIGNAL
+from PyQt4.QtCore import QObject, SIGNAL

-from ib.ext.Contract import Contract
-from ib.ext.ExecutionFilter import ExecutionFilter
-from ib.ext.Order import Order
-from ib.ext.TickType import TickType
from ib.opt import ibConnection
-from ib.opt.message import registry
+from ib.opt.message import messageTypeNames
+from ib.ext.ExecutionFilter import ExecutionFilter

+from profit.lib import logging
from profit.lib.core import Signals
-from profit.lib.series import Series, MACDHistogram
-from profit.lib.session.collection import (AccountCollection,
- TickerCollection,
- HistoricalDataCollection, )
+from profit.lib.session import collection
from profit.lib.session.savethread import SaveThread
-try:
- from profit.lib.series import EMA, KAMA
-except (ImportError, ):
- EMA = KAMA = None
-
from profit.lib.strategy.builder import SessionStrategyBuilder

+from profit.lib.models import accountdata

+class SessionModels(object):
+ def __init__(self, session):
+ self.accountDataAll = accountdata.AllAccountData(session)
+ self.accountDataLast = accountdata.LatestAccountData(session)

class Session(QObject):
""" This is the big-honkin Session class.
@@ -45,84 +35,129 @@
"""
def __init__(self, strategy=None):
QObject.__init__(self)
- self.setObjectName('session')
self.strategy = strategy if strategy else SessionStrategyBuilder(self)
- self.connection = self.sessionFile = self.nextId = None
+ self.connection = self.filename = None
self.messages = []
self.bareMessages = []
self.savedLength = 0
self.typedMessages = {}
- self.accountCollection = ac = AccountCollection(self)
- self.tickerCollection = tc = TickerCollection(self)
- self.historicalDataCollection = hc = HistoricalDataCollection(self)
- connect = self.connect
+ self.tickerCollection = collection.TickerCollection(self)
+ self.historicalDataCollection = collection.HistoricalDataCollection(self)
+ self.models = SessionModels(self)

def __str__(self):
- fmt = '<Session 0x%x messages:%s connected:%s>'
- args = id(self), len(self.messages), int(self.isConnected)
- return fmt % args
+ """ x.__str__() <==> str(x)

- def disconnectTWS(self):
- if self.isConnected:
- self.connection.disconnect()
- self.emit(Signals.disconnectedTWS)
+ @return string representation of this object
+ """
+ format = '<Session 0x%x messages:%s connected:%s>'
+ args = id(self), len(self.messages), self.isConnected()
+ return format % args

- @property
def isConnected(self):
+ """ Returns True if this object has a TWS connection.
+
+ @return True if this object is connected to TWS
+ """
return bool(self.connection and self.connection.isConnected())

- @property
def isModified(self):
+ """ Returns True if this object has unsaved messages.
+
+ @return True if this object has unsaved messages
+ """
return len(self.messages) != self.savedLength

def register(self, obj, name, other=None):
+ """ Connects TWS message signal sent from this object to another.
+
+ @param obj slot, method, or function to receive signals
+ @param name signal name as string
+ @keyparam other=None if not None, slot to receive signals
+ @return None
+ """
if other is None:
self.connect(self, SIGNAL(name), obj)
else:
self.connect(self, SIGNAL(name), obj, other)

def registerAll(self, obj, other=None):
- names = [typ.__name__ for typ in registry.values()]
- for name in names:
- if other is None:
- self.connect(self, SIGNAL(name), obj)
- else:
- self.connect(self, SIGNAL(name), obj, other)
-
- def registerMeta(self, instance):
- prefix = 'on_session_'
- names = [n for n in dir(instance) if n.startswith('on_session_')]
- for name in names:
+ """ Connects all TWS message signals sent from this object to another.
+
+ @param obj slot, method, or function to receive signals
+ @keyparam other=None if not None, slot to receive signals
+ @return None
+ """
+ for name in messageTypeNames():
+ self.register(obj, name, other)
+
+ def registerMeta(self, instance, prefix='on_session_'):
+ """ Inspects instance for named message slots and connects
those found.
+
+ @param instance object with zero or more session message slots
+ @keyparam prefix='on_session_' session message method name prefix
+ @return None
+ """
+ for name in [n for n in dir(instance) if n.startswith(prefix)]:
keys = name[len(prefix):].split('_')
for key in keys:
self.register(getattr(instance, name), key)

def deregister(self, obj, name, other=None):
+ """ Disconnects TWS message signal sent from this object.
+
+ @param obj slot, method, or function to receive signals
+ @param name signal name as string
+ @keyparam other=None if not None, slot to receive signals
+ @return None
+ """
if other is None:
self.disconnect(self, SIGNAL(name), obj)
else:
self.disconnect(self, SIGNAL(name), obj, other)

def deregisterAll(self, obj, other=None):
- names = [typ.__name__ for typ in registry.values()]
- for name in names:
- if other is None:
- self.disconnect(self, SIGNAL(name), obj)
- else:
- self.disconnect(self, SIGNAL(name), obj, other)
-
- def deregisterMeta(self, instance):
- prefix = 'on_session_'
- names = [n for n in dir(instance) if n.startswith('on_session_')]
- for name in names:
+ """ Disconnects all TWS message signals sent from this object
to another.
+
+ @param obj slot, method, or function to receive signals
+ @keyparam other=None if not None, slot to receive signals
+ @return None
+ """
+ for name in messageTypeNames():
+ self.deregister(obj, name, other)
+
+ def deregisterMeta(self, instance, prefix='on_session_'):
+ """ Inspects instance for named message slots and disconnects
those found.
+
+ @param instance object with zero or more session message slots
+ @keyparam prefix='on_session_' session message method name prefix
+ @return None
+ """
+ for name in [n for n in dir(instance) if n.startswith(prefix)]:
keys = name[len(prefix):].split('_')
for key in keys:
self.deregister(getattr(instance, name), key)

+ ##
+ # This special clientId is set in the connection display spinbox.
+ # We support it by substituting a random id for it when
+ # connecting.
specialClientId = -1
+
+ ##
+ # We interpret this privileged port number to mean instead the
+ # default TWS port.
specialPortNo = 1023

def connectTWS(self, hostName, portNo, clientId, enableLogging=False):
+ """ Connect this instance to TWS.
+
+ @param hostName name or IP address of host
+ @param portNo port number for connection
+ @param clientId connection client id
+ @keyparam enableLogging=False enables or disables connection logging
+ @return None
+ """
if clientId == self.specialClientId:
clientId = randint(100, 999)
if portNo == self.specialPortNo:
@@ -131,17 +166,24 @@
con.enableLogging(enableLogging)
con.connect()
con.registerAll(self.receiveMessage)
- con.register(self.on_nextValidId, 'NextValidId')
self.emit(Signals.connectedTWS)
- con.register(self.on_error, 'Error')

- def on_nextValidId(self, message):
- self.nextId = int(message.orderId)
+ def disconnectTWS(self):
+ """ Disconnects this instance from TWS.

- def on_error(self, message):
- logging.debug(str(message))
+ @return None
+ """
+ if self.isConnected():
+ self.connection.disconnect()
+ self.emit(Signals.disconnectedTWS)

def receiveMessage(self, message, mtime=time):
+ """ Receive a message from TWS and propagate it as a Qt signal.
+
+ @param message IbPy message instance
+ @keyparam mtime=time message timestamp or function to generate timestamp
+ @return None
+ """
messages = self.messages
try:
mtime = mtime()
@@ -156,28 +198,47 @@
self.emit(SIGNAL(typename), message)

def requestTickers(self):
+ """ Request market data and depth for each of the strategy contracts.
+
+ @return None
+ """
connection = self.connection
- if connection:
- for tickerId, contract in self.strategy.makeContracts():
+ if connection and connection.isConnected():
+ for tickerId, contract in self.strategy.makeContracts():
connection.reqMktData(tickerId, contract, '', False)
connection.reqMktDepth(tickerId, contract, 1)
## else queue for later?

def requestAccount(self):
- self.connection.reqAccountUpdates(True, "")
+ """ Request account data.
+
+ @return None
+ """
+ connection = self.connection
+ if connection and connection.isConnected():
+ connection.reqAccountUpdates(True, '')

def requestOrders(self):
+ """ Request orders.
+
+ @return None
+ """
connection = self.connection
- filt = ExecutionFilter()
- connection.reqExecutions(filt)
- connection.reqAllOpenOrders()
- connection.reqOpenOrders()
+ 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)

def saveFinished(self):
+ """ Slot that updates this instance after a save thread has completed.
+
+ @return None
+ """
if self.saveThread.status:
count = self.saveThread.writeCount
self.savedLength = count
@@ -187,6 +248,10 @@
self.emit(Signals.sessionStatus, msg)

def exportFinished(self):
+ """ Slot that updates this instance after an export thread has completed.
+
+ @return None
+ """
if self.exportThread.status:
count = self.exportThread.writeCount
msg = 'Session exported. Wrote %s messages.' % count
@@ -195,25 +260,38 @@
self.emit(Signals.sessionStatus, msg)

def saveTerminated(self):
+ """ Slot for handling a canceled save thread.
+
+ @return None
+ """
self.emit(Signals.sessionStatus, 'Session file save terminated.')

def exportTerminated(self):
+ """ Slot for handling a canceled export thread.
+
+ @return None
+ """
self.emit(Signals.sessionStatus, 'Session export terminated.')

- @property
def saveInProgress(self):
+ """ Returns True if this instance has a running save thread
+
+ @return True if save thread is running, otherwise False
+ """
try:
- thread = self.saveThread
+ return self.saveThread.isRunning()
except (AttributeError, ):
return False
- else:
- return thread.isRunning()

def save(self):
- if self.saveInProgress:
+ """ Save the messages in this object to a file.
+
+ @return None
+ """
+ if self.saveInProgress():
return
self.saveThread = thread = \
- SaveThread(filename=self.sessionFile, types=None, parent=self)
+ SaveThread(filename=self.filename, types=None, parent=self)
self.connect(thread, Signals.finished, self.saveFinished)
self.connect(thread, Signals.terminated, self.saveTerminated)
thread.start()
@@ -245,7 +323,7 @@
except (UnpicklingError, ):
pass
finally:
- self.sessionFile= filename
+ self.filename = filename
self.savedLength = len(messages)
handle.close()

@@ -281,17 +359,24 @@
finally:
handle.close()

- @property
def exportInProgress(self):
+ """ Returns True if this instance has a running export thread
+
+ @return True if save thread is running, otherwise False
+ """
try:
- thread = self.exportThread
+ return self.exportThread.isRunning()
except (AttributeError, ):
return False
- else:
- return thread.isRunning()

def exportMessages(self, filename, types):
- if self.exportInProgress:
+ """ Export some or all session messages.
+
+ @param filename name of file to write
+ @param types sequence of types to export; use false value to
export all
+ @return None
+ """
+ if self.exportInProgress():
return
self.exportThread = thread = \
SaveThread(filename=filename, types=types, parent=self)

Modified: trunk/profit/lib/session/collection.py
==============================================================================
--- trunk/profit/lib/session/collection.py (original)
+++ trunk/profit/lib/session/collection.py Tue Jul 22 17:47:38 2008
@@ -7,10 +7,10 @@
# Yichun Wei <yichu...@gmail.com>

import os
-import logging
from cPickle import PicklingError, UnpicklingError, dump, load

from PyQt4.QtCore import QObject, QThread
+from profit.lib import logging
from profit.lib.core import Signals


@@ -57,7 +57,8 @@
try:
iv = float(message.value)
except (ValueError, ):
- pass
+ ## log this
+ return
else:
acctdata = self[key] = \
self.session.strategy.makeAccountSeries(key)
@@ -102,11 +103,6 @@
seq.append(value)


-def historyMessages(reqId, msgs):
- return (m for m in msgs
- if m[1].reqId==reqId
- and not m[1].date.startswith('finished'))
-

class HistoricalDataCollection(DataCollection):
sessionResendSignals = [Signals.historicalDataStart,
@@ -120,7 +116,7 @@
reqId = message.reqId
reqData = self.setdefault(reqId, {})
histMsgs = self.session.typedMessages['HistoricalData']
- reqData['messages'] = historyMessages(reqId, histMsgs)
+ reqData['messages'] = self.historyMessages(reqId, histMsgs)
self.emit(Signals.historicalDataFinish, reqId)

def begin(self, params):
@@ -129,3 +125,23 @@
reqData.update(params)
self.emit(Signals.historicalDataStart, reqId, reqData)
self.session.connection.reqHistoricalData(**reqData)
+
+ @staticmethod
+ def historyMessages(reqId, msgs):
+ return (m for m in msgs
+ if m[1].reqId==reqId
+ and not m[1].date.startswith('finished'))
+
+
+
+class OrderDataCollection(DataCollection):
+ nextId = 0
+
+ def on_session_nextValidId(self, message):
+ self.nextId = int(message.orderId)
+
+
+class ErrorDataCollection(DataCollection):
+ def on_session_Error(self, message):
+ logging.debug(str(message))
+

Modified: trunk/profit/lib/session/savethread.py
==============================================================================
--- trunk/profit/lib/session/savethread.py (original)
+++ trunk/profit/lib/session/savethread.py Tue Jul 22 17:47:38 2008
@@ -11,12 +11,27 @@


class SaveThread(QThread):
+ """ SaveThread -> Thread class for saving session messages asynchronously.
+
+
+ """
def __init__(self, filename, types, parent):
+ """ Initializer.
+
+ @param filename name of file to write
+ @param types sequence of types to save; use a false value to
save all
+ @param parent parent of this object; should be a Session instance
+ @return None
+ """
QThread.__init__(self, parent)
self.filename = filename
self.types = types

def run(self):
+ """ Saves parent's messages to a file with the pickling protocol.
+
+ @return None
+ """
status = False
session = self.parent()
try:

Modified: trunk/profit/lib/widgets/plot.py
==============================================================================
--- trunk/profit/lib/widgets/plot.py (original)
+++ trunk/profit/lib/widgets/plot.py Tue Jul 22 17:47:38 2008
@@ -303,7 +303,7 @@

"""
def __init__(self, parent=None):
- """ Constructor.
+ """ Initializer.

@param parent ancestor of this widget
"""
@@ -427,7 +427,7 @@
tree.sortByColumn(0, Qt.AscendingOrder)
try:
ticker = self.collection[self.key]
- except (KeyError, ):
+ except (KeyError, TypeError, ):
pass
else:
for field, series in ticker.series.items():

Modified: trunk/profit/workbench/connectiondisplay.py
==============================================================================
--- trunk/profit/workbench/connectiondisplay.py (original)
+++ trunk/profit/workbench/connectiondisplay.py Tue Jul 22 17:47:38 2008
@@ -58,7 +58,7 @@

def setSession(self, session):
self.session = session
- connected = session.isConnected
+ connected = session.isConnected()
self.setControlsEnabled(not connected, connected)
session.registerMeta(self)
session.registerAll(self.updateLastMessage)
@@ -83,7 +83,7 @@

def on_connectedTWS(self):
session = self.session
- if session.isConnected:
+ if session.isConnected():
self.setControlsEnabled(False, True)
try:
if self.requestAccount.isChecked():
@@ -117,7 +117,7 @@

@pyqtSignature('')
def on_disconnectButton_clicked(self):
- if self.session and self.session.isConnected:
+ if self.session and self.session.isConnected():
self.session.disconnectTWS()
self.setControlsEnabled(True, False)


Modified: trunk/profit/workbench/main.py
==============================================================================
--- trunk/profit/workbench/main.py (original)
+++ trunk/profit/workbench/main.py Tue Jul 22 17:47:38 2008
@@ -80,10 +80,10 @@
settings.beginGroup(settings.keys.main)
confirm = settings.value('confirmCloseWhenModified', QVariant(1))
confirm = confirm.toInt()[0]
- if self.session.isModified and confirm:
+ if self.session.isModified() and confirm:
buttons = QMessageBox.Save|QMessageBox.Discard|QMessageBox.Cancel
text = 'This session has been modified'
- if self.session.isConnected:
+ if self.session.isConnected():
text += ' and is connected and receiving messages.'
else:
text += '.'
@@ -167,7 +167,7 @@
dlg = HistoricalDataDialog(self)
if dlg.exec_() != dlg.Accepted:
return
- if self.session.isConnected:
+ if self.session.isConnected():
params = dlg.historicalRequestParameters()
self.session.requestHistoricalData(params)

@@ -178,7 +178,7 @@
filename = QFileDialog.getSaveFileName(
self, 'Export Session To File')
if filename:
- if self.session.exportInProgress:
+ if self.session.exportInProgress():
warningBox('Export in Progress',
'Session export already in progress.')
else:
@@ -190,7 +190,7 @@
return
self.session.exportMessages(filename, types)
def lookup():
- return not self.session.exportInProgress
+ return not self.session.exportInProgress()
dlg = WaitMessageBox(lookup, self)
dlg.setText('Export in Progress...')
dlg.setWindowTitle('Profit Workbench Session Export')
@@ -288,16 +288,16 @@

@pyqtSignature('')
def on_actionSaveSession_triggered(self):
- if self.session.sessionFile is None:
+ if self.session.filename is None:
self.actionSaveSessionAs.trigger()
else:
- if self.session.saveInProgress:
+ if self.session.saveInProgress():
warningBox('Save in Progress',
'Session save already in progress.')
else:
self.session.save()
def lookup():
- return not self.session.saveInProgress
+ return not self.session.saveInProgress()
dlg = WaitMessageBox(lookup, self)
dlg.setText('Save in Progress...')
dlg.setWindowTitle('Profit Workbench Session Save')
@@ -307,7 +307,7 @@
def on_actionSaveSessionAs_triggered(self):
filename = QFileDialog.getSaveFileName(self, 'Save Session As')
if filename:
- self.session.sessionFile = str(filename)
+ self.session.filename = str(filename)
self.actionSaveSession.trigger()

@pyqtSignature('')
@@ -338,7 +338,7 @@
if reason == QSystemTrayIcon.Trigger:
self.setVisible(not self.isVisible())
elif reason == QSystemTrayIcon.MiddleClick:
- if self.session and self.session.isConnected:
+ if self.session and self.session.isConnected():
msg = 'Connected'
else:
msg = 'Not Connected'

Reply all
Reply to author
Forward
0 new messages