[profitpy commit] r327 - in trunk: . profit/lib profit/models profit/session profit/strategy profit/workbench profi...

1 view
Skip to first unread message

codesite...@google.com

unread,
Aug 8, 2008, 4:44:37 AM8/8/08
to profitp...@googlegroups.com
Author: troy.melhase
Date: Fri Aug 8 01:43:20 2008
New Revision: 327

Added:
trunk/README.dev
trunk/TODO
- copied unchanged from r324, /trunk/README
trunk/profit/models/histdata.py (contents, props changed)
Removed:
trunk/README
Modified:
trunk/profit/lib/__init__.py
trunk/profit/models/__init__.py
trunk/profit/models/executions.py
trunk/profit/models/orders.py
trunk/profit/models/portfolio.py
trunk/profit/models/strategy.py
trunk/profit/models/ticker.py
trunk/profit/session/__init__.py
trunk/profit/strategy/builder.py
trunk/profit/workbench/historicaldatadisplay.py
trunk/profit/workbench/main.py
trunk/profit/workbench/tickerdisplay.py
trunk/profit/workbench/widgets/ui_historicaldatadisplay.ui

Log:
Partial implementation of histdata model.

Added: trunk/README.dev
==============================================================================

Modified: trunk/profit/lib/__init__.py
==============================================================================
--- trunk/profit/lib/__init__.py (original)
+++ trunk/profit/lib/__init__.py Fri Aug 8 01:43:20 2008
@@ -119,6 +119,7 @@
created = SIGNAL('createdContract')

class histdata:
+ request = SIGNAL('historicalDataRequest')
start = SIGNAL('historicalDataStart')
finish = SIGNAL('historicalDataFinish')

@@ -321,5 +322,14 @@
settings = property(settingsGetter, settingsSetter)


-class BasicHandler(SessionHandler, SettingsHandler):
+class InstanceReflector(object):
+ def reflectSignal(self, signal):
+ app = instance()
+ if app:
+ self.connect(self, signal, app, signal)
+ else:
+ logging.warn('No application instance to connect %s', signal)
+
+
+class BasicHandler(SessionHandler, SettingsHandler, InstanceReflector):
pass

Modified: trunk/profit/models/__init__.py
==============================================================================
--- trunk/profit/models/__init__.py (original)
+++ trunk/profit/models/__init__.py Fri Aug 8 01:43:20 2008
@@ -48,6 +48,7 @@
def __init__(self, root, parent=None):
QAbstractItemModel.__init__(self, parent)
self.invisibleRootItem = root
+ self.symbolIcon = lambda x:None

def indexItem(self, index):
valid = index.isValid()

Modified: trunk/profit/models/executions.py
==============================================================================
--- trunk/profit/models/executions.py (original)
+++ trunk/profit/models/executions.py Fri Aug 8 01:43:20 2008
@@ -17,7 +17,6 @@
"""
def __init__(self, session=None, parent=None):
BasicItemModel.__init__(self, RootExecutionsItem(), parent)
- self.symbolIcon = lambda x:None
self.session = session
if session is not None:
session.registerMeta(self)

Added: trunk/profit/models/histdata.py
==============================================================================
--- (empty file)
+++ trunk/profit/models/histdata.py Fri Aug 8 01:43:20 2008
@@ -0,0 +1,251 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Copyright 2007 Troy Melhase <tr...@gci.net>
+# Distributed under the terms of the GNU General Public License v2
+
+from PyQt4.QtCore import Qt, QModelIndex, QVariant, QString
+from ib.opt.message import HistoricalData as HistDataMessage
+from profit.lib import Signals, logging, valueAlign
+from profit.models import BasicItem, BasicItemModel
+
+## TODO: add the incoming requests to the parent session's extra
+## object list.
+
+
+class HistoricalDataModel(BasicItemModel):
+ """ HistoricalDataModel -> model of hist data requests and responses
+
+ This model supports online and offline processing of messages. In
+ the case of missing requests (as in when messages are read from
+ disk), the model simply fills in what it can. In the case where
+ the model is given a request, it enqueues the request with the
+ session and then associates the responses accordingly.
+ """
+ def __init__(self, session=None, parent=None):
+ """ Initializer.
+
+ @param session=None session reference or None
+ @param parent=None ancestor of this object or None
+ """
+ BasicItemModel.__init__(self, RootHistoricalDataItem(), parent)
+ self.requests = {}
+ self.session = session
+ if session is not None:
+ session.registerMeta(self)
+ self.startTimer(1000)
+
+ def data(self, index, role):
+ """ Framework hook to retreive data stored at index for given role.
+
+ @param index QModelIndex instance
+ @param role Qt.DisplayRole flags
+ @return QVariant instance
+ """
+ if not index.isValid():
+ return QVariant()
+ item = index.internalPointer()
+ data = QVariant()
+ column = index.column()
+ amChild = index.parent().isValid()
+ if role == Qt.DecorationRole and column==2:
+ if not amChild:
+ data = QVariant(self.symbolIcon(item.symbol()))
+ elif role in (Qt.DisplayRole, Qt.ToolTipRole):
+ if amChild and (column==0):
+ data = QVariant(item.row())
+ else:
+ data = QVariant(item[column])
+ elif role in (Qt.TextAlignmentRole, ):
+ try:
+ float(item[column])
+ data = QVariant(valueAlign)
+ except (TypeError, ValueError, ):
+ pass
+ return data
+
+ def findHistDataItem(self, reqId):
+ """ Returns the item for the given hist data message, or None.
+
+ """
+ items = self.invisibleRootItem.children
+ try:
+ return [i for i in items if i.reqId==reqId][0]
+ except (IndexError, ):
+ pass
+
+ def on_session_HistoricalData(self, message):
+ """ Called when the session receives a HistoricalData message.
+
+ @param message ib.opt.message instance
+ """
+ reqId = message.reqId
+ req = self.requests.get(reqId, {})
+ item = self.findHistDataItem(reqId)
+ if item:
+ item.append(HistoricalDataItem.fromMessage(reqId, message,
item, req))
+ if message.date.startswith('finished'):
+ item.setStatus('Finished')
+ self.emit(Signals.histdata.finish, reqId)
+ else:
+ root = self.invisibleRootItem
+ root.append(HistoricalDataItem.fromMessage(reqId, message,
root, req))
+ self.emit(Signals.histdata.start, reqId)
+ ## cheater
+ self.reset()
+
+ def on_session_historicalDataRequest(self, params):
+ """ Called when a request for historical data is made.
+
+ """
+ reqId = params['tickerId']
+ requests = self.requests
+ if reqId in requests:
+ logging.warn('Ignoring duplicate hist data request %s', reqId)
+ return
+ requests[reqId] = params.copy()
+ root = self.invisibleRootItem
+ root.append(HistoricalDataItem.fromRequest(reqId, params, root))
+ self.reset()
+
+ def busy(self):
+ for item in iter(self.invisibleRootItem.children):
+ if item[1] == States.active:
+ return True
+
+ def next(self):
+ for item in iter(self.invisibleRootItem.children):
+ if item[1] == States.unsubmitted and item.req:
+ return item
+
+ def timerEvent(self, event):
+ if not self.session.isConnected() or self.busy():
+ return
+ next = self.next()
+ if next:
+ self.session.connection.reqHistoricalData(**next.req)
+
+
+class States(object):
+ unsubmitted, active, finished, errored = range(4)
+ labelMap = {
+ unsubmitted:'Unsubmitted',
+ active:'Active',
+ finished:'Finished',
+ errored:'Errored',
+ }
+
+
+class HistoricalDataItem(BasicItem):
+ """ Base class for items in the portfolio model.
+
+ """
+ columnLookups = [
+ ('Request Id', lambda x:x.reqId),
+ ('Status', lambda x:x.request.status),
+ ('Symbol', lambda x:x.request.contract.symbol),
+ ('Date', lambda x:x.date),
+ ('Open', lambda x:x.open),
+ ('High', lambda x:x.high),
+ ('Low', lambda x:x.low),
+ ('Close', lambda x:x.close),
+ ('Volume', lambda x:x.volume),
+ ('Count', lambda x:x.count),
+ ('Weighted Average Price', lambda x:x.WAP),
+ ('Has Gaps', lambda x:x.hasGaps),
+ ]
+
+ def __init__(self, data, parent=None, message=None,
+ reqId=None,
+ req={},
+ state=States.unsubmitted):
+ BasicItem.__init__(self, data, parent)
+ self.message = message
+ self.reqId = reqId
+ self.req = req
+ self.state = state
+
+ def setStatus(self, text):
+ self.data[1] = text
+
+ @classmethod
+ def fromRequest(cls, requestId, params, parent):
+ """ New instance from a request
+
+ @param cls class object
+ @param requestId client identifier for request as int
+ @param params request parameter as dictionary
+ @param parent parent of this item
+ @return new instance of cls
+ """
+ values = [None for item in cls.columnLookups]
+ values[0] = requestId
+ return cls(values, parent, None, requestId, params.copy())
+
+ @classmethod
+ def fromMessage(cls, requestId, message, parent, request):
+ """ New instance from message values
+
+ @param cls class object
+ @param requestId client identifier for request as int
+ @param message ib.opt.message object
+ @param parent parent of this item
+ @return new instance of cls
+ """
+ values = []
+ for label, lookup in cls.columnLookups:
+ try:
+ value = lookup(message)
+ except (AttributeError, ):
+ value = None
+ values.append(value)
+ item = cls(values, parent, message, requestId, request)
+ if message.date.startswith('finished-'):
+ parent.data[1] = item.data[1] =
States.labelMap[States.finished]
+ item[2:] = [None for i in item[2:]]
+ return item
+
+ def symbol(self):
+ """ Returns the symbol for this item or ''
+
+ """
+ try:
+ return self.message.contract.m_symbol
+ except (AttributeError, ):
+ return ''
+
+ def update(self, message):
+ """ Update the item with values from a message.
+
+ @param message ib.opt.message object
+ @return None
+ """
+ for column, (label, lookup) in enumerate(self.columnLookups):
+ try:
+ self[column] = lookup(message)
+ except (AttributeError, ):
+ pass
+
+
+class RootHistoricalDataItem(HistoricalDataItem):
+ """ HistoricalData model item with automatic values (for horizontal
headers).
+
+ """
+ def __init__(self):
+ HistoricalDataItem.__init__(self, self.horizontalLabels())
+
+ def horizontalLabels(self):
+ """ Generates list of horizontal header values.
+
+ """
+ return map(QVariant, [label for label, lookup in
self.columnLookups])
+
+
+## params = {'endDateTime': '20080707 08:00:00',
+## 'durationStr': '2 D',
+## 'whatToShow': 'TRADES',
+## 'contract': <ib.ext.Contract.Contract object at 0x8dd8f0c>,
+## 'barSizeSetting': '1 min',
+## 'formatDate': 1,
+## 'tickerId': 1146,
+## 'useRTH': 1}

Modified: trunk/profit/models/orders.py
==============================================================================
--- trunk/profit/models/orders.py (original)
+++ trunk/profit/models/orders.py Fri Aug 8 01:43:20 2008
@@ -15,7 +15,6 @@
"""
def __init__(self, session=None, parent=None):
BasicItemModel.__init__(self, RootOrderItem(), parent)
- self.symbolIcon = lambda x:None
self.session = session
if session is not None:
session.registerMeta(self)

