pysailfish


Namepysailfish JSON
Version 1.18.0 PyPI version JSON
download
home_page
Summary
upload_time2023-12-02 06:07:32
maintainer
docs_urlNone
authorSulfredLee
requires_python>=3.8,<4.0
license
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            
## pysailfish

### Example EA --- simple client
```python
from pysailfish.internal.MT_EA.MT4_EA import MT4_EA

class MT4DemoEA(MT4_EA):
    def __init__(self):
        super().__init__()

    # override
    def _OnInit(self) -> int:
        self._logger.info(self._user_inputs)
        self._logger.info("Here")
        return 0

    # override
    def _OnDeinit(self, reason: int) -> None:
        self._logger.info(f"Here reason: {reason}")
        return None

    # override
    def _OnTick(self) -> None:
        self._logger.info("Here")
        return None

    # override
    def _OnTimer(self) -> None:
        return None

    # override
    def _OnTester(self) -> float:
        self._logger.info("Here")
        return 0.0

    # override
    def _OnChartEvent(self
                      , id: int
                      , lparam: int
                      , dparam: float
                      , sparam: str) -> None:
        self._logger.info(f"Here id: {id} lparam: {lparam} dparam: {dparam} sparam: {sparam}")
        return None

def main() -> None:
    ea = MT4DemoEA()
    ea.InitComponent(server_ip="127.0.0.1"
                     , server_port=23456
                     , ea_name="MT4DemoEA")
    ea.StartEA()

if __name__ == "__main__":
    main()
```

