Skip to content

Commit

Permalink
doc fixed and speed optimizations
Browse files Browse the repository at this point in the history
  • Loading branch information
grzesir committed Dec 15, 2023
1 parent 9d87345 commit a3323bc
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 69 deletions.
4 changes: 2 additions & 2 deletions docsrc/getting_started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Here are the steps to get started using the Alpaca broker, if you want to use a
# Put your own Alpaca secret here:
"API_SECRET": "YOUR_ALPACA_SECRET",
# If you want to go live, you must change this. It is currently set for paper trading
"ENDPOINT": "https://paper-api.alpaca.markets"
"PAPER": True,
}
5. Create a strategy class (See strategy section) e.g. class MyStrategy(Strategy) or import an example from our libraries, like this:
Expand Down Expand Up @@ -117,7 +117,7 @@ Here it is all together:
# Put your own Alpaca secret here:
"API_SECRET": "YOUR_ALPACA_SECRET",
# If you want to go live, you must change this. It is currently set for paper trading
"ENDPOINT": "https://paper-api.alpaca.markets"
"PAPER": True,
}
Expand Down
2 changes: 1 addition & 1 deletion docsrc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Once you have backtested your strategy and found it to be profitable on historic
# Put your own Alpaca secret here:
"API_SECRET": "YOUR_ALPACA_SECRET",
# If you want to go live, you must change this. It is currently set for paper trading
"ENDPOINT": "https://paper-api.alpaca.markets"
"PAPER": True,
}
Expand Down
33 changes: 25 additions & 8 deletions lumibot/backtesting/polygon_backtesting.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,15 +138,32 @@ def update_pandas_data(self, asset, quote, length, timestep, start_dt=None):

def _pull_source_symbol_bars(
self,
asset,
length,
timestep=None,
timeshift=None,
quote=None,
exchange=None,
include_after_hours=True,
asset: Asset,
length: int,
timestep: str = "day",
timeshift: int = None,
quote: Asset = None,
exchange: str = None,
include_after_hours: bool = True,
):
pandas_data_update = self.update_pandas_data(asset, quote, length, timestep)
# Get the current datetime
dt = self.get_datetime()

# Calculate the start datetime
if timestep == "minute":
start_dt = dt - timedelta(minutes=length)
start_dt = (start_dt - timedelta(minutes=timeshift)) if timeshift is not None else start_dt
elif timestep == "hour":
start_dt = dt - timedelta(hours=length)
start_dt = (start_dt - timedelta(hours=timeshift)) if timeshift is not None else start_dt
elif timestep == "day":
start_dt = dt - timedelta(days=length)
start_dt = (start_dt - timedelta(days=timeshift)) if timeshift is not None else start_dt
else:
raise Exception(f"Invalid timestep: {timestep}")

# Get data from Polygon
pandas_data_update = self.update_pandas_data(asset, quote, length, timestep, start_dt)

if pandas_data_update is not None:
# Add the keys to the self.pandas_data dictionary
Expand Down
7 changes: 4 additions & 3 deletions lumibot/brokers/alpaca.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
from alpaca.trading.requests import GetOrdersRequest
from alpaca.trading.stream import TradingStream
from dateutil import tz
from termcolor import colored

from lumibot.data_sources import AlpacaData
from lumibot.entities import Asset, Order, Position
from termcolor import colored

from .broker import Broker

Expand Down Expand Up @@ -61,7 +62,7 @@ class Alpaca(Broker):
... # Put your own Alpaca secret here:
... "API_SECRET": "YOUR_API_SECRET",
... # If you want to go live, you must change this
... "ENDPOINT": "https://paper-api.alpaca.markets",
... "PAPER": True,
... }
>>> alpaca = Alpaca(ALPACA_CONFIG)
>>> print(alpaca.get_time_to_open())
Expand All @@ -79,7 +80,7 @@ class Alpaca(Broker):
... # Put your own Alpaca secret here:
... "API_SECRET": "YOUR_API_SECRET",
... # If you want to go live, you must change this
... "ENDPOINT": "https://paper-api.alpaca.markets",
... "PAPER": True,
... }
>>>
>>> class AlpacaStrategy(Strategy):
Expand Down
75 changes: 20 additions & 55 deletions lumibot/brokers/ccxt.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@
import logging
from decimal import ROUND_DOWN, Decimal

from termcolor import colored

from lumibot.data_sources import CcxtData
from lumibot.entities import Asset, Order, Position
from termcolor import colored

from .broker import Broker

Expand All @@ -14,6 +13,7 @@ class Ccxt(Broker):
"""
Crypto broker using CCXT.
"""

