Skip to content
This repository was archived by the owner on Oct 5, 2021. It is now read-only.

Commit 04568f8

Browse files
committed
refactoring strategy registration, fixing configs
1 parent 67dc3d4 commit 04568f8

File tree

9 files changed

+82
-43
lines changed

9 files changed

+82
-43
lines changed

algocoin/lib/config.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
@config
66
class ExchangeConfig:
77
exchange_type = ExchangeType, ExchangeType.NONE
8-
exchange_types = [ExchangeType], [ExchangeType.NONE]
8+
exchange_types = [ExchangeType], []
99
trading_type = TradingType, TradingType.NONE
1010

1111

@@ -29,7 +29,7 @@ class ExecutionConfig:
2929

3030
@config
3131
class StrategyConfig:
32-
classname = str, ''
32+
clazz = type
3333
args = tuple, ()
3434
kwargs = dict, {}
3535

@@ -42,4 +42,4 @@ class TradingEngineConfig:
4242
backtest_options = BacktestConfig, BacktestConfig()
4343
risk_options = RiskConfig, RiskConfig()
4444
execution_options = ExecutionConfig, ExecutionConfig()
45-
strategy_options = [StrategyConfig], [StrategyConfig()] # List of strategy options
45+
strategy_options = [StrategyConfig], [] # List of strategy options

algocoin/lib/parser.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from configparser import ConfigParser
2-
from .config import TradingEngineConfig, BacktestConfig
2+
from pydoc import locate
3+
from .config import TradingEngineConfig, BacktestConfig, StrategyConfig
34
from .enums import TradingType
45
from .exceptions import ConfigException
56
from .utils import str_to_exchange, exchange_to_file, set_verbose
@@ -63,8 +64,19 @@ def _parse_exchange(exchange, config) -> None:
6364

6465

6566
def _parse_strategy(strategy, config) -> None:
66-
# TODO
67-
pass
67+
strat_configs = []
68+
69+
if 'strategies' not in strategy:
70+
raise Exception('No Strategies specified')
71+
72+
for strat in strategy['strategies'].split('\n'):
73+
if strat == '':
74+
continue
75+
splits = [x for x in strat.split(',') if x]
76+
cls = locate(splits[0])
77+
args = tuple(splits[1:]) if len(splits) > 1 else ()
78+
strat_configs.append(StrategyConfig(clazz=cls, args=args))
79+
config.strategy_options = strat_configs
6880

6981

7082
def _parse_risk(risk, config) -> None:
@@ -114,12 +126,16 @@ def _parse_sandbox_options(argv, config) -> None:
114126

115127
def _parse_backtest_options(argv, config) -> None:
116128
log.critical("Backtesting")
129+
117130
config.backtest_options = BacktestConfig()
131+
118132
config.backtest_options.file = \
119133
exchange_to_file(str_to_exchange(argv.get('exchange', '')))
134+
120135
config.exchange_options.exchange_type = \
121136
str_to_exchange(argv.get('exchange', ''))
122-
config.risk_options.total_funds = 20000.0
137+
138+
config.risk_options.total_funds = 20000.0 # FIXME
123139

124140

125141
def parse_command_line_config(argv: list) -> TradingEngineConfig:

