Skip to content

Commit

Permalink
Added CTRL+C interrupt handling for a graceful shutdown attempt
Browse files Browse the repository at this point in the history
  • Loading branch information
orominiyi committed Oct 9, 2018
1 parent 4725267 commit 40784a4
Show file tree
Hide file tree
Showing 14 changed files with 515 additions and 103 deletions.
92 changes: 47 additions & 45 deletions cosine/config.yaml.example
@@ -1,63 +1,65 @@
system:
EventLoop: feed
EventLoopThrottle: 0.16 # secs
network:
ssl:
CertFile: dd
EventLoop: <val> # the event loop configuration mode, "feed" or "timer"
EventLoopThrottle: <val> # event loop rate limit in seconds
network: # general network level configuration
ssl: # SSL related configuration
CertFile: <val> # [optional] path to the SSL certificate authority cert file

# general order worker related configurations
orders:
ActiveDepth: 10
ActiveDepth: <val> # active depth on each side of book respectively (bid and ask)

# set of configured venues (with their contextual configurations) to initialise for use with the order workers
venues:
cosine.venues.bem:
Username: BEMUser123
Password: BEMUserPWD123!
APIDomain: dd
APIID: asdsdvsv
ConnectSignalR: true
cosine.venues.bem: # [optional] the fully qualified module path of the BlockExMarketsVenue (CosineBaseVenue derivative) class to load + configure
Username: <val> # [venue-specific] the username of the trader account to authenticate against
Password: <val> # [venue-specific] the password of the trader account to authenticate against
APIDomain: <val> # [venue-specific] the top-level domain of the BEM venue
APIID: <val> # [venue-specific] the dedicated APIID for the BEM venue
ConnectSignalR: <val> # [venue-specific] tells BEM whether to subscribe to the async signalR feed or not, "true" or "false"

# the set of configured instruments to work markets in. Order workers will be created against each of these on the relevant venue(s)
instruments:
- "XTN/EUR"
- "RCC/Euro"
- "RCC/EUR"
- "ETH4/EUR"

# the set of configured pricing feeds to connect and subscribe to for market data consumption
feeds:
cosine.pricing.cryptocompare:
type: stream
endpoint: https://streamer.cryptocompare.com
port: 443
framework: socket.io
triangulator: https://min-api.cryptocompare.com
triangulator_throttle: 0.4 # secs
instruments:
cosine.pricing.cryptocompare: # [optional] the fully qualified module path of the CryptoCompareSocketIOFeed (CosineBaseFeed derivative) class to load + configure
type: <val> # [feed-specific] the type of connection ("stream" only for this feed)
endpoint: <val> # [feed-specific] the websockets/socket.io endpoint hostname to connect to
port: <val> # [feed-specific] the port to connect to
framework: <val> # [feed-specific] the framework for connectivity
triangulator: <val> # [feed-specific] the REST endpoint to use to pull triangulation info for implying pricing for pairs with no direct subscription
triangulator_throttle: <val> # [feed-specific] the rate limit for running triangulation queries in seconds
instruments: # the set of instruments to subscribe to
"XTN/EUR":
Ticker: "BTC"
"RCC/Euro":
BaseCCY: "ETH"
"ETH4/EUR":
Ticker: "ETH"
Ticker: <val> # ticker re-mapping for the base/top-level currency, e.g. "BTC"
BaseCCY: <val> # [optional] forces the feed to e.g. if the value is "ETH" for an RCC/EUR pair, subscribe to RCC/ETH and then run triangulation on each price tick to calculate the RCC/EUR price
"RCC/EUR": {}
"ETH4/EUR": {}

# [optional] the configured primary feed, such that when "system.EventLoop: feed", this CosineBaseFeed derivative will be configured to drive the main event loop
feed:
Primary: cosine.pricing.cryptocompare
Primary: cosine.pricing.cryptocompare # primary feed to drive the main event loop

# the set of configured pricers to pipeline for processing pricing data. Can be used to consume raw price feed data and generate theoretical pricing or other price-derived values
pricers:
Default: cosine.pricing.pricers.nullpricer
settings:
cosine.pricing.pricers.nullpricer:
nop: nop
Default: cosine.pricing.pricers.nullpricer # a comma-separated list of pricer modules to load and pipeline in-order for pricing generation
settings: # the set of pricer-specific configurations
cosine.pricing.pricers.nullpricer: {} # [pricer-specific] pricer configuration

# the configuration for the configured strategy to run
strategy:
type: cosine.strategies.noddy_floater
settings:
cosine.strategies.noddy_floater:
Spread: 0.20
MaxSpread: 0.5
instrument_settings:
type: cosine.strategies.noddy_floater # the strategy module to load and run under the algo. This contains the core business logic of the algo
settings: # the set of strategy-specific settings configurations
cosine.strategies.noddy_floater: # [optional] the noddy_floater strategy settings
Spread: <val> # [strategy-specific] the % spread to maintain around the spot mid-price, e.g. 0.20
MaxSpread: <val> # [strategy-specific] the maximum % spread based on dynamic widening of quotes, e.g. 0.50
instrument_settings: # [strategy-specific] instrument specific strategy settings
"XTN/EUR":
MinVol: 5
MaxVol: 10
"RCC/Euro":
MinVol: 5
MaxVol: 10
"ETH4/EUR":
MinVol: 5
MaxVol: 10
MinVol: <val> # [strategy-specific] minimum volume per quoted price step
MaxVol: <val> # [strategy-specific] maximum volume per quoted price step
"RCC/EUR": {}
"ETH4/EUR": {}
1 change: 1 addition & 0 deletions cosine/core/algo.py
Expand Up @@ -32,6 +32,7 @@ def __init__(self, cmdline_args, logger=None):
self._venues = None
self._base_cfg_file = None
self._environment = None
self._appname = None
self._cfg_file = None
self.logger = None
self._args = cmdline_args
Expand Down
2 changes: 1 addition & 1 deletion cosine/pricing/cryptocompare.py
Expand Up @@ -133,7 +133,7 @@ def _on_raw_tick(self, msg):
if triangulator:
return self._process_triangulator(trisym=triangulator, subdata=data)

