mirror of
				https://github.com/bspeice/metrik
				synced 2025-11-03 18:00:51 -05:00 
			
		
		
		
	Add tradeking into the Equities flow
This commit is contained in:
		@ -1,28 +1,35 @@
 | 
				
			|||||||
from luigi.task import WrapperTask
 | 
					from luigi.task import Task
 | 
				
			||||||
from luigi.parameter import DateMinuteParameter, BoolParameter
 | 
					from luigi.parameter import DateMinuteParameter, BoolParameter
 | 
				
			||||||
import pandas as pd
 | 
					import pandas as pd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from metrik.trading_days import is_trading_day
 | 
					from metrik.trading_days import is_trading_day
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Flow(WrapperTask):
 | 
					class Flow(Task):
 | 
				
			||||||
    present = DateMinuteParameter()
 | 
					    present = DateMinuteParameter()
 | 
				
			||||||
    live = BoolParameter()
 | 
					    live = BoolParameter()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, force=False, *args, **kwargs):
 | 
					    def __init__(self, force=False, *args, **kwargs):
 | 
				
			||||||
        super(Flow, self).__init__(*args, **kwargs)
 | 
					        super(Flow, self).__init__(*args, **kwargs)
 | 
				
			||||||
        self.force = force
 | 
					        self.force = force
 | 
				
			||||||
 | 
					        self.did_run = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def complete(self):
 | 
				
			||||||
 | 
					        return self.did_run
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @staticmethod
 | 
					    @staticmethod
 | 
				
			||||||
    def get_schedule():
 | 
					    def get_schedule():
 | 
				
			||||||
        raise NotImplementedError('Your flow should know when it should be run.')
 | 
					        raise NotImplementedError('Your flow should know when it should be run.')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _requires(self):
 | 
					    def _run(self):
 | 
				
			||||||
        raise NotImplementedError('I need to know what tasks should be run!')
 | 
					        raise NotImplementedError('I need to know what tasks should be run!')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def requires(self):
 | 
					    def run(self):
 | 
				
			||||||
        if self.force or self.get_schedule().check_trigger(self.present):
 | 
					        if self.force or self.get_schedule().check_trigger(self.present):
 | 
				
			||||||
            return self._requires()
 | 
					            for yielded in self._run():
 | 
				
			||||||
 | 
					                yield yielded
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.did_run = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Schedule(object):
 | 
					class Schedule(object):
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,34 @@
 | 
				
			|||||||
from metrik.flows.base import Flow, MarketClose
 | 
					from metrik.flows.base import Flow, MarketClose
 | 
				
			||||||