algocoin/lib/utils.py

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ def create_pair(key: str, typ: type, default=None, container=None) -> property:
2020
def get(self):
2121
if hasattr(self, '__' + str(key)):
2222
return getattr(self, '__' + str(key))
23-
if default is not None and type(default) == typ:
23+
if default is not None and (type(default) == typ or (container == list and (default == [] or type(default[0]) == typ))):
2424
if container:
2525
if container == list:
26-
return [default]
26+
return default
2727
else:
2828
raise TypeError('Unrecognized container: %s',
2929
str(container))
@@ -53,33 +53,55 @@ def config(cls):
5353
vars = []
5454
for k, v in cls.__dict__.items():
5555
if isinstance(v, type):
56+
# V is a type, no default value
5657
v = create_pair(k, v)
5758
vars.append(k)
5859
elif isinstance(v, tuple) and \
5960
isinstance(v[0], type) and \
6061
isinstance(v[1], v[0]):
62+
# v is a pair, (type, instance)
6163
v = create_pair(k, v[0], v[1])
6264
vars.append(k)
6365
elif isinstance(v, list) and \
6466
isinstance(v[0], type):
67+
# v is a list [type]
6568
v = create_pair(k, v[0], container=list)
6669
vars.append(k)
6770
elif isinstance(v, tuple) and \
6871
isinstance(v[0], list) and \
6972
isinstance(v[0][0], type) and \
70-
isinstance(v[1], list) and \
71-
isinstance(v[1][0], v[0][0]):
72-
v = create_pair(k, v[0][0], v[1][0], container=list)
73-
vars.append(k)
73+
isinstance(v[1], list):
74+
# v is a pair, ([type], [instance?])
75+
if len(v[1]) > 0 and isinstance(v[1][0], v[0][0]): # TODO check all
76+
v = create_pair(k, v[0][0], v[1], container=list)
77+
vars.append(k)
78+
elif v[1] == []:
79+
v = create_pair(k, v[0][0], v[1], container=list)
80+
vars.append(k)
81+
else:
82+
print(v)
83+
raise Exception('Unexpected list instance: %s' % v[1][0])
7484

7585
new_cls_dict[k] = v
86+
new_cls_dict['__init__'] = __init__config
7687
new_cls_dict['__repr__'] = __repr__
7788
new_cls_dict['_vars'] = vars
7889
new_cls_dict['_excludes'] = []
7990
return type(cls)(cls.__name__, cls.__bases__, new_cls_dict)
8091

8192

82-
def __init__(self, **kwargs) -> None:
93+
def __init__config(self, **kwargs) -> None:
94+
for item in self._vars:
95+
if item not in kwargs:
96+
log.debug('WARNING %s unset!', item)
97+
else:
98+
setattr(self, item, kwargs.get(item))
99+
for k, v in kwargs.items():
100+
if k not in self._vars:
101+
raise Exception('Attribute not found! %s' % k)
102+
103+
104+
def __init__struct(self, **kwargs) -> None:
83105
for item in self._vars:
84106
if item not in kwargs:
85107
pass
@@ -132,7 +154,7 @@ def struct(cls):
132154
# v = create_pair(k, v[0])
133155

134156
new_cls_dict[k] = v
135-
new_cls_dict['__init__'] = __init__
157+
new_cls_dict['__init__'] = __init__struct
136158
new_cls_dict['__repr__'] = __repr__
137159
new_cls_dict['_vars'] = vars
138160
new_cls_dict['_excludes'] = excludes

algocoin/main.py

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
import tornado
22
import threading
3-
# from .custom_strategies import CustomStrategy
4-
# from .lib.strategies.sma_crosses_strategy import SMACrossesStrategy
5-
from .lib.strategies.test_strat import TestStrategy
63
from .trading import TradingEngine
7-
# from .lib.parser import parse_command_line_config, parse_config
84
from .lib.parser import parse_command_line_config
95
from .lib.logging import LOG as log
106
from .ui.server import ServerApplication
@@ -14,33 +10,16 @@ def main(argv: list) -> None:
1410
config = parse_command_line_config(argv)
1511

1612
port = 8889
17-
# config = parse_config(argł)
1813

1914
# Instantiate trading engine
2015
#
2116
# The engine is responsible for managing the different components,
2217
# including the strategies, the bank/risk engine, and the
2318
# exchange/backtest engine.
19+
2420
te = TradingEngine(config)
2521
application = ServerApplication(te)
2622

27-
# A sample strategy that impelements the correct interface
28-
# ts = CustomStrategy(50)
29-
# ts2 = SMACrossesStrategy(5, 10)
30-
ts = TestStrategy()
31-
te.registerStrategy(ts)
32-
# te.registerStrategy(ts)
33-
# te.registerStrategy(ts2)
34-
35-
# for i in [5, 10, 20, 25, 50, 100]:
36-
# for j in [10, 20, 25, 75, 100, 150, 200]:
37-
# if j > i:
38-
# ts = CustomStrategy(i, j)
39-
40-
# # Register the strategy with the Trading engine
41-
# log.critical("registering %d - %d", i, j)
42-
# te.registerStrategy(ts)
43-
4423
log.critical('Server listening on port: %s', port)
4524
application.listen(port)
4625
t = threading.Thread(target=tornado.ioloop.IOLoop.current().start)