### Example EA --- Moving Average EA
```python
from datetime import datetime

import pysailfish.internal.MT_EA.mt4_const as mc
from pysailfish.internal.MT_EA.MT4_EA import MT4_EA

class MA_EA(MT4_EA):
    def __init__(self):
        super().__init__()

    # override
    def _OnInit(self) -> int:
        self._logger.info(self._user_inputs)
        self._logger.info("Here")
        return 0

    # override
    def _OnDeinit(self, reason: int) -> None:
        self._logger.info(f"Here reason: {reason}")
        return None

    # override
    def _OnTick(self) -> None:
        vv = self.iADX(symbol=self._pv.symbol
                       , timeframe=mc.PERIOD_CURRENT
                       , period=self._user_inputs["MovingPeriod"]
                       , applied_price=mc.PRICE_CLOSE
                       , mode=mc.MODE_MAIN
                       , shift=0)
        # self._logger.info(f"Here {self._pv.symbol} {self._pv.time[0]}")
        # --- check for history and trading
        if self._pv.bars < 100 or self._pv.is_trade_allowed == False:
            return

        # --- calculate open orders by current symbol
        if self.__calculate_current_orders(self._pv.symbol) == 0:
            self.__check_for_open()
        else:
            self.__check_for_close()
        return None

    # override
    def _OnTimer(self) -> None:
        return None

    # override
    def _OnTester(self) -> float:
        self._logger.info("Here")
        return 0.0

    # override
    def _OnChartEvent(self
                      , id: int
                      , lparam: int
                      , dparam: float
                      , sparam: str) -> None:
        self._logger.info(f"Here id: {id} lparam: {lparam} dparam: {dparam} sparam: {sparam}")
        return None

    def __check_for_close(self) -> None:
        ma: float = 0
        #--- go trading only for first tiks of new bar
        if self._pv.volume[0] > 1:
            return None
        #--- get Moving Average
        ma = self.iMA(symbol=self._pv.symbol
                      , timeframe=mc.PERIOD_CURRENT
                      , ma_period=self._user_inputs["MovingPeriod"]
                      , ma_shift=self._user_inputs["MovingShift"]
                      , ma_method=mc.MODE_SMA
                      , applied_price=mc.PRICE_CLOSE
                      , shift=0)
        #---
        for i in range(self.OrdersTotal()):
            if self.OrderSelect(index=i, select=mc.SELECT_BY_POS, pool=mc.MODE_TRADES) == False:
                break
            if self.OrderMagicNumber() != self._pv.magic_num or self.OrderSymbol() != self._pv.symbol:
                continue
            #--- check order type
            if self.OrderType() == mc.OP_BUY:
                if self._pv.open[1] > ma and self._pv.close[1] < ma:
                    if not self.OrderClose(ticket=self.OrderTicket(), lots=self.OrderLots(), price=self._pv.bid, slippage=3, arrow_color=mc.clrWhite):
                        self._logger.error(f"OrderClose error {self.GetLastError()}")
                break
            if self.OrderType() == mc.OP_SELL:
                if self._pv.open[1] < ma and self._pv.close[1] > ma:
                    if not self.OrderClose(ticket=self.OrderTicket(), lots=self.OrderLots(), price=self._pv.ask, slippage=3, arrow_color=mc.clrWhite):
                        self._logger.error(f"OrderClose error {self.GetLastError()}");
                break

    def __lots_optimized(self) -> float:
        lot: float = float(self._user_inputs["Lots"])
        maximum_risk: float = float(self._user_inputs["MaximumRisk"])
        decrease_factor: float = float(self._user_inputs["DecreaseFactor"])
        orders: int = self.OrdersHistoryTotal() # history orders total
        losses: int = 0 # number of losses orders without a break
        #--- select lot size
        lot = self.NormalizeDouble(value=(self.AccountFreeMargin() * maximum_risk / 1000.0), digits=1);
        #--- calcuulate number of losses orders without a break
        if decrease_factor > 0:
            for i in reversed(range(orders)):
                if self.OrderSelect(index=i, select=mc.SELECT_BY_POS, pool=mc.MODE_HISTORY) == False:
                    self._logger.error("Error in history")
                    break
                if self.OrderSymbol() != self._pv.symbol or self.OrderType() > mc.OP_SELL:
                    continue
                #---
                if self.OrderProfit() > 0:
                    break
                if self.OrderProfit() < 0:
                    losses += 1
            if losses > 1:
                lot = self.NormalizeDouble(value=(lot - lot * losses / decrease_factor), digits=1)
        #--- return lot size
        if lot < 0.1:
            lot = 0.1
        return lot

    def __check_for_open(self) -> None:
        ma: float = 0
        res: int = 0
        #--- go trading only for first tiks of new bar
        if self._pv.volume[0] > 1:
            return None
        #--- get Moving Average
        ma = self.iMA(symbol=self._pv.symbol
                      , timeframe=mc.PERIOD_CURRENT
                      , ma_period=self._user_inputs["MovingPeriod"]
                      , ma_shift=self._user_inputs["MovingShift"]
                      , ma_method=mc.MODE_SMA
                      , applied_price=mc.PRICE_CLOSE
                      , shift=0)
        #--- sell conditions
        if self._pv.open[1] > ma and self._pv.close[1] < ma:
            res = self.OrderSend(symbol=self._pv.symbol
                                 , cmd=mc.OP_SELL
                                 , volume=self.__lots_optimized()
                                 , price=self._pv.bid
                                 , slippage=3
                                 , stoploss=0
                                 , takeprofit=0
                                 , comment=""
                                 , magic=self._pv.magic_num
                                 , expiration=datetime(1970, 1, 1, 0, 0, 0)
                                 , arrow_color=mc.clrRed)
            return None
        #--- buy conditions
        if self._pv.open[1] < ma and self._pv.close[1] > ma:
            res = self.OrderSend(symbol=self._pv.symbol
                                 , cmd=mc.OP_BUY
                                 , volume=self.__lots_optimized()
                                 , price=self._pv.ask
                                 , slippage=3
                                 , stoploss=0
                                 , takeprofit=0
                                 , comment=""
                                 , magic=self._pv.magic_num
                                 , expiration=datetime(1970, 1, 1, 0, 0, 0)
                                 , arrow_color=mc.clrBlue)
            return None

    def __calculate_current_orders(self, symbol: str) -> int:
        buys: int = 0
        sells: int = 0
        # ---
        for i in range(self.OrdersTotal()):
            if self._tf.order_select(index=i, select=mc.SELECT_BY_POS, pool=mc.MODE_TRADES) == False:
                break
            if self.OrderSymbol() == self._pv.symbol and self.OrderMagicNumber() == self._pv.magic_num:
                if self.OrderType() == mc.OP_BUY:
                    buys += 1
                if self.OrderType() == mc.OP_SELL:
                    sells += 1
        # --- return orders volume
        if buys > 0:
            return buys
        else:
            return -sells

def main() -> None:
    ea = MA_EA()
    ea.InitComponent(server_ip="127.0.0.1"
                     , server_port=23456
                     , ea_name="MA_EA")
    ea.StartEA()

if __name__ == "__main__":
    main()
```

### How to publish to pypi
```bash
# set up pypi token
$ poetry config pypi-token.pypi my-token

# build the project
$ poetry build

# publish the project
$ poetry publish

# DONE
```

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "pysailfish",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.8,<4.0",
    "maintainer_email": "",
    "keywords": "",
    "author": "SulfredLee",
    "author_email": "sflee1112@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/5f/e1/cdf62ae0c64b4df2304a4a15393e281f1f65e1e10a3658b119eac1ef326f/pysailfish-1.18.0.tar.gz",
    "platform": null,
    "description": "\n## pysailfish\n\n### Example EA --- simple client\n```python\nfrom pysailfish.internal.MT_EA.MT4_EA import MT4_EA\n\nclass MT4DemoEA(MT4_EA):\n    def __init__(self):\n        super().__init__()\n\n    # override\n    def _OnInit(self) -> int:\n        self._logger.info(self._user_inputs)\n        self._logger.info(\"Here\")\n        return 0\n\n    # override\n    def _OnDeinit(self, reason: int) -> None:\n        self._logger.info(f\"Here reason: {reason}\")\n        return None\n\n    # override\n    def _OnTick(self) -> None:\n        self._logger.info(\"Here\")\n        return None\n\n    # override\n    def _OnTimer(self) -> None:\n        return None\n\n    # override\n    def _OnTester(self) -> float:\n        self._logger.info(\"Here\")\n        return 0.0\n\n    # override\n    def _OnChartEvent(self\n                      , id: int\n                      , lparam: int\n                      , dparam: float\n                      , sparam: str) -> None:\n        self._logger.info(f\"Here id: {id} lparam: {lparam} dparam: {dparam} sparam: {sparam}\")\n        return None\n\ndef main() -> None:\n    ea = MT4DemoEA()\n    ea.InitComponent(server_ip=\"127.0.0.1\"\n                     , server_port=23456\n                     , ea_name=\"MT4DemoEA\")\n    ea.StartEA()\n\nif __name__ == \"__main__\":\n    main()\n```\n\n### Example EA --- Moving Average EA\n```python\nfrom datetime import datetime\n\nimport pysailfish.internal.MT_EA.mt4_const as mc\nfrom pysailfish.internal.MT_EA.MT4_EA import MT4_EA\n\nclass MA_EA(MT4_EA):\n    def __init__(self):\n        super().__init__()\n\n    # override\n    def _OnInit(self) -> int:\n        self._logger.info(self._user_inputs)\n        self._logger.info(\"Here\")\n        return 0\n\n    # override\n    def _OnDeinit(self, reason: int) -> None:\n        self._logger.info(f\"Here reason: {reason}\")\n        return None\n\n    # override\n    def _OnTick(self) -> None:\n        vv = self.iADX(symbol=self._pv.symbol\n                       , timeframe=mc.PERIOD_CURRENT\n                       , period=self._user_inputs[\"MovingPeriod\"]\n                       , applied_price=mc.PRICE_CLOSE\n                       , mode=mc.MODE_MAIN\n                       , shift=0)\n        # self._logger.info(f\"Here {self._pv.symbol} {self._pv.time[0]}\")\n        # --- check for history and trading\n        if self._pv.bars < 100 or self._pv.is_trade_allowed == False:\n            return\n\n        # --- calculate open orders by current symbol\n        if self.__calculate_current_orders(self._pv.symbol) == 0:\n            self.__check_for_open()\n        else:\n            self.__check_for_close()\n        return None\n\n    # override\n    def _OnTimer(self) -> None:\n        return None\n\n    # override\n    def _OnTester(self) -> float:\n        self._logger.info(\"Here\")\n        return 0.0\n\n    # override\n    def _OnChartEvent(self\n                      , id: int\n                      , lparam: int\n                      , dparam: float\n                      , sparam: str) -> None:\n        self._logger.info(f\"Here id: {id} lparam: {lparam} dparam: {dparam} sparam: {sparam}\")\n        return None\n\n    def __check_for_close(self) -> None:\n        ma: float = 0\n        #--- go trading only for first tiks of new bar\n        if self._pv.volume[0] > 1:\n            return None\n        #--- get Moving Average\n        ma = self.iMA(symbol=self._pv.symbol\n                      , timeframe=mc.PERIOD_CURRENT\n                      , ma_period=self._user_inputs[\"MovingPeriod\"]\n                      , ma_shift=self._user_inputs[\"MovingShift\"]\n                      , ma_method=mc.MODE_SMA\n                      , applied_price=mc.PRICE_CLOSE\n                      , shift=0)\n        #---\n        for i in range(self.OrdersTotal()):\n            if self.OrderSelect(index=i, select=mc.SELECT_BY_POS, pool=mc.MODE_TRADES) == False:\n                break\n            if self.OrderMagicNumber() != self._pv.magic_num or self.OrderSymbol() != self._pv.symbol:\n                continue\n            #--- check order type\n            if self.OrderType() == mc.OP_BUY:\n                if self._pv.open[1] > ma and self._pv.close[1] < ma:\n                    if not self.OrderClose(ticket=self.OrderTicket(), lots=self.OrderLots(), price=self._pv.bid, slippage=3, arrow_color=mc.clrWhite):\n                        self._logger.error(f\"OrderClose error {self.GetLastError()}\")\n                break\n            if self.OrderType() == mc.OP_SELL:\n                if self._pv.open[1] < ma and self._pv.close[1] > ma:\n                    if not self.OrderClose(ticket=self.OrderTicket(), lots=self.OrderLots(), price=self._pv.ask, slippage=3, arrow_color=mc.clrWhite):\n                        self._logger.error(f\"OrderClose error {self.GetLastError()}\");\n                break\n\n    def __lots_optimized(self) -> float:\n        lot: float = float(self._user_inputs[\"Lots\"])\n        maximum_risk: float = float(self._user_inputs[\"MaximumRisk\"])\n        decrease_factor: float = float(self._user_inputs[\"DecreaseFactor\"])\n        orders: int = self.OrdersHistoryTotal() # history orders total\n        losses: int = 0 # number of losses orders without a break\n        #--- select lot size\n        lot = self.NormalizeDouble(value=(self.AccountFreeMargin() * maximum_risk / 1000.0), digits=1);\n        #--- calcuulate number of losses orders without a break\n        if decrease_factor > 0:\n            for i in reversed(range(orders)):\n                if self.OrderSelect(index=i, select=mc.SELECT_BY_POS, pool=mc.MODE_HISTORY) == False:\n                    self._logger.error(\"Error in history\")\n                    break\n                if self.OrderSymbol() != self._pv.symbol or self.OrderType() > mc.OP_SELL:\n                    continue\n                #---\n                if self.OrderProfit() > 0:\n                    break\n                if self.OrderProfit() < 0:\n                    losses += 1\n            if losses > 1:\n                lot = self.NormalizeDouble(value=(lot - lot * losses / decrease_factor), digits=1)\n        #--- return lot size\n        if lot < 0.1:\n            lot = 0.1\n        return lot\n\n    def __check_for_open(self) -> None:\n        ma: float = 0\n        res: int = 0\n        #--- go trading only for first tiks of new bar\n        if self._pv.volume[0] > 1:\n            return None\n        #--- get Moving Average\n        ma = self.iMA(symbol=self._pv.symbol\n                      , timeframe=mc.PERIOD_CURRENT\n                      , ma_period=self._user_inputs[\"MovingPeriod\"]\n                      , ma_shift=self._user_inputs[\"MovingShift\"]\n                      , ma_method=mc.MODE_SMA\n                      , applied_price=mc.PRICE_CLOSE\n                      , shift=0)\n        #--- sell conditions\n        if self._pv.open[1] > ma and self._pv.close[1] < ma:\n            res = self.OrderSend(symbol=self._pv.symbol\n                                 , cmd=mc.OP_SELL\n                                 , volume=self.__lots_optimized()\n                                 , price=self._pv.bid\n                                 , slippage=3\n                                 , stoploss=0\n                                 , takeprofit=0\n                                 , comment=\"\"\n                                 , magic=self._pv.magic_num\n                                 , expiration=datetime(1970, 1, 1, 0, 0, 0)\n                                 , arrow_color=mc.clrRed)\n            return None\n        #--- buy conditions\n        if self._pv.open[1] < ma and self._pv.close[1] > ma:\n            res = self.OrderSend(symbol=self._pv.symbol\n                                 , cmd=mc.OP_BUY\n                                 , volume=self.__lots_optimized()\n                                 , price=self._pv.ask\n                                 , slippage=3\n                                 , stoploss=0\n                                 , takeprofit=0\n                                 , comment=\"\"\n                                 , magic=self._pv.magic_num\n                                 , expiration=datetime(1970, 1, 1, 0, 0, 0)\n                                 , arrow_color=mc.clrBlue)\n            return None\n\n    def __calculate_current_orders(self, symbol: str) -> int:\n        buys: int = 0\n        sells: int = 0\n        # ---\n        for i in range(self.OrdersTotal()):\n            if self._tf.order_select(index=i, select=mc.SELECT_BY_POS, pool=mc.MODE_TRADES) == False:\n                break\n            if self.OrderSymbol() == self._pv.symbol and self.OrderMagicNumber() == self._pv.magic_num:\n                if self.OrderType() == mc.OP_BUY:\n                    buys += 1\n                if self.OrderType() == mc.OP_SELL:\n                    sells += 1\n        # --- return orders volume\n        if buys > 0:\n            return buys\n        else:\n            return -sells\n\ndef main() -> None:\n    ea = MA_EA()\n    ea.InitComponent(server_ip=\"127.0.0.1\"\n                     , server_port=23456\n                     , ea_name=\"MA_EA\")\n    ea.StartEA()\n\nif __name__ == \"__main__\":\n    main()\n```\n\n### How to publish to pypi\n```bash\n# set up pypi token\n$ poetry config pypi-token.pypi my-token\n\n# build the project\n$ poetry build\n\n# publish the project\n$ poetry publish\n\n# DONE\n```\n",
    "bugtrack_url": null,
    "license": "",
    "summary": "",
    "version": "1.18.0",
    "project_urls": null,
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "be83fdf23266ca13a4766872eb0d5088629db9e766fe564450df4c434e321011",
                "md5": "cc8748d4b3e3f4a4e197423697aecd16",
                "sha256": "fb4029b3138ebe9516a15bb4a7923be369329d4270765b00250a3018877c7971"
            },
            "downloads": -1,
            "filename": "pysailfish-1.18.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "cc8748d4b3e3f4a4e197423697aecd16",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8,<4.0",
            "size": 48239,
            "upload_time": "2023-12-02T06:07:28",
            "upload_time_iso_8601": "2023-12-02T06:07:28.467477Z",
            "url": "https://files.pythonhosted.org/packages/be/83/fdf23266ca13a4766872eb0d5088629db9e766fe564450df4c434e321011/pysailfish-1.18.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "5fe1cdf62ae0c64b4df2304a4a15393e281f1f65e1e10a3658b119eac1ef326f",
                "md5": "7a7fe046aafbcdec3d1d7cad91425c66",
                "sha256": "60c321a234e9beb60270b10e61b77f793b570909d43f74b454f007dbad91adb6"
            },
            "downloads": -1,
            "filename": "pysailfish-1.18.0.tar.gz",
            "has_sig": false,
            "md5_digest": "7a7fe046aafbcdec3d1d7cad91425c66",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8,<4.0",
            "size": 33633,
            "upload_time": "2023-12-02T06:07:32",
            "upload_time_iso_8601": "2023-12-02T06:07:32.980253Z",
            "url": "https://files.pythonhosted.org/packages/5f/e1/cdf62ae0c64b4df2304a4a15393e281f1f65e1e10a3658b119eac1ef326f/pysailfish-1.18.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-12-02 06:07:32",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "pysailfish"
}
        
Elapsed time: 0.15966s