from metrik.tasks.nasdaq import NasdaqETFList, NasdaqCompanyList
 | 
					from metrik.tasks.nasdaq import NasdaqETFList, NasdaqCompanyList
 | 
				
			||||||
 | 
					from metrik.tasks.tradeking import Tradeking1mTimesales
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class EquitiesFlow(Flow):
 | 
					class EquitiesFlow(Flow):
 | 
				
			||||||
 | 
					    def __init__(self, *args, **kwargs):
 | 
				
			||||||
 | 
					        super(EquitiesFlow, self).__init__(*args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @staticmethod
 | 
					    @staticmethod
 | 
				
			||||||
    def get_schedule():
 | 
					    def get_schedule():
 | 
				
			||||||
        return MarketClose()
 | 
					        return MarketClose()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _requires(self):
 | 
					    def _run(self):
 | 
				
			||||||
        return [NasdaqETFList(current_datetime=self.present, live=self.live),
 | 
					        # When we yield dependencies in `run` instead of `_requires`,
 | 
				
			||||||
                NasdaqCompanyList(current_datetime=self.present,
 | 
					        # they get executed dynamically and we can use their results inline
 | 
				
			||||||
                                  live=self.live)]
 | 
					        etfs = NasdaqETFList(current_datetime=self.present, live=self.live)
 | 
				
			||||||
 | 
					        companies = NasdaqCompanyList(current_datetime=self.present,
 | 
				
			||||||
 | 
					                                      live=self.live)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        yield etfs
 | 
				
			||||||
 | 
					        yield companies
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tradeking_etfs = [Tradeking1mTimesales(
 | 
				
			||||||
 | 
					            present=self.present.date(),
 | 
				
			||||||
 | 
					            symbol=e['Symbol']
 | 
				
			||||||
 | 
					        ) for e in etfs.output().retrieve()['etfs']]
 | 
				
			||||||
 | 
					        yield tradeking_etfs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tradeking_companies = [Tradeking1mTimesales(
 | 
				
			||||||
 | 
					            present=self.present.date(),
 | 
				
			||||||
 | 
					            symbol=c['Symbol']
 | 
				
			||||||
 | 
					        ) for c in companies.output().retrieve()['companies']]
 | 
				
			||||||
 | 
					        yield tradeking_companies
 | 
				
			||||||
 | 
				
			|||||||
@ -91,9 +91,8 @@ class MongoRateLimit(object):
 | 
				
			|||||||
            '_created_at': present, 'service': service
 | 
					            '_created_at': present, 'service': service
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def sleep_until(self, present, interval, backoff):
 | 
					    def sleep_for(self, interval, backoff):
 | 
				
			||||||
        future_time = present + interval * backoff
 | 
					        return interval.total_seconds() * backoff
 | 
				
			||||||
        return (future_time - present).total_seconds()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def acquire_lock(self, service, limit, interval, max_tries=5, backoff=.5):
 | 
					    def acquire_lock(self, service, limit, interval, max_tries=5, backoff=.5):
 | 
				
			||||||
        num_tries = 0
 | 
					        num_tries = 0
 | 
				
			||||||
@ -109,13 +108,7 @@ class MongoRateLimit(object):
 | 
				
			|||||||
                self.save_lock(self.get_present(), service)
 | 
					                self.save_lock(self.get_present(), service)
 | 
				
			||||||
                return True
 | 
					                return True
 | 
				
			||||||
            elif num_tries < max_tries:
 | 
					            elif num_tries < max_tries:
 | 
				
			||||||
                sleep_amount = self.sleep_until(
 | 
					                sleep_amount = self.sleep_for(interval, backoff)
 | 
				
			||||||
                    self.get_present(),
 | 
					 | 
				
			||||||
                    interval,
 | 
					 | 
				
			||||||
                    backoff
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
                sleep(sleep_amount)
 | 
					                sleep(sleep_amount)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return False
 | 
					        return False
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -35,10 +35,8 @@ class Tradeking1mTimesales(MongoCreateTask):
 | 
				
			|||||||
    present = DateParameter()
 | 
					    present = DateParameter()
 | 
				
			||||||
    symbol = Parameter()
 | 
					    symbol = Parameter()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def acquire_lock(self, service, limit, interval, max_tries=5, backoff=.5):
 | 
					    def get_collection_name(self):
 | 
				
			||||||
        return super(Tradeking1mTimesales, self).acquire_lock(
 | 
					        return 'tradeking_1min'
 | 
				
			||||||
            'tradeking', 60, timedelta(minutes=1)
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @staticmethod
 | 
					    @staticmethod
 | 
				
			||||||
    def retrieve_data(present, symbol):
 | 
					    def retrieve_data(present, symbol):
 | 
				
			||||||
 | 
				
			|||||||
@ -92,4 +92,39 @@ class RateLimitTest(MongoTest):
 | 
				
			|||||||
        # Check that we acquired the lock
 | 
					        # Check that we acquired the lock
 | 
				
			||||||
        assert did_acquire
 | 
					        assert did_acquire
 | 
				
			||||||
        # Check that we only used one backoff period
 | 
					        # Check that we only used one backoff period
 | 
				
			||||||
        assert (end - start).total_seconds() < 2
 | 
					        total_seconds = (end - start).total_seconds()
 | 
				
			||||||
 | 
					        assert 1 < total_seconds < 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_acquire_lock_succeeds_float(self):
 | 
				
			||||||
 | 
					        # And do the whole thing all over again, but with a floating backoff
 | 
				
			||||||
 | 
					        service = 'testing_ratelimit'
 | 
				
			||||||
 | 
					        limit = 1
 | 
				
			||||||
 | 
					        interval = timedelta(seconds=1)
 | 
				
			||||||
 | 
					        max_tries = 2
 | 
				
			||||||
 | 
					        backoff = 1.01
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # The first scenario is as follows:
 | 
				
			||||||
 | 
					        # We try to acquire a lock with two tries, backoff is 1.
 | 
				
			||||||
 | 
					        # We put a single lock in initially (a half second in the past),
 | 
				
			||||||
 | 
					        # thus when we try to acquire on the first try, we should fail.
 | 
				
			||||||
 | 
					        # However, the backoff should kick in, and we acquire successfully
 | 
				
			||||||
 | 
					        # on the second try
 | 
				
			||||||
 | 
					        ratelimit = MongoRateLimit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        start = datetime.now()
 | 
				
			||||||
 | 
					        ratelimit.save_lock(start - timedelta(seconds=.5), service)
 | 
				
			||||||
 | 
					        did_acquire = ratelimit.acquire_lock(service, limit, interval,
 | 
				
			||||||
 | 
					                                             max_tries, backoff)
 | 
				
			||||||
 | 
					        end = datetime.now()
 | 
				
			||||||
 | 
					        # Check that we acquired the lock
 | 
				
			||||||
 | 
					        assert did_acquire
 | 
				
			||||||
 | 
					        # Check that we only used one backoff period
 | 
				
			||||||
 | 
					        total_seconds = (end - start).total_seconds()
 | 
				
			||||||
 | 
					        assert 1 < total_seconds < 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_sleep_for_gives_correct_time(self):
 | 
				
			||||||
 | 
					        ratelimit = MongoRateLimit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert ratelimit.sleep_for(timedelta(seconds=1), 1) == 1
 | 
				
			||||||
 | 
					        assert ratelimit.sleep_for(timedelta(seconds=1), 2) == 2
 | 
				
			||||||
 | 
					        assert ratelimit.sleep_for(timedelta(seconds=1), 1.1) == 1.1
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user