olympustrader


Nameolympustrader JSON
Version 0.3.5.1 PyPI version JSON
download
home_pagehttps://olympustrader.readthedocs.io/en/latest/
Summarytrading bot framework
upload_time2024-12-16 04:05:48
maintainerNone
docs_urlNone
authorMoustapha Diaby
requires_python<4.0,>=3.12
licenseApache 2.0
keywords trading bot framework backtesting trading hft
VCS
bugtrack_url
requirements olympustrader
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Welcome to OlympusTrader

  [![Documentation Status](https://readthedocs.org/projects/olympustrader/badge/?version=latest)](https://olympustrader.readthedocs.io/en/latest/?badge=latest)
  [![PyPI version](https://badge.fury.io/py/olympustrader.svg)](https://badge.fury.io/py/olympustrader)
  [![Downloads](https://pepy.tech/badge/olympustrader)](https://pepy.tech/project/olympustrader)

This is my implementation of a quant trading framework inspired by frameworks such as QuantConnect and Blankly. The main idea is to create a workflow that allows users to create their trading strategy and execute complex trades with dependencies and a Risk-first approach. The core components are Insights(potential trade idea) which can have dynamic components such as:

- How to enter
- What price to enter
- Quantity
- How long should it be kept alive (expires) filled or unfilled
- Its confidence score
- Multiple take profit level (with adjustable risk)
- and much more

The base strategy class manages everything else:

- Getting the latest bar price for your timeframe and featured timeframes (multi-timeframe support)
- Manages and tracking open and closed positions
- Execution of Alpha models, Executors, your Strategy core functions such as start(), init(), generateInsight(),

Feel free to fork the project if you want to add a new broker or anything you want and submit a PR - right now I just made it to use alpaca-py.

## In Progress

- [ ] Add more brokers support
  - [ ] Binance
  - [ ] Trade Locker
  - [ ] Interactive Brokers
  - [ ] Hyper Liquid
  - [x] MT5 - MetaTrader5

- [x] Dashboard
- [x] Multiple timeframe support
- [x] Backtesting

## OlympusTrader

To get started you can look at the main.py file for my strategy for the general workflow.

**Requirement** are: `pip install olympustrader`. Or run `docker compose up` to get both the UI and main.py script to run in a container (for deploy).

Create a .env file for your environment variables as of now the only available broker is alpaca so you will need to set ALPACA_API_KEY and ALPACA_SECRET_KEY in the .env file.

```py

from OlympusTrader.utils.insight import Insight, StrategyTypes, InsightState
from OlympusTrader.utils.timeframe import TimeFrame, TimeFrameUnit
from OlympusTrader import AlpacaBroker, Strategy

class  QbitTB(Strategy):

 def  start(self):
  # Your strategy starting point is the first thing that runs and only runs once so you can use it to load models or set base variables
  # Executos, Alphas, and other models should be added here
  pass

 def  init(self,  asset):
  # Your strategy init point for each asset in the universe
  state = self.state
  pass



 def  universe(self):
  # The universe of assets that you want to trade
  universe =  {'BTC/USD',  'ETH/USD',  'TSLA'}
  return universe



 def  on_bar(self,  symbol,  bar):
  # Your strategy, TA's and other functions should be called here
  pass



 def  generateInsights(self,  symbol:  str):
  # Generate insights for your strategy. This is run after the on_bar function
  #  self.add_insight(...
  pass



 def  executeInsight(self,  insight: Insight):
  
  match insight.state:
    case InsightState.NEW:
    # How to execution new insights that are generated
      pass

    case InsightState.FILLED:
    # How to manage open insights
      pass

    # ... other cases
    case _:
      pass



 def  teardown(self):
  pass

# Close all open positions and pending orders om tear down

self.BROKER.close_all_positions()



if __name__ ==  "__main__":
 broker = AlpacaBroker(paper=True)  # Set your broker and paper to True for a demo account
 strategy = QbitTB(broker,  {},  resolution=TimeFrame(1, TimeFrameUnit.Minute),  ui=True)  # Set your strategy resolution and ui to True to use the Streamlit dashboard
 strategy.add_events('bar')  # Add events to your strategy
 strategy.run()  # To run your live/ demo account trade

```

## That easy to get started

The `strategy.add_events('bar')` function is used to add events to your strategy. We have plans for more events such as quotes, multi-timeframe and news evntsin the future but for now, we only have bar events for your current strategy resolution.

If you want to get started with backtesting your strategy you can use the backtest flag and the PaperBroker - with the same code!

```py

broker = PaperBroker(cash=1_000_000,  start_date=datetime(2024,  5,  27),  end_date=datetime(2024,  5,  31))
strategy = QbitTB(broker,  variables={},  resolution=TimeFrame(1, TimeFrameUnit.Minute),  verbose=0,  ui=True,  mode=IStrategyMode.BACKTEST)
strategy.add_events('bar',  stored=True,  stored_path='data', start=broker.START_DATE,  end=broker.END_DATE)

```

## Alpha Models

The framework is designed to be flexible and allow you to build simple to complex strategies with dependencies and a risk-first approach. You could use the generateInsights(self, symbol: str) function to generate your models or use our community models that we will be adding over time to the framework. Feel free to make a PR if you have a model you want to add just make sure that it inherits the BaseAlpha in `OlympusTrader.alpha.base_alpha.py`.

Another added benefit of using Alpha models is that it each alpha model should include the right indicators that it requirers via Pandas_Ta in order to be able to function correctly. This would also include the default params of the indicators and set the `strategy.warm_up(int)` value accordingly. Each indicator that a Alpha model uses should be customisable and its part of the community guidelines for it to do so.

### Example of using an Alpha Model

```py

from OlympusTrader.alpha.test_entry import TestEntryAlpha
from OlympusTrader.alpha.ema_price_crossover import EMAPriceCrossoverAlpha


strategy.add_alphas([
 TestEntryAlpha(strategy),
 EMAPriceCrossoverAlpha(strategy,  atrPeriod=14,  emaPeriod=9,  baseConfidenceModifierField='market_state'),
])
```

Using the add_alphas function, you can add multiple alpha models from the community (or your own) to your strategy and they will be run before your own generateInsights function. You can read about what each alpha does in the `OlympusTrader.alpha` folder.

**Recommended**: You should add the alpha models in the `start()` function.

#### Alpha Results

Each alpha returns an AlphaResults object that tells you if the alpha was successful or not and the reason why it failed. You can use this to log the results of the alpha and make decisions based on the results. But by default, the framework will log the results of failed alphas for you and will always run your generateInsights function after the alphas are run.

## Executors Models

You can manually manage and execute your insights with the `executeInsight(self, insight: Insight)` function or use a set of executor models that the community add over time to the framework. Feel free to make a PR if you have a model you want to add just make sure that it inherits the BaseExecutor in `OlympusTrader.insight.executors.base_executor.py`.

### Example of using an Executor Model

```py

from OlympusTrader.insight.executors.new.cancelAllOppositeSide import CancelAllOppositeSidetExecutor
from OlympusTrader.insight.executors.new.dynamicQuantityToRisk import DynamicQuantityToRiskExecutor
from OlympusTrader.insight.executors.new.marketOrderEntryPrice import MarketOrderEntryPriceExecutor
from OlympusTrader.insight.executors.new.minimumRiskToReward import MinimumRiskToRewardExecutor
from OlympusTrader.insight.executors.new.rejectExpiredInsight import RejectExpiredInsightExecutor
from OlympusTrader.insight.executors.filled.basicStopLoss import BasicStopLossExecutor
from OlympusTrader.insight.executors.filled.basicTakeProfit import BasicTakeProfitExecutor
from OlympusTrader.insight.executors.filled.closeExhaustedInsight import CloseExhaustedInsightExecutor
from OlympusTrader.insight.executors.filled.closeMarketChanged import CloseMarketChangedExecutor
from OlympusTrader.insight.executors.canceled.defaultOnCancelled import DefaultOnCancelledExecutor
from OlympusTrader.insight.executors.rejected.defaultOnReject import DefaultOnRejectExecutor
from OlympusTrader.insight.executors.closed.defaultOnClosed import DefaultOnClosedExecutor

# New Executors

strategy.add_executors([
RejectExpiredInsightExecutor(strategy),
MarketOrderEntryPriceExecutor(strategy),
MinimumRiskToRewardExecutor(strategy),
DynamicQuantityToRiskExecutor(strategy),
CancelAllOppositeSidetExecutor(strategy)
])

# Executed Executors
RejectExpiredExecutedExecutor = RejectExpiredInsightExecutor(strategy)
RejectExpiredExecutedExecutor._override_state(InsightState.EXECUTED)
strategy.add_executors([
RejectExpiredExecutedExecutor,
])

# Cancelled Executors
strategy.add_executors([
DefaultOnCancelledExecutor(strategy),
])

# Filled Executors
strategy.add_executors([
CloseExhaustedInsightExecutor(strategy),
CloseMarketChangedExecutor(strategy),
BasicStopLossExecutor(strategy),
BasicTakeProfitExecutor(strategy)
])

# Closed Executors
strategy.add_executors([
DefaultOnClosedExecutor(strategy),
])

# Rejected Executors
strategy.add_executors([
DefaultOnRejectExecutor(strategy)
])

```

**Recommended**: You should add the executors models in the `start()` function.

You can add multiple executors to your strategy and they will be run after your executeInsight function. We do not execute or submit any insights as that should be done by the users for insights in the NEW state. As you can see you can add executors for different states of the insight such as filled, rejected, cancelled, executed and closed. This can all be done with the same add_executors function but we just added the different states for clarity. In some cases, you may want to override the state of the default state that the executor runs on - you can do this with the `_override_state` function. 

You can read about what each executor does in the `OlympusTrader.insight.executors` folder.

#### Executor Results

Each executor returns an ExecutorResults object that tells you if the executor was successful or not and the reason why it failed. You can use this to log the results of the executor and make decisions based on the results. But by default the framework will log the results of failed executors for you and skip the insight if it fails (eg if the insight is in the NEW state - based on the executor it may reject the insight).

## Documentation

The documentation is still in progress but you can find the documentation for the framework at [OlympusTrader](https://olympustrader.readthedocs.io/en/latest/)


## Collaborations

If you're interested in helping out just make a PR and ill happy to merge! I just to build a framework that is easy enough for people to build a live trading bot but flexible for users to to build complex strategies. Thanks

## Star History

[![Star History Chart](https://api.star-history.com/svg?repos=isaac-diaby/OlympusTrader&type=Timeline)](https://star-history.com/#isaac-diaby/OlympusTrader&Timeline)

            

Raw data

            {
    "_id": null,
    "home_page": "https://olympustrader.readthedocs.io/en/latest/",
    "name": "olympustrader",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4.0,>=3.12",
    "maintainer_email": null,
    "keywords": "Trading bot, Framework, backtesting, trading, HFT",
    "author": "Moustapha Diaby",
    "author_email": "me@isaacdiaby.tech",
    "download_url": "https://files.pythonhosted.org/packages/f7/b7/b3883700fe47a89253baa166ec13f8a9a12d3bcf5c948a20b712454a1b5c/olympustrader-0.3.5.1.tar.gz",
    "platform": null,
    "description": "# Welcome to OlympusTrader\n\n  [![Documentation Status](https://readthedocs.org/projects/olympustrader/badge/?version=latest)](https://olympustrader.readthedocs.io/en/latest/?badge=latest)\n  [![PyPI version](https://badge.fury.io/py/olympustrader.svg)](https://badge.fury.io/py/olympustrader)\n  [![Downloads](https://pepy.tech/badge/olympustrader)](https://pepy.tech/project/olympustrader)\n\nThis is my implementation of a quant trading framework inspired by frameworks such as QuantConnect and Blankly. The main idea is to create a workflow that allows users to create their trading strategy and execute complex trades with dependencies and a Risk-first approach. The core components are Insights(potential trade idea) which can have dynamic components such as:\n\n- How to enter\n- What price to enter\n- Quantity\n- How long should it be kept alive (expires) filled or unfilled\n- Its confidence score\n- Multiple take profit level (with adjustable risk)\n- and much more\n\nThe base strategy class manages everything else:\n\n- Getting the latest bar price for your timeframe and featured timeframes (multi-timeframe support)\n- Manages and tracking open and closed positions\n- Execution of Alpha models, Executors, your Strategy core functions such as start(), init(), generateInsight(),\n\nFeel free to fork the project if you want to add a new broker or anything you want and submit a PR - right now I just made it to use alpaca-py.\n\n## In Progress\n\n- [ ] Add more brokers support\n  - [ ] Binance\n  - [ ] Trade Locker\n  - [ ] Interactive Brokers\n  - [ ] Hyper Liquid\n  - [x] MT5 - MetaTrader5\n\n- [x] Dashboard\n- [x] Multiple timeframe support\n- [x] Backtesting\n\n## OlympusTrader\n\nTo get started you can look at the main.py file for my strategy for the general workflow.\n\n**Requirement** are: `pip install olympustrader`. Or run `docker compose up` to get both the UI and main.py script to run in a container (for deploy).\n\nCreate a .env file for your environment variables as of now the only available broker is alpaca so you will need to set ALPACA_API_KEY and ALPACA_SECRET_KEY in the .env file.\n\n```py\n\nfrom OlympusTrader.utils.insight import Insight, StrategyTypes, InsightState\nfrom OlympusTrader.utils.timeframe import TimeFrame, TimeFrameUnit\nfrom OlympusTrader import AlpacaBroker, Strategy\n\nclass  QbitTB(Strategy):\n\n def  start(self):\n  # Your strategy starting point is the first thing that runs and only runs once so you can use it to load models or set base variables\n  # Executos, Alphas, and other models should be added here\n  pass\n\n def  init(self,  asset):\n  # Your strategy init point for each asset in the universe\n  state = self.state\n  pass\n\n\n\n def  universe(self):\n  # The universe of assets that you want to trade\n  universe =  {'BTC/USD',  'ETH/USD',  'TSLA'}\n  return universe\n\n\n\n def  on_bar(self,  symbol,  bar):\n  # Your strategy, TA's and other functions should be called here\n  pass\n\n\n\n def  generateInsights(self,  symbol:  str):\n  # Generate insights for your strategy. This is run after the on_bar function\n  #  self.add_insight(...\n  pass\n\n\n\n def  executeInsight(self,  insight: Insight):\n  \n  match insight.state:\n    case InsightState.NEW:\n    # How to execution new insights that are generated\n      pass\n\n    case InsightState.FILLED:\n    # How to manage open insights\n      pass\n\n    # ... other cases\n    case _:\n      pass\n\n\n\n def  teardown(self):\n  pass\n\n# Close all open positions and pending orders om tear down\n\nself.BROKER.close_all_positions()\n\n\n\nif __name__ ==  \"__main__\":\n broker = AlpacaBroker(paper=True)  # Set your broker and paper to True for a demo account\n strategy = QbitTB(broker,  {},  resolution=TimeFrame(1, TimeFrameUnit.Minute),  ui=True)  # Set your strategy resolution and ui to True to use the Streamlit dashboard\n strategy.add_events('bar')  # Add events to your strategy\n strategy.run()  # To run your live/ demo account trade\n\n```\n\n## That easy to get started\n\nThe `strategy.add_events('bar')` function is used to add events to your strategy. We have plans for more events such as quotes, multi-timeframe and news evntsin the future but for now, we only have bar events for your current strategy resolution.\n\nIf you want to get started with backtesting your strategy you can use the backtest flag and the PaperBroker - with the same code!\n\n```py\n\nbroker = PaperBroker(cash=1_000_000,  start_date=datetime(2024,  5,  27),  end_date=datetime(2024,  5,  31))\nstrategy = QbitTB(broker,  variables={},  resolution=TimeFrame(1, TimeFrameUnit.Minute),  verbose=0,  ui=True,  mode=IStrategyMode.BACKTEST)\nstrategy.add_events('bar',  stored=True,  stored_path='data', start=broker.START_DATE,  end=broker.END_DATE)\n\n```\n\n## Alpha Models\n\nThe framework is designed to be flexible and allow you to build simple to complex strategies with dependencies and a risk-first approach. You could use the generateInsights(self, symbol: str) function to generate your models or use our community models that we will be adding over time to the framework. Feel free to make a PR if you have a model you want to add just make sure that it inherits the BaseAlpha in `OlympusTrader.alpha.base_alpha.py`.\n\nAnother added benefit of using Alpha models is that it each alpha model should include the right indicators that it requirers via Pandas_Ta in order to be able to function correctly. This would also include the default params of the indicators and set the `strategy.warm_up(int)` value accordingly. Each indicator that a Alpha model uses should be customisable and its part of the community guidelines for it to do so.\n\n### Example of using an Alpha Model\n\n```py\n\nfrom OlympusTrader.alpha.test_entry import TestEntryAlpha\nfrom OlympusTrader.alpha.ema_price_crossover import EMAPriceCrossoverAlpha\n\n\nstrategy.add_alphas([\n TestEntryAlpha(strategy),\n EMAPriceCrossoverAlpha(strategy,  atrPeriod=14,  emaPeriod=9,  baseConfidenceModifierField='market_state'),\n])\n```\n\nUsing the add_alphas function, you can add multiple alpha models from the community (or your own) to your strategy and they will be run before your own generateInsights function. You can read about what each alpha does in the `OlympusTrader.alpha` folder.\n\n**Recommended**: You should add the alpha models in the `start()` function.\n\n#### Alpha Results\n\nEach alpha returns an AlphaResults object that tells you if the alpha was successful or not and the reason why it failed. You can use this to log the results of the alpha and make decisions based on the results. But by default, the framework will log the results of failed alphas for you and will always run your generateInsights function after the alphas are run.\n\n## Executors Models\n\nYou can manually manage and execute your insights with the `executeInsight(self, insight: Insight)` function or use a set of executor models that the community add over time to the framework. Feel free to make a PR if you have a model you want to add just make sure that it inherits the BaseExecutor in `OlympusTrader.insight.executors.base_executor.py`.\n\n### Example of using an Executor Model\n\n```py\n\nfrom OlympusTrader.insight.executors.new.cancelAllOppositeSide import CancelAllOppositeSidetExecutor\nfrom OlympusTrader.insight.executors.new.dynamicQuantityToRisk import DynamicQuantityToRiskExecutor\nfrom OlympusTrader.insight.executors.new.marketOrderEntryPrice import MarketOrderEntryPriceExecutor\nfrom OlympusTrader.insight.executors.new.minimumRiskToReward import MinimumRiskToRewardExecutor\nfrom OlympusTrader.insight.executors.new.rejectExpiredInsight import RejectExpiredInsightExecutor\nfrom OlympusTrader.insight.executors.filled.basicStopLoss import BasicStopLossExecutor\nfrom OlympusTrader.insight.executors.filled.basicTakeProfit import BasicTakeProfitExecutor\nfrom OlympusTrader.insight.executors.filled.closeExhaustedInsight import CloseExhaustedInsightExecutor\nfrom OlympusTrader.insight.executors.filled.closeMarketChanged import CloseMarketChangedExecutor\nfrom OlympusTrader.insight.executors.canceled.defaultOnCancelled import DefaultOnCancelledExecutor\nfrom OlympusTrader.insight.executors.rejected.defaultOnReject import DefaultOnRejectExecutor\nfrom OlympusTrader.insight.executors.closed.defaultOnClosed import DefaultOnClosedExecutor\n\n# New Executors\n\nstrategy.add_executors([\nRejectExpiredInsightExecutor(strategy),\nMarketOrderEntryPriceExecutor(strategy),\nMinimumRiskToRewardExecutor(strategy),\nDynamicQuantityToRiskExecutor(strategy),\nCancelAllOppositeSidetExecutor(strategy)\n])\n\n# Executed Executors\nRejectExpiredExecutedExecutor = RejectExpiredInsightExecutor(strategy)\nRejectExpiredExecutedExecutor._override_state(InsightState.EXECUTED)\nstrategy.add_executors([\nRejectExpiredExecutedExecutor,\n])\n\n# Cancelled Executors\nstrategy.add_executors([\nDefaultOnCancelledExecutor(strategy),\n])\n\n# Filled Executors\nstrategy.add_executors([\nCloseExhaustedInsightExecutor(strategy),\nCloseMarketChangedExecutor(strategy),\nBasicStopLossExecutor(strategy),\nBasicTakeProfitExecutor(strategy)\n])\n\n# Closed Executors\nstrategy.add_executors([\nDefaultOnClosedExecutor(strategy),\n])\n\n# Rejected Executors\nstrategy.add_executors([\nDefaultOnRejectExecutor(strategy)\n])\n\n```\n\n**Recommended**: You should add the executors models in the `start()` function.\n\nYou can add multiple executors to your strategy and they will be run after your executeInsight function. We do not execute or submit any insights as that should be done by the users for insights in the NEW state. As you can see you can add executors for different states of the insight such as filled, rejected, cancelled, executed and closed. This can all be done with the same add_executors function but we just added the different states for clarity. In some cases, you may want to override the state of the default state that the executor runs on - you can do this with the `_override_state` function. \n\nYou can read about what each executor does in the `OlympusTrader.insight.executors` folder.\n\n#### Executor Results\n\nEach executor returns an ExecutorResults object that tells you if the executor was successful or not and the reason why it failed. You can use this to log the results of the executor and make decisions based on the results. But by default the framework will log the results of failed executors for you and skip the insight if it fails (eg if the insight is in the NEW state - based on the executor it may reject the insight).\n\n## Documentation\n\nThe documentation is still in progress but you can find the documentation for the framework at [OlympusTrader](https://olympustrader.readthedocs.io/en/latest/)\n\n\n## Collaborations\n\nIf you're interested in helping out just make a PR and ill happy to merge! I just to build a framework that is easy enough for people to build a live trading bot but flexible for users to to build complex strategies. Thanks\n\n## Star History\n\n[![Star History Chart](https://api.star-history.com/svg?repos=isaac-diaby/OlympusTrader&type=Timeline)](https://star-history.com/#isaac-diaby/OlympusTrader&Timeline)\n",
    "bugtrack_url": null,
    "license": "Apache 2.0",
    "summary": "trading bot framework",
    "version": "0.3.5.1",
    "project_urls": {
        "Documentation": "https://olympustrader.readthedocs.io/en/latest/",
        "Homepage": "https://olympustrader.readthedocs.io/en/latest/",
        "Repository": "https://github.com/isaac-diaby/OlympusTrader"
    },
    "split_keywords": [
        "trading bot",
        " framework",
        " backtesting",
        " trading",
        " hft"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "d2b2fea1bfb4c439b28ad47f38245a0982498d45c267673dd36ca95b1fc64699",
                "md5": "d8b632aadb8e94b30324973ecf8d4aef",
                "sha256": "5ef30e7e767b7162b0d057e5b169fc6eb68d79077632d92c068c95e6127be641"
            },
            "downloads": -1,
            "filename": "olympustrader-0.3.5.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "d8b632aadb8e94b30324973ecf8d4aef",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4.0,>=3.12",
            "size": 114617,
            "upload_time": "2024-12-16T04:05:44",
            "upload_time_iso_8601": "2024-12-16T04:05:44.753331Z",
            "url": "https://files.pythonhosted.org/packages/d2/b2/fea1bfb4c439b28ad47f38245a0982498d45c267673dd36ca95b1fc64699/olympustrader-0.3.5.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "f7b7b3883700fe47a89253baa166ec13f8a9a12d3bcf5c948a20b712454a1b5c",
                "md5": "3c7b894c08d376563155cc9e5aa577c5",
                "sha256": "8824473077f2a019e92efb3a2f45e3db83acf93143d0f5a0658367488d5ae831"
            },
            "downloads": -1,
            "filename": "olympustrader-0.3.5.1.tar.gz",
            "has_sig": false,
            "md5_digest": "3c7b894c08d376563155cc9e5aa577c5",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4.0,>=3.12",
            "size": 87777,
            "upload_time": "2024-12-16T04:05:48",
            "upload_time_iso_8601": "2024-12-16T04:05:48.619546Z",
            "url": "https://files.pythonhosted.org/packages/f7/b7/b3883700fe47a89253baa166ec13f8a9a12d3bcf5c948a20b712454a1b5c/olympustrader-0.3.5.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-12-16 04:05:48",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "isaac-diaby",
    "github_project": "OlympusTrader",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "requirements": [
        {
            "name": "olympustrader",
            "specs": []
        }
    ],
    "lcname": "olympustrader"
}
        
Elapsed time: 0.47143s