Modified: trunk/profit/models/portfolio.py
==============================================================================
--- trunk/profit/models/portfolio.py (original)
+++ trunk/profit/models/portfolio.py Fri Aug 8 01:43:20 2008
@@ -15,7 +15,6 @@
"""
def __init__(self, session=None, parent=None):
BasicItemModel.__init__(self, RootPortfolioItem(), parent)
- self.symbolIcon = lambda x:None
self.session = session
if session is not None:
session.registerMeta(self)

Modified: trunk/profit/models/strategy.py
==============================================================================
--- trunk/profit/models/strategy.py (original)
+++ trunk/profit/models/strategy.py Fri Aug 8 01:43:20 2008
@@ -5,24 +5,26 @@
# Distributed under the terms of the GNU General Public License v2

from PyQt4.QtCore import Qt, QModelIndex, QVariant, QSize, QString
-from profit.lib import Signals, valueAlign, SettingsHandler
+from profit.lib import Signals, valueAlign, BasicHandler
from profit.models import BasicItem, BasicItemModel


-class StrategyModel(BasicItemModel, SettingsHandler):
+class StrategyModel(BasicItemModel, BasicHandler):
""" Model for strategies.

"""
def __init__(self, session=None, parent=None):
""" Initializer.

+ @param session=None session reference or None
+ @param parent=None ancestor of this object or None
"""
BasicItemModel.__init__(self, RootStrategyItem(), parent)
- self.symbolIcon = lambda x:None
self.iconMap = {}
self.session = session
if session is not None:
session.registerMeta(self)
+ self.reflectSignal(Signals.strategy.requestActivate)
self.readSettings()

def data(self, index, role=Qt.DisplayRole):

Modified: trunk/profit/models/ticker.py
==============================================================================
--- trunk/profit/models/ticker.py (original)
+++ trunk/profit/models/ticker.py Fri Aug 8 01:43:20 2008
@@ -17,7 +17,6 @@
"""
def __init__(self, session=None, parent=None):
BasicItemModel.__init__(self, TickerRootItem(), parent)
- self.symbolIcon = lambda x:None
self.session = session
self.data = {}
if session is not None:

