├── .gitignore ├── LICENSE ├── README.md ├── bitmex ├── bitmex.py ├── bitmex.xlsx └── pyxll.cfg ├── contextmenus ├── context_menus.py ├── pyxll.cfg └── ribbon.xml ├── formatters ├── README.md ├── fill_formatter.py ├── formatters.xlsx └── pyxll.cfg ├── highcharts ├── highcharts.xlsx ├── highcharts │ ├── basic-line.py │ └── line-time-series.py ├── highcharts_xl.py ├── highstocks │ └── candlestick-and-volume.py ├── pyxll.cfg └── requirements.txt ├── ipython ├── 0.13 │ └── ipython.py ├── 1.x │ └── ipython.py ├── 4.x │ └── ipython.py ├── 6.x │ ├── ipython.py │ ├── requirements.txt │ └── ribbon.xml ├── 7.x │ ├── ipython.py │ ├── pyxll.cfg │ ├── requirements.txt │ └── ribbon.xml └── README.md ├── machine-learning ├── data │ ├── zoo.csv │ └── zoo.names.txt ├── decision_tree.py ├── decision_tree.xlsx ├── images │ ├── amphibian.jpg │ ├── bird.jpg │ ├── fish.jpg │ ├── insect.jpg │ ├── mammal.jpg │ ├── mollusc.jpg │ └── reptile.jpg └── pyxll.cfg ├── matplotlib ├── README.md ├── embeddedplot.py ├── interactiveplot.py ├── matplotlib.xlsx ├── pyxll.cfg ├── requirements.txt └── ribbon.xml ├── montecarlo ├── README.md ├── montecarlo.py ├── montecarlo.xlsx ├── pyxll.cfg └── requirements.txt ├── multiprocessing ├── running_object_table.py └── running_object_table.xlsx ├── objectcache └── README.md ├── optimization ├── README.md ├── optimize1.py ├── optimize2.py ├── optimize3.py ├── optimizer.xlsx └── pyxll.cfg ├── pandas ├── archive │ ├── README.md │ ├── pandas_example.py │ ├── pandas_example.xlsx │ ├── pandastypes.py │ └── pyxll.cfg └── uscities │ ├── README.md │ ├── pyxll.cfg │ ├── uscities.py │ └── uscities.xlsx ├── profiling ├── average_tol.py ├── profiling.xlsm ├── profiling_tools.py └── pyxll.cfg ├── pubsub ├── README.md ├── pubsub.py ├── pubsub_example.py ├── pubsub_example.xlsx └── pyxll.cfg ├── pylambda ├── README.md ├── pylambda.py └── pylambda.xlsx ├── pytorch ├── abstract-art │ ├── README.md │ ├── pytorch_abstract_art.py │ ├── pytorch_abstract_art.xlsx │ └── pyxll.cfg └── parlai │ ├── README.md │ ├── excel_agent.py │ ├── parlai-example.xlsx │ ├── parlai_excel.py │ └── pyxll.cfg ├── qtgraphics ├── README.md ├── op_dialog.py ├── optimize4.py ├── optimize5.py ├── optimizer4.xlsx ├── pyxll.cfg ├── qt_ex1.py ├── qt_ex2.py └── qt_ex3.py ├── rtd ├── README.md └── rtd_function.py ├── scikit-learn └── nlp_vlookup │ ├── README.md │ ├── nlp_vlookup.py │ └── nlp_vlookup.xlsx ├── scripts └── uninstall.py ├── shortcuts ├── README.md ├── resize_array_formula.py ├── shortcut_test.py └── shortcuts.py ├── twitter ├── README.md ├── twitterxl.cfg ├── twitterxl.py └── twitterxl.xlsx ├── unitttests ├── README.md ├── test_macro.py └── test_udf.py └── xldoom ├── README.md ├── pyxll.cfg ├── requirements.txt └── xldoom.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | __pycache__ 21 | 22 | # Installer logs 23 | pip-log.txt 24 | 25 | # Unit test / coverage reports 26 | .coverage 27 | .tox 28 | nosetests.xml 29 | 30 | # Translations 31 | *.mo 32 | 33 | # Mr Developer 34 | .mr.developer.cfg 35 | .project 36 | .pydevproject 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pyxll-examples 2 | ============== 3 | 4 | Example code to be used with PyXLL. 5 | 6 | See http://www.pyxll.com for information about how PyXLL can be used to embed Python code in Excel. 7 | -------------------------------------------------------------------------------- /bitmex/bitmex.py: -------------------------------------------------------------------------------- 1 | """ 2 | Get BitMEX prices into Excel using websockets and PyXLL RTD functions. 3 | 4 | Requires: 5 | - Python 3.5 or higher 6 | - websockets 7 | 8 | >> pip install websockets 9 | 10 | See https://pyxll.medium.com/live-streaming-crypto-prices-in-excel-aaa41628bc53 11 | """ 12 | from pyxll import xl_func, RTD, get_event_loop 13 | import websockets 14 | import asyncio 15 | import json 16 | 17 | 18 | class BitMex: 19 | """Class to manage subscriptions to instrument prices.""" 20 | 21 | URI = "wss://www.bitmex.com/realtime" 22 | 23 | def __init__(self): 24 | self.__websocket = None 25 | self.__running = False 26 | self.__running_task = None 27 | self.__subscriptions = {} 28 | self.__data = {} 29 | self.__lock = asyncio.Lock() 30 | 31 | async def __connect(self): 32 | # Connect to the websocket API and start the __run coroutine 33 | self.__running = True 34 | self.__websocket = await websockets.connect(self.URI) 35 | self.__running_task = asyncio.create_task(self.__run()) 36 | 37 | async def __disconnect(self): 38 | # Close the websocket and wait for __run to complete 39 | self.__running = False 40 | await self.__websocket.close() 41 | self.__websocket = None 42 | await self.__running_task 43 | 44 | async def __run(self): 45 | # Read from the websocket until disconnected 46 | while self.__running: 47 | msg = await self.__websocket.recv() 48 | await self.__process_message(json.loads(msg)) 49 | 50 | async def __process_message(self, msg): 51 | if msg.get("table", None) == "instrument": 52 | # Extract the data from the message, update our data dictionary and notify subscribers 53 | for data in msg.get("data", []): 54 | symbol = data["symbol"] 55 | timestamp = data["symbol"] 56 | 57 | # Update the latest values in our data dictionary and notify any subscribers 58 | tasks = [] 59 | subscribers = self.__subscriptions.get(symbol, {}) 60 | latest = self.__data.setdefault(symbol, {}) 61 | for field, value in data.items(): 62 | latest[field] = (value, timestamp) 63 | 64 | # Notify the subscribers with the updated field 65 | for subscriber in subscribers.get(field, []): 66 | coro = subscriber(symbol, field, value, timestamp) 67 | tasks.append(asyncio.create_task(coro)) 68 | 69 | # await all the tasks from the subscribers 70 | if tasks: 71 | await asyncio.wait(tasks) 72 | 73 | async def subscribe(self, symbol, field, callback): 74 | """Subscribe to updates for a specific symbol and field. 75 | 76 | The callback will be called as 'await callback(symbol, field, value, timestamp)' 77 | whenever an update is received. 78 | """ 79 | async with self.__lock: 80 | # Connect the websocket if necessary 81 | if self.__websocket is None: 82 | await self.__connect() 83 | 84 | # Send the subscribe message if we're not already subscribed 85 | if symbol not in self.__subscriptions: 86 | msg = {"op": "subscribe", "args": [f"instrument:{symbol}"]} 87 | await self.__websocket.send(json.dumps(msg)) 88 | 89 | # Add the subscriber to the dict of subscriptions 90 | self.__subscriptions.setdefault(symbol, {}).setdefault(field, []).append(callback) 91 | 92 | # Call the callback with the latest data 93 | data = self.__data.get(symbol, {}) 94 | if field in data: 95 | (value, timestamp) = data[field] 96 | await callback(symbol, field, value, timestamp) 97 | 98 | async def unsubscribe(self, symbol, field, callback): 99 | async with self.__lock: 100 | # Remove the subscriber from the list of subscriptions 101 | self.__subscriptions[symbol][field].remove(callback) 102 | if not self.__subscriptions[symbol][field]: 103 | del self.__subscriptions[symbol][field] 104 | 105 | # Unsubscribe if we no longer have any subscriptions for this instrument 106 | if not self.__subscriptions[symbol]: 107 | msg = {"op": "unsubscribe", "args": [f"instrument:{symbol}"]} 108 | await self.__websocket.send(json.dumps(msg)) 109 | del self.__subscriptions[symbol] 110 | self.__data.pop(symbol, None) 111 | 112 | # Disconnect if we no longer have any subscriptions 113 | if not self.__subscriptions: 114 | async with self.__lock: 115 | await self.__disconnect() 116 | 117 | 118 | class BitMexRTD(RTD): 119 | """RTD class for subscribing to BitMEX prices using the 120 | BitMex class above. 121 | """ 122 | 123 | # Use a single BitMex object for all RTD functions 124 | __bitmex_client = None 125 | 126 | def __init__(self, symbol, field): 127 | super().__init__(value="Waiting...") 128 | self.__symbol = symbol 129 | self.__field = field 130 | 131 | async def connect(self): 132 | # Subscribe to BitMix updates when Excel connects to the RTD object 133 | bitmex = await self.__get_bitmex_client() 134 | await bitmex.subscribe(self.__symbol, self.__field, self.__update) 135 | 136 | async def disconnect(self): 137 | # Unsubscribe to BitMix updates when Excel disconnects from the RTD object 138 | bitmex = await self.__get_bitmex_client() 139 | await bitmex.unsubscribe(self.__symbol, self.__field, self.__update) 140 | 141 | @classmethod 142 | async def __get_bitmex_client(cls): 143 | # This is async as the BitMex object should be created while the 144 | # asyncio event loop is active. 145 | if cls.__bitmex_client is None: 146 | cls.__bitmex_client = BitMex() 147 | return cls.__bitmex_client 148 | 149 | async def __update(self, symbol, field, value, timestamp): 150 | # Update the value in Excel 151 | self.value = value 152 | 153 | 154 | @xl_func("string symbol, string field: rtd", recalc_on_open=True) 155 | def bitmex_rtd(symbol, field="lastPrice"): 156 | """Subscribe to BitMEX prices for a given symbol.""" 157 | return BitMexRTD(symbol, field) 158 | 159 | 160 | if __name__ == "__main__": 161 | 162 | async def main(): 163 | # This is the callback that will be called whenever there's an update 164 | async def callback(symbol, field, value, timestamp): 165 | print((symbol, field, value, timestamp)) 166 | 167 | bm = BitMex() 168 | 169 | await bm.subscribe("XBTUSD", "lastPrice", callback) 170 | 171 | await asyncio.sleep(60) 172 | 173 | await bm.unsubscribe("XBTUSD", "lastPrice", callback) 174 | 175 | print("DONE!") 176 | 177 | # Run the async main function 178 | asyncio.run(main()) 179 | -------------------------------------------------------------------------------- /bitmex/bitmex.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyxll/pyxll-examples/d10646781ea95268359e4e42da0ba237f737b806/bitmex/bitmex.xlsx -------------------------------------------------------------------------------- /bitmex/pyxll.cfg: -------------------------------------------------------------------------------- 1 | # Get BitMEX prices into Excel using websockets and PyXLL RTD functions. 2 | # 3 | # Requires: 4 | # - Python 3.5 or higher 5 | # - websockets 6 | # 7 | # pip install websockets 8 | # 9 | 10 | [PYXLL] 11 | pythonpath = . 12 | modules = 13 | bitmex 14 | -------------------------------------------------------------------------------- /contextmenus/context_menus.py: -------------------------------------------------------------------------------- 1 | """ 2 | Callbacks for context menus example. 3 | Requires PyXLL >= 4 and Excel >= 2010. 4 | See ribbon.xml. 5 | """ 6 | from pyxll import xl_app 7 | 8 | def toggle_case(control): 9 | """Toggle the case of the currently selected cells""" 10 | # get the Excel Application object 11 | xl = xl_app() 12 | 13 | # iterate over the currently selected cells 14 | for cell in xl.Selection: 15 | # get the cell value 16 | value = cell.Value 17 | 18 | # skip any cells that don't contain text 19 | if not isinstance(value, str): 20 | continue 21 | 22 | # toggle between upper, lower and proper case 23 | if value.isupper(): 24 | value = value.lower() 25 | elif value.islower(): 26 | value = value.title() 27 | else: 28 | value = value.upper() 29 | 30 | # set the modified value on the cell 31 | cell.Value = value 32 | 33 | 34 | def tolower(control): 35 | """Set the currently selected cells to lower case""" 36 | # get the Excel Application object 37 | xl = xl_app() 38 | 39 | # iterate over the currently selected cells 40 | for cell in xl.Selection: 41 | # get the cell value 42 | value = cell.Value 43 | 44 | # skip any cells that don't contain text 45 | if not isinstance(value, str): 46 | continue 47 | 48 | cell.Value = value.lower() 49 | 50 | 51 | def toupper(control): 52 | """Set the currently selected cells to upper case""" 53 | # get the Excel Application object 54 | xl = xl_app() 55 | 56 | # iterate over the currently selected cells 57 | for cell in xl.Selection: 58 | # get the cell value 59 | value = cell.Value 60 | 61 | # skip any cells that don't contain text 62 | if not isinstance(value, str): 63 | continue 64 | 65 | cell.Value = value.upper() 66 | 67 | 68 | def toproper(control): 69 | """Set the currently selected cells to 'proper' case""" 70 | # get the Excel Application object 71 | xl = xl_app() 72 | 73 | # iterate over the currently selected cells 74 | for cell in xl.Selection: 75 | # get the cell value 76 | value = cell.Value 77 | 78 | # skip any cells that don't contain text 79 | if not isinstance(value, str): 80 | continue 81 | 82 | cell.Value = value.title() 83 | 84 | 85 | def dynamic_menu(control): 86 | """Return an xml fragment for the dynamic menu""" 87 | xml = """ 88 | 89 | 101 | """ 102 | return xml 103 | -------------------------------------------------------------------------------- /contextmenus/pyxll.cfg: -------------------------------------------------------------------------------- 1 | ; Example showing how to configure Excel context menus 2 | ; 3 | ; To try this out, add this file to your existing pyxll.cfg 4 | ; file using the 'external_config' option. 5 | ; 6 | ; Requires PyXLL >= 4 and Excel >= 2010. 7 | ; 8 | [PYXLL] 9 | ribbon = ribbon.xml 10 | modules = context_menus 11 | 12 | [PYTHON] 13 | pythonpath = . 14 | -------------------------------------------------------------------------------- /contextmenus/ribbon.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |