Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions python/fastquant/data/stocks/stocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,15 @@ def get_stock_data(
if source == "yahoo":
# The query is run on 'yahoo', but if the symbol isn't found, the same query is run on 'phisix'.
df = get_yahoo_data(symbol, start_date, end_date, dividends)
if df is None or symbol == "JFC":
format = "c"
df = get_pse_data(symbol, start_date, end_date, format=format)
if df is None:
raise Exception(f"{symbol} could not be found on Yahoo")

elif source == "phisix":
# The query is run on 'phisix', but if the symbol isn't found, the same query is run on 'yahoo'.
format = "c"
df = get_pse_data(symbol, start_date, end_date, format=format)
if df is None:
df = get_yahoo_data(symbol, start_date, end_date, dividends)
raise Exception(f"{symbol} could not be found on Phisix")

else:
raise Exception("Source must be either 'phisix' or 'yahoo'")
Expand Down
12 changes: 9 additions & 3 deletions python/fastquant/data/stocks/yahoofinance.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,23 @@ def get_yahoo_data(symbol, start_date, end_date, dividends=True):
"High": "high",
"Low": "low",
"Close": "close",
"Adj Close": "adj_close",
"Volume": "volume",
"Dividends": "dividend",
}

# Multi-index dataframes are not supported in fastquant
if "Ticker" in df.columns.names:
df.columns = df.columns.droplevel("Ticker")

# "Adj Close" is missing from the recent versions of YFinance
df = df.drop(['Adj Close'], errors='ignore')

if dividends:
ticker = yf.Ticker(symbol)
div_df = ticker.dividends

if div_df.shape[0] > 0:
df = df.join(div_df, how="left", on="Date")
df = df.join(div_df.tz_localize(None), how="left", on="Date")
else:
df["dividend"] = 0
else:
Expand All @@ -54,7 +61,6 @@ def get_yahoo_data(symbol, start_date, end_date, dividends=True):
"high",
"low",
"close",
"adj_close",
"volume",
"dividend",
]
Expand Down
49 changes: 21 additions & 28 deletions python/fastquant/strategies/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,10 @@ def __init__(self):
self.price_bought = 0

# Initialize stoploss order
self.stoploss_order = None
self.stoploss_orders = []

# Initialize stoploss trail order
self.stoploss_trail_order = None
self.stoploss_trail_orders = []

def buy_signal(self):
return False
Expand Down Expand Up @@ -289,8 +289,8 @@ def start(self):
def next(self):

# add dividend to cash
if self.invest_div and self.datadiv is not None:
self.broker.add_cash(self.datadiv)
if self.invest_div and self.datadiv is not None and self.datadiv.get()[0] != 0:
self.broker.add_cash(self.datadiv.get()[0])

if self.add_cash_amount:
if self.first_timepoint:
Expand Down Expand Up @@ -400,25 +400,21 @@ def next(self):
stop_price = self.data.close[0] * (1.0 - self.stop_loss)
if self.transaction_logging:
self.log("Stop price: {}".format(stop_price))
self.stoploss_order = self.sell(
self.stoploss_orders.append(self.sell(
exectype=bt.Order.Stop,
price=stop_price,
size=final_size,
)
))

if self.stop_trail:
# Create a stoploss trail order if None
if self.stoploss_trail_order is None:
if self.transaction_logging:
self.log("Stop trail: {}".format(self.stop_trail))
self.stoploss_trail_order = self.sell(
exectype=bt.Order.StopTrail,
trailpercent=self.stop_trail,
size=final_size,
)
# Cancel existing stoploss trail order
else:
self.cancel(self.stoploss_trail_order)
if self.transaction_logging:
self.log("Stop trail: {}".format(self.stop_trail))
self.stoploss_trail_orders.append(self.sell(
exectype=bt.Order.StopTrail,
trailpercent=self.stop_trail,
size=final_size,
))

# Buy based on the opening price of the next closing day (only works "open" data exists in the dataset)
else:
Expand All @@ -443,20 +439,20 @@ def next(self):
stop_price = self.data.close[0] * (1.0 - self.stop_loss)
if self.transaction_logging:
self.log("Stop price: {}".format(stop_price))
self.stoploss_order = self.sell(
self.stoploss_orders.append(self.sell(
exectype=bt.Order.Stop,
price=stop_price,
size=final_size,
)
))

if self.stop_trail:
if self.transaction_logging:
self.log("Stop trail: {}".format(self.stop_trail))
self.stoploss_trail_order = self.sell(
self.stoploss_trail_orders.append(self.sell(
exectype=bt.Order.StopTrail,
trailpercent=self.stop_trail,
size=final_size,
)
))

elif self.sell_signal() and (self.strategy_position in [1, -1, None]):
self.strategy_position = 0 if self.strategy_position in [1, -1] else None
Expand Down Expand Up @@ -531,13 +527,10 @@ def next(self):
size=int((self.init_cash / self.dataopen[1]) * self.sell_prop)
)

# Explicitly cancel stoploss order
if self.stoploss_order:
self.cancel(self.stoploss_order)

# Explicitly cancel stoploss trail order
if self.stoploss_trail_order:
self.cancel(self.stoploss_trail_order)
# Explicitly cancel active stop-loss (trail) orders
for order in self.stoploss_orders + self.stoploss_trail_orders:
if order.status not in ["Completed", "Canceled", "Expired", "Rejected"]:
self.cancel(order)

elif self.take_profit_signal():
# Take profit
Expand Down