Modified: trunk/profit/session/__init__.py
==============================================================================
--- trunk/profit/session/__init__.py (original)
+++ trunk/profit/session/__init__.py Fri Aug 8 01:43:20 2008
@@ -15,8 +15,9 @@
from ib.opt import ibConnection
from ib.opt.message import messageTypeNames

-from profit.lib import Signals, logging, instance
+from profit.lib import Signals, logging
from profit.models.executions import ExecutionsModel
+from profit.models.histdata import HistoricalDataModel
from profit.models.orders import OrdersModel
from profit.models.portfolio import PortfolioModel
from profit.models.strategy import StrategyModel
@@ -30,16 +31,14 @@
class DataMaps(object):
def __init__(self, session):
self.account = collection.AccountCollection(session)
- self.historical = collection.HistoricalDataCollection(session)
self.ticker = collection.TickerCollection(session)
- self.error = collection.ErrorDataCollection(session)
- self.order = collection.OrderDataCollection(session)
- self.contract = collection.ContractDataCollection(session)
+ self.historical = collection.HistoricalDataCollection(session)


class DataModels(object):
def __init__(self, session):
self.executions = ExecutionsModel(session)
+ self.histdata = HistoricalDataModel(session)
self.orders = OrdersModel(session)
self.portfolio = PortfolioModel(session)
self.strategy = StrategyModel(session)
@@ -62,11 +61,6 @@
self.savedLength = 0
self.maps = DataMaps(self)
self.models = DataModels(self)
- app = instance()
- self.connect(self.strategy, Signals.contract.created,
- self, Signals.contract.created)
- self.connect(self.models.strategy,
Signals.strategy.requestActivate,
- app, Signals.strategy.requestActivate)

def __str__(self):
""" x.__str__() <==> str(x)
@@ -259,7 +253,7 @@

def requestHistoricalData(self, params):
## we should msg the object instead
- self.maps.historical.begin(params)
+ self.models.histdata.begin(params)

def saveFinished(self):
""" Slot that updates this instance after a save thread has
completed.

Modified: trunk/profit/strategy/builder.py
==============================================================================
--- trunk/profit/strategy/builder.py (original)
+++ trunk/profit/strategy/builder.py Fri Aug 8 01:43:20 2008
@@ -11,7 +11,7 @@

from PyQt4.QtCore import QObject

-from profit.lib import Signals, instance, logging
+from profit.lib import BasicHandler, Signals, instance, logging
from profit.series import Series, MACDHistogram, EMA

from ib.ext.Contract import Contract
@@ -23,7 +23,7 @@
self.series = {}