# if it's a non-triangulated pair then just cache it as is...
# if it's a non-triangulated pair then just cache it as-is...
self._cache_price_data(data=data)

# fire main tick...
Expand Down
22 changes: 22 additions & 0 deletions cosine/strategies/base_strategy.py
Expand Up @@ -40,3 +40,25 @@ def find_instrument(self, instruments, term):
def find_by_instrument(self, instr_data, instr):
return find_by_instrument(instr_data, instr=instr)


def _get_venue_orderworkers(self, venue_module):
return self._cxt.orders[venue_module]

def _get_instruments_for_orderworkers(self, oworkers):
return map(lambda wrkr: wrkr.instrument, oworkers.values())

def _get_instruments_for_venue(self, venue_module):
bem_workers = self._get_venue_orderworkers(venue_module)
return map(lambda wrkr: wrkr.instrument, bem_workers.values())

def _capture_feed_prices(self, feed_module, instruments):
feed = self._cxt.feeds[feed_module]
return feed.capture_latest_prices(instruments=instruments)

def _run_pipelined_pricers(self, prices, cls=None):
cls = cls if cls is not None else self.__name__
for p in self._cxt.pricer_seq:
self.logger.debug(f"{cls} - calc pricing: [{p}]")
prices = self._cxt.pricers[p].generate_theo_prices(instrument_prices=prices)
return prices

49 changes: 49 additions & 0 deletions cosine/strategies/multi_strategy.py
@@ -0,0 +1,49 @@
"""
#
# 09/10/2018
# Oladotun Rominiyi - Copyright © 2018. all rights reserved.
"""
__author__ = 'dotun rominiyi'

# IMPORTS
import random
from decimal import Decimal
from cosine.core.order_worker import Pos
from cosine.strategies import locate_strategy
from .base_strategy import CosineBaseStrategy


# MODULE CLASSES
class CosineMultiStrategy(CosineBaseStrategy):

def __init__(self, cfg, cxt, venues, pool, logger=None, **kwargs):
super().__init__(cfg, cxt, venues, pool, logger=logger, **kwargs)
self.strategies = map(lambda strat_name: self._create_strategy(strat_name), kwargs['strategies'])


def setup(self):
for strategy in self.strategies:
strategy.setup()


def teardown(self):
for strategy in self.strategies:
strategy.teardown()


def update(self):
self.logger.debug("MultiStrategy - ** update **")
for strategy in self.strategies:
strategy.update()
self.logger.debug("MultiStrategy - ** update complete **")


def _create_strategy(self, strat_name):
strat = self._cfg.get("strategy", {})
strategy_def = strat.get("settings", {}).get(strat_name, {}, split=False)
StrategyClass = locate_strategy(module_name=strat_name)
if not StrategyClass:
raise ValueError("MultiStrategy - Failed to identify a valid strategy")
self.logger.info(f"MultiStrategy - Loading CosineStrategy: [{strat_name}]")
strategy = StrategyClass(self._cfg, self._cxt, self._venues, self._pool, logger=self.logger, **strategy_def)
return strategy
11 changes: 4 additions & 7 deletions cosine/strategies/noddy_floater.py
Expand Up @@ -23,18 +23,15 @@ def update(self):
self.logger.debug("NoddyFloaterStrategy - ** update **")

# pull instruments...
bem_workers = self._cxt.orders['cosine.venues.bem']
instruments = map(lambda wrkr: wrkr.instrument, bem_workers.values())
bem_workers = self._get_venue_orderworkers('cosine.venues.bem')
instruments = self._get_instruments_for_orderworkers(bem_workers)

# pull prices for instruments...
self.logger.debug("NoddyFloaterStrategy - source instrument prices from feed cache...")
feed = self._cxt.feeds['cosine.pricing.cryptocompare']
prices = feed.capture_latest_prices(instruments=instruments)
prices = self._capture_feed_prices('cosine.pricing.cryptocompare', instruments=instruments)

# massage pricing...
for p in self._cxt.pricer_seq:
self.logger.debug(f"NoddyFloaterStrategy - calc pricing: [{p}]")
prices = self._cxt.pricers[p].generate_theo_prices(instrument_prices=prices)
prices = self._run_pipelined_pricers(prices, "NoddyFloaterStrategy")

# update the order quotes...
self.logger.debug("NoddyFloaterStrategy - updating quotes...")
Expand Down
2 changes: 1 addition & 1 deletion cosine/venues/base_venue.py
Expand Up @@ -177,7 +177,7 @@ def update(self):
pass

def on(self, event_name, handler):
raise NotImplementedError()
pass

def get_instrument_defs(self):
raise NotImplementedError()
Expand Down

0 comments on commit 40784a4

Please sign in to comment.