def update(self, data):
        recordIndex = ''

        if len(data) == 4:
            # data comming from archive file (mode simulation)
            recordIndex, timestampMilliSec, priceFloat, volumeFloat = data
        else:
            # data comming from exchange (mode real time)
            timestampMilliSec, priceFloat, volumeFloat = data

        secondaryData = False

        while not secondaryData:
            secondaryData = self.aggregateSecondaryData(
                timestampMilliSec, priceFloat, volumeFloat)

        #calling the criterion to check if it should raise an alarm
        criterionData = self.criterion.check(secondaryData)

        # sending the secondary data to the archiver so that the sd are written in the
        # sd file to enable viewing them in a price/volume chart. Note that the archiver
        # takes care of implementing the secondary data record index.
        self.archiver.update(secondaryData + criterionData)

        if self.isVerbose:
            print("SecondaryDataAggregator: {} {} {} {}".format(
                recordIndex, timestampMilliSec, priceFloat, volumeFloat))

        SeqDiagBuilder.recordFlow(
        )  # called to build the sequence diagram. Can be commented out later ...
Exemple #2
0
    def testStartModeRealtimeWithDurationInSecondsBuildSeqDiag(self):
        controller = Controller()
        duration = 2
        print("running c2 in real time mode for {} seconds".format(duration))

        SeqDiagBuilder.activate(parentdir, 'Controller',
                                'start')  # activate sequence diagram building

        #IMPORTANT: when forcing execution parms, no space separate parm name and parm value !
        try:
            controller.start(['-mr', '-d{}'.format(duration)])
            time.sleep(duration)
        except SystemExit:
            pass

        csvPrimaryDataFileName = controller.primaryDataFileName
        csvSecondaryDataFileName = controller.buildSecondaryFileNameFromPrimaryFileName(
            csvPrimaryDataFileName, "secondary")

        os.remove(csvPrimaryDataFileName)
        os.remove(csvSecondaryDataFileName)

        commands = SeqDiagBuilder.createSeqDiaqCommands('USER')

        with open("c:\\temp\\ess.txt", "w") as f:
            f.write(commands)

        SeqDiagBuilder.deactivate()  # deactivate sequence diagram building
Exemple #3
0
    def check(self, data):
        '''
        Check if the criterion is reached.

        :seqdiag_note method to be implemented by Philippe
        '''
        SeqDiagBuilder.recordFlow(
        )  # called to build the sequence diagram. Can be commented out later ...

        return data
Exemple #4
0
    def update(self, data):
        self.recordIndex += 1
        timestampMilliSecCriterion = -1
        priceFloatCriterion = -1
        volumeFloatCriterion = -1
        if len(data) == 6:
            # data comming from archive file (mode simulation)
            timestampMilliSec, priceFloat, volumeFloat, timestampMilliSecCriterion, priceFloatCriterion, volumeFloatCriterion = data
        else:
            # data comming from exchange (mode real time)
            timestampMilliSec, priceFloat, volumeFloat = data

        self.writer.writerow([self.recordIndex, timestampMilliSec, priceFloat, volumeFloat, timestampMilliSecCriterion, priceFloatCriterion, volumeFloatCriterion])

        if self.isVerbose:
            print("{} {} {} {}".format(self.recordIndex, timestampMilliSec, priceFloat, volumeFloat, timestampMilliSecCriterion, priceFloatCriterion, volumeFloatCriterion))

        SeqDiagBuilder.recordFlow() # called to build the sequence diagram. Can be commented out later ...