algocoin/tests/test_config.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ def teardown_class(cls):
2525
def test_ExchangeConfig(self):
2626
x = ExchangeConfig()
2727
assert(x.exchange_type == ExchangeType.NONE)
28-
assert(x.exchange_types == [ExchangeType.NONE])
28+
print(x.exchange_types)
29+
assert(x.exchange_types == [])
2930
assert(x.trading_type == TradingType.NONE)
3031

3132
def test_BacktestConfig(self):

algocoin/tests/test_main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,5 @@ def test_parse_command_line(self):
4343
def test_main(self):
4444
from ..main import main
4545
with patch('algocoin.main.TradingEngine'), \
46-
patch('algocoin.main.TestStrategy'):
46+
patch('algocoin.lib.strategies.test_strat.TestStrategy'):
4747
main(['', '--live'])

algocoin/tests/test_utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class Test:
4444

4545
t.c = ['test']
4646
assert t.c == ['test']
47+
print(t.d)
4748
assert t.d == ['']
4849

4950
def test_struct(self):

algocoin/trading.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,25 @@
1313

1414
class TradingEngine(object):
1515
def __init__(self, options: TradingEngineConfig) -> None:
16+
# running live?
1617
self._live = options.type == TradingType.LIVE
18+
# running sandbox?
1719
self._sandbox = options.type == TradingType.SANDBOX
20+
# running backtest?
1821
self._backtest = options.type == TradingType.BACKTEST
22+
23+
# running print callback for debug?
1924
self._print = options.print
2025

21-
self._strats = [] # type: List[TradingStrategy]
26+
# the strategies to run
27+
self._strats = []
2228

2329
self._ex = ex_type_to_ex(options.exchange_options.exchange_type)(options.exchange_options) if self._live or self._sandbox else None
2430
# self._exchanges = [ex_type_to_ex(o.exchange_options.exchange_type)(o.exchange_options) if self._live or self._sandbox else None for o in options]
31+
2532
self._exchanges = []
2633

34+
# if live or sandbox, get account information and balances
2735
if self._live or self._sandbox:
2836
accounts = self._ex.accounts()
2937

@@ -36,8 +44,13 @@ def __init__(self, options: TradingEngineConfig) -> None:
3644
log.info(accounts)
3745
log.info("Running with %.2f USD" % options.risk_options.total_funds)
3846

47+
# instantiate backtest engine
3948
self._bt = Backtest(options.backtest_options) if self._backtest else None
49+
50+
# instantiate riskengine
4051
self._rk = Risk(options.risk_options)
52+
53+
# instantiate execution engine
4154
self._ec = Execution(options.execution_options, self._ex)
4255

4356
# sanity check
@@ -58,8 +71,15 @@ def __init__(self, options: TradingEngineConfig) -> None:
5871
if self._backtest:
5972
self._bt.registerCallback(Print())
6073

61-
self._ticked = [] # type: List
74+
# register strategies from config
75+
for x in options.strategy_options:
76+
log.critical('Registering strategy: %s', str(x.clazz))
77+
self.registerStrategy(x.clazz(*x.args, **x.kwargs))
78+
79+
# actively trading or halted?
6280
self._trading = True # type: bool
81+
82+
# pending orders to process (partial fills)
6383
self._pending = {OrderType.MARKET: [], OrderType.LIMIT: []} # TODO in progress
6484

6585
def exchange(self):

config/sandbox_gemini.cfg

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ exchange=gemini
88

99
[strategy]
1010
strategies =
11-
SMACrossesStrategyWithRegressionFollow, 15, 25
12-
SMACrossesStrategy, 15, 25
11+
algocoin.lib.strategies.test_strat.TestStrategy,
12+
algocoin.lib.strategies.sma_crosses_strategy.SMACrossesStrategy, 15, 25
1313

1414
[risk]
1515
max_drawdown = 100.0

0 commit comments

Comments
 (0)