Skip to content

Commit

Permalink
Upgraded Backtesting to v2 APIs (continued...)
Browse files Browse the repository at this point in the history
  • Loading branch information
guanidene committed Jul 26, 2020
1 parent 0eb79e8 commit 8621da5
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 49 deletions.
50 changes: 37 additions & 13 deletions pyalgotrading/algobulls/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ def __init__(self):
Init method that is used while creating an object of this class
"""
self.headers = None
self.__key_backtesting = None # cstc id
self.__key_papertrading = None # cstc id
self.__key_realtrading = None # cstc id

def set_access_token(self, access_token: str):
"""
Expand Down Expand Up @@ -75,6 +78,30 @@ def _send_request(self, method: str = 'get', endpoint: str = '', base_url: str =
response.raw.decode_content = True
raise AlgoBullsAPIBaseException(f'Unknown non-200 status code --> Method: {method} | URL: {url} | Response: {response_json} | Response code: {response.status_code}')

def __fetch_key(self, strategy_code, trading_type):
# Add strategy to backtesting
endpoint = f'v2/portfolio/strategy'
json_data = {'strategyId': strategy_code, 'tradingType': trading_type.value}
response = self._send_request(method='options', endpoint=endpoint, json_data=json_data)
key = response.get('key')
return key

def __get_key(self, strategy_code, trading_type):
if trading_type is TradingType.BACKTESTING:
if self.__key_backtesting is None:
self.__key_backtesting = self.__fetch_key(strategy_code=strategy_code, trading_type=TradingType.BACKTESTING)
return self.__key_backtesting
elif trading_type is TradingType.PAPERTRADING:
if self.__key_papertrading is None:
self.__key_papertrading = self.__fetch_key(strategy_code=strategy_code, trading_type=TradingType.PAPERTRADING)
return self.__key_papertrading
elif trading_type is TradingType.REALTRADING:
if self.__key_realtrading is None:
self.__key_realtrading = self.__fetch_key(strategy_code=strategy_code, trading_type=TradingType.REALTRADING)
return self.__key_realtrading
else:
raise NotImplementedError

def create_strategy(self, strategy_name: str, strategy_details: str, abc_version: str) -> dict:
"""
Create a new strategy for the user on the AlgoBulls platform.
Expand Down Expand Up @@ -181,20 +208,15 @@ def set_strategy_config(self, strategy_code: str, strategy_config: dict, trading
Info: ENDPOINT
PATCH v2/portfolio/strategy
"""
# Add strategy to backtesting
endpoint = f'v2/portfolio/strategy'
json_data = {'strategyId': strategy_code, 'tradingType': trading_type.value}
response1 = self._send_request(method='options', endpoint=endpoint, json_data=json_data)
key = response1.get('key')
# TODO: Check response to know if request was successful

# Configure the params
json_data = strategy_config
key = self.__get_key(strategy_code=strategy_code, trading_type=trading_type)
endpoint = f'v2/user/strategy/{key}/tweak'
response2 = self._send_request(method='patch', endpoint=endpoint, json_data=json_data)
return key, response2

def start_strategy_algotrading(self, key: str, trading_type: TradingType, broker: str = '') -> dict:
def start_strategy_algotrading(self, strategy_code: str, trading_type: TradingType, broker: str = '') -> dict:
"""
Submit Backtesting / Paper Trading / Real Trading job for strategy with code strategy_code & return the job ID.
Expand All @@ -210,11 +232,12 @@ def start_strategy_algotrading(self, key: str, trading_type: TradingType, broker
else:
raise NotImplementedError

key = self.__get_key(strategy_code=strategy_code, trading_type=trading_type)
json_data = {'method': 'update', 'newVal': 1, 'key': key, 'record': {'status': 0}}
response = self._send_request(method='post', endpoint=endpoint, json_data=json_data)
return response

def stop_strategy_algotrading(self, key: str, trading_type: str, broker: str = '') -> dict:
def stop_strategy_algotrading(self, strategy_code: str, trading_type: str, broker: str = '') -> dict:
"""
Stop Backtesting / Paper Trading / Real Trading job for strategy with code strategy_code & return the job ID.
Expand All @@ -230,11 +253,12 @@ def stop_strategy_algotrading(self, key: str, trading_type: str, broker: str = '
else:
raise NotImplementedError

key = self.__get_key(strategy_code=strategy_code, trading_type=trading_type)
json_data = {'method': 'update', 'newVal': 0, 'key': key, 'record': {'status': 2}}
response = self._send_request(method='post', endpoint=endpoint, json_data=json_data)
return response

def get_job_status(self, strategy_code: str, trading_type: str, broker: str = '') -> dict:
def get_job_status(self, strategy_code: str, trading_type: TradingType) -> dict:
"""
Expand All @@ -243,16 +267,16 @@ def get_job_status(self, strategy_code: str, trading_type: str, broker: str = ''
Args:
strategy_code: Strategy code
trading_type: Trading type
broker: Name of the broker
Returns:
Job status
Info: ENDPOINT
`GET` v1/customer_strategy_algotrading
`GET` v2/user/strategy/status
"""
params = {'strategyCode': strategy_code, 'strategyType': StrategyType.PYTHON.value, 'tradingType': trading_type.value, 'broker': broker}
endpoint = f'v1/customer_strategy_algotrading'
key = self.__get_key(strategy_code=strategy_code, trading_type=trading_type)
params = {'key': key}
endpoint = f'v2/user/strategy/status'
response = self._send_request(endpoint=endpoint, params=params)
return response

Expand Down
66 changes: 30 additions & 36 deletions pyalgotrading/algobulls/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@

from .api import AlgoBullsAPI
from .exceptions import AlgoBullsAPIBadRequest
from ..constants import StrategyMode, TradingType, TradingReportType, AlgoBullsJobStatus, AlgoBullsJobSubmissionResponse, CandleInterval, AlgoBullsSupportedBrokers
from ..constants import StrategyMode, TradingType, TradingReportType, AlgoBullsJobSubmissionResponse, CandleInterval, AlgoBullsSupportedBrokers
from ..strategy.strategy_base import StrategyBase


class AlgoBullsConnection:
"""
Class for Algobulls connection
"""

def __init__(self):
"""
Init method that is used while creating an object of this class
Expand Down Expand Up @@ -122,25 +123,21 @@ def search_instrument(self, instrument):
True or False
"""
assert (isinstance(instrument, str) is True), f'Argument instrument should be a string'

response = self.api.search_instrument(instrument).get('data')
return response

def get_job_status(self, strategy_code, trading_type, broker=None):
def get_job_status(self, strategy_code, trading_type):
"""
Gets job status for given strategy_code and trading_type
"""
assert (isinstance(strategy_code, str) is True), f'Argument strategy_code should be a string'
assert (isinstance(trading_type, TradingType) is True), f'Argument trading_type should be an enum of type {TradingType.__name__}'
assert (broker is None or isinstance(broker, AlgoBullsSupportedBrokers) is True), f'Argument broker should be None or an enum of type {AlgoBullsSupportedBrokers.__name__}'
# assert (broker is None or isinstance(broker, AlgoBullsSupportedBrokers) is True), f'Argument broker should be None or an enum of type {AlgoBullsSupportedBrokers.__name__}'

response = self.api.get_job_status(strategy_code=strategy_code, trading_type=trading_type, broker=broker.value)
if response.get('success') is True:
return AlgoBullsJobStatus(response['data'].upper())
else:
return AlgoBullsJobStatus.JOB_STATUS_UNKNOWN, response
response = self.api.get_job_status(strategy_code=strategy_code, trading_type=trading_type)
return response

def stop_job(self, strategy_code, trading_type, broker=None):
def stop_job(self, strategy_code, trading_type):
"""
Stops a job
Args:
Expand All @@ -153,13 +150,11 @@ def stop_job(self, strategy_code, trading_type, broker=None):
"""
assert (isinstance(strategy_code, str) is True), f'Argument strategy_code should be a string'
assert (isinstance(trading_type, TradingType) is True), f'Argument trading_type should be an enum of type {TradingType.__name__}'
assert (broker is None or isinstance(broker, AlgoBullsSupportedBrokers) is True), f'Argument broker should be None or an enum of type {AlgoBullsSupportedBrokers.__name__}'
# assert (broker is None or isinstance(broker, AlgoBullsSupportedBrokers) is True), f'Argument broker should be None or an enum of type {AlgoBullsSupportedBrokers.__name__}'

response = self.api.stop_strategy_algotrading(strategy_code=strategy_code, trading_type=trading_type, broker=broker.value)
if response.get('success') is True:
return AlgoBullsJobSubmissionResponse(response['data'].upper())
else:
return AlgoBullsJobSubmissionResponse.ERROR, response
print(f'Stopping {trading_type.name} job...', end=' ')
response = self.api.stop_strategy_algotrading(strategy_code=strategy_code, trading_type=trading_type)
print('Success.')

def get_report(self, strategy_code, trading_type, report_type, render_as_dataframe=False, show_all_rows=False, broker=None):
"""
Expand Down Expand Up @@ -194,15 +189,15 @@ def get_report(self, strategy_code, trading_type, report_type, render_as_datafra
else:
return AlgoBullsJobSubmissionResponse.ERROR, response

def backtest(self, strategy_code, start_timestamp, end_timestamp, instrument_id, strategy_parameters, candle_interval, strategy_mode=StrategyMode.INTRADAY):
def backtest(self, strategy_code, start_timestamp, end_timestamp, instrument, strategy_parameters, candle_interval, strategy_mode=StrategyMode.INTRADAY):
"""
Submit a backtesting job for a strategy on the AlgoBulls Platform
Args:
strategy_code: strategy code
start_timestamp: start date/time
end_timestamp: end date/time
instrument_id: instrument key
instrument: instrument key
strategy_parameters: parameters
candle_interval: candle interval
strategy_mode: intraday or delivery
Expand All @@ -214,24 +209,24 @@ def backtest(self, strategy_code, start_timestamp, end_timestamp, instrument_id,
assert (isinstance(strategy_code, str) is True), f'Argument strategy_code should be a string'
assert (isinstance(start_timestamp, dt) is True), f'Argument start_timestamp should be an instance of type datetime.datetime'
assert (isinstance(end_timestamp, dt) is True), f'Argument start_timestamp should be an instance of type datetime.datetime'
# assert (isinstance(instrument_id, int) is True), f'Argument instrument_id should be a integer. You can find the right id using the \'get_instrument()\' method of AlgoBullsConnection class'
assert (isinstance(instrument, str) is True), f'Argument instrument should be a string. You can find the right id using the \'get_instrument()\' method of AlgoBullsConnection class'
assert (isinstance(strategy_parameters, dict) is True), f'Argument strategy_parameters should be a dict'
assert (isinstance(strategy_mode, StrategyMode) is True), f'Argument strategy_mode should be enum of type StrategyMode'
assert (isinstance(candle_interval, CandleInterval)), f'Argument candle_interval should be an enum of type CandleInterval'

# Setup config for Backtesting
strategy_config = {'tradingTime': [start_timestamp.strftime('%d-%m-%Y %H:%M'), end_timestamp.strftime('%d-%m-%Y %H:%M')],
'instruments': [instrument_id],
'instruments': [instrument],
'parameters': strategy_parameters,
'candle': candle_interval.value,
'strategyMode': strategy_mode.value}
print('Setting Strategy Config...', end=' ')
key, _ = self.api.set_strategy_config(strategy_code=strategy_code, strategy_config=strategy_config, trading_type=TradingType.BACKTESTING)
self.api.set_strategy_config(strategy_code=strategy_code, strategy_config=strategy_config, trading_type=TradingType.BACKTESTING)
print('Success.')

# Submit Backtesting job
print('Submitting Backtesting Job...', end=' ')
response = self.api.start_strategy_algotrading(key=key, trading_type=TradingType.BACKTESTING)
print(f'Submitting {TradingType.BACKTESTING.name} Job...', end=' ')
response = self.api.start_strategy_algotrading(strategy_code=strategy_code, trading_type=TradingType.BACKTESTING)
print('Success.')

# print(response)
Expand Down Expand Up @@ -327,7 +322,7 @@ def papertrade(self, strategy_code, start_time, end_time, instrument_id, strateg
assert (isinstance(strategy_code, str) is True), f'Argument strategy_code should be a string'
assert (isinstance(start_time, time) is True), f'Argument start_timestamp should be an instance of type datetime.datetime'
assert (isinstance(end_time, time) is True), f'Argument start_timestamp should be an instance of type datetime.datetime'
assert (isinstance(instrument_id, int) is True), f'Argument instrument_id should be a integer. You can find the right id using the \'get_instrument()\' method of AlgoBullsConnection class'
assert (isinstance(instrument_id, int) is True), f'Argument instrument should be a integer. You can find the right id using the \'get_instrument()\' method of AlgoBullsConnection class'
assert (isinstance(strategy_parameters, dict) is True), f'Argument strategy_parameters should be a dict'
assert (isinstance(strategy_mode, StrategyMode) is True), f'Argument strategy_mode should be enum of type StrategyMode'
assert (isinstance(candle_interval, CandleInterval)), f'Argument candle_interval should be an enum of type CandleInterval'
Expand All @@ -340,12 +335,12 @@ def papertrade(self, strategy_code, start_time, end_time, instrument_id, strateg
'candle_interval': candle_interval.value,
'strategy_mode': strategy_mode.value}
print('Setting Strategy Config...', end=' ')
key, _ = self.api.set_strategy_config(strategy_code=strategy_code, strategy_config=strategy_config, trading_type=TradingType.PAPERTRADING)
self.api.set_strategy_config(strategy_code=strategy_code, strategy_config=strategy_config, trading_type=TradingType.PAPERTRADING)
print('Success.')

# Submit Paper Trading job
print('Submitting Paper Trading Job...', end=' ')
response = self.api.start_strategy_algotrading(key=key, trading_type=TradingType.PAPERTRADING)
print(f'Submitting {TradingType.PAPERTRADING.name} Job...', end=' ')
response = self.api.start_strategy_algotrading(strategy_code=strategy_code, trading_type=TradingType.PAPERTRADING)
print('Success.')

# if response.get('success') is True:
Expand Down Expand Up @@ -447,7 +442,7 @@ def realtrade(self, broker, strategy_code, start_time, end_time, instrument_id,
assert (isinstance(strategy_code, str) is True), f'Argument strategy_code should be a string'
assert (isinstance(start_time, time) is True), f'Argument start_time should be an instance of type datetime.time'
assert (isinstance(end_time, time) is True), f'Argument end_time should be an instance of type datetime.time'
assert (isinstance(instrument_id, int) is True), f'Argument instrument_id should be a integer. You can find the right id using the \'get_instrument()\' method of AlgoBullsConnection class'
assert (isinstance(instrument_id, int) is True), f'Argument instrument should be a integer. You can find the right id using the \'get_instrument()\' method of AlgoBullsConnection class'
assert (isinstance(strategy_parameters, dict) is True), f'Argument strategy_parameters should be a dict'
assert (isinstance(strategy_mode, StrategyMode) is True), f'Argument strategy_mode should be enum of type StrategyMode'
assert (isinstance(candle_interval, CandleInterval)), f'Argument candle_interval should be an enum of type CandleInterval'
Expand All @@ -460,12 +455,12 @@ def realtrade(self, broker, strategy_code, start_time, end_time, instrument_id,
'candle_interval': candle_interval.value,
'strategy_mode': strategy_mode.value}
print('Setting Strategy Config...', end=' ')
key, _ = self.api.set_strategy_config(strategy_code=strategy_code, strategy_config=strategy_config, trading_type=TradingType.REALTRADING)
self.api.set_strategy_config(strategy_code=strategy_code, strategy_config=strategy_config, trading_type=TradingType.REALTRADING)
print('Success.')

# Submit Real Trading job
print('Submitting Real Trading Job...', end=' ')
response = self.api.start_strategy_algotrading(key=key, trading_type=TradingType.REALTRADING, broker=broker.value)
print(f'Submitting {TradingType.REALTRADING.name} Job...', end=' ')
response = self.api.start_strategy_algotrading(strategy_code=strategy_code, trading_type=TradingType.REALTRADING, broker=broker.value)
print('Success.')

# if response.get('success') is True:
Expand All @@ -486,9 +481,9 @@ def get_realtrading_job_status(self, broker, strategy_code):
assert (isinstance(broker, AlgoBullsSupportedBrokers) is True), f'Argument broker should be an enum of type {AlgoBullsSupportedBrokers.__name__}'
assert (isinstance(strategy_code, str) is True), f'Argument strategy_code should be a string'

return self.get_job_status(strategy_code, TradingType.REALTRADING, broker=broker)
return self.get_job_status(strategy_code, TradingType.REALTRADING)

def stop_realtrading_job(self, broker, strategy_code):
def stop_realtrading_job(self, strategy_code):
"""
Stop the realtrading session
Args:
Expand All @@ -498,10 +493,9 @@ def stop_realtrading_job(self, broker, strategy_code):
Returns:
None
"""
assert (isinstance(broker, AlgoBullsSupportedBrokers) is True), f'Argument broker should be an enum of type {AlgoBullsSupportedBrokers.__name__}'
# assert (isinstance(broker, AlgoBullsSupportedBrokers) is True), f'Argument broker should be an enum of type {AlgoBullsSupportedBrokers.__name__}'
assert (isinstance(strategy_code, str) is True), f'Argument strategy_code should be a string'

return self.stop_job(strategy_code=strategy_code, trading_type=TradingType.REALTRADING, broker=broker)
return self.stop_job(strategy_code=strategy_code, trading_type=TradingType.REALTRADING)

def get_realtrading_logs(self, broker, strategy_code):
"""
Expand Down

0 comments on commit 8621da5

Please sign in to comment.