def __init__(self, config, data_source: CcxtData = None, max_workers=20, chunk_size=100, **kwargs):
if data_source is None:
data_source = CcxtData(config, max_workers=max_workers, chunk_size=chunk_size)
Expand All @@ -30,9 +30,7 @@ def __init__(self, config, data_source: CcxtData = None, max_workers=20, chunk_s

def get_timestamp(self):
"""Returns the current UNIX timestamp representation from CCXT"""
logging.warning(
"The method 'get_time_to_close' is not applicable with Crypto 24/7 markets."
)
logging.warning("The method 'get_time_to_close' is not applicable with Crypto 24/7 markets.")
return self.api.microseconds() / 1000000

def is_market_open(self):
Expand All @@ -51,9 +49,7 @@ def get_time_to_open(self):
-------
None
"""
logging.warning(
"The method 'get_time_to_open' is not applicable with Crypto 24/7 markets."
)
logging.warning("The method 'get_time_to_open' is not applicable with Crypto 24/7 markets.")
return None

def get_time_to_close(self):
Expand All @@ -63,9 +59,7 @@ def get_time_to_close(self):
-------
None
"""
logging.debug(
"The method 'get_time_to_close' is not applicable with Crypto 24/7 markets."
)
logging.debug("The method 'get_time_to_close' is not applicable with Crypto 24/7 markets.")
return None

def is_margin_enabled(self):
Expand Down Expand Up @@ -143,14 +137,12 @@ def _get_balances_at_broker(self, quote_asset):
precision_price = self.api.markets[market]["precision"]["price"]

if self.api.exchangeId == "binance":
precision_amount = 10 ** -precision_amount
precision_price = 10 ** -precision_price
precision_amount = 10**-precision_amount
precision_price = 10**-precision_price

# Binance only has `free` and `locked`.
if self.api.exchangeId == "binance":
total_balance = Decimal(currency_info["free"]) + Decimal(
currency_info["locked"]
)
total_balance = Decimal(currency_info["free"]) + Decimal(currency_info["locked"])
else:
total_balance = currency_info["balance"]

Expand Down Expand Up @@ -208,9 +200,7 @@ def _parse_broker_position(self, position, strategy, orders=None):
precision=precision,
)

position_return = Position(
strategy, asset, quantity, hold=hold, available=available, orders=orders
)
position_return = Position(strategy, asset, quantity, hold=hold, available=available, orders=orders)
return position_return

def _pull_broker_position(self, asset):
Expand Down Expand Up @@ -306,10 +296,7 @@ def _pull_broker_open_orders(self):
if self.fetch_open_orders_last_request_time is not None:
net_rate_limit = (
self.binance_all_orders_rate_limit
- (
datetime.datetime.now()
- self.fetch_open_orders_last_request_time
).seconds
- (datetime.datetime.now() - self.fetch_open_orders_last_request_time).seconds
)
if net_rate_limit > 0:
logging.info(
Expand Down Expand Up @@ -346,31 +333,20 @@ def _submit_order(self, order):
order_class = ""
order_types = ["market", "limit", "stop_limit"]
# TODO: Is this actually true?? Try testing this with a bunch of different exchanges.
markets_error_message = (
f"Only `market`, `limit`, or `stop_limit` orders work "
f"with crypto currency markets."
)
markets_error_message = f"Only `market`, `limit`, or `stop_limit` orders work " f"with crypto currency markets."

if order.order_class != order_class:
logging.error(
f"A compound order of {order.order_class} was entered. "
f"{markets_error_message}"
)
logging.error(f"A compound order of {order.order_class} was entered. " f"{markets_error_message}")
return

if order.type not in order_types:
logging.error(
f"An order type of {order.type} was entered which is not "
f"valid. {markets_error_message}"
)
logging.error(f"An order type of {order.type} was entered which is not " f"valid. {markets_error_message}")
return

# Check order within limits.
market = self.api.markets.get(order.pair, None)
if market is None:
logging.error(
f"An order for {order.pair} was submitted. The market for that pair does not exist"
)
logging.error(f"An order for {order.pair} was submitted. The market for that pair does not exist")
order.set_error("No market for pair.")
return order

Expand All @@ -385,7 +361,7 @@ def _submit_order(self, order):
precision_exp_modifier = 2
initial_precision_exp = abs(initial_precision_amount.as_tuple().exponent)
new_precision_exp = initial_precision_exp - precision_exp_modifier
factor = 10 ** new_precision_exp
factor = 10**new_precision_exp
precision_amount = Decimal(1) / Decimal(factor)
else:
precision_amount = str(precision["amount"])
Expand Down Expand Up @@ -440,9 +416,7 @@ def _submit_order(self, order):
setattr(
order,
price_type,
Decimal(getattr(order, price_type)).quantize(
Decimal(str(precision["price"]))
),
Decimal(getattr(order, price_type)).quantize(Decimal(str(precision["price"]))),
)
else:
continue
Expand Down Expand Up @@ -488,9 +462,7 @@ def _submit_order(self, order):

try:
if limits["cost"]["max"] is not None:
assert getattr(order, price_type) * order.quantity <= Decimal(
limits["cost"]["max"]
)
assert getattr(order, price_type) * order.quantity <= Decimal(limits["cost"]["max"])
except AssertionError:
logging.warning(
f"\nThe order {order} was rejected as the order total cost \n"
Expand Down Expand Up @@ -521,9 +493,7 @@ def _submit_order(self, order):
except Exception as e:
order.set_error(e)
message = str(e)
full_message = (
f"{order} did not go through. The following error occurred: {message}"
)
full_message = f"{order} did not go through. The following error occurred: {message}"
logging.info(colored(full_message, "red"))

return order
Expand Down Expand Up @@ -647,9 +617,7 @@ def create_order_args(self, order):
# Remove items with None values
params = {k: v for k, v in params.items() if v}

order_type_map = dict(
market="MARKET", limit="LIMIT", stop_limit="STOP_LOSS_LIMIT"
)
order_type_map = dict(market="MARKET", limit="LIMIT", stop_limit="STOP_LOSS_LIMIT")

args = [
order.pair,
Expand Down Expand Up @@ -716,10 +684,7 @@ def cancel_open_orders(self, strategy):
self.api.cancel_order(order["id"], symbol=order["symbol"])

def get_historical_account_value(self):
logging.error(
"The function get_historical_account_value is not "
"implemented yet for CCXT."
)
logging.error("The function get_historical_account_value is not " "implemented yet for CCXT.")
return {"hourly": None, "daily": None}

def wait_for_order_registration(self, order):
Expand Down

0 comments on commit a3323bc

Please sign in to comment.