<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>quantbang</title>
        <link>https://paragraph.com/@quantbang</link>
        <description>专注加密货币量化交易 , focus on crypto quant</description>
        <lastBuildDate>Mon, 06 Apr 2026 17:10:23 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>quantbang</title>
            <url>https://storage.googleapis.com/papyrus_images/5b662693828e163dd0f5204336b5c1b9dd597cdee99c0e1f656e000f68c76b41.jpg</url>
            <link>https://paragraph.com/@quantbang</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[用基于 Python 的开发框架 Brownie 部署以太坊智能合约]]></title>
            <link>https://paragraph.com/@quantbang/python-brownie</link>
            <guid>SUXCEWKeHG79ofjeLZUi</guid>
            <pubDate>Thu, 24 Mar 2022 13:31:45 GMT</pubDate>
            <description><![CDATA[在本文中，我们将使用Python部署智能合约。这篇文章可能是您走向智能合约和区块链开发的桥梁！ 介绍 我希望可以在任何开发场景都尽量用Python。在区块链开发中，常用的是以太坊虚拟机智能合约语言Solidity，它具有许多不错的功能，并且仍然可以使用 Python 进行部署。刚开始使用Solidity时，我使用了Remix（https://remix.ethereum.org/），这是一个强大的Web IDE，可让您进行智能合约可视化。Remix很棒，我现在仍然使用它，但是在单个IDE之外可以实现很多其他功能。后来我开始学习Truffle（https://www.trufflesuite.com/）和HardHat（https://hardhat.org/guides/mainnet-forking.html），它们是用于部署智能合约的Node.js框架。 这些是到目前为止我所见过的主要框架，这些框架都不错，但是我更喜欢Python。所以当我发现Brownie 和web3.py：一个用于部署智能合约的Python框架和一个用于区块链开发的开源协议之后非常兴奋。我们将在本文中同时...]]></description>
            <content:encoded><![CDATA[<figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f9f460a7009aef3218cd8fac7258390dd4e1068993e3ac4c0ee256f8083807fa.jpg" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>在本文中，我们将使用Python部署智能合约。这篇文章可能是您走向智能合约和区块链开发的桥梁！</p><p><strong>介绍</strong></p><p>我希望可以在任何开发场景都尽量用Python。在区块链开发中，常用的是以太坊虚拟机智能合约语言Solidity，它具有许多不错的功能，并且仍然可以使用 Python 进行部署。刚开始使用Solidity时，我使用了Remix（<code>https://remix.ethereum.org/</code>），这是一个强大的Web IDE，可让您进行智能合约可视化。Remix很棒，我现在仍然使用它，但是在单个IDE之外可以实现很多其他功能。后来我开始学习Truffle（<code>https://www.trufflesuite.com/</code>）和HardHat（<code>https://hardhat.org/guides/mainnet-forking.html</code>），它们是用于部署智能合约的<code>Node.js</code>框架。</p><p>这些是到目前为止我所见过的主要框架，这些框架都不错，但是我更喜欢Python。所以当我发现Brownie 和web3.py：一个用于部署智能合约的Python框架和一个用于区块链开发的开源协议之后非常兴奋。我们将在本文中同时介绍Brownie和Web3.py。</p><p><strong>为什么选择Python？</strong></p><p>有这么多数据科学家、学者和金融科技机构使用Python是有原因的。它用途广泛，具有轻松的开发体验，并且与各种第三方库紧密结合在一起。顶级 defi 项目开始意识到这一点，诸如<code>yearn.finance</code>之类的项目使用python来部署其所有生产代码。<code>Yearn.finance</code>由一群非常有才华的金融科技工程师经营，他们转向了区块链，带着他们熟悉和喜爱的Python工具。</p><p><strong>Brownie是什么？</strong></p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/61bc73ae80f1794a63479694e283d8ca2b408dc691c63f785e17a9e2d11105f7.jpg" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Brownie是由Ben Hauser创建的Python智能合约开源框架，又名“iamdefinitelyahuman”（中文意思“非绝对人类”），是一件艺术品。这就是<code>yearn.finance</code>团队用来部署和维护智能合约的工具。您可以使用简单的命令启动项目，然后立即开始使用代码。</p><p><strong>用Python部署您的第一个智能合约</strong></p><p><strong>1. 安装 Brownie 和 bake</strong></p><p>Brownie具有“baking”功能，可让您使用一些基础代码启动存储库，因为大多数项目都需要很多相同的部分，类似于<code>create-eth-app</code>。要开始使用，和其他所有Python软件包的安装方式一样。</p><pre data-type="codeBlock" text="pip install eth-brownie
"><code>pip install eth<span class="hljs-operator">-</span>brownie
</code></pre><p>我们还需要安装ganache-cli一个用于部署本地区块链的软件包。为此，您需要安装npm和nodejs。</p><pre data-type="codeBlock" text="npm install -g ganache-cli
"><code>npm install <span class="hljs-operator">-</span>g ganache<span class="hljs-operator">-</span>cli
</code></pre><p>准备开始！我们将使用<code>chainlink-mix</code>入门，因为许多顶级defi项目都使用Chainlink来获取其资产数据。</p><pre data-type="codeBlock" text="brownie bake chainlink-mix
cd chainlink
"><code>brownie bake chainlink-mix
<span class="hljs-built_in">cd</span> chainlink
</code></pre><p>通过<code>ls</code>命令将向我们展示项目的结构布局</p><p>Brownie项目布局</p><pre data-type="codeBlock" text="build : This is where the project keeps track of your deployed smart contracts and compiled contracts
contracts : The source code of your contracts, typically written in solidity or vyper
interfaces : A layout of interfaces you’ll need to work with deployed contracts. Every interaction with a contract needs an ABI and an address. Interfaces are great ways to get a contract’s ABI
scripts : Scripts we create to automate processes of working with our contracts
tests : Tests
brownie-config.yaml : This is where we have all the information for brownie to understand how to work with our smart contract. What blockchain do we want to deploy to? Are there any special parameters we want to set? All these are set in the config file.
"><code>build : This <span class="hljs-built_in">is</span> <span class="hljs-keyword">where</span> the project keeps track <span class="hljs-keyword">of</span> your deployed smart contracts <span class="hljs-built_in">and</span> compiled contracts
contracts : The source code <span class="hljs-keyword">of</span> your contracts, typically written <span class="hljs-keyword">in</span> solidity <span class="hljs-built_in">or</span> vyper
interfaces : A layout <span class="hljs-keyword">of</span> interfaces you’ll need <span class="hljs-keyword">to</span> work <span class="hljs-keyword">with</span> deployed contracts. Every interaction <span class="hljs-keyword">with</span> a contract needs an ABI <span class="hljs-built_in">and</span> an address. Interfaces are great ways <span class="hljs-keyword">to</span> <span class="hljs-keyword">get</span> a contract’s ABI
scripts : Scripts we create <span class="hljs-keyword">to</span> automate processes <span class="hljs-keyword">of</span> working <span class="hljs-keyword">with</span> our contracts
tests : Tests
brownie-config.yaml : This <span class="hljs-built_in">is</span> <span class="hljs-keyword">where</span> we have all the information <span class="hljs-keyword">for</span> brownie <span class="hljs-keyword">to</span> understand how <span class="hljs-keyword">to</span> work <span class="hljs-keyword">with</span> our smart contract. What blockchain <span class="hljs-keyword">do</span> we want <span class="hljs-keyword">to</span> deploy <span class="hljs-keyword">to</span>? Are there any special parameters we want <span class="hljs-keyword">to</span> <span class="hljs-keyword">set</span>? All these are <span class="hljs-keyword">set</span> <span class="hljs-keyword">in</span> the config file.
</code></pre><p><code>requirements.txt</code>，<code>README.md</code>，<code>LICENSE</code>和<code>.gitignore</code>可以忽略，您将在后面了解它们的用途。</p><p><strong>2.设置环境变量</strong></p><p>如果您熟悉区块链开发，就会知道本地区块链，测试网区块链和主网区块链都是不同的东西。我们将部署到测试网，以便我们可以与真实的实时区块链网络进行交互。您需要一个<code>WEB3_INFURA_PROJECT_ID</code>，可以通过创建<code>Infura</code>帐户来检索该<code>WEB3_INFURA_PROJECT_ID</code>。这就是我们用来连接到测试网络的东西。我们还将获得一个<code>metamask</code>或其他web3以太坊钱包，并用一些ETH进行注资。对于这个demo，我们要使用<code>Kovan</code>测试网络。</p><p>您可以跳过有关LINK资金的部分，我们只需要testnet ETH。我们也不会使用Ropsten，而是使用Kovan。如果您已经有了钱包，请从<code>https://gitter.im/kovan-testnet/faucet</code>获取一些Kovan Ether。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/ee89f4c04b046a23e09b0e5b1b46fb928216ac1ec29e7acaeb1f5cc649f1f04f.jpg" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><strong>安装，配置和Metamask</strong></p><p>一旦有了Metamask钱包，就可以将私钥导出到<code>PRIVATE_KEY</code>环境变量。在此处(<code>https://www.twilio.com/blog/2017/01/how-to-set-environment-variables.html</code>)阅读有关设置环境变量的信息。如果这仍然使您感到困惑，并且这只是一个测试钱包，请随意将代码中的<code>PRIVATE_KEY</code>替换为您的私钥和<code>WEB3_INFURA_PROJECT_ID</code>。</p><p><strong>3.部署您的智能合约</strong></p><p>在我们的脚本文件夹中，我们有一个名为<code>deploy_price_consumer_v3.py</code>的脚本，该脚本将部署我们的智能合约，该合约读取以太坊的美元价格。如果您想更轻松地了解该合约的功能以及如何部署它，请随时查看有关部署价格订阅合同的Chainlink教程(<code>https://docs.chain.link/docs/beginners-tutorial/</code>)。<code>brownie run</code>是我们可以用来运行脚本的命令。如果仅运行<code>brownie</code>，则可以看到所有命令的列表。</p><pre data-type="codeBlock" text="brownie run scripts/price_feed_scripts/deploy_price_consumer_v3.py --network kovan
"><code>brownie run scripts<span class="hljs-operator">/</span>price_feed_scripts<span class="hljs-operator">/</span>deploy_price_consumer_v3.py <span class="hljs-operator">-</span><span class="hljs-operator">-</span>network kovan
</code></pre><p><code>--network kovan</code>允许我们设置要使用的网络。我们正在使用kovan testnet进行此演示。您将需要Kovan ETH来做到这一点！您将获得很多输出内容，但最终会得到类似以下结果：</p><pre data-type="codeBlock" text="Running &apos;scripts/price_feed_scripts/deploy_price_consumer_v3.py::main&apos;...
Transaction sent: 0x23d1dfa3937e0cfbab58f8d5ecabe2bfffc28bbe2349527dabe9289e747bac56
Gas price: 20.0 gwei   Gas limit: 145600   Nonce: 1339
PriceFeed.constructor confirmed - Block: 22721813   Gas used: 132364 (90.91%)
PriceFeed deployed at: 0x6B2305935DbC77662811ff817cF3Aa54fc585816
"><code>Running <span class="hljs-string">'scripts/price_feed_scripts/deploy_price_consumer_v3.py::main'</span>...
Transaction sent: <span class="hljs-number">0x23d1dfa3937e0cfbab58f8d5ecabe2bfffc28bbe2349527dabe9289e747bac56</span>
Gas price: <span class="hljs-number">20.0</span> <span class="hljs-literal">gwei</span>   Gas limit: <span class="hljs-number">145600</span>   Nonce: <span class="hljs-number">1339</span>
PriceFeed.constructor confirmed <span class="hljs-operator">-</span> Block: <span class="hljs-number">22721813</span>   Gas used: <span class="hljs-number">132364</span> (<span class="hljs-number">90.91</span><span class="hljs-operator">%</span>)
PriceFeed deployed at: <span class="hljs-number">0x6B2305935DbC77662811ff817cF3Aa54fc585816</span>
</code></pre><p>如果此方法正常运行，我们可以转到kovan etherscan并找到我们部署的合约。上面的链接显示了此示例中部署的合约。</p><p><strong>4.读取您的智能合约</strong></p><p>现在我们已经部署了智能合约，我们可以从刚刚部署的合约中读取以太坊的价格。我们将运行另一个脚本：</p><pre data-type="codeBlock" text="brownie run scripts/price_feed_scripts/read_price_feed.py --network kovan
"><code>brownie run scripts<span class="hljs-operator">/</span>price_feed_scripts<span class="hljs-operator">/</span>read_price_feed.py <span class="hljs-operator">-</span><span class="hljs-operator">-</span>network kovan
</code></pre><p>得到类似以下的输出：</p><pre data-type="codeBlock" text="Brownie v1.12.2 - Python development framework for Ethereum
ChainlinkProject is the active project.
Running &apos;scripts/price_feed_scripts/read_price_feed.py::main&apos;...
Reading data from 0x6B2305935DbC77662811ff817cF3Aa54fc585816
62322000000
Where 62322000000 is the current price of ETH in USD! Solidity doesn’t understand decimals, and we know that this example has 8 decimals, so the price is $623.22 .
"><code>Brownie v1<span class="hljs-number">.12</span><span class="hljs-number">.2</span> <span class="hljs-operator">-</span> Python development framework <span class="hljs-keyword">for</span> Ethereum
ChainlinkProject <span class="hljs-keyword">is</span> the active project.
Running <span class="hljs-string">'scripts/price_feed_scripts/read_price_feed.py::main'</span>...
Reading data <span class="hljs-keyword">from</span> <span class="hljs-number">0x6B2305935DbC77662811ff817cF3Aa54fc585816</span>
<span class="hljs-number">62322000000</span>
Where <span class="hljs-number">62322000000</span> <span class="hljs-keyword">is</span> the current price of ETH in USD<span class="hljs-operator">!</span> Solidity doesn’t understand decimals, and we know that <span class="hljs-built_in">this</span> example has <span class="hljs-number">8</span> decimals, so the price <span class="hljs-keyword">is</span> $623<span class="hljs-number">.22</span> .
</code></pre><p>您刚刚使用Python和Brownie部署了您的第一个智能合约！</p><p><strong>使用web3.py</strong></p><p>Brownie使用名为<code>web3.py</code>的工具让您的开发更轻松，但是如果机智点，则我们始终可以直接在没有框架的情况下使用合约。<code>Web3.py</code>是一个原始程序包，我们可以使用它来更直接地处理合同。为此，我们只需要上面的Kovan infura项目ID。请记住，要与任何智能合约进行交互，您需要做两件事：</p><ul><li><p>智能合约ABI</p></li><li><p>智能合约地址</p></li></ul><p>Brownie 会在后台处理很多此类工作，但我们也可以手动进行。这是通过web3.py从链上合同中读取的内容。首先，我们需要安装web3.py。</p><pre data-type="codeBlock" text="pip install web3
"><code></code></pre><p>然后，我们可以在文件中运行以下内容。</p><pre data-type="codeBlock" text="web3 = Web3(Web3.HTTPProvider(&apos;https://kovan.infura.io/v3/&lt;infura_project_id&gt;&apos;)) 
abi = &apos;[{&quot;inputs&quot;:[],&quot;name&quot;:&quot;decimals&quot;,&quot;outputs&quot;:[{&quot;internalType&quot;:&quot;uint8&quot;,&quot;name&quot;:&quot;&quot;,&quot;type&quot;:&quot;uint8&quot;}],&quot;stateMutability&quot;:&quot;view&quot;,&quot;type&quot;:&quot;function&quot;},{&quot;inputs&quot;:[],&quot;name&quot;:&quot;description&quot;,&quot;outputs&quot;:[{&quot;internalType&quot;:&quot;string&quot;,&quot;name&quot;:&quot;&quot;,&quot;type&quot;:&quot;string&quot;}],&quot;stateMutability&quot;:&quot;view&quot;,&quot;type&quot;:&quot;function&quot;},{&quot;inputs&quot;:[{&quot;internalType&quot;:&quot;uint80&quot;,&quot;name&quot;:&quot;_roundId&quot;,&quot;type&quot;:&quot;uint80&quot;}],&quot;name&quot;:&quot;getRoundData&quot;,&quot;outputs&quot;:[{&quot;internalType&quot;:&quot;uint80&quot;,&quot;name&quot;:&quot;roundId&quot;,&quot;type&quot;:&quot;uint80&quot;},{&quot;internalType&quot;:&quot;int256&quot;,&quot;name&quot;:&quot;answer&quot;,&quot;type&quot;:&quot;int256&quot;},{&quot;internalType&quot;:&quot;uint256&quot;,&quot;name&quot;:&quot;startedAt&quot;,&quot;type&quot;:&quot;uint256&quot;},{&quot;internalType&quot;:&quot;uint256&quot;,&quot;name&quot;:&quot;updatedAt&quot;,&quot;type&quot;:&quot;uint256&quot;},{&quot;internalType&quot;:&quot;uint80&quot;,&quot;name&quot;:&quot;answeredInRound&quot;,&quot;type&quot;:&quot;uint80&quot;}],&quot;stateMutability&quot;:&quot;view&quot;,&quot;type&quot;:&quot;function&quot;},{&quot;inputs&quot;:[],&quot;name&quot;:&quot;latestRoundData&quot;,&quot;outputs&quot;:[{&quot;internalType&quot;:&quot;uint80&quot;,&quot;name&quot;:&quot;roundId&quot;,&quot;type&quot;:&quot;uint80&quot;},{&quot;internalType&quot;:&quot;int256&quot;,&quot;name&quot;:&quot;answer&quot;,&quot;type&quot;:&quot;int256&quot;},{&quot;internalType&quot;:&quot;uint256&quot;,&quot;name&quot;:&quot;startedAt&quot;,&quot;type&quot;:&quot;uint256&quot;},{&quot;internalType&quot;:&quot;uint256&quot;,&quot;name&quot;:&quot;updatedAt&quot;,&quot;type&quot;:&quot;uint256&quot;},{&quot;internalType&quot;:&quot;uint80&quot;,&quot;name&quot;:&quot;answeredInRound&quot;,&quot;type&quot;:&quot;uint80&quot;}],&quot;stateMutability&quot;:&quot;view&quot;,&quot;type&quot;:&quot;function&quot;},{&quot;inputs&quot;:[],&quot;name&quot;:&quot;version&quot;,&quot;outputs&quot;:[{&quot;internalType&quot;:&quot;uint256&quot;,&quot;name&quot;:&quot;&quot;,&quot;type&quot;:&quot;uint256&quot;}],&quot;stateMutability&quot;:&quot;view&quot;,&quot;type&quot;:&quot;function&quot;}]&apos; 
addr = &apos;0x9326BFA02ADD2366b30bacB125260Af641031331&apos; 
contract = web3.eth.contract(address=addr, abi=abi) 
latestData = contract.functions.latestRoundData().call() print(latestData)
"><code>web3 = Web3(Web3.HTTPProvider('https://kovan.infura.io/v3/&#x3C;infura_project_id>')) 
abi = '[{<span class="hljs-string">"inputs"</span>:[],<span class="hljs-string">"name"</span>:<span class="hljs-string">"decimals"</span>,<span class="hljs-string">"outputs"</span>:[{<span class="hljs-string">"internalType"</span>:<span class="hljs-string">"uint8"</span>,<span class="hljs-string">"name"</span>:<span class="hljs-string">""</span>,<span class="hljs-string">"type"</span>:<span class="hljs-string">"uint8"</span>}],<span class="hljs-string">"stateMutability"</span>:<span class="hljs-string">"view"</span>,<span class="hljs-string">"type"</span>:<span class="hljs-string">"function"</span>},{<span class="hljs-string">"inputs"</span>:[],<span class="hljs-string">"name"</span>:<span class="hljs-string">"description"</span>,<span class="hljs-string">"outputs"</span>:[{<span class="hljs-string">"internalType"</span>:<span class="hljs-string">"string"</span>,<span class="hljs-string">"name"</span>:<span class="hljs-string">""</span>,<span class="hljs-string">"type"</span>:<span class="hljs-string">"string"</span>}],<span class="hljs-string">"stateMutability"</span>:<span class="hljs-string">"view"</span>,<span class="hljs-string">"type"</span>:<span class="hljs-string">"function"</span>},{<span class="hljs-string">"inputs"</span>:[{<span class="hljs-string">"internalType"</span>:<span class="hljs-string">"uint80"</span>,<span class="hljs-string">"name"</span>:<span class="hljs-string">"_roundId"</span>,<span class="hljs-string">"type"</span>:<span class="hljs-string">"uint80"</span>}],<span class="hljs-string">"name"</span>:<span class="hljs-string">"getRoundData"</span>,<span class="hljs-string">"outputs"</span>:[{<span class="hljs-string">"internalType"</span>:<span class="hljs-string">"uint80"</span>,<span class="hljs-string">"name"</span>:<span class="hljs-string">"roundId"</span>,<span class="hljs-string">"type"</span>:<span class="hljs-string">"uint80"</span>},{<span class="hljs-string">"internalType"</span>:<span class="hljs-string">"int256"</span>,<span class="hljs-string">"name"</span>:<span class="hljs-string">"answer"</span>,<span class="hljs-string">"type"</span>:<span class="hljs-string">"int256"</span>},{<span class="hljs-string">"internalType"</span>:<span class="hljs-string">"uint256"</span>,<span class="hljs-string">"name"</span>:<span class="hljs-string">"startedAt"</span>,<span class="hljs-string">"type"</span>:<span class="hljs-string">"uint256"</span>},{<span class="hljs-string">"internalType"</span>:<span class="hljs-string">"uint256"</span>,<span class="hljs-string">"name"</span>:<span class="hljs-string">"updatedAt"</span>,<span class="hljs-string">"type"</span>:<span class="hljs-string">"uint256"</span>},{<span class="hljs-string">"internalType"</span>:<span class="hljs-string">"uint80"</span>,<span class="hljs-string">"name"</span>:<span class="hljs-string">"answeredInRound"</span>,<span class="hljs-string">"type"</span>:<span class="hljs-string">"uint80"</span>}],<span class="hljs-string">"stateMutability"</span>:<span class="hljs-string">"view"</span>,<span class="hljs-string">"type"</span>:<span class="hljs-string">"function"</span>},{<span class="hljs-string">"inputs"</span>:[],<span class="hljs-string">"name"</span>:<span class="hljs-string">"latestRoundData"</span>,<span class="hljs-string">"outputs"</span>:[{<span class="hljs-string">"internalType"</span>:<span class="hljs-string">"uint80"</span>,<span class="hljs-string">"name"</span>:<span class="hljs-string">"roundId"</span>,<span class="hljs-string">"type"</span>:<span class="hljs-string">"uint80"</span>},{<span class="hljs-string">"internalType"</span>:<span class="hljs-string">"int256"</span>,<span class="hljs-string">"name"</span>:<span class="hljs-string">"answer"</span>,<span class="hljs-string">"type"</span>:<span class="hljs-string">"int256"</span>},{<span class="hljs-string">"internalType"</span>:<span class="hljs-string">"uint256"</span>,<span class="hljs-string">"name"</span>:<span class="hljs-string">"startedAt"</span>,<span class="hljs-string">"type"</span>:<span class="hljs-string">"uint256"</span>},{<span class="hljs-string">"internalType"</span>:<span class="hljs-string">"uint256"</span>,<span class="hljs-string">"name"</span>:<span class="hljs-string">"updatedAt"</span>,<span class="hljs-string">"type"</span>:<span class="hljs-string">"uint256"</span>},{<span class="hljs-string">"internalType"</span>:<span class="hljs-string">"uint80"</span>,<span class="hljs-string">"name"</span>:<span class="hljs-string">"answeredInRound"</span>,<span class="hljs-string">"type"</span>:<span class="hljs-string">"uint80"</span>}],<span class="hljs-string">"stateMutability"</span>:<span class="hljs-string">"view"</span>,<span class="hljs-string">"type"</span>:<span class="hljs-string">"function"</span>},{<span class="hljs-string">"inputs"</span>:[],<span class="hljs-string">"name"</span>:<span class="hljs-string">"version"</span>,<span class="hljs-string">"outputs"</span>:[{<span class="hljs-string">"internalType"</span>:<span class="hljs-string">"uint256"</span>,<span class="hljs-string">"name"</span>:<span class="hljs-string">""</span>,<span class="hljs-string">"type"</span>:<span class="hljs-string">"uint256"</span>}],<span class="hljs-string">"stateMutability"</span>:<span class="hljs-string">"view"</span>,<span class="hljs-string">"type"</span>:<span class="hljs-string">"function"</span>}]' 
addr = '0x9326BFA02ADD2366b30bacB125260Af641031331' 
contract = web3.eth.contract(address=addr, abi=abi) 
latestData = contract.functions.latestRoundData().call() print(latestData)
</code></pre><p>运行上述操作后将在我们的控制台中打印以美元为单位的ETH的最新价格。请查看Chainlink文档以确定是否有问题。</p><p><strong>结论</strong></p><p>您可以从他们的文档中了解有关Web3.py和Brown的更多信息。这两个项目都是开源的，任何人都可以做出贡献！</p><p><code>https://github.com/eth-brownie/brownie</code></p><p><code>https://github.com/ethereum/web3.py</code></p>]]></content:encoded>
            <author>quantbang@newsletter.paragraph.com (quantbang)</author>
        </item>
        <item>
            <title><![CDATA[用 Python 开发 DeFi 去中心化借贷应用（下）]]></title>
            <link>https://paragraph.com/@quantbang/python-defi-4</link>
            <guid>dmRle0sk09h8lYVM5R74</guid>
            <pubDate>Thu, 24 Mar 2022 13:29:53 GMT</pubDate>
            <description><![CDATA[传统的金融科技世界充满了使用户能够制作复杂算法交易模型和系统的工具。而去中心化金融 (DeFi) 也同样为用户和开发人员提供了相同的工具，围绕底层金融协议和工具具有更高的透明度和灵活性，从而催生了 DeFi 量化交易和应用开发。DeFi 开发人员和 DeFi quants 甚至可以利用这些工具的衍生品，并将它们组合成新的服务，以建立在传统金融科技世界中没有的创新金融头寸。DeFi 开发人员的基本工具之一是能够以非托管方式借出和借入加密货币资产。阅读本文前请先查看：\ 用 Python 开发 DeFi 去中心化借贷应用（上） 用 Python 开发 DeFi 去中心化借贷应用（中） 获取可借资产价值 现在，当我们借款时，我们会被告知我们拥有的以 ETH 表示的资产数量。然而，也许我们想借用一个不同的代币，比如 DAI。如果我们要以 ETH 为抵押借入 DAI，我们首先要得到 ETH -> DAI 的兑换率。 Chainlink 是 DeFi 和智能合约生态系统中获取数据和执行外部计算的标准，也是 Aave 用来获取费率的标准！他们有一个价格 Oracle 部分，描述了如何通过他们的...]]></description>
            <content:encoded><![CDATA[<figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/58a8a1dcf46b0604a65911945b2f7cebc935c1973bf5199d608e03cf23f74863.jpg" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>传统的金融科技世界充满了使用户能够制作复杂算法交易模型和系统的工具。而去中心化金融 (DeFi) 也同样为用户和开发人员提供了相同的工具，围绕底层金融协议和工具具有更高的透明度和灵活性，从而催生了 DeFi 量化交易和应用开发。DeFi 开发人员和 DeFi quants 甚至可以利用这些工具的衍生品，并将它们组合成新的服务，以建立在传统金融科技世界中没有的创新金融头寸。DeFi 开发人员的基本工具之一是能够以非托管方式借出和借入加密货币资产。阅读本文前请先查看：\</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://mp.weixin.qq.com/s?__biz=MzAxMjUyNDQ5OA%3D%3D&amp;chksm=806e7382b719fa94b728a56f3034317036624771ca2ce60db4715b808cfcf3c1af5b21e7aa35&amp;idx=1&amp;mid=2653576511&amp;scene=21&amp;sn=f801e6fd57da1609a8a4f361d8fbc6cb#wechat_redirect">用 Python 开发 DeFi 去中心化借贷应用（上）</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://mp.weixin.qq.com/s?__biz=MzAxMjUyNDQ5OA%3D%3D&amp;chksm=806e7325b719fa337d5e3a56fe324d6bcb24281d8e06871f6643ad3611d3a869aa332fce352b&amp;idx=1&amp;mid=2653576600&amp;scene=21&amp;sn=773dff58760c033db1426a9db08af622#wechat_redirect">用 Python 开发 DeFi 去中心化借贷应用（中）</a></p><p><strong>获取可借资产价值</strong></p><p>现在，当我们借款时，我们会被告知我们拥有的以 ETH 表示的资产数量。然而，也许我们想借用一个不同的代币，比如 DAI。如果我们要以 ETH 为抵押借入 DAI，我们首先要得到 ETH -&gt; DAI 的兑换率。</p><p>Chainlink 是 DeFi 和智能合约生态系统中获取数据和执行外部计算的标准，也是 Aave 用来获取费率的标准！他们有一个价格 Oracle 部分，描述了如何通过他们的智能合约 API 获取 Chainlink 数据。如果你学会了一般地使用 Chainlink 数据，你可以跨协议使用它，即使他们没有使用 Chainlink，或者没有价格预言机机制！</p><p>Chainlink 通过去中心化的预言机网络获取数据，其中多个节点将跨多个 API 和服务的数据聚合到一个去中心化的来源，该来源经过验证并记录在链上供我们使用。我们可以在 data.chain.link 页面或文档上看到当前可用数据提要的列表。如您所知，数据是大多数量化系统、算法交易者和整个 DeFi 生态系统的支柱，因此您要始终确保自己是从去中心化系统中提取的，这样您就可以获得最准确、最可靠和最安全的数据。</p><p>此外，如果您不想要某些数据，您可以从任何 API 调用您的智能合约，获得可证明的随机数以及许多其他不同的服务。这些使得智能合约数据安全、可靠和去中心化。</p><p>但是让我们回到 Python！</p><p>我们有一个名为 <code>get_asset_price</code> 的函数，它从 <code>dai_eth_price_feed</code> 中读取数据，如下所示：</p><pre data-type="codeBlock" text="def get_asset_price():
   # For mainnet we can just do:
   # return Contract(f&quot;{pair}.data.eth&quot;).latestAnswer() / 1e8
   dai_eth_price_feed = interface.AggregatorV3Interface(
       config[&quot;networks&quot;][network.show_active()][&quot;dai_eth_price_feed&quot;]
   )
   latest_price = Web3.fromWei(dai_eth_price_feed.latestRoundData()[1], &quot;ether&quot;)
   print(f&quot;The DAI/ETH price is {latest_price}&quot;)
   return float(latest_price)
"><code>def get_asset_price():
   # For mainnet we can just do:
   # <span class="hljs-keyword">return</span> Contract(f<span class="hljs-string">"{pair}.data.eth"</span>).latestAnswer() <span class="hljs-operator">/</span> <span class="hljs-number">1e8</span>
   dai_eth_price_feed <span class="hljs-operator">=</span> <span class="hljs-keyword">interface</span>.AggregatorV3Interface(
       config[<span class="hljs-string">"networks"</span>][network.show_active()][<span class="hljs-string">"dai_eth_price_feed"</span>]
   )
   latest_price <span class="hljs-operator">=</span> Web3.fromWei(dai_eth_price_feed.latestRoundData()[<span class="hljs-number">1</span>], <span class="hljs-string">"ether"</span>)
   print(f<span class="hljs-string">"The DAI/ETH price is {latest_price}"</span>)
   <span class="hljs-keyword">return</span> float(latest_price)
</code></pre><p>我们从接口中提取的 Chainlink 合约 <code>AggregatorV3Interface</code> 已存储在我们的配置文件中。在该合约中，我们调用 <code>latestRoundData</code> 函数。</p><p>你会注意到我们也不需要为这个做一个交易，这也是一个视图函数！</p><p>你会看到通过<code>brownie</code>获得 Chainlink 数据价格的另一种方式是：</p><pre data-type="codeBlock" text="from brownie import Contract
print(Contract(f&quot;{pair}.data.eth&quot;).latestAnswer() / 1e8)
"><code><span class="hljs-keyword">from</span> brownie <span class="hljs-keyword">import</span> Contract
<span class="hljs-built_in">print</span>(Contract(<span class="hljs-string">f"<span class="hljs-subst">{pair}</span>.data.eth"</span>).latestAnswer() / <span class="hljs-number">1e8</span>)
</code></pre><p>这是使用 ENS 来获取价格对的地址。ENS 是一种获取智能合约地址并使其可读的方法。例如，您可以执行以下操作：</p><pre data-type="codeBlock" text="print(Contract(f&quot;eth-usd.data.eth&quot;).latestAnswer() / 1e8)
"><code>print(Contract(f<span class="hljs-string">"eth-usd.data.eth"</span>).latestAnswer() <span class="hljs-operator">/</span> <span class="hljs-number">1e8</span>)
</code></pre><p>以美元为单位获取以太坊的最新价格。请注意，ENS 仅适用于主网网络，因此您在运行 <code>brownie</code> 时必须使用 <code>--network mainnet</code> 或 <code>--network mainnet-fork</code>。</p><p><strong>将价值转换为借用</strong></p><p>现在我们有了抵押品和我们想要借入的资产之间的转换率，我们可以设置一个变量来确定我们想要借入多少。</p><pre data-type="codeBlock" text="amount_erc20_to_borrow = (1 / erc20_eth_price) * (borrowable_eth * 0.95)
"><code>amount_erc20_to_borrow <span class="hljs-operator">=</span> (<span class="hljs-number">1</span> <span class="hljs-operator">/</span> erc20_eth_price) <span class="hljs-operator">*</span> (borrowable_eth <span class="hljs-operator">*</span> <span class="hljs-number">0</span><span class="hljs-number">.95</span>)
</code></pre><p>我们得到 ETH/DAI 价格的倒数，并将其乘以我们可以借入的金额（以 ETH 为单位）。为了安全起见，我们还将其乘以 0.95。然后，我们调用借用函数！</p><pre data-type="codeBlock" text="borrow_erc20(lending_pool, amount_erc20_to_borrow, account)
"><code><span class="hljs-built_in">borrow_erc20</span>(lending_pool, amount_erc20_to_borrow, account)
</code></pre><pre data-type="codeBlock" text="def borrow_erc20(lending_pool, amount, account, erc20_address=None):
   erc20_address = (
       erc20_address
       if erc20_address
       else config[&quot;networks&quot;][network.show_active()][&quot;aave_dai_token&quot;]
   )
   # 1 is stable interest rate
   # 0 is the referral code
   transaction = lending_pool.borrow(
       erc20_address,
       Web3.toWei(amount, &quot;ether&quot;),
       1,
       0,
       account.address,
       {&quot;from&quot;: account},
   )
   transaction.wait(1)
   print(f&quot;Congratulations! We have just borrowed {amount}&quot;)
"><code>def borrow_erc20(lending_pool, amount, account, erc20_address<span class="hljs-operator">=</span>None):
   erc20_address <span class="hljs-operator">=</span> (
       erc20_address
       <span class="hljs-keyword">if</span> erc20_address
       <span class="hljs-keyword">else</span> config[<span class="hljs-string">"networks"</span>][network.show_active()][<span class="hljs-string">"aave_dai_token"</span>]
   )
   # <span class="hljs-number">1</span> <span class="hljs-keyword">is</span> stable interest rate
   # <span class="hljs-number">0</span> <span class="hljs-keyword">is</span> the referral code
   transaction <span class="hljs-operator">=</span> lending_pool.borrow(
       erc20_address,
       Web3.toWei(amount, <span class="hljs-string">"ether"</span>),
       <span class="hljs-number">1</span>,
       <span class="hljs-number">0</span>,
       account.<span class="hljs-built_in">address</span>,
       {<span class="hljs-string">"from"</span>: account},
   )
   transaction.wait(<span class="hljs-number">1</span>)
   print(f<span class="hljs-string">"Congratulations! We have just borrowed {amount}"</span>)
</code></pre><p>借用函数需要几个参数：</p><p>•资产的地址（在我们的例子中是 DAI 代币）•我们要借的金额，单位为 wei•<code>interestRateMode</code>，（0, 1, 2: 1 是稳定的，2 是可变的……现在不用担心 0）•推荐码（忽略）•<code>onBehalfOf</code> 值，即谁将承担债务</p><p>让我们看一下 Etherscan 上这笔交易的样本，以了解刚刚发生的事情。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/265cc75d3207e0159737f8ed24f27e58567a63130ffaba33b8b67a697fdec53e.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>按顺序，代币转移的方向是：</p><p>•Aave 协议正在铸造一些 aDAI•我们的钱包正在铸造一些 <code>Aave Stable Debt Bearing DAI</code>•我们的钱包获得了一些 DAI</p><p>重要说明：Aave 有时会更改其测试网token。通过检查已部署的合约地址，确保您拥有正确的 testnet DAI 地址。并检查官方 json 列表。</p><p>这一次，我们得到了一种不同类型的计息代币，一种债务代币。这是我们必须偿还的金额，并且与 <code>aToken</code> 一样，它的价值也会增长。如果你欠更多的债务（用这个债务代币表示），有人可以清算你并拿走你所有的抵押品！所以要警惕你有多少债务。</p><p>通过添加新的代币地址，我们可以再次在我们的 MetaNask 中看到借入的资产。我们还可以通过添加债务token地址来查看我们的债务。</p><p>这才是真正有趣的地方。现在最简单的卖空方法之一就是出售我们的资产。如果资产价格下跌，我们将不得不偿还比借入的更少的钱。</p><p>然而，我越来越担心我会被清算。让我们偿还我们的债务。</p><p><strong>偿还我们的债务</strong></p><p>我们想弄清楚我们欠了多少，然后偿还！我们可以用这些函数做到这一点：</p><pre data-type="codeBlock" text="borrowable_eth, total_debt_eth = get_borrowable_data(lending_pool, account)
amount_erc20_to_repay = (1 / erc20_eth_price) * (total_debt_eth * 0.95)
repay_all(amount_erc20_to_repay, lending_pool, account)
"><code>borrowable_eth, total_debt_eth = <span class="hljs-built_in">get_borrowable_data</span>(lending_pool, account)
amount_erc20_to_repay = (<span class="hljs-number">1</span> / erc20_eth_price) * (total_debt_eth * <span class="hljs-number">0.95</span>)
<span class="hljs-built_in">repay_all</span>(amount_erc20_to_repay, lending_pool, account)
</code></pre><pre data-type="codeBlock" text="def repay_all(amount, lending_pool, account):
   approve_erc20(
       Web3.toWei(amount, &quot;ether&quot;),
       lending_pool,
       config[&quot;networks&quot;][network.show_active()][&quot;aave_dai_token&quot;],
       account,
   )
   tx = lending_pool.repay(
       config[&quot;networks&quot;][network.show_active()][&quot;aave_dai_token&quot;],
       Web3.toWei(amount, &quot;ether&quot;),
       1,
       account.address,
       {&quot;from&quot;: account},
   )
   tx.wait(1)
   print(&quot;Repaid!&quot;)
"><code>def repay_all(amount, lending_pool, account):
   approve_erc20(
       Web3.toWei(amount, <span class="hljs-string">"ether"</span>),
       lending_pool,
       config[<span class="hljs-string">"networks"</span>][network.show_active()][<span class="hljs-string">"aave_dai_token"</span>],
       account,
   )
   <span class="hljs-built_in">tx</span> <span class="hljs-operator">=</span> lending_pool.repay(
       config[<span class="hljs-string">"networks"</span>][network.show_active()][<span class="hljs-string">"aave_dai_token"</span>],
       Web3.toWei(amount, <span class="hljs-string">"ether"</span>),
       <span class="hljs-number">1</span>,
       account.<span class="hljs-built_in">address</span>,
       {<span class="hljs-string">"from"</span>: account},
   )
   <span class="hljs-built_in">tx</span>.wait(<span class="hljs-number">1</span>)
   print(<span class="hljs-string">"Repaid!"</span>)
</code></pre><p><strong>总结</strong></p><p>我们已经完成了与 Aave 合作的所有步骤。</p><p>•抵押•拿出贷款•偿还贷款</p><p>您接下来还可以做一些事情，比如：</p><p>•使用 Synthetix 或 Bancor 等协议交易资产•申请闪贷•了解如何质押和参与治理</p><p>您还可以学习如何成为智能合约开发人员，在 Solidity 中构建去中心化协议，并发现智能合约的广泛功能。你的旅程才刚刚开始！</p>]]></content:encoded>
            <author>quantbang@newsletter.paragraph.com (quantbang)</author>
        </item>
        <item>
            <title><![CDATA[用 Python 开发 DeFi 去中心化借贷应用（中）]]></title>
            <link>https://paragraph.com/@quantbang/python-defi-3</link>
            <guid>Xj6Ctbkesdt4leojLvOw</guid>
            <pubDate>Thu, 24 Mar 2022 13:28:13 GMT</pubDate>
            <description><![CDATA[传统的金融科技世界充满了使用户能够制作复杂算法交易模型和系统的工具。而去中心化金融 (DeFi) 也同样为用户和开发人员提供了相同的工具，围绕底层金融协议和工具具有更高的透明度和灵活性，从而催生了 DeFi 量化交易和应用开发。DeFi 开发人员和 DeFi quants 甚至可以利用这些工具的衍生品，并将它们组合成新的服务，以建立在传统金融科技世界中没有的创新金融头寸。DeFi 开发人员的基本工具之一是能够以非托管方式借出和借入加密货币资产。阅读本文前请先查看： 《用 Python 开发 DeFi 去中心化借贷应用（上）》 存入抵押品 接下来，我们运行 aave_borrow.py 脚本，它做了 4 件事： •将抵押品存入 Aave 贷款池•获得了我们的抵押品和另一种资产之间的对话率•使用该抵押品借入不同的资产（贷款）•还清了贷款 我们看一下main函数的开头：def main(): account = get_account() erc20_address = config["networks"][network.show_active()]["weth_token"] if...]]></description>
            <content:encoded><![CDATA[<figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/6878520b4315482ce3c120e90d4d0d5121242c74a1e959e8196ab638e9294d94.jpg" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>传统的金融科技世界充满了使用户能够制作复杂算法交易模型和系统的工具。而去中心化金融 (DeFi) 也同样为用户和开发人员提供了相同的工具，围绕底层金融协议和工具具有更高的透明度和灵活性，从而催生了 DeFi 量化交易和应用开发。DeFi 开发人员和 DeFi quants 甚至可以利用这些工具的衍生品，并将它们组合成新的服务，以建立在传统金融科技世界中没有的创新金融头寸。DeFi 开发人员的基本工具之一是能够以非托管方式借出和借入加密货币资产。阅读本文前请先查看：</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://mp.weixin.qq.com/s?__biz=MzAxMjUyNDQ5OA%3D%3D&amp;chksm=806e7382b719fa94b728a56f3034317036624771ca2ce60db4715b808cfcf3c1af5b21e7aa35&amp;idx=1&amp;mid=2653576511&amp;scene=21&amp;sn=f801e6fd57da1609a8a4f361d8fbc6cb#wechat_redirect">《用 Python 开发 DeFi 去中心化借贷应用（上）》</a></p><p><strong>存入抵押品</strong></p><p>接下来，我们运行 <code>aave_borrow.py</code> 脚本，它做了 4 件事：</p><p>•将抵押品存入 Aave 贷款池•获得了我们的抵押品和另一种资产之间的对话率•使用该抵押品借入不同的资产（贷款）•还清了贷款</p><p>我们看一下<code>main</code>函数的开头：</p><pre data-type="codeBlock" text="def main():
   account = get_account()
   erc20_address = config[&quot;networks&quot;][network.show_active()][&quot;weth_token&quot;]
   if network.show_active() in [&quot;mainnet-fork&quot;]:
       get_weth(account=account)
   lending_pool = get_lending_pool()
"><code>def main():
   <span class="hljs-attr">account</span> = get_account()
   <span class="hljs-attr">erc20_address</span> = config[<span class="hljs-string">"networks"</span>][network.show_active()][<span class="hljs-string">"weth_token"</span>]
   if network.show_active() in <span class="hljs-section">["mainnet-fork"]</span>:
       get_weth(<span class="hljs-attr">account</span>=account)
   <span class="hljs-attr">lending_pool</span> = get_lending_pool()
</code></pre><p>我们首先从配置文件中获取我们的地址（如果我们在测试网上，它会再次从我们的配置中提取）。如果您在本地链上进行测试，我们只使用它生成的第一个帐户。这在 <code>get_account</code> 函数中定义。然后，我们也从配置文件中获取 WETH 令牌的地址，如果我们在本地链上，我们调用之前调用的相同 <code>get_weth</code> 函数。这是为了确保如果我们使用本地链，我们的钱包中有 WETH。</p><p>最后，我们得到了<code>lending_pool</code>的地址。 <code>lending_pool</code>是管理借贷功能的链上智能合约，因此得名。您可以在 Aave 文档中查看功能，或者您可以直接在链上查看合约。</p><p>我们在这个合约中看到了许多有用的功能：</p><p>•借贷•抵押•获取用户帐户数据•偿还</p><p>这些是我们将要使用的函数。请记住，链上智能合约与传统软件工程中的对象或类相同，每个都有与之相关的功能。如果您要<code>print(type(lending_pool))</code>，我们会得到这种类型：</p><pre data-type="codeBlock" text="&lt;class &apos;brownie.network.contract.Contract&apos;&gt;
"><code><span class="hljs-operator">&#x3C;</span>class <span class="hljs-string">'brownie.network.contract.Contract'</span><span class="hljs-operator">></span>
</code></pre><p>这意味着借贷池是一个合约，就像 WETH 一样！我们的 <code>get_lending_pool()</code> 函数实际上做了一些额外的步骤来获取贷款池合约。让我们看看这个函数：</p><pre data-type="codeBlock" text="def get_lending_pool():
   lending_pool_addresses_provider = interface.ILendingPoolAddressesProvider(
       config[&quot;networks&quot;][network.show_active(
       )][&quot;lending_poll_addresses_provider&quot;]
   )
   lending_pool_address = lending_pool_addresses_provider.getLendingPool()
   lending_pool = interface.ILendingPool(lending_pool_address)
   return lending_pool
"><code>def get_lending_pool():
   lending_pool_addresses_provider <span class="hljs-operator">=</span> <span class="hljs-keyword">interface</span>.ILendingPoolAddressesProvider(
       config[<span class="hljs-string">"networks"</span>][network.show_active(
       )][<span class="hljs-string">"lending_poll_addresses_provider"</span>]
   )
   lending_pool_address <span class="hljs-operator">=</span> lending_pool_addresses_provider.getLendingPool()
   lending_pool <span class="hljs-operator">=</span> <span class="hljs-keyword">interface</span>.ILendingPool(lending_pool_address)
   <span class="hljs-keyword">return</span> lending_pool
</code></pre><p>我们需要两个东西才能与智能合约进行交互：</p><p>•ABI/接口•地址</p><p>我们有位于<code>interfaces</code>文件夹中的借贷池的接口。你会注意到一个流行的接口约定是让它们以字母“I”开头。我们在这个例子中有一个mix，表示它不必以“I”开头。</p><p>为了让我们获得借贷池的地址，我们实际上必须通过 <code>LendingPoolAddressesProvider</code> 的地址。这是一个链上合约，其中包含 Aave 借贷池的所有地址。有时，出借池地址发生变化，但出借池提供者地址永远不会改变，因此我们始终可以使用该合约的地址来获取出借池地址。你会看到我们在<code>lenting_pool_addresses_provider</code> 变量上调用了 <code>getLendingPool</code> 函数。</p><p>让我们回到主函数。在我们得到了贷款池合约的地址后，我们要存入抵押品。这是我们需要做的第一步，因为只有放置了抵押品，我们才能获得贷款。我们也可以假设只存入抵押品！存入抵押品将使我们获得：</p><p>1、收益</p><p>该协议向将抵押品存入平台的用户支付报酬。</p><p>2、取出贷款</p><p>我们只有在有足够的抵押品的情况下才能贷款/借入。Aave 有一个名为 <code>Liquidationcall</code> 的函数，如果您没有足够的抵押品，任何人都可以调用该函数并获得奖励。抵押品的价值必须保持大于贷款的价值，这就是为什么这被称为超额抵押贷款。</p><p>3、流动性挖矿</p><p>也称为“收益耕作”，流动性挖矿是一种 DeFi 机制，它使用户能够以治理或其他类型代币的形式获得奖励，以向协议提供流动性。但是，为了让我们存入一些代币，我们必须批准智能合约才能将代币从我们的钱包中取出。我们将把我们的 WETH 作为抵押品。</p><pre data-type="codeBlock" text="approve_erc20(amount, lending_pool.address, erc20_address, account)
"><code>approve_erc20(amount, lending_pool.<span class="hljs-built_in">address</span>, erc20_address, account)
</code></pre><p>让我们看看我们的<code>approve_erc20</code>函数。</p><pre data-type="codeBlock" text="def approve_erc20(amount, lending_pool_address, erc20_address, account):
   print(&quot;Approving ERC20...&quot;)
   erc20 = interface.IERC20(erc20_address)
   tx_hash = erc20.approve(lending_pool_address, amount, {&quot;from&quot;: account})
   tx_hash.wait(1)
   print(&quot;Approved!&quot;)
   return True
"><code>def approve_erc20(amount, lending_pool_address, erc20_address, account):
   print(<span class="hljs-string">"Approving ERC20..."</span>)
   erc20 <span class="hljs-operator">=</span> <span class="hljs-keyword">interface</span>.IERC20(erc20_address)
   tx_hash <span class="hljs-operator">=</span> erc20.approve(lending_pool_address, amount, {<span class="hljs-string">"from"</span>: account})
   tx_hash.wait(<span class="hljs-number">1</span>)
   print(<span class="hljs-string">"Approved!"</span>)
   <span class="hljs-keyword">return</span> True
</code></pre><p>•<code>amount</code>：允许协议使用多少 ERC20，以 wei 为单位。 <code>wei</code> 是区块链世界中最小的计量单位。1 ETH == 1000000000000000000wei•<code>lending_pool_address</code>：借贷池地址。•<code>erc20_address</code>：ERC20 代币的地址。•<code>account</code>：我们想要交易的账户。</p><p>在使用 ERC20 代币时，如果我们希望从我们的钱包中“调用”另一个合约，我们必须首先批准该合约。我们调用 ERC20 合约（我们的 WETH）上的批准函数，然后我们“等待”该交易继续进行。</p><pre data-type="codeBlock" text="tx_hash.wait(1)
"><code>tx_hash.wait(<span class="hljs-number">1</span>)
</code></pre><p>现在我们已经批准了合同，我们可以将我们的抵押品存入我们的主函数中的 <code>Lotion_pool</code> 合同中。</p><pre data-type="codeBlock" text="print(&quot;Depositing...&quot;)
   lending_pool.deposit(erc20_address, amount, account.address, 0, {&quot;from&quot;: account})
   print(&quot;Deposited!&quot;)
"><code>print(<span class="hljs-string">"Depositing..."</span>)
   lending_pool.deposit(erc20_address, amount, account.<span class="hljs-built_in">address</span>, <span class="hljs-number">0</span>, {<span class="hljs-string">"from"</span>: account})
   print(<span class="hljs-string">"Deposited!"</span>)
</code></pre><p>我们可以在这里看到，我们在合约上调用了<code>deposit</code>函数，我们给了它：</p><p>•<code>erc20_address</code>：要存入的代币（在我们的例子中是 WETH）•<code>amount</code>：要存入的 wei 金额。•<code>account.address</code>：我们想为谁贷款，在这种情况下是我们自己。•<code>0</code>：这是推荐代码（已折旧，始终使用 0）</p><p>我们可以从 Aave 文档中了解更多关于智能合约中的存款功能。</p><p>如果你正确地完成了这部分，你会得到一个交易哈希，它会告诉你发生了什么。让我们看一个关于 Etherscan 上 Kovan 测试网的例子。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/ceb15e77f958d4a14ec0efa680929a8c2815de89ec1b35c247de39e7251a354e.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>让我们深入了解发生了什么。如果我们查看 <code>Tokens Transferred</code> 部分，我们可以看到 1 笔存款交易发生了 3 笔交易。按顺序，这些是：</p><p>•一些 <code>aToken</code> 被铸造出来供我们存入 (aWETH)•我们发送了 1 个 WETH 到 lending_pool 合约（这是我们的存款）•我们收到了一些 <code>aToken</code></p><p>所以中间代币转移是有道理的——我们正在向借贷池发送一些 WETH。但是其他<code>token</code>是什么？</p><p><code>aToken</code> 是一种计息代币，在存款时铸造并在赎回时销毁。所以当你取回你的存款时，你烧掉你的 <code>aToken</code> 以获得等量的基础资产。 <code>aTokens</code> 最酷的部分是它们的价值每时每刻都在上涨！余额增加是因为人们使用 Aave 协议并借用您的资产，因此您因此获得报酬！</p><p>您可以查看某人的余额并每隔几秒钟刷新一次，以实时查看余额的增加情况。</p><p>每当我们想要赎回或提取我们的 WETH 时，无论我们拥有多少 <code>aWETH</code>，我们都会提取，而这些 <code>aWETH</code> 将被烧毁。</p><p>与 WETH token相同，您现在可以在您的 MetaMask 钱包中看到 aWETH。只需进入您的 MetaMask，并执行我们上面所做的添加的token部分，Kovan WETH 地址为 <code>0x87b1f4cf9bd63f7bbd3ee1ad04e8f52540349347</code>。</p><p><strong>抵押/贷款</strong></p><p>现在我们已经存入了一些抵押品，我们可以贷款了。现在我们为什么要贷款？</p><p>•无摩擦卖空•无需平仓即可获得流动性</p><p>您可以借入资产以保持对标的抵押品的敞口，同时还可以用另一种资产进行交易或支付。只要确保将您的健康系数保持在 1 以上。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/1cdab3e45dd76c6e2103c6f6c96f2541b10044b363615945a2b153faba7c9b38.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/265cc75d3207e0159737f8ed24f27e58567a63130ffaba33b8b67a697fdec53e.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>您的健康因素是您贷款的抵押品数量。假设我存入了 1 ETH，借了 0.5 ETH。如果清算阈值为 1，那么我的健康系数将为 2。</p><p>清算门槛是贷款被定义为抵押不足的百分比。例如，清算门槛为 80% 意味着如果价值超过抵押品的 80%，则贷款抵押不足，可以清算。</p><p>但是假设我有 2 ETH 借入和 1 ETH 作为抵押品，1 作为清算门槛。我的健康系数是 0.5，我会被要求清算。这是清算人代表借款人偿还部分或全部未偿还借入金额的情况，同时获得折扣金额的抵押品作为回报（也称为清算“奖金”）。清算人可以决定他们是否想要获得等值的金额 抵押aToken，或者直接标的资产，当清算成功完成后，增加仓位的健康系数，使健康系数在1以上。通过这种方式，来保持贷款健康！</p><p>以我们的抵押品借款，我们首先要衡量我们作为抵押品的抵押品数据。我们可以调用贷款池上的 <code>getUserAccountData</code> 函数来找出这一点。</p><p>我们将它封装到一个名为 <code>get_borrowable_data</code> 的函数中</p><pre data-type="codeBlock" text="borrowable_eth, total_debt_eth = get_borrowable_data(lending_pool, account)
"><code>borrowable_eth, <span class="hljs-attr">total_debt_eth</span> = get_borrowable_data(lending_pool, account)
</code></pre><pre data-type="codeBlock" text="def get_borrowable_data(lending_pool, account):
   (
       total_collateral_eth,
       total_debt_eth,
       available_borrow_eth,
       current_liquidation_threshold,
       tlv,
       health_factor,
   ) = lending_pool.getUserAccountData(account.address)
   available_borrow_eth = Web3.fromWei(available_borrow_eth, &quot;ether&quot;)
   total_collateral_eth = Web3.fromWei(total_collateral_eth, &quot;ether&quot;)
   total_debt_eth = Web3.fromWei(total_debt_eth, &quot;ether&quot;)
   print(f&quot;You have {total_collateral_eth} worth of ETH deposited.&quot;)
   print(f&quot;You have {total_debt_eth} worth of ETH borrowed.&quot;)
   print(f&quot;You can borrow {available_borrow_eth} worth of ETH.&quot;)
   return (float(available_borrow_eth), float(total_debt_eth))
"><code>def get_borrowable_data(lending_pool, account):
   (
       total_collateral_eth,
       total_debt_eth,
       available_borrow_eth,
       current_liquidation_threshold,
       tlv,
       health_factor,
   ) <span class="hljs-operator">=</span> lending_pool.getUserAccountData(account.<span class="hljs-built_in">address</span>)
   available_borrow_eth <span class="hljs-operator">=</span> Web3.fromWei(available_borrow_eth, <span class="hljs-string">"ether"</span>)
   total_collateral_eth <span class="hljs-operator">=</span> Web3.fromWei(total_collateral_eth, <span class="hljs-string">"ether"</span>)
   total_debt_eth <span class="hljs-operator">=</span> Web3.fromWei(total_debt_eth, <span class="hljs-string">"ether"</span>)
   print(f<span class="hljs-string">"You have {total_collateral_eth} worth of ETH deposited."</span>)
   print(f<span class="hljs-string">"You have {total_debt_eth} worth of ETH borrowed."</span>)
   print(f<span class="hljs-string">"You can borrow {available_borrow_eth} worth of ETH."</span>)
   <span class="hljs-keyword">return</span> (float(available_borrow_eth), float(total_debt_eth))
</code></pre><p><code>getUserAccountData</code> 返回：</p><p>•<code>totalCollateralETH</code>•<code>totalDebtETH</code>•<code>availableBorrowsETH</code>•<code>currentLiquidationThreshold</code>•<code>Ltv</code>•<code>healthFactor</code></p><p>您可以在 Aave 文档中阅读这些函数。我们只关注 <code>available_borrow_eth</code> 和 <code>total_debt_eth</code>，这样我们就会知道：</p><p>•我们可以借多少钱•我们目前有多少债务</p><p><strong>为什么我还没有进行交易？</strong></p><p>您可能已经注意到，这是对智能合约进行函数调用，而我们上面刚刚说过，为了调用函数，我们必须进行交易，那么我们给出了什么呢？其实有两种类型的智能合约功能，您无需进行交易即可调用。</p><p>•视图函数•纯函数</p><p>这些函数不会修改区块链的状态，因此我们可以调用它们，因为我们只是在读取智能合约的状态。请记住，如果您修改区块链的状态，它只会花费 gas。</p><p>回到贷款。每项资产都有不同的贷款借入比率，它告诉您的清算门槛，以便您借入资产。</p><p>一旦我们得出了可借贷款量，我们就可以借了！</p><pre data-type="codeBlock" text="borrowable_eth, total_debt_eth = get_borrowable_data(lending_pool, account)
print(f&quot;LETS BORROW IT ALL&quot;)
erc20_eth_price = get_asset_price()
amount_erc20_to_borrow = (1 / erc20_eth_price) * (borrowable_eth * 0.95)
print(f&quot;We are going to borrow {amount_erc20_to_borrow} DAI&quot;)
borrow_erc20(lending_pool, amount_erc20_to_borrow, account)
"><code>borrowable_eth, total_debt_eth = <span class="hljs-built_in">get_borrowable_data</span>(lending_pool, account)
<span class="hljs-built_in">print</span>(f"LETS BORROW IT ALL")
erc20_eth_price = <span class="hljs-built_in">get_asset_price</span>()
amount_erc20_to_borrow = (<span class="hljs-number">1</span> / erc20_eth_price) * (borrowable_eth * <span class="hljs-number">0.95</span>)
<span class="hljs-built_in">print</span>(f"We are going to borrow {amount_erc20_to_borrow} DAI")
<span class="hljs-built_in">borrow_erc20</span>(lending_pool, amount_erc20_to_borrow, account)
</code></pre>]]></content:encoded>
            <author>quantbang@newsletter.paragraph.com (quantbang)</author>
        </item>
        <item>
            <title><![CDATA[用 Python 开发 DeFi 去中心化应用（下）]]></title>
            <link>https://paragraph.com/@quantbang/python-defi-2</link>
            <guid>5fu7WmqRVArL9UfXjvmc</guid>
            <pubDate>Thu, 24 Mar 2022 13:15:13 GMT</pubDate>
            <description><![CDATA[在本教程中，我们将介绍如何使用 Python 开发 DeFi 项目。去中心化金融 (DeFi) 是区块链和智能合约世界最重要的进步之一，通常被称为“新金融科技”。 读取您的智能合约 现在我们已经部署了一个智能合约，我们可以从刚刚部署的合约中读取 ETH 的价格。我们将使用我们拥有的另一个脚本：brownie run scripts/price_feed_scripts/read_price_feed.py --network kovan 我们将得到如下输出：Brownie v1.12.2- Python development framework forEthereum ChainlinkProjectis the active project. Running'scripts/price_feed_scripts/read_price_feed.py::main'... Reading data from0x5A…. 122322000000 其中 122322000000 是 ETH 的当前美元价格！Solidity 不理解小数，我们知道 Chainlink Price Fe...]]></description>
            <content:encoded><![CDATA[<figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/49c2bffd237beb7ddcec7d31603abcc8f8425b5c3a76ab3f2c6cb8ab092c8a2a.jpg" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>在本教程中，我们将介绍如何使用 Python 开发 DeFi 项目。去中心化金融 (DeFi) 是区块链和智能合约世界最重要的进步之一，通常被称为“新金融科技”。</p><p><strong>读取您的智能合约</strong></p><p>现在我们已经部署了一个智能合约，我们可以从刚刚部署的合约中读取 ETH 的价格。我们将使用我们拥有的另一个脚本：</p><pre data-type="codeBlock" text="brownie run scripts/price_feed_scripts/read_price_feed.py --network kovan
"><code>brownie run scripts<span class="hljs-operator">/</span>price_feed_scripts<span class="hljs-operator">/</span>read_price_feed.py <span class="hljs-operator">-</span><span class="hljs-operator">-</span>network kovan
</code></pre><p>我们将得到如下输出：</p><pre data-type="codeBlock" text="Brownie v1.12.2- Python development framework forEthereum
ChainlinkProjectis the active project.
Running&apos;scripts/price_feed_scripts/read_price_feed.py::main&apos;...
Reading data from0x5A….
122322000000
"><code>Brownie v1<span class="hljs-number">.12</span><span class="hljs-number">.2</span><span class="hljs-operator">-</span> Python development framework forEthereum
ChainlinkProjectis the active project.
Running'scripts<span class="hljs-operator">/</span>price_feed_scripts<span class="hljs-operator">/</span>read_price_feed.py::main<span class="hljs-string">'...
Reading data from0x5A….
122322000000
</span></code></pre><p>其中 122322000000 是 ETH 的当前美元价格！Solidity 不理解小数，我们知道 Chainlink Price Feeds 有 8 位小数，所以价格是 $1,223.22 。</p><p>您刚刚使用 Python 和 Brownie 部署了您的第一个智能合约！</p><p><strong>测试你的智能合约</strong></p><p>这也是如何测试智能合约的一个很好的例子。我们甚至使用模拟对象在本地进行测试！</p><p>只需运行：</p><pre data-type="codeBlock" text="brownie test
"><code>brownie <span class="hljs-built_in">test</span>
</code></pre><p>您的测试将在本地 Ganache 实例上运行！</p><p>您还可以使用以下内容在测试网上进行测试：</p><pre data-type="codeBlock" text="brownie test --network kovan
"><code>brownie test <span class="hljs-operator">-</span><span class="hljs-operator">-</span>network kovan
</code></pre><p>这些测试具有一些功能，可以知道您是否在测试网络上工作。如果您在本地工作，它会部署 虚拟或“模拟”的预言机代码，以便我们可以对其进行测试。</p><p><strong>总结</strong></p><p>现在您知道如何使用 Python 部署智能合约，您可以开始在此框架之上构建以做更多有趣的事情。Python 拥有强大的软件包，如 Numpy、Scikit、Pandas 和 TensorFlow，可用于执行定量工作、机器学习等。能够最终将这些技术结合在一起是金融科技新时代成功的秘诀：去中心化金融。</p><p>Chainlink 是一个灵活的框架，用于将外部金融数据和系统引入链上，并与 Numpy 和 Pandas 等以数据为中心的软件包无缝集成。如果您是开发人员并希望快速将您的应用程序连接到 Chainlink，请访问开发人员文档。</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.chain.link/?_ga=2.263617100.1196435661.1623840137-1423897284.1623840137">https://docs.chain.link/?_ga=2.263617100.1196435661.1623840137-1423897284.1623840137</a></p>]]></content:encoded>
            <author>quantbang@newsletter.paragraph.com (quantbang)</author>
        </item>
        <item>
            <title><![CDATA[用 Python 开发 DeFi 去中心化应用（上）]]></title>
            <link>https://paragraph.com/@quantbang/python-defi</link>
            <guid>jPLKyhrySDf1buqPoN6F</guid>
            <pubDate>Thu, 24 Mar 2022 13:14:14 GMT</pubDate>
            <description><![CDATA[在本教程中，我们将介绍如何使用 Python 开发 DeFi 项目。去中心化金融 (DeFi) 是区块链和智能合约世界最重要的进步之一，通常被称为“新金融科技”。目前，智能合约开发由 JavaScript 主导，部分原因可能是 JavaScript 是地球上最常用的语言，并且与 Node.js 配合构建全栈应用程序更容易。然而，量化分析师、股票交易员和对冲基金的金融科技世界并非如此。大多数金融科技公司出于各种原因使用 Python：更高效的开发体验强大的人工智能和机器学习捆绑的金融科技包综合分析工具生产环境中的可靠性如此多的数据科学家、学者和金融科技机构使用 Python 是有原因的。DeFi 领域的一些项目，如yearn.finance，分享了这种观点，并使用 Python 和 Solidity 构建了他们的整个 DeFi 平台。正是由于像 web3.py 和 Brownie 这样的库和框架，我们才能看到这些项目变得生动起来。Brownie 是一个类似于 Truffle 的框架（它们都非常“sweet”），它维护您的部署、脚本、测试，并允许您制作端到端的后台。 Web3.py和...]]></description>
            <content:encoded><![CDATA[<figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/6134b35abf506d5a278fa033078cc1a246d77812d4cd36ba1ad82225d5c4a916.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>在本教程中，我们将介绍如何使用 Python 开发 DeFi 项目。去中心化金融 (DeFi) 是区块链和智能合约世界最重要的进步之一，通常被称为“新金融科技”。目前，智能合约开发由 JavaScript 主导，部分原因可能是 JavaScript 是地球上最常用的语言，并且与 Node.js 配合构建全栈应用程序更容易。然而，量化分析师、股票交易员和对冲基金的金融科技世界并非如此。大多数金融科技公司出于各种原因使用 Python：</p><ul><li><p>更高效的开发体验</p></li><li><p>强大的人工智能和机器学习</p></li><li><p>捆绑的金融科技包</p></li><li><p>综合分析工具</p></li><li><p>生产环境中的可靠性</p></li></ul><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/05eb883ee7c4451cae8f1818833e55f6c3c38314b09e3c3c759c20b26fa42a47.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>如此多的数据科学家、学者和金融科技机构使用 Python 是有原因的。DeFi 领域的一些项目，如yearn.finance，分享了这种观点，并使用 Python 和 Solidity 构建了他们的整个 DeFi 平台。正是由于像 web3.py 和 Brownie 这样的库和框架，我们才能看到这些项目变得生动起来。Brownie 是一个类似于 Truffle 的框架（它们都非常“sweet”），它维护您的部署、脚本、测试，并允许您制作端到端的后台。</p><p>Web3.py和Brownie还提供了“mix”，你可以在其中使用一些样板代码预打开项目。这就是我们今天将使用的Chainlink Brownie mix。</p><p>现在我们为什么要使用 Chainlink + Python？正如 Python 是金融科技事实上的编程语言一样，Chainlink 是将链下数据提供给 DeFi 智能合约的事实上的预言机解决方案，目前为顶级 DeFi 协议提供了超过 7B 美元的价值。这两种技术的结合为去中心化金融科技领域提供了一个安全而强大的框架。</p><p>以下是 Chainlink 在 2020 年启用的一些功能的快速浏览。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/23c1ba9fb87bb6164b6cfc8ca16db29c748de98eb776ea790065290a4e41a03d.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>要开始使用 Brownie 和 Python 构建 DeFi 应用程序，您首先需要安装 Python。此时不建议 Python 低于 3.4 版本，因此如果您的版本低于 3.4，请升级。您可以通过运行以下命令查看您的 Python 版本并验证它是否已正确安装：</p><pre data-type="codeBlock" text="python --version
"><code>python <span class="hljs-operator">-</span><span class="hljs-operator">-</span>version
</code></pre><p>或者，如果使用 python3：</p><pre data-type="codeBlock" text="python3 --version
"><code>python3 <span class="hljs-operator">-</span><span class="hljs-operator">-</span>version
</code></pre><p>您还需要安装 Ganache。Ganache 是一个用 Python 编写的一键式区块链应用，可让您轻松启动本地区块链。尽管您必须使用 npm 和 node.js 下载它，但这将是您必须与之交互的唯一 JavaScript 片段。</p><p>首先，您需要安装 node.js 和 npm。Node.js 随 npm 一起安装。下载后，您可以通过运行以下命令来检查它是否正确完成：</p><pre data-type="codeBlock" text="npm -v
"><code>npm <span class="hljs-operator">-</span>v
</code></pre><p>然后，您可以通过命令行安装 Ganache。</p><pre data-type="codeBlock" text="npm install -g ganache-cli
"><code>npm install <span class="hljs-operator">-</span>g ganache<span class="hljs-operator">-</span>cli
</code></pre><p>安装完成后，我们将像安装所有 Python 存储库一样使用 pip 安装 eth-brownie！</p><pre data-type="codeBlock" text="pip install eth-brownie
"><code>pip install eth<span class="hljs-operator">-</span>brownie
</code></pre><p>或者如果使用 pip3：</p><pre data-type="codeBlock" text="pip3 install eth-brownie
"><code>pip3 install eth<span class="hljs-operator">-</span>brownie
</code></pre><p>如果您在终端中运行 brownie，您就会知道您做对了，您会得到类似于以下内容的输出：</p><pre data-type="codeBlock" text="Brownie v1.13.0- Python development framework forEthereum
Usage:  brownie &amp;lt;command&gt; [&amp;lt;args&gt;...] [options &amp;lt;args&gt;]
Commands:
  init               Initialize a new brownie project
  bake               Initializefrom a brownie-mix template
  pm                 Installand manage external packages
  compile            Compile the contract source files
  console            Load the console
  test               Run test cases in the tests/ folder
  run                Run a script in the scripts/ folder
  accounts           Managelocal accounts
  networks           Manage network settings
  gui                Load the GUI to view opcodes and test coverage
  analyze            Find security vulnerabilities using the MythX API
Options:
--help -h          Displaythis message
--version          Show version andexit
Type&apos;brownie &amp;lt;command&gt; --help&apos;for specific options and more information about
each command.
"><code>Brownie v1<span class="hljs-number">.13</span><span class="hljs-number">.0</span><span class="hljs-operator">-</span> Python development framework forEthereum
Usage:  brownie <span class="hljs-operator">&#x26;</span>lt;command<span class="hljs-operator">></span> [<span class="hljs-operator">&#x26;</span>lt;args<span class="hljs-operator">></span>...] [options <span class="hljs-operator">&#x26;</span>lt;args<span class="hljs-operator">></span>]
Commands:
  init               Initialize a <span class="hljs-keyword">new</span> brownie project
  bake               Initializefrom a brownie<span class="hljs-operator">-</span>mix template
  pm                 Installand manage <span class="hljs-keyword">external</span> packages
  compile            Compile the <span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">source</span> <span class="hljs-title">files</span>
  <span class="hljs-title">console</span>            <span class="hljs-title">Load</span> <span class="hljs-title">the</span> <span class="hljs-title">console</span>
  <span class="hljs-title">test</span>               <span class="hljs-title">Run</span> <span class="hljs-title">test</span> <span class="hljs-title">cases</span> <span class="hljs-title">in</span> <span class="hljs-title">the</span> <span class="hljs-title">tests</span>/ <span class="hljs-title">folder</span>
  <span class="hljs-title">run</span>                <span class="hljs-title">Run</span> <span class="hljs-title">a</span> <span class="hljs-title">script</span> <span class="hljs-title">in</span> <span class="hljs-title">the</span> <span class="hljs-title">scripts</span>/ <span class="hljs-title">folder</span>
  <span class="hljs-title">accounts</span>           <span class="hljs-title">Managelocal</span> <span class="hljs-title">accounts</span>
  <span class="hljs-title">networks</span>           <span class="hljs-title">Manage</span> <span class="hljs-title">network</span> <span class="hljs-title">settings</span>
  <span class="hljs-title">gui</span>                <span class="hljs-title">Load</span> <span class="hljs-title">the</span> <span class="hljs-title">GUI</span> <span class="hljs-title">to</span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title">opcodes</span> <span class="hljs-title">and</span> <span class="hljs-title">test</span> <span class="hljs-title">coverage</span>
  <span class="hljs-title">analyze</span>            <span class="hljs-title">Find</span> <span class="hljs-title">security</span> <span class="hljs-title">vulnerabilities</span> <span class="hljs-title"><span class="hljs-keyword">using</span></span> <span class="hljs-title">the</span> <span class="hljs-title">MythX</span> <span class="hljs-title">API</span>
<span class="hljs-title">Options</span>:
--<span class="hljs-title">help</span> -<span class="hljs-title">h</span>          <span class="hljs-title">Displaythis</span> <span class="hljs-title">message</span>
--<span class="hljs-title">version</span>          <span class="hljs-title">Show</span> <span class="hljs-title">version</span> <span class="hljs-title">andexit</span>
<span class="hljs-title">Type</span>'<span class="hljs-title">brownie</span> &#x26;<span class="hljs-title">lt</span>;<span class="hljs-title">command</span>> --<span class="hljs-title">help</span>'<span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">specific</span> <span class="hljs-title">options</span> <span class="hljs-title">and</span> <span class="hljs-title">more</span> <span class="hljs-title">information</span> <span class="hljs-title">about</span>
<span class="hljs-title">each</span> <span class="hljs-title">command</span>.
</span></code></pre><p>此外，您还需要一个 MetaMask 或其他以太坊钱包。请使用 Kovan测试网获得ETH。请确保您的 ETH 钱包中有一些 testnet LINK 和 Kovan ETH。您可以在 LINK faucet 和 ETH Kovan faucet 中找到一些。</p><p><strong>启动 Chainlink 项目</strong></p><p>要开始使用Brownie,，我们可以使用所谓的Brownie mix 来为我们提供样板代码。在这个例子中，我们将部署一个简单的 Chainlink Price Feed 作为了解 Brownie 框架的一种方式。让我们bake chainlink-mix。</p><pre data-type="codeBlock" text="brownie bake chainlink-mix
cd chainlink
"><code>brownie bake chainlink-mix
<span class="hljs-built_in">cd</span> chainlink
</code></pre><p>这将使我们进入一个新项目，其中已经为我们构建了一些默认代码。如果我们运行 ls 我们可以看到文件的布局是什么样的：</p><ul><li><p><code>build</code> ：这是项目跟踪您部署的智能合约和编译的合约的地方</p></li><li><p><code>contracts</code>：合同的源代码，通常用 Solidity 或 Vyper 编写</p></li><li><p><code>interfaces</code> ：您需要使用已部署合同的接口布局。与合约的每次交互都需要一个 ABI 和一个地址。接口是获取合约 ABI 的好方法</p></li><li><p><code>scripts</code>：我们创建的脚本来自动化处理我们的合同的过程 测试</p></li><li><p><code>tests</code>：测试</p></li><li><p><code>brownie-config.yaml</code> ：这是我们为 Brownie 了解如何使用我们的智能合约提供所有信息的地方。我们要部署到哪个区块链？有没有我们想要设置的特殊参数？所有这些都在配置文件中设置。</p></li></ul><p>现在可以忽略 <code>requirements.txt</code> 、 <code>README.md</code> 、 <code>LICENSE</code> 和 <code>.gitignore</code>。当您练习时，您会发现它们的用途。</p><p><strong>设置环境变量</strong></p><p>尽管我们只是安装了 Ganache 来进行本地测试，但我们也希望能够连接到 ETH 主网和测试网，以便我们可以将它们部署在真实的测试网上。为此，我们需要设置 <code>WEB3_INFURA_PROJECT_ID</code>。您可以从 Infura 站点免费获得一个 Infura ID。您也可以使用其他 web3 提供程序或您自己的节点，但您必须为此做更多的配置。</p><p>在您的 <code>brownie-config.yaml</code> 文件中，您可以使用主机密钥设置网络，并定义您不想使用 MetaMask 时想要连接的 URL。</p><p>现在您有了 web3 ID，我们需要将我们的私钥作为环境变量，以便我们可以将我们的帐户与我们的钱包一起使用。如果您使用 MetaMask，请查找导出密钥。使用 MetaMask，您可能需要在私钥的开头添加 <code>0x</code>。建议在测试和导出密钥时使用与主帐户不同的帐户，以防万一。</p><p>现在，要使它们成为环境变量，只需在终端中运行以下命令：</p><pre data-type="codeBlock" text="export PRIVATE_KEY=0x96789…..
export WEB3_INFURA_PROJECT_ID=’dog cat mouse….’
"><code>export PRIVATE_KEY<span class="hljs-operator">=</span><span class="hljs-number">0x96789</span>…..
export WEB3_INFURA_PROJECT_ID<span class="hljs-operator">=</span>’dog cat mouse….’
</code></pre><p>如果你运行 <code>printenv</code> 并在那里看到你的环境变量，你就会知道你做对了。</p><p><strong>部署您的智能合约</strong></p><p>现在我们已经设置好了一切，我们甚至可以继续将智能合约部署到 Kovan 测试网！</p><p>在我们的脚本文件夹中，我们有一个名为 <code>deploy_price_consumer_v3.py</code>的脚本。这将部署我们的智能合约，以美元读取以太坊的价格。</p><p>只需使用 brownie run 即可使用部署脚本：</p><pre data-type="codeBlock" text="brownie run scripts/price_feed_scripts/deploy_price_consumer_v3.py --network kovan
"><code>brownie run scripts<span class="hljs-operator">/</span>price_feed_scripts<span class="hljs-operator">/</span>deploy_price_consumer_v3.py <span class="hljs-operator">-</span><span class="hljs-operator">-</span>network kovan
</code></pre><p>你会看到这样的东西：</p><pre data-type="codeBlock" text="Running&apos;scripts/price_feed_scripts/deploy_price_consumer_v3.py::main&apos;...
Transaction sent: 0x23d1dfa3937e0cfbab58f8d5ecabe2bfffc28bbe2349527dabe9289e747bac56
Gas price: 20.0 gwei   Gas limit: 145600Nonce: 1339
PriceFeed.constructor confirmed - Block: 22721813Gas used: 132364(90.91%)
PriceFeed deployed at: 0x6B2305935DbC77662811ff817cF3Aa54fc585816
"><code>Running<span class="hljs-string">'scripts/price_feed_scripts/deploy_price_consumer_v3.py::main'</span>...
Transaction sent: <span class="hljs-number">0x23d1dfa3937e0cfbab58f8d5ecabe2bfffc28bbe2349527dabe9289e747bac56</span>
Gas price: <span class="hljs-number">20.0</span> <span class="hljs-literal">gwei</span>   Gas limit: 145600Nonce: <span class="hljs-number">1339</span>
PriceFeed.constructor confirmed <span class="hljs-operator">-</span> Block: 22721813Gas used: <span class="hljs-number">132364</span>(<span class="hljs-number">90.91</span><span class="hljs-operator">%</span>)
PriceFeed deployed at: <span class="hljs-number">0x6B2305935DbC77662811ff817cF3Aa54fc585816</span>
</code></pre><p>如果这工作正常，我们可以去 Kovan Etherscan 并找到我们部署的合约。上面的链接显示了此示例中部署的合约。</p>]]></content:encoded>
            <author>quantbang@newsletter.paragraph.com (quantbang)</author>
        </item>
        <item>
            <title><![CDATA[用 Python 制作 NFT 区块链作品（下）]]></title>
            <link>https://paragraph.com/@quantbang/python-nft-2</link>
            <guid>BdIcqnBvkOsshqzLuOie</guid>
            <pubDate>Thu, 24 Mar 2022 13:12:49 GMT</pubDate>
            <description><![CDATA[在本文中，我们将学习如何使用 Brownie、Python 和 Chainlink 来制作非同质化的 NFT 作品，并在 OpenSea NFT 市场上展示和销售我们的成果。 动态和高级 NFT 动态 NFT 是可以随时间变化的 NFT，或者具有我们可以用来相互交互的链上功能。这些 NFT 可以无限定制，让我们可以制作整个游戏、元宇宙（metaverse）或某种互动艺术。下面我们进入高级部分。 高级快速入门 确保您的metamask中有足够的测试网 ETH 和 LINK，然后运行以下命令：brownie run scripts/advanced_collectible/deploy_advanced.py --network rinkeby brownie run scripts/advanced_collectible/create_collectible.py --network rinkeby 我们的收藏品是从 Chainlink VRF 返回的随机犬种。Chainlink VRF 是一种获得可证明随机数的方法，因此我们的 NFT 真正稀缺。然后我们想要创建它的元数据。br...]]></description>
            <content:encoded><![CDATA[<figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/eb9796ada62204690b081f5cb9be1b4c959505a746d8c006c368fdf18131763c.jpg" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>在本文中，我们将学习如何使用 Brownie、Python 和 Chainlink 来制作非同质化的 NFT 作品，并在 OpenSea NFT 市场上展示和销售我们的成果。</p><p><strong>动态和高级 NFT</strong></p><p>动态 NFT 是可以随时间变化的 NFT，或者具有我们可以用来相互交互的链上功能。这些 NFT 可以无限定制，让我们可以制作整个游戏、元宇宙（metaverse）或某种互动艺术。下面我们进入高级部分。</p><p><strong>高级快速入门</strong></p><p>确保您的metamask中有足够的测试网 ETH 和 LINK，然后运行以下命令：</p><pre data-type="codeBlock" text="brownie run scripts/advanced_collectible/deploy_advanced.py --network rinkeby
brownie run scripts/advanced_collectible/create_collectible.py --network rinkeby
"><code>brownie run scripts<span class="hljs-operator">/</span>advanced_collectible<span class="hljs-operator">/</span>deploy_advanced.py <span class="hljs-operator">-</span><span class="hljs-operator">-</span>network rinkeby
brownie run scripts<span class="hljs-operator">/</span>advanced_collectible<span class="hljs-operator">/</span>create_collectible.py <span class="hljs-operator">-</span><span class="hljs-operator">-</span>network rinkeby
</code></pre><p>我们的收藏品是从 Chainlink VRF 返回的随机犬种。Chainlink VRF 是一种获得可证明随机数的方法，因此我们的 NFT 真正稀缺。然后我们想要创建它的元数据。</p><pre data-type="codeBlock" text="brownie run scripts/advanced_collectible/create_metadata.py --network rinkeby
"><code>brownie run scripts<span class="hljs-operator">/</span>advanced_collectible<span class="hljs-operator">/</span>create_metadata.py <span class="hljs-operator">-</span><span class="hljs-operator">-</span>network rinkeby
</code></pre><p>然后我们可以选择将此数据上传到 IPFS，以便我们可以拥有一个 <code>tokenURI</code>。稍后我会告诉你如何做到这一点。现在，我们将仅使用以下示例 <code>tokenURI</code>：</p><pre data-type="codeBlock" text="https://ipfs.io/ipfs/Qmd9MCGtdVz2miNumBHDbvj8bigSgTwnr4SbyH6DNnpWdt?filename=1-PUG.json
"><code>https://ipfs.io/ipfs/Qmd9MCGtdVz2miNumBHDbvj8bigSgTwnr4SbyH6DNnpWdt?<span class="hljs-attr">filename</span>=<span class="hljs-number">1</span>-PUG.json
</code></pre><p>如果您将 IPFS Companion 下载到您的浏览器中，您可以使用该 URL 来查看 URI 返回的内容。它看起来像这样：</p><pre data-type="codeBlock" text="{
    &quot;name&quot;: &quot;PUG&quot;,
    &quot;description&quot;: &quot;An adorable PUG pup!&quot;,
    &quot;image&quot;: &quot;https://ipfs.io/ipfs/QmSsYRx3LpDAb1GZQm7zZ1AuHZjfbPkD6J7s9r41xu1mf8?filename=pug.png&quot;,
    &quot;attributes&quot;: [
        {
            &quot;trait_type&quot;: &quot;cuteness&quot;,
            &quot;value&quot;: 100
        }
    ]
}
"><code><span class="hljs-punctuation">{</span>
    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"PUG"</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"description"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"An adorable PUG pup!"</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"image"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"https://ipfs.io/ipfs/QmSsYRx3LpDAb1GZQm7zZ1AuHZjfbPkD6J7s9r41xu1mf8?filename=pug.png"</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"attributes"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span>
        <span class="hljs-punctuation">{</span>
            <span class="hljs-attr">"trait_type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"cuteness"</span><span class="hljs-punctuation">,</span>
            <span class="hljs-attr">"value"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">100</span>
        <span class="hljs-punctuation">}</span>
    <span class="hljs-punctuation">]</span>
<span class="hljs-punctuation">}</span>
</code></pre><p>然后我们可以运行我们的 <code>set_tokenuri.py</code> 脚本：</p><pre data-type="codeBlock" text="brownie run scripts/advanced_collectible/set_tokenuri.py --network rinkeby
"><code>brownie run scripts<span class="hljs-operator">/</span>advanced_collectible<span class="hljs-operator">/</span>set_tokenuri.py <span class="hljs-operator">-</span><span class="hljs-operator">-</span>network rinkeby
</code></pre><p>我们会得到这样的输出：</p><pre data-type="codeBlock" text="Running &apos;scripts/advanced_collectible/set_tokenuri.py::main&apos;...
Working on rinkeby
Transaction sent: 0x8a83a446c306d6255952880c0ca35fa420248a84ba7484c3798d8bbad421f88e
  Gas price: 1.0 gwei   Gas limit: 44601   Nonce: 354
  AdvancedCollectible.setTokenURI confirmed - Block: 8331653   Gas used: 40547 (90.91%)
Awesome! You can view your NFT at https://testnets.opensea.io/assets/0x679c5f9adC630663a6e63Fa27153B215fe021b34/0
Please give up to 20 minutes, and hit the &quot;refresh metadata&quot; button
"><code>Running <span class="hljs-string">'scripts/advanced_collectible/set_tokenuri.py::main'</span>...
Working on rinkeby
Transaction sent: <span class="hljs-number">0x8a83a446c306d6255952880c0ca35fa420248a84ba7484c3798d8bbad421f88e</span>
  Gas price: <span class="hljs-number">1.0</span> <span class="hljs-literal">gwei</span>   Gas limit: <span class="hljs-number">44601</span>   Nonce: <span class="hljs-number">354</span>
  AdvancedCollectible.setTokenURI confirmed <span class="hljs-operator">-</span> Block: <span class="hljs-number">8331653</span>   Gas used: <span class="hljs-number">40547</span> (<span class="hljs-number">90.91</span><span class="hljs-operator">%</span>)
Awesome<span class="hljs-operator">!</span> You can <span class="hljs-keyword">view</span> your NFT at https:<span class="hljs-comment">//testnets.opensea.io/assets/0x679c5f9adC630663a6e63Fa27153B215fe021b34/0</span>
Please give up to <span class="hljs-number">20</span> <span class="hljs-literal">minutes</span>, and hit the <span class="hljs-string">"refresh metadata"</span> button
</code></pre><p>我们可以点击给出的链接，看看它在 Opensea 上的样子！您可能需要点击刷新元数据按钮并等待几分钟。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/77be9b2f699a79c6e6697c79857908191cf5a5bef0a28178d668a1a9ef82145d.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><strong>随机品种</strong></p><p>然我们看一下刚刚做了什么。这是我们的 <code>AdvancedCollectible.sol</code>：</p><pre data-type="codeBlock" text="pragma solidity 0.6.6;
import &quot;@openzeppelin/contracts/token/ERC721/ERC721.sol&quot;;
import &quot;@chainlink/contracts/src/v0.6/VRFConsumerBase.sol&quot;;
contract AdvancedCollectible is ERC721, VRFConsumerBase {
    uint256 public tokenCounter;
    enum Breed{PUG, SHIBA_INU, BRENARD}
    // add other things
    mapping(bytes32 =&gt; address) public requestIdToSender;
    mapping(bytes32 =&gt; string) public requestIdToTokenURI;
    mapping(uint256 =&gt; Breed) public tokenIdToBreed;
    mapping(bytes32 =&gt; uint256) public requestIdToTokenId;
    event requestedCollectible(bytes32 indexed requestId); 
    bytes32 internal keyHash;
    uint256 internal fee;
    uint256 public randomResult;
    constructor(address _VRFCoordinator, address _LinkToken, bytes32 _keyhash)
    public 
    VRFConsumerBase(_VRFCoordinator, _LinkToken)
    ERC721(&quot;Dogie&quot;, &quot;DOG&quot;)
    {
        tokenCounter = 0;
        keyHash = _keyhash;
        fee = 0.1 * 10 ** 18;
    }
    function createCollectible(string memory tokenURI, uint256 userProvidedSeed) 
        public returns (bytes32){
            bytes32 requestId = requestRandomness(keyHash, fee, userProvidedSeed);
            requestIdToSender[requestId] = msg.sender;
            requestIdToTokenURI[requestId] = tokenURI;
            emit requestedCollectible(requestId);
    }
    function fulfillRandomness(bytes32 requestId, uint256 randomNumber) internal override {
        address dogOwner = requestIdToSender[requestId];
        string memory tokenURI = requestIdToTokenURI[requestId];
        uint256 newItemId = tokenCounter;
        _safeMint(dogOwner, newItemId);
        _setTokenURI(newItemId, tokenURI);
        Breed breed = Breed(randomNumber % 3); 
        tokenIdToBreed[newItemId] = breed;
        requestIdToTokenId[requestId] = newItemId;
        tokenCounter = tokenCounter + 1;
    }
    function setTokenURI(uint256 tokenId, string memory _tokenURI) public {
        require(
            _isApprovedOrOwner(_msgSender(), tokenId),
            &quot;ERC721: transfer caller is not owner nor approved&quot;
        );
        _setTokenURI(tokenId, _tokenURI);
    }
}
"><code><span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> 0.6.6;</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC721/ERC721.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@chainlink/contracts/src/v0.6/VRFConsumerBase.sol"</span>;
<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">AdvancedCollectible</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ERC721</span>, <span class="hljs-title">VRFConsumerBase</span> </span>{
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> tokenCounter;
    <span class="hljs-keyword">enum</span> <span class="hljs-title">Breed</span>{PUG, SHIBA_INU, BRENARD}
    <span class="hljs-comment">// add other things</span>
    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">bytes32</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">address</span>) <span class="hljs-keyword">public</span> requestIdToSender;
    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">bytes32</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">string</span>) <span class="hljs-keyword">public</span> requestIdToTokenURI;
    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">uint256</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> Breed) <span class="hljs-keyword">public</span> tokenIdToBreed;
    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">bytes32</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">uint256</span>) <span class="hljs-keyword">public</span> requestIdToTokenId;
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">requestedCollectible</span>(<span class="hljs-params"><span class="hljs-keyword">bytes32</span> <span class="hljs-keyword">indexed</span> requestId</span>)</span>; 
    <span class="hljs-keyword">bytes32</span> <span class="hljs-keyword">internal</span> keyHash;
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">internal</span> fee;
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> randomResult;
    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _VRFCoordinator, <span class="hljs-keyword">address</span> _LinkToken, <span class="hljs-keyword">bytes32</span> _keyhash</span>)
    <span class="hljs-title"><span class="hljs-keyword">public</span></span> 
    <span class="hljs-title">VRFConsumerBase</span>(<span class="hljs-params">_VRFCoordinator, _LinkToken</span>)
    <span class="hljs-title">ERC721</span>(<span class="hljs-params"><span class="hljs-string">"Dogie"</span>, <span class="hljs-string">"DOG"</span></span>)
    </span>{
        tokenCounter <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;
        keyHash <span class="hljs-operator">=</span> _keyhash;
        fee <span class="hljs-operator">=</span> <span class="hljs-number">0</span><span class="hljs-number">.1</span> <span class="hljs-operator">*</span> <span class="hljs-number">10</span> <span class="hljs-operator">*</span><span class="hljs-operator">*</span> <span class="hljs-number">18</span>;
    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createCollectible</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> tokenURI, <span class="hljs-keyword">uint256</span> userProvidedSeed</span>) 
        <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bytes32</span></span>)</span>{
            <span class="hljs-keyword">bytes32</span> requestId <span class="hljs-operator">=</span> requestRandomness(keyHash, fee, userProvidedSeed);
            requestIdToSender[requestId] <span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>;
            requestIdToTokenURI[requestId] <span class="hljs-operator">=</span> tokenURI;
            <span class="hljs-keyword">emit</span> requestedCollectible(requestId);
    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fulfillRandomness</span>(<span class="hljs-params"><span class="hljs-keyword">bytes32</span> requestId, <span class="hljs-keyword">uint256</span> randomNumber</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> </span>{
        <span class="hljs-keyword">address</span> dogOwner <span class="hljs-operator">=</span> requestIdToSender[requestId];
        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> tokenURI <span class="hljs-operator">=</span> requestIdToTokenURI[requestId];
        <span class="hljs-keyword">uint256</span> newItemId <span class="hljs-operator">=</span> tokenCounter;
        _safeMint(dogOwner, newItemId);
        _setTokenURI(newItemId, tokenURI);
        Breed breed <span class="hljs-operator">=</span> Breed(randomNumber <span class="hljs-operator">%</span> <span class="hljs-number">3</span>); 
        tokenIdToBreed[newItemId] <span class="hljs-operator">=</span> breed;
        requestIdToTokenId[requestId] <span class="hljs-operator">=</span> newItemId;
        tokenCounter <span class="hljs-operator">=</span> tokenCounter <span class="hljs-operator">+</span> <span class="hljs-number">1</span>;
    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setTokenURI</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> tokenId, <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> _tokenURI</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-built_in">require</span>(
            _isApprovedOrOwner(_msgSender(), tokenId),
            <span class="hljs-string">"ERC721: transfer caller is not owner nor approved"</span>
        );
        _setTokenURI(tokenId, _tokenURI);
    }
}
</code></pre><p>我们使用 Chainlink VRF 从 PUG、SHIBA_INU、BRENARD 列表中创建一个随机品种。当我们这次调用 <code>createCollectible</code> 时，我们实际上向链下的 Chainlink VRF 节点发起了一个请求，并返回一个随机数，以使用这 3 个品种之一创建 NFT。</p><p>在你的 NFT 中使用真正的随机性是创造真正稀缺性的好方法，使用 Chainlink oracle 随机数意味着你的数字可以证明是随机的，并且不会受到矿工的影响。</p><p>您可以在文档中了解有关 Chainlink VRF 的更多信息。</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.chain.link/docs/chainlink-vrf/">https://docs.chain.link/docs/chainlink-vrf/</a></p><p>Chainlink 节点通过调用 <code>fulfillRandomness</code> 函数进行响应，并根据随机数创建收藏品。然后我们仍然需要调用 <code>_setTokenURI</code> 来为我们的 NFT 提供它需要的外观。</p><p>我们没有在这里给出我们的 NFT 属性，但属性是让我们的 NFT 进行交互的好方法。您可以在此 <code>龙与地下城</code>示例中看到具有属性的 NFT 的一个很好的示例。</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/PatrickAlphaC/dungeons-and-dragons-nft">https://github.com/PatrickAlphaC/dungeons-and-dragons-nft</a></p><p><strong>来自 IPFS 的元数据</strong></p><p>我们使用 IPFS 来存储两个文件：</p><ul><li><p>NFT 的形象（哈巴狗形象）</p></li><li><p><code>tokenURI</code> 文件（JSON 文件，其中还包含图像的链接）</p></li></ul><p>我们使用 IPFS 是因为它是一个免费的去中心化平台。我们可以通过下载 IPFS 桌面并点击导入按钮将我们的 tokenURI 和图像添加到 IPFS。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/2ea30a486da345fc0079e0c55d637892e1205d7bc27a0521baa3bc7ee7d8d1ff.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>然后，我们可以通过点击要共享的文件旁边的 3 个点、点击共享链接并复制给定的链接来共享 URI。然后我们可以将此链接添加到我们的 <code>set_tokenuri.py</code> 文件中以更改我们想要使用的 <code>tokenURI</code>。</p><p><strong>持久性</strong></p><p>但是，如果 tokenURI 仅在我们的节点上，这意味着当我们的节点关闭时，没有其他人可以查看它。所以我们希望其他人 <code>pin</code>我们的 NFT。我们可以使用 <code>Pinata</code> 之类的 <code>pin</code>服务来帮助我们的数据保持活动状态，即使我们的 IPFS 节点已关闭。</p><p>我想未来会有越来越多的元数据存储在 IPFS 和去中心化存储平台上。集中式服务器可能会宕机，这意味着这些 NFT 上的艺术将永远丢失。请务必检查您使用的 NFT 的 tokenURI 所在的位置！</p><p>我也希望更多的人会使用像 Filecoin 这样的 dStorage 平台，因为使用 <code>pin</code>服务也没有像它应该的那样去中心化。</p><p>现在，您已经具备了制作漂亮有趣、可定制、交互式 NFT 的技能，并让它们在市场上呈现。</p><p>NFT 是一种有趣、强大的方式，可以补偿艺术家们所做的辛勤工作。</p>]]></content:encoded>
            <author>quantbang@newsletter.paragraph.com (quantbang)</author>
        </item>
        <item>
            <title><![CDATA[用 Python 制作 NFT 区块链作品（上）]]></title>
            <link>https://paragraph.com/@quantbang/python-nft</link>
            <guid>voh2nXKVb18lIBtO6GVk</guid>
            <pubDate>Thu, 24 Mar 2022 13:11:04 GMT</pubDate>
            <description><![CDATA[在本文中，我们将学习如何使用 Brownie、Python 和 Chainlink 来制作非同质化的 NFT 作品，并在 OpenSea NFT 市场上展示和销售我们的成果。 什么是 NFT？ NFT英文全称为Non-Fungible Token，翻译成中文就是：非同质化代币，具有不可分割、不可替代、独一无二等特点。NFT由于其非同质化、不可拆分的特性，使得它可以和现实世界中的一些商品绑定。换言之，其实就是发行在区块链上的数字资产，这个资产可以是游戏道具、数字艺术品、门票等，并且具有唯一性和不可复制性。由于NFT具备天然的收藏属性和便于交易，加密艺术家们可以利用NFT创造出独一无二的数字艺术品。 ERC20 与 ERC721 NFT 是类似于 ERC20 的区块链token标准，如 AAVE、SNX 和 LINK（技术上为 ERC677）。ERC20 是“可替代”的代币，意思是“可替换”或“可互换”。 例如，无论您使用什么美元纸币，您的美元纸币都将值 1 美元。美元钞票上的序列号可能不同，但钞票是可以互换的，无论如何它们都值 1 美元。 另一方面，NFT 是“不可替代的”，它们遵...]]></description>
            <content:encoded><![CDATA[<figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e02407e3b9994e8bb31a8bcd6fc6ea05988c389692f44d9c53b9536909fffbfe.jpg" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>在本文中，我们将学习如何使用 Brownie、Python 和 Chainlink 来制作非同质化的 NFT 作品，并在 OpenSea NFT 市场上展示和销售我们的成果。</p><p><strong>什么是 NFT？</strong></p><p>NFT英文全称为Non-Fungible Token，翻译成中文就是：非同质化代币，具有不可分割、不可替代、独一无二等特点。NFT由于其非同质化、不可拆分的特性，使得它可以和现实世界中的一些商品绑定。换言之，其实就是发行在区块链上的数字资产，这个资产可以是游戏道具、数字艺术品、门票等，并且具有唯一性和不可复制性。由于NFT具备天然的收藏属性和便于交易，加密艺术家们可以利用NFT创造出独一无二的数字艺术品。</p><p><strong>ERC20 与 ERC721</strong></p><p>NFT 是类似于 ERC20 的区块链token标准，如 AAVE、SNX 和 LINK（技术上为 ERC677）。ERC20 是“可替代”的代币，意思是“可替换”或“可互换”。</p><p>例如，无论您使用什么美元纸币，您的美元纸币都将值 1 美元。美元钞票上的序列号可能不同，但钞票是可以互换的，无论如何它们都值 1 美元。</p><p>另一方面，NFT 是“不可替代的”，它们遵循自己的代币标准 ERC721。例如，蒙娜丽莎是“不可替代的”。即使有人可以复制它，但永远只有一个蒙娜丽莎。如果蒙娜丽莎是在区块链上创建的，它将是 NFT。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/8bf01d758a5844aa7f39d681fa5fffa886d4bb32aa548791f655bce3d8981f34.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><strong>NFT 有什么用？</strong></p><p>NFT 通过将合约永久地部署在链上，让创作者、艺术家、游戏设计师等创造价值。</p><p>你将永远知道谁创造了 NFT，谁拥有 NFT，它来自哪里等等，这种独特性让他们比传统艺术更有价值。在传统艺术中，理解什么是“假货”可能很棘手，而链上的历史很容易追溯。</p><p>由于智能合约和 NFT 是 100% 可编程的，NFT 还可以添加内置的版税和任何其他功能。补偿艺术家一直是一个问题，因为通常艺术家的作品在没有任何归属的情况下四处传播。</p><p>越来越多的艺术家和工程师开始利用这一巨大的附加值，因为这最终成为艺术家获得工作报酬的好方法。不仅如此，NFT 还是一种展示您的创造力并成为数字世界收藏家的有趣方式。</p><p><strong>NFT 的价值</strong></p><p>NFT 已经走过了漫长的道路，我们不断看到 NFT 的销售额创下纪录，例如下面这幅名为“每天：前 5,000 天”的画作售价为 6930 万美元。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/4aa8417505dce1f8a388079f9ff4e79f87dc395db3f59c0a540503ca8b946134.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>NFT也是在数字世界中创造艺术和了解智能合约创建的一种有趣、动态和引人入胜的方式。所以现在将教你制作 NFT 所需的一切知识。</p><p><strong>如何制作 NFT</strong></p><p>现在，制作 NFT 的最简单方法就是前往 Opensea、Rarible 或 Mintible 等平台，并按照他们的分步指南在他们的平台上进行部署即可。</p><p>你可以 100% 走这条路，但是你可能会被平台绑定。你无法实现无限的定制，或者真正利用 NFT 的任何优势。</p><p>如果你想用代码来实现，学习一些扎实的知识，并有能力以无限的创造力创造出一些东西，那么请继续阅读！</p><p><strong>如何进行无限定制的 NFT</strong></p><p>首先介绍NFT Brownie Mix。这是一个包含大量样板代码的工作仓库。</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/PatrickAlphaC/nft-mix">https://github.com/PatrickAlphaC/nft-mix</a></p><p><strong>先决条件</strong></p><p>我们需要安装一些东西才能开始：</p><ul><li><p>Python</p></li><li><p>Nodejs 和 npm</p></li><li><p>Metamask</p></li></ul><p>如果您不熟悉 Metamask，您可以按照下面教程进行设置。</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.chain.link/docs/install-metamask/">https://docs.chain.link/docs/install-metamask/</a></p><p><strong>Rinkeby Testnet ETH 和 LINK</strong></p><p>我们还将部署在 Rinkeby 以太坊测试链上！</p><p>测试链是测试我们的智能合约在现实世界中表现的好方法。我们需要 Rinkeby ETH 和 Rinkeby LINK，我们可以从 Chainlink 文档中最新水龙头的链接中免费获得它们。</p><p>我们还需要将 rinkeby LINK 的token添加到metamask中，我们可以按照 LINK 文档进行操作。</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.chain.link/docs/acquire-link/">https://docs.chain.link/docs/acquire-link/</a></p><p>注意一定要使用 Rinkeby 而不是 Ropsten。在使用像以太坊这样的智能合约平台时，我们需要支付一点 ETH，而从链下获取数据时，我们需要支付一点 LINK。这就是我们需要获取测试链上的LINK 和 ETH 的原因。</p><p>下面这件作品就是我们要部署到 OpenSea 的 NFT。</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://testnets.opensea.io/assets/0x8acb7ca932892eb83e4411b59309d44dddbc4cdf/0">https://testnets.opensea.io/assets/0x8acb7ca932892eb83e4411b59309d44dddbc4cdf/0</a></p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/0c3ee5e50d76da19a2aa47e1dde1c664d6da20b23a8dcddebaba663d142cb1c7.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><strong>快速上手</strong></p><pre data-type="codeBlock" text="git clone https://github.com/PatrickAlphaC/nft-mix
cd nft-mix
"><code>git <span class="hljs-built_in">clone</span> https://github.com/PatrickAlphaC/nft-mix
<span class="hljs-built_in">cd</span> nft-mix
</code></pre><p>现在我们需要安装 <code>ganache-cli</code> 和 <code>eth-brownie</code>。</p><pre data-type="codeBlock" text="pip install eth-brownie
npm install -g ganache-cli
"><code>pip install eth<span class="hljs-operator">-</span>brownie
npm install <span class="hljs-operator">-</span>g ganache<span class="hljs-operator">-</span>cli
</code></pre><p>现在我们可以设置我们的环境变量。如果您不熟悉环境变量，只需将它们添加到 <code>.env</code> 文件中，然后运行：</p><pre data-type="codeBlock" text="source .env
"><code><span class="hljs-built_in">source</span> .<span class="hljs-built_in">env</span>
</code></pre><p><code>.env</code>的示例应该在你刚刚克隆的 repo 中，并注释掉了环境变量。请确保取消注释以使用它们！</p><p>您需要一个 <code>WEB3_INFURA_PROJECT_ID</code>和一个 <code>PRIVATE_KEY</code> 。<code>WEB3_INFURA_PROJECT_ID</code> 可以在注册免费 Infura 帐户时找到。这将为我们提供一种将交易发送到区块链的方法。</p><p>我们还需要一个私钥，您可以从您的 Metamask 中获取。点击 3 个小点，然后单击帐户详细信息和导出私钥。如果您投入现金进去，请不要与任何人分享此密钥！</p><pre data-type="codeBlock" text="export PRIVATE_KEY=YOUR_KEY_HERE
export WEB3_INFURA_PROJECT_ID=YOUR_PROJECT_ID_HERE
"><code>export <span class="hljs-attr">PRIVATE_KEY</span>=YOUR_KEY_HERE
export <span class="hljs-attr">WEB3_INFURA_PROJECT_ID</span>=YOUR_PROJECT_ID_HERE
</code></pre><p>现在我们可以部署我们的 NFT 合约并使用以下两个命令创建我们的第一个收藏品。</p><pre data-type="codeBlock" text="brownie run scripts/simple_collectible/deploy_simple.py --network rinkeby
brownie run scripts/simple_collectible/create_collectible.py --network rinkeby
"><code>brownie run scripts<span class="hljs-operator">/</span>simple_collectible<span class="hljs-operator">/</span>deploy_simple.py <span class="hljs-operator">-</span><span class="hljs-operator">-</span>network rinkeby
brownie run scripts<span class="hljs-operator">/</span>simple_collectible<span class="hljs-operator">/</span>create_collectible.py <span class="hljs-operator">-</span><span class="hljs-operator">-</span>network rinkeby
</code></pre><p>第一个脚本将我们的 NFT 合约部署到 Rinkeby 区块链，第二个脚本创建了我们的第一个收藏品。</p><p>您刚刚部署了第一个智能合约！</p><p>它根本没有什么作用，但别担心——我将在本教程的高级部分向您展示如何在 OpenSea 上渲染它。首先，让我们看看 ERC721 代币标准。</p><p><strong>ERC721 代币标准</strong></p><p>我们来看看我们刚刚部署的合约，在 SimpleCollectible.sol 文件中。</p><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity 0.6.6;
import&quot;@openzeppelin/contracts/token/ERC721/ERC721.sol&quot;;
contract SimpleCollectibleis ERC721 {
    uint256 public tokenCounter;
    constructor () public ERC721 (&quot;Dogie&quot;, &quot;DOG&quot;){
        tokenCounter = 0;
}
function createCollectible(string memory tokenURI) public returns (uint256) {
        uint256 newItemId = tokenCounter;
        _safeMint(msg.sender, newItemId);
        _setTokenURI(newItemId, tokenURI);
        tokenCounter = tokenCounter + 1;
return newItemId;
}
}
"><code><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> 0.6.6;</span>
<span class="hljs-keyword">import</span><span class="hljs-string">"@openzeppelin/contracts/token/ERC721/ERC721.sol"</span>;
<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">SimpleCollectibleis</span> <span class="hljs-title">ERC721</span> </span>{
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> tokenCounter;
    <span class="hljs-function"><span class="hljs-keyword">constructor</span> (<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">ERC721</span> (<span class="hljs-params"><span class="hljs-string">"Dogie"</span>, <span class="hljs-string">"DOG"</span></span>)</span>{
        tokenCounter <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;
}
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createCollectible</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> tokenURI</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">uint256</span> newItemId <span class="hljs-operator">=</span> tokenCounter;
        _safeMint(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, newItemId);
        _setTokenURI(newItemId, tokenURI);
        tokenCounter <span class="hljs-operator">=</span> tokenCounter <span class="hljs-operator">+</span> <span class="hljs-number">1</span>;
<span class="hljs-keyword">return</span> newItemId;
}
}
</code></pre><p>我们将 OpenZeplin 包用于 ERC721 token。导入的这个包允许我们使用典型 ERC721 token的所有功能。这定义了我们的代币将具有的所有功能，例如 <code>transfer</code>——将代币转移给新用户， <code>safeMint</code>——创建新代币，等等。</p><p>您可以通过查看 OpenZepplin ERC721 代币合约找到赋予我们合约的所有功能。我们的合约在下面一行继承了这些功能：</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol">https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol</a></p><pre data-type="codeBlock" text="contract SimpleCollectibleis ERC721 {
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">SimpleCollectibleis</span> <span class="hljs-title">ERC721</span> </span>{
</code></pre><p>这就是 Solidity 继承的方式。当我们部署一个合约时，构造函数会被自动调用，它需要一些参数。</p><pre data-type="codeBlock" text="constructor () public ERC721 (&quot;Dogie&quot;, &quot;DOG&quot;){
        tokenCounter = 0;
}
"><code><span class="hljs-function"><span class="hljs-keyword">constructor</span> (<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">ERC721</span> (<span class="hljs-params"><span class="hljs-string">"Dogie"</span>, <span class="hljs-string">"DOG"</span></span>)</span>{
        tokenCounter <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;
}
</code></pre><p>我们还使用了 ERC721 的构造函数，在我们的构造函数中，我们只需要给它一个名称和一个符号。在我们的例子中，它是“Dogie”和“DOG”。这意味着我们创建的每个 NFT 都将是 Dogie/DOG 类型。</p><p>这就像每张口袋妖怪卡仍然是口袋妖怪，或者交易卡上的每个棒球运动员仍然是棒球运动员。每个棒球运动员都是独一无二的，但他们仍然都是棒球运动员。我们只是使用 DOG 类型。</p><p>我们在顶部有 <code>tokenCounter</code> 来计算我们创建了多少这种类型的 NFT。每个新token都会根据当前的 <code>tokenCounter</code> 获得一个 <code>tokenId</code>。</p><p>实际上可以使用 <code>createCollectible</code> 函数创建 NFT。这就是我们在 <code>create_collectible.py</code> 脚本中所写的。</p><pre data-type="codeBlock" text="function createCollectible(string memory tokenURI) public returns (uint256) {
        uint256 newItemId = tokenCounter;
        _safeMint(msg.sender, newItemId);
        _setTokenURI(newItemId, tokenURI);
        tokenCounter = tokenCounter + 1;
return newItemId;
}
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createCollectible</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> tokenURI</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">uint256</span> newItemId <span class="hljs-operator">=</span> tokenCounter;
        _safeMint(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, newItemId);
        _setTokenURI(newItemId, tokenURI);
        tokenCounter <span class="hljs-operator">=</span> tokenCounter <span class="hljs-operator">+</span> <span class="hljs-number">1</span>;
<span class="hljs-keyword">return</span> newItemId;
}
</code></pre><p><code>_safeMint</code>函数创建新的 NFT，并将其分配给调用 <code>createdCollectible</code>的人，也就是 <code>msg.sender</code>，并使用从 <code>tokenCounter</code> 派生的 <code>newItemId</code>。这就是我们如何通过检查 <code>tokenId</code>的所有者来跟踪谁拥有什么。</p><p>您会注意到我们还调用了 <code>_setTokenURI</code>。让我们来看一下。</p><p><strong>什么是 NFT 元数据和 TokenURI？</strong></p><p>当创建智能合约和创建 NFT 时，人们很快意识到将大量数据部署到区块链是非常昂贵的。小至 1 KB 的图像的存储成本很容易超过 100 万美元。</p><p>这显然是 NFT 的一个问题，因为拥有创意艺术意味着您必须将这些信息存储在某个地方。他们还想要一种轻量级的方式来存储有关 NFT 的属性——这就是 <code>tokenURI</code> 和元数据发挥作用的地方。</p><p><strong>TokenURI</strong></p><p>NFT 上的 <code>tokenURI</code> 是token“外观”的唯一标识符。URI 可以是通过 HTTPS 的 API 调用、IPFS 哈希值或任何其他独特的东西。</p><p>它们遵循显示元数据的标准，如下所示：</p><pre data-type="codeBlock" text="{
&quot;name&quot;: &quot;name&quot;,
&quot;description&quot;: &quot;description&quot;,
&quot;image&quot;: &quot;https://ipfs.io/ipfs/QmTgqnhFBMkfT9s8PHKcdXBn1f5bG3Q5hmBaR4U6hoTvb1?filename=Chainlink_Elf.png&quot;,
&quot;attributes&quot;: [
{
&quot;trait_type&quot;: &quot;trait&quot;,
&quot;value&quot;: 100
}
]
}
"><code><span class="hljs-punctuation">{</span>
<span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"name"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"description"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"description"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"image"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"https://ipfs.io/ipfs/QmTgqnhFBMkfT9s8PHKcdXBn1f5bG3Q5hmBaR4U6hoTvb1?filename=Chainlink_Elf.png"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"attributes"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span>
<span class="hljs-punctuation">{</span>
<span class="hljs-attr">"trait_type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"trait"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"value"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">100</span>
<span class="hljs-punctuation">}</span>
<span class="hljs-punctuation">]</span>
<span class="hljs-punctuation">}</span>
</code></pre><p>这些显示了 NFT 的外观及其属性。图像部分指向 NFT 外观的另一个 URI。这使得 Opensea、Rarible 和 Mintable 等 NFT 平台可以轻松地在其平台上呈现 NFT，因为它们都在寻找这种元数据。</p><p><strong>链下元数据与链上元数据</strong></p><p>现在你可能会想“等等......如果元数据不在链上，这是否意味着我的 NFT 可能会在某个时候消失”？你是对的。</p><p>您认为链下元数据意味着您不能使用该元数据让您的智能合约相互交互也是正确的。</p><p>这就是为什么我们要专注于链上元数据，以便我们可以对 NFT 进行编程以相互交互。</p><p>但是，我们仍然需要链下元数据的图像部分，因为我们没有一种很好的方法来在链上存储大图像。但是别担心，我们仍然可以通过使用 IPFS 在去中心化网络上免费做到这一点。</p><p>这是来自 IPFS 的 <code>imageURI</code>示例，它显示了在龙与地下城教程中创建的 <code>ChainlinkElf</code>。</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.chain.link/build-deploy-and-sell-your-own-dynamic-nft/">https://blog.chain.link/build-deploy-and-sell-your-own-dynamic-nft/</a></p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e3493123b2b22e8fe7ccb6ef652c0ab9372efd60b50d8eb02189c7c069ff7928.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>我们没有为简单的 NFT 设置 <code>tokenURI</code>，因为我们只想展示一个基本示例。</p><p>下一篇将讲解 NFT 进阶知识，这样就可以看到我们用链上元数据实现的一些惊人功能，在 opeansea 上渲染 NFT，并让我们的狗狗振作起来！</p>]]></content:encoded>
            <author>quantbang@newsletter.paragraph.com (quantbang)</author>
        </item>
        <item>
            <title><![CDATA[用 Python 监控 Uniswap 加密货币价格]]></title>
            <link>https://paragraph.com/@quantbang/python-uniswap</link>
            <guid>9Crv0cSOyvfaPHPRCVeH</guid>
            <pubDate>Thu, 24 Mar 2022 13:02:59 GMT</pubDate>
            <description><![CDATA[Uniswap是一个基于以太坊的交易所协议，允许任何人交换ERC20代币。普遍上讲，交易代币要求买卖双方创造委托订单，而Uniswap则是自动创建一个交易市场。Uniswap旨在帮助解决去中心化交易所的流动性问题，是一种去中心化的交易所。 在过去的几个月里，我在 Uniswap 上新上市的币上观察到了一个非常有趣的规律，尤其是那些被炒作的币种。在新币上市后的前 10-15 分钟内，价格上涨非常高，不过在接下来的几个小时内大幅下跌，然后在接下来的 24 小时内上涨超过 50%。不要以为你可以在价格低的时候买进，然后10分钟后高价卖出。因为以太坊网络拥堵的原因，这段时间的gas费用非常高（数百美元）。Superfarm 是之前比较受欢迎的一个新项目，在 Telegram 上拥有超过 86,000 名成员，在 Twitter 上拥有超过 88,000 名关注者。所以我决定来测试一下这个规律，或者说我决定进行推测。在最初的几分钟里，价格飙升至 2 美元左右，然后跌至 1 美元左右，因此我决定投资 0.98 eth（按当前价格计算约 1.5K 美元）。 第二天，如果价格要上涨，我必须观察价...]]></description>
            <content:encoded><![CDATA[<figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/649c1c629f9088f720812ee249dfc5adc3ac72c54e7506b81ed48c593359e658.jpg" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Uniswap是一个基于以太坊的交易所协议，允许任何人交换ERC20代币。普遍上讲，交易代币要求买卖双方创造委托订单，而Uniswap则是自动创建一个交易市场。Uniswap旨在帮助解决去中心化交易所的流动性问题，是一种去中心化的交易所。</p><p>在过去的几个月里，我在 Uniswap 上新上市的币上观察到了一个非常有趣的规律，尤其是那些被炒作的币种。在新币上市后的前 10-15 分钟内，价格上涨非常高，不过在接下来的几个小时内大幅下跌，然后在接下来的 24 小时内上涨超过 50%。不要以为你可以在价格低的时候买进，然后10分钟后高价卖出。因为以太坊网络拥堵的原因，这段时间的gas费用非常高（数百美元）。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/03d6e5897af3a749f92082e8e492344a0ea43edf09cb7fffd412e8fbbec97ff6.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Superfarm 是之前比较受欢迎的一个新项目，在 Telegram 上拥有超过 86,000 名成员，在 Twitter 上拥有超过 88,000 名关注者。所以我决定来测试一下这个规律，或者说我决定进行推测。在最初的几分钟里，价格飙升至 2 美元左右，然后跌至 1 美元左右，因此我决定投资 0.98 eth（按当前价格计算约 1.5K 美元）。</p><p>第二天，如果价格要上涨，我必须观察价格才能退出我在 Superfarm 代币上的头寸。每分钟检查价格真的是在浪费时间，所以我决定制作一个脚本来监控价格，如果价格达到某个目标，它会通知我。</p><p>为了查看 Uniswap 的价格，我使用了 uniswap-python 库。</p><pre data-type="codeBlock" text="!pip install uniswap-python
"><code><span class="hljs-operator">!</span>pip install uniswap<span class="hljs-operator">-</span>python
</code></pre><p>如果你想知道我为什么使用它，pip 前面的感叹号是因为我已经在 Jupyter Notebook 中进行了编码，并且我已经从 Notebook 单元安装了所有必要的 python 库。</p><pre data-type="codeBlock" text="eth = &quot;0x0000000000000000000000000000000000000000&quot;
dai = &quot;0x6B175474E89094C44Da98b954EedeAC495271d0F&quot;
sup = &quot;0xe53EC727dbDEB9E2d5456c3be40cFF031AB40A55&quot;

address = &quot;0x0000000000000000000000000000000000000000&quot;  #  if you&apos;re not making transactions
private_key = None  # if you&apos;re not going to make transactions

provider = &lt;the url of your provider&gt; # if you use Infura 
 will be 
 like &apos;https://mainnet.infura.io/v3/########&apos;
uniswap_wrapper = Uniswap(address, private_key, version=2, provider=provider)  # use Uniswap v2
"><code><span class="hljs-attr">eth</span> = <span class="hljs-string">"0x0000000000000000000000000000000000000000"</span>
<span class="hljs-attr">dai</span> = <span class="hljs-string">"0x6B175474E89094C44Da98b954EedeAC495271d0F"</span>
<span class="hljs-attr">sup</span> = <span class="hljs-string">"0xe53EC727dbDEB9E2d5456c3be40cFF031AB40A55"</span>

<span class="hljs-attr">address</span> = <span class="hljs-string">"0x0000000000000000000000000000000000000000"</span>  <span class="hljs-comment">#  if you're not making transactions</span>
<span class="hljs-attr">private_key</span> = None  <span class="hljs-comment"># if you're not going to make transactions</span>

<span class="hljs-attr">provider</span> = &#x3C;the url of your provider> <span class="hljs-comment"># if you use Infura </span>
 will be 
 like 'https://mainnet.infura.io/v3/<span class="hljs-comment">########'</span>
<span class="hljs-attr">uniswap_wrapper</span> = Uniswap(address, private_key, version=<span class="hljs-number">2</span>, provider=provider)  <span class="hljs-comment"># use Uniswap v2</span>
</code></pre><p>第一个变量包含我们想要在 Uniswap 上获得加密货币的以太坊地址校验和。</p><p>要获取加密货币的以太坊地址校验和，您可以使用 Coingecko 首先获取合约地址：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b0d231f5ae6fac5dfed76853b624c51691e7e4f0bb02f8bd8498102af1363ec4.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>然后你就可以使用下面网站轻松从合约地址中获取以太坊地址校验和。</p><p><code>https://ethsum.netlify.app/</code></p><p>如果您不太熟悉什么是以太坊地址校验和，您可以将其视为一个标识符，基于这个加密货币标识符 Uniswap 将能够为您提供价格。</p><p><code>provider</code>（<code>web3 provider</code>）是一个运行 geth 或 parity 节点的网站，它与以太坊网络交互。要获得<code>provider</code>，您可以使用 Infura。如果您使用 Infura，必须注册然后创建一个新项目，在设置选项卡上您可以看到提供商 URL：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/3cb2066d80751c0016dc194a2c8de473da9151d00109195a24eee13516cf17b5.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>要获得以 eth 表示的 Superfarm 币价格：</p><pre data-type="codeBlock" text="sup_price = uniswap_wrapper.get_token_eth_input_price(sup, 10 ** 18)

print(sup_price / 10**18)
# 0.000901625376604887
"><code>sup_price <span class="hljs-operator">=</span> uniswap_wrapper.get_token_eth_input_price(sup, <span class="hljs-number">10</span> <span class="hljs-operator">*</span><span class="hljs-operator">*</span> <span class="hljs-number">18</span>)

print(sup_price <span class="hljs-operator">/</span> <span class="hljs-number">10</span><span class="hljs-operator">*</span><span class="hljs-operator">*</span><span class="hljs-number">18</span>)
# <span class="hljs-number">0</span><span class="hljs-number">.000901625376604887</span>
</code></pre><p><code>10 ** 18</code>代表Superfarm 币的数量，它必须是一个整数值，如果我们选择小数量值将是0（由于近似），所以我们需要一个大数量。我们将获得 <code>10 ** 18</code>个 Superfarm 币的 eth 价格。</p><p>如果我们想以美元计算 1 个 Superfarm 币的价格，我们可以使用 Dai 稳定币（1 Dai ~= 1 美元）。我们可以请求相同数量的 Superfarm 和 Dai 币，然后分割它们。</p><pre data-type="codeBlock" text="def get_sup_price_in_dollars():
    sup_price = uniswap_wrapper.get_token_eth_input_price(sup, 10 ** 18)
    dai_price = uniswap_wrapper.get_token_eth_input_price(dai, 10 ** 18)
    return round(sup_price / dai_price, 4)

print(get_sup_price_in_dollars())
# 1.357 --&gt; the price in dollars for 1 Superfarm coin
"><code>def get_sup_price_in_dollars():
    sup_price <span class="hljs-operator">=</span> uniswap_wrapper.get_token_eth_input_price(sup, <span class="hljs-number">10</span> <span class="hljs-operator">*</span><span class="hljs-operator">*</span> <span class="hljs-number">18</span>)
    dai_price <span class="hljs-operator">=</span> uniswap_wrapper.get_token_eth_input_price(dai, <span class="hljs-number">10</span> <span class="hljs-operator">*</span><span class="hljs-operator">*</span> <span class="hljs-number">18</span>)
    <span class="hljs-keyword">return</span> round(sup_price <span class="hljs-operator">/</span> dai_price, <span class="hljs-number">4</span>)

print(get_sup_price_in_dollars())
# <span class="hljs-number">1.357</span> <span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">></span> the price in dollars <span class="hljs-keyword">for</span> <span class="hljs-number">1</span> Superfarm coin
</code></pre><p>为了在我的桌面上推送通知，我使用了<code>plyer</code>库：</p><pre data-type="codeBlock" text="!pip install plyer
"><code><span class="hljs-addition">!pip install plyer</span>
</code></pre><p>这是监控 Superfarm 价格和自前一个值以来的变化的函数：</p><pre data-type="codeBlock" text="from plyer import notification

def notify(price: int, new_price: int):
    notification.notify(
    title = &apos;Superfarm price whatcher&apos;,
    message = f&quot;Total value of SUPER = {new_price}, change: {round(new_price - price, 3)}&quot;,
    timeout = 10,
    app_icon = &apos;/home/kederrac/Pictures/uniswap.ICO&apos;,
    )
"><code><span class="hljs-keyword">from</span> plyer <span class="hljs-keyword">import</span> notification

<span class="hljs-keyword">def</span> <span class="hljs-title function_">notify</span>(<span class="hljs-params">price: <span class="hljs-built_in">int</span>, new_price: <span class="hljs-built_in">int</span></span>):
    notification.notify(
    title = <span class="hljs-string">'Superfarm price whatcher'</span>,
    message = <span class="hljs-string">f"Total value of SUPER = <span class="hljs-subst">{new_price}</span>, change: <span class="hljs-subst">{<span class="hljs-built_in">round</span>(new_price - price, <span class="hljs-number">3</span>)}</span>"</span>,
    timeout = <span class="hljs-number">10</span>,
    app_icon = <span class="hljs-string">'/home/kederrac/Pictures/uniswap.ICO'</span>,
    )
</code></pre><p>如您所见，我还添加了一个应用程序图标，为了能够使用应用程序图标，我还必须将 .png 图片转换为 .ico 格式，安装 dbus-python 库（如果您使用的是 Windows，则不需要）。</p><pre data-type="codeBlock" text="!pip install  dbus-python
"><code><span class="hljs-operator">!</span>pip install  dbus<span class="hljs-operator">-</span>python
</code></pre><p>现在让我们把它们放在一起：</p><pre data-type="codeBlock" text="import time

price = get_sup_price_in_dollars()
notify(price, price)

while True:
    new_price = get_sup_price_in_dollars()
    
    if abs(price - new_price) &gt; 0.05:
        notify(price, new_price)
        
    price = new_price
    time.sleep(10)
"><code>import <span class="hljs-selector-tag">time</span>

price = <span class="hljs-built_in">get_sup_price_in_dollars</span>()
<span class="hljs-built_in">notify</span>(price, price)

while True:
    new_price = <span class="hljs-built_in">get_sup_price_in_dollars</span>()
    
    if <span class="hljs-built_in">abs</span>(price - new_price) > <span class="hljs-number">0.05</span>:
        <span class="hljs-built_in">notify</span>(price, new_price)
        
    price = new_price
    time.<span class="hljs-built_in">sleep</span>(<span class="hljs-number">10</span>)
</code></pre><p>在这里，我每 10 秒调用一次 <code>get_sup_price_in_dollars()</code> 函数来检查价格是否发生变化，如果变化大于 0.05 美元，将推送通知，您可以更改变化的最小值以获得通知，或者您 可以设置要通知的最低价格目标。</p><p>结果如下：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/6075df56e7c44dc647c220444665640529430f0b158bf239639f1e692488cd89.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>（在 ubuntu 20.04 上）</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/28304b60c2a8f49d299bbc82318ffbd80902ecbe38e97498962490aef8608a3c.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>（在 windows 10 上）</p><p>这对于实时价格通知非常有帮助，这种方法的另一优点是速度，与必须使用 Coingecko 相比，您将获得数分钟的时间，如果您使用 Uniswap 网络界面，时间将更快。</p><p>在这里你可以看到我一开始所说的规律：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/50a6f721adf53b874f7414bee5706ead31425bd583b27c4bfcddbbb9f76d9c1b.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure>]]></content:encoded>
            <author>quantbang@newsletter.paragraph.com (quantbang)</author>
        </item>
        <item>
            <title><![CDATA[用 Python 中的 web3.py 库开发 Dapp]]></title>
            <link>https://paragraph.com/@quantbang/python-web3-py-dapp</link>
            <guid>FvVd4Z7vrUUbl7xDmWcG</guid>
            <pubDate>Thu, 24 Mar 2022 12:57:42 GMT</pubDate>
            <description><![CDATA[什么是 DApp “DApp”代表去中心化应用程序。与传统应用程序一样，去中心化应用程序也有前端（客户端）和后端（服务器端）。DApp 的用户界面可以用任何语言编写（就像传统应用程序一样），并且可以调用其后端。那么，Dapps 与传统应用程序有何不同？DApp 的后端代码运行在分散的对等网络（即区块链）上。您可能听说过 BitTorrent、Tor、Popcorn Time——它们是在点对点网络上运行但不在区块链上运行的 DApp。区块链 DApp 有自己的后端代码，称为智能合约，可以部署到区块链（最常见的是以太坊）。智能合约定义了整个 DApp 的逻辑。它是使用区块链作为后端的命脉。那么Python 开发人员可以在哪里利用它？重点来了——web3。Web3 是一组库，允许您与本地或远程以太坊区块链进行交互。简单地说，web3 是与您的后端（区块链）通信的桥梁。幸运的是，以太坊开发人员已经制作了一个 python 库 web3.py 用于与以太坊进行交互。它的 API 源自 web3 的 JavaScript 版本。因此，除了 web3.js，我们也可以通过 web3.py 库与...]]></description>
            <content:encoded><![CDATA[<figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/ceb5e2c548d885e7cb46660f8da39ff00fb7dc48c0c6a81279f4032e6b554ea6.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><strong>什么是 DApp</strong></p><p>“DApp”代表去中心化应用程序。与传统应用程序一样，去中心化应用程序也有前端（客户端）和后端（服务器端）。DApp 的用户界面可以用任何语言编写（就像传统应用程序一样），并且可以调用其后端。那么，Dapps 与传统应用程序有何不同？DApp 的后端代码运行在分散的对等网络（即区块链）上。您可能听说过 BitTorrent、Tor、Popcorn Time——它们是在点对点网络上运行但不在区块链上运行的 DApp。区块链 DApp 有自己的后端代码，称为智能合约，可以部署到区块链（最常见的是以太坊）。智能合约定义了整个 DApp 的逻辑。它是使用区块链作为后端的命脉。那么Python 开发人员可以在哪里利用它？重点来了——web3。Web3 是一组库，允许您与本地或远程以太坊区块链进行交互。简单地说，web3 是与您的后端（区块链）通信的桥梁。幸运的是，以太坊开发人员已经制作了一个 python 库 web3.py 用于与以太坊进行交互。它的 API 源自 web3 的 JavaScript 版本。因此，除了 web3.js，我们也可以通过 web3.py 库与区块链进行交互。</p><p>Dapps 开发包括三个简单的步骤：</p><ul><li><p>在区块链网络上部署智能合约</p></li><li><p>从部署的智能合约中读取数据</p></li><li><p>将交易发送到部署的智能合约</p></li></ul><p>我们将在带有 web3.py 库的 python 环境中一步一步地进行这三个操作要与区块链交互，我们必须连接到任何完全同步的节点。在本教程中，我们指向一个 Infura 节点。确保你有一个以太坊钱包（使用 Metamask chrome 扩展或 myetherwallet 创建以太坊钱包并安全地存储你的私钥）并在其中添加一些测试 Ether 以进行操作。</p><p><strong>安装</strong></p><ul><li><p>Python 2.7 +</p></li><li><p>Node.js</p></li><li><p>Truffle</p></li></ul><pre data-type="codeBlock" text="npm install -g truffle
"><code>npm install <span class="hljs-operator">-</span>g truffle
</code></pre><ul><li><p>Pip</p></li></ul><pre data-type="codeBlock" text="npm i pip
"><code>npm <span class="hljs-selector-tag">i</span> pip
</code></pre><ul><li><p>web3.py</p></li></ul><pre data-type="codeBlock" text="pip install web3
"><code></code></pre><ul><li><p>Infura 项目 API</p></li></ul><p>前往 Infura 网站并注册。创建一个新项目并复制 Ropsten Infura RPC URL。我们将把我们的智能合约部署到 Ropsten 测试网络。</p><p><strong>智能合约</strong></p><p>每个程序员都用他们最喜欢的编程语言执行了一个“hello world”程序，以了解运行该语言的基础知识。这是我们使用 Solidity 语言编写的简单的“hello world”版本的智能合约，我们可以在区块链上添加问候语并检索它。Solidity 是编写智能合约最常用的语言，它编译为可以在节点上运行的以太坊虚拟机上执行的字节码。</p><pre data-type="codeBlock" text="pragma solidity ^0.5.7;
contract greeter{
string greeting;
function greet(string memory _greeting)public{
        greeting=_greeting;
}
function getGreeting() public view returns(string memory) {
return greeting;
}
}
"><code><span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.5.7;</span>
<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">greeter</span></span>{
<span class="hljs-keyword">string</span> greeting;
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">greet</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> _greeting</span>)<span class="hljs-title"><span class="hljs-keyword">public</span></span></span>{
        greeting<span class="hljs-operator">=</span>_greeting;
}
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getGreeting</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span></span>) </span>{
<span class="hljs-keyword">return</span> greeting;
}
}
</code></pre><p>您可以通过传递字符串值使用 <code>greet()</code> 方法添加问候语，并使用 <code>getGreting()</code> 方法检索问候语。</p><p><strong>1. 在区块链网络上部署智能合约</strong></p><p><strong>a) 创建项目：</strong></p><pre data-type="codeBlock" text="mkdir pythonDapp
cd pythonDapp
truffle init
"><code><span class="hljs-built_in">mkdir</span> pythonDapp
<span class="hljs-built_in">cd</span> pythonDapp
truffle init
</code></pre><p>成功初始化项目后，转到您的文件夹并在 <code>/contracts</code>目录中创建 <code>greeter.sol</code> 文件。在网络上部署合约之前，我们必须编译它并构建工件。</p><p><strong>b) 智能合约的编译：</strong></p><p>因此，对于编译，我们将使用 Truffle solc 编译器。在您的主目录中，运行以下命令：</p><pre data-type="codeBlock" text="truffle compile
(or)
truffle.cmd compile #(for windows only)
"><code>truffle <span class="hljs-built_in">compile</span>
(<span class="hljs-keyword">or</span>)
truffle.cmd <span class="hljs-built_in">compile</span> <span class="hljs-comment">#(for windows only)</span>
</code></pre><p>上面的命令将在 <code>/contracts</code> 目录中编译你的合约，并在 <code>/build</code> 目录中创建二进制工件文件 <code>greeter.json</code>。</p><p><strong>c) 部署合约：</strong></p><p>打开您的 Python IDLE 编辑器，并在主目录 deploy.py 中使用以下代码创建一个新文件，然后在您的目录中运行 py deploy.py。</p><pre data-type="codeBlock" text="import json
from web3 importWeb3, HTTPProvider
from web3.contract importConciseContract
# web3.py instance
w3 = Web3(HTTPProvider(&quot;https://ropsten.infura.io/v3/&lt;API key&gt;&quot;))
print(w3.isConnected())
key=&quot;&lt;Private Key here with 0x prefix&gt;&quot;
acct = w3.eth.account.privateKeyToAccount(key)
# compile your smart contract with truffle first
truffleFile = json.load(open(&apos;./build/contracts/greeter.json&apos;))
abi = truffleFile[&apos;abi&apos;]
bytecode = truffleFile[&apos;bytecode&apos;]
contract= w3.eth.contract(bytecode=bytecode, abi=abi)
#building transaction
construct_txn = contract.constructor().buildTransaction({
&apos;from&apos;: acct.address,
&apos;nonce&apos;: w3.eth.getTransactionCount(acct.address),
&apos;gas&apos;: 1728712,
&apos;gasPrice&apos;: w3.toWei(&apos;21&apos;, &apos;gwei&apos;)})
signed = acct.signTransaction(construct_txn)
tx_hash=w3.eth.sendRawTransaction(signed.rawTransaction)
print(tx_hash.hex())
tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)
print(&quot;Contract Deployed At:&quot;, tx_receipt[&apos;contractAddress&apos;])
"><code><span class="hljs-keyword">import</span> <span class="hljs-title">json</span>
<span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">web3</span> <span class="hljs-title">importWeb3</span>, <span class="hljs-title">HTTPProvider</span>
<span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">web3</span>.<span class="hljs-title"><span class="hljs-keyword">contract</span></span> <span class="hljs-title">importConciseContract</span>
# <span class="hljs-title">web3</span>.<span class="hljs-title">py</span> <span class="hljs-title">instance</span>
<span class="hljs-title">w3</span> <span class="hljs-operator">=</span> <span class="hljs-title">Web3</span>(<span class="hljs-title">HTTPProvider</span>(<span class="hljs-string">"https://ropsten.infura.io/v3/&#x3C;API key>"</span>))
<span class="hljs-title">print</span>(<span class="hljs-title">w3</span>.<span class="hljs-title">isConnected</span>())
<span class="hljs-title">key</span><span class="hljs-operator">=</span><span class="hljs-string">"&#x3C;Private Key here with 0x prefix>"</span>
<span class="hljs-title">acct</span> <span class="hljs-operator">=</span> <span class="hljs-title">w3</span>.<span class="hljs-title">eth</span>.<span class="hljs-title">account</span>.<span class="hljs-title">privateKeyToAccount</span>(<span class="hljs-title">key</span>)
# <span class="hljs-title">compile</span> <span class="hljs-title">your</span> <span class="hljs-title">smart</span> <span class="hljs-title"><span class="hljs-keyword">contract</span></span> <span class="hljs-title">with</span> <span class="hljs-title">truffle</span> <span class="hljs-title">first</span>
<span class="hljs-title">truffleFile</span> <span class="hljs-operator">=</span> <span class="hljs-title">json</span>.<span class="hljs-title">load</span>(<span class="hljs-title">open</span>(<span class="hljs-string">'./build/contracts/greeter.json'</span>))
<span class="hljs-title"><span class="hljs-built_in">abi</span></span> <span class="hljs-operator">=</span> <span class="hljs-title">truffleFile</span>[<span class="hljs-string">'abi'</span>]
<span class="hljs-title">bytecode</span> <span class="hljs-operator">=</span> <span class="hljs-title">truffleFile</span>[<span class="hljs-string">'bytecode'</span>]
<span class="hljs-title"><span class="hljs-keyword">contract</span></span><span class="hljs-operator">=</span> <span class="hljs-title">w3</span>.<span class="hljs-title">eth</span>.<span class="hljs-title"><span class="hljs-keyword">contract</span></span>(<span class="hljs-title">bytecode</span><span class="hljs-operator">=</span><span class="hljs-title">bytecode</span>, <span class="hljs-title"><span class="hljs-built_in">abi</span></span><span class="hljs-operator">=</span><span class="hljs-title"><span class="hljs-built_in">abi</span></span>)
#<span class="hljs-title">building</span> <span class="hljs-title">transaction</span>
<span class="hljs-title">construct_txn</span> <span class="hljs-operator">=</span> <span class="hljs-title"><span class="hljs-keyword">contract</span></span>.<span class="hljs-title"><span class="hljs-keyword">constructor</span></span>().<span class="hljs-title">buildTransaction</span>({
<span class="hljs-string">'from'</span>: <span class="hljs-title">acct</span>.<span class="hljs-title"><span class="hljs-keyword">address</span></span>,
<span class="hljs-string">'nonce'</span>: <span class="hljs-title">w3</span>.<span class="hljs-title">eth</span>.<span class="hljs-title">getTransactionCount</span>(<span class="hljs-title">acct</span>.<span class="hljs-title"><span class="hljs-keyword">address</span></span>),
<span class="hljs-string">'gas'</span>: 1728712,
<span class="hljs-string">'gasPrice'</span>: <span class="hljs-title">w3</span>.<span class="hljs-title">toWei</span>(<span class="hljs-string">'21'</span>, <span class="hljs-string">'gwei'</span>)})
<span class="hljs-title">signed</span> <span class="hljs-operator">=</span> <span class="hljs-title">acct</span>.<span class="hljs-title">signTransaction</span>(<span class="hljs-title">construct_txn</span>)
<span class="hljs-title">tx_hash</span><span class="hljs-operator">=</span><span class="hljs-title">w3</span>.<span class="hljs-title">eth</span>.<span class="hljs-title">sendRawTransaction</span>(<span class="hljs-title">signed</span>.<span class="hljs-title">rawTransaction</span>)
<span class="hljs-title">print</span>(<span class="hljs-title">tx_hash</span>.<span class="hljs-title">hex</span>())
<span class="hljs-title">tx_receipt</span> <span class="hljs-operator">=</span> <span class="hljs-title">w3</span>.<span class="hljs-title">eth</span>.<span class="hljs-title">waitForTransactionReceipt</span>(<span class="hljs-title">tx_hash</span>)
<span class="hljs-title">print</span>(<span class="hljs-string">"Contract Deployed At:"</span>, <span class="hljs-title">tx_receipt</span>[<span class="hljs-string">'contractAddress'</span>])
</code></pre><p>这是我所做的：</p><ul><li><p>导入的 web3 库和所有其他必需的模块</p></li><li><p>通过指向 Ropsten Infura 节点启动 web3 提供程序</p></li><li><p>添加了用于签署交易的帐户地址和私钥。不要忘记在代码中添加您的凭据。</p></li><li><p>通过指向 Truffle 编译的工件文件greeter.json的abi和字节码启动合约实例</p></li><li><p>添加了带有随机数、gas、gasPrice 等参数的construct_txn。此处，gas 是指交易应在以太坊中使用和支付的最大计算资源量。gasPrice 是指在交易中使用该数量的 gas 时的最小 Ether 数量。to 指的是您发送交易的地址。仅当您将 Ether 发送到帐户或智能合约时才需要 to 参数。</p></li><li><p>使用我们的私钥签署交易并在网络上广播。</p></li><li><p>在控制台中记录交易哈希和部署的合约地址。根据以太坊的说法，事务处理时间 &lt;20 秒。所以你必须等待 20 秒才能获得部署的合约地址。您的后端现在已成功部署在以太坊区块链上。现在您可以使用此地址与您的智能合约进行交互。复制此合约地址。</p></li></ul><p><strong>2. 向部署的合约发送交易</strong></p><p>在我们的合约中，有一个方法greet()。我们可以单独使用这种方法在我们的合同中添加问候语。让我们看看我们如何使用 web3.py 来做到这一点。打开您的 Python IDLE 编辑器并使用以下代码创建一个新文件 sign.py。然后在您的目录中运行 py sign.py。</p><pre data-type="codeBlock" text="import json
from web3 importWeb3, HTTPProvider
from web3.contract importConciseContract
# compile your smart contract with truffle first
truffleFile = json.load(open(&apos;./build/contracts/greeter.json&apos;))
abi = truffleFile[&apos;abi&apos;]
bytecode = truffleFile[&apos;bytecode&apos;]
# web3.py instance
w3 = Web3(HTTPProvider(&quot;https://ropsten.infura.io/v3/&lt;API key&gt;&quot;)) #modify
print(w3.isConnected())
contract_address = Web3.toChecksumAddress(&quot;&lt;Deployed Contract Address here&gt;&quot;) #modify
key=&quot;&lt;Private key with 0x prefix here&gt;&quot;#modify
acct = w3.eth.account.privateKeyToAccount(key)
account_address= acct.address
# Instantiate and deploy contract
contract = w3.eth.contract(abi=abi, bytecode=bytecode)
# Contract instance
contract_instance = w3.eth.contract(abi=abi, address=contract_address)
# Contract instance in concise mode
#contract_instance = w3.eth.contract(abi=abi, address=contract_address, ContractFactoryClass=ConciseContract)
tx = contract_instance.functions.greet(&quot;Hello all  my goody people&quot;).buildTransaction({&apos;nonce&apos;: w3.eth.getTransactionCount(account_address)})
#Get tx receipt to get contract address
signed_tx = w3.eth.account.signTransaction(tx, key)
#tx_receipt = w3.eth.getTransactionReceipt(tx_hash)
hash= w3.eth.sendRawTransaction(signed_tx.rawTransaction)
print(hash.hex())
"><code><span class="hljs-keyword">import</span> <span class="hljs-title">json</span>
<span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">web3</span> <span class="hljs-title">importWeb3</span>, <span class="hljs-title">HTTPProvider</span>
<span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">web3</span>.<span class="hljs-title"><span class="hljs-keyword">contract</span></span> <span class="hljs-title">importConciseContract</span>
# <span class="hljs-title">compile</span> <span class="hljs-title">your</span> <span class="hljs-title">smart</span> <span class="hljs-title"><span class="hljs-keyword">contract</span></span> <span class="hljs-title">with</span> <span class="hljs-title">truffle</span> <span class="hljs-title">first</span>
<span class="hljs-title">truffleFile</span> <span class="hljs-operator">=</span> <span class="hljs-title">json</span>.<span class="hljs-title">load</span>(<span class="hljs-title">open</span>(<span class="hljs-string">'./build/contracts/greeter.json'</span>))
<span class="hljs-title"><span class="hljs-built_in">abi</span></span> <span class="hljs-operator">=</span> <span class="hljs-title">truffleFile</span>[<span class="hljs-string">'abi'</span>]
<span class="hljs-title">bytecode</span> <span class="hljs-operator">=</span> <span class="hljs-title">truffleFile</span>[<span class="hljs-string">'bytecode'</span>]
# <span class="hljs-title">web3</span>.<span class="hljs-title">py</span> <span class="hljs-title">instance</span>
<span class="hljs-title">w3</span> <span class="hljs-operator">=</span> <span class="hljs-title">Web3</span>(<span class="hljs-title">HTTPProvider</span>(<span class="hljs-string">"https://ropsten.infura.io/v3/&#x3C;API key>"</span>)) #<span class="hljs-title">modify</span>
<span class="hljs-title">print</span>(<span class="hljs-title">w3</span>.<span class="hljs-title">isConnected</span>())
<span class="hljs-title">contract_address</span> <span class="hljs-operator">=</span> <span class="hljs-title">Web3</span>.<span class="hljs-title">toChecksumAddress</span>(<span class="hljs-string">"&#x3C;Deployed Contract Address here>"</span>) #<span class="hljs-title">modify</span>
<span class="hljs-title">key</span><span class="hljs-operator">=</span><span class="hljs-string">"&#x3C;Private key with 0x prefix here>"</span>#<span class="hljs-title">modify</span>
<span class="hljs-title">acct</span> <span class="hljs-operator">=</span> <span class="hljs-title">w3</span>.<span class="hljs-title">eth</span>.<span class="hljs-title">account</span>.<span class="hljs-title">privateKeyToAccount</span>(<span class="hljs-title">key</span>)
<span class="hljs-title">account_address</span><span class="hljs-operator">=</span> <span class="hljs-title">acct</span>.<span class="hljs-title"><span class="hljs-keyword">address</span></span>
# <span class="hljs-title">Instantiate</span> <span class="hljs-title">and</span> <span class="hljs-title">deploy</span> <span class="hljs-title"><span class="hljs-keyword">contract</span></span>
<span class="hljs-title"><span class="hljs-keyword">contract</span></span> <span class="hljs-operator">=</span> <span class="hljs-title">w3</span>.<span class="hljs-title">eth</span>.<span class="hljs-title"><span class="hljs-keyword">contract</span></span>(<span class="hljs-title"><span class="hljs-built_in">abi</span></span><span class="hljs-operator">=</span><span class="hljs-title"><span class="hljs-built_in">abi</span></span>, <span class="hljs-title">bytecode</span><span class="hljs-operator">=</span><span class="hljs-title">bytecode</span>)
# <span class="hljs-title">Contract</span> <span class="hljs-title">instance</span>
<span class="hljs-title">contract_instance</span> <span class="hljs-operator">=</span> <span class="hljs-title">w3</span>.<span class="hljs-title">eth</span>.<span class="hljs-title"><span class="hljs-keyword">contract</span></span>(<span class="hljs-title"><span class="hljs-built_in">abi</span></span><span class="hljs-operator">=</span><span class="hljs-title"><span class="hljs-built_in">abi</span></span>, <span class="hljs-title"><span class="hljs-keyword">address</span></span><span class="hljs-operator">=</span><span class="hljs-title">contract_address</span>)
# <span class="hljs-title">Contract</span> <span class="hljs-title">instance</span> <span class="hljs-title">in</span> <span class="hljs-title">concise</span> <span class="hljs-title">mode</span>
#<span class="hljs-title">contract_instance</span> <span class="hljs-operator">=</span> <span class="hljs-title">w3</span>.<span class="hljs-title">eth</span>.<span class="hljs-title"><span class="hljs-keyword">contract</span></span>(<span class="hljs-title"><span class="hljs-built_in">abi</span></span><span class="hljs-operator">=</span><span class="hljs-title"><span class="hljs-built_in">abi</span></span>, <span class="hljs-title"><span class="hljs-keyword">address</span></span><span class="hljs-operator">=</span><span class="hljs-title">contract_address</span>, <span class="hljs-title">ContractFactoryClass</span><span class="hljs-operator">=</span><span class="hljs-title">ConciseContract</span>)
<span class="hljs-title"><span class="hljs-built_in">tx</span></span> <span class="hljs-operator">=</span> <span class="hljs-title">contract_instance</span>.<span class="hljs-title">functions</span>.<span class="hljs-title">greet</span>(<span class="hljs-string">"Hello all  my goody people"</span>).<span class="hljs-title">buildTransaction</span>({<span class="hljs-string">'nonce'</span>: <span class="hljs-title">w3</span>.<span class="hljs-title">eth</span>.<span class="hljs-title">getTransactionCount</span>(<span class="hljs-title">account_address</span>)})
#<span class="hljs-title">Get</span> <span class="hljs-title"><span class="hljs-built_in">tx</span></span> <span class="hljs-title">receipt</span> <span class="hljs-title">to</span> <span class="hljs-title">get</span> <span class="hljs-title"><span class="hljs-keyword">contract</span></span> <span class="hljs-title"><span class="hljs-keyword">address</span></span>
<span class="hljs-title">signed_tx</span> <span class="hljs-operator">=</span> <span class="hljs-title">w3</span>.<span class="hljs-title">eth</span>.<span class="hljs-title">account</span>.<span class="hljs-title">signTransaction</span>(<span class="hljs-title"><span class="hljs-built_in">tx</span></span>, <span class="hljs-title">key</span>)
#<span class="hljs-title">tx_receipt</span> <span class="hljs-operator">=</span> <span class="hljs-title">w3</span>.<span class="hljs-title">eth</span>.<span class="hljs-title">getTransactionReceipt</span>(<span class="hljs-title">tx_hash</span>)
<span class="hljs-title">hash</span><span class="hljs-operator">=</span> <span class="hljs-title">w3</span>.<span class="hljs-title">eth</span>.<span class="hljs-title">sendRawTransaction</span>(<span class="hljs-title">signed_tx</span>.<span class="hljs-title">rawTransaction</span>)
<span class="hljs-title">print</span>(<span class="hljs-title">hash</span>.<span class="hljs-title">hex</span>())
</code></pre><p>这是我所做的：</p><ul><li><p>导入的 web3 库和所有其他必需的模块</p></li><li><p>通过指向 Ropsten Infura 节点启动 web3 提供程序</p></li><li><p>添加了用于签署交易的帐户地址和私钥</p></li><li><p>通过指向 Truffle 编译的工件文件greeter.json的abi和字节码启动合约实例</p></li><li><p>创建 tx 对象以添加问候语“hello all my goody people”并建立交易</p></li><li><p>使用我们的私钥签署交易并在网络上广播。</p></li><li><p>在控制台中记录交易哈希。您可以使用您的交易哈希在 etherscan 上检查交易状态。一旦交易被矿工验证，我们的问候语将被添加到区块链上。您可以使用 getGreeting() 函数检索问候语，如下所述。</p></li></ul><p><strong>3. 从部署的智能合约中读取数据</strong></p><p>在我们的合约中，有一个方法 getGreeting() 可以检索我们在区块链中添加的问候语。我们将使用 web3.py 调用此方法 打开您的 Python IDLE 编辑器并使用以下代码创建一个新文件 read.py。运行 py read.py 读取问候语。</p><pre data-type="codeBlock" text="import json
from web3 importWeb3, HTTPProvider
from web3.contract importConciseContract
# compile your smart contract with truffle first
truffleFile = json.load(open(&apos;./build/contracts/greeter.json&apos;))
abi = truffleFile[&apos;abi&apos;]
bytecode = truffleFile[&apos;bytecode&apos;]
# web3.py instance
w3 = Web3(HTTPProvider(&quot;https://ropsten.infura.io/&lt;ApI Key here&gt;&quot;))
print(w3.isConnected())
contract_address = Web3.toChecksumAddress(&quot;&lt;Deployed Contract Address here&gt;&quot;)
# Instantiate and deploy contract
contract = w3.eth.contract(abi=abi, bytecode=bytecode)
# Contract instance
contract_instance = w3.eth.contract(abi=abi, address=contract_address)
# Contract instance in concise mode
#contract_instance = w3.eth.contract(abi=abi, address=contract_address, ContractFactoryClass=ConciseContract)
# Getters + Setters for web3.eth.contract object ConciseContract
#print(format(contract_instance.getGreeting()))
print(&apos;Contract value: {}&apos;.format(contract_instance.functions.getGreeting().call()))
"><code><span class="hljs-keyword">import</span> <span class="hljs-title">json</span>
<span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">web3</span> <span class="hljs-title">importWeb3</span>, <span class="hljs-title">HTTPProvider</span>
<span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">web3</span>.<span class="hljs-title"><span class="hljs-keyword">contract</span></span> <span class="hljs-title">importConciseContract</span>
# <span class="hljs-title">compile</span> <span class="hljs-title">your</span> <span class="hljs-title">smart</span> <span class="hljs-title"><span class="hljs-keyword">contract</span></span> <span class="hljs-title">with</span> <span class="hljs-title">truffle</span> <span class="hljs-title">first</span>
<span class="hljs-title">truffleFile</span> <span class="hljs-operator">=</span> <span class="hljs-title">json</span>.<span class="hljs-title">load</span>(<span class="hljs-title">open</span>(<span class="hljs-string">'./build/contracts/greeter.json'</span>))
<span class="hljs-title"><span class="hljs-built_in">abi</span></span> <span class="hljs-operator">=</span> <span class="hljs-title">truffleFile</span>[<span class="hljs-string">'abi'</span>]
<span class="hljs-title">bytecode</span> <span class="hljs-operator">=</span> <span class="hljs-title">truffleFile</span>[<span class="hljs-string">'bytecode'</span>]
# <span class="hljs-title">web3</span>.<span class="hljs-title">py</span> <span class="hljs-title">instance</span>
<span class="hljs-title">w3</span> <span class="hljs-operator">=</span> <span class="hljs-title">Web3</span>(<span class="hljs-title">HTTPProvider</span>(<span class="hljs-string">"https://ropsten.infura.io/&#x3C;ApI Key here>"</span>))
<span class="hljs-title">print</span>(<span class="hljs-title">w3</span>.<span class="hljs-title">isConnected</span>())
<span class="hljs-title">contract_address</span> <span class="hljs-operator">=</span> <span class="hljs-title">Web3</span>.<span class="hljs-title">toChecksumAddress</span>(<span class="hljs-string">"&#x3C;Deployed Contract Address here>"</span>)
# <span class="hljs-title">Instantiate</span> <span class="hljs-title">and</span> <span class="hljs-title">deploy</span> <span class="hljs-title"><span class="hljs-keyword">contract</span></span>
<span class="hljs-title"><span class="hljs-keyword">contract</span></span> <span class="hljs-operator">=</span> <span class="hljs-title">w3</span>.<span class="hljs-title">eth</span>.<span class="hljs-title"><span class="hljs-keyword">contract</span></span>(<span class="hljs-title"><span class="hljs-built_in">abi</span></span><span class="hljs-operator">=</span><span class="hljs-title"><span class="hljs-built_in">abi</span></span>, <span class="hljs-title">bytecode</span><span class="hljs-operator">=</span><span class="hljs-title">bytecode</span>)
# <span class="hljs-title">Contract</span> <span class="hljs-title">instance</span>
<span class="hljs-title">contract_instance</span> <span class="hljs-operator">=</span> <span class="hljs-title">w3</span>.<span class="hljs-title">eth</span>.<span class="hljs-title"><span class="hljs-keyword">contract</span></span>(<span class="hljs-title"><span class="hljs-built_in">abi</span></span><span class="hljs-operator">=</span><span class="hljs-title"><span class="hljs-built_in">abi</span></span>, <span class="hljs-title"><span class="hljs-keyword">address</span></span><span class="hljs-operator">=</span><span class="hljs-title">contract_address</span>)
# <span class="hljs-title">Contract</span> <span class="hljs-title">instance</span> <span class="hljs-title">in</span> <span class="hljs-title">concise</span> <span class="hljs-title">mode</span>
#<span class="hljs-title">contract_instance</span> <span class="hljs-operator">=</span> <span class="hljs-title">w3</span>.<span class="hljs-title">eth</span>.<span class="hljs-title"><span class="hljs-keyword">contract</span></span>(<span class="hljs-title"><span class="hljs-built_in">abi</span></span><span class="hljs-operator">=</span><span class="hljs-title"><span class="hljs-built_in">abi</span></span>, <span class="hljs-title"><span class="hljs-keyword">address</span></span><span class="hljs-operator">=</span><span class="hljs-title">contract_address</span>, <span class="hljs-title">ContractFactoryClass</span><span class="hljs-operator">=</span><span class="hljs-title">ConciseContract</span>)
# <span class="hljs-title">Getters</span> <span class="hljs-operator">+</span> <span class="hljs-title">Setters</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">web3</span>.<span class="hljs-title">eth</span>.<span class="hljs-title"><span class="hljs-keyword">contract</span></span> <span class="hljs-title">object</span> <span class="hljs-title">ConciseContract</span>
#<span class="hljs-title">print</span>(<span class="hljs-title">format</span>(<span class="hljs-title">contract_instance</span>.<span class="hljs-title">getGreeting</span>()))
<span class="hljs-title">print</span>(<span class="hljs-string">'Contract value: {}'</span>.<span class="hljs-title">format</span>(<span class="hljs-title">contract_instance</span>.<span class="hljs-title">functions</span>.<span class="hljs-title">getGreeting</span>().<span class="hljs-title">call</span>()))
</code></pre><p>这是我所做的：</p><ul><li><p>导入的 web3 库和所有其他必需的模块</p></li><li><p>通过指向 Ropsten Infura 节点启动 web3 提供程序</p></li><li><p>通过指向 abi 和 contract_Address 创建合约实例（来自上一步）</p></li><li><p>调用 getGreeting() 方法并在控制台打印结果</p></li></ul><p><strong>结论</strong></p><p>您现在应该了解如何使用 web3.py 部署、交互和签署交易。让我们通过查看我使用 web3.py 创建的实时 DApp 并在此处使用 Flask 作为前端服务器来将所有部分组合在一起。它将帮助您更深入地了解使用 Python 进行 DApp 开发。</p>]]></content:encoded>
            <author>quantbang@newsletter.paragraph.com (quantbang)</author>
        </item>
        <item>
            <title><![CDATA[用 Python 筛选收益最优的加密货币]]></title>
            <link>https://paragraph.com/@quantbang/python</link>
            <guid>fi0PtzB05KO7FBU8cRJW</guid>
            <pubDate>Thu, 24 Mar 2022 12:55:15 GMT</pubDate>
            <description><![CDATA[在市值排名前 10 的加密货币中，从纯粹的经济角度来看，你认为自 2017 年以来表现最好的加密货币是哪一种？\ 不管你信不信，币安自己的 BNB 实际上远远超过了其他所有加密货币。我编写了一个脚本来帮助我了解几种加密货币的历史表现，当我决定只加入前 10 名加密货币并看看表现最好的货币是哪个。 在运行脚本之前，我很确定它可能将是 DOGE。所以我坐在这里，等待历史数据下载，以便我的脚本可以绘制一些加密图表。 脚本运行完毕，结果出来了，感谢中本聪，这不是 DOGE。哦，等等，这更有趣——它是 BNB。 自 2017 年以来，BNB 已上涨超过 20,000%。程序能够为你下载历史数据，并分析任意数量的币种。如果您想对任意数量的加密货币的收益百分比进行快速比较分析，这很方便。您所需要的只是一些 Python 知识。 编写加密货币分析工具 该代码也可在 GitHub 上找到。https://github.com/CyberPunkMetalHead/crypto-performance-tracker 首先创建一个文本文件并将其命名为coins.txt。在此文本文件中，放入一些您想要...]]></description>
            <content:encoded><![CDATA[<figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/903e59887e0d094eb5d91d8653a20d70c8f2a60cc8e9ebecb47ba2cb5807ea8a.jpg" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>在市值排名前 10 的加密货币中，从纯粹的经济角度来看，你认为自 2017 年以来表现最好的加密货币是哪一种？\</p><p>不管你信不信，币安自己的 BNB 实际上远远超过了其他所有加密货币。我编写了一个脚本来帮助我了解几种加密货币的历史表现，当我决定只加入前 10 名加密货币并看看表现最好的货币是哪个。</p><p>在运行脚本之前，我很确定它可能将是 DOGE。所以我坐在这里，等待历史数据下载，以便我的脚本可以绘制一些加密图表。</p><p>脚本运行完毕，结果出来了，感谢中本聪，这不是 DOGE。哦，等等，这更有趣——它是 BNB。</p><p>自 2017 年以来，BNB 已上涨超过 20,000%。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/d80e9c6e45169413b900eaae7503018f2c258f807dedc0c0a66223f326ba251c.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>程序能够为你下载历史数据，并分析任意数量的币种。如果您想对任意数量的加密货币的收益百分比进行快速比较分析，这很方便。您所需要的只是一些 Python 知识。</p><p><strong>编写加密货币分析工具</strong></p><p>该代码也可在 GitHub 上找到。</p><pre data-type="codeBlock" text="https://github.com/CyberPunkMetalHead/crypto-performance-tracker
"><code>https://github.com/CyberPunkMetalHead/crypto-performance-tracker
</code></pre><p>首先创建一个文本文件并将其命名为<code>coins.txt</code>。在此文本文件中，放入一些您想要分析的币种名称。它们需要包含配对符号，并且每行必须是 1 个货币，不能有逗号：</p><pre data-type="codeBlock" text="BTCUSDT
ETHUSDT
BNBUSDT
"><code></code></pre><p>创建一个 <code>binancedata.py</code> 文件。我们将使用此文件轮询 Binance API 以获取我们需要的金融数据。由于我们使用的是开放端口，因此不需要 API 密钥和密码。</p><p>让我们导入一些依赖项并定义一个空的 Binance 客户端：</p><pre data-type="codeBlock" text="# needed for the binance API and websockets
from binance.client import Client
import csv
import os
import time
from datetime import date, datetime

client = Client()
"><code># needed <span class="hljs-keyword">for</span> the binance API and websockets
<span class="hljs-keyword">from</span> binance.client <span class="hljs-keyword">import</span> <span class="hljs-title">Client</span>
<span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">csv</span>
<span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">os</span>
<span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">time</span>
<span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">datetime</span> <span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">date</span>, <span class="hljs-title">datetime</span>

<span class="hljs-title">client</span> <span class="hljs-operator">=</span> <span class="hljs-title">Client</span>()
</code></pre><p>现在让我们编写一个函数来从我们的coins.txt文件中打开和读取货币：</p><pre data-type="codeBlock" text="def get_coins():
    with open(&apos;coins.txt&apos;, &apos;r&apos;) as f:
        coins = f.readlines()
        coins = [coin.strip(&apos;\n&apos;) for coin in coins]
    return coins
"><code>def get_coins():
    with open(<span class="hljs-string">'coins.txt'</span>, <span class="hljs-string">'r'</span>) <span class="hljs-keyword">as</span> f:
        coins <span class="hljs-operator">=</span> f.readlines()
        coins <span class="hljs-operator">=</span> [coin.strip(<span class="hljs-string">'\n'</span>) <span class="hljs-keyword">for</span> coin in coins]
    <span class="hljs-keyword">return</span> coins
</code></pre><p>此文件中的最后一个函数将为我们获取历史数据并以 CSV 格式保存：</p><pre data-type="codeBlock" text="def get_historical_data(coin, since, kline_interval):
    &quot;&quot;&quot;
    Args example:
    coin = &apos;BTCUSDT&apos;
    since = &apos;1 Jan 2021&apos;
    kline_interval = Client.KLINE_INTERVAL_1MINUTE
    &quot;&quot;&quot;
    if os.path.isfile(f&apos;data/{coin}_{since}.csv&apos;):
        print(&apos;Datafile already exists, loading file...&apos;)

    else:
        print(f&apos;Fetching historical data for {coin}, this may take a few minutes...&apos;)

        start_time = time.perf_counter()
        data = client.get_historical_klines(coin, kline_interval, since)
        data = [item[0:5] for item in data]

        # field names
        fields = [&apos;timstamp&apos;, &apos;high&apos;, &apos;low&apos;, &apos;open&apos;, &apos;close&apos;]

        # save the data
        with open(f&apos;data/{coin}_{since}.csv&apos;, &apos;w&apos;, newline=&apos;&apos;) as f:

            # using csv.writer method from CSV package
            write = csv.writer(f)

            write.writerow(fields)
            write.writerows(data)

        end_time = time.perf_counter()

        # calculate how long it took to produce the file
        time_elapsed = round(end_time - start_time)

        print(f&apos;Historical data for {coin} saved as {coin}_{since}.csv. Time elapsed: {time_elapsed} seconds&apos;)
    return f&apos;{coin}_{since}.csv&apos;
"><code><span class="hljs-keyword">def</span> <span class="hljs-title function_">get_historical_data</span>(<span class="hljs-params">coin, since, kline_interval</span>):
    <span class="hljs-string">"""
    Args example:
    coin = 'BTCUSDT'
    since = '1 Jan 2021'
    kline_interval = Client.KLINE_INTERVAL_1MINUTE
    """</span>
    <span class="hljs-keyword">if</span> os.path.isfile(<span class="hljs-string">f'data/<span class="hljs-subst">{coin}</span>_<span class="hljs-subst">{since}</span>.csv'</span>):
        <span class="hljs-built_in">print</span>(<span class="hljs-string">'Datafile already exists, loading file...'</span>)

    <span class="hljs-keyword">else</span>:
        <span class="hljs-built_in">print</span>(<span class="hljs-string">f'Fetching historical data for <span class="hljs-subst">{coin}</span>, this may take a few minutes...'</span>)

        start_time = time.perf_counter()
        data = client.get_historical_klines(coin, kline_interval, since)
        data = [item[<span class="hljs-number">0</span>:<span class="hljs-number">5</span>] <span class="hljs-keyword">for</span> item <span class="hljs-keyword">in</span> data]

        <span class="hljs-comment"># field names</span>
        fields = [<span class="hljs-string">'timstamp'</span>, <span class="hljs-string">'high'</span>, <span class="hljs-string">'low'</span>, <span class="hljs-string">'open'</span>, <span class="hljs-string">'close'</span>]

        <span class="hljs-comment"># save the data</span>
        <span class="hljs-keyword">with</span> <span class="hljs-built_in">open</span>(<span class="hljs-string">f'data/<span class="hljs-subst">{coin}</span>_<span class="hljs-subst">{since}</span>.csv'</span>, <span class="hljs-string">'w'</span>, newline=<span class="hljs-string">''</span>) <span class="hljs-keyword">as</span> f:

            <span class="hljs-comment"># using csv.writer method from CSV package</span>
            write = csv.writer(f)

            write.writerow(fields)
            write.writerows(data)

        end_time = time.perf_counter()

        <span class="hljs-comment"># calculate how long it took to produce the file</span>
        time_elapsed = <span class="hljs-built_in">round</span>(end_time - start_time)

        <span class="hljs-built_in">print</span>(<span class="hljs-string">f'Historical data for <span class="hljs-subst">{coin}</span> saved as <span class="hljs-subst">{coin}</span>_<span class="hljs-subst">{since}</span>.csv. Time elapsed: <span class="hljs-subst">{time_elapsed}</span> seconds'</span>)
    <span class="hljs-keyword">return</span> <span class="hljs-string">f'<span class="hljs-subst">{coin}</span>_<span class="hljs-subst">{since}</span>.csv'</span>
</code></pre><p>此函数还将检查文件是否已经存在，如果存在它不会再次下载。该函数接受 3 个参数：<code>coin</code>、<code>since</code> 和 <code>kline_interval</code>。检查函数下方的注释，了解我们将传递给这些参数的正确格式。</p><p>保存文件，现在是创建我们的主要执行文件的时候了，我们将把这个文件的内容导入到其中。</p><p>继续创建一个 <code>main.py</code> 文件并安装以下依赖项：</p><pre data-type="codeBlock" text="
from binancedata import *
import threading


import matplotlib.pyplot as plt
import matplotlib.cbook as cbook

import numpy as np
import pandas as pd

# needed for the binance API and websockets
from binance.client import Client
import csv
import os
import time
from datetime import datetime, date
"><code>
<span class="hljs-keyword">from</span> binancedata <span class="hljs-keyword">import</span> *
<span class="hljs-keyword">import</span> threading


<span class="hljs-keyword">import</span> matplotlib.pyplot <span class="hljs-keyword">as</span> plt
<span class="hljs-keyword">import</span> matplotlib.cbook <span class="hljs-keyword">as</span> cbook

<span class="hljs-keyword">import</span> numpy <span class="hljs-keyword">as</span> np
<span class="hljs-keyword">import</span> pandas <span class="hljs-keyword">as</span> pd

<span class="hljs-comment"># needed for the binance API and websockets</span>
<span class="hljs-keyword">from</span> binance.client <span class="hljs-keyword">import</span> Client
<span class="hljs-keyword">import</span> csv
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> time
<span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime, date
</code></pre><p>让我们开始一些线程。该脚本是为了一次下载多个数据文件，所以为了避免等待一次下载每个历史数据文件，我们将使用线程并下载这些文件，如下所示：</p><pre data-type="codeBlock" text="threads = []
coins = get_coins()
for coin in coins:

    t = threading.Thread(target=get_historical_data, args=(coin, &apos;1 Jan 2017&apos;, Client.KLINE_INTERVAL_1DAY) ) #&apos;get_historical_data(&apos;ETHUSDT&apos;, &apos;1 Jan 2021&apos;, Client.KLINE_INTERVAL_1MINUTE)
    t.start()
    threads.append(t)


[thread.join() for thread in threads]
"><code>threads <span class="hljs-operator">=</span> []
coins <span class="hljs-operator">=</span> get_coins()
<span class="hljs-keyword">for</span> coin in coins:

    t <span class="hljs-operator">=</span> threading.Thread(target<span class="hljs-operator">=</span>get_historical_data, args<span class="hljs-operator">=</span>(coin, <span class="hljs-string">'1 Jan 2017'</span>, Client.KLINE_INTERVAL_1DAY) ) #<span class="hljs-string">'get_historical_data('</span>ETHUSDT<span class="hljs-string">', '</span><span class="hljs-number">1</span> Jan <span class="hljs-number">2021</span><span class="hljs-string">', Client.KLINE_INTERVAL_1MINUTE)
    t.start()
    threads.append(t)


[thread.join() for thread in threads]
</span></code></pre><p>现在我们需要一个函数来返回我们下载的所有数据文件的文件名：</p><pre data-type="codeBlock" text="def get_all_filenames():
    return [get_historical_data(coin, &apos;1 Jan 2017&apos;, Client.KLINE_INTERVAL_1DAY) for coin in coins]
"><code><span class="hljs-keyword">def</span> <span class="hljs-title function_">get_all_filenames</span>():
    <span class="hljs-keyword">return</span> [get_historical_data(coin, <span class="hljs-string">'1 Jan 2017'</span>, <span class="hljs-title class_">Client</span>.<span class="hljs-variable constant_">KLINE_INTERVAL_1DAY</span>) <span class="hljs-keyword">for</span> coin <span class="hljs-keyword">in</span> coins]
</code></pre><p>最后，我们将定义主要函数，我们将在其中绘制这些数据并运行脚本：</p><pre data-type="codeBlock" text="def main():
    historical_data = get_all_filenames()

    for file in historical_data:
        data = pd.read_csv(f&apos;data/{file}&apos;)

        rolling_percentage = data[&apos;close&apos;]
        rolling_percentage = [(item - rolling_percentage[0]) / rolling_percentage[0]*100 for item in rolling_percentage ]

        timestamp = data[&apos;timstamp&apos;]
        timestamp = [datetime.fromtimestamp(item/1000) for item in timestamp]

        plt.legend()
        plt.plot(timestamp, rolling_percentage, label=file)
        plt.xlabel(&quot;Date&quot;)
        plt.ylabel(&quot;% gain&quot;)

    plt.show()

if __name__ == &quot;__main__&quot;:
    main()
"><code>def main():
    historical_data <span class="hljs-operator">=</span> get_all_filenames()

    <span class="hljs-keyword">for</span> file in historical_data:
        data <span class="hljs-operator">=</span> pd.read_csv(f<span class="hljs-string">'data/{file}'</span>)

        rolling_percentage <span class="hljs-operator">=</span> data[<span class="hljs-string">'close'</span>]
        rolling_percentage <span class="hljs-operator">=</span> [(item <span class="hljs-operator">-</span> rolling_percentage[<span class="hljs-number">0</span>]) <span class="hljs-operator">/</span> rolling_percentage[<span class="hljs-number">0</span>]<span class="hljs-operator">*</span><span class="hljs-number">100</span> <span class="hljs-keyword">for</span> item in rolling_percentage ]

        timestamp <span class="hljs-operator">=</span> data[<span class="hljs-string">'timstamp'</span>]
        timestamp <span class="hljs-operator">=</span> [datetime.fromtimestamp(item<span class="hljs-operator">/</span><span class="hljs-number">1000</span>) <span class="hljs-keyword">for</span> item in timestamp]

        plt.legend()
        plt.plot(timestamp, rolling_percentage, label<span class="hljs-operator">=</span>file)
        plt.xlabel(<span class="hljs-string">"Date"</span>)
        plt.ylabel(<span class="hljs-string">"% gain"</span>)

    plt.show()

<span class="hljs-keyword">if</span> __name__ <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"__main__"</span>:
    main()
</code></pre><p>现在剩下要做的就是在脚本目录中创建一个空文件夹并将其命名为 <code>data</code>。大功告成，您现在可以分析您想要的所有代币的历史收益。</p>]]></content:encoded>
            <author>quantbang@newsletter.paragraph.com (quantbang)</author>
        </item>
        <item>
            <title><![CDATA[CCXT：加密货币量化交易神器]]></title>
            <link>https://paragraph.com/@quantbang/ccxt</link>
            <guid>D3qjCxYmevXbZYNsJnXw</guid>
            <pubDate>Thu, 24 Mar 2022 12:54:09 GMT</pubDate>
            <description><![CDATA[CCXT框架 是一个Python/Javascript/PHP的一个交易API框架，对接超过130多个交易所。可用于世界各地的加密货币交易所的连接和交易，以及转账支付处理，可用于存储数据，分析，可视化，指标开发，算法交易，是一个非常容易集成的开箱即用的统一API。 CCXT框架Github地址： https://github.com/ccxt/ccxt 当前功能列表：支持许多交易市场，甚至即将推出的为所有交易提供完整的公共和私人API所有货币，山寨币和标记，价格，订单，交易，代码等…提供用于交叉交换或跨货币分析和套利的可选标准化数据开箱即用的统一的一体化API，非常易于集成适用于Node7.6+，Python2和3，PHP5.4+，Web浏览器认证交易所支持的加密货币交易所 ccxt库目前支持以下131个加密货币交易所和交易API，可以在github查看。上面的列表经常更新，新的加密市场，山寨币交换，错误修复，API端点定期引入和添加。有关详细信息，请参阅手册，如果你没有在上面的列表中找到加密货币交易市场和/或想要添加其他交易所，请通过GitHub或通过电子邮件在此处发布问题来发...]]></description>
            <content:encoded><![CDATA[<figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b1639474fbe611f03bbd4cd4fb8001b64931531bb6418f8502ab95649df26ac2.jpg" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>CCXT框架 是一个Python/Javascript/PHP的一个交易API框架，对接超过130多个交易所。可用于世界各地的加密货币交易所的连接和交易，以及转账支付处理，可用于存储数据，分析，可视化，指标开发，算法交易，是一个非常容易集成的开箱即用的统一API。</p><p>CCXT框架Github地址：</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/ccxt/ccxt">https://github.com/ccxt/ccxt</a></p><p>当前功能列表：</p><ul><li><p>支持许多交易市场，甚至即将推出的</p></li><li><p>为所有交易提供完整的公共和私人API</p></li><li><p>所有货币，山寨币和标记，价格，订单，交易，代码等…</p></li><li><p>提供用于交叉交换或跨货币分析和套利的可选标准化数据</p></li><li><p>开箱即用的统一的一体化API，非常易于集成</p></li><li><p>适用于Node7.6+，Python2和3，PHP5.4+，Web浏览器</p></li></ul><p><strong>认证交易所</strong></p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/ca293e0fe09575a8a8dfc08982bca91a73fe55e2d15ba011179927e883e2e08c.jpg" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><strong>支持的加密货币交易所</strong></p><p>ccxt库目前支持以下131个加密货币交易所和交易API，可以在github查看。上面的列表经常更新，新的加密市场，山寨币交换，错误修复，API端点定期引入和添加。有关详细信息，请参阅手册，如果你没有在上面的列表中找到加密货币交易市场和/或想要添加其他交易所，请通过GitHub或通过电子邮件在此处发布问题来发布或向我们发送链接。</p><p>该库受到MIT许可，这意味着任何开发人员都可以完全免费构建商业和开源软件，但使用它需要你自担风险且无需担保。</p><p><strong>安装</strong></p><p>安装ccxt库的最简单方法是使用内置包管理器：</p><ul><li><p>NPM中的ccxt（JavaScript/Node v7.6+）</p></li><li><p>PyPI中的ccxt（Python 2和3.5.3+）</p></li><li><p>Packagist/Composer中的ccxt（PHP 5.4+）</p></li></ul><p>该库作为一体化模块实现提供，具有最小的依赖性和要求：</p><ul><li><p>js/在JavaScript中</p></li><li><p>python/在Python中（从JS生成）</p></li><li><p>PHP/ PHP（从JS生成）</p></li></ul><p>也可以从ccxt GitHub存储库将其克隆到项目目录中：</p><pre data-type="codeBlock" text="git clone https://github.com/ccxt/ccxt.git
"><code>git <span class="hljs-built_in">clone</span> https://github.com/ccxt/ccxt.git
</code></pre><p>将此库安装到代码中的另一种方法是将单个文件手动复制到工作目录中，并使用适合你环境的语言扩展名。</p><p><strong>安装</strong></p><pre data-type="codeBlock" text="pip install ccxt
"><code></code></pre><p><strong>常用接口</strong></p><pre data-type="codeBlock" text="# 初始化交易所
binance_exchange = ccxt.binance({
&apos;timeout&apos;: 15000,
&apos;enableRateLimit&apos;: True
})
# 获取单个交易对ticker数据
binance_exchange.fetchTicker(symbol)
# 获取多个交易对ticker数据
tickers_data = binance_exchange.fetchTickers([&apos;BTC/USDT&apos;, &apos;ETH/USDT&apos;])
# 交易委托账本数据获取
binance_exchange.fetch_order_book(symbol)
# K线数据数据获取
binance_exchange.fetch_ohlcv(symbol, timeframe=&apos;1d&apos;)
"><code># 初始化交易所
binance_exchange <span class="hljs-operator">=</span> ccxt.binance({
<span class="hljs-string">'timeout'</span>: <span class="hljs-number">15000</span>,
<span class="hljs-string">'enableRateLimit'</span>: True
})
# 获取单个交易对ticker数据
binance_exchange.fetchTicker(symbol)
# 获取多个交易对ticker数据
tickers_data <span class="hljs-operator">=</span> binance_exchange.fetchTickers([<span class="hljs-string">'BTC/USDT'</span>, <span class="hljs-string">'ETH/USDT'</span>])
# 交易委托账本数据获取
binance_exchange.fetch_order_book(symbol)
# K线数据数据获取
binance_exchange.fetch_ohlcv(symbol, timeframe<span class="hljs-operator">=</span><span class="hljs-string">'1d'</span>)
</code></pre><p><strong>使用</strong></p><p>CCXT里面的交易所都集成来自Exchange的基类，然后每个交易所实现了一些统一的api接口，另外也实现自己交易所特有的api方法。统一的api方法分为不需要权限就能访问的，比如loadmarkets(加载市场的交易对)、 fetchticker（获取ticker）等，需要权限访问的方法如fetchbalance(获取张账户资金)、createorder(生成订单)等。CCXT的方法名称有两种实现方式，一种是驼峰法, 另外一种是下划线命名法，在python中，推荐使用下划线方法来调用。</p><p><strong>公共API包括</strong></p><ul><li><p>市场数据</p></li><li><p>交易对</p></li><li><p>交易手续费</p></li><li><p>订单薄/深度数据</p></li><li><p>交易历史</p></li><li><p>行情/Tickers</p></li><li><p>用以制图的 OHLC(V)/K线</p></li><li><p>其他公共接口</p></li></ul><p><strong>私有API包括</strong></p><ul><li><p>管理个人账户信息</p></li><li><p>查询账户余额</p></li><li><p>通过市价单和限价单进行交易</p></li><li><p>存入和提取法币和加密货币</p></li><li><p>查询个人订单</p></li><li><p>获取交易明细/历史</p></li><li><p>在账户之间转移资金</p></li><li><p>使用商业服务</p></li></ul><p>此库为所有交换实现完整的公共和私有REST API。即将推出JavaScript，PHP，Python和其他语言的WebSocket和FIX实现。</p><p>ccxt库支持 <code>camelcase</code>表示法（在JavaScript中是首选）和下划线表示法（在Python和PHP中首选），因此所有方法都可以用任何语言的符号或编码方式调用。</p><pre data-type="codeBlock" text="// both of these notations work in JavaScript/Python/PHP
exchange.methodName ()  // camelcase pseudocode
exchange.method_name () // underscore pseudocode
"><code><span class="hljs-comment">// both of these notations work in JavaScript/Python/PHP</span>
exchange.methodName ()  <span class="hljs-comment">// camelcase pseudocode</span>
exchange.method_name () <span class="hljs-comment">// underscore pseudocode</span>
</code></pre><p><strong>Python</strong></p><pre data-type="codeBlock" text="# coding=utf-8
import ccxt
hitbtc = ccxt.hitbtc({&apos;verbose&apos;: True})
bitmex = ccxt.bitmex()
huobi  = ccxt.huobi()
exmo   = ccxt.exmo({
&apos;apiKey&apos;: &apos;YOUR_PUBLIC_API_KEY&apos;,
&apos;secret&apos;: &apos;YOUR_SECRET_PRIVATE_KEY&apos;,
})
kraken = ccxt.kraken({
&apos;apiKey&apos;: &apos;YOUR_PUBLIC_API_KEY&apos;,
&apos;secret&apos;: &apos;YOUR_SECRET_PRIVATE_KEY&apos;,
})
exchange_id = &apos;binance&apos;
exchange_class = getattr(ccxt, exchange_id)
exchange = exchange_class({
&apos;apiKey&apos;: &apos;YOUR_API_KEY&apos;,
&apos;secret&apos;: &apos;YOUR_SECRET&apos;,
&apos;timeout&apos;: 30000,
&apos;enableRateLimit&apos;: True,
})
hitbtc_markets = hitbtc.load_markets()
print(hitbtc.id, hitbtc_markets)
print(bitmex.id, bitmex.load_markets())
print(huobi.id, huobi.load_markets())
print(hitbtc.fetch_order_book(hitbtc.symbols[0]))
print(bitmex.fetch_ticker(&apos;BTC/USD&apos;))
print(huobi.fetch_trades(&apos;LTC/CNY&apos;))
print(exmo.fetch_balance())
# sell one ฿ for market price and receive $ right now
print(exmo.id, exmo.create_market_sell_order(&apos;BTC/USD&apos;, 1))
# limit buy BTC/EUR, you pay €2500 and receive ฿1  when the order is closed
print(exmo.id, exmo.create_limit_buy_order(&apos;BTC/EUR&apos;, 1, 2500.00))
# pass/redefine custom exchange-specific order params: type, amount, price, flags, etc...
kraken.create_market_buy_order(&apos;BTC/USD&apos;, 1, {&apos;trading_agreement&apos;: &apos;agree&apos;})
"><code><span class="hljs-comment"># coding=utf-8</span>
<span class="hljs-keyword">import</span> ccxt
hitbtc = ccxt.hitbtc({<span class="hljs-string">'verbose'</span>: <span class="hljs-literal">True</span>})
bitmex = ccxt.bitmex()
huobi  = ccxt.huobi()
exmo   = ccxt.exmo({
<span class="hljs-string">'apiKey'</span>: <span class="hljs-string">'YOUR_PUBLIC_API_KEY'</span>,
<span class="hljs-string">'secret'</span>: <span class="hljs-string">'YOUR_SECRET_PRIVATE_KEY'</span>,
})
kraken = ccxt.kraken({
<span class="hljs-string">'apiKey'</span>: <span class="hljs-string">'YOUR_PUBLIC_API_KEY'</span>,
<span class="hljs-string">'secret'</span>: <span class="hljs-string">'YOUR_SECRET_PRIVATE_KEY'</span>,
})
exchange_id = <span class="hljs-string">'binance'</span>
exchange_class = <span class="hljs-built_in">getattr</span>(ccxt, exchange_id)
exchange = exchange_class({
<span class="hljs-string">'apiKey'</span>: <span class="hljs-string">'YOUR_API_KEY'</span>,
<span class="hljs-string">'secret'</span>: <span class="hljs-string">'YOUR_SECRET'</span>,
<span class="hljs-string">'timeout'</span>: <span class="hljs-number">30000</span>,
<span class="hljs-string">'enableRateLimit'</span>: <span class="hljs-literal">True</span>,
})
hitbtc_markets = hitbtc.load_markets()
<span class="hljs-built_in">print</span>(hitbtc.<span class="hljs-built_in">id</span>, hitbtc_markets)
<span class="hljs-built_in">print</span>(bitmex.<span class="hljs-built_in">id</span>, bitmex.load_markets())
<span class="hljs-built_in">print</span>(huobi.<span class="hljs-built_in">id</span>, huobi.load_markets())
<span class="hljs-built_in">print</span>(hitbtc.fetch_order_book(hitbtc.symbols[<span class="hljs-number">0</span>]))
<span class="hljs-built_in">print</span>(bitmex.fetch_ticker(<span class="hljs-string">'BTC/USD'</span>))
<span class="hljs-built_in">print</span>(huobi.fetch_trades(<span class="hljs-string">'LTC/CNY'</span>))
<span class="hljs-built_in">print</span>(exmo.fetch_balance())
<span class="hljs-comment"># sell one ฿ for market price and receive $ right now</span>
<span class="hljs-built_in">print</span>(exmo.<span class="hljs-built_in">id</span>, exmo.create_market_sell_order(<span class="hljs-string">'BTC/USD'</span>, <span class="hljs-number">1</span>))
<span class="hljs-comment"># limit buy BTC/EUR, you pay €2500 and receive ฿1  when the order is closed</span>
<span class="hljs-built_in">print</span>(exmo.<span class="hljs-built_in">id</span>, exmo.create_limit_buy_order(<span class="hljs-string">'BTC/EUR'</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2500.00</span>))
<span class="hljs-comment"># pass/redefine custom exchange-specific order params: type, amount, price, flags, etc...</span>
kraken.create_market_buy_order(<span class="hljs-string">'BTC/USD'</span>, <span class="hljs-number">1</span>, {<span class="hljs-string">'trading_agreement'</span>: <span class="hljs-string">'agree'</span>})
</code></pre>]]></content:encoded>
            <author>quantbang@newsletter.paragraph.com (quantbang)</author>
        </item>
        <item>
            <title><![CDATA[Python 通过 requests 调用 Binance API]]></title>
            <link>https://paragraph.com/@quantbang/python-requests-binance-api</link>
            <guid>nJiCy1JyQwPRtWHvBRoS</guid>
            <pubDate>Thu, 24 Mar 2022 12:52:08 GMT</pubDate>
            <description><![CDATA[本文使用 requests 库来调用 Binance REST API。如果你更喜欢 API 库，你可以尝试 python-binance。 Binance REST API 有 3 种类型的安全接口：NONE：可以自由访问USER_STREAM 和 MARKET_DATA：需要 APIKeyTRADE 和 USER_DATA：需要 APIKey 和签名获取币安 APIKey 和 SecretKey BinanceDashboard -> Settings -> APIManagementhttps://www.binance.com/en/usercenter/settings/api-management 创建带有标签的 APIKey（用于标识密钥用途和用途的名称）：生成 APIKey 和 SecretKey。 注意：请立即复制密钥，因为您无法在稍后阶段再次检索密钥，除非您创建另一个新密钥。 您可以设置 API 限制：只读、启用交易、启用提款。 您可以限制某些特定 IP 访问 API。import time import json import hmac import has...]]></description>
            <content:encoded><![CDATA[<figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e98e9efb244d9f20480e90a3df90aa750de2bf7d4019a46a1f6ff111d9042c90.jpg" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>本文使用 requests 库来调用 Binance REST API。如果你更喜欢 API 库，你可以尝试 <code>python-binance</code>。</p><p><strong>Binance REST API</strong></p><p>有 3 种类型的安全接口：</p><ul><li><p><code>NONE</code>：可以自由访问</p></li><li><p><code>USER_STREAM</code> 和 <code>MARKET_DATA</code>：需要 <code>APIKey</code></p></li><li><p><code>TRADE</code> 和 <code>USER_DATA</code>：需要 <code>APIKey</code> 和签名</p></li></ul><p>获取币安 APIKey 和 SecretKey</p><p><code>BinanceDashboard</code> -&gt; <code>Settings</code> -&gt; <code>APIManagement</code></p><pre data-type="codeBlock" text="https://www.binance.com/en/usercenter/settings/api-management
"><code><span class="hljs-symbol">https:</span>/<span class="hljs-regexp">/www.binance.com/en</span><span class="hljs-regexp">/usercenter/settings</span><span class="hljs-regexp">/api-management
</span></code></pre><p>创建带有标签的 <code>APIKey</code>（用于标识密钥用途和用途的名称）：生成 <code>APIKey</code> 和 <code>SecretKey</code>。</p><p>注意：请立即复制密钥，因为您无法在稍后阶段再次检索密钥，除非您创建另一个新密钥。</p><p>您可以设置 API 限制：只读、启用交易、启用提款。</p><p>您可以限制某些特定 IP 访问 API。</p><pre data-type="codeBlock" text="import time
import json
import hmac
import hashlib
import requests
from urllib.parse import urljoin, urlencode
API_KEY = &apos;UIGu...&apos;
SECRET_KEY = &apos;VyX...&apos;
BASE_URL = &apos;https://api.binance.com&apos;
headers = {
&apos;X-MBX-APIKEY&apos;: API_KEY
}
"><code><span class="hljs-keyword">import</span> <span class="hljs-title">time</span>
<span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">json</span>
<span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">hmac</span>
<span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">hashlib</span>
<span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">requests</span>
<span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">urllib</span>.<span class="hljs-title">parse</span> <span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">urljoin</span>, <span class="hljs-title">urlencode</span>
<span class="hljs-title">API_KEY</span> <span class="hljs-operator">=</span> <span class="hljs-string">'UIGu...'</span>
<span class="hljs-title">SECRET_KEY</span> <span class="hljs-operator">=</span> <span class="hljs-string">'VyX...'</span>
<span class="hljs-title">BASE_URL</span> <span class="hljs-operator">=</span> <span class="hljs-string">'https://api.binance.com'</span>
<span class="hljs-title">headers</span> <span class="hljs-operator">=</span> {
<span class="hljs-string">'X-MBX-APIKEY'</span>: <span class="hljs-title">API_KEY</span>
}
</code></pre><pre data-type="codeBlock" text="classBinanceException(Exception):
def __init__(self, status_code, data):
self.status_code = status_code
if data:
self.code = data[&apos;code&apos;]
self.msg = data[&apos;msg&apos;]
else:
self.code = None
self.msg = None
        message = f&quot;{status_code} [{self.code}] {self.msg}&quot;
# Python 2.x
# super(BinanceException, self).__init__(message)
super().__init__(message)
"><code>classBinanceException(Exception):
def __init__(<span class="hljs-built_in">self</span>, status_code, data):
<span class="hljs-built_in">self</span>.status_code <span class="hljs-operator">=</span> status_code
<span class="hljs-keyword">if</span> data:
<span class="hljs-built_in">self</span>.<span class="hljs-built_in">code</span> <span class="hljs-operator">=</span> data[<span class="hljs-string">'code'</span>]
<span class="hljs-built_in">self</span>.msg <span class="hljs-operator">=</span> data[<span class="hljs-string">'msg'</span>]
<span class="hljs-keyword">else</span>:
<span class="hljs-built_in">self</span>.<span class="hljs-built_in">code</span> <span class="hljs-operator">=</span> None
<span class="hljs-built_in">self</span>.msg <span class="hljs-operator">=</span> None
        message <span class="hljs-operator">=</span> f<span class="hljs-string">"{status_code} [{self.code}] {self.msg}"</span>
# Python <span class="hljs-number">2</span>.x
# <span class="hljs-built_in">super</span>(BinanceException, <span class="hljs-built_in">self</span>).__init__(message)
<span class="hljs-built_in">super</span>().__init__(message)
</code></pre><p><strong>服务器时间</strong></p><pre data-type="codeBlock" text="PATH =  &apos;/api/v1/time&apos;
params= None
timestamp = int(time.time() * 1000)
url = urljoin(BASE_URL, PATH)
r = requests.get(url, params=params)
if r.status_code == 200:
# print(json.dumps(r.json(), indent=2))
    data = r.json()
print(f&quot;diff={timestamp - data[&apos;serverTime&apos;]}ms&quot;)
else:
raiseBinanceException(status_code=r.status_code, data=r.json())
"><code>PATH <span class="hljs-operator">=</span>  <span class="hljs-string">'/api/v1/time'</span>
params<span class="hljs-operator">=</span> None
timestamp <span class="hljs-operator">=</span> <span class="hljs-keyword">int</span>(time.time() <span class="hljs-operator">*</span> <span class="hljs-number">1000</span>)
url <span class="hljs-operator">=</span> urljoin(BASE_URL, PATH)
r <span class="hljs-operator">=</span> requests.get(url, params<span class="hljs-operator">=</span>params)
<span class="hljs-keyword">if</span> r.status_code <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">200</span>:
# print(json.dumps(r.json(), indent<span class="hljs-operator">=</span><span class="hljs-number">2</span>))
    data <span class="hljs-operator">=</span> r.json()
print(f<span class="hljs-string">"diff={timestamp - data['serverTime']}ms"</span>)
<span class="hljs-keyword">else</span>:
raiseBinanceException(status_code<span class="hljs-operator">=</span>r.status_code, data<span class="hljs-operator">=</span>r.json())
</code></pre><p><strong>获取价格</strong></p><pre data-type="codeBlock" text="PATH = &apos;/api/v3/ticker/price&apos;
params= {
&apos;symbol&apos;: &apos;BTCUSDT&apos;
}
url = urljoin(BASE_URL, PATH)
r = requests.get(url, headers=headers, params=params)
if r.status_code == 200:
print(json.dumps(r.json(), indent=2))
else:
raiseBinanceException(status_code=r.status_code, data=r.json())
"><code>PATH <span class="hljs-operator">=</span> <span class="hljs-string">'/api/v3/ticker/price'</span>
params<span class="hljs-operator">=</span> {
<span class="hljs-string">'symbol'</span>: <span class="hljs-string">'BTCUSDT'</span>
}
url <span class="hljs-operator">=</span> urljoin(BASE_URL, PATH)
r <span class="hljs-operator">=</span> requests.get(url, headers<span class="hljs-operator">=</span>headers, params<span class="hljs-operator">=</span>params)
<span class="hljs-keyword">if</span> r.status_code <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">200</span>:
print(json.dumps(r.json(), indent<span class="hljs-operator">=</span><span class="hljs-number">2</span>))
<span class="hljs-keyword">else</span>:
raiseBinanceException(status_code<span class="hljs-operator">=</span>r.status_code, data<span class="hljs-operator">=</span>r.json())
</code></pre><p>输出：</p><pre data-type="codeBlock" text="{
&quot;symbol&quot;: &quot;BTCUSDT&quot;,
&quot;price&quot;: &quot;10261.03000000&quot;
}
"><code><span class="hljs-punctuation">{</span>
<span class="hljs-attr">"symbol"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"BTCUSDT"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"price"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"10261.03000000"</span>
<span class="hljs-punctuation">}</span>
</code></pre><p><strong>获取订单簿</strong></p><pre data-type="codeBlock" text="PATH = &apos;/api/v1/depth&apos;
params= {
&apos;symbol&apos;: &apos;BTCUSDT&apos;,
&apos;limit&apos;: 5
}
url = urljoin(BASE_URL, PATH)
r = requests.get(url, headers=headers, params=params)
if r.status_code == 200:
print(json.dumps(r.json(), indent=2))
else:
raiseBinanceException(status_code=r.status_code, data=r.json())
"><code>PATH <span class="hljs-operator">=</span> <span class="hljs-string">'/api/v1/depth'</span>
params<span class="hljs-operator">=</span> {
<span class="hljs-string">'symbol'</span>: <span class="hljs-string">'BTCUSDT'</span>,
<span class="hljs-string">'limit'</span>: <span class="hljs-number">5</span>
}
url <span class="hljs-operator">=</span> urljoin(BASE_URL, PATH)
r <span class="hljs-operator">=</span> requests.get(url, headers<span class="hljs-operator">=</span>headers, params<span class="hljs-operator">=</span>params)
<span class="hljs-keyword">if</span> r.status_code <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">200</span>:
print(json.dumps(r.json(), indent<span class="hljs-operator">=</span><span class="hljs-number">2</span>))
<span class="hljs-keyword">else</span>:
raiseBinanceException(status_code<span class="hljs-operator">=</span>r.status_code, data<span class="hljs-operator">=</span>r.json())
</code></pre><p>输出：</p><pre data-type="codeBlock" text="{
&quot;lastUpdateId&quot;: 784184836,
&quot;bids&quot;: [
[
&quot;10229.67000000&quot;,
&quot;0.01954500&quot;
],
[
&quot;10229.58000000&quot;,
&quot;6.90000000&quot;
],
[
&quot;10229.56000000&quot;,
&quot;0.33099600&quot;
],
[
&quot;10228.54000000&quot;,
&quot;0.02600900&quot;
],
[
&quot;10227.71000000&quot;,
&quot;0.50000000&quot;
]
],
&quot;asks&quot;: [
[
&quot;10232.59000000&quot;,
&quot;0.01703200&quot;
],
[
&quot;10232.60000000&quot;,
&quot;3.05388400&quot;
],
[
&quot;10232.63000000&quot;,
&quot;0.25000000&quot;
],
[
&quot;10235.07000000&quot;,
&quot;0.15200000&quot;
],
[
&quot;10236.35000000&quot;,
&quot;0.25000000&quot;
]
]
}
"><code>{
"<span class="hljs-selector-tag">lastUpdateId</span>": <span class="hljs-number">784184836</span>,
"<span class="hljs-selector-tag">bids</span>": <span class="hljs-selector-attr">[[<span class="hljs-string">"10229.67000000"</span>,<span class="hljs-string">"0.01954500"</span>]</span>,
<span class="hljs-selector-attr">[<span class="hljs-string">"10229.58000000"</span>,<span class="hljs-string">"6.90000000"</span>]</span>,
<span class="hljs-selector-attr">[<span class="hljs-string">"10229.56000000"</span>,<span class="hljs-string">"0.33099600"</span>]</span>,
<span class="hljs-selector-attr">[<span class="hljs-string">"10228.54000000"</span>,<span class="hljs-string">"0.02600900"</span>]</span>,
<span class="hljs-selector-attr">[<span class="hljs-string">"10227.71000000"</span>,<span class="hljs-string">"0.50000000"</span>]</span>
],
"<span class="hljs-selector-tag">asks</span>": <span class="hljs-selector-attr">[[<span class="hljs-string">"10232.59000000"</span>,<span class="hljs-string">"0.01703200"</span>]</span>,
<span class="hljs-selector-attr">[<span class="hljs-string">"10232.60000000"</span>,<span class="hljs-string">"3.05388400"</span>]</span>,
<span class="hljs-selector-attr">[<span class="hljs-string">"10232.63000000"</span>,<span class="hljs-string">"0.25000000"</span>]</span>,
<span class="hljs-selector-attr">[<span class="hljs-string">"10235.07000000"</span>,<span class="hljs-string">"0.15200000"</span>]</span>,
<span class="hljs-selector-attr">[<span class="hljs-string">"10236.35000000"</span>,<span class="hljs-string">"0.25000000"</span>]</span>
]
}
</code></pre><p><strong>创建订单</strong></p><pre data-type="codeBlock" text="PATH = &apos;/api/v3/order&apos;
timestamp = int(time.time() * 1000)
params= {
&apos;symbol&apos;: &apos;ETHUSDT&apos;,
&apos;side&apos;: &apos;SELL&apos;,
&apos;type&apos;: &apos;LIMIT&apos;,
&apos;timeInForce&apos;: &apos;GTC&apos;,
&apos;quantity&apos;: 0.1,
&apos;price&apos;: 500.0,
&apos;recvWindow&apos;: 5000,
&apos;timestamp&apos;: timestamp
}
query_string = urlencode(params)
params[&apos;signature&apos;] = hmac.new(SECRET_KEY.encode(&apos;utf-8&apos;), query_string.encode(&apos;utf-8&apos;), hashlib.sha256).hexdigest()
url = urljoin(BASE_URL, PATH)
r = requests.post(url, headers=headers, params=params)
if r.status_code == 200:
    data = r.json()
print(json.dumps(data, indent=2))
else:
raiseBinanceException(status_code=r.status_code, data=r.json())
"><code>PATH <span class="hljs-operator">=</span> <span class="hljs-string">'/api/v3/order'</span>
timestamp <span class="hljs-operator">=</span> <span class="hljs-keyword">int</span>(time.time() <span class="hljs-operator">*</span> <span class="hljs-number">1000</span>)
params<span class="hljs-operator">=</span> {
<span class="hljs-string">'symbol'</span>: <span class="hljs-string">'ETHUSDT'</span>,
<span class="hljs-string">'side'</span>: <span class="hljs-string">'SELL'</span>,
<span class="hljs-string">'type'</span>: <span class="hljs-string">'LIMIT'</span>,
<span class="hljs-string">'timeInForce'</span>: <span class="hljs-string">'GTC'</span>,
<span class="hljs-string">'quantity'</span>: <span class="hljs-number">0</span><span class="hljs-number">.1</span>,
<span class="hljs-string">'price'</span>: <span class="hljs-number">500.0</span>,
<span class="hljs-string">'recvWindow'</span>: <span class="hljs-number">5000</span>,
<span class="hljs-string">'timestamp'</span>: timestamp
}
query_string <span class="hljs-operator">=</span> urlencode(params)
params[<span class="hljs-string">'signature'</span>] <span class="hljs-operator">=</span> hmac.new(SECRET_KEY.encode(<span class="hljs-string">'utf-8'</span>), query_string.encode(<span class="hljs-string">'utf-8'</span>), hashlib.sha256).hexdigest()
url <span class="hljs-operator">=</span> urljoin(BASE_URL, PATH)
r <span class="hljs-operator">=</span> requests.post(url, headers<span class="hljs-operator">=</span>headers, params<span class="hljs-operator">=</span>params)
<span class="hljs-keyword">if</span> r.status_code <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">200</span>:
    data <span class="hljs-operator">=</span> r.json()
print(json.dumps(data, indent<span class="hljs-operator">=</span><span class="hljs-number">2</span>))
<span class="hljs-keyword">else</span>:
raiseBinanceException(status_code<span class="hljs-operator">=</span>r.status_code, data<span class="hljs-operator">=</span>r.json())
</code></pre><pre data-type="codeBlock" text="{
&quot;symbol&quot;: &quot;ETHUSDT&quot;,
&quot;orderId&quot;: 336683281,
&quot;clientOrderId&quot;: &quot;IVGyfNu88LhRnpZFa56JA4&quot;,
&quot;transactTime&quot;: 1562252912748,
&quot;price&quot;: &quot;500.00000000&quot;,
&quot;origQty&quot;: &quot;0.10000000&quot;,
&quot;executedQty&quot;: &quot;0.00000000&quot;,
&quot;cummulativeQuoteQty&quot;: &quot;0.00000000&quot;,
&quot;status&quot;: &quot;NEW&quot;,
&quot;timeInForce&quot;: &quot;GTC&quot;,
&quot;type&quot;: &quot;LIMIT&quot;,
&quot;side&quot;: &quot;SELL&quot;,
&quot;fills&quot;: [
{
&quot;price&quot;: &quot;500.00000000&quot;,
&quot;qty&quot;: &quot;0.050000000&quot;,
&quot;commission&quot;: &quot;1.00000000&quot;,
&quot;commissionAsset&quot;: &quot;USDT&quot;
},
{
&quot;price&quot;: &quot;500.00000000&quot;,
&quot;qty&quot;: &quot;0.03000000&quot;,
&quot;commission&quot;: &quot;0.50000000&quot;,
&quot;commissionAsset&quot;: &quot;USDT&quot;
},
]
}
"><code><span class="hljs-punctuation">{</span>
<span class="hljs-attr">"symbol"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"ETHUSDT"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"orderId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">336683281</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"clientOrderId"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"IVGyfNu88LhRnpZFa56JA4"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"transactTime"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1562252912748</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"price"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"500.00000000"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"origQty"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0.10000000"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"executedQty"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0.00000000"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"cummulativeQuoteQty"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0.00000000"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"status"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"NEW"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"timeInForce"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"GTC"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"LIMIT"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"side"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"SELL"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"fills"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span>
<span class="hljs-punctuation">{</span>
<span class="hljs-attr">"price"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"500.00000000"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"qty"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0.050000000"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"commission"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"1.00000000"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"commissionAsset"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"USDT"</span>
<span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
<span class="hljs-punctuation">{</span>
<span class="hljs-attr">"price"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"500.00000000"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"qty"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0.03000000"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"commission"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0.50000000"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"commissionAsset"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"USDT"</span>
<span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
<span class="hljs-punctuation">]</span>
<span class="hljs-punctuation">}</span>
</code></pre><p><strong>获取订单</strong></p><pre data-type="codeBlock" text="PATH = &apos;/api/v3/order&apos;
timestamp = int(time.time() * 1000)
params= {
&apos;symbol&apos;: &apos;ETHUSDT&apos;,
&apos;orderId&apos;: &apos;336683281&apos;,
&apos;recvWindow&apos;: 5000,
&apos;timestamp&apos;: timestamp
}
query_string = urlencode(params)
params[&apos;signature&apos;] = hmac.new(SECRET_KEY.encode(&apos;utf-8&apos;), query_string.encode(&apos;utf-8&apos;), hashlib.sha256).hexdigest()
url = urljoin(BASE_URL, PATH)
r = requests.get(url, headers=headers, params=params)
if r.status_code == 200:
    data = r.json()
print(json.dumps(data, indent=2))
else:
raiseBinanceException(status_code=r.status_code, data=r.json())
"><code>PATH <span class="hljs-operator">=</span> <span class="hljs-string">'/api/v3/order'</span>
timestamp <span class="hljs-operator">=</span> <span class="hljs-keyword">int</span>(time.time() <span class="hljs-operator">*</span> <span class="hljs-number">1000</span>)
params<span class="hljs-operator">=</span> {
<span class="hljs-string">'symbol'</span>: <span class="hljs-string">'ETHUSDT'</span>,
<span class="hljs-string">'orderId'</span>: <span class="hljs-string">'336683281'</span>,
<span class="hljs-string">'recvWindow'</span>: <span class="hljs-number">5000</span>,
<span class="hljs-string">'timestamp'</span>: timestamp
}
query_string <span class="hljs-operator">=</span> urlencode(params)
params[<span class="hljs-string">'signature'</span>] <span class="hljs-operator">=</span> hmac.new(SECRET_KEY.encode(<span class="hljs-string">'utf-8'</span>), query_string.encode(<span class="hljs-string">'utf-8'</span>), hashlib.sha256).hexdigest()
url <span class="hljs-operator">=</span> urljoin(BASE_URL, PATH)
r <span class="hljs-operator">=</span> requests.get(url, headers<span class="hljs-operator">=</span>headers, params<span class="hljs-operator">=</span>params)
<span class="hljs-keyword">if</span> r.status_code <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">200</span>:
    data <span class="hljs-operator">=</span> r.json()
print(json.dumps(data, indent<span class="hljs-operator">=</span><span class="hljs-number">2</span>))
<span class="hljs-keyword">else</span>:
raiseBinanceException(status_code<span class="hljs-operator">=</span>r.status_code, data<span class="hljs-operator">=</span>r.json())
</code></pre><pre data-type="codeBlock" text="{
&quot;symbol&quot;: &quot;ETHUSDT&quot;,
&quot;orderId&quot;: 336683281,
&quot;clientOrderId&quot;: &quot;IVGyfNu88LhRnpZFa56JA4&quot;,
&quot;price&quot;: &quot;500.00000000&quot;,
&quot;origQty&quot;: &quot;0.10000000&quot;,
&quot;executedQty&quot;: &quot;0.00000000&quot;,
&quot;cummulativeQuoteQty&quot;: &quot;0.00000000&quot;,
&quot;status&quot;: &quot;NEW&quot;,
&quot;timeInForce&quot;: &quot;GTC&quot;,
&quot;type&quot;: &quot;LIMIT&quot;,
&quot;side&quot;: &quot;SELL&quot;,
&quot;stopPrice&quot;: &quot;0.00000000&quot;,
&quot;icebergQty&quot;: &quot;0.00000000&quot;,
&quot;time&quot;: 1562252912748,
&quot;updateTime&quot;: 1562252912748,
&quot;isWorking&quot;: true
}
"><code><span class="hljs-punctuation">{</span>
<span class="hljs-attr">"symbol"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"ETHUSDT"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"orderId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">336683281</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"clientOrderId"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"IVGyfNu88LhRnpZFa56JA4"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"price"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"500.00000000"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"origQty"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0.10000000"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"executedQty"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0.00000000"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"cummulativeQuoteQty"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0.00000000"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"status"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"NEW"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"timeInForce"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"GTC"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"LIMIT"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"side"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"SELL"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"stopPrice"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0.00000000"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"icebergQty"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0.00000000"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"time"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1562252912748</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"updateTime"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1562252912748</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"isWorking"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span>
<span class="hljs-punctuation">}</span>
</code></pre><p><strong>删除订单</strong></p><pre data-type="codeBlock" text="PATH = &apos;/api/v3/order&apos;
timestamp = int(time.time() * 1000)
params= {
&apos;symbol&apos;: &apos;ETHUSDT&apos;,
&apos;orderId&apos;: &apos;336683281&apos;,
&apos;recvWindow&apos;: 5000,
&apos;timestamp&apos;: timestamp
}
query_string = urlencode(params)
params[&apos;signature&apos;] = hmac.new(SECRET_KEY.encode(&apos;utf-8&apos;), query_string.encode(&apos;utf-8&apos;), hashlib.sha256).hexdigest()
url = urljoin(BASE_URL, PATH)
r = requests.delete(url, headers=headers, params=params)
if r.status_code == 200:
    data = r.json()
print(json.dumps(data, indent=2))
else:
raiseBinanceException(status_code=r.status_code, data=r.json())
"><code>PATH <span class="hljs-operator">=</span> <span class="hljs-string">'/api/v3/order'</span>
timestamp <span class="hljs-operator">=</span> <span class="hljs-keyword">int</span>(time.time() <span class="hljs-operator">*</span> <span class="hljs-number">1000</span>)
params<span class="hljs-operator">=</span> {
<span class="hljs-string">'symbol'</span>: <span class="hljs-string">'ETHUSDT'</span>,
<span class="hljs-string">'orderId'</span>: <span class="hljs-string">'336683281'</span>,
<span class="hljs-string">'recvWindow'</span>: <span class="hljs-number">5000</span>,
<span class="hljs-string">'timestamp'</span>: timestamp
}
query_string <span class="hljs-operator">=</span> urlencode(params)
params[<span class="hljs-string">'signature'</span>] <span class="hljs-operator">=</span> hmac.new(SECRET_KEY.encode(<span class="hljs-string">'utf-8'</span>), query_string.encode(<span class="hljs-string">'utf-8'</span>), hashlib.sha256).hexdigest()
url <span class="hljs-operator">=</span> urljoin(BASE_URL, PATH)
r <span class="hljs-operator">=</span> requests.delete(url, headers<span class="hljs-operator">=</span>headers, params<span class="hljs-operator">=</span>params)
<span class="hljs-keyword">if</span> r.status_code <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">200</span>:
    data <span class="hljs-operator">=</span> r.json()
print(json.dumps(data, indent<span class="hljs-operator">=</span><span class="hljs-number">2</span>))
<span class="hljs-keyword">else</span>:
raiseBinanceException(status_code<span class="hljs-operator">=</span>r.status_code, data<span class="hljs-operator">=</span>r.json())
</code></pre><pre data-type="codeBlock" text="{
&quot;symbol&quot;: &quot;ETHUSDT&quot;,
&quot;origClientOrderId&quot;: &quot;IVGyfNu88LhRnpZFa56JA4&quot;,
&quot;orderId&quot;: 336683281,
&quot;clientOrderId&quot;: &quot;2Fh1EdAmHU8ZUR0TwjrQAR&quot;,
&quot;price&quot;: &quot;500.00000000&quot;,
&quot;origQty&quot;: &quot;0.10000000&quot;,
&quot;executedQty&quot;: &quot;0.00000000&quot;,
&quot;cummulativeQuoteQty&quot;: &quot;0.00000000&quot;,
&quot;status&quot;: &quot;CANCELED&quot;,
&quot;timeInForce&quot;: &quot;GTC&quot;,
&quot;type&quot;: &quot;LIMIT&quot;,
&quot;side&quot;: &quot;SELL&quot;
}
"><code><span class="hljs-punctuation">{</span>
<span class="hljs-attr">"symbol"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"ETHUSDT"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"origClientOrderId"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"IVGyfNu88LhRnpZFa56JA4"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"orderId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">336683281</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"clientOrderId"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2Fh1EdAmHU8ZUR0TwjrQAR"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"price"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"500.00000000"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"origQty"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0.10000000"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"executedQty"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0.00000000"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"cummulativeQuoteQty"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0.00000000"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"status"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"CANCELED"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"timeInForce"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"GTC"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"LIMIT"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"side"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"SELL"</span>
<span class="hljs-punctuation">}</span>
</code></pre>]]></content:encoded>
            <author>quantbang@newsletter.paragraph.com (quantbang)</author>
        </item>
        <item>
            <title><![CDATA[用 Python 和币安 API 构建数字货币交易机器人（三）]]></title>
            <link>https://paragraph.com/@quantbang/python-api-3</link>
            <guid>ZULn2inxIul6w8wtspvi</guid>
            <pubDate>Thu, 24 Mar 2022 12:45:48 GMT</pubDate>
            <description><![CDATA[欢迎来到本系列的第三部分。 数据集创建 首先，让我们介绍一个新的“数据集”业务对象来对价格进行分组。 ./models/dataset.pyfrom datetime import datetime from api import utils from models.model import AbstractModel from models.exchange import Exchange from models.currency import Currency class Dataset(AbstractModel): resource_name = 'datasets' pair: str = '' exchange: str = '' period_start: str = '' period_end: str = '' currency: str = '' asset: str = '' relations = {'exchange': Exchange, 'currency': Currency, 'asset': Currency} def __init__(sel...]]></description>
            <content:encoded><![CDATA[<p>欢迎来到本系列的第三部分。</p><p><strong>数据集创建</strong></p><p>首先，让我们介绍一个新的“数据集”业务对象来对价格进行分组。</p><p><code>./models/dataset.py</code></p><pre data-type="codeBlock" text="from datetime import datetime

from api import utils
from models.model import AbstractModel
from models.exchange import Exchange
from models.currency import Currency

class Dataset(AbstractModel):
    resource_name = &apos;datasets&apos;

    pair: str = &apos;&apos;
    exchange: str = &apos;&apos;
    period_start: str = &apos;&apos;
    period_end: str = &apos;&apos;
    currency: str = &apos;&apos;
    asset: str = &apos;&apos;

    relations = {&apos;exchange&apos;: Exchange, &apos;currency&apos;: Currency, &apos;asset&apos;: Currency}

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.pair = self.get_pair()

    def get_pair(self):
        return utils.format_pair(self.currency, self.asset)
"><code><span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> <span class="hljs-title">datetime</span>

<span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">api</span> <span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">utils</span>
<span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">models</span>.<span class="hljs-title">model</span> <span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">AbstractModel</span>
<span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">models</span>.<span class="hljs-title">exchange</span> <span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">Exchange</span>
<span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">models</span>.<span class="hljs-title">currency</span> <span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">Currency</span>

<span class="hljs-title">class</span> <span class="hljs-title">Dataset</span>(<span class="hljs-title">AbstractModel</span>):
    <span class="hljs-title">resource_name</span> <span class="hljs-operator">=</span> <span class="hljs-string">'datasets'</span>

    <span class="hljs-title">pair</span>: <span class="hljs-title">str</span> <span class="hljs-operator">=</span> <span class="hljs-string">''</span>
    <span class="hljs-title">exchange</span>: <span class="hljs-title">str</span> <span class="hljs-operator">=</span> <span class="hljs-string">''</span>
    <span class="hljs-title">period_start</span>: <span class="hljs-title">str</span> <span class="hljs-operator">=</span> <span class="hljs-string">''</span>
    <span class="hljs-title">period_end</span>: <span class="hljs-title">str</span> <span class="hljs-operator">=</span> <span class="hljs-string">''</span>
    <span class="hljs-title">currency</span>: <span class="hljs-title">str</span> <span class="hljs-operator">=</span> <span class="hljs-string">''</span>
    <span class="hljs-title">asset</span>: <span class="hljs-title">str</span> <span class="hljs-operator">=</span> <span class="hljs-string">''</span>

    <span class="hljs-title">relations</span> <span class="hljs-operator">=</span> {<span class="hljs-string">'exchange'</span>: <span class="hljs-title">Exchange</span>, <span class="hljs-string">'currency'</span>: <span class="hljs-title">Currency</span>, <span class="hljs-string">'asset'</span>: <span class="hljs-title">Currency</span>}

    <span class="hljs-title">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-title"><span class="hljs-built_in">self</span></span>, <span class="hljs-operator">*</span><span class="hljs-operator">*</span><span class="hljs-title">kwargs</span>):
        <span class="hljs-title"><span class="hljs-built_in">super</span></span>().<span class="hljs-title">__init__</span>(<span class="hljs-operator">*</span><span class="hljs-operator">*</span><span class="hljs-title">kwargs</span>)
        <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">pair</span> <span class="hljs-operator">=</span> <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">get_pair</span>()

    <span class="hljs-title">def</span> <span class="hljs-title">get_pair</span>(<span class="hljs-title"><span class="hljs-built_in">self</span></span>):
        <span class="hljs-title"><span class="hljs-keyword">return</span></span> <span class="hljs-title">utils</span>.<span class="hljs-title">format_pair</span>(<span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">currency</span>, <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">asset</span>)
</code></pre><p><strong>引入服务对象</strong></p><p>我们需要构建一个服务来解析和加载Binance交易所或任何其他具有API和此类历史行情自动收录器端点的历史数据。</p><p><code>./services/importer.py</code></p><pre data-type="codeBlock" text="import sys
from datetime import datetime
from models.dataset import Dataset

class Importer:
    def __init__(self, exchange, period_start: datetime, period_end=None, interval=60, *args, **kwargs):
        self.exchange = exchange
        self.interval = interval
        self.period_start = period_start
        self.period_end = period_end
        self.start = datetime.now()
        self.dataset = Dataset().create(
            data={&apos;exchange&apos;: &apos;/api/exchanges/&apos;+self.exchange.name.lower(), &apos;periodStart&apos;: self.period_start, &apos;periodEnd&apos;: self.period_end,
                  &apos;candleSize&apos;: 60,
                  &apos;currency&apos;: &apos;/api/currencies/&apos;+self.exchange.currency.lower(), &apos;asset&apos;: &apos;/api/currencies/&apos;+self.exchange.asset.lower()})

    def process(self):
        for price in self.exchange.historical_symbol_ticker_candle(self.period_start, self.period_end, self.interval):
            print(price.create({&apos;dataset&apos;: &apos;/api/datasets/&apos;+self.dataset.uuid}))

        execution_time = datetime.now() - self.start
        print(&apos;Execution time: &apos; + str(execution_time.total_seconds()) + &apos; seconds&apos;)
        sys.exit()
"><code><span class="hljs-keyword">import</span> <span class="hljs-title">sys</span>
<span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">datetime</span> <span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">datetime</span>
<span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">models</span>.<span class="hljs-title">dataset</span> <span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">Dataset</span>

<span class="hljs-title">class</span> <span class="hljs-title">Importer</span>:
    <span class="hljs-title">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-title"><span class="hljs-built_in">self</span></span>, <span class="hljs-title">exchange</span>, <span class="hljs-title">period_start</span>: <span class="hljs-title">datetime</span>, <span class="hljs-title">period_end</span><span class="hljs-operator">=</span><span class="hljs-title">None</span>, <span class="hljs-title">interval</span><span class="hljs-operator">=</span>60, <span class="hljs-operator">*</span><span class="hljs-title">args</span>, <span class="hljs-operator">*</span><span class="hljs-operator">*</span><span class="hljs-title">kwargs</span>):
        <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">exchange</span> <span class="hljs-operator">=</span> <span class="hljs-title">exchange</span>
        <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">interval</span> <span class="hljs-operator">=</span> <span class="hljs-title">interval</span>
        <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">period_start</span> <span class="hljs-operator">=</span> <span class="hljs-title">period_start</span>
        <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">period_end</span> <span class="hljs-operator">=</span> <span class="hljs-title">period_end</span>
        <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">start</span> <span class="hljs-operator">=</span> <span class="hljs-title">datetime</span>.<span class="hljs-title"><span class="hljs-built_in">now</span></span>()
        <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">dataset</span> <span class="hljs-operator">=</span> <span class="hljs-title">Dataset</span>().<span class="hljs-title">create</span>(
            <span class="hljs-title">data</span><span class="hljs-operator">=</span>{<span class="hljs-string">'exchange'</span>: <span class="hljs-string">'/api/exchanges/'</span><span class="hljs-operator">+</span><span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">exchange</span>.<span class="hljs-title">name</span>.<span class="hljs-title">lower</span>(), <span class="hljs-string">'periodStart'</span>: <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">period_start</span>, <span class="hljs-string">'periodEnd'</span>: <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">period_end</span>,
                  <span class="hljs-string">'candleSize'</span>: 60,
                  <span class="hljs-string">'currency'</span>: <span class="hljs-string">'/api/currencies/'</span><span class="hljs-operator">+</span><span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">exchange</span>.<span class="hljs-title">currency</span>.<span class="hljs-title">lower</span>(), <span class="hljs-string">'asset'</span>: <span class="hljs-string">'/api/currencies/'</span><span class="hljs-operator">+</span><span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">exchange</span>.<span class="hljs-title">asset</span>.<span class="hljs-title">lower</span>()})

    <span class="hljs-title">def</span> <span class="hljs-title">process</span>(<span class="hljs-title"><span class="hljs-built_in">self</span></span>):
        <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">price</span> <span class="hljs-title">in</span> <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">exchange</span>.<span class="hljs-title">historical_symbol_ticker_candle</span>(<span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">period_start</span>, <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">period_end</span>, <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">interval</span>):
            <span class="hljs-title">print</span>(<span class="hljs-title">price</span>.<span class="hljs-title">create</span>({<span class="hljs-string">'dataset'</span>: <span class="hljs-string">'/api/datasets/'</span><span class="hljs-operator">+</span><span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">dataset</span>.<span class="hljs-title">uuid</span>}))

        <span class="hljs-title">execution_time</span> <span class="hljs-operator">=</span> <span class="hljs-title">datetime</span>.<span class="hljs-title"><span class="hljs-built_in">now</span></span>() <span class="hljs-operator">-</span> <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">start</span>
        <span class="hljs-title">print</span>(<span class="hljs-string">'Execution time: '</span> <span class="hljs-operator">+</span> <span class="hljs-title">str</span>(<span class="hljs-title">execution_time</span>.<span class="hljs-title">total_seconds</span>()) <span class="hljs-operator">+</span> <span class="hljs-string">' seconds'</span>)
        <span class="hljs-title">sys</span>.<span class="hljs-title">exit</span>()
</code></pre><p>这项服务职责非常简单明了，他的名字说明了一切，我们从交易所导入和存储历史行情自动收录器数据。</p><p>在这里，您可以将对象直接存储在诸如PostgreSQL之类的关系数据库中，也可以构建内部REST API并将其用作数据库的代理，以实现高性能。</p><p><strong>回测</strong></p><p>回测是编写您未来的机器人并根据历史行情自动收录器数据针对所有市场情况进行测试的最重要工具。</p><p>为此，我们将创建一个回测服务，他的职责是从您当前的本地数据中加载数据集，如果找不到，则直接从交易所加载（默认情况下为Binance）。然后针对历史数据集中的每个价格数据运行给定策略。</p><p><code>/services/backtest.py</code></p><pre data-type="codeBlock" text="import sys
from datetime import datetime

from exchanges.exchange import Exchange
from models.dataset import Dataset
from models.price import Price

class Backtest:
    def __init__(self, exchange: Exchange, period_start: datetime, period_end=None, interval=60):
        self.launchedAt = datetime.now()
        # Try to find dataset
        dataset = Dataset().query(&apos;get&apos;, {&quot;exchange&quot;: &apos;/api/exchanges/&apos; + exchange.name.lower(),
                                          &quot;currency&quot;: &apos;/api/currencies/&apos; + exchange.currency.lower(),
                                          &quot;asset&quot;: &apos;/api/currencies/&apos; + exchange.asset.lower(),
                                          &quot;period_start&quot;: period_start, &quot;period_end&quot;: period_end, &quot;candleSize&quot;: interval})

        if dataset and len(dataset) &gt; 0:
            print(dataset[0])
            price = Price()
            for price in price.query(&apos;get&apos;, {&quot;dataset&quot;: dataset[0][&apos;uuid&apos;]}):
                newPrice = Price()
                newPrice.populate(price)
                exchange.strategy.set_price(newPrice)
                exchange.strategy.run()
        else:
            print(&quot;Dataset not found, external API call to &quot; + exchange.name)
            for price in exchange.historical_symbol_ticker_candle(period_start, period_end, interval):
                exchange.strategy.set_price(price)
                exchange.strategy.run()

        execution_time = datetime.now() - self.launchedAt
        print(&apos;Execution time: &apos; + str(execution_time.total_seconds()) + &apos; seconds&apos;)
        sys.exit()
"><code><span class="hljs-keyword">import</span> <span class="hljs-title">sys</span>
<span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">datetime</span> <span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">datetime</span>

<span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">exchanges</span>.<span class="hljs-title">exchange</span> <span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">Exchange</span>
<span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">models</span>.<span class="hljs-title">dataset</span> <span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">Dataset</span>
<span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">models</span>.<span class="hljs-title">price</span> <span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">Price</span>

<span class="hljs-title">class</span> <span class="hljs-title">Backtest</span>:
    <span class="hljs-title">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-title"><span class="hljs-built_in">self</span></span>, <span class="hljs-title">exchange</span>: <span class="hljs-title">Exchange</span>, <span class="hljs-title">period_start</span>: <span class="hljs-title">datetime</span>, <span class="hljs-title">period_end</span><span class="hljs-operator">=</span><span class="hljs-title">None</span>, <span class="hljs-title">interval</span><span class="hljs-operator">=</span>60):
        <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">launchedAt</span> <span class="hljs-operator">=</span> <span class="hljs-title">datetime</span>.<span class="hljs-title"><span class="hljs-built_in">now</span></span>()
        # <span class="hljs-title">Try</span> <span class="hljs-title">to</span> <span class="hljs-title">find</span> <span class="hljs-title">dataset</span>
        <span class="hljs-title">dataset</span> <span class="hljs-operator">=</span> <span class="hljs-title">Dataset</span>().<span class="hljs-title">query</span>(<span class="hljs-string">'get'</span>, {<span class="hljs-string">"exchange"</span>: <span class="hljs-string">'/api/exchanges/'</span> <span class="hljs-operator">+</span> <span class="hljs-title">exchange</span>.<span class="hljs-title">name</span>.<span class="hljs-title">lower</span>(),
                                          <span class="hljs-string">"currency"</span>: <span class="hljs-string">'/api/currencies/'</span> <span class="hljs-operator">+</span> <span class="hljs-title">exchange</span>.<span class="hljs-title">currency</span>.<span class="hljs-title">lower</span>(),
                                          <span class="hljs-string">"asset"</span>: <span class="hljs-string">'/api/currencies/'</span> <span class="hljs-operator">+</span> <span class="hljs-title">exchange</span>.<span class="hljs-title">asset</span>.<span class="hljs-title">lower</span>(),
                                          <span class="hljs-string">"period_start"</span>: <span class="hljs-title">period_start</span>, <span class="hljs-string">"period_end"</span>: <span class="hljs-title">period_end</span>, <span class="hljs-string">"candleSize"</span>: <span class="hljs-title">interval</span>})

        <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">dataset</span> <span class="hljs-title">and</span> <span class="hljs-title">len</span>(<span class="hljs-title">dataset</span>) <span class="hljs-operator">></span> 0:
            <span class="hljs-title">print</span>(<span class="hljs-title">dataset</span>[0])
            <span class="hljs-title">price</span> <span class="hljs-operator">=</span> <span class="hljs-title">Price</span>()
            <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">price</span> <span class="hljs-title">in</span> <span class="hljs-title">price</span>.<span class="hljs-title">query</span>(<span class="hljs-string">'get'</span>, {<span class="hljs-string">"dataset"</span>: <span class="hljs-title">dataset</span>[0][<span class="hljs-string">'uuid'</span>]}):
                <span class="hljs-title">newPrice</span> <span class="hljs-operator">=</span> <span class="hljs-title">Price</span>()
                <span class="hljs-title">newPrice</span>.<span class="hljs-title">populate</span>(<span class="hljs-title">price</span>)
                <span class="hljs-title">exchange</span>.<span class="hljs-title">strategy</span>.<span class="hljs-title">set_price</span>(<span class="hljs-title">newPrice</span>)
                <span class="hljs-title">exchange</span>.<span class="hljs-title">strategy</span>.<span class="hljs-title">run</span>()
        <span class="hljs-title"><span class="hljs-keyword">else</span></span>:
            <span class="hljs-title">print</span>(<span class="hljs-string">"Dataset not found, external API call to "</span> <span class="hljs-operator">+</span> <span class="hljs-title">exchange</span>.<span class="hljs-title">name</span>)
            <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">price</span> <span class="hljs-title">in</span> <span class="hljs-title">exchange</span>.<span class="hljs-title">historical_symbol_ticker_candle</span>(<span class="hljs-title">period_start</span>, <span class="hljs-title">period_end</span>, <span class="hljs-title">interval</span>):
                <span class="hljs-title">exchange</span>.<span class="hljs-title">strategy</span>.<span class="hljs-title">set_price</span>(<span class="hljs-title">price</span>)
                <span class="hljs-title">exchange</span>.<span class="hljs-title">strategy</span>.<span class="hljs-title">run</span>()

        <span class="hljs-title">execution_time</span> <span class="hljs-operator">=</span> <span class="hljs-title">datetime</span>.<span class="hljs-title"><span class="hljs-built_in">now</span></span>() <span class="hljs-operator">-</span> <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">launchedAt</span>
        <span class="hljs-title">print</span>(<span class="hljs-string">'Execution time: '</span> <span class="hljs-operator">+</span> <span class="hljs-title">str</span>(<span class="hljs-title">execution_time</span>.<span class="hljs-title">total_seconds</span>()) <span class="hljs-operator">+</span> <span class="hljs-string">' seconds'</span>)
        <span class="hljs-title">sys</span>.<span class="hljs-title">exit</span>()
</code></pre><p><strong>项目配置</strong></p><p>我们将使用dotenv库来管理环境变量。这是项目的默认值：</p><p><code>./.env.local</code></p><pre data-type="codeBlock" text="AVAILABLE_EXCHANGES=&quot;coinbase,binance&quot;
EXCHANGE=&quot;binance&quot;

BINANCE_API_KEY=&quot;Your Binance API KEY&quot;
BINANCE_API_SECRET=&quot;Your Binance API SECRET&quot;

COINBASE_API_KEY=&quot;Your Coinbase API KEY&quot;&quot;
COINBASE_API_SECRET=&quot;Your Coinbase API SECRET&quot;&quot;

# Available modes
# &quot;trade&quot; to trade on candlesticks
# &quot;live&quot; to live trade throught WebSocket
# &quot;backtest&quot; to test a strategy for a given symbol pair and a period
# &quot;import&quot; to import dataset from exchanges for a given symbol pair and a period
MODE=&quot;trade&quot;
STRATEGY=&quot;logger&quot;
# Allow trading &quot;test&quot; mode or &quot;real&quot; trading
TRADING_MODE=&quot;test&quot;
# Default candle size in seconds
CANDLE_INTERVAL=60
CURRENCY=&quot;BTC&quot;
ASSET=&quot;EUR&quot;
# Default period for backtesting: string in UTC format
PERIOD_START=&quot;2021-02-28T08:49&quot;
PERIOD_END=&quot;2021-03-09T08:49&quot;

DATABASE_URL=&quot;postgresql://postgres:password@127.0.0.1:15432/cryptobot&quot;
"><code><span class="hljs-attr">AVAILABLE_EXCHANGES</span>=<span class="hljs-string">"coinbase,binance"</span>
<span class="hljs-attr">EXCHANGE</span>=<span class="hljs-string">"binance"</span>

<span class="hljs-attr">BINANCE_API_KEY</span>=<span class="hljs-string">"Your Binance API KEY"</span>
<span class="hljs-attr">BINANCE_API_SECRET</span>=<span class="hljs-string">"Your Binance API SECRET"</span>

<span class="hljs-attr">COINBASE_API_KEY</span>=<span class="hljs-string">"Your Coinbase API KEY"</span><span class="hljs-string">"
COINBASE_API_SECRET="</span>Your Coinbase API SECRET<span class="hljs-string">""</span>

<span class="hljs-comment"># Available modes</span>
<span class="hljs-comment"># "trade" to trade on candlesticks</span>
<span class="hljs-comment"># "live" to live trade throught WebSocket</span>
<span class="hljs-comment"># "backtest" to test a strategy for a given symbol pair and a period</span>
<span class="hljs-comment"># "import" to import dataset from exchanges for a given symbol pair and a period</span>
<span class="hljs-attr">MODE</span>=<span class="hljs-string">"trade"</span>
<span class="hljs-attr">STRATEGY</span>=<span class="hljs-string">"logger"</span>
<span class="hljs-comment"># Allow trading "test" mode or "real" trading</span>
<span class="hljs-attr">TRADING_MODE</span>=<span class="hljs-string">"test"</span>
<span class="hljs-comment"># Default candle size in seconds</span>
<span class="hljs-attr">CANDLE_INTERVAL</span>=<span class="hljs-number">60</span>
<span class="hljs-attr">CURRENCY</span>=<span class="hljs-string">"BTC"</span>
<span class="hljs-attr">ASSET</span>=<span class="hljs-string">"EUR"</span>
<span class="hljs-comment"># Default period for backtesting: string in UTC format</span>
<span class="hljs-attr">PERIOD_START</span>=<span class="hljs-string">"2021-02-28T08:49"</span>
<span class="hljs-attr">PERIOD_END</span>=<span class="hljs-string">"2021-03-09T08:49"</span>

<span class="hljs-attr">DATABASE_URL</span>=<span class="hljs-string">"postgresql://postgres:password@127.0.0.1:15432/cryptobot"</span>
</code></pre><p><strong>主线程</strong></p><p>然后将所有这些部分放到一个主线程上，主要是使用args以及环境变量的CLI命令。</p><p>这样，我们可以覆盖任何默认环境设置，并直接使用基于命令行的客户端直接调整所有输入参数。</p><p>例如，在使用诸如Docker之类的容器化工具时，它也确实非常有用，只需启动此主线程，它将与特定容器的环境变量一起运行。</p><p>我们将根据设置动态加载和导入我们创建的每个组件。</p><p><code>./main.py</code></p><pre data-type="codeBlock" text="#!/usr/bin/python3

import importlib
import signal
import sys
import threading
from decouple import config

from services.backtest import Backtest
from services.importer import Importer

exchange_name = config(&apos;EXCHANGE&apos;)
available_exchanges = config(&apos;AVAILABLE_EXCHANGES&apos;).split(&apos;,&apos;)
mode: str = config(&apos;MODE&apos;)
strategy: str = config(&apos;STRATEGY&apos;)
trading_mode: str = config(&apos;TRADING_MODE&apos;)
interval: int = int(config(&apos;CANDLE_INTERVAL&apos;))
currency: str = config(&apos;CURRENCY&apos;)
asset: str = config(&apos;ASSET&apos;)

if trading_mode == &apos;real&apos;:
    print(&quot;*** Caution: Real trading mode activated ***&quot;)
else:
    print(&quot;Test mode&quot;)

# Parse symbol pair from first command argument
if len(sys.argv) &gt; 1:
    currencies = sys.argv[1].split(&apos;_&apos;)
    if len(currencies) &gt; 1:
        currency = currencies[0]
        asset = currencies[1]

# Load exchange
print(&quot;Connecting to {} exchange...&quot;.format(exchange_name[0].upper() + exchange_name[1:]))
exchangeModule = importlib.import_module(&apos;exchanges.&apos; + exchange_name, package=None)
exchangeClass = getattr(exchangeModule, exchange_name[0].upper() + exchange_name[1:])
exchange = exchangeClass(config(exchange_name.upper() + &apos;_API_KEY&apos;), config(exchange_name.upper() + &apos;_API_SECRET&apos;))

# Load currencies
exchange.set_currency(currency)
exchange.set_asset(asset)

# Load strategy
strategyModule = importlib.import_module(&apos;strategies.&apos; + strategy, package=None)
strategyClass = getattr(strategyModule, strategy[0].upper() + strategy[1:])
exchange.set_strategy(strategyClass(exchange, interval))

# mode
print(&quot;{} mode on {} symbol&quot;.format(mode, exchange.get_symbol()))
if mode == &apos;trade&apos;:
    exchange.strategy.start()

elif mode == &apos;live&apos;:
    exchange.start_symbol_ticker_socket(exchange.get_symbol())

elif mode == &apos;backtest&apos;:
    period_start = config(&apos;PERIOD_START&apos;)
    period_end = config(&apos;PERIOD_END&apos;)

    print(
        &quot;Backtest period from {} to {} with {} seconds candlesticks.&quot;.format(
            period_start,
            period_end,
            interval
        )
    )
    Backtest(exchange, period_start, period_end, interval)

elif mode == &apos;import&apos;:
    period_start = config(&apos;PERIOD_START&apos;)
    period_end = config(&apos;PERIOD_END&apos;)

    print(
        &quot;Import mode on {} symbol for period from {} to {} with {} seconds candlesticks.&quot;.format(
            exchange.get_symbol(),
            period_start,
            period_end,
            interval
        )
    )
    importer = Importer(exchange, period_start, period_end, interval)
    importer.process()

else:
    print(&apos;Not supported mode.&apos;)

def signal_handler(signal, frame):
    if (exchange.socket):
        print(&apos;Closing WebSocket connection...&apos;)
        exchange.close_socket()
        sys.exit(0)
    else:
        print(&apos;stopping strategy...&apos;)
        exchange.strategy.stop()
        sys.exit(0)

# Listen for keyboard interrupt event
signal.signal(signal.SIGINT, signal_handler)
forever = threading.Event()
forever.wait()
exchange.strategy.stop()
sys.exit(0)
"><code>#<span class="hljs-operator">!</span><span class="hljs-operator">/</span>usr<span class="hljs-operator">/</span>bin<span class="hljs-operator">/</span>python3

<span class="hljs-keyword">import</span> <span class="hljs-title">importlib</span>
<span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">signal</span>
<span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">sys</span>
<span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">threading</span>
<span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">decouple</span> <span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">config</span>

<span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">services</span>.<span class="hljs-title">backtest</span> <span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">Backtest</span>
<span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">services</span>.<span class="hljs-title">importer</span> <span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">Importer</span>

<span class="hljs-title">exchange_name</span> <span class="hljs-operator">=</span> <span class="hljs-title">config</span>(<span class="hljs-string">'EXCHANGE'</span>)
<span class="hljs-title">available_exchanges</span> <span class="hljs-operator">=</span> <span class="hljs-title">config</span>(<span class="hljs-string">'AVAILABLE_EXCHANGES'</span>).<span class="hljs-title">split</span>(<span class="hljs-string">','</span>)
<span class="hljs-title">mode</span>: <span class="hljs-title">str</span> <span class="hljs-operator">=</span> <span class="hljs-title">config</span>(<span class="hljs-string">'MODE'</span>)
<span class="hljs-title">strategy</span>: <span class="hljs-title">str</span> <span class="hljs-operator">=</span> <span class="hljs-title">config</span>(<span class="hljs-string">'STRATEGY'</span>)
<span class="hljs-title">trading_mode</span>: <span class="hljs-title">str</span> <span class="hljs-operator">=</span> <span class="hljs-title">config</span>(<span class="hljs-string">'TRADING_MODE'</span>)
<span class="hljs-title">interval</span>: <span class="hljs-title"><span class="hljs-keyword">int</span></span> <span class="hljs-operator">=</span> <span class="hljs-title"><span class="hljs-keyword">int</span></span>(<span class="hljs-title">config</span>(<span class="hljs-string">'CANDLE_INTERVAL'</span>))
<span class="hljs-title">currency</span>: <span class="hljs-title">str</span> <span class="hljs-operator">=</span> <span class="hljs-title">config</span>(<span class="hljs-string">'CURRENCY'</span>)
<span class="hljs-title">asset</span>: <span class="hljs-title">str</span> <span class="hljs-operator">=</span> <span class="hljs-title">config</span>(<span class="hljs-string">'ASSET'</span>)

<span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">trading_mode</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">'real'</span>:
    <span class="hljs-title">print</span>(<span class="hljs-string">"*** Caution: Real trading mode activated ***"</span>)
<span class="hljs-title"><span class="hljs-keyword">else</span></span>:
    <span class="hljs-title">print</span>(<span class="hljs-string">"Test mode"</span>)

# <span class="hljs-title">Parse</span> <span class="hljs-title">symbol</span> <span class="hljs-title">pair</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">first</span> <span class="hljs-title">command</span> <span class="hljs-title">argument</span>
<span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">len</span>(<span class="hljs-title">sys</span>.<span class="hljs-title">argv</span>) <span class="hljs-operator">></span> 1:
    <span class="hljs-title">currencies</span> <span class="hljs-operator">=</span> <span class="hljs-title">sys</span>.<span class="hljs-title">argv</span>[1].<span class="hljs-title">split</span>(<span class="hljs-string">'_'</span>)
    <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">len</span>(<span class="hljs-title">currencies</span>) <span class="hljs-operator">></span> 1:
        <span class="hljs-title">currency</span> <span class="hljs-operator">=</span> <span class="hljs-title">currencies</span>[0]
        <span class="hljs-title">asset</span> <span class="hljs-operator">=</span> <span class="hljs-title">currencies</span>[1]

# <span class="hljs-title">Load</span> <span class="hljs-title">exchange</span>
<span class="hljs-title">print</span>(<span class="hljs-string">"Connecting to {} exchange..."</span>.<span class="hljs-title">format</span>(<span class="hljs-title">exchange_name</span>[0].<span class="hljs-title">upper</span>() <span class="hljs-operator">+</span> <span class="hljs-title">exchange_name</span>[1:]))
<span class="hljs-title">exchangeModule</span> <span class="hljs-operator">=</span> <span class="hljs-title">importlib</span>.<span class="hljs-title">import_module</span>(<span class="hljs-string">'exchanges.'</span> <span class="hljs-operator">+</span> <span class="hljs-title">exchange_name</span>, <span class="hljs-title">package</span><span class="hljs-operator">=</span><span class="hljs-title">None</span>)
<span class="hljs-title">exchangeClass</span> <span class="hljs-operator">=</span> <span class="hljs-title">getattr</span>(<span class="hljs-title">exchangeModule</span>, <span class="hljs-title">exchange_name</span>[0].<span class="hljs-title">upper</span>() <span class="hljs-operator">+</span> <span class="hljs-title">exchange_name</span>[1:])
<span class="hljs-title">exchange</span> <span class="hljs-operator">=</span> <span class="hljs-title">exchangeClass</span>(<span class="hljs-title">config</span>(<span class="hljs-title">exchange_name</span>.<span class="hljs-title">upper</span>() <span class="hljs-operator">+</span> <span class="hljs-string">'_API_KEY'</span>), <span class="hljs-title">config</span>(<span class="hljs-title">exchange_name</span>.<span class="hljs-title">upper</span>() <span class="hljs-operator">+</span> <span class="hljs-string">'_API_SECRET'</span>))

# <span class="hljs-title">Load</span> <span class="hljs-title">currencies</span>
<span class="hljs-title">exchange</span>.<span class="hljs-title">set_currency</span>(<span class="hljs-title">currency</span>)
<span class="hljs-title">exchange</span>.<span class="hljs-title">set_asset</span>(<span class="hljs-title">asset</span>)

# <span class="hljs-title">Load</span> <span class="hljs-title">strategy</span>
<span class="hljs-title">strategyModule</span> <span class="hljs-operator">=</span> <span class="hljs-title">importlib</span>.<span class="hljs-title">import_module</span>(<span class="hljs-string">'strategies.'</span> <span class="hljs-operator">+</span> <span class="hljs-title">strategy</span>, <span class="hljs-title">package</span><span class="hljs-operator">=</span><span class="hljs-title">None</span>)
<span class="hljs-title">strategyClass</span> <span class="hljs-operator">=</span> <span class="hljs-title">getattr</span>(<span class="hljs-title">strategyModule</span>, <span class="hljs-title">strategy</span>[0].<span class="hljs-title">upper</span>() <span class="hljs-operator">+</span> <span class="hljs-title">strategy</span>[1:])
<span class="hljs-title">exchange</span>.<span class="hljs-title">set_strategy</span>(<span class="hljs-title">strategyClass</span>(<span class="hljs-title">exchange</span>, <span class="hljs-title">interval</span>))

# <span class="hljs-title">mode</span>
<span class="hljs-title">print</span>(<span class="hljs-string">"{} mode on {} symbol"</span>.<span class="hljs-title">format</span>(<span class="hljs-title">mode</span>, <span class="hljs-title">exchange</span>.<span class="hljs-title">get_symbol</span>()))
<span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">mode</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">'trade'</span>:
    <span class="hljs-title">exchange</span>.<span class="hljs-title">strategy</span>.<span class="hljs-title">start</span>()

<span class="hljs-title">elif</span> <span class="hljs-title">mode</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">'live'</span>:
    <span class="hljs-title">exchange</span>.<span class="hljs-title">start_symbol_ticker_socket</span>(<span class="hljs-title">exchange</span>.<span class="hljs-title">get_symbol</span>())

<span class="hljs-title">elif</span> <span class="hljs-title">mode</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">'backtest'</span>:
    <span class="hljs-title">period_start</span> <span class="hljs-operator">=</span> <span class="hljs-title">config</span>(<span class="hljs-string">'PERIOD_START'</span>)
    <span class="hljs-title">period_end</span> <span class="hljs-operator">=</span> <span class="hljs-title">config</span>(<span class="hljs-string">'PERIOD_END'</span>)

    <span class="hljs-title">print</span>(
        <span class="hljs-string">"Backtest period from {} to {} with {} seconds candlesticks."</span>.<span class="hljs-title">format</span>(
            <span class="hljs-title">period_start</span>,
            <span class="hljs-title">period_end</span>,
            <span class="hljs-title">interval</span>
        )
    )
    <span class="hljs-title">Backtest</span>(<span class="hljs-title">exchange</span>, <span class="hljs-title">period_start</span>, <span class="hljs-title">period_end</span>, <span class="hljs-title">interval</span>)

<span class="hljs-title">elif</span> <span class="hljs-title">mode</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">'import'</span>:
    <span class="hljs-title">period_start</span> <span class="hljs-operator">=</span> <span class="hljs-title">config</span>(<span class="hljs-string">'PERIOD_START'</span>)
    <span class="hljs-title">period_end</span> <span class="hljs-operator">=</span> <span class="hljs-title">config</span>(<span class="hljs-string">'PERIOD_END'</span>)

    <span class="hljs-title">print</span>(
        <span class="hljs-string">"Import mode on {} symbol for period from {} to {} with {} seconds candlesticks."</span>.<span class="hljs-title">format</span>(
            <span class="hljs-title">exchange</span>.<span class="hljs-title">get_symbol</span>(),
            <span class="hljs-title">period_start</span>,
            <span class="hljs-title">period_end</span>,
            <span class="hljs-title">interval</span>
        )
    )
    <span class="hljs-title">importer</span> <span class="hljs-operator">=</span> <span class="hljs-title">Importer</span>(<span class="hljs-title">exchange</span>, <span class="hljs-title">period_start</span>, <span class="hljs-title">period_end</span>, <span class="hljs-title">interval</span>)
    <span class="hljs-title">importer</span>.<span class="hljs-title">process</span>()

<span class="hljs-title"><span class="hljs-keyword">else</span></span>:
    <span class="hljs-title">print</span>(<span class="hljs-string">'Not supported mode.'</span>)

<span class="hljs-title">def</span> <span class="hljs-title">signal_handler</span>(<span class="hljs-title">signal</span>, <span class="hljs-title">frame</span>):
    <span class="hljs-title"><span class="hljs-keyword">if</span></span> (<span class="hljs-title">exchange</span>.<span class="hljs-title">socket</span>):
        <span class="hljs-title">print</span>(<span class="hljs-string">'Closing WebSocket connection...'</span>)
        <span class="hljs-title">exchange</span>.<span class="hljs-title">close_socket</span>()
        <span class="hljs-title">sys</span>.<span class="hljs-title">exit</span>(0)
    <span class="hljs-title"><span class="hljs-keyword">else</span></span>:
        <span class="hljs-title">print</span>(<span class="hljs-string">'stopping strategy...'</span>)
        <span class="hljs-title">exchange</span>.<span class="hljs-title">strategy</span>.<span class="hljs-title">stop</span>()
        <span class="hljs-title">sys</span>.<span class="hljs-title">exit</span>(0)

# <span class="hljs-title">Listen</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">keyboard</span> <span class="hljs-title">interrupt</span> <span class="hljs-title"><span class="hljs-keyword">event</span></span>
<span class="hljs-title">signal</span>.<span class="hljs-title">signal</span>(<span class="hljs-title">signal</span>.<span class="hljs-title">SIGINT</span>, <span class="hljs-title">signal_handler</span>)
<span class="hljs-title">forever</span> <span class="hljs-operator">=</span> <span class="hljs-title">threading</span>.<span class="hljs-title">Event</span>()
<span class="hljs-title">forever</span>.<span class="hljs-title">wait</span>()
<span class="hljs-title">exchange</span>.<span class="hljs-title">strategy</span>.<span class="hljs-title">stop</span>()
<span class="hljs-title">sys</span>.<span class="hljs-title">exit</span>(0)
</code></pre><p><strong>用法</strong></p><pre data-type="codeBlock" text="# Real time trading mode via WebSocket
MODE=live ./main.py BTC_EUR

# Trading mode with default 1 minute candle
MODE=trade ./main.py BTC_EUR

# Import data from Exchange
MODE=import ./main.py BTC_EUR

# Backtest with an imported dataset or Binance Exchange API
MODE=backtest ./main.py BTC_EUR
"><code># Real time trading mode via WebSocket
MODE<span class="hljs-operator">=</span>live ./main.py BTC_EUR

# Trading mode with default <span class="hljs-number">1</span> minute candle
MODE<span class="hljs-operator">=</span>trade ./main.py BTC_EUR

# Import data <span class="hljs-keyword">from</span> Exchange
MODE<span class="hljs-operator">=</span><span class="hljs-keyword">import</span> .<span class="hljs-operator">/</span><span class="hljs-title">main</span>.<span class="hljs-title">py</span> <span class="hljs-title">BTC_EUR</span>

# <span class="hljs-title">Backtest</span> <span class="hljs-title">with</span> <span class="hljs-title">an</span> <span class="hljs-title">imported</span> <span class="hljs-title">dataset</span> <span class="hljs-title">or</span> <span class="hljs-title">Binance</span> <span class="hljs-title">Exchange</span> <span class="hljs-title">API</span>
<span class="hljs-title">MODE</span><span class="hljs-operator">=</span><span class="hljs-title">backtest</span> .<span class="hljs-operator">/</span><span class="hljs-title">main</span>.<span class="hljs-title">py</span> <span class="hljs-title">BTC_EUR</span>
</code></pre><p>您可以轻松地覆盖任何设置，如下所示：</p><pre data-type="codeBlock" text="PERIOD_START=&quot;2021-04-16 00:00&quot; PERIOD_END=&quot;2021-04-16 00:00&quot; STRATEGY=myCustomStrategy MODE=backtest ./main.py BTC_EUR
"><code>PERIOD_START<span class="hljs-operator">=</span><span class="hljs-string">"2021-04-16 00:00"</span> PERIOD_END<span class="hljs-operator">=</span><span class="hljs-string">"2021-04-16 00:00"</span> STRATEGY<span class="hljs-operator">=</span>myCustomStrategy MODE<span class="hljs-operator">=</span>backtest ./main.py BTC_EUR
</code></pre><p>要退出测试模式并进行真实交易，只需将“ trading_mode”从“ test”切换为“ real”。谨慎使用，后果自负。</p><pre data-type="codeBlock" text="TRADING_MODE=real ./main.py BTC_EUR
"><code>TRADING_MODE<span class="hljs-operator">=</span>real ./main.py BTC_EUR
</code></pre><p><strong>集成化项目</strong></p><p>我们可以使用Docker对该程序进行容器化。这是一个简单的自我解释Docker构建文件的例子。</p><pre data-type="codeBlock" text="FROM python:3.9

WORKDIR /usr/src/app

COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD [ &quot;python&quot;, &quot;./main.py&quot; ]
"><code>FROM python:<span class="hljs-number">3.9</span>

WORKDIR <span class="hljs-operator">/</span>usr<span class="hljs-operator">/</span>src<span class="hljs-operator">/</span>app

COPY requirements.txt ./
RUN pip install <span class="hljs-operator">-</span><span class="hljs-operator">-</span>no<span class="hljs-operator">-</span>cache<span class="hljs-operator">-</span>dir <span class="hljs-operator">-</span>r requirements.txt

COPY . .

CMD [ <span class="hljs-string">"python"</span>, <span class="hljs-string">"./main.py"</span> ]
</code></pre><p><strong>基准</strong></p><p>使用带有16go DDR3 ram的旧AMD Phenom II 955四核CPU，并运行其他进程。</p><p><strong>导入</strong></p><p>导入价格并将其部署到内部API</p><p>1天的行情</p><pre data-type="codeBlock" text="Execution time: 82.716666 seconds
"><code>Execution <span class="hljs-selector-tag">time</span>: <span class="hljs-number">82.716666</span> seconds
</code></pre><p>一周的行情</p><pre data-type="codeBlock" text="Execution time: 9,423079183 minutes
"><code>Execution <span class="hljs-selector-tag">time</span>: <span class="hljs-number">9</span>,<span class="hljs-number">423079183</span> minutes
</code></pre><p>一个月的行情</p><pre data-type="codeBlock" text="Execution time: 27,48139456 minutes
"><code>Execution <span class="hljs-selector-tag">time</span>: <span class="hljs-number">27</span>,<span class="hljs-number">48139456</span> minutes
</code></pre><p>六个月的行情</p><pre data-type="codeBlock" text="Execution time: 3.032364739 hours
"><code>Execution <span class="hljs-selector-tag">time</span>: <span class="hljs-number">3.032364739</span> hours
</code></pre><p><strong>回测</strong></p><p>从导入的数据集</p><p>1天的行情</p><pre data-type="codeBlock" text="Execution time: 3.746787 seconds
"><code>Execution <span class="hljs-selector-tag">time</span>: <span class="hljs-number">3.746787</span> seconds
</code></pre><p>一周的行情</p><pre data-type="codeBlock" text="Execution time: 46.900068 seconds
"><code>Execution <span class="hljs-selector-tag">time</span>: <span class="hljs-number">46.900068</span> seconds
</code></pre><p>一个月的行情</p><pre data-type="codeBlock" text="Execution time: 1.8953 seconds
"><code>Execution <span class="hljs-selector-tag">time</span>: <span class="hljs-number">1.8953</span> seconds
</code></pre><p>六个月的行情</p><pre data-type="codeBlock" text="Execution time: 12,15175435 minutes
"><code>Execution <span class="hljs-selector-tag">time</span>: <span class="hljs-number">12</span>,<span class="hljs-number">15175435</span> minutes
</code></pre><p><strong>结论</strong></p><p>我们构建了一个kickass性能的实时加密交易机器人。他能够使用少量的CPU和RAM在大型市场数据集上对您的策略进行回测。从交易所导入数据集，甚至使用WebSocket实时执行实时交易。</p><p><strong>更进一步</strong></p><ul><li><p>编写一个涵盖所有程序行为的测试套件，以确保将来不会退化。</p></li><li><p>构建并使用内部Rest API实时持久保存所有加密货币交易所市场数据。</p></li><li><p>建立最终用户客户端，例如移动应用或网络应用。使用WebSocket或服务器发送事件，以显示实时指标。</p></li></ul><p>感谢您阅读这三部分的文章，内容涉及如何使用python 3和Binance API构建加密机器人。</p>]]></content:encoded>
            <author>quantbang@newsletter.paragraph.com (quantbang)</author>
        </item>
        <item>
            <title><![CDATA[用 Python 和币安 API 构建数字货币交易机器人（二）]]></title>
            <link>https://paragraph.com/@quantbang/python-api-2</link>
            <guid>tho1xJ13m7lrNShlNknu</guid>
            <pubDate>Thu, 24 Mar 2022 12:42:25 GMT</pubDate>
            <description><![CDATA[在本系列的第一部分中，我们讨论了文件夹结构，添加了我们的第一个Price业务对象，并为策略抽象层进行编码。 下面将通过编码我们需要的货币和订单的业务对象开始。 此处，货币业务对象存储所有不同的加密货币或法定货币。 ./models/order.pyfrom models.model import AbstractModel class Currency(AbstractModel): name: str = '' symbol: str = '' fiat: bool def __init__(self, **kwargs): super().__init__(**kwargs) 然后是订单业务对象处理我们在交易所创建的订单。 ./models/order.pyfrom models.model import AbstractModel class Order(AbstractModel): BUY = 'BUY' SELL = 'SELL' TYPE_LIMIT = 'LIMIT' TYPE_MARKET = 'MARKET' TYPE_STOP_LOSS = 'STOP_LO...]]></description>
            <content:encoded><![CDATA[<figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/509e0c629c951292af63b41997bdb884c1c064f184ca08d19ff9877d1bab9427.jpg" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>在本系列的第一部分中，我们讨论了文件夹结构，添加了我们的第一个Price业务对象，并为策略抽象层进行编码。</p><p>下面将通过编码我们需要的货币和订单的业务对象开始。</p><p>此处，货币业务对象存储所有不同的加密货币或法定货币。</p><p><code>./models/order.py</code></p><pre data-type="codeBlock" text="from models.model import AbstractModel


class Currency(AbstractModel):
    name: str = &apos;&apos;
    symbol: str = &apos;&apos;
    fiat: bool

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
"><code><span class="hljs-keyword">from</span> models.model <span class="hljs-keyword">import</span> <span class="hljs-title">AbstractModel</span>


<span class="hljs-title">class</span> <span class="hljs-title">Currency</span>(<span class="hljs-title">AbstractModel</span>):
    <span class="hljs-title">name</span>: <span class="hljs-title">str</span> <span class="hljs-operator">=</span> <span class="hljs-string">''</span>
    <span class="hljs-title">symbol</span>: <span class="hljs-title">str</span> <span class="hljs-operator">=</span> <span class="hljs-string">''</span>
    <span class="hljs-title">fiat</span>: <span class="hljs-title"><span class="hljs-keyword">bool</span></span>

    <span class="hljs-title">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-title"><span class="hljs-built_in">self</span></span>, <span class="hljs-operator">*</span><span class="hljs-operator">*</span><span class="hljs-title">kwargs</span>):
        <span class="hljs-title"><span class="hljs-built_in">super</span></span>().<span class="hljs-title">__init__</span>(<span class="hljs-operator">*</span><span class="hljs-operator">*</span><span class="hljs-title">kwargs</span>)
</code></pre><p>然后是订单业务对象处理我们在交易所创建的订单。</p><p><code>./models/order.py</code></p><pre data-type="codeBlock" text="from models.model import AbstractModel


class Order(AbstractModel):
    BUY = &apos;BUY&apos;
    SELL = &apos;SELL&apos;

    TYPE_LIMIT = &apos;LIMIT&apos;
    TYPE_MARKET = &apos;MARKET&apos;
    TYPE_STOP_LOSS = &apos;STOP_LOSS&apos;
    TYPE_STOP_LOSS_LIMIT = &apos;STOP_LOSS_LIMIT&apos;
    TYPE_TAKE_PROFIT = &apos;TAKE_PROFIT&apos;
    TYPE_TAKE_PROFIT_LIMIT = &apos;TAKE_PROFIT_LIMIT&apos;
    TYPE_LIMIT_MAKER = &apos;LIMIT_MAKER&apos;

    uuid = &apos;&apos;
    side: str = &apos;&apos;
    type: str = TYPE_LIMIT
    symbol: str = &apos;&apos;
    currency: str = &apos;&apos;
    asset: str = &apos;&apos;
    price: float = 0
    quantity: int = 0
    test: bool = False

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
"><code>from models.model import AbstractModel


class Order(AbstractModel):
    <span class="hljs-attr">BUY</span> = <span class="hljs-string">'BUY'</span>
    <span class="hljs-attr">SELL</span> = <span class="hljs-string">'SELL'</span>

    <span class="hljs-attr">TYPE_LIMIT</span> = <span class="hljs-string">'LIMIT'</span>
    <span class="hljs-attr">TYPE_MARKET</span> = <span class="hljs-string">'MARKET'</span>
    <span class="hljs-attr">TYPE_STOP_LOSS</span> = <span class="hljs-string">'STOP_LOSS'</span>
    <span class="hljs-attr">TYPE_STOP_LOSS_LIMIT</span> = <span class="hljs-string">'STOP_LOSS_LIMIT'</span>
    <span class="hljs-attr">TYPE_TAKE_PROFIT</span> = <span class="hljs-string">'TAKE_PROFIT'</span>
    <span class="hljs-attr">TYPE_TAKE_PROFIT_LIMIT</span> = <span class="hljs-string">'TAKE_PROFIT_LIMIT'</span>
    <span class="hljs-attr">TYPE_LIMIT_MAKER</span> = <span class="hljs-string">'LIMIT_MAKER'</span>

    <span class="hljs-attr">uuid</span> = <span class="hljs-string">''</span>
    side: <span class="hljs-attr">str</span> = <span class="hljs-string">''</span>
    type: <span class="hljs-attr">str</span> = TYPE_LIMIT
    symbol: <span class="hljs-attr">str</span> = <span class="hljs-string">''</span>
    currency: <span class="hljs-attr">str</span> = <span class="hljs-string">''</span>
    asset: <span class="hljs-attr">str</span> = <span class="hljs-string">''</span>
    price: <span class="hljs-attr">float</span> = <span class="hljs-number">0</span>
    quantity: <span class="hljs-attr">int</span> = <span class="hljs-number">0</span>
    test: <span class="hljs-attr">bool</span> = <span class="hljs-literal">False</span>

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
</code></pre><p>然后构建交换抽象层，并开发我们的第一个连接器。由于这里不是编写API包装器的重点，因此为了方便起见，我们将使用非官方的Binance API包装器库python-binance（<code>https://python-binance.readthedocs.io/en/latest/binance.html</code>）。</p><p><code>./exchanges/exchange.py</code></p><pre data-type="codeBlock" text="import datetime
from api import utils
from abc import ABC, abstractmethod
from twisted.internet import reactor
from strategies.strategy import Strategy
from models.order import Order


class Exchange(ABC):
    currency: str
    asset: str
    strategy: Strategy

    def __init__(self, key: str, secret: str):
        self.apiKey = key
        self.apiSecret = secret
        self.name = None
        self.client = None
        self.socketManager = None
        self.socket = None
        self.currency = &apos;&apos;
        self.asset = &apos;&apos;
        self.strategy = None

    def set_currency(self, symbol: str):
        self.currency = symbol

    def set_asset(self, symbol: str):
        self.asset = symbol

    def set_strategy(self, strategy: Strategy):
        self.strategy = strategy

    def compute_symbol_pair(self):
        return utils.format_pair(self.currency, self.asset)

    # abstract methods

    # Override to set current exchange symbol pair notation (default with _ separator currency_asset ex: eur_btc)
    @abstractmethod
    def get_symbol(self):
        return self.compute_symbol_pair(self)

    # Get current symbol ticker
    @abstractmethod
    def symbol_ticker(self):
        pass

    # Get current symbol ticker candle for given interval
    @abstractmethod
    def symbol_ticker_candle(self, interval):
        pass

    # Get current symbol historic value
    @abstractmethod
    def historical_symbol_ticker_candle(self, start: datetime, end=None, interval=60):
        pass

    # Get balance for a given currency
    @abstractmethod
    def get_asset_balance(self, currency):
        pass

    # Create an exchange order
    @abstractmethod
    def order(self, order: Order):
        pass

    # Create an exchange test order
    @abstractmethod
    def test_order(self, order: Order):
        pass

    # Check an exchange order status
    @abstractmethod
    def check_order(self, orderId):
        pass

    # Cancel an exchange order
    @abstractmethod
    def cancel_order(self, orderId):
        pass

    # WebSocket related methods

    @abstractmethod
    def get_socket_manager(self, purchase):
        pass

    @abstractmethod
    def websocket_event_handler(self, msg):
        pass

    def start_socket(self):
        print(&apos;Starting WebSocket connection...&apos;)
        self.socketManager.start()

    def close_socket(self):
        self.socketManager.stop_socket(self.socket)
        self.socketManager.close()
        # properly terminate WebSocket
        reactor.stop()

    @abstractmethod
    def start_symbol_ticker_socket(self, symbol: str):
        pass
"><code><span class="hljs-keyword">import</span> datetime
<span class="hljs-keyword">from</span> api <span class="hljs-keyword">import</span> utils
<span class="hljs-keyword">from</span> abc <span class="hljs-keyword">import</span> ABC, abstractmethod
<span class="hljs-keyword">from</span> twisted.internet <span class="hljs-keyword">import</span> reactor
<span class="hljs-keyword">from</span> strategies.strategy <span class="hljs-keyword">import</span> Strategy
<span class="hljs-keyword">from</span> models.order <span class="hljs-keyword">import</span> Order


<span class="hljs-keyword">class</span> <span class="hljs-title class_">Exchange</span>(<span class="hljs-title class_ inherited__">ABC</span>):
    currency: <span class="hljs-built_in">str</span>
    asset: <span class="hljs-built_in">str</span>
    strategy: Strategy

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, key: <span class="hljs-built_in">str</span>, secret: <span class="hljs-built_in">str</span></span>):
        self.apiKey = key
        self.apiSecret = secret
        self.name = <span class="hljs-literal">None</span>
        self.client = <span class="hljs-literal">None</span>
        self.socketManager = <span class="hljs-literal">None</span>
        self.socket = <span class="hljs-literal">None</span>
        self.currency = <span class="hljs-string">''</span>
        self.asset = <span class="hljs-string">''</span>
        self.strategy = <span class="hljs-literal">None</span>

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">set_currency</span>(<span class="hljs-params">self, symbol: <span class="hljs-built_in">str</span></span>):
        self.currency = symbol

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">set_asset</span>(<span class="hljs-params">self, symbol: <span class="hljs-built_in">str</span></span>):
        self.asset = symbol

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">set_strategy</span>(<span class="hljs-params">self, strategy: Strategy</span>):
        self.strategy = strategy

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">compute_symbol_pair</span>(<span class="hljs-params">self</span>):
        <span class="hljs-keyword">return</span> utils.format_pair(self.currency, self.asset)

    <span class="hljs-comment"># abstract methods</span>

    <span class="hljs-comment"># Override to set current exchange symbol pair notation (default with _ separator currency_asset ex: eur_btc)</span>
    @abstractmethod
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_symbol</span>(<span class="hljs-params">self</span>):
        <span class="hljs-keyword">return</span> self.compute_symbol_pair(self)

    <span class="hljs-comment"># Get current symbol ticker</span>
    @abstractmethod
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">symbol_ticker</span>(<span class="hljs-params">self</span>):
        <span class="hljs-keyword">pass</span>

    <span class="hljs-comment"># Get current symbol ticker candle for given interval</span>
    @abstractmethod
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">symbol_ticker_candle</span>(<span class="hljs-params">self, interval</span>):
        <span class="hljs-keyword">pass</span>

    <span class="hljs-comment"># Get current symbol historic value</span>
    @abstractmethod
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">historical_symbol_ticker_candle</span>(<span class="hljs-params">self, start: datetime, end=<span class="hljs-literal">None</span>, interval=<span class="hljs-number">60</span></span>):
        <span class="hljs-keyword">pass</span>

    <span class="hljs-comment"># Get balance for a given currency</span>
    @abstractmethod
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_asset_balance</span>(<span class="hljs-params">self, currency</span>):
        <span class="hljs-keyword">pass</span>

    <span class="hljs-comment"># Create an exchange order</span>
    @abstractmethod
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">order</span>(<span class="hljs-params">self, order: Order</span>):
        <span class="hljs-keyword">pass</span>

    <span class="hljs-comment"># Create an exchange test order</span>
    @abstractmethod
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">test_order</span>(<span class="hljs-params">self, order: Order</span>):
        <span class="hljs-keyword">pass</span>

    <span class="hljs-comment"># Check an exchange order status</span>
    @abstractmethod
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">check_order</span>(<span class="hljs-params">self, orderId</span>):
        <span class="hljs-keyword">pass</span>

    <span class="hljs-comment"># Cancel an exchange order</span>
    @abstractmethod
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">cancel_order</span>(<span class="hljs-params">self, orderId</span>):
        <span class="hljs-keyword">pass</span>

    <span class="hljs-comment"># WebSocket related methods</span>

    @abstractmethod
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_socket_manager</span>(<span class="hljs-params">self, purchase</span>):
        <span class="hljs-keyword">pass</span>

    @abstractmethod
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">websocket_event_handler</span>(<span class="hljs-params">self, msg</span>):
        <span class="hljs-keyword">pass</span>

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">start_socket</span>(<span class="hljs-params">self</span>):
        <span class="hljs-built_in">print</span>(<span class="hljs-string">'Starting WebSocket connection...'</span>)
        self.socketManager.start()

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">close_socket</span>(<span class="hljs-params">self</span>):
        self.socketManager.stop_socket(self.socket)
        self.socketManager.close()
        <span class="hljs-comment"># properly terminate WebSocket</span>
        reactor.stop()

    @abstractmethod
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">start_symbol_ticker_socket</span>(<span class="hljs-params">self, symbol: <span class="hljs-built_in">str</span></span>):
        <span class="hljs-keyword">pass</span>
</code></pre><p>我们的第一个Binance API连接器。</p><p><code>./exchanges/binance.py</code></p><pre data-type="codeBlock" text="from datetime import datetime
from math import floor

from binance.client import Client
from binance.enums import *
from binance.websockets import BinanceSocketManager

from api import utils
from exchanges import exchange
from models.order import Order
from models.price import Price


class Binance(exchange.Exchange):
    def __init__(self, key: str, secret: str):
        super().__init__(key, secret)

        self.client = Client(self.apiKey, self.apiSecret)
        self.name = self.__class__.__name__

    def get_client(self):
        return self.client

    def get_symbol(self):
        return self.currency + self.asset

    def symbol_ticker(self):
        response = self.client.get_symbol_ticker(symbol=self.get_symbol())
        return Price(pair=self.get_symbol(), currency=self.currency.lower(), asset=self.asset.lower(), exchange=self.name.lower(),
                     current=response[&apos;price&apos;])

    def symbol_ticker_candle(self, interval=Client.KLINE_INTERVAL_1MINUTE):
        return self.client.get_klines(symbol=self.get_symbol(), interval=interval)

    def historical_symbol_ticker_candle(self, start: datetime, end=None, interval=Client.KLINE_INTERVAL_1MINUTE):
        # Convert default seconds interval to string like &quot;1m&quot;
        if isinstance(interval, int):
            interval = str(floor(interval/60)) + &apos;m&apos;

        output = []
        for candle in self.client.get_historical_klines_generator(self.get_symbol(), interval, start, end):
            output.append(
                Price(pair=self.compute_symbol_pair(), currency=self.currency.lower(), asset=self.asset.lower(), exchange=self.name.lower(),
                      current=candle[1], lowest=candle[3], highest=candle[2], volume=candle[5], openAt=utils.format_date(datetime.fromtimestamp(int(candle[0])/1000)))
            )

        return output

    def get_asset_balance(self, currency):
        response = self.client.get_asset_balance(currency)
        return response[&apos;free&apos;]

    def order(self, order: Order):
        return self.client.create_order(
            symbol=order.symbol,
            side=order.side,
            type=order.type,
            timeInForce=TIME_IN_FORCE_GTC,
            quantity=order.quantity,
            price=order.price
        )

    def test_order(self, order: Order):
        return self.client.create_test_order(
            symbol=order.symbol,
            side=order.side,
            type=order.type,
            timeInForce=TIME_IN_FORCE_GTC,
            quantity=order.quantity,
            price=order.price
        )

    def check_order(self, orderId):
        return self.client.get_order(
            symbol=self.get_symbol(),
            orderId=orderId
        )

    def cancel_order(self, orderId):
        return self.client.cancel_order(
            symbol=self.get_symbol(),
            orderId=orderId
        )

    def get_socket_manager(self):
        return BinanceSocketManager(self.client)

    def start_symbol_ticker_socket(self, symbol: str):
        self.socketManager = self.get_socket_manager()
        self.socket = self.socketManager.start_symbol_ticker_socket(
            symbol=self.get_symbol(),
            callback=self.websocket_event_handler
        )

        self.start_socket()

    def websocket_event_handler(self, msg):
        if msg[&apos;e&apos;] == &apos;error&apos;:
            print(msg)
            self.close_socket()
        else:
            self.strategy.set_price(
                Price(pair=self.compute_symbol_pair(), currency=self.currency, asset=self.asset, exchange=self.name,
                      current=msg[&apos;b&apos;], lowest=msg[&apos;l&apos;], highest=msg[&apos;h&apos;])
            )
            self.strategy.run()
"><code><span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime
<span class="hljs-keyword">from</span> math <span class="hljs-keyword">import</span> floor

<span class="hljs-keyword">from</span> binance.client <span class="hljs-keyword">import</span> Client
<span class="hljs-keyword">from</span> binance.enums <span class="hljs-keyword">import</span> *
<span class="hljs-keyword">from</span> binance.websockets <span class="hljs-keyword">import</span> BinanceSocketManager

<span class="hljs-keyword">from</span> api <span class="hljs-keyword">import</span> utils
<span class="hljs-keyword">from</span> exchanges <span class="hljs-keyword">import</span> exchange
<span class="hljs-keyword">from</span> models.order <span class="hljs-keyword">import</span> Order
<span class="hljs-keyword">from</span> models.price <span class="hljs-keyword">import</span> Price


<span class="hljs-keyword">class</span> <span class="hljs-title class_">Binance</span>(exchange.Exchange):
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, key: <span class="hljs-built_in">str</span>, secret: <span class="hljs-built_in">str</span></span>):
        <span class="hljs-built_in">super</span>().__init__(key, secret)

        self.client = Client(self.apiKey, self.apiSecret)
        self.name = self.__class__.__name__

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_client</span>(<span class="hljs-params">self</span>):
        <span class="hljs-keyword">return</span> self.client

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_symbol</span>(<span class="hljs-params">self</span>):
        <span class="hljs-keyword">return</span> self.currency + self.asset

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">symbol_ticker</span>(<span class="hljs-params">self</span>):
        response = self.client.get_symbol_ticker(symbol=self.get_symbol())
        <span class="hljs-keyword">return</span> Price(pair=self.get_symbol(), currency=self.currency.lower(), asset=self.asset.lower(), exchange=self.name.lower(),
                     current=response[<span class="hljs-string">'price'</span>])

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">symbol_ticker_candle</span>(<span class="hljs-params">self, interval=Client.KLINE_INTERVAL_1MINUTE</span>):
        <span class="hljs-keyword">return</span> self.client.get_klines(symbol=self.get_symbol(), interval=interval)

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">historical_symbol_ticker_candle</span>(<span class="hljs-params">self, start: datetime, end=<span class="hljs-literal">None</span>, interval=Client.KLINE_INTERVAL_1MINUTE</span>):
        <span class="hljs-comment"># Convert default seconds interval to string like "1m"</span>
        <span class="hljs-keyword">if</span> <span class="hljs-built_in">isinstance</span>(interval, <span class="hljs-built_in">int</span>):
            interval = <span class="hljs-built_in">str</span>(floor(interval/<span class="hljs-number">60</span>)) + <span class="hljs-string">'m'</span>

        output = []
        <span class="hljs-keyword">for</span> candle <span class="hljs-keyword">in</span> self.client.get_historical_klines_generator(self.get_symbol(), interval, start, end):
            output.append(
                Price(pair=self.compute_symbol_pair(), currency=self.currency.lower(), asset=self.asset.lower(), exchange=self.name.lower(),
                      current=candle[<span class="hljs-number">1</span>], lowest=candle[<span class="hljs-number">3</span>], highest=candle[<span class="hljs-number">2</span>], volume=candle[<span class="hljs-number">5</span>], openAt=utils.format_date(datetime.fromtimestamp(<span class="hljs-built_in">int</span>(candle[<span class="hljs-number">0</span>])/<span class="hljs-number">1000</span>)))
            )

        <span class="hljs-keyword">return</span> output

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_asset_balance</span>(<span class="hljs-params">self, currency</span>):
        response = self.client.get_asset_balance(currency)
        <span class="hljs-keyword">return</span> response[<span class="hljs-string">'free'</span>]

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">order</span>(<span class="hljs-params">self, order: Order</span>):
        <span class="hljs-keyword">return</span> self.client.create_order(
            symbol=order.symbol,
            side=order.side,
            <span class="hljs-built_in">type</span>=order.<span class="hljs-built_in">type</span>,
            timeInForce=TIME_IN_FORCE_GTC,
            quantity=order.quantity,
            price=order.price
        )

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">test_order</span>(<span class="hljs-params">self, order: Order</span>):
        <span class="hljs-keyword">return</span> self.client.create_test_order(
            symbol=order.symbol,
            side=order.side,
            <span class="hljs-built_in">type</span>=order.<span class="hljs-built_in">type</span>,
            timeInForce=TIME_IN_FORCE_GTC,
            quantity=order.quantity,
            price=order.price
        )

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">check_order</span>(<span class="hljs-params">self, orderId</span>):
        <span class="hljs-keyword">return</span> self.client.get_order(
            symbol=self.get_symbol(),
            orderId=orderId
        )

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">cancel_order</span>(<span class="hljs-params">self, orderId</span>):
        <span class="hljs-keyword">return</span> self.client.cancel_order(
            symbol=self.get_symbol(),
            orderId=orderId
        )

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_socket_manager</span>(<span class="hljs-params">self</span>):
        <span class="hljs-keyword">return</span> BinanceSocketManager(self.client)

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">start_symbol_ticker_socket</span>(<span class="hljs-params">self, symbol: <span class="hljs-built_in">str</span></span>):
        self.socketManager = self.get_socket_manager()
        self.socket = self.socketManager.start_symbol_ticker_socket(
            symbol=self.get_symbol(),
            callback=self.websocket_event_handler
        )

        self.start_socket()

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">websocket_event_handler</span>(<span class="hljs-params">self, msg</span>):
        <span class="hljs-keyword">if</span> msg[<span class="hljs-string">'e'</span>] == <span class="hljs-string">'error'</span>:
            <span class="hljs-built_in">print</span>(msg)
            self.close_socket()
        <span class="hljs-keyword">else</span>:
            self.strategy.set_price(
                Price(pair=self.compute_symbol_pair(), currency=self.currency, asset=self.asset, exchange=self.name,
                      current=msg[<span class="hljs-string">'b'</span>], lowest=msg[<span class="hljs-string">'l'</span>], highest=msg[<span class="hljs-string">'h'</span>])
            )
            self.strategy.run()
</code></pre><p>现在，只需调用我们的策略启动方法，我们便拥有了一个极简而强大的交易机器人系统。使用自己的指标和策略，您可以通过传递订单来开始买卖。但是，在开始编写自己的策略代码之前，请确保对其进行测试并安全运行。所以你需要一个系统来做到这一点。</p><p>在下一部分中，我们将实现回测模式，以针对交易所的历史数据来测试您的策略，并提供导入这些数据的服务。</p><p>另外，我们将所有这些部分与全局配置和一些依赖项一起打包进命令行工具中。</p><p>最后我们将通过查看如何对该代码进行容器化和工业化实现来进一步介绍。</p>]]></content:encoded>
            <author>quantbang@newsletter.paragraph.com (quantbang)</author>
        </item>
        <item>
            <title><![CDATA[用 Python 和币安 API 构建数字货币交易机器人（一）]]></title>
            <link>https://paragraph.com/@quantbang/python-api</link>
            <guid>1x9kQhlzH9gYMgTmFMBw</guid>
            <pubDate>Thu, 24 Mar 2022 12:13:25 GMT</pubDate>
            <description><![CDATA[交易数字货币或任何其他资产的首要任务是要有目标和策略来实现。在这里，我们不是在谈论交易策略，而只是构建一个简单而功能强大的数字货币交易机器人来应用您的策略。本系列更像是一个数字货币自动交易机器人框架。 我们将使用python 3.9（3.9.2）首先创建项目文件结构。 /exchanges /strategies /models 在这里，“ exchanges”文件夹存储了Exchange API包装器，为您的策略制定策略并为我们将要使用的业务对象建模。 模型 我们将为此项目定义几个业务对象，例如价格，货币类型或订单。首先，让我们从价格开始，将其用于我们随后将要创建的策略。 在我们为业务对象编写通用抽象层之前： ./models/model.pyfrom datetime import datetime class AbstractModel: created: datetime def __init__(self, **kwargs): for key, value in kwargs.items(): setattr(self, key, value) 然后是我们的第一个Pr...]]></description>
            <content:encoded><![CDATA[<figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/0a50f471221ce17f53fdde6ddcaeda1dadef131aa540bd9f62d4098a6777b4b0.jpg" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>交易数字货币或任何其他资产的首要任务是要有目标和策略来实现。在这里，我们不是在谈论交易策略，而只是构建一个简单而功能强大的数字货币交易机器人来应用您的策略。本系列更像是一个数字货币自动交易机器人框架。</p><p>我们将使用python 3.9（3.9.2）首先创建项目文件结构。</p><p><code>/exchanges</code></p><p><code>/strategies</code></p><p><code>/models</code></p><p>在这里，“ exchanges”文件夹存储了Exchange API包装器，为您的策略制定策略并为我们将要使用的业务对象建模。</p><p><strong>模型</strong></p><p>我们将为此项目定义几个业务对象，例如价格，货币类型或订单。首先，让我们从价格开始，将其用于我们随后将要创建的策略。</p><p>在我们为业务对象编写通用抽象层之前：</p><p><code>./models/model.py</code></p><pre data-type="codeBlock" text="from datetime import datetime


class AbstractModel:
    created: datetime

    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)
"><code><span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime


<span class="hljs-keyword">class</span> <span class="hljs-title class_">AbstractModel</span>:
    created: datetime

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, **kwargs</span>):
        <span class="hljs-keyword">for</span> key, value <span class="hljs-keyword">in</span> kwargs.items():
            <span class="hljs-built_in">setattr</span>(self, key, value)
</code></pre><p>然后是我们的第一个Price类：</p><p><code>./models/price</code></p><pre data-type="codeBlock" text="from models.model import AbstractModel


class Price(AbstractModel):
    pair: str = &apos;&apos;
    exchange: str = &apos;&apos;
    current: float = 0
    lowest: float = 0
    highest: float = 0
    currency: str = &apos;&apos;
    asset: str = &apos;&apos;

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.pair = self.get_pair()

    def get_pair(self):
        return self.currency + &apos;_&apos; + self.asset
"><code><span class="hljs-keyword">from</span> models.model <span class="hljs-keyword">import</span> <span class="hljs-title">AbstractModel</span>


<span class="hljs-title">class</span> <span class="hljs-title">Price</span>(<span class="hljs-title">AbstractModel</span>):
    <span class="hljs-title">pair</span>: <span class="hljs-title">str</span> <span class="hljs-operator">=</span> <span class="hljs-string">''</span>
    <span class="hljs-title">exchange</span>: <span class="hljs-title">str</span> <span class="hljs-operator">=</span> <span class="hljs-string">''</span>
    <span class="hljs-title">current</span>: <span class="hljs-title">float</span> <span class="hljs-operator">=</span> 0
    <span class="hljs-title">lowest</span>: <span class="hljs-title">float</span> <span class="hljs-operator">=</span> 0
    <span class="hljs-title">highest</span>: <span class="hljs-title">float</span> <span class="hljs-operator">=</span> 0
    <span class="hljs-title">currency</span>: <span class="hljs-title">str</span> <span class="hljs-operator">=</span> <span class="hljs-string">''</span>
    <span class="hljs-title">asset</span>: <span class="hljs-title">str</span> <span class="hljs-operator">=</span> <span class="hljs-string">''</span>

    <span class="hljs-title">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-title"><span class="hljs-built_in">self</span></span>, <span class="hljs-operator">*</span><span class="hljs-operator">*</span><span class="hljs-title">kwargs</span>):
        <span class="hljs-title"><span class="hljs-built_in">super</span></span>().<span class="hljs-title">__init__</span>(<span class="hljs-operator">*</span><span class="hljs-operator">*</span><span class="hljs-title">kwargs</span>)
        <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">pair</span> <span class="hljs-operator">=</span> <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">get_pair</span>()

    <span class="hljs-title">def</span> <span class="hljs-title">get_pair</span>(<span class="hljs-title"><span class="hljs-built_in">self</span></span>):
        <span class="hljs-title"><span class="hljs-keyword">return</span></span> <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">currency</span> <span class="hljs-operator">+</span> <span class="hljs-string">'_'</span> <span class="hljs-operator">+</span> <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">asset</span>
</code></pre><p><strong>策略部分</strong></p><p>这个系统的最大部分——策略将负责运行您的交易逻辑。有两种方法，一种是经典的利用时间间隔然后运行对外部交易所API的调用，或者运行内部webhooks、路由，以及 使用WebSocket的实时事件。</p><p>为此，我们将首先创建一个抽象策略类，该策略类将进行扩展。</p><p><code>./strategies/strategy.py</code></p><pre data-type="codeBlock" text="import json
import threading
import time
from datetime import datetime
from decouple import config
from models.price import Price


class Strategy(object):
    price: Price

    def __init__(self, exchange, interval=60, *args, **kwargs):
        self._timer = None
        self.interval = interval
        self.args = args
        self.kwargs = kwargs
        self.is_running = False
        self.next_call = time.time()
        self.portfolio = {}
        self.exchange = exchange
        # Load account portfolio for pair at load
        self.get_portfolio()

    def _run(self):
        self.is_running = False
        self.start()
        self.run(*self.args, **self.kwargs)

    def start(self):
        if not self.is_running:
            print(datetime.now())
            if self._timer is None:
                self.next_call = time.time()
            else:
                self.next_call += self.interval

            self._timer = threading.Timer(self.next_call - time.time(), self._run)
            self._timer.start()
            self.is_running = True

    def stop(self):
        self._timer.cancel()
        self.is_running = False

    def get_portfolio(self):
        self.portfolio = {&apos;currency&apos;: self.exchange.get_asset_balance(self.exchange.currency),
                          &apos;asset&apos;: self.exchange.get_asset_balance(self.exchange.asset)}

    def get_price(self):
        try:
            self.price = self.exchange.symbol_ticker()
        except Exception as e:
            pass

...
"><code><span class="hljs-keyword">import</span> json
<span class="hljs-keyword">import</span> threading
<span class="hljs-keyword">import</span> time
<span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime
<span class="hljs-keyword">from</span> decouple <span class="hljs-keyword">import</span> config
<span class="hljs-keyword">from</span> models.price <span class="hljs-keyword">import</span> Price


<span class="hljs-keyword">class</span> <span class="hljs-title class_">Strategy</span>(<span class="hljs-title class_ inherited__">object</span>):
    price: Price

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, exchange, interval=<span class="hljs-number">60</span>, *args, **kwargs</span>):
        self._timer = <span class="hljs-literal">None</span>
        self.interval = interval
        self.args = args
        self.kwargs = kwargs
        self.is_running = <span class="hljs-literal">False</span>
        self.next_call = time.time()
        self.portfolio = {}
        self.exchange = exchange
        <span class="hljs-comment"># Load account portfolio for pair at load</span>
        self.get_portfolio()

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">_run</span>(<span class="hljs-params">self</span>):
        self.is_running = <span class="hljs-literal">False</span>
        self.start()
        self.run(*self.args, **self.kwargs)

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">start</span>(<span class="hljs-params">self</span>):
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> self.is_running:
            <span class="hljs-built_in">print</span>(datetime.now())
            <span class="hljs-keyword">if</span> self._timer <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span>:
                self.next_call = time.time()
            <span class="hljs-keyword">else</span>:
                self.next_call += self.interval

            self._timer = threading.Timer(self.next_call - time.time(), self._run)
            self._timer.start()
            self.is_running = <span class="hljs-literal">True</span>

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">stop</span>(<span class="hljs-params">self</span>):
        self._timer.cancel()
        self.is_running = <span class="hljs-literal">False</span>

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_portfolio</span>(<span class="hljs-params">self</span>):
        self.portfolio = {<span class="hljs-string">'currency'</span>: self.exchange.get_asset_balance(self.exchange.currency),
                          <span class="hljs-string">'asset'</span>: self.exchange.get_asset_balance(self.exchange.asset)}

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_price</span>(<span class="hljs-params">self</span>):
        <span class="hljs-keyword">try</span>:
            self.price = self.exchange.symbol_ticker()
        <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
            <span class="hljs-keyword">pass</span>

...
</code></pre><p>在这里，我们的策略抽象层构造函数签名需要一个Exchange实例，我们将在稍后使用Binance API编写第一个包装器来完成这一部分。</p><p>我们在没有任何额外库的情况下定义了一个简单但功能强大的无限间隔运行器，请注意，每次运行都会在另一个线程上启动下一个调用，但是实际上，您的策略将永远使用不超过两个线程，如果您使用一个主线程和当前运行策略需要大量的外部调用或繁重的计算。每个线程消耗0.3％的RAM和0或0.1 的CPU使用率，这还涉及以下策略：获取报价单和订单，然后将价格和订单相关的数据存储在另一个内部API中。</p><p>虽然间隔运行精度可以在微秒上漂移一点，但是在秒级别上将保持稳定。</p><p>这是该层的简单用法，它是一种基本打印您的兑换账户投资组合和兑换价格的策略。我们提供了一种方法，该方法可以从以后要连接的外部交易所中检索交易品种代码，还可以找到一种方法来检索您在已连接的交易所中可用的当前投资组合。</p><p><code>./strategies/watcher.py</code></p><pre data-type="codeBlock" text="from exchanges.exchange import Exchange
from strategies.strategy import Strategy


class Watcher(Strategy):
    def __init__(self, exchange: Exchange, timeout=60, *args, **kwargs):
        super().__init__(exchange, timeout, *args, **kwargs)

    def run(self):
        self.get_price()
        print(&apos;*******************************&apos;)
        print(&apos;Exchange: &apos;, self.exchange.name)
        print(&apos;Pair: &apos;, self.exchange.get_symbol())
        print(&apos;Available: &apos;, self.portfolio[&apos;currency&apos;] + &apos; &apos; + self.exchange.currency)
        print(&apos;Available: &apos;, self.portfolio[&apos;asset&apos;] + &apos; &apos; + self.exchange.asset)
        print(&apos;Price: &apos;, self.price.current)
"><code><span class="hljs-keyword">from</span> exchanges.exchange <span class="hljs-keyword">import</span> <span class="hljs-title">Exchange</span>
<span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">strategies</span>.<span class="hljs-title">strategy</span> <span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">Strategy</span>


<span class="hljs-title">class</span> <span class="hljs-title">Watcher</span>(<span class="hljs-title">Strategy</span>):
    <span class="hljs-title">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-title"><span class="hljs-built_in">self</span></span>, <span class="hljs-title">exchange</span>: <span class="hljs-title">Exchange</span>, <span class="hljs-title">timeout</span><span class="hljs-operator">=</span>60, <span class="hljs-operator">*</span><span class="hljs-title">args</span>, <span class="hljs-operator">*</span><span class="hljs-operator">*</span><span class="hljs-title">kwargs</span>):
        <span class="hljs-title"><span class="hljs-built_in">super</span></span>().<span class="hljs-title">__init__</span>(<span class="hljs-title">exchange</span>, <span class="hljs-title">timeout</span>, <span class="hljs-operator">*</span><span class="hljs-title">args</span>, <span class="hljs-operator">*</span><span class="hljs-operator">*</span><span class="hljs-title">kwargs</span>)

    <span class="hljs-title">def</span> <span class="hljs-title">run</span>(<span class="hljs-title"><span class="hljs-built_in">self</span></span>):
        <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">get_price</span>()
        <span class="hljs-title">print</span>(<span class="hljs-string">'*******************************'</span>)
        <span class="hljs-title">print</span>(<span class="hljs-string">'Exchange: '</span>, <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">exchange</span>.<span class="hljs-title">name</span>)
        <span class="hljs-title">print</span>(<span class="hljs-string">'Pair: '</span>, <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">exchange</span>.<span class="hljs-title">get_symbol</span>())
        <span class="hljs-title">print</span>(<span class="hljs-string">'Available: '</span>, <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">portfolio</span>[<span class="hljs-string">'currency'</span>] <span class="hljs-operator">+</span> <span class="hljs-string">' '</span> <span class="hljs-operator">+</span> <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">exchange</span>.<span class="hljs-title">currency</span>)
        <span class="hljs-title">print</span>(<span class="hljs-string">'Available: '</span>, <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">portfolio</span>[<span class="hljs-string">'asset'</span>] <span class="hljs-operator">+</span> <span class="hljs-string">' '</span> <span class="hljs-operator">+</span> <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">exchange</span>.<span class="hljs-title">asset</span>)
        <span class="hljs-title">print</span>(<span class="hljs-string">'Price: '</span>, <span class="hljs-title"><span class="hljs-built_in">self</span></span>.<span class="hljs-title">price</span>.<span class="hljs-title">current</span>)
</code></pre><p>在接下来的部分中，我们将通过使用币安官方API：</p><pre data-type="codeBlock" text="https://github.com/binance/binance/binance-spot-api-docs
"><code><span class="hljs-symbol">https:</span>/<span class="hljs-regexp">/github.com/binance</span><span class="hljs-regexp">/binance/binance</span>-spot-api-docs
</code></pre><p>和Python request库：</p><pre data-type="codeBlock" text="https://pypi.org/project/requests/
"><code><span class="hljs-symbol">https:</span>/<span class="hljs-regexp">/pypi.org/project</span><span class="hljs-regexp">/requests/</span>
</code></pre><p>对一个简单的策略进行编码，来连接我们的第一个Exchange Binance。然后，对业务对象订单和货币进行编码，以分别存储您发送给交易所的订单和我们将要操作的法定货币或数字货币，然后将所有这些不同的货币放在一起。</p><p>感谢您的阅读，敬请期待下一部分。</p>]]></content:encoded>
            <author>quantbang@newsletter.paragraph.com (quantbang)</author>
        </item>
    </channel>
</rss>