-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' of github.com:tfukaza/harvest
- Loading branch information
Showing
36 changed files
with
578 additions
and
372 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,22 @@ | ||
# Overview | ||
## The Harvest Workflow | ||
|
||
### Harvest architecture | ||
![Harvest architecture](harvest_architecture.png) | ||
Because Harvest is an extensive framework it can be hard to understand how the system works at times. This document server to provide a high-level overview of just how Harvests works after a user starts the trader. | ||
|
||
### Harvest Flow on Trader `start()` | ||
![Harvest start flow](harvest_start_flow.png) | ||
## Fetching Data | ||
|
||
After the user starts the trader Harvest will fetch data from the streamer and update its storage on interval. | ||
|
||
![Fetching Data Workflow](fetch-data.png) | ||
|
||
1. First Harvest will run the streamer on the specified interval. Once the data has been collected, the streamer will call a callback or hook function that will pass operation back to the trader. In this callback function the streamer will return the latest OHLC data for the assets specified by the trader. | ||
2. In this callback, the trader will update the storage with the latest data and the will run each algorithm. | ||
|
||
## Running Algorithms | ||
|
||
After data is fetched, the algorithms are run linearly. | ||
|
||
![Running Algorithm Workflow](run-algo.png) | ||
|
||
1. The algorithm the user created will user functions provided in the `BaseAlgo` class which communicate with the Trader. | ||
2. Typically the user's algorithms will first ask for data on the assets they specified which will be stored in the Storage. | ||
3. After that the user's algoirthms will decided when to buy or sell assets based on the data they got from the Storage. This will leverage the Broker. |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
# HARVEST_SKIP | ||
# Builtin imports | ||
import logging | ||
import datetime as dt | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
# HARVEST_SKIP | ||
# Builtin imports | ||
import logging | ||
import datetime as dt | ||
|
||
# Harvest imports | ||
from harvest.algo import BaseAlgo | ||
from harvest.trader import LiveTrader | ||
from harvest.api.kraken import Kraken | ||
from harvest.storage.csv_storage import CSVStorage | ||
|
||
# Third-party imports | ||
import pandas as pd | ||
import matplotlib.pyplot as plt | ||
import mplfinance as mpf | ||
|
||
|
||
class EMAlgo(BaseAlgo): | ||
def setup(self): | ||
now = dt.datetime.now() | ||
logging.info(f"EMAlgo.setup ran at: {now}") | ||
|
||
def init_ticker(ticker): | ||
fig = mpf.figure() | ||
ax1 = fig.add_subplot(2, 1, 1) | ||
ax2 = fig.add_subplot(3, 1, 3) | ||
|
||
return { | ||
ticker: { | ||
"initial_price": None, "ohlc": pd.DataFrame(), | ||
"fig": fig, | ||
"ax1": ax1, | ||
"ax2": ax2 | ||
} | ||
} | ||
|
||
self.tickers = {} | ||
# self.tickers.update(init_ticker("@BTC")) | ||
self.tickers.update(init_ticker("@DOGE")) | ||
|
||
def main(self): | ||
now = dt.datetime.now() | ||
logging.info(f"EMAlgo.main ran at: {now}") | ||
|
||
if now - now.replace(hour=0, minute=0, second=0, microsecond=0) <= dt.timedelta( | ||
seconds=60 | ||
): | ||
logger.info(f"It's a new day! Clearning OHLC caches!") | ||
for ticker_value in self.tickers.values(): | ||
ticker_value["ohlc"] = pd.DataFrame() | ||
|
||
for ticker, ticker_value in self.tickers.items(): | ||
current_price = self.get_asset_price(ticker) | ||
current_ohlc = self.get_asset_candle(ticker) | ||
if ticker_value["initial_price"] is None: | ||
ticker_value["initial_price"] = current_price | ||
|
||
if current_ohlc.empty: | ||
logging.warn(f"{ticker}'s get_asset_candle_list returned an empty list.") | ||
return | ||
|
||
ticker_value["ohlc"] = ticker_value["ohlc"].append(current_ohlc) | ||
|
||
self.process_ticker(ticker, ticker_value, current_price) | ||
|
||
def process_ticker(self, ticker, ticker_data, current_price): | ||
initial_price = ticker_data["initial_price"] | ||
ohlc = ticker_data["ohlc"] | ||
|
||
# Calculate the price change | ||
delta_price = current_price - initial_price | ||
|
||
# Print stock info | ||
logging.info(f"{ticker} current price: ${current_price}") | ||
logging.info(f"{ticker} price change: ${delta_price}") | ||
|
||
# Update the OHLC graph | ||
ticker_data['ax1'].clear() | ||
ticker_data['ax2'].clear() | ||
mpf.plot(ohlc, ax=ticker_data['ax1'], volume=ticker_data['ax2'], type="candle") | ||
plt.pause(3) | ||
|
||
|
||
if __name__ == "__main__": | ||
# Store the OHLC data in a folder called `em_storage` with each file stored as a csv document | ||
csv_storage = CSVStorage(save_dir="em_storage") | ||
# Our streamer and broker will be Alpaca. My secret keys are stored in `alpaca_secret.yaml` | ||
kraken = Kraken( | ||
path="accounts/kraken-secret.yaml" | ||
) | ||
em_algo = EMAlgo() | ||
trader = LiveTrader(streamer=kraken, broker=kraken, storage=csv_storage, debug=True) | ||
|
||
# trader.set_symbol("@BTC") | ||
trader.set_symbol("@DOGE") | ||
trader.set_algo(em_algo) | ||
mpf.show() | ||
|
||
# Update every minute | ||
trader.start("1MIN", all_history=False) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
# HARVEST_SKIP | ||
# Builtin imports | ||
import logging | ||
import datetime as dt | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.