Exemple #5
0
    def update(self, data):
        SeqDiagBuilder.recordFlow(
        )  # called to build the sequence diagram. Can be commented out later ...

        self.recordIndex += 1

        try:
            if len(data) == 4:
                # data comming from archive file (mode simulation)
                timestampMilliSec, numberOfTrades, volumeFloat, priceFloat = data
                self.writer.writerow([
                    self.recordIndex, numberOfTrades, timestampMilliSec,
                    "{:.6f}".format(volumeFloat), "{:.2f}".format(priceFloat)
                ])

                if self.isVerbose:
                    print("{} {} {} {}".format(self.recordIndex,
                                               timestampMilliSec, priceFloat,
                                               volumeFloat))
            else:
                # data comming from exchange (mode real time)
                timestampMilliSec, volumeFloat, priceFloat = data
                self.writer.writerow([
                    self.recordIndex, timestampMilliSec,
                    "{:.6f}".format(volumeFloat), "{:.2f}".format(priceFloat)
                ])

                if self.isVerbose:
                    print("{} {} {} {}".format(self.recordIndex,
                                               timestampMilliSec, priceFloat,
                                               volumeFloat))
        except ValueError as e:
            '''
            This happens sometimes when C2 is started in mode RT for a specified duration.
            When the duration is reached, all the Observers, namely the Archivers, are closed 
            preamptively, which sometimes causes this exception due to a tentative to write the last 
            received data into an already closed file.
            '''
            if str(e) == 'I/O operation on closed file.':
                print(
                    'Last real time data received after closing {}. Consequence: {} not saved/processed !'
                    .format(self.filename, data))
            else:
                raise e
    def aggregateSecondaryData(self, timestampMilliSec, priceFloat,
                               volumeFloat):
        '''
        This method is called each time primary data are received. It aggregates the passed
        primary data and returns None if the aggregation interval (typically 1 second9 is not
        reached or the tuple timestampMilliSec, priceFloat, volumeFloat once data
        for the aggregation interval was reached.

        :seqdiag_note method to be implemented by Philippe

        :param timestampMilliSec:
        :param priceFloat:
        :param volumeFloat:
        :return: timestampMilliSec, priceFloat, volumeFloat
        '''

        SeqDiagBuilder.recordFlow(
        )  # called to build the sequence diagram. Can be commented out later ...

        return timestampMilliSec, priceFloat, volumeFloat  # temporally returning silly value !
    def update(self, data):
        recordIndex = ''

        if len(data) == 4:
            # data comming from archive file (mode simulation)
            recordIndexStr, timestampMilliSecStr, volumeFloatStr, priceFloatStr = data
        else:
            # data comming from exchange (mode real time)
            timestampMilliSecStr, volumeFloatStr, priceFloatStr = data

        timestampMilliSec = int(timestampMilliSecStr)

        if self.lastSecBeginTimestamp == 0:
            startTimestamp = self.calculateStartTimestamp(timestampMilliSec)
            self.lastSecBeginTimestamp = startTimestamp
            self.lastSecEndTimestamp = startTimestamp + 1000
            self.lastSecTradeNumber = 0
            self.lastSecVolume = 0
            self.lastSecPriceVolumeTotal = 0
        else:
            priceFloat = float(priceFloatStr)
            volumeFloat = float(volumeFloatStr)
            lastNotifiedPriceFloat = priceFloat
            lastNotifiedVolumeFloat = volumeFloat
            secondary_data = self.aggregateSecondaryData(
                timestampMilliSec, priceFloat, volumeFloat)
            if not secondary_data:
                return
            catchUpSdTimestamp, catchUpSdTradesNumber, catchUpSdVolumeFloat, sdPricefloat = secondary_data

            # calling the criterion to check if it should raise an alarm
            criterionData = self.criterion.check(data)

        if self.isOneSecondIntervalReached:
            # sending the secondary data to the archiver so that the sd are written in the
            # sd file to enable viewing them in a price/volume chart. Note that the archiver
            # takes care of implementing the secondary data record index.
            #            self.archiver.update((sdTimestamp, sdTradesNumber, sdVolumeFloat, sdPricefloat) + criterionData)
            if sdPricefloat and sdPricefloat > 0:
                self.storeAndPrintSecondaryData(sdPricefloat,
                                                catchUpSdTimestamp,
                                                catchUpSdTradesNumber,
                                                catchUpSdVolumeFloat)

            wasTimeCaughtUp = False
            catchUpSdTradesNumber = 0
            catchUpSdVolumeFloat = 0
            catchUpSdPricefloat = 0

            while int(timestampMilliSec /
                      1000) * 1000 > self.lastSecEndTimestamp:
                wasTimeCaughtUp = True
                catchUpSdTimestamp = self.lastSecEndTimestamp
                if self.lastSecVolume > 0:
                    # calculating the price to display for the 0 volume catchup secondary data
                    catchUpSdPricefloat = self.lastSecPriceVolumeTotal / self.lastSecVolume
                    self.storeAndPrintSecondaryData(catchUpSdPricefloat,
                                                    catchUpSdTimestamp,
                                                    catchUpSdTradesNumber,
                                                    catchUpSdVolumeFloat)
                else:
                    # happens if no transaction were yet processed
                    catchUpSdPricefloat = 0

                self.lastSecBeginTimestamp += 1000
                self.lastSecEndTimestamp += 1000

            if wasTimeCaughtUp:
                self.lastSecVolume = lastNotifiedVolumeFloat
                self.lastSecPriceVolumeTotal = lastNotifiedVolumeFloat * lastNotifiedPriceFloat
                self.lastSecTradeNumber = 1
                self.lastSecBeginTimestamp += 1000
                self.lastSecEndTimestamp += 1000
                self.isOneSecondIntervalReached = False

        SeqDiagBuilder.recordFlow(
        )  # called to build the sequence diagram. Can be commented out later ...
    def aggregateSecondaryData(self, timestampMilliSec, priceFloat,
                               volumeFloat):
        '''
        This method is called each time primary data are received. It aggregates the passed
        primary data and returns None if the aggregation interval (typically 1 second9 is not
        reached or the tuple timestampMilliSec, priceFloat, volumeFloat once data
        for the aggregation interval was reached.

        :seqdiag_note method to be implemented by Philippe

        :param timestampMilliSec:
        :param priceFloat:
        :param volumeFloat:
        :return: timestampMilliSec, priceFloat, volumeFloat
        '''

        SeqDiagBuilder.recordFlow(
        )  # called to build the sequence diagram. Can be commented out later ...

        if timestampMilliSec >= self.lastSecBeginTimestamp and timestampMilliSec < self.lastSecEndTimestamp:
            # here, the current primary data ts is within the current second frame
            self.lastSecTradeNumber += 1
            self.lastSecVolume += volumeFloat
            self.lastSecPriceVolumeTotal += priceFloat * volumeFloat

            return None, None, None, None  # since we are still within the current second time frame !
        elif timestampMilliSec >= self.lastSecEndTimestamp:
            if timestampMilliSec < self.lastSecEndTimestamp + 1000:
                # here, the current primary data ts is within the next second frame
                lastSecBeginTimestamp = self.lastSecBeginTimestamp
                lastSecTradeNumber = self.lastSecTradeNumber
                lastSecVolume = self.lastSecVolume

                if self.lastSecVolume > 0:
                    lastSecAvgPrice = self.lastSecPriceVolumeTotal / self.lastSecVolume
                else:
                    # happens if no transaction were yet processed, at start of RT or simulation
                    lastSecAvgPrice = 0

                self.lastSecTradeNumber = 1
                self.lastSecVolume = volumeFloat
                self.lastSecPriceVolumeTotal = priceFloat * volumeFloat
                self.lastSecBeginTimestamp += 1000
                self.lastSecEndTimestamp += 1000
                self.isOneSecondIntervalReached = True

                return lastSecBeginTimestamp, lastSecTradeNumber, lastSecVolume, lastSecAvgPrice
            else:
                # here, the current primary data ts is after the next second frame
                lastSecBeginTimestamp = self.lastSecBeginTimestamp
                lastSecTradeNumber = self.lastSecTradeNumber
                lastSecVolume = self.lastSecVolume

                if self.lastSecVolume > 0:
                    lastSecAvgPrice = self.lastSecPriceVolumeTotal / self.lastSecVolume
                else:
                    # happens if no transaction were yet processed, at start of RT or simulation
                    lastSecAvgPrice = 0

                self.isOneSecondIntervalReached = True

                return lastSecBeginTimestamp, lastSecTradeNumber, lastSecVolume, lastSecAvgPrice
        else:
            # here, the current primary data ts is before the current second frame. This
            # situation occurs at the very start of receiving the RT data or when processing
            # the first lines of the primary data input file in simulation mode when the ts
            # of those lines is before the ts calculated by the calculateStartTimestamp() method.
            # In this case, the line must simply be ignored.
            return None
