Skip to content

Commit 28be6ea

Browse files
add support for ASK quotes for CASH assets (mementum#395)
Co-authored-by: Gunther Schulz <[email protected]>
1 parent edba801 commit 28be6ea

3 files changed

Lines changed: 121 additions & 3 deletions

File tree

backtrader/feeds/ibdata.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ class IBData(with_metaclass(MetaIBData, DataBase)):
113113
- 'BID' for CASH assets
114114
- 'TRADES' for any other
115115
116+
Use 'ASK' for the Ask quote of cash assets
117+
116118
Check the IB API docs if another value is wished
117119
118120
- ``rtbar`` (default: ``False``)
@@ -412,7 +414,7 @@ def reqdata(self):
412414
return
413415

414416
if self._usertvol:
415-
self.qlive = self.ib.reqMktData(self.contract)
417+
self.qlive = self.ib.reqMktData(self.contract, self.p.what)
416418
else:
417419
self.qlive = self.ib.reqRealTimeBars(self.contract)
418420

backtrader/stores/ibstore.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,8 @@ def reqHistoricalData(self, contract, enddate, duration, barsize,
759759
self.iscash[tickerId] = True
760760
if not what:
761761
what = 'BID' # TRADES doesn't work
762+
elif what is 'ASK':
763+
self.iscash[tickerId] = 2
762764
else:
763765
what = what or 'TRADES'
764766

@@ -827,7 +829,7 @@ def cancelRealTimeBars(self, q):
827829

828830
self.cancelQueue(q, True)
829831

830-
def reqMktData(self, contract):
832+
def reqMktData(self, contract, what=None):
831833
'''Creates a MarketData subscription
832834
833835
Params:
@@ -843,7 +845,9 @@ def reqMktData(self, contract):
843845
if contract.m_secType in ['CASH', 'CFD']:
844846
self.iscash[tickerId] = True
845847
ticks = '' # cash markets do not get RTVOLUME
846-
848+
if what is 'ASK':
849+
self.iscash[tickerId] = 2
850+
847851
# q.put(None) # to kickstart backfilling
848852
# Can request 233 also for cash ... nothing will arrive
849853
self.conn.reqMktData(tickerId, contract, bytes(ticks), False)
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8; py-indent-offset:4 -*-
3+
###############################################################################
4+
#
5+
# Copyright (C) 2018 Daniel Rodriguez
6+
#
7+
# This program is free software: you can redistribute it and/or modify
8+
# it under the terms of the GNU General Public License as published by
9+
# the Free Software Foundation, either version 3 of the License, or
10+
# (at your option) any later version.
11+
#
12+
# This program is distributed in the hope that it will be useful,
13+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
# GNU General Public License for more details.
16+
#
17+
# You should have received a copy of the GNU General Public License
18+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
19+
#
20+
###############################################################################
21+
22+
# When setting the parameter "what='ASK'" the quoted price for Ask will be used from the incoming messages (field 2) instead of the default Bid price (field 1).
23+
24+
# BID: <tickPrice tickerId=16777217, field=1, price=1.11582, canAutoExecute=1>
25+
# ASK: <tickPrice tickerId=16777219, field=2, price=1.11583, canAutoExecute=1>
26+
27+
from __future__ import (absolute_import, division, print_function,
28+
unicode_literals)
29+
30+
import backtrader as bt
31+
import datetime
32+
33+
34+
class St(bt.Strategy):
35+
def logdata(self):
36+
txt = []
37+
txt.append('{}'.format(len(self)))
38+
txt.append('{}'.format(self.data.datetime.datetime(0).isoformat()))
39+
txt.append(' open BID: ' + '{}'.format(self.datas[0].open[0]))
40+
txt.append(' open ASK: ' + '{}'.format(self.datas[1].open[0]))
41+
txt.append(' high BID: ' + '{}'.format(self.datas[0].high[0]))
42+
txt.append(' high ASK: ' + '{}'.format(self.datas[1].high[0]))
43+
txt.append(' low BID: ' + '{}'.format(self.datas[0].low[0]))
44+
txt.append(' low ASK: ' + '{}'.format(self.datas[1].low[0]))
45+
txt.append(' close BID: ' + '{}'.format(self.datas[0].close[0]))
46+
txt.append(' close ASK: ' + '{}'.format(self.datas[1].close[0]))
47+
txt.append(' volume: ' + '{:.2f}'.format(self.data.volume[0]))
48+
print(','.join(txt))
49+
50+
data_live = False
51+
52+
def notify_data(self, data, status, *args, **kwargs):
53+
print('*' * 5, 'DATA NOTIF:', data._getstatusname(status), *args)
54+
if self.datas[0]._laststatus == self.datas[0].LIVE and self.datas[1]._laststatus == self.datas[1].LIVE:
55+
self.data_live = True
56+
57+
# def notify_order(self, order):
58+
# if order.status == order.Completed:
59+
# buysell = 'BUY ' if order.isbuy() else 'SELL'
60+
# txt = '{} {}@{}'.format(buysell, order.executed.size,
61+
# order.executed.price)
62+
# print(txt)
63+
64+
# bought = 0
65+
# sold = 0
66+
67+
def next(self):
68+
self.logdata()
69+
if not self.data_live:
70+
return
71+
72+
# if not self.bought:
73+
# self.bought = len(self) # keep entry bar
74+
# self.buy()
75+
# elif not self.sold:
76+
# if len(self) == (self.bought + 3):
77+
# self.sell()
78+
79+
80+
ib_symbol = 'EUR.USD-CASH-IDEALPRO'
81+
compression = 5
82+
83+
def run(args=None):
84+
cerebro = bt.Cerebro(stdstats=False)
85+
store = bt.stores.IBStore(port=7497,
86+
# _debug=True
87+
)
88+
89+
data0 = store.getdata(dataname=ib_symbol,
90+
timeframe=bt.TimeFrame.Ticks,
91+
)
92+
cerebro.resampledata(data0,
93+
timeframe=bt.TimeFrame.Seconds,
94+
compression=compression
95+
)
96+
97+
data1 = store.getdata(dataname=ib_symbol,
98+
timeframe=bt.TimeFrame.Ticks,
99+
what='ASK'
100+
)
101+
cerebro.resampledata(data1,
102+
timeframe=bt.TimeFrame.Seconds,
103+
compression=compression
104+
)
105+
106+
cerebro.broker = store.getbroker()
107+
cerebro.addstrategy(St)
108+
cerebro.run()
109+
110+
111+
if __name__ == '__main__':
112+
run()

0 commit comments

Comments
 (0)