-class SessionStrategyBuilder(QObject):
+class SessionStrategyBuilder(QObject, BasicHandler):
default_paramsHistoricalData = {
## change to use datetime
"endDateTime" : strftime("%Y%m%d %H:%M:%S PST",
(2007,1,1,0,0,0,0,0,0)),
@@ -40,6 +40,7 @@
self.isActive = self.loadMessage = False
self.threads = []
self.tickers = []
+ self.reflectSignal(Signals.contract.created)
app = instance()
if app:
connect = self.connect

Modified: trunk/profit/workbench/historicaldatadisplay.py
==============================================================================
--- trunk/profit/workbench/historicaldatadisplay.py (original)
+++ trunk/profit/workbench/historicaldatadisplay.py Fri Aug 8 01:43:20 2008
@@ -10,18 +10,10 @@
from PyQt4.QtGui import QFrame, QIcon

from profit.lib import BasicHandler, makeCheckNames
-from profit.lib.gui import ValueTableItem
+from profit.lib.gui import ValueTableItem, symbolIcon
from profit.workbench.widgets.ui_historicaldatadisplay import
Ui_HistoricalDataDisplay


-def replayHistoricalData(messages, callback):
- isHistMessage = makeCheckNames('HistoricalData')
- def pred((t, m)):
- return isHistMessage(m)
- for time, message in ifilter(pred, reversed(messages)):
- callback(message)
-
-
class HistoricalDataDisplay(QFrame, Ui_HistoricalDataDisplay,
BasicHandler):
def __init__(self, parent=None):
QFrame.__init__(self, parent)
@@ -30,9 +22,7 @@

def setSession(self, session):
self.session = session
- replayHistoricalData(session.messages,
self.on_session_HistoricalData)
+ model = session.models.histdata
+ model.symbolIcon = symbolIcon
+ self.histDataView.setModel(model)
session.registerMeta(self)
-
-
- def on_session_HistoricalData(self, message):
- pass

Modified: trunk/profit/workbench/main.py
==============================================================================
--- trunk/profit/workbench/main.py (original)
+++ trunk/profit/workbench/main.py Fri Aug 8 01:43:20 2008
@@ -67,6 +67,7 @@
connect(app, Signals.session.request, sessreq)
connect(app, Signals.lastWindowClosed, self.writeSettings)
connect(self, Signals.openUrl, app, Signals.openUrl)
+ connect(self, Signals.histdata.request, app,
Signals.histdata.request)
connect(self, Signals.session.created, app,
Signals.session.created)
connect(self, Signals.settingsChanged, self.setupColors)
connect(self, Signals.settingsChanged, self.setupSysTray)
@@ -188,9 +189,9 @@
dlg = HistoricalDataDialog(self)
if dlg.exec_() != dlg.Accepted:
return
- if self.session.isConnected():
- params = dlg.historicalRequestParameters()
- self.session.requestHistoricalData(params)
+ params = dlg.historicalRequestParameters()
+ ## cheater!
+ self.session.emit(Signals.histdata.request, params)

@pyqtSignature('')
def on_actionExportSession_triggered(self, filename=None):

Modified: trunk/profit/workbench/tickerdisplay.py
==============================================================================
--- trunk/profit/workbench/tickerdisplay.py (original)
+++ trunk/profit/workbench/tickerdisplay.py Fri Aug 8 01:43:20 2008
@@ -46,10 +46,8 @@
userFields = settings.valueLoad('selectedFields', defaultFields)
#self.tickFieldSelect.setCheckedFields(userFields)
settings.endGroup()
- app = instance()
- connect = self.connect
- connect(self, Signals.openUrl, app, Signals.openUrl)
- connect(self, Signals.tickerClicked, app, Signals.tickerClicked)
+ self.reflectSignal(Signals.openUrl)
+ self.reflectSignal(Signals.tickerClicked)

def setSession(self, session):
""" Configures this instance for a session.

Modified: trunk/profit/workbench/widgets/ui_historicaldatadisplay.ui
==============================================================================
--- trunk/profit/workbench/widgets/ui_historicaldatadisplay.ui (original)
+++ trunk/profit/workbench/widgets/ui_historicaldatadisplay.ui Fri Aug 8
01:43:20 2008
@@ -5,8 +5,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>400</width>
- <height>300</height>
+ <width>521</width>
+ <height>407</height>
</rect>
</property>
<property name="windowTitle" >
@@ -14,11 +14,30 @@
</property>
<layout class="QVBoxLayout" >
<property name="spacing" >
- <number>6</number>
+ <number>0</number>
</property>
<property name="margin" >
<number>0</number>
</property>
+ <item>
+ <widget class="QTreeView" name="histDataView" >
+ <property name="frameShape" >
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="editTriggers" >
+ <set>QAbstractItemView::NoEditTriggers</set>
+ </property>
+ <property name="alternatingRowColors" >
+ <bool>true</bool>
+ </property>
+ <property name="uniformRowHeights" >
+ <bool>true</bool>
+ </property>
+ <property name="animated" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
<resources/>

Reply all
Reply to author
Forward
0 new messages