Exemple #9
0
    def testStartModeRTBuildSeqDiag(self):
        controller = Controller()
        classCtorArgsDic = {
            'ArchivedDatasource': ['primary-two.csv'],
            'SecondaryDataAggregator': ['secondary.csv', False],
            'Archiver': ['secondary.csv', 'csv dummy col titles', False]
        }

        # SeqDiagBuilder.activate(parentdir, 'Controller', 'start',
        #                         classCtorArgsDic)  # activate sequence diagram building

        duration = 3

        # IMPORTANT: when forcing execution parms, no space separate parm name and parm value !
        try:
            controller.start(['-mr', '-d{}'.format(duration)])
        except SystemExit:
            pass

        while not controller.wasStopped():
            print('controller still working')

        csvPrimaryDataFileName = controller.primaryDataFileName
        csvSecondaryDataFileName = controller.buildSecondaryFileNameFromPrimaryFileName(
            csvPrimaryDataFileName, "secondary")

        commands = SeqDiagBuilder.createSeqDiaqCommands(
            actorName='USER',
            title='C2 simulation mode sequence diagram',
            maxSigArgNum=None,
            maxSigCharLen=15,
            maxNoteCharLen=23)

        with open("c:\\temp\\C2 RT mode sequence diagram.txt", "w") as f:
            f.write(commands)

        SeqDiagBuilder.deactivate()  # deactivate sequence diagram building

        os.remove(csvPrimaryDataFileName)
        os.remove(csvSecondaryDataFileName)

        self.assertEqual(
            '''@startuml

title C2 simulation mode sequence diagram
actor USER
participant Controller
    /note over of Controller
        Entry point of the C2 application.
        When started at the commandline,
        accepts parameters like RT or
        simulation mode.
    end note
participant ArchivedDatasource
    /note over of ArchivedDatasource
        In simulation mode, reads data
        line by line from a primary data
        file. For every data line read,
        calls the notifyObservers(data)
        method of its parent class,
        Observable.
    end note
participant Observable
    /note over of Observable
        Pivot class in the Observable
        design pattern. Each time its
        notifyObservers(data) method is
        called, Observable notifies its
        subscribed Observers of the
        received data calling update(data)
        on each registered Observer.
    end note
participant SecondaryDataAggregator
    /note over of SecondaryDataAggregator
        Implements the Observer part in
        the Observable design pattern.
        Each tima its update(data) method
        is called, it adds this data to
        the current secondary aggreagated
        data and sends the secondary data
        when appropriate to the Criterion
        calling its check() method.
    end note
participant PriceVolumeCriterion
    /note over of PriceVolumeCriterion
        Inherits from Criterion. Is
        responsible for computing if an
        alarm must be raised. Performs its
        calculation each time its check()
        method is called.
    end note
participant Archiver
    /note over of Archiver
        In simulation mode, this Observer
        (Archiver, like SecondaryData
        Aggregator, inherits from
        Observer) writes the secondary
        data on disk. In real time mode,
        saves on disk the primary data.
    end note
USER -> Controller: start(...)
    activate Controller
    Controller -> ArchivedDatasource: processArchivedData()
        activate ArchivedDatasource
        ArchivedDatasource -> Observable: notifyObservers(data)
            activate Observable
            Observable -> SecondaryDataAggregator: update(data)
                activate SecondaryDataAggregator
                SecondaryDataAggregator -> SecondaryDataAggregator: aggregateSecondaryData(...)
                    activate SecondaryDataAggregator
                    note right
                        method to be implemented by
                        Philippe
                    end note
                    SecondaryDataAggregator <-- SecondaryDataAggregator: 
                    deactivate SecondaryDataAggregator
                SecondaryDataAggregator -> PriceVolumeCriterion: check(data)
                    activate PriceVolumeCriterion
                    note right
                        method to be implemented by
                        Philippe
                    end note
                    SecondaryDataAggregator <-- PriceVolumeCriterion: 
                    deactivate PriceVolumeCriterion
                SecondaryDataAggregator -> Archiver: update(data)
                    activate Archiver
                    SecondaryDataAggregator <-- Archiver: 
                    deactivate Archiver
                Observable <-- SecondaryDataAggregator: 
                deactivate SecondaryDataAggregator
            ArchivedDatasource <-- Observable: 
            deactivate Observable
        Controller <-- ArchivedDatasource: 
        deactivate ArchivedDatasource
    USER <-- Controller: 
    deactivate Controller
@enduml''', commands)