mhallsmoore / qstrader

QuantStart.com - QSTrader backtesting simulation engine.
https://www.quantstart.com/qstrader/
MIT License
2.93k stars 855 forks source link

An idea to ensure the PriceHandler has the up to date prices? #265

Closed JamesKBowler closed 8 months ago

JamesKBowler commented 6 years ago

Hi,

Looking for ways to integrate FXCM data into QSTrader and one problem is the PriceHandler has a built-in 'price lag' which amplifies on higher times frames.

Take the following example:

Current Short Position

Based on how the Position object is coded and with zero commission, this would translate to 10000 * 1.12035 = 11200.35USD

My account currency is GBP, so on each iteration, the Portfolio should update equity in GBP, not USD. This is easy, just make sure that GBPUSD is included in the backtest data and make the conversion.

If using say Daily data, the values used to calculate the exchange rate would be 1 Day behind, because the Portfolio is calling the PriceHandler for the last known value and of course the most current value has not been iterated through the event loop.

To make sure the PriceHandler has all the values stored with the same timestamp. I have implemented the following code below.

The code will call next until the date is greater than the last. The last greater than date is saved and only placed into the queue on the next iteration.

Some Event log output to visualise.

Type: EventType.BAR, Ticker: EURUSD, Time: 2013-12-23 22:00:00, Period: 1day, BidOpen: 13696800, BidHigh: 13715599, BidLow: 13655899, BidClose: 13676400, AskOpen: 13695300, AskHigh: 13713400, AskLow: 13654300, AskClose: 13674400,Volume: 49603
Type: EventType.BAR, Ticker: GBPUSD, Time: 2013-12-23 22:00:00, Period: 1day, BidOpen: 16361000, BidHigh: 16382000, BidLow: 16323700, BidClose: 16370399, AskOpen: 16351900, AskHigh: 16380999, AskLow: 16321500, AskClose: 16367500,Volume: 61296
Type: EventType.BAR, Ticker: USDOLLAR, Time: 2013-12-23 22:00:00, Period: 1day, BidOpen: 106830000000, BidHigh: 107000000000, BidLow: 106820000000, BidClose: 106920000000, AskOpen: 106800000000, AskHigh: 106970000000, AskLow: 106770000000, AskClose: 106890000000,Volume: 2171
Reset
Type: EventType.BAR, Ticker: USDOLLAR, Time: 2013-12-24 22:00:00, Period: 1day, BidOpen: 106920000000, BidHigh: 106920000000, BidLow: 106920000000, BidClose: 106920000000, AskOpen: 106890000000, AskHigh: 106890000000, AskLow: 106890000000, AskClose: 106890000000,Volume: 1
Reset
Type: EventType.BAR, Ticker: EURUSD, Time: 2013-12-25 22:00:00, Period: 1day, BidOpen: 13676400, BidHigh: 13703100, BidLow: 13675700, BidClose: 13692100, AskOpen: 13674400, AskHigh: 13701400, AskLow: 13673200, AskClose: 13689900,Volume: 38743
Type: EventType.BAR, Ticker: GBPUSD, Time: 2013-12-25 22:00:00, Period: 1day, BidOpen: 16370399, BidHigh: 16439199, BidLow: 16370399, BidClose: 16412500, AskOpen: 16367500, AskHigh: 16437200, AskLow: 16367500, AskClose: 16408799,Volume: 54611
Type: EventType.BAR, Ticker: USDOLLAR, Time: 2013-12-25 22:00:00, Period: 1day, BidOpen: 106920000000, BidHigh: 107080000000, BidLow: 106920000000, BidClose: 106980000000, AskOpen: 106890000000, AskHigh: 107050000000, AskLow: 106890000000, AskClose: 106950000000,Volume: 1784
Reset
Type: EventType.BAR, Ticker: EURUSD, Time: 2013-12-26 22:00:00, Period: 1day, BidOpen: 13692100, BidHigh: 13894899, BidLow: 13688200, BidClose: 13749799, AskOpen: 13689900, AskHigh: 13892900, AskLow: 13686399, AskClose: 13745800,Volume: 141970
Type: EventType.BAR, Ticker: GBPUSD, Time: 2013-12-26 22:00:00, Period: 1day, BidOpen: 16412500, BidHigh: 16578900, BidLow: 16405599, BidClose: 16486599, AskOpen: 16408799, AskHigh: 16577400, AskLow: 16405200, AskClose: 16473099,Volume: 114972
Type: EventType.BAR, Ticker: USDOLLAR, Time: 2013-12-26 22:00:00, Period: 1day, BidOpen: 106980000000, BidHigh: 107030000000, BidLow: 106330000000, BidClose: 106910000000, AskOpen: 106950000000, AskHigh: 107000000000, AskLow: 106310000000, AskClose: 106850000000,Volume: 4002
Reset
Type: EventType.BAR, Ticker: EURUSD, Time: 2013-12-29 22:00:00, Period: 1day, BidOpen: 13760200, BidHigh: 13819600, BidLow: 13729000, BidClose: 13802400, AskOpen: 13753700, AskHigh: 13818500, AskLow: 13727600, AskClose: 13797900,Volume: 98382
Type: EventType.BAR, Ticker: GBPUSD, Time: 2013-12-29 22:00:00, Period: 1day, BidOpen: 16485700, BidHigh: 16533799, BidLow: 16460800, BidClose: 16498499, AskOpen: 16473099, AskHigh: 16531900, AskLow: 16459200, AskClose: 16495899,Volume: 92096
Type: EventType.BAR, Ticker: USDOLLAR, Time: 2013-12-29 22:00:00, Period: 1day, BidOpen: 106910000000, BidHigh: 107050000000, BidLow: 106500000000, BidClose: 106670000000, AskOpen: 106850000000, AskHigh: 107030000000, AskLow: 106480000000, AskClose: 106640000000,Volume: 3341
Reset
Type: EventType.BAR, Ticker: EURUSD, Time: 2013-12-30 22:00:00, Period: 1day, BidOpen: 13802400, BidHigh: 13813400, BidLow: 13760299, BidClose: 13779500, AskOpen: 13797900, AskHigh: 13811900, AskLow: 13759200, AskClose: 13777400,Volume: 60476
Type: EventType.BAR, Ticker: GBPUSD, Time: 2013-12-30 22:00:00, Period: 1day, BidOpen: 16498499, BidHigh: 16579499, BidLow: 16475400, BidClose: 16572800, AskOpen: 16495899, AskHigh: 16577400, AskLow: 16473799, AskClose: 16570799,Volume: 55302
Type: EventType.BAR, Ticker: USDOLLAR, Time: 2013-12-30 22:00:00, Period: 1day, BidOpen: 106670000000, BidHigh: 106710000000, BidLow: 106470000000, BidClose: 106560000000, AskOpen: 106640000000, AskHigh: 106670000000, AskLow: 106450000000, AskClose: 106530000000,Volume: 2345
Reset

    def __init__(self):
        .....
        .....
        self.last_row = None
        self.row_saved = False
        .....
        [snip]

    def _iter_nextrow(self):
        """
        Get next row from the pandas DataFrame
        """
        try:
            return next(self.bar_stream)
        except StopIteration:
            self.continue_backtest = False
            return None, None

    def place_into_queue(self, index, row):
        """
        Place the next FXCMBarEvent onto the event queue
        """
        # Obtain all elements of the bar from the dataframe
        ticker = row["Ticker"]
        period = 86400  # Seconds in a day
        # Create the tick event for the queue
        bev = self._create_event(index, period, ticker, row)
        # Store event
        self._store_event(bev)
        # Send event to queue
        self.events_queue.put(bev)

    def stream_next(self):
        """
        Stream all bars that have the same date.
        """
        # No row saved on system startup
        if self.row_saved:
            # Get last saved row and reset
            index, row = self.last_row
            self.place_into_queue(index, row)
            self.row_saved = False

        # System starts here on first run
        while True:
            index, row = self._iter_nextrow()
            if row is None:
                return
            if self.last_row is not None:
                if index > self.last_row[0]:
                    # Save last row for later
                    self.last_row = index, row
                    self.row_saved = True
                    return
            # Update last row
            self.last_row = index, row
            self.place_into_queue(index, row)

Any ideas or comments please?

Many Thanks

James

JamesKBowler commented 6 years ago

is anyone there?