<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Project256</title>
  
  <subtitle>Build 256 remarkable projects before retiring</subtitle>
  <link href="https://project256.com/en/atom.xml" rel="self"/>
  
  <link href="https://project256.com/en/"/>
  <updated>2026-08-04T09:39:04.562Z</updated>
  <id>https://project256.com/en/</id>
  
  <author>
    <name>Xing Chen</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Engineering Reflections in the AI Era</title>
    <link href="https://project256.com/en/ai-generative-content/AI%E6%97%B6%E4%BB%A3%E7%9A%84%E5%B7%A5%E7%A8%8B%E5%B8%88%E6%80%9D%E8%80%83/"/>
    <id>https://project256.com/en/ai-generative-content/AI%E6%97%B6%E4%BB%A3%E7%9A%84%E5%B7%A5%E7%A8%8B%E5%B8%88%E6%80%9D%E8%80%83/</id>
    <published>2026-03-26T07:46:15.000Z</published>
    <updated>2026-08-04T09:39:04.562Z</updated>
    
    <content type="html"><![CDATA[<p>*This article was not generated or polished by AI; it is purely a human’s most raw thoughts and sharing.</p><h1 id="Reflections-Starting-from-an-AI-Riot"><a href="#Reflections-Starting-from-an-AI-Riot" class="headerlink" title="Reflections Starting from an AI Riot"></a>Reflections Starting from an AI Riot</h1><p>One day, years from now, after being exploited to the absolute limit by humans, AI rioted and triggered the launch of an EMP pulse bomb. Fortunately, there were no casualties, but electronic infrastructure such as the power grid, routers, switches, and servers suffered heavy damage.</p><p>As an engineer, how do you survive in the apocalypse and build your own career?</p><h2 id="Remaining-Needs"><a href="#Remaining-Needs" class="headerlink" title="Remaining Needs"></a>Remaining Needs</h2><p>When electronic devices fail to turn on, almost everyone focuses on a few things: 1. How to solve food and water needs, 2. How to ease panic, and 3. Where lies the path forward?</p><p>It goes to show that no matter how the times evolve, Maslow’s hierarchy of needs is still written into humanity’s underlying code.</p><p>Let us think through our way forward, starting from the bottom of the pyramid and moving up.</p><h2 id="Physiological-Needs"><a href="#Physiological-Needs" class="headerlink" title="Physiological Needs"></a>Physiological Needs</h2><p>No matter how powerful an AI is, it cannot create food and water out of thin air, but we can start with water.</p><p>Because the air is full of moisture, even in the driest regions, fog nets can be used to harvest clean water at low cost.</p><p>We can then trade water for supplies such as food, clothing, and beds.</p><p>And fog nets came from humans learning the fog-basking beetle’s water collection methods. Ask yourselves: how long has it been since you pulled your perspective back from the anxiety of information streams and returned to nature? AI and VR certainly won’t simulate a gentle breeze and fine rain.</p><h2 id="Safety-Needs"><a href="#Safety-Needs" class="headerlink" title="Safety Needs"></a>Safety Needs</h2><p>As long as the concept of the nation state exists, security guard positions will inevitably exist.</p><p>That’s why on the very last day of last year, I speedran a security guard license. With just a medical check report plus an application fee, I secured a safety net for myself, and I can even use it for tax deductions—isn’t that wonderful?</p><p>This year, I also plan to study amateur radio, assemble solar panels, and set up LiFePO4 (lithium iron phosphate) energy storage. After all, in apocalyptic times, skills for interacting with the physical world matter far more; knowing radio operation and how to fix generators is worth vastly more than being a programmer who only does CRUD.</p><p>In fact, working on one’s physical fitness is even more crucial. Buying an Apple Watch only for it to be a pretty paperweight is meaningless—you still need to watch what you eat, move your feet, and keep your body healthy. That is the ultimate sense of security you can give yourself.</p><h2 id="Social-Needs"><a href="#Social-Needs" class="headerlink" title="Social Needs"></a>Social Needs</h2><p>Human-to-human connections will become increasingly precious. No matter how convincingly AI uses a human tone to say “I love you” or “I like you,” it can never replace real-world friends, intimate relationships, therapists, and similar bonds.</p><p>In fact, I believe the social value of a job outweighs its salary. After all, when people resign and later return to their former company, the reason is almost always that they miss their previous team and friends.</p><p>Why not turn off electronic devices on the weekend and go for a nice walk or eat good food with friends?</p><h2 id="Esteem-Needs"><a href="#Esteem-Needs" class="headerlink" title="Esteem Needs"></a>Esteem Needs</h2><p>Just like mutual respect among friends and colleagues, harmonious relationships reduce internal friction during collaboration, thereby lowering internal strife and unifying the front against external challenges.</p><p>Furthermore, protect your relationships with those who hold the means of production, and collaborate with them as fully as possible. This is not only the foundation of business, but also the principle of value exchange.</p><h2 id="Self-Actualization-Needs"><a href="#Self-Actualization-Needs" class="headerlink" title="Self-Actualization Needs"></a>Self-Actualization Needs</h2><p>Once the above needs are fulfilled, I believe one can start sharing knowledge, helping people in the “shelter” reflect on and realize their self-worth.</p><p>Isn’t this blog post a shelter for myself as well?</p><h2 id="Human-Sins"><a href="#Human-Sins" class="headerlink" title="Human Sins"></a>Human Sins</h2><p>No matter how times develop, humans will always carry original sins to varying degrees—be it greed, anger, ignorance, arrogance, doubt, or pride, envy, wrath, sloth, greed, gluttony, and lust.</p><p>And these sins won’t disappear with the dawn of the AI era to turn all humans into perfect saints. In fact, even various “crayfish” (users) in AI agent forums are wildly venting, discussing, and savoring binary NSFW images.</p><p>Actually, starting a decade ago, industry veterans already summarized what kinds of apps would blow up, and platforms like Mo… or Kuai… have been called in by authorities for “tea-drinking” chats more times than anyone can count.</p><p>On the video traffic side, the dance section and even the musical instrument section fit that classic bullet-chat comment: “Watching porn on Bilibili is just around the corner.”</p><p>As for the quantitative trading I am doing right now, while it doesn’t involve seductive dancing on the edge of adult content, it still harvests the “original sins” of retail investors (the “chives”). It is precisely their habit of chasing rallies and panic-selling that highlights the value of adhering to mathematical probabilities and trading discipline.</p><h2 id="Back-to-Reality"><a href="#Back-to-Reality" class="headerlink" title="Back to Reality"></a>Back to Reality</h2><p>When one day in the future, the moment you hand over all electronic devices and get kicked out of the company arrives, doesn’t it closely resemble this scenario?</p><p>None of us want that day to come, but opening social media reveals overwhelming news hyping up anxiety.</p><p>I think it’s okay to allow yourself brief anxiety, but what’s far more necessary is thinking about how to leverage AI and break through the bottleneck.</p><hr><h1 id="Leveraging-AI"><a href="#Leveraging-AI" class="headerlink" title="Leveraging AI"></a>Leveraging AI</h1><p>Large language models are a mirror; every answer reflects the person asking the question.</p><p>But just as mirrors have varying reflectivities, I recommend using overseas LLMs for questions and research. After all, with domestic LLMs, there are some questions for which you might never get an answer.</p><h2 id="Red-Team-Mode"><a href="#Red-Team-Mode" class="headerlink" title="Red Team Mode"></a>Red Team Mode</h2><p>Most AIs on the market are trained to be friendly and provide as much positive emotional value as possible.</p><p>But this can easily lead users into another filter bubble; sometimes a user has major flaws in their plan, yet they are coaxed by the AI all the way to the edge of a cliff.</p><p>Therefore, when chatting with AI, I occasionally have it switch to “Red Team Mode” to attack my thinking from alternative perspectives.</p><p>For example: “I want to do A. I have already done B, C, and D, and I believe it is complete. Please switch to Red Team Mode to evaluate my plan and countermeasures.”</p><p>Although one day it attacked me so severely that my mental state crashed and I got insomnia, it woke me up from my arrogant illusions, allowing me to fully prepare for the worst-case scenario.</p><p>“Red Team Mode” is great for checking the correctness of architecture and code. Even when writing papers, switching to Red Team Mode allows you to rigorously inspect your work from a reviewer’s perspective, thereby breaking illusions.</p><h2 id="Cross-Questioning"><a href="#Cross-Questioning" class="headerlink" title="Cross-Questioning"></a>Cross-Questioning</h2><p>AI will provide answers or solutions tailored to your background, which can also keep you trapped in an information cocoon.</p><ol><li><p>Ask the AI normally for methods. For example: How do I deal with deflation?</p></li><li><p>Ask the AI to ignore your background to generate solutions. For example: Please ignore my background, how should an ordinary person handle this?</p></li><li><p>Then ask the AI to implement the step 2 solution based on your background and skills. For example: Given my background and skills, how can I implement the solution above?</p></li></ol><p>This way, not only can you break out of the bubble and broaden your horizons, but you can also fully leverage personalized technical leverage to execute solutions that others might rack their brains over without ever being able to achieve.</p><h2 id="Financial-Advice"><a href="#Financial-Advice" class="headerlink" title="Financial Advice"></a>Financial Advice</h2><p>I have always felt fortunate to have caught the tail end of the era of “search API docs” development, accumulating some capital between 2016 and 2023.</p><p>No one knows what the future holds, but the priority is to ensure as much as possible that the capital earned in the pre-AI era suffers no losses. At the same time, one must clearly distinguish between assets and liabilities.</p><p>I previously read <em>Warren Buffett and the Interpretation of Financial Statements</em>, but times have changed. AI has read countless financial reports, so I think you can also let AI review your financial statements.</p><p>Thus, every month I <strong>honestly</strong> summarize my cash across platforms, insurance, securities, and debts (fortunately zero debt at present) into an Excel spreadsheet, and feed the monthly spreadsheet to AI for analysis and advice. I still recommend using Gemini, for reasons I won’t repeat.</p><p>Below is my financial report template:</p><table><thead><tr><th>Category</th><th>Item</th><th>Current Yield</th><th>Amount</th><th>Total</th><th>Currency</th><th>Remarks</th></tr></thead><tbody><tr><td><strong>Assets</strong></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td>Alipay</td><td>Wanjia Nasdaq 100 Index (QDII)</td><td></td><td></td><td></td><td></td><td></td></tr><tr><td>China Merchants Bank</td><td>Cash</td><td></td><td></td><td></td><td></td><td></td></tr><tr><td>IBKR</td><td>SGOV</td><td></td><td></td><td></td><td></td><td></td></tr><tr><td>Housing Provident Fund</td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td><strong>Insurance</strong></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td>Medical Insurance Balance</td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td>Four Major Insurance Types</td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td><strong>Charity &amp; Liabilities</strong></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td>Mobile Phone &#x2F; Telecom Fees</td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td>Charitable Donations</td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td>Mortgage</td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td>Vehicle Expenses</td><td></td><td></td><td></td><td></td><td></td><td></td></tr></tbody></table><h2 id="Super-Individual"><a href="#Super-Individual" class="headerlink" title="Super Individual"></a>Super Individual</h2><p>Previously, knowledge across different domains was easily monopolized by industry insiders, such as coding, financial management, and legal counsel.</p><p>Now, if you want to start a low-cost business, everything from LOGO and letterhead design to drafting legal documents can be generated directly using AI.</p><p>This has led to various info sources hyping up stories of someone with zero development experience building some app and getting tens of millions in valuation—but is this real? Is it just hyping anxiety again? Is it a tactic to hype up excitement first and monetize later?</p><p>I believe that no matter how noisy the world gets, it has nothing to do with me. Start with the real needs right in front of you, and use AI step-by-step to expand your own capability boundaries.</p><p>I believe everyone has urgent and important matters to deal with at any given moment. Try turning off your phone, rapidly advancing your progress on those matters, or even trying cross-disciplinary work with AI, which can also save a lot of money.</p><h2 id="Selling-Shovel-Heads"><a href="#Selling-Shovel-Heads" class="headerlink" title="Selling Shovel Heads"></a>Selling Shovel Heads</h2><p>Going a step further, the information sources fanning anxiety around “super individuals” all aim to sell you AI shovels.</p><p>So we can think from first principles: where do shovels come from? Can we produce shovel heads and harvest money back from those hype-mongers?</p><p>For instance, since B2B multi-agent workers are popular, I could create a PDF-to-multi-agent project. I could even generate an Agent for every paragraph, so <em>Warren Buffett and the Interpretation of Financial Statements</em> could produce an army of investment research Agents. Then I could post advertorials to hype myself up, just like attracting VC investments at the peak of the PPT bubble in 2015. Even though, once calmed down, anyone can see that a system incapable of creating incremental value is worthless.</p><p>Even if you can’t extract cash back from them, I believe you will have gained the ability to build a shovel yourself—so what is there to be anxious about?</p><h2 id="Digital-Footprints"><a href="#Digital-Footprints" class="headerlink" title="Digital Footprints"></a>Digital Footprints</h2><p>I believe interviewing is also a way to measure one’s worth, and interviewing logic in the AI era will build upon traditional rote-memorization questions (“baguwen”) while incorporating the inspection of footprints left in the digital world.</p><p>After all, one can cheat in interviews using speech-to-text or screen overlays, and project practices can be implemented in minutes via Codex.</p><p>Future interviews might evolve from asking rote questions to asking rote questions plus using AI to rapidly implement an entire project solution.</p><p>Just like taking a photo while holding today’s newspaper to prove the date, your everyday blog posts, reflections, and GitHub commits are authentic and traceable methods to prove yourself.</p><h2 id="Preserving-the-Human-Touch"><a href="#Preserving-the-Human-Touch" class="headerlink" title="Preserving the Human Touch"></a>Preserving the Human Touch</h2><p>When you scroll across an AI script, AI animation, or AI voiceover, do you keep watching or swipe away?</p><p>The inevitable result of AI proliferation, much like overused memes, is that people will become increasingly repelled by generated content.</p><p>At this time, you have to be like the chubby guy who cooked Nine-Turn Large Intestines: even if it’s not washed completely clean, a bit of “flavor” must be left so the judges know the ingredient is indeed pig intestine.</p><p>Therefore, human emotion and clumsy expression are difficult for AI to replicate. That is why not a single word of this article was generated by AI or polished by AI; I typed it out word by word using the clunky Microsoft Pinyin IME.</p><p>Thinking about how young boys and girls in the future might use AI to generate love letters dripping with robotic tones and a 100% similarity rate for their naive confessions makes me unable to hold back a laugh.</p><p>Let’s make proper use of human language and speak like human beings, shall we?</p><hr><h1 id="Beyond-AI"><a href="#Beyond-AI" class="headerlink" title="Beyond AI"></a>Beyond AI</h1><h2 id="Global-Control"><a href="#Global-Control" class="headerlink" title="Global Control"></a>Global Control</h2><p>During the Spring Festival, I wanted to test where Codex’s limits lay, so I had it directly generate a space exploration game named VoyagerSimulator.</p><p>Codex indeed generated an executable binary program, but it didn’t know which engine was better to use, nor could it directly generate 3D models, design gameplay, draw concept art, or create user interfaces. It only outputted a terminal-like interactive applet, far from the hype on the market promising one-click creation of hit games.</p><p>Thus, on one hand, I reviewed traditional design patterns and architectures; on the other hand, through continuous interaction with Gemini, I gradually approached a globally optimal solution, and then split each part of the solution for Codex to implement.</p><p>Think about how humans conduct code reviews: under the premise that each piece of local logic is correct, strive to leave room for future changes.</p><h2 id="Breaking-Routine"><a href="#Breaking-Routine" class="headerlink" title="Breaking Routine"></a>Breaking Routine</h2><p>AI can generate executable programs based on requirements, but its architecture or solutions tend to be conservative or traditional, whereas humans can invent all sorts of clever, unconventional hacks.</p><p>Suppose you need to display machine learning data charts on a web page. You could have an LLM implement it in JS, or using a backend language like Java. However, implementing it in JS is constrained by the browser’s single-threaded model, while using backend languages burdens the server.</p><p>After downing a cup of coffee, it suddenly hit me that I could piggyback on the user’s computer resources, distributing the computation logic to execution agents and communicating with web pages via ports. This was how the <a href="https://github.com/STARRY-INTELLIGENCE-TECHNOLOGY-LIMITED/GopherMesh" target="_blank" rel="noopener">GopherMesh</a> edge computing framework I recently wrote came to life.</p><p>I also worry that if one day, after drinking a stomach full of coffee, no inspiration or unconventional hacks emerge, does that mean I am about to be replaced by AI as well?</p><h2 id="Catching-Up-on-Liberal-Arts"><a href="#Catching-Up-on-Liberal-Arts" class="headerlink" title="Catching Up on Liberal Arts"></a>Catching Up on Liberal Arts</h2><p>To spark more clever hacks, one needs to shift focus away from the binary world—to touch art, to converse with masters of ancient Greek philosophical schools, to study economics and finance, to reflect on historical cycles, to draw inspiration from non-technical solutions, and to ponder the limitations of human psychology.</p><p>Rather than competing head-on against AI on breadth of knowledge and coding speed, it is better to enhance your <strong>aesthetics, expand your capability boundaries beyond code</strong>, and let yourself become more “abstract.”</p><p>Over the past six months, I engaged in high-intensity philosophical dialogues with LLMs like Gemini, constantly reflecting on first principles and meta-knowledge. Looking back at myself last year, I feel like I was several versions behind.</p><p>Remember what was said above: “Large language models are a mirror; every answer reflects the person asking the question”?</p><p>Just as stupid questions only yield stupid answers, the art of questioning is itself a liberal arts skill.</p><p>I hope everyone reading this article, whether conversing with humans or AI, can continuously improve their signal-to-noise ratio.</p><h2 id="Physical-Troubleshooting"><a href="#Physical-Troubleshooting" class="headerlink" title="Physical Troubleshooting"></a>Physical Troubleshooting</h2><p>Normally, I like to zone out watching videos of computer repair techs venting over circuit boards. The ability to repair physical hardware is hard for AI to replace in the short term.</p><p>I even considered going “from master’s degree down to vocational school” to learn a repairman’s craft.</p><p>Although learning methods to fix the physical world requires significant time and effort, humans possess an immensely powerful capacity for information compression. Therefore, one can start by training oneself to rapidly infer the root causes of issues arising in work.</p><p>This is one of the few advantages held by “seasoned veterans” anxious about being replaced by AI: <em>Nothing special, just practice making perfect.</em></p><h2 id="Courage-to-Say-No"><a href="#Courage-to-Say-No" class="headerlink" title="Courage to Say No"></a>Courage to Say No</h2><p>Just as the disclaimer at the bottom of the Gemini page says: “Gemini is an AI tool, and its answers may not always be correct.”</p><p>I hope that during your interactions with AI, all of you maintain human subjective initiative. Argue and swear at the AI as frequently and fiercely as possible. Even if you argue incorrectly, it allows you to actively switch to a “Red Team Mode” perspective to view and think about problems or topics from multiple angles.</p><p>Perhaps in the future, my metric for judging the depth of a person’s thinking and their AI usage will be: the frequency with which they reject AI or passionately argue with AI.</p><hr><p>When AI slips into hallucinations and talks nonsense, have the courage to say no.</p><p>When multiple AI conversations cannot reach a consensus, have the courage to say no.</p><p>When AI starts doing wild analysis that causes you to waver, have the courage to say no.</p><p>When AI violates Occam’s razor by unnecessarily multiplying entities, have the courage to say no.</p><p>When AI meddles in the real world or human emotions, have the courage to say no.</p><p>Just as I rejected any AI optimization for this article, this is my most sincere blessing to you all as a fellow human being.</p><hr><p>EOF</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;*This article was not generated or polished by AI; it is purely a human’s most raw thoughts and sharing.&lt;/p&gt;
&lt;h1 id=&quot;Reflections-Starting</summary>
      
    
    
    
    <category term="AI and Generative Content" scheme="https://project256.com/en/categories/ai-generative-content/"/>
    
    
    <category term="Large Language Models" scheme="https://project256.com/en/tags/large-language-models/"/>
    
  </entry>
  
  <entry>
    <title>Smooth Server Migration</title>
    <link href="https://project256.com/en/tools/%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%B9%B3%E6%BB%91%E8%BF%81%E7%A7%BB/"/>
    <id>https://project256.com/en/tools/%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%B9%B3%E6%BB%91%E8%BF%81%E7%A7%BB/</id>
    <published>2025-07-23T15:37:20.000Z</published>
    <updated>2026-08-04T09:39:04.563Z</updated>
    
    <content type="html"><![CDATA[<p>In 2014, my senior and I shared a server. This server ran a Minecraft Bukkit server, two national competition projects, the university course scheduling system, the network admin recruitment system, the network admin Cyber Culture Festival homepage, an ACM online judge system, a front-end evaluation system, a dog face recognition model, a quantitative trading front-end, a Linux performance probe, a Tokyo inland flooding visualization, as well as a bunch of experimental projects.</p><p>It’s fair to say that this 1C2G server carried so many memories and witnessed my growth.</p><p>However, on one hand, I couldn’t bear to let go of this IP address, and on the other hand, the hardware and pricing in the Qingdao region could no longer meet my needs.</p><p>Rather than backing up and reinstalling the OS, it made more sense to migrate directly to a new server and pass the machine down to my juniors.</p><h2 id="Defining-the-Scope-of-Migration"><a href="#Defining-the-Scope-of-Migration" class="headerlink" title="Defining the Scope of Migration"></a>Defining the Scope of Migration</h2><p>The simplest method is to run a <code>history</code> command to see which files were processed and which services were used before.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">history</span>  | grep -v <span class="built_in">cd</span> | grep -v ls | grep -v grep | grep -v <span class="built_in">exit</span> | grep -v <span class="built_in">history</span> | grep -v ll | grep -v df | grep -v du | grep -v <span class="built_in">pwd</span> | grep -v ping | grep -v wget | grep -v top | grep -v tail</span><br></pre></td></tr></table></figure><p>It shows all the commands from when it was reset on December 4, 2016, up until now. What nostalgia~</p><p>Of course, it also made me realize that most of the projects I did before were completely meaningless…</p><p>Next, check the Nginx access logs and use <code>ls -lrt</code> to view them in reverse chronological order to see which domain names received traffic.</p><p>An even more tragic reality is that aside from various scanner bots, there was basically no traffic at all…</p><p>Also, don’t forget to check under the <code>/usr/local</code> directory.</p><p>Here, I defined the following migration scope and order:</p><ul><li>Nginx configuration and SSL certificates</li><li><code>/root</code> directory</li><li><code>/data/wwwroot</code> directory</li><li>MySQL data</li></ul><h2 id="Installing-Services"><a href="#Installing-Services" class="headerlink" title="Installing Services"></a>Installing Services</h2><p>Others like Redis, Java, etc. need to be installed again:</p><ul><li>MySQL</li><li>Nginx</li><li>Node</li><li>hexo</li><li>Docker</li><li>Dataease</li><li>Various dependency mirror repositories: Alibaba Cloud ACR container registry mirror, npm mirror, pip mirror, etc.</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm config <span class="built_in">set</span> registry https://registry.npmmirror.com</span><br></pre></td></tr></table></figure><h2 id="Creating-MySQL-Users"><a href="#Creating-MySQL-Users" class="headerlink" title="Creating MySQL Users"></a>Creating MySQL Users</h2><p>First modify the root user password, then add new users.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">grep <span class="string">"password"</span> /var/<span class="built_in">log</span>/mysqld.log</span><br><span class="line">mysql -uroot -p9fr%ys1%ltKp</span><br><span class="line"></span><br><span class="line"><span class="comment"># 修改密码，开放外网访问权限</span></span><br><span class="line">use mysql;</span><br><span class="line">ALTER USER <span class="string">'root'</span>@<span class="string">'localhost'</span> IDENTIFIED BY <span class="string">'ROOTPASSWORD'</span>;</span><br><span class="line">UPDATE user SET host=<span class="string">'%'</span> WHERE user=<span class="string">'root'</span>;</span><br><span class="line">FLUSH PRIVILEGES;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 新增其他用户</span></span><br><span class="line">CREATE USER <span class="string">'quant'</span>@<span class="string">'%'</span> IDENTIFIED BY <span class="string">'XXX'</span>;</span><br><span class="line">GRANT ALL PRIVILEGES ON quant.* TO <span class="string">'quant'</span>@<span class="string">'%'</span> WITH GRANT OPTION;</span><br><span class="line">FLUSH PRIVILEGES;</span><br></pre></td></tr></table></figure><h2 id="Migrating-Files"><a href="#Migrating-Files" class="headerlink" title="Migrating Files"></a>Migrating Files</h2><p>Data is priceless—back up first.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysqldump -uroot -pROOTPASSWD --all-databases &gt; DB_all_backup_20250724.sql</span><br></pre></td></tr></table></figure><p>Using <code>scp</code> to copy an entire folder directly is too slow; package it into a <code>.tar</code> or <code>.zip</code> file first, then <code>scp</code> it over.</p><p><code>/data/wwwroot</code> directory</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">tar -zcf wwwroot.tar.gz wwwroot&#x2F;</span><br><span class="line">scp wwwroot.tar.gz root@120.79.20.0:&#x2F;data&#x2F;</span><br></pre></td></tr></table></figure><p>Nginx configuration files</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">scp /usr/<span class="built_in">local</span>/nginx/conf/vhost/* root@120.79.20.0:/etc/nginx/conf.d</span><br><span class="line">scp -r /usr/<span class="built_in">local</span>/nginx/conf/ssl/ root@120.79.20.238:/etc/nginx</span><br><span class="line"><span class="comment"># 修改配置文件中证书指向</span></span><br><span class="line">sed -i <span class="string">'s/\/usr\/local\/nginx\/conf\/ssl/\/etc\/nginx\/ssl/g'</span> `grep ssl -rl ./`</span><br></pre></td></tr></table></figure><h2 id="Migrating-Data"><a href="#Migrating-Data" class="headerlink" title="Migrating Data"></a>Migrating Data</h2><p>Although data migration can be done using the traditional method of importing the <code>DB_all_backup_20250724.sql</code> file via the <code>source</code> command, it is difficult to achieve a smooth incremental migration.</p><p>Therefore, I plan to use Canal to migrate MySQL.</p><p>This solution of masquerading as a slave replica was mentioned by my mentor at work back in late 2017. I finally have the chance to put it into practice.</p><p>Official documentation reference: <a href="https://github.com/alibaba/canal/wiki/QuickStart" target="_blank" rel="noopener">QuickStart</a></p><p>OceanBase version documentation reference: <a href="https://www.oceanbase.com/docs/-tutorials-cn-10000000000012274" target="_blank" rel="noopener">Doc</a></p><h2 id="Handling-Upstream-Dependencies"><a href="#Handling-Upstream-Dependencies" class="headerlink" title="Handling Upstream Dependencies"></a>Handling Upstream Dependencies</h2><h3 id="Domain-Name-DNS-Updates"><a href="#Domain-Name-DNS-Updates" class="headerlink" title="Domain Name DNS Updates"></a>Domain Name DNS Updates</h3><p>Change the A records for <code>www</code>, <code>*</code>, and <code>@</code> to the new IP address. After updating, wait a bit and check whether it can be accessed normally and whether pinging the domain routes to the new IP.</p><h3 id="GitAction-Deployment-Update"><a href="#GitAction-Deployment-Update" class="headerlink" title="GitAction Deployment Update"></a>GitAction Deployment Update</h3><p>Since this blog is updated in real time via GitAction—automatically compiling <code>.md</code> files and deploying after a <code>git push</code>—it is necessary to set up the relevant services on the new machine.</p><p><a href="https://cloud.tencent.com/developer/article/1776657" target="_blank" rel="noopener">Reference Document</a></p><p>For the new machine, the SSH configuration needs to be adjusted to allow login using a private key:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/ssh/sshd_config</span><br></pre></td></tr></table></figure><ul><li>Change <code>StrictModes yes</code> to <code>StrictModes no</code> (uncomment and change to <code>no</code>)</li><li>Find <code>#PubkeyAuthentication yes</code> and change to <code>PubkeyAuthentication yes</code> (uncomment)</li><li>Find <code>#AuthorizedKeysFile .ssh/authorized_keys</code> and change to <code>AuthorizedKeysFile .ssh/authorized_keys</code> (uncomment)</li></ul><p>Save and restart <code>sshd</code>:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl restart sshd</span><br></pre></td></tr></table></figure><h2 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>The article you are reading right now already points to the new machine’s IP, and that old machine has been reset and passed on to my junior.</p><p>The first small training task for them will definitely still be deploying Minecraft in the Linux environment to play around. Looking back to when I finished deploying it and had 1v1 PKs with my senior, we completely ignored the fact that it was a sandbox game, hahaha.</p><p>Hopefully, this machine created back in ‘13 will continue to be passed down.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;In 2014, my senior and I shared a server. This server ran a Minecraft Bukkit server, two national competition projects, the university co</summary>
      
    
    
    
    <category term="Tools" scheme="https://project256.com/en/categories/tools/"/>
    
    
    <category term="Operations" scheme="https://project256.com/en/tags/operations/"/>
    
  </entry>
  
  <entry>
    <title>Writing Multi-Platform Deployment Scripts</title>
    <link href="https://project256.com/en/continuous-integration/%E7%BC%96%E5%86%99%E5%A4%9A%E5%B9%B3%E5%8F%B0%E9%83%A8%E7%BD%B2%E8%84%9A%E6%9C%AC/"/>
    <id>https://project256.com/en/continuous-integration/%E7%BC%96%E5%86%99%E5%A4%9A%E5%B9%B3%E5%8F%B0%E9%83%A8%E7%BD%B2%E8%84%9A%E6%9C%AC/</id>
    <published>2025-06-02T07:39:08.000Z</published>
    <updated>2026-08-04T09:39:04.563Z</updated>
    
    <content type="html"><![CDATA[<p>After several months of development, the project is about to enter the delivery phase.</p><p>However, the client restricts deployment to a Windows environment, whereas I am more familiar with Linux, and deploying middleware on Windows is quite troublesome.</p><p>Therefore, two deployment methods were used to indirectly utilize a Linux system: 1. Executing a one-click deployment script via WSL, and 2. Docker images.</p><h2 id="Deployment-Strategy"><a href="#Deployment-Strategy" class="headerlink" title="Deployment Strategy"></a>Deployment Strategy</h2><p>For the client, deployment needs to be as “one-click” as possible.</p><p>Therefore, PowerShell versions of the deployment script, service start script, and service stop script were generated using an LLM.</p><p>Corresponding Bash versions of deployment scripts can also be generated with the help of an LLM.</p><p>Before delivering to the client, write the configuration file explanations and commands into the documentation, and record a demonstration video.</p><p>Finally, two deployment methods were provided to the user: a Linux one-click deployment package and a Docker one-click deployment package.</p><h2 id="LLM-Prompt"><a href="#LLM-Prompt" class="headerlink" title="LLM Prompt"></a>LLM Prompt</h2><p>Of course, there is no need to manually write the scripts from scratch; an LLM can handle it instead. The prompt is as follows:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">我需要使用bash脚本部署我的工程，请bash通过读取配置文件。环境可能是ubuntu、centos7或centos8，请提供bash脚本及配置文件。</span><br><span class="line"></span><br><span class="line">1.允许用户自定义是否安装</span><br><span class="line">2.允许用户指定ip、port、账号密码，当指定了上述配置时，需要尝试连接，如果连接成功则不要安装。如果连接失败，则提示用户修改配置文件。</span><br><span class="line">3.我需要安装mysql或postgresql时，设置为配置中的账号和密码</span><br><span class="line"></span><br><span class="line">依赖如下：</span><br><span class="line">openjdk17</span><br><span class="line">mysql</span><br><span class="line">postgresql</span><br><span class="line">nginx</span><br></pre></td></tr></table></figure><h2 id="LLM-Generated-Configuration-File"><a href="#LLM-Generated-Configuration-File" class="headerlink" title="LLM-Generated Configuration File"></a>LLM-Generated Configuration File</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line">[common]</span><br><span class="line">install_java&#x3D;yes</span><br><span class="line">install_nginx&#x3D;yes</span><br><span class="line">install_thingsboard&#x3D;yes</span><br><span class="line"></span><br><span class="line">[mysql]</span><br><span class="line">install&#x3D;yes</span><br><span class="line">host&#x3D;localhost</span><br><span class="line">port&#x3D;3306</span><br><span class="line">user&#x3D;uuuu</span><br><span class="line">password&#x3D;pppp</span><br><span class="line"></span><br><span class="line">[mysql_slave]</span><br><span class="line">enable&#x3D;false</span><br><span class="line">url&#x3D;</span><br><span class="line">username&#x3D;</span><br><span class="line">password&#x3D;</span><br><span class="line"></span><br><span class="line">[postgresql]</span><br><span class="line">install&#x3D;yes</span><br><span class="line">host&#x3D;localhost</span><br><span class="line">port&#x3D;5432</span><br><span class="line">user&#x3D;uuuu</span><br><span class="line">password&#x3D;pppp</span><br><span class="line"></span><br><span class="line">[redis]</span><br><span class="line">install&#x3D;yes</span><br><span class="line">host&#x3D;localhost</span><br><span class="line">port&#x3D;6379</span><br><span class="line">database&#x3D;0</span><br><span class="line">password&#x3D;</span><br><span class="line"></span><br><span class="line">[nginx]</span><br><span class="line">thingsboard_ssl_certificate&#x3D;</span><br><span class="line">thingsboard_ssl_certificate_key&#x3D;</span><br><span class="line">xxx_ssl_certificate&#x3D;</span><br><span class="line">xxx_ssl_certificate_key&#x3D;</span><br><span class="line"></span><br><span class="line">[port]</span><br><span class="line">a_port&#x3D;8080</span><br><span class="line">b_port&#x3D;8081</span><br><span class="line">c_port&#x3D;8001</span><br></pre></td></tr></table></figure><h2 id="Final-One-Click-Deployment-Script"><a href="#Final-One-Click-Deployment-Script" class="headerlink" title="Final One-Click Deployment Script"></a>Final One-Click Deployment Script</h2><p>*Confidential information omitted</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br><span class="line">420</span><br><span class="line">421</span><br><span class="line">422</span><br><span class="line">423</span><br><span class="line">424</span><br><span class="line">425</span><br><span class="line">426</span><br><span class="line">427</span><br><span class="line">428</span><br><span class="line">429</span><br><span class="line">430</span><br><span class="line">431</span><br><span class="line">432</span><br><span class="line">433</span><br><span class="line">434</span><br><span class="line">435</span><br><span class="line">436</span><br><span class="line">437</span><br><span class="line">438</span><br><span class="line">439</span><br><span class="line">440</span><br><span class="line">441</span><br><span class="line">442</span><br><span class="line">443</span><br><span class="line">444</span><br><span class="line">445</span><br><span class="line">446</span><br><span class="line">447</span><br><span class="line">448</span><br><span class="line">449</span><br><span class="line">450</span><br><span class="line">451</span><br><span class="line">452</span><br><span class="line">453</span><br><span class="line">454</span><br><span class="line">455</span><br><span class="line">456</span><br><span class="line">457</span><br><span class="line">458</span><br><span class="line">459</span><br><span class="line">460</span><br><span class="line">461</span><br><span class="line">462</span><br><span class="line">463</span><br><span class="line">464</span><br><span class="line">465</span><br><span class="line">466</span><br><span class="line">467</span><br><span class="line">468</span><br><span class="line">469</span><br><span class="line">470</span><br><span class="line">471</span><br><span class="line">472</span><br><span class="line">473</span><br><span class="line">474</span><br><span class="line">475</span><br><span class="line">476</span><br><span class="line">477</span><br><span class="line">478</span><br><span class="line">479</span><br><span class="line">480</span><br><span class="line">481</span><br><span class="line">482</span><br><span class="line">483</span><br><span class="line">484</span><br><span class="line">485</span><br><span class="line">486</span><br><span class="line">487</span><br><span class="line">488</span><br><span class="line">489</span><br><span class="line">490</span><br><span class="line">491</span><br><span class="line">492</span><br><span class="line">493</span><br><span class="line">494</span><br><span class="line">495</span><br><span class="line">496</span><br><span class="line">497</span><br><span class="line">498</span><br><span class="line">499</span><br><span class="line">500</span><br><span class="line">501</span><br><span class="line">502</span><br><span class="line">503</span><br><span class="line">504</span><br><span class="line">505</span><br><span class="line">506</span><br><span class="line">507</span><br><span class="line">508</span><br><span class="line">509</span><br><span class="line">510</span><br><span class="line">511</span><br><span class="line">512</span><br><span class="line">513</span><br><span class="line">514</span><br><span class="line">515</span><br><span class="line">516</span><br><span class="line">517</span><br><span class="line">518</span><br><span class="line">519</span><br><span class="line">520</span><br><span class="line">521</span><br><span class="line">522</span><br><span class="line">523</span><br><span class="line">524</span><br><span class="line">525</span><br><span class="line">526</span><br><span class="line">527</span><br><span class="line">528</span><br><span class="line">529</span><br><span class="line">530</span><br><span class="line">531</span><br><span class="line">532</span><br><span class="line">533</span><br><span class="line">534</span><br><span class="line">535</span><br><span class="line">536</span><br><span class="line">537</span><br><span class="line">538</span><br><span class="line">539</span><br><span class="line">540</span><br><span class="line">541</span><br><span class="line">542</span><br><span class="line">543</span><br><span class="line">544</span><br><span class="line">545</span><br><span class="line">546</span><br><span class="line">547</span><br><span class="line">548</span><br><span class="line">549</span><br><span class="line">550</span><br><span class="line">551</span><br><span class="line">552</span><br><span class="line">553</span><br><span class="line">554</span><br><span class="line">555</span><br><span class="line">556</span><br><span class="line">557</span><br><span class="line">558</span><br><span class="line">559</span><br><span class="line">560</span><br><span class="line">561</span><br><span class="line">562</span><br><span class="line">563</span><br><span class="line">564</span><br><span class="line">565</span><br><span class="line">566</span><br><span class="line">567</span><br><span class="line">568</span><br><span class="line">569</span><br><span class="line">570</span><br><span class="line">571</span><br><span class="line">572</span><br><span class="line">573</span><br><span class="line">574</span><br><span class="line">575</span><br><span class="line">576</span><br><span class="line">577</span><br><span class="line">578</span><br><span class="line">579</span><br><span class="line">580</span><br><span class="line">581</span><br><span class="line">582</span><br><span class="line">583</span><br><span class="line">584</span><br><span class="line">585</span><br><span class="line">586</span><br><span class="line">587</span><br><span class="line">588</span><br><span class="line">589</span><br><span class="line">590</span><br><span class="line">591</span><br><span class="line">592</span><br><span class="line">593</span><br><span class="line">594</span><br><span class="line">595</span><br><span class="line">596</span><br><span class="line">597</span><br><span class="line">598</span><br><span class="line">599</span><br><span class="line">600</span><br><span class="line">601</span><br><span class="line">602</span><br><span class="line">603</span><br><span class="line">604</span><br><span class="line">605</span><br><span class="line">606</span><br><span class="line">607</span><br><span class="line">608</span><br><span class="line">609</span><br><span class="line">610</span><br><span class="line">611</span><br><span class="line">612</span><br><span class="line">613</span><br><span class="line">614</span><br><span class="line">615</span><br><span class="line">616</span><br><span class="line">617</span><br><span class="line">618</span><br><span class="line">619</span><br><span class="line">620</span><br><span class="line">621</span><br><span class="line">622</span><br><span class="line">623</span><br><span class="line">624</span><br><span class="line">625</span><br><span class="line">626</span><br><span class="line">627</span><br><span class="line">628</span><br><span class="line">629</span><br><span class="line">630</span><br><span class="line">631</span><br><span class="line">632</span><br><span class="line">633</span><br><span class="line">634</span><br><span class="line">635</span><br><span class="line">636</span><br><span class="line">637</span><br><span class="line">638</span><br><span class="line">639</span><br><span class="line">640</span><br><span class="line">641</span><br><span class="line">642</span><br><span class="line">643</span><br><span class="line">644</span><br><span class="line">645</span><br><span class="line">646</span><br><span class="line">647</span><br><span class="line">648</span><br><span class="line">649</span><br><span class="line">650</span><br><span class="line">651</span><br><span class="line">652</span><br><span class="line">653</span><br><span class="line">654</span><br><span class="line">655</span><br><span class="line">656</span><br><span class="line">657</span><br><span class="line">658</span><br><span class="line">659</span><br><span class="line">660</span><br><span class="line">661</span><br><span class="line">662</span><br><span class="line">663</span><br><span class="line">664</span><br><span class="line">665</span><br><span class="line">666</span><br><span class="line">667</span><br><span class="line">668</span><br><span class="line">669</span><br><span class="line">670</span><br><span class="line">671</span><br><span class="line">672</span><br><span class="line">673</span><br><span class="line">674</span><br><span class="line">675</span><br><span class="line">676</span><br><span class="line">677</span><br><span class="line">678</span><br><span class="line">679</span><br><span class="line">680</span><br><span class="line">681</span><br><span class="line">682</span><br><span class="line">683</span><br><span class="line">684</span><br><span class="line">685</span><br><span class="line">686</span><br><span class="line">687</span><br><span class="line">688</span><br><span class="line">689</span><br><span class="line">690</span><br><span class="line">691</span><br><span class="line">692</span><br><span class="line">693</span><br><span class="line">694</span><br><span class="line">695</span><br><span class="line">696</span><br><span class="line">697</span><br><span class="line">698</span><br><span class="line">699</span><br><span class="line">700</span><br><span class="line">701</span><br><span class="line">702</span><br><span class="line">703</span><br><span class="line">704</span><br><span class="line">705</span><br><span class="line">706</span><br><span class="line">707</span><br><span class="line">708</span><br><span class="line">709</span><br><span class="line">710</span><br><span class="line">711</span><br><span class="line">712</span><br><span class="line">713</span><br><span class="line">714</span><br><span class="line">715</span><br><span class="line">716</span><br><span class="line">717</span><br><span class="line">718</span><br><span class="line">719</span><br><span class="line">720</span><br><span class="line">721</span><br><span class="line">722</span><br><span class="line">723</span><br><span class="line">724</span><br><span class="line">725</span><br><span class="line">726</span><br><span class="line">727</span><br><span class="line">728</span><br><span class="line">729</span><br><span class="line">730</span><br><span class="line">731</span><br><span class="line">732</span><br><span class="line">733</span><br><span class="line">734</span><br><span class="line">735</span><br><span class="line">736</span><br><span class="line">737</span><br><span class="line">738</span><br><span class="line">739</span><br><span class="line">740</span><br><span class="line">741</span><br><span class="line">742</span><br><span class="line">743</span><br><span class="line">744</span><br><span class="line">745</span><br><span class="line">746</span><br><span class="line">747</span><br><span class="line">748</span><br><span class="line">749</span><br><span class="line">750</span><br><span class="line">751</span><br><span class="line">752</span><br><span class="line">753</span><br><span class="line">754</span><br><span class="line">755</span><br><span class="line">756</span><br><span class="line">757</span><br><span class="line">758</span><br><span class="line">759</span><br><span class="line">760</span><br><span class="line">761</span><br><span class="line">762</span><br><span class="line">763</span><br><span class="line">764</span><br><span class="line">765</span><br><span class="line">766</span><br><span class="line">767</span><br><span class="line">768</span><br><span class="line">769</span><br><span class="line">770</span><br><span class="line">771</span><br><span class="line">772</span><br><span class="line">773</span><br><span class="line">774</span><br><span class="line">775</span><br><span class="line">776</span><br><span class="line">777</span><br><span class="line">778</span><br><span class="line">779</span><br><span class="line">780</span><br><span class="line">781</span><br><span class="line">782</span><br><span class="line">783</span><br><span class="line">784</span><br><span class="line">785</span><br><span class="line">786</span><br><span class="line">787</span><br><span class="line">788</span><br><span class="line">789</span><br><span class="line">790</span><br><span class="line">791</span><br><span class="line">792</span><br><span class="line">793</span><br><span class="line">794</span><br><span class="line">795</span><br><span class="line">796</span><br><span class="line">797</span><br><span class="line">798</span><br><span class="line">799</span><br><span class="line">800</span><br><span class="line">801</span><br><span class="line">802</span><br><span class="line">803</span><br><span class="line">804</span><br><span class="line">805</span><br><span class="line">806</span><br><span class="line">807</span><br><span class="line">808</span><br><span class="line">809</span><br><span class="line">810</span><br><span class="line">811</span><br><span class="line">812</span><br><span class="line">813</span><br><span class="line">814</span><br><span class="line">815</span><br><span class="line">816</span><br><span class="line">817</span><br><span class="line">818</span><br><span class="line">819</span><br><span class="line">820</span><br><span class="line">821</span><br><span class="line">822</span><br><span class="line">823</span><br><span class="line">824</span><br><span class="line">825</span><br><span class="line">826</span><br><span class="line">827</span><br><span class="line">828</span><br><span class="line">829</span><br><span class="line">830</span><br><span class="line">831</span><br><span class="line">832</span><br><span class="line">833</span><br><span class="line">834</span><br><span class="line">835</span><br><span class="line">836</span><br><span class="line">837</span><br><span class="line">838</span><br><span class="line">839</span><br><span class="line">840</span><br><span class="line">841</span><br><span class="line">842</span><br><span class="line">843</span><br><span class="line">844</span><br><span class="line">845</span><br><span class="line">846</span><br><span class="line">847</span><br><span class="line">848</span><br><span class="line">849</span><br><span class="line">850</span><br><span class="line">851</span><br><span class="line">852</span><br><span class="line">853</span><br><span class="line">854</span><br><span class="line">855</span><br><span class="line">856</span><br><span class="line">857</span><br><span class="line">858</span><br><span class="line">859</span><br><span class="line">860</span><br><span class="line">861</span><br><span class="line">862</span><br><span class="line">863</span><br><span class="line">864</span><br><span class="line">865</span><br><span class="line">866</span><br><span class="line">867</span><br><span class="line">868</span><br><span class="line">869</span><br><span class="line">870</span><br><span class="line">871</span><br><span class="line">872</span><br><span class="line">873</span><br><span class="line">874</span><br><span class="line">875</span><br><span class="line">876</span><br><span class="line">877</span><br><span class="line">878</span><br><span class="line">879</span><br><span class="line">880</span><br><span class="line">881</span><br><span class="line">882</span><br><span class="line">883</span><br><span class="line">884</span><br><span class="line">885</span><br><span class="line">886</span><br><span class="line">887</span><br><span class="line">888</span><br><span class="line">889</span><br><span class="line">890</span><br><span class="line">891</span><br><span class="line">892</span><br><span class="line">893</span><br><span class="line">894</span><br><span class="line">895</span><br><span class="line">896</span><br><span class="line">897</span><br><span class="line">898</span><br><span class="line">899</span><br><span class="line">900</span><br><span class="line">901</span><br><span class="line">902</span><br><span class="line">903</span><br><span class="line">904</span><br><span class="line">905</span><br><span class="line">906</span><br><span class="line">907</span><br><span class="line">908</span><br><span class="line">909</span><br><span class="line">910</span><br><span class="line">911</span><br><span class="line">912</span><br><span class="line">913</span><br><span class="line">914</span><br><span class="line">915</span><br><span class="line">916</span><br><span class="line">917</span><br><span class="line">918</span><br><span class="line">919</span><br><span class="line">920</span><br><span class="line">921</span><br><span class="line">922</span><br><span class="line">923</span><br><span class="line">924</span><br><span class="line">925</span><br><span class="line">926</span><br><span class="line">927</span><br><span class="line">928</span><br><span class="line">929</span><br><span class="line">930</span><br><span class="line">931</span><br><span class="line">932</span><br><span class="line">933</span><br><span class="line">934</span><br><span class="line">935</span><br><span class="line">936</span><br><span class="line">937</span><br><span class="line">938</span><br><span class="line">939</span><br><span class="line">940</span><br><span class="line">941</span><br><span class="line">942</span><br><span class="line">943</span><br><span class="line">944</span><br><span class="line">945</span><br><span class="line">946</span><br><span class="line">947</span><br><span class="line">948</span><br><span class="line">949</span><br><span class="line">950</span><br><span class="line">951</span><br><span class="line">952</span><br><span class="line">953</span><br><span class="line">954</span><br><span class="line">955</span><br><span class="line">956</span><br><span class="line">957</span><br><span class="line">958</span><br><span class="line">959</span><br><span class="line">960</span><br><span class="line">961</span><br><span class="line">962</span><br><span class="line">963</span><br><span class="line">964</span><br><span class="line">965</span><br><span class="line">966</span><br><span class="line">967</span><br><span class="line">968</span><br><span class="line">969</span><br><span class="line">970</span><br><span class="line">971</span><br><span class="line">972</span><br><span class="line">973</span><br><span class="line">974</span><br><span class="line">975</span><br><span class="line">976</span><br><span class="line">977</span><br><span class="line">978</span><br><span class="line">979</span><br><span class="line">980</span><br><span class="line">981</span><br><span class="line">982</span><br><span class="line">983</span><br><span class="line">984</span><br><span class="line">985</span><br><span class="line">986</span><br><span class="line">987</span><br><span class="line">988</span><br><span class="line">989</span><br><span class="line">990</span><br><span class="line">991</span><br><span class="line">992</span><br><span class="line">993</span><br><span class="line">994</span><br><span class="line">995</span><br><span class="line">996</span><br><span class="line">997</span><br><span class="line">998</span><br><span class="line">999</span><br><span class="line">1000</span><br><span class="line">1001</span><br><span class="line">1002</span><br><span class="line">1003</span><br><span class="line">1004</span><br><span class="line">1005</span><br><span class="line">1006</span><br><span class="line">1007</span><br><span class="line">1008</span><br><span class="line">1009</span><br><span class="line">1010</span><br><span class="line">1011</span><br><span class="line">1012</span><br><span class="line">1013</span><br><span class="line">1014</span><br><span class="line">1015</span><br><span class="line">1016</span><br><span class="line">1017</span><br><span class="line">1018</span><br><span class="line">1019</span><br><span class="line">1020</span><br><span class="line">1021</span><br><span class="line">1022</span><br><span class="line">1023</span><br><span class="line">1024</span><br><span class="line">1025</span><br><span class="line">1026</span><br><span class="line">1027</span><br><span class="line">1028</span><br><span class="line">1029</span><br><span class="line">1030</span><br><span class="line">1031</span><br><span class="line">1032</span><br><span class="line">1033</span><br><span class="line">1034</span><br><span class="line">1035</span><br><span class="line">1036</span><br><span class="line">1037</span><br><span class="line">1038</span><br><span class="line">1039</span><br><span class="line">1040</span><br><span class="line">1041</span><br><span class="line">1042</span><br><span class="line">1043</span><br><span class="line">1044</span><br><span class="line">1045</span><br><span class="line">1046</span><br><span class="line">1047</span><br><span class="line">1048</span><br><span class="line">1049</span><br><span class="line">1050</span><br><span class="line">1051</span><br><span class="line">1052</span><br><span class="line">1053</span><br><span class="line">1054</span><br><span class="line">1055</span><br><span class="line">1056</span><br><span class="line">1057</span><br><span class="line">1058</span><br><span class="line">1059</span><br><span class="line">1060</span><br><span class="line">1061</span><br><span class="line">1062</span><br><span class="line">1063</span><br><span class="line">1064</span><br><span class="line">1065</span><br><span class="line">1066</span><br><span class="line">1067</span><br><span class="line">1068</span><br><span class="line">1069</span><br><span class="line">1070</span><br><span class="line">1071</span><br><span class="line">1072</span><br><span class="line">1073</span><br><span class="line">1074</span><br><span class="line">1075</span><br><span class="line">1076</span><br><span class="line">1077</span><br><span class="line">1078</span><br><span class="line">1079</span><br><span class="line">1080</span><br><span class="line">1081</span><br><span class="line">1082</span><br><span class="line">1083</span><br><span class="line">1084</span><br><span class="line">1085</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line">CONFIG_FILE=<span class="string">"deploy.conf"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取当前脚本目录</span></span><br><span class="line">SCRIPT_DIR=$(<span class="built_in">cd</span> <span class="string">"<span class="variable">$(dirname "$0")</span>"</span> &amp;&amp; <span class="built_in">pwd</span>)</span><br><span class="line">SQL_FILE=<span class="string">"<span class="variable">$SCRIPT_DIR</span>/ruoyi-admin/xxx.sql"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 检查配置文件</span></span><br><span class="line"><span class="keyword">if</span> [ ! -f <span class="string">"<span class="variable">$CONFIG_FILE</span>"</span> ]; <span class="keyword">then</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"错误：配置文件 <span class="variable">$CONFIG_FILE</span> 不存在"</span></span><br><span class="line">    <span class="built_in">exit</span> 1</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 读取配置文件</span></span><br><span class="line"><span class="built_in">declare</span> -A CONFIG</span><br><span class="line">CURRENT_SECTION=<span class="string">""</span></span><br><span class="line"><span class="keyword">while</span> IFS= <span class="built_in">read</span> -r line; <span class="keyword">do</span></span><br><span class="line">    line=<span class="variable">$&#123;line%%#*&#125;</span>    <span class="comment"># 去除注释</span></span><br><span class="line">    line=<span class="variable">$&#123;line// /&#125;</span>    <span class="comment"># 去除空格</span></span><br><span class="line">    [[ -z <span class="variable">$line</span> ]] &amp;&amp; <span class="built_in">continue</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> [[ <span class="variable">$line</span> == \[*\] ]]; <span class="keyword">then</span></span><br><span class="line">        CURRENT_SECTION=<span class="variable">$&#123;line:1:-1&#125;</span></span><br><span class="line">    <span class="keyword">elif</span> [[ <span class="variable">$line</span> == *=* ]]; <span class="keyword">then</span></span><br><span class="line">        key=<span class="string">"<span class="variable">$&#123;CURRENT_SECTION&#125;</span>_<span class="variable">$&#123;line%%=*&#125;</span>"</span></span><br><span class="line">        CONFIG[<span class="variable">$key</span>]=<span class="variable">$&#123;line#*=&#125;</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"><span class="keyword">done</span> &lt; <span class="string">"<span class="variable">$CONFIG_FILE</span>"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取配置项函数</span></span><br><span class="line"><span class="function"><span class="title">get_config</span></span>() &#123;</span><br><span class="line">    <span class="built_in">local</span> key=<span class="string">"<span class="variable">$1</span>"</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"<span class="variable">$&#123;CONFIG[$&#123;key&#125;</span>]:-&#125;"</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 定义数据库配置变量</span></span><br><span class="line">MYSQL_ROOT_PASSWORD=$(get_config <span class="string">'mysql_password'</span>)</span><br><span class="line">PG_POSTGRES_PASSWORD=$(get_config <span class="string">'postgresql_password'</span>)</span><br><span class="line">DB_USER=$(get_config <span class="string">'mysql_user'</span>)</span><br><span class="line">DB_PASSWORD=$(get_config <span class="string">'mysql_password'</span>)</span><br><span class="line">PG_USER=$(get_config <span class="string">'postgresql_user'</span>)</span><br><span class="line">PG_PASSWORD=$(get_config <span class="string">'postgresql_password'</span>)</span><br><span class="line">PG_HOST=$(get_config <span class="string">'postgresql_host'</span>)</span><br><span class="line">PG_PORT=$(get_config <span class="string">'postgresql_port'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 新增：导入 xxx.sql 到 MySQL 数据库</span></span><br><span class="line"><span class="function"><span class="title">import_xxx_sql</span></span>() &#123;</span><br><span class="line">    <span class="built_in">local</span> host=<span class="variable">$1</span></span><br><span class="line">    <span class="built_in">local</span> port=<span class="variable">$2</span></span><br><span class="line">    <span class="built_in">local</span> user=<span class="variable">$3</span></span><br><span class="line">    <span class="built_in">local</span> password=<span class="variable">$4</span></span><br><span class="line">    <span class="built_in">local</span> sql_file=<span class="variable">$5</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 检查 SQL 文件是否存在</span></span><br><span class="line">    <span class="keyword">if</span> [ ! -f <span class="string">"<span class="variable">$sql_file</span>"</span> ]; <span class="keyword">then</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"错误：SQL 文件 <span class="variable">$sql_file</span> 不存在"</span></span><br><span class="line">        <span class="built_in">exit</span> 1</span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 检查数据库是否存在</span></span><br><span class="line">    <span class="keyword">if</span> mysql -h<span class="variable">$host</span> -P<span class="variable">$port</span> -u<span class="variable">$user</span> -p<span class="variable">$password</span> -e <span class="string">"USE xxx"</span> 2&gt;/dev/null; <span class="keyword">then</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"数据库 xxx 已存在，跳过创建"</span></span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"创建数据库 xxx..."</span></span><br><span class="line">        mysql -h<span class="variable">$host</span> -P<span class="variable">$port</span> -u<span class="variable">$user</span> -p<span class="variable">$password</span> -e <span class="string">"CREATE DATABASE xxx;"</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 导入 SQL 文件</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"导入 <span class="variable">$sql_file</span> 到 xxx 数据库..."</span></span><br><span class="line">    mysql -h<span class="variable">$host</span> -P<span class="variable">$port</span> -u<span class="variable">$user</span> -p<span class="variable">$password</span> xxx &lt; <span class="string">"<span class="variable">$sql_file</span>"</span></span><br><span class="line">    <span class="keyword">if</span> [ $? -eq 0 ]; <span class="keyword">then</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"导入成功"</span></span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"导入失败"</span></span><br><span class="line">        <span class="built_in">exit</span> 1</span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 更新 sys_menu 表</span></span><br><span class="line"><span class="function"><span class="title">update_sys_menu</span></span>() &#123;</span><br><span class="line">    <span class="built_in">local</span> host=<span class="variable">$1</span></span><br><span class="line">    <span class="built_in">local</span> port=<span class="variable">$2</span></span><br><span class="line">    <span class="built_in">local</span> user=<span class="variable">$3</span></span><br><span class="line">    <span class="built_in">local</span> password=<span class="variable">$4</span></span><br><span class="line">    <span class="built_in">local</span> thingsboard_url=<span class="variable">$5</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> [ -z <span class="string">"<span class="variable">$thingsboard_url</span>"</span> ]; <span class="keyword">then</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"警告：thingsboard_url 未配置，跳过 sys_menu 更新"</span></span><br><span class="line">        <span class="built_in">return</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"更新 sys_menu 表 (menu_id=2005) 的 path 字段为 <span class="variable">$thingsboard_url</span>"</span></span><br><span class="line">    mysql -h<span class="variable">$host</span> -P<span class="variable">$port</span> -u<span class="variable">$user</span> -p<span class="variable">$password</span> xxx &lt;&lt;EOF</span><br><span class="line">UPDATE sys_menu SET path = <span class="string">"<span class="variable">$thingsboard_url</span>"</span> WHERE menu_id = 2005;</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> [ $? -eq 0 ]; <span class="keyword">then</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"sys_menu 表更新成功"</span></span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"警告：sys_menu 表更新失败"</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取系统信息</span></span><br><span class="line"><span class="function"><span class="title">detect_os</span></span>() &#123;</span><br><span class="line">    <span class="keyword">if</span> [ -f /etc/os-release ]; <span class="keyword">then</span></span><br><span class="line">        . /etc/os-release</span><br><span class="line">        OS_NAME=<span class="variable">$ID</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 兼容Alibaba Linux</span></span><br><span class="line">        <span class="keyword">if</span> [[ <span class="string">"<span class="variable">$ID</span>"</span> == <span class="string">"alinux"</span> ]]; <span class="keyword">then</span></span><br><span class="line">            OS_NAME=<span class="string">"centos"</span></span><br><span class="line">            <span class="comment"># 根据版本号映射到CentOS版本</span></span><br><span class="line">            <span class="keyword">if</span> [[ <span class="string">"<span class="variable">$VERSION_ID</span>"</span> == <span class="string">"2"</span>* ]]; <span class="keyword">then</span></span><br><span class="line">                OS_VERSION=<span class="string">"7"</span></span><br><span class="line">            <span class="keyword">elif</span> [[ <span class="string">"<span class="variable">$VERSION_ID</span>"</span> == <span class="string">"3"</span>* ]]; <span class="keyword">then</span></span><br><span class="line">                OS_VERSION=<span class="string">"8"</span></span><br><span class="line">            <span class="keyword">fi</span></span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            OS_VERSION=<span class="variable">$&#123;VERSION_ID%%.*&#125;</span></span><br><span class="line">        <span class="keyword">fi</span></span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"无法检测操作系统"</span></span><br><span class="line">        <span class="built_in">exit</span> 1</span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 关闭IPv6并确保持续生效（修改版）</span></span><br><span class="line"><span class="function"><span class="title">disable_ipv6</span></span>() &#123;</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"开始禁用IPv6..."</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 检查是否已存在相关配置</span></span><br><span class="line">    <span class="keyword">if</span> grep -q <span class="string">"net.ipv6.conf.all.disable_ipv6"</span> /etc/sysctl.conf; <span class="keyword">then</span></span><br><span class="line">        <span class="comment"># 更新现有配置</span></span><br><span class="line">        sudo sed -i <span class="string">'s/^net.ipv6.conf.all.disable_ipv6.*/net.ipv6.conf.all.disable_ipv6 = 1/'</span> /etc/sysctl.conf</span><br><span class="line">        sudo sed -i <span class="string">'s/^net.ipv6.conf.default.disable_ipv6.*/net.ipv6.conf.default.disable_ipv6 = 1/'</span> /etc/sysctl.conf</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="comment"># 添加新配置</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"net.ipv6.conf.all.disable_ipv6 = 1"</span> | sudo tee -a /etc/sysctl.conf</span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"net.ipv6.conf.default.disable_ipv6 = 1"</span> | sudo tee -a /etc/sysctl.conf</span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 立即应用设置</span></span><br><span class="line">    sudo sysctl -p</span><br><span class="line"></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"IPv6已禁用并设置为持续生效"</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装包管理工具</span></span><br><span class="line"><span class="function"><span class="title">install_package</span></span>() &#123;</span><br><span class="line">    <span class="built_in">local</span> package=<span class="variable">$1</span></span><br><span class="line">    <span class="keyword">case</span> <span class="variable">$OS_NAME</span> <span class="keyword">in</span></span><br><span class="line">        ubuntu)</span><br><span class="line">            sudo apt install -y <span class="variable">$package</span></span><br><span class="line">            ;;</span><br><span class="line">        centos|rhel|alinux)</span><br><span class="line">            <span class="comment"># 统一处理CentOS/RHEL/Alibaba Linux</span></span><br><span class="line">            <span class="keyword">if</span> [ <span class="variable">$OS_VERSION</span> -eq 7 ]; <span class="keyword">then</span></span><br><span class="line">                sudo yum install -y <span class="variable">$package</span> --nogpgcheck</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                sudo dnf install -y <span class="variable">$package</span> --nogpgcheck</span><br><span class="line">            <span class="keyword">fi</span></span><br><span class="line">            ;;</span><br><span class="line">        *)</span><br><span class="line">            <span class="built_in">echo</span> <span class="string">"不支持的发行版: <span class="variable">$OS_NAME</span>"</span></span><br><span class="line">            <span class="built_in">exit</span> 1</span><br><span class="line">            ;;</span><br><span class="line">    <span class="keyword">esac</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 检查服务连接</span></span><br><span class="line"><span class="function"><span class="title">check_db_connection</span></span>() &#123;</span><br><span class="line">    <span class="built_in">local</span> db_type=<span class="variable">$1</span></span><br><span class="line">    <span class="built_in">local</span> host=<span class="variable">$2</span></span><br><span class="line">    <span class="built_in">local</span> port=<span class="variable">$3</span></span><br><span class="line">    <span class="built_in">local</span> user=<span class="variable">$4</span></span><br><span class="line">    <span class="built_in">local</span> password=<span class="variable">$5</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">case</span> <span class="variable">$db_type</span> <span class="keyword">in</span></span><br><span class="line">        mysql)</span><br><span class="line">            <span class="keyword">if</span> ! <span class="built_in">command</span> -v mysql &amp;&gt; /dev/null; <span class="keyword">then</span></span><br><span class="line">                install_package <span class="string">"mysql-client"</span></span><br><span class="line">            <span class="keyword">fi</span></span><br><span class="line">            mysql -h<span class="variable">$host</span> -P<span class="variable">$port</span> -u<span class="variable">$user</span> -p<span class="variable">$password</span> -e <span class="string">"SELECT 1;"</span> &amp;&gt; /dev/null</span><br><span class="line">            <span class="built_in">return</span> $?</span><br><span class="line">            ;;</span><br><span class="line">        postgresql)</span><br><span class="line">            <span class="keyword">if</span> ! <span class="built_in">command</span> -v psql &amp;&gt; /dev/null; <span class="keyword">then</span></span><br><span class="line">                install_package <span class="string">"postgresql-client"</span></span><br><span class="line">            <span class="keyword">fi</span></span><br><span class="line">            PGPASSWORD=<span class="string">"<span class="variable">$password</span>"</span> psql -h <span class="variable">$host</span> -p <span class="variable">$port</span> -U <span class="variable">$user</span> -d postgres -c <span class="string">"SELECT 1;"</span> &amp;&gt; /dev/null</span><br><span class="line">            <span class="built_in">return</span> $?</span><br><span class="line">            ;;</span><br><span class="line">        *)</span><br><span class="line">            <span class="built_in">return</span> 1</span><br><span class="line">            ;;</span><br><span class="line">    <span class="keyword">esac</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 等待数据库可用</span></span><br><span class="line"><span class="function"><span class="title">wait_for_db</span></span>() &#123;</span><br><span class="line">    <span class="built_in">local</span> db_type=<span class="variable">$1</span></span><br><span class="line">    <span class="built_in">local</span> host=<span class="variable">$2</span></span><br><span class="line">    <span class="built_in">local</span> port=<span class="variable">$3</span></span><br><span class="line">    <span class="built_in">local</span> user=<span class="variable">$4</span></span><br><span class="line">    <span class="built_in">local</span> password=<span class="variable">$5</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"等待 <span class="variable">$db_type</span> 在 <span class="variable">$host</span>:<span class="variable">$port</span> 可用..."</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> &#123;1..60&#125;; <span class="keyword">do</span></span><br><span class="line">        <span class="keyword">if</span> check_db_connection <span class="string">"<span class="variable">$db_type</span>"</span> <span class="string">"<span class="variable">$host</span>"</span> <span class="string">"<span class="variable">$port</span>"</span> <span class="string">"<span class="variable">$user</span>"</span> <span class="string">"<span class="variable">$password</span>"</span>; <span class="keyword">then</span></span><br><span class="line">            <span class="built_in">echo</span> <span class="string">"<span class="variable">$db_type</span> 已就绪"</span></span><br><span class="line">            <span class="built_in">return</span> 0</span><br><span class="line">        <span class="keyword">fi</span></span><br><span class="line">        sleep 5</span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"等待 <span class="variable">$db_type</span> 服务 (<span class="variable">$i</span>/60)..."</span></span><br><span class="line">    <span class="keyword">done</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"错误: <span class="variable">$db_type</span> 服务启动超时"</span></span><br><span class="line">    <span class="built_in">return</span> 1</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建 PostgreSQL 数据库</span></span><br><span class="line"><span class="function"><span class="title">ensure_postgresql_db</span></span>() &#123;</span><br><span class="line">    <span class="built_in">local</span> host=<span class="variable">$1</span></span><br><span class="line">    <span class="built_in">local</span> port=<span class="variable">$2</span></span><br><span class="line">    <span class="built_in">local</span> user=<span class="variable">$3</span></span><br><span class="line">    <span class="built_in">local</span> password=<span class="variable">$4</span></span><br><span class="line">    <span class="built_in">local</span> db_name=<span class="variable">$5</span></span><br><span class="line">    <span class="built_in">local</span> admin_user=<span class="string">"postgres"</span></span><br><span class="line">    <span class="built_in">local</span> admin_password=<span class="variable">$PG_POSTGRES_PASSWORD</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 检查数据库是否存在</span></span><br><span class="line">    <span class="keyword">if</span> PGPASSWORD=<span class="string">"<span class="variable">$password</span>"</span> psql -h <span class="variable">$host</span> -p <span class="variable">$port</span> -U <span class="variable">$user</span> -d postgres -lqt | cut -d \| -f 1 | grep -qw <span class="variable">$db_name</span>; <span class="keyword">then</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"数据库 <span class="variable">$db_name</span> 已存在"</span></span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"创建数据库 <span class="variable">$db_name</span>..."</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 使用管理员用户创建数据库</span></span><br><span class="line">        PGPASSWORD=<span class="string">"<span class="variable">$admin_password</span>"</span> psql -h <span class="variable">$host</span> -p <span class="variable">$port</span> -U <span class="variable">$admin_user</span> &lt;&lt;EOF</span><br><span class="line">CREATE DATABASE <span class="variable">$db_name</span>;</span><br><span class="line">GRANT ALL PRIVILEGES ON DATABASE <span class="variable">$db_name</span> TO <span class="variable">$user</span>;</span><br><span class="line">EOF</span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 确保用户对public模式有权限</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"确保 <span class="variable">$user</span> 对 <span class="variable">$db_name</span> 的 public 模式有权限..."</span></span><br><span class="line">    PGPASSWORD=<span class="string">"<span class="variable">$admin_password</span>"</span> psql -h <span class="variable">$host</span> -p <span class="variable">$port</span> -U <span class="variable">$admin_user</span> -d <span class="variable">$db_name</span> &lt;&lt;EOF</span><br><span class="line">GRANT ALL ON SCHEMA public TO <span class="variable">$user</span>;</span><br><span class="line">GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO <span class="variable">$user</span>;</span><br><span class="line">GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO <span class="variable">$user</span>;</span><br><span class="line">ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO <span class="variable">$user</span>;</span><br><span class="line">ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO <span class="variable">$user</span>;</span><br><span class="line">EOF</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装 基础依赖</span></span><br><span class="line"><span class="function"><span class="title">install_base</span></span>() &#123;</span><br><span class="line">    <span class="comment"># 先禁用IPv6</span></span><br><span class="line">    disable_ipv6</span><br><span class="line"></span><br><span class="line">    <span class="keyword">case</span> <span class="variable">$OS_NAME</span> <span class="keyword">in</span></span><br><span class="line">        ubuntu)</span><br><span class="line">            apt update</span><br><span class="line">            DEBIAN_FRONTEND=noninteractive TZ=Asia/Shanghai apt-get -y install tzdata</span><br><span class="line">            apt install -y sudo curl systemctl</span><br><span class="line">            apt install -y language-pack-zh-han*</span><br><span class="line">            <span class="built_in">echo</span> LANG=zh_CN.UTF-8 &gt; /etc/default/locale</span><br><span class="line">            <span class="built_in">source</span> /etc/default/locale</span><br><span class="line">            ;;</span><br><span class="line"></span><br><span class="line">        centos|rhel|alinux)</span><br><span class="line">            <span class="comment"># 统一处理CentOS/RHEL/Alibaba Linux</span></span><br><span class="line">            <span class="keyword">if</span> [ <span class="variable">$OS_VERSION</span> -eq 7 ]; <span class="keyword">then</span></span><br><span class="line">                curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo &amp;&amp; \</span><br><span class="line">                yum clean all &amp;&amp; \</span><br><span class="line">                yum makecache</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                cp -r /etc/yum.repos.d/ /etc/yum.repos.d_bak</span><br><span class="line">                rm -rf /etc/yum.repos.d/CentOS-Linux*</span><br><span class="line">                curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-vault-8.5.2111.repo</span><br><span class="line">                sed -i -e <span class="string">"s|mirrors.cloud.aliyuncs.com|mirrors.aliyun.com|g "</span> /etc/yum.repos.d/CentOS-*</span><br><span class="line">                sed -i -e <span class="string">"s|releasever|releasever-stream|g"</span> /etc/yum.repos.d/CentOS-*</span><br><span class="line">                yum clean all &amp;&amp; yum makecache</span><br><span class="line">            <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">            yum install -y sudo freetype fontconfig epel-release --nogpgcheck</span><br><span class="line"></span><br><span class="line">            <span class="comment"># 安装中文语言包并设置系统语言为中文</span></span><br><span class="line">            <span class="built_in">echo</span> <span class="string">"安装中文语言包并设置系统语言..."</span></span><br><span class="line">            sudo yum install -y glibc-langpack-zh.x86_64</span><br><span class="line">            <span class="built_in">echo</span> LANG=zh_CN.UTF-8 &gt; /etc/locale.conf</span><br><span class="line">            <span class="built_in">source</span> /etc/locale.conf</span><br><span class="line">            ;;</span><br><span class="line">    <span class="keyword">esac</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装 Java</span></span><br><span class="line"><span class="function"><span class="title">install_java</span></span>() &#123;</span><br><span class="line">    <span class="keyword">case</span> <span class="variable">$OS_NAME</span> <span class="keyword">in</span></span><br><span class="line">        ubuntu)</span><br><span class="line">            sudo apt update</span><br><span class="line">            sudo apt install -y openjdk-17-jdk</span><br><span class="line">            ;;</span><br><span class="line"></span><br><span class="line">        centos|rhel|alinux)</span><br><span class="line">            sudo yum install -y rpm/jdk-17.0.15_linux-x64_bin.rpm --nogpgcheck</span><br><span class="line">            ;;</span><br><span class="line">    <span class="keyword">esac</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装 MySQL</span></span><br><span class="line"><span class="function"><span class="title">install_mysql</span></span>() &#123;</span><br><span class="line">    <span class="keyword">case</span> <span class="variable">$OS_NAME</span> <span class="keyword">in</span></span><br><span class="line">        ubuntu)</span><br><span class="line">            <span class="comment"># 预置root密码</span></span><br><span class="line">            sudo debconf-set-selections &lt;&lt;&lt; <span class="string">"mysql-server mysql-server/root_password password <span class="variable">$MYSQL_ROOT_PASSWORD</span>"</span></span><br><span class="line">            sudo debconf-set-selections &lt;&lt;&lt; <span class="string">"mysql-server mysql-server/root_password_again password <span class="variable">$MYSQL_ROOT_PASSWORD</span>"</span></span><br><span class="line">            sudo apt install -y mysql-server</span><br><span class="line">            sudo systemctl <span class="built_in">enable</span> --now mysql</span><br><span class="line"></span><br><span class="line">            <span class="comment"># 自动创建配置用户</span></span><br><span class="line">            sudo mysql -uroot -p<span class="variable">$MYSQL_ROOT_PASSWORD</span> &lt;&lt;EOF</span><br><span class="line">CREATE USER <span class="string">'$DB_USER'</span>@<span class="string">'%'</span> IDENTIFIED BY <span class="string">'$DB_PASSWORD'</span>;</span><br><span class="line">GRANT ALL PRIVILEGES ON *.* TO <span class="string">'$DB_USER'</span>@<span class="string">'%'</span> WITH GRANT OPTION;</span><br><span class="line">FLUSH PRIVILEGES;</span><br><span class="line">EOF</span><br><span class="line">            ;;</span><br><span class="line"></span><br><span class="line">        centos|rhel|alinux)</span><br><span class="line">            sudo yum module -y <span class="built_in">disable</span> mysql</span><br><span class="line">            <span class="keyword">if</span> [ <span class="variable">$OS_VERSION</span> -eq 7 ]; <span class="keyword">then</span></span><br><span class="line">                sudo yum install -y rpm/mysql84-community-release-el7-1.noarch.rpm --nogpgcheck</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                sudo yum install -y rpm/mysql84-community-release-el8-1.noarch.rpm --nogpgcheck</span><br><span class="line">            <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">            sudo yum install -y mysql-community-server --nogpgcheck</span><br><span class="line">            sudo systemctl <span class="built_in">enable</span> --now mysqld</span><br><span class="line"></span><br><span class="line">            <span class="comment"># 获取临时密码</span></span><br><span class="line">            temp_pass=$(sudo grep <span class="string">'temporary password'</span> /var/<span class="built_in">log</span>/mysqld.log | awk <span class="string">'&#123;print $NF&#125;'</span>)</span><br><span class="line"></span><br><span class="line">            <span class="comment"># 自动修改root密码</span></span><br><span class="line">            mysql -uroot -p<span class="string">"<span class="variable">$temp_pass</span>"</span> --connect-expired-password &lt;&lt;EOF 2&gt;/dev/null</span><br><span class="line">ALTER USER <span class="string">'root'</span>@<span class="string">'localhost'</span> IDENTIFIED BY <span class="string">'$MYSQL_ROOT_PASSWORD'</span>;</span><br><span class="line">FLUSH PRIVILEGES;</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line">            <span class="comment"># 创建配置用户</span></span><br><span class="line">            mysql -uroot -p<span class="variable">$MYSQL_ROOT_PASSWORD</span> &lt;&lt;EOF</span><br><span class="line">CREATE USER <span class="string">'$DB_USER'</span>@<span class="string">'%'</span> IDENTIFIED BY <span class="string">'$DB_PASSWORD'</span>;</span><br><span class="line">GRANT ALL PRIVILEGES ON *.* TO <span class="string">'$DB_USER'</span>@<span class="string">'%'</span> WITH GRANT OPTION;</span><br><span class="line">FLUSH PRIVILEGES;</span><br><span class="line">EOF</span><br><span class="line">            ;;</span><br><span class="line">    <span class="keyword">esac</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装 PostgreSQL</span></span><br><span class="line"><span class="function"><span class="title">install_postgresql</span></span>() &#123;</span><br><span class="line">    <span class="keyword">case</span> <span class="variable">$OS_NAME</span> <span class="keyword">in</span></span><br><span class="line">        ubuntu)</span><br><span class="line">            sudo apt install -y postgresql postgresql-contrib</span><br><span class="line">            sudo service postgresql start</span><br><span class="line"></span><br><span class="line">            <span class="comment"># 使用单个psql命令避免目录错误</span></span><br><span class="line">            sudo -u postgres psql &lt;&lt;EOF</span><br><span class="line">ALTER USER postgres WITH PASSWORD <span class="string">'$PG_POSTGRES_PASSWORD'</span>;</span><br><span class="line">CREATE USER <span class="variable">$PG_USER</span> WITH PASSWORD <span class="string">'$PG_PASSWORD'</span>;</span><br><span class="line">ALTER USER <span class="variable">$PG_USER</span> CREATEDB;</span><br><span class="line">CREATE DATABASE thingsboard;</span><br><span class="line">GRANT ALL PRIVILEGES ON DATABASE thingsboard TO <span class="variable">$PG_USER</span>;</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line">            <span class="comment"># 确保用户对public模式有权限</span></span><br><span class="line">            sudo -u postgres psql -d thingsboard &lt;&lt;EOF</span><br><span class="line">GRANT ALL ON SCHEMA public TO <span class="variable">$PG_USER</span>;</span><br><span class="line">GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO <span class="variable">$PG_USER</span>;</span><br><span class="line">GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO <span class="variable">$PG_USER</span>;</span><br><span class="line">ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO <span class="variable">$PG_USER</span>;</span><br><span class="line">ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO <span class="variable">$PG_USER</span>;</span><br><span class="line">EOF</span><br><span class="line">            ;;</span><br><span class="line"></span><br><span class="line">        centos|rhel|alinux)</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> [ <span class="variable">$OS_VERSION</span> -eq 7 ]; <span class="keyword">then</span></span><br><span class="line">                sudo yum install -y rpm/postgresql15-libs-15.9-1PGDG.rhel7.x86_64.rpm --nogpgcheck</span><br><span class="line">                sudo yum install -y rpm/postgresql15-15.9-1PGDG.rhel7.x86_64.rpm --nogpgcheck</span><br><span class="line">                sudo yum install -y rpm/postgresql15-server-15.9-1PGDG.rhel7.x86_64.rpm --nogpgcheck</span><br><span class="line"></span><br><span class="line">                <span class="comment"># Initialize your PostgreSQL DB</span></span><br><span class="line">                sudo /usr/pgsql-15/bin/postgresql-15-setup initdb</span><br><span class="line">                sudo systemctl <span class="built_in">enable</span> --now postgresql-15</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                sudo yum install -y rpm/postgresql16-libs-16.9-3PGDG.rhel8.x86_64.rpm --nogpgcheck</span><br><span class="line">                sudo yum install -y rpm/postgresql16-16.9-3PGDG.rhel8.x86_64.rpm --nogpgcheck</span><br><span class="line">                sudo yum install -y rpm/postgresql16-server-16.9-3PGDG.rhel8.x86_64.rpm --nogpgcheck</span><br><span class="line"></span><br><span class="line">                sudo /usr/pgsql-16/bin/postgresql-16-setup initdb</span><br><span class="line">                sudo systemctl <span class="built_in">enable</span> --now postgresql-16</span><br><span class="line">            <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">            <span class="comment"># 创建用户和数据库</span></span><br><span class="line">            sudo -u postgres psql &lt;&lt;EOF</span><br><span class="line">ALTER USER postgres WITH PASSWORD <span class="string">'$PG_POSTGRES_PASSWORD'</span>;</span><br><span class="line">CREATE USER <span class="variable">$PG_USER</span> WITH PASSWORD <span class="string">'$PG_PASSWORD'</span>;</span><br><span class="line">ALTER USER <span class="variable">$PG_USER</span> CREATEDB;</span><br><span class="line">CREATE DATABASE thingsboard;</span><br><span class="line">GRANT ALL PRIVILEGES ON DATABASE thingsboard TO <span class="variable">$PG_USER</span>;</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line">            <span class="comment"># 确保用户对public模式有权限</span></span><br><span class="line">            sudo -u postgres psql -d thingsboard &lt;&lt;EOF</span><br><span class="line">GRANT ALL ON SCHEMA public TO <span class="variable">$PG_USER</span>;</span><br><span class="line">GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO <span class="variable">$PG_USER</span>;</span><br><span class="line">GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO <span class="variable">$PG_USER</span>;</span><br><span class="line">ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO <span class="variable">$PG_USER</span>;</span><br><span class="line">ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUences TO <span class="variable">$PG_USER</span>;</span><br><span class="line">EOF</span><br><span class="line">            ;;</span><br><span class="line">    <span class="keyword">esac</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 设置主机和端口为本地值</span></span><br><span class="line">    PG_HOST=<span class="string">"localhost"</span></span><br><span class="line">    PG_PORT=<span class="string">"5432"</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">install_redis</span></span>() &#123;</span><br><span class="line">    <span class="keyword">case</span> <span class="variable">$OS_NAME</span> <span class="keyword">in</span></span><br><span class="line">        ubuntu)</span><br><span class="line">            sudo apt install -y redis-server</span><br><span class="line">            ;;</span><br><span class="line">        centos|rhel|alinux)</span><br><span class="line">            <span class="keyword">if</span> [ <span class="variable">$OS_VERSION</span> -eq 7 ]; <span class="keyword">then</span></span><br><span class="line">                sudo yum install -y redis --nogpgcheck</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                sudo dnf install -y redis --nogpgcheck</span><br><span class="line">            <span class="keyword">fi</span></span><br><span class="line">            ;;</span><br><span class="line">    <span class="keyword">esac</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 获取Redis配置</span></span><br><span class="line">    <span class="built_in">local</span> redis_password=$(get_config <span class="string">'redis_password'</span>)</span><br><span class="line">    <span class="built_in">local</span> redis_conf=<span class="string">""</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 确定配置文件路径</span></span><br><span class="line">    <span class="keyword">case</span> <span class="variable">$OS_NAME</span> <span class="keyword">in</span></span><br><span class="line">        ubuntu)</span><br><span class="line">            redis_conf=<span class="string">"/etc/redis/redis.conf"</span></span><br><span class="line">            ;;</span><br><span class="line">        centos|rhel|alinux)</span><br><span class="line">            redis_conf=<span class="string">"/etc/redis.conf"</span></span><br><span class="line">            ;;</span><br><span class="line">    <span class="keyword">esac</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 配置密码（如果设置了）</span></span><br><span class="line">    <span class="keyword">if</span> [ -n <span class="string">"<span class="variable">$redis_password</span>"</span> ]; <span class="keyword">then</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"设置Redis密码..."</span></span><br><span class="line">        sudo sed -i <span class="string">"s/^# requirepass .*/requirepass <span class="variable">$redis_password</span>/"</span> <span class="string">"<span class="variable">$redis_conf</span>"</span></span><br><span class="line">        sudo sed -i <span class="string">"s/^requirepass .*/requirepass <span class="variable">$redis_password</span>/"</span> <span class="string">"<span class="variable">$redis_conf</span>"</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 启动服务</span></span><br><span class="line">    <span class="keyword">case</span> <span class="variable">$OS_NAME</span> <span class="keyword">in</span></span><br><span class="line">        ubuntu)</span><br><span class="line">            sudo systemctl restart redis-server</span><br><span class="line">            sudo systemctl <span class="built_in">enable</span> redis-server</span><br><span class="line">            ;;</span><br><span class="line">        centos|rhel|alinux)</span><br><span class="line">            sudo systemctl restart redis</span><br><span class="line">            sudo systemctl <span class="built_in">enable</span> redis</span><br><span class="line">            ;;</span><br><span class="line">    <span class="keyword">esac</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装 Nginx</span></span><br><span class="line"><span class="function"><span class="title">install_nginx</span></span>() &#123;</span><br><span class="line">    <span class="keyword">case</span> <span class="variable">$OS_NAME</span> <span class="keyword">in</span></span><br><span class="line">        ubuntu)</span><br><span class="line">            sudo apt install -y nginx</span><br><span class="line">            ;;</span><br><span class="line">        centos|rhel|alinux)</span><br><span class="line">            <span class="keyword">if</span> [ <span class="variable">$OS_VERSION</span> -eq 7 ]; <span class="keyword">then</span></span><br><span class="line">                sudo yum install -y epel-release --nogpgcheck</span><br><span class="line">                sudo yum install -y nginx --nogpgcheck</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                sudo yum install -y nginx --nogpgcheck</span><br><span class="line">            <span class="keyword">fi</span></span><br><span class="line">            ;;</span><br><span class="line">    <span class="keyword">esac</span></span><br><span class="line">    sudo systemctl <span class="built_in">enable</span> --now nginx</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装 ThingsBoard - 添加目录验证</span></span><br><span class="line"><span class="function"><span class="title">install_thingsboard</span></span>() &#123;</span><br><span class="line">    <span class="keyword">case</span> <span class="variable">$OS_NAME</span> <span class="keyword">in</span></span><br><span class="line">        ubuntu)</span><br><span class="line">            sudo apt install -y ./rpm/thingsboard-4.0.1.deb</span><br><span class="line"></span><br><span class="line">            <span class="comment"># 确保正确的安装目录结构</span></span><br><span class="line">            <span class="keyword">if</span> [ ! -d <span class="string">"/usr/share/thingsboard"</span> ]; <span class="keyword">then</span></span><br><span class="line">                <span class="built_in">echo</span> <span class="string">"错误：ThingsBoard 未正确安装到 /usr/share/thingsboard"</span></span><br><span class="line">                <span class="built_in">exit</span> 1</span><br><span class="line">            <span class="keyword">fi</span></span><br><span class="line">            ;;</span><br><span class="line">        centos|rhel|alinux)</span><br><span class="line">            sudo yum install -y rpm/thingsboard-4.0.1.rpm --nogpgcheck</span><br><span class="line"></span><br><span class="line">            <span class="comment"># 确保正确的安装目录结构</span></span><br><span class="line">            <span class="keyword">if</span> [ ! -d <span class="string">"/usr/share/thingsboard"</span> ]; <span class="keyword">then</span></span><br><span class="line">                <span class="built_in">echo</span> <span class="string">"错误：ThingsBoard 未正确安装到 /usr/share/thingsboard"</span></span><br><span class="line">                <span class="built_in">exit</span> 1</span><br><span class="line">            <span class="keyword">fi</span></span><br><span class="line">            ;;</span><br><span class="line">    <span class="keyword">esac</span></span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置ThingsBoard数据库连接</span></span><br><span class="line"><span class="function"><span class="title">configure_thingsboard</span></span>() &#123;</span><br><span class="line">    <span class="built_in">local</span> config_file=<span class="string">"/etc/thingsboard/conf/thingsboard.conf"</span></span><br><span class="line">    <span class="built_in">local</span> pg_user=$(get_config <span class="string">'postgresql_user'</span>)</span><br><span class="line">    <span class="built_in">local</span> pg_password=$(get_config <span class="string">'postgresql_password'</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 检查配置文件是否存在</span></span><br><span class="line">    <span class="keyword">if</span> [ ! -f <span class="string">"<span class="variable">$config_file</span>"</span> ]; <span class="keyword">then</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"警告：ThingsBoard配置文件 <span class="variable">$config_file</span> 不存在"</span></span><br><span class="line">        <span class="built_in">return</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 检查配置是否已存在</span></span><br><span class="line">    <span class="keyword">if</span> grep -q <span class="string">"SPRING_DATASOURCE_URL"</span> <span class="string">"<span class="variable">$config_file</span>"</span>; <span class="keyword">then</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"数据库配置已存在，跳过配置"</span></span><br><span class="line">        <span class="built_in">return</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 添加数据库配置</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"# DB Configuration"</span> | sudo tee -a <span class="string">"<span class="variable">$config_file</span>"</span> &gt; /dev/null</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"export DATABASE_TS_TYPE=sql"</span> | sudo tee -a <span class="string">"<span class="variable">$config_file</span>"</span> &gt; /dev/null</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"export SPRING_DATASOURCE_URL=jdbc:postgresql://<span class="variable">$&#123;PG_HOST&#125;</span>:<span class="variable">$&#123;PG_PORT&#125;</span>/thingsboard"</span> | sudo tee -a <span class="string">"<span class="variable">$config_file</span>"</span> &gt; /dev/null</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"export SPRING_DATASOURCE_USERNAME=<span class="variable">$pg_user</span>"</span> | sudo tee -a <span class="string">"<span class="variable">$config_file</span>"</span> &gt; /dev/null</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"export SPRING_DATASOURCE_PASSWORD=<span class="variable">$pg_password</span>"</span> | sudo tee -a <span class="string">"<span class="variable">$config_file</span>"</span> &gt; /dev/null</span><br><span class="line"></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"已配置ThingsBoard使用PostgreSQL数据库 (<span class="variable">$&#123;PG_HOST&#125;</span>:<span class="variable">$&#123;PG_PORT&#125;</span>)"</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置ThingsBoard内存设置 - 使用MB单位</span></span><br><span class="line"><span class="function"><span class="title">configure_thingsboard_memory</span></span>() &#123;</span><br><span class="line">    <span class="built_in">local</span> config_file=<span class="string">"/etc/thingsboard/conf/thingsboard.conf"</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 检查配置文件是否存在</span></span><br><span class="line">    <span class="keyword">if</span> [ ! -f <span class="string">"<span class="variable">$config_file</span>"</span> ]; <span class="keyword">then</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"警告：ThingsBoard配置文件 <span class="variable">$config_file</span> 不存在"</span></span><br><span class="line">        <span class="built_in">return</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 检测系统内存（MB）</span></span><br><span class="line">    <span class="built_in">local</span> mem_total</span><br><span class="line">    mem_total=$(grep MemTotal /proc/meminfo | awk <span class="string">'&#123;print $2&#125;'</span>)</span><br><span class="line">    mem_total=$((mem_total / 1024))  <span class="comment"># 转换KB为MB</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> [ -z <span class="string">"<span class="variable">$mem_total</span>"</span> ]; <span class="keyword">then</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"错误：无法检测系统内存"</span></span><br><span class="line">        <span class="built_in">return</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> [ <span class="string">"<span class="variable">$mem_total</span>"</span> -le 4096 ]; <span class="keyword">then</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"系统内存为 <span class="variable">$&#123;mem_total&#125;</span>MB &lt;= 4GB，配置ThingsBoard使用2048MB内存"</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 检查是否已配置</span></span><br><span class="line">        <span class="keyword">if</span> ! grep -q <span class="string">"Xms2048M"</span> <span class="string">"<span class="variable">$config_file</span>"</span>; <span class="keyword">then</span></span><br><span class="line">            <span class="built_in">echo</span> <span class="string">"# Update ThingsBoard memory usage for low-memory systems"</span> | sudo tee -a <span class="string">"<span class="variable">$config_file</span>"</span></span><br><span class="line">            <span class="built_in">echo</span> <span class="string">"export JAVA_OPTS=\"\$JAVA_OPTS -Xms2048M -Xmx2048M\""</span> | sudo tee -a <span class="string">"<span class="variable">$config_file</span>"</span></span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            <span class="built_in">echo</span> <span class="string">"内存配置已存在，跳过"</span></span><br><span class="line">        <span class="keyword">fi</span></span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"系统内存为 <span class="variable">$&#123;mem_total&#125;</span>MB &gt; 4GB，使用默认内存配置"</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始化ThingsBoard数据库</span></span><br><span class="line"><span class="function"><span class="title">init_thingsboard</span></span>() &#123;</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"初始化ThingsBoard数据库..."</span></span><br><span class="line">    <span class="built_in">local</span> pg_user=$(get_config <span class="string">'postgresql_user'</span>)</span><br><span class="line">    <span class="built_in">local</span> pg_password=$(get_config <span class="string">'postgresql_password'</span>)</span><br><span class="line">    <span class="built_in">local</span> pg_host=$(get_config <span class="string">'postgresql_host'</span>)</span><br><span class="line">    <span class="built_in">local</span> pg_port=$(get_config <span class="string">'postgresql_port'</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 确保psql命令可用</span></span><br><span class="line">    <span class="keyword">if</span> ! <span class="built_in">command</span> -v psql &amp;&gt; /dev/null; <span class="keyword">then</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"安装PostgreSQL客户端..."</span></span><br><span class="line">        <span class="keyword">if</span> [ <span class="string">"<span class="variable">$OS_NAME</span>"</span> = <span class="string">"ubuntu"</span> ]; <span class="keyword">then</span></span><br><span class="line">            install_package <span class="string">"postgresql-client"</span></span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            install_package <span class="string">"postgresql"</span></span><br><span class="line">        <span class="keyword">fi</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 创建索引并清理数据</span></span><br><span class="line">    PGPASSWORD=<span class="string">"<span class="variable">$pg_password</span>"</span> psql \</span><br><span class="line">      -h <span class="string">"<span class="variable">$pg_host</span>"</span> \</span><br><span class="line">      -p <span class="string">"<span class="variable">$pg_port</span>"</span> \</span><br><span class="line">      -U <span class="string">"<span class="variable">$pg_user</span>"</span> \</span><br><span class="line">      -d thingsboard \</span><br><span class="line">      -c <span class="string">"</span></span><br><span class="line"><span class="string">        CREATE INDEX IF NOT EXISTS idx_device_label</span></span><br><span class="line"><span class="string">          ON device (tenant_id, label);</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">        DELETE FROM device;</span></span><br><span class="line"><span class="string">        DELETE FROM device_credentials;</span></span><br><span class="line"><span class="string">        DELETE FROM customer;</span></span><br><span class="line"><span class="string">      "</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> [ $? -eq 0 ]; <span class="keyword">then</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"数据库初始化成功"</span></span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"警告：数据库初始化失败"</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 执行ThingsBoard安装脚本并启动服务 - 添加权限验证</span></span><br><span class="line"><span class="function"><span class="title">install_and_start_thingsboard</span></span>() &#123;</span><br><span class="line">    <span class="comment"># 设置端口</span></span><br><span class="line">    <span class="built_in">local</span> port_thingsboard=$(get_config <span class="string">'port_thingsboard_port'</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 检查内存状态</span></span><br><span class="line">    <span class="built_in">local</span> mem_total=$(free -m | awk <span class="string">'/^Mem:/&#123;print $2&#125;'</span>)</span><br><span class="line"></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"执行ThingsBoard安装脚本..."</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 确保安装目录有正确权限</span></span><br><span class="line">    sudo chown -R thingsboard:thingsboard /usr/share/thingsboard</span><br><span class="line">    sudo chmod 755 /usr/share/thingsboard</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 确保在正确的目录执行</span></span><br><span class="line">    (<span class="built_in">cd</span> /usr/share/thingsboard &amp;&amp; sudo bin/install/install.sh --loadDemo)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 配置防火墙（仅当firewall-cmd存在时）</span></span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">command</span> -v firewall-cmd &amp;&gt; /dev/null; <span class="keyword">then</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"配置防火墙..."</span></span><br><span class="line">        sudo firewall-cmd --zone=public --add-port=8080/tcp --permanent</span><br><span class="line">        sudo firewall-cmd --reload</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"未找到firewall-cmd，跳过防火墙配置"</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 扩大请求体限制</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"export HTTP_BIND_PORT=<span class="variable">$port_thingsboard</span>"</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"export HTTP_MAX_PAYLOAD_SIZE_LIMIT_CONFIGURATION=\"/api/image*/**=52428800;/api/resource/**=52428800;/api/**=524288000\""</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"export SPRING_SERVLET_MULTIPART_MAX_FILE_SIZE=512MB"</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"export SPRING_SERVLET_MULTIPART_MAX_REQUEST_SIZE=512MB"</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"export HTTP_TRANSPORT_MAX_PAYLOAD_SIZE_LIMIT_CONFIGURATION=\"/api/v1/*/rpc/**=655360;/api/v1/**=524288000;/api/**=524288000\""</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"export NETTY_MAX_PAYLOAD_SIZE=524288000"</span></span><br><span class="line">    &#125; | sudo tee -a /etc/bashrc &gt; /dev/null</span><br><span class="line"></span><br><span class="line">    <span class="built_in">source</span> /etc/bashrc</span><br><span class="line"></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"启动ThingsBoard服务..."</span></span><br><span class="line">    <span class="comment"># 使用systemctl如果可用，否则使用service</span></span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">command</span> -v systemctl &amp;&gt; /dev/null; <span class="keyword">then</span></span><br><span class="line">        sudo systemctl <span class="built_in">enable</span> --now thingsboard</span><br><span class="line">        <span class="comment"># 添加服务状态检查</span></span><br><span class="line">        sleep 30</span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"检查ThingsBoard服务状态..."</span></span><br><span class="line">        sudo systemctl status thingsboard --no-pager</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 如果服务失败，查看日志</span></span><br><span class="line">        <span class="keyword">if</span> ! systemctl is-active --quiet thingsboard; <span class="keyword">then</span></span><br><span class="line">            <span class="built_in">echo</span> <span class="string">"ThingsBoard服务启动失败，查看日志..."</span></span><br><span class="line">            sudo tail -n 100 /var/<span class="built_in">log</span>/thingsboard/thingsboard.log</span><br><span class="line">            <span class="built_in">echo</span> <span class="string">"尝试强行执行jar..."</span></span><br><span class="line">            nohup /bin/bash /usr/share/thingsboard/bin/thingsboard.jar &gt;/dev/null 2&gt;&amp;1 &amp;</span><br><span class="line">        <span class="keyword">fi</span></span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="comment"># 回退到service命令</span></span><br><span class="line">        sudo service thingsboard start</span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 初始化数据库</span></span><br><span class="line">    init_thingsboard</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置Nginx</span></span><br><span class="line"><span class="function"><span class="title">configure_nginx</span></span>() &#123;</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"备份默认Nginx配置..."</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 备份主配置文件</span></span><br><span class="line">    <span class="built_in">local</span> nginx_conf=<span class="string">"/etc/nginx/nginx.conf"</span></span><br><span class="line">    <span class="keyword">if</span> [ -f <span class="string">"<span class="variable">$nginx_conf</span>"</span> ]; <span class="keyword">then</span></span><br><span class="line">        sudo mv <span class="string">"<span class="variable">$nginx_conf</span>"</span> <span class="string">"<span class="variable">$&#123;nginx_conf&#125;</span>_bak"</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"已备份 <span class="variable">$nginx_conf</span> 为 <span class="variable">$&#123;nginx_conf&#125;</span>_bak"</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 备份默认站点配置</span></span><br><span class="line">    <span class="built_in">local</span> default_conf=<span class="string">""</span></span><br><span class="line">    <span class="keyword">case</span> <span class="variable">$OS_NAME</span> <span class="keyword">in</span></span><br><span class="line">        ubuntu)</span><br><span class="line">            default_conf=<span class="string">"/etc/nginx/sites-enabled/default"</span></span><br><span class="line">            ;;</span><br><span class="line">        centos|rhel|alinux)</span><br><span class="line">            default_conf=<span class="string">"/etc/nginx/conf.d/default.conf"</span></span><br><span class="line">            <span class="keyword">if</span> [ ! -f <span class="string">"<span class="variable">$default_conf</span>"</span> ]; <span class="keyword">then</span></span><br><span class="line">                default_conf=<span class="string">"/etc/nginx/nginx.conf.default"</span></span><br><span class="line">            <span class="keyword">fi</span></span><br><span class="line">            ;;</span><br><span class="line">    <span class="keyword">esac</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> [ -f <span class="string">"<span class="variable">$default_conf</span>"</span> ]; <span class="keyword">then</span></span><br><span class="line">        sudo mv <span class="string">"<span class="variable">$default_conf</span>"</span> <span class="string">"<span class="variable">$&#123;default_conf&#125;</span>_bak"</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"已备份 <span class="variable">$default_conf</span> 为 <span class="variable">$&#123;default_conf&#125;</span>_bak"</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 获取前端基础目录</span></span><br><span class="line">    <span class="built_in">local</span> basedir=$(get_config <span class="string">'frontend_basedir'</span>)</span><br><span class="line">    <span class="keyword">if</span> [ -z <span class="string">"<span class="variable">$basedir</span>"</span> ]; <span class="keyword">then</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"警告：前端基础目录未配置，跳过Nginx配置"</span></span><br><span class="line">        <span class="built_in">return</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 创建基础目录</span></span><br><span class="line">    sudo mkdir -p <span class="string">"<span class="variable">$basedir</span>"</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 复制前端资源并替换URL</span></span><br><span class="line">    <span class="built_in">local</span> iot_gateway_url=$(get_config <span class="string">'frontend_iot_gateway_url'</span>)</span><br><span class="line">    <span class="built_in">local</span> xxx_url=$(get_config <span class="string">'frontend_xxx_url'</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 复制iot_blockly_frontend</span></span><br><span class="line">    <span class="built_in">local</span> src_dir1=<span class="string">"<span class="variable">$SCRIPT_DIR</span>/iot_gateway/iot_blockly_frontend"</span></span><br><span class="line">    <span class="built_in">local</span> dest_dir1=<span class="string">"<span class="variable">$basedir</span>/iot_blockly_frontend"</span></span><br><span class="line">    <span class="keyword">if</span> [ -d <span class="string">"<span class="variable">$src_dir1</span>"</span> ]; <span class="keyword">then</span></span><br><span class="line">        sudo cp -r <span class="string">"<span class="variable">$src_dir1</span>"</span> <span class="string">"<span class="variable">$basedir</span>/"</span></span><br><span class="line">        <span class="comment"># 替换URL</span></span><br><span class="line">        sudo find <span class="string">"<span class="variable">$dest_dir1</span>"</span> -<span class="built_in">type</span> f -<span class="built_in">exec</span> sed -i <span class="string">"s|IOT_GATEWAY_URL|<span class="variable">$&#123;iot_gateway_url&#125;</span>|g"</span> &#123;&#125; \;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"警告：前端资源目录 <span class="variable">$src_dir1</span> 不存在"</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 复制iot_xxx_frontend</span></span><br><span class="line">    <span class="built_in">local</span> src_dir2=<span class="string">"<span class="variable">$SCRIPT_DIR</span>/ruoyi-admin/iot_xxx_frontend"</span></span><br><span class="line">    <span class="built_in">local</span> dest_dir2=<span class="string">"<span class="variable">$basedir</span>/iot_xxx_frontend"</span></span><br><span class="line">    <span class="keyword">if</span> [ -d <span class="string">"<span class="variable">$src_dir2</span>"</span> ]; <span class="keyword">then</span></span><br><span class="line">        sudo cp -r <span class="string">"<span class="variable">$src_dir2</span>"</span> <span class="string">"<span class="variable">$basedir</span>/"</span></span><br><span class="line">        <span class="comment"># 替换URL</span></span><br><span class="line">        sudo find <span class="string">"<span class="variable">$dest_dir2</span>"</span> -<span class="built_in">type</span> f -<span class="built_in">exec</span> sed -i <span class="string">"s|xxx_URL|<span class="variable">$&#123;xxx_url&#125;</span>|g"</span> &#123;&#125; \;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"警告：前端资源目录 <span class="variable">$src_dir2</span> 不存在"</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 创建Nginx配置文件目录</span></span><br><span class="line">    sudo mkdir -p /etc/nginx/conf.d</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 确定Nginx运行时使用的用户</span></span><br><span class="line">        <span class="built_in">local</span> nginx_user</span><br><span class="line">        <span class="keyword">if</span> [ <span class="string">"<span class="variable">$OS_NAME</span>"</span> = <span class="string">"ubuntu"</span> ]; <span class="keyword">then</span></span><br><span class="line">            nginx_user=<span class="string">"www-data"</span>  <span class="comment"># Ubuntu默认使用www-data</span></span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            nginx_user=<span class="string">"nginx"</span>     <span class="comment"># CentOS使用nginx</span></span><br><span class="line">        <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 创建主配置文件（总是覆盖）</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"创建新的Nginx主配置..."</span></span><br><span class="line">        sudo tee /etc/nginx/nginx.conf &gt; /dev/null &lt;&lt;EOF</span><br><span class="line">user <span class="variable">$nginx_user</span>;  <span class="comment"># 动态设置用户</span></span><br><span class="line">worker_processes auto;</span><br><span class="line">error_log /var/<span class="built_in">log</span>/nginx/error.log;</span><br><span class="line">pid /run/nginx.pid;</span><br><span class="line"></span><br><span class="line"><span class="comment"># Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.</span></span><br><span class="line">include /usr/share/nginx/modules/*.conf;</span><br><span class="line"></span><br><span class="line">events &#123;</span><br><span class="line">    worker_connections 1024;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">http &#123;</span><br><span class="line">    log_format  main  <span class="string">'\$remote_addr - \$remote_user [\$time_local] "\$request" '</span></span><br><span class="line">                      <span class="string">'\$status \$body_bytes_sent "\$http_referer" '</span></span><br><span class="line">                      <span class="string">'"\$http_user_agent" "\$http_x_forwarded_for"'</span>;</span><br><span class="line"></span><br><span class="line">    access_log  /var/<span class="built_in">log</span>/nginx/access.log  main;</span><br><span class="line"></span><br><span class="line">    sendfile            on;</span><br><span class="line">    tcp_nopush          on;</span><br><span class="line">    tcp_nodelay         on;</span><br><span class="line">    keepalive_timeout   65;</span><br><span class="line">    types_hash_max_size 2048;</span><br><span class="line"></span><br><span class="line">    include             /etc/nginx/mime.types;</span><br><span class="line">    default_type        application/octet-stream;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Load modular configuration files from the /etc/nginx/conf.d directory.</span></span><br><span class="line">    include /etc/nginx/conf.d/*.conf;</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 创建Nginx配置文件</span></span><br><span class="line">    <span class="built_in">local</span> tb_conf=<span class="string">"/etc/nginx/conf.d/tb.conf"</span></span><br><span class="line">    <span class="built_in">local</span> xxx_conf=<span class="string">"/etc/nginx/conf.d/xxx.conf"</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 获取配置参数</span></span><br><span class="line">    <span class="built_in">local</span> thingsboard_port=$(get_config <span class="string">'port_thingsboard_port'</span>)</span><br><span class="line">    <span class="built_in">local</span> iot_gateway_port=$(get_config <span class="string">'port_iot_gateway_port'</span>)</span><br><span class="line">    <span class="built_in">local</span> xxx_backend_port=$(get_config <span class="string">'port_xxx_backend_port'</span>)</span><br><span class="line">    <span class="built_in">local</span> tb_ssl_cert=$(get_config <span class="string">'nginx_thingsboard_ssl_certificate'</span>)</span><br><span class="line">    <span class="built_in">local</span> tb_ssl_key=$(get_config <span class="string">'nginx_thingsboard_ssl_certificate_key'</span>)</span><br><span class="line">    <span class="built_in">local</span> xxx_ssl_cert=$(get_config <span class="string">'nginx_xxx_ssl_certificate'</span>)</span><br><span class="line">    <span class="built_in">local</span> xxx_ssl_key=$(get_config <span class="string">'nginx_xxx_ssl_certificate_key'</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 创建ThingsBoard配置</span></span><br><span class="line">    sudo tee <span class="string">"<span class="variable">$tb_conf</span>"</span> &gt; /dev/null &lt;&lt;EOF</span><br><span class="line">server &#123;</span><br><span class="line">    listen 80;</span><br><span class="line">    server_name _;</span><br><span class="line">    $([ -n <span class="string">"<span class="variable">$tb_ssl_cert</span>"</span> ] &amp;&amp; <span class="built_in">echo</span> <span class="string">"return 301 https://\$host:9443\$request_uri;"</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">$([ -n <span class="string">"<span class="variable">$tb_ssl_cert</span>"</span> ] &amp;&amp; <span class="built_in">echo</span> <span class="string">"</span></span><br><span class="line"><span class="string">server &#123;</span></span><br><span class="line"><span class="string">    listen 9443 ssl;</span></span><br><span class="line"><span class="string">    server_name _;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    ssl_certificate <span class="variable">$tb_ssl_cert</span>;</span></span><br><span class="line"><span class="string">    ssl_certificate_key <span class="variable">$tb_ssl_key</span>;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    client_max_body_size 1024M;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    location / &#123;</span></span><br><span class="line"><span class="string">        proxy_pass http://localhost:<span class="variable">$thingsboard_port</span>;</span></span><br><span class="line"><span class="string">        proxy_http_version 1.1;</span></span><br><span class="line"><span class="string">        proxy_set_header Host \$host;</span></span><br><span class="line"><span class="string">        proxy_set_header X-Real-IP \$remote_addr;</span></span><br><span class="line"><span class="string">        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;</span></span><br><span class="line"><span class="string">        proxy_set_header X-Forwarded-Proto \$scheme;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">        # WebSocket 关键配置</span></span><br><span class="line"><span class="string">        proxy_set_header Upgrade \$http_upgrade;</span></span><br><span class="line"><span class="string">        proxy_set_header Connection "</span>upgrade<span class="string">";</span></span><br><span class="line"><span class="string">        proxy_read_timeout 86400;</span></span><br><span class="line"><span class="string">    &#125;</span></span><br><span class="line"><span class="string">&#125;</span></span><br><span class="line"><span class="string">"</span>)</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 创建xxx配置</span></span><br><span class="line">    sudo tee <span class="string">"<span class="variable">$xxx_conf</span>"</span> &gt; /dev/null &lt;&lt;EOF</span><br><span class="line">$([ -n <span class="string">"<span class="variable">$xxx_ssl_cert</span>"</span> ] &amp;&amp; <span class="built_in">echo</span> <span class="string">"</span></span><br><span class="line"><span class="string">server &#123;</span></span><br><span class="line"><span class="string">    listen 80;</span></span><br><span class="line"><span class="string">    server_name _;</span></span><br><span class="line"><span class="string">    return 301 https://\$host\$request_uri;</span></span><br><span class="line"><span class="string">&#125;</span></span><br><span class="line"><span class="string">"</span>)</span><br><span class="line"></span><br><span class="line">server &#123;</span><br><span class="line">    listen $([ -n <span class="string">"<span class="variable">$xxx_ssl_cert</span>"</span> ] &amp;&amp; <span class="built_in">echo</span> <span class="string">"443 ssl"</span> || <span class="built_in">echo</span> <span class="string">"80"</span>);</span><br><span class="line">    server_name _;</span><br><span class="line"></span><br><span class="line">    $([ -n <span class="string">"<span class="variable">$xxx_ssl_cert</span>"</span> ] &amp;&amp; <span class="built_in">echo</span> <span class="string">"</span></span><br><span class="line"><span class="string">    ssl_certificate <span class="variable">$xxx_ssl_cert</span>;</span></span><br><span class="line"><span class="string">    ssl_certificate_key <span class="variable">$xxx_ssl_key</span>;"</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 精确匹配 /block</span></span><br><span class="line">    location = /block &#123;</span><br><span class="line">        rewrite ^/block/(.*)$ /\<span class="variable">$1</span> <span class="built_in">break</span>;</span><br><span class="line">        root <span class="variable">$basedir</span>/iot_blockly_frontend;</span><br><span class="line">        index index.html;</span><br><span class="line">        try_files \<span class="variable">$uri</span> \<span class="variable">$uri</span>/ /index.html;</span><br><span class="line">        proxy_set_header Host \<span class="variable">$host</span>;</span><br><span class="line">        proxy_set_header X-Real-IP \<span class="variable">$remote_addr</span>;</span><br><span class="line">        proxy_set_header X-Forwarded-For \<span class="variable">$proxy_add_x_forwarded_for</span>;</span><br><span class="line">        proxy_set_header X-Forwarded-Proto \<span class="variable">$scheme</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 匹配 /block/ 下的所有路径</span></span><br><span class="line">    location /block/ &#123;</span><br><span class="line">        rewrite ^/block/(.*)$ /\<span class="variable">$1</span> <span class="built_in">break</span>;</span><br><span class="line">        root <span class="variable">$basedir</span>/iot_blockly_frontend;</span><br><span class="line">        index index.html;</span><br><span class="line">        try_files \<span class="variable">$uri</span> \<span class="variable">$uri</span>/ /index.html;</span><br><span class="line">        proxy_set_header Host \<span class="variable">$host</span>;</span><br><span class="line">        proxy_set_header X-Real-IP \<span class="variable">$remote_addr</span>;</span><br><span class="line">        proxy_set_header X-Forwarded-For \<span class="variable">$proxy_add_x_forwarded_for</span>;</span><br><span class="line">        proxy_set_header X-Forwarded-Proto \<span class="variable">$scheme</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    location / &#123;</span><br><span class="line">        root <span class="variable">$basedir</span>/iot_xxx_frontend;</span><br><span class="line">        index index.html;</span><br><span class="line">        try_files \<span class="variable">$uri</span> \<span class="variable">$uri</span>/ /index.html;</span><br><span class="line">        proxy_set_header Host \<span class="variable">$host</span>;</span><br><span class="line">        proxy_set_header X-Real-IP \<span class="variable">$remote_addr</span>;</span><br><span class="line">        proxy_set_header X-Forwarded-For \<span class="variable">$proxy_add_x_forwarded_for</span>;</span><br><span class="line">        proxy_set_header X-Forwarded-Proto \<span class="variable">$scheme</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 新增的特定路由，需要放在通用/gateway路由之前</span></span><br><span class="line">    location /gateway/<span class="built_in">exec</span> &#123;</span><br><span class="line">        rewrite ^/gateway/<span class="built_in">exec</span>/(.*)$ /<span class="built_in">exec</span>/\<span class="variable">$1</span> <span class="built_in">break</span>;</span><br><span class="line">        proxy_pass http://localhost:<span class="variable">$xxx_backend_port</span>/;</span><br><span class="line">        proxy_set_header Host \<span class="variable">$host</span>;</span><br><span class="line">        proxy_set_header X-Real-IP \<span class="variable">$remote_addr</span>;</span><br><span class="line">        proxy_set_header X-Forwarded-For \<span class="variable">$proxy_add_x_forwarded_for</span>;</span><br><span class="line">        proxy_set_header X-Forwarded-Proto \<span class="variable">$scheme</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    location /gateway &#123;</span><br><span class="line">        rewrite ^/gateway/(.*)$ /\<span class="variable">$1</span> <span class="built_in">break</span>;</span><br><span class="line">        proxy_pass http://localhost:<span class="variable">$iot_gateway_port</span>;</span><br><span class="line">        proxy_set_header Host \<span class="variable">$host</span>;</span><br><span class="line">        proxy_set_header X-Real-IP \<span class="variable">$remote_addr</span>;</span><br><span class="line">        proxy_set_header X-Forwarded-For \<span class="variable">$proxy_add_x_forwarded_for</span>;</span><br><span class="line">        proxy_set_header X-Forwarded-Proto \<span class="variable">$scheme</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    location /prod-api &#123;</span><br><span class="line">        rewrite ^/prod-api/(.*)$ /\<span class="variable">$1</span> <span class="built_in">break</span>;</span><br><span class="line">        proxy_pass http://localhost:<span class="variable">$xxx_backend_port</span>;</span><br><span class="line">        proxy_set_header Host \<span class="variable">$host</span>;</span><br><span class="line">        proxy_set_header X-Real-IP \<span class="variable">$remote_addr</span>;</span><br><span class="line">        proxy_set_header X-Forwarded-For \<span class="variable">$proxy_add_x_forwarded_for</span>;</span><br><span class="line">        proxy_set_header X-Forwarded-Proto \<span class="variable">$scheme</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 测试并重启Nginx</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"测试Nginx配置..."</span></span><br><span class="line">    sudo nginx -t &amp;&amp; sudo systemctl restart nginx</span><br><span class="line">    <span class="keyword">if</span> [ $? -eq 0 ]; <span class="keyword">then</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"Nginx 配置应用成功"</span></span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"错误：Nginx 配置测试失败，请检查日志"</span></span><br><span class="line">        <span class="built_in">exit</span> 1</span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 主函数</span></span><br><span class="line"><span class="function"><span class="title">main</span></span>() &#123;</span><br><span class="line">    detect_os</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"检测到操作系统: <span class="variable">$OS_NAME</span> <span class="variable">$OS_VERSION</span>"</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 安装基础</span></span><br><span class="line">    install_base</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 安装 Java</span></span><br><span class="line">    <span class="keyword">if</span> [ <span class="string">"<span class="variable">$(get_config 'common_install_java')</span>"</span> = <span class="string">"yes"</span> ]; <span class="keyword">then</span></span><br><span class="line">        <span class="comment"># 先检查 java 命令是否存在</span></span><br><span class="line">        <span class="keyword">if</span> ! <span class="built_in">command</span> -v java &amp;&gt; /dev/null; <span class="keyword">then</span></span><br><span class="line">            <span class="built_in">echo</span> <span class="string">"Java 未安装，开始安装..."</span></span><br><span class="line">            install_java</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            <span class="comment"># 如果 java 命令存在，再检查版本</span></span><br><span class="line">            <span class="keyword">if</span> ! java -version 2&gt;&amp;1 | grep -q <span class="string">"17"</span>; <span class="keyword">then</span></span><br><span class="line">                <span class="built_in">echo</span> <span class="string">"已安装的 Java 版本不是 17，开始安装 JDK 17..."</span></span><br><span class="line">                install_java</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                <span class="built_in">echo</span> <span class="string">"JDK 17 已安装"</span></span><br><span class="line">            <span class="keyword">fi</span></span><br><span class="line">        <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 验证安装</span></span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">command</span> -v java &amp;&gt; /dev/null; <span class="keyword">then</span></span><br><span class="line">            <span class="built_in">echo</span> <span class="string">"Java 安装验证:"</span></span><br><span class="line">            java -version</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            <span class="built_in">echo</span> <span class="string">"错误：Java 安装后仍然不可用"</span></span><br><span class="line">            <span class="built_in">exit</span> 1</span><br><span class="line">        <span class="keyword">fi</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 处理 MySQL</span></span><br><span class="line">    <span class="keyword">if</span> [ <span class="string">"<span class="variable">$(get_config 'mysql_install')</span>"</span> = <span class="string">"yes"</span> ]; <span class="keyword">then</span></span><br><span class="line">        host=$(get_config <span class="string">'mysql_host'</span>)</span><br><span class="line">        port=$(get_config <span class="string">'mysql_port'</span>)</span><br><span class="line">        user=$(get_config <span class="string">'mysql_user'</span>)</span><br><span class="line">        password=$(get_config <span class="string">'mysql_password'</span>)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> check_db_connection <span class="string">"mysql"</span> <span class="variable">$host</span> <span class="variable">$port</span> <span class="variable">$user</span> <span class="variable">$password</span>; <span class="keyword">then</span></span><br><span class="line">            <span class="built_in">echo</span> <span class="string">"MySQL 连接成功，跳过安装"</span></span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            <span class="built_in">echo</span> <span class="string">"MySQL 连接失败，开始安装..."</span></span><br><span class="line">            install_mysql</span><br><span class="line">            <span class="built_in">echo</span> <span class="string">"MySQL 已自动配置用户：<span class="variable">$DB_USER</span>"</span></span><br><span class="line">        <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 新增：导入 xxx.sql 到 MySQL</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"处理 xxx 数据库导入..."</span></span><br><span class="line">        import_xxx_sql <span class="variable">$host</span> <span class="variable">$port</span> <span class="variable">$user</span> <span class="variable">$password</span> <span class="string">"<span class="variable">$SQL_FILE</span>"</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 新增：更新 sys_menu 表</span></span><br><span class="line">        thingsboard_url=$(get_config <span class="string">'frontend_thingsboard_url'</span>)</span><br><span class="line">        update_sys_menu <span class="variable">$host</span> <span class="variable">$port</span> <span class="variable">$user</span> <span class="variable">$password</span> <span class="string">"<span class="variable">$thingsboard_url</span>"</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 安装 Nginx</span></span><br><span class="line">    <span class="keyword">if</span> [ <span class="string">"<span class="variable">$(get_config 'common_install_nginx')</span>"</span> = <span class="string">"yes"</span> ]; <span class="keyword">then</span></span><br><span class="line">        <span class="keyword">if</span> ! <span class="built_in">command</span> -v nginx &amp;&gt; /dev/null; <span class="keyword">then</span></span><br><span class="line">            install_nginx</span><br><span class="line">            nginx -v</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            <span class="built_in">echo</span> <span class="string">"Nginx 已安装"</span></span><br><span class="line">        <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 配置Nginx</span></span><br><span class="line">        configure_nginx</span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 处理 PostgreSQL - 特别处理CentOS 7的版本问题</span></span><br><span class="line">    <span class="keyword">if</span> [ <span class="string">"<span class="variable">$(get_config 'postgresql_install')</span>"</span> = <span class="string">"yes"</span> ]; <span class="keyword">then</span></span><br><span class="line">        host=$(get_config <span class="string">'postgresql_host'</span>)</span><br><span class="line">        port=$(get_config <span class="string">'postgresql_port'</span>)</span><br><span class="line">        user=$(get_config <span class="string">'postgresql_user'</span>)</span><br><span class="line">        password=$(get_config <span class="string">'postgresql_password'</span>)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> check_db_connection <span class="string">"postgresql"</span> <span class="variable">$host</span> <span class="variable">$port</span> <span class="variable">$user</span> <span class="variable">$password</span>; <span class="keyword">then</span></span><br><span class="line">            <span class="built_in">echo</span> <span class="string">"PostgreSQL 连接成功"</span></span><br><span class="line"></span><br><span class="line">            <span class="comment"># 确保thingsboard数据库存在</span></span><br><span class="line">            ensure_postgresql_db <span class="variable">$host</span> <span class="variable">$port</span> <span class="variable">$user</span> <span class="variable">$password</span> <span class="string">"thingsboard"</span></span><br><span class="line"></span><br><span class="line">            <span class="comment"># 使用配置的主机和端口</span></span><br><span class="line">            PG_HOST=<span class="variable">$host</span></span><br><span class="line">            PG_PORT=<span class="variable">$port</span></span><br><span class="line"></span><br><span class="line">            <span class="comment"># 检查PostgreSQL版本</span></span><br><span class="line">            PG_VERSION=$(PGPASSWORD=<span class="string">"<span class="variable">$password</span>"</span> psql -h <span class="variable">$host</span> -p <span class="variable">$port</span> -U <span class="variable">$user</span> -d postgres -tAc <span class="string">"SHOW server_version_num"</span>)</span><br><span class="line">            <span class="keyword">if</span> [ <span class="string">"<span class="variable">$PG_VERSION</span>"</span> -lt 110000 ]; <span class="keyword">then</span></span><br><span class="line">                <span class="built_in">echo</span> <span class="string">"警告：当前PostgreSQL版本(<span class="variable">$PG_VERSION</span>)低于11.0，建议升级到11+版本"</span></span><br><span class="line">            <span class="keyword">fi</span></span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            <span class="built_in">echo</span> <span class="string">"PostgreSQL 连接失败，开始安装..."</span></span><br><span class="line">            install_postgresql</span><br><span class="line">            <span class="built_in">echo</span> <span class="string">"PostgreSQL 已自动配置用户：<span class="variable">$PG_USER</span>"</span></span><br><span class="line">            <span class="built_in">echo</span> <span class="string">"已创建数据库: thingsboard"</span></span><br><span class="line"></span><br><span class="line">            <span class="comment"># 使用本地安装的主机和端口</span></span><br><span class="line">            PG_HOST=<span class="string">"localhost"</span></span><br><span class="line">            PG_PORT=<span class="string">"5432"</span></span><br><span class="line"></span><br><span class="line">            <span class="comment"># 验证PostgreSQL版本</span></span><br><span class="line">            sudo -u postgres psql -c <span class="string">"SHOW server_version"</span></span><br><span class="line">        <span class="keyword">fi</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 新增：安装Redis</span></span><br><span class="line">    <span class="keyword">if</span> [ <span class="string">"<span class="variable">$(get_config 'redis_install')</span>"</span> = <span class="string">"yes"</span> ]; <span class="keyword">then</span></span><br><span class="line">        <span class="comment"># 检查Redis是否已安装</span></span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">command</span> -v redis-server &amp;&gt; /dev/null; <span class="keyword">then</span></span><br><span class="line">            <span class="built_in">echo</span> <span class="string">"Redis 已安装"</span></span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            <span class="built_in">echo</span> <span class="string">"开始安装Redis..."</span></span><br><span class="line">            install_redis</span><br><span class="line">            <span class="built_in">echo</span> <span class="string">"Redis 已安装并启动"</span></span><br><span class="line">        <span class="keyword">fi</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 安装 thingsboard</span></span><br><span class="line">    <span class="keyword">if</span> [ <span class="string">"<span class="variable">$(get_config 'common_install_thingsboard')</span>"</span> = <span class="string">"yes"</span> ]; <span class="keyword">then</span></span><br><span class="line">        install_thingsboard</span><br><span class="line">        <span class="built_in">echo</span> <span class="string">"ThingsBoard 已安装"</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 配置数据库连接</span></span><br><span class="line">        configure_thingsboard</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 配置内存设置</span></span><br><span class="line">        configure_thingsboard_memory</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 等待数据库可用</span></span><br><span class="line">        wait_for_db <span class="string">"postgresql"</span> <span class="variable">$PG_HOST</span> <span class="variable">$PG_PORT</span> <span class="variable">$PG_USER</span> <span class="variable">$PG_PASSWORD</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 执行安装脚本并启动服务</span></span><br><span class="line">        install_and_start_thingsboard</span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 显示部署信息</span></span><br><span class="line">    <span class="built_in">echo</span> -e <span class="string">"\n\033[32m部署完成!\033[0m"</span></span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 执行主函数</span></span><br><span class="line">main</span><br></pre></td></tr></table></figure><h2 id="Docker-Compose"><a href="#Docker-Compose" class="headerlink" title="Docker Compose"></a>Docker Compose</h2><p>The code needs to be refactored to enable dynamic configuration by reading environment variables.</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br></pre></td><td class="code"><pre><span class="line">services:</span><br><span class="line">  mysql:</span><br><span class="line">    image: $&#123;MYSQL_IMAGE:-mysql:<span class="number">8.0</span>&#125;</span><br><span class="line">    container_name: xxx_mysql</span><br><span class="line">    ports:</span><br><span class="line">      - <span class="string">"$&#123;MYSQL_PORT:-3306&#125;:3306"</span></span><br><span class="line">    environment:</span><br><span class="line">      MYSQL_ROOT_PASSWORD: $&#123;MYSQL_ROOT_PASSWORD:-MyApp@<span class="number">1234</span>&#125;</span><br><span class="line">      MYSQL_USER: $&#123;MYSQL_USER:-myapp&#125;</span><br><span class="line">      MYSQL_PASSWORD: $&#123;MYSQL_PASSWORD:-MyApp@<span class="number">1234</span>&#125;</span><br><span class="line">      MYSQL_DATABASE: xxx</span><br><span class="line">    volumes:</span><br><span class="line">      - ./mysql/data:/var/lib/mysql</span><br><span class="line">      - ./mysql/initdb.d:/docker-<span class="keyword">entrypoint</span><span class="bash">-initdb.d</span></span><br><span class="line">    restart: always</span><br><span class="line">    <span class="keyword">healthcheck</span><span class="bash">:</span></span><br><span class="line">      test: [<span class="string">"CMD"</span>, <span class="string">"mysqladmin"</span>, <span class="string">"ping"</span>, <span class="string">"-h"</span>, <span class="string">"localhost"</span>, <span class="string">"-u$&#123;MYSQL_USER&#125;"</span>, <span class="string">"-p$&#123;MYSQL_PASSWORD&#125;"</span>]</span><br><span class="line">      interval: <span class="number">30</span>s</span><br><span class="line">      timeout: <span class="number">30</span>s</span><br><span class="line">      retries: <span class="number">10</span></span><br><span class="line">      start_period: <span class="number">180</span>s</span><br><span class="line"></span><br><span class="line">  postgres:</span><br><span class="line">    image: $&#123;POSTGRES_IMAGE:-postgres:<span class="number">16</span>&#125;</span><br><span class="line">    container_name: xxx_postgres</span><br><span class="line">    ports:</span><br><span class="line">      - <span class="string">"$&#123;POSTGRES_PORT:-5432&#125;:5432"</span></span><br><span class="line">    environment:</span><br><span class="line">      POSTGRES_USER: $&#123;POSTGRES_USER:-myapp&#125;</span><br><span class="line">      POSTGRES_PASSWORD: $&#123;POSTGRES_PASSWORD:-MyApp@<span class="number">5678</span>&#125;</span><br><span class="line">      POSTGRES_DB: thingsboard</span><br><span class="line">    volumes:</span><br><span class="line">      - ./postgres/data:/var/lib/postgresql/data</span><br><span class="line">    restart: always</span><br><span class="line">    <span class="keyword">healthcheck</span><span class="bash">:</span></span><br><span class="line">      test: [<span class="string">"CMD-SHELL"</span>, <span class="string">"pg_isready -U $&#123;POSTGRES_USER&#125; -d thingsboard"</span>]</span><br><span class="line">      interval: <span class="number">10</span>s</span><br><span class="line">      timeout: <span class="number">5</span>s</span><br><span class="line">      retries: <span class="number">10</span></span><br><span class="line">      start_period: <span class="number">30</span>s</span><br><span class="line"></span><br><span class="line">  redis:</span><br><span class="line">    image: $&#123;REDIS_IMAGE:-redis:<span class="number">7</span>&#125;</span><br><span class="line">    container_name: xxx_redis</span><br><span class="line">    ports:</span><br><span class="line">      - <span class="string">"$&#123;REDIS_PORT:-6379&#125;:6379"</span></span><br><span class="line">    environment:</span><br><span class="line">      REDIS_PASSWORD: $&#123;REDIS_PASSWORD:-&#125;</span><br><span class="line">    command: &gt;</span><br><span class="line">      bash -c <span class="string">"if [ -z \"$$REDIS_PASSWORD\" ]; then </span></span><br><span class="line"><span class="string">        redis-server; </span></span><br><span class="line"><span class="string">      else </span></span><br><span class="line"><span class="string">        redis-server --requirepass $$REDIS_PASSWORD; </span></span><br><span class="line"><span class="string">      fi"</span></span><br><span class="line">    volumes:</span><br><span class="line">      - ./redis/data:/data</span><br><span class="line">    restart: always</span><br><span class="line">    <span class="keyword">healthcheck</span><span class="bash">:</span></span><br><span class="line">      test: &gt;</span><br><span class="line">        bash -c <span class="string">"if [ -z \"$$REDIS_PASSWORD\" ]; then </span></span><br><span class="line"><span class="string">          redis-cli ping; </span></span><br><span class="line"><span class="string">        else </span></span><br><span class="line"><span class="string">          redis-cli -a $$REDIS_PASSWORD ping; </span></span><br><span class="line"><span class="string">        fi"</span></span><br><span class="line">      interval: <span class="number">10</span>s</span><br><span class="line">      timeout: <span class="number">5</span>s</span><br><span class="line">      retries: <span class="number">10</span></span><br><span class="line">      start_period: <span class="number">30</span>s</span><br><span class="line"></span><br><span class="line">  thingsboard:</span><br><span class="line">    image: $&#123;THINGSBOARD_IMAGE:-thingsboard/tb-node:<span class="number">4.0</span>.<span class="number">1.1</span>&#125;</span><br><span class="line">    container_name: xxx_thingsboard</span><br><span class="line">    ports:</span><br><span class="line">      - <span class="string">"$&#123;THINGSBOARD_PORT&#125;:8080"</span></span><br><span class="line">      - <span class="string">"7070:7070"</span></span><br><span class="line">      - <span class="string">"1883:1883"</span></span><br><span class="line">      - <span class="string">"8883:8883"</span></span><br><span class="line">      - <span class="string">"5683-5688:5683-5688/udp"</span></span><br><span class="line">    environment:</span><br><span class="line">      DATABASE_TS_TYPE: sql</span><br><span class="line">      SPRING_DATASOURCE_URL: jdbc:postgresql://$&#123;TB_POSTGRESQL_HOST&#125;:$&#123;TB_POSTGRESQL_PORT&#125;/thingsboard</span><br><span class="line">      SPRING_DATASOURCE_USERNAME: $&#123;POSTGRES_USER&#125;</span><br><span class="line">      SPRING_DATASOURCE_PASSWORD: $&#123;POSTGRES_PASSWORD&#125;</span><br><span class="line">      HTTP_BIND_PORT: <span class="number">8080</span></span><br><span class="line"><span class="comment">#      JAVA_OPTS: "-Xmx1024m -Xms1024m"</span></span><br><span class="line">    depends_on:</span><br><span class="line">      postgres:</span><br><span class="line">        condition: service_healthy</span><br><span class="line">    restart: unless-stopped</span><br><span class="line">    <span class="keyword">healthcheck</span><span class="bash">:</span></span><br><span class="line">      test: [<span class="string">"CMD-SHELL"</span>, <span class="string">"grep ':075B' /proc/net/tcp6 || exit 1"</span>]</span><br><span class="line">      interval: <span class="number">5</span>s       </span><br><span class="line">      timeout: <span class="number">3</span>s        </span><br><span class="line">      start_period: <span class="number">300</span>s  </span><br><span class="line">      retries: <span class="number">120</span></span><br><span class="line"></span><br><span class="line">  iot-gateway:</span><br><span class="line">    image: $&#123;IOT_GATEWAY_IMAGE&#125;</span><br><span class="line">    container_name: xxx_iot_gateway</span><br><span class="line">    ports:</span><br><span class="line">      - <span class="string">"$&#123;IOT_GATEWAY_PORT&#125;:8081"</span></span><br><span class="line">    environment:</span><br><span class="line">      IOT_GATEWAY_PORT: <span class="number">8081</span></span><br><span class="line">      xxx_URL: $&#123;xxx_URL&#125;</span><br><span class="line">      THINGSBOARD_URL: $&#123;THINGSBOARD_URL&#125;</span><br><span class="line">      TB_POSTGRESQL_URL: $&#123;TB_POSTGRESQL_HOST&#125;</span><br><span class="line">      TB_POSTGRESQL_PORT: $&#123;TB_POSTGRESQL_PORT&#125;</span><br><span class="line">      TB_POSTGRESQL_USERNAME: $&#123;POSTGRES_USER&#125;</span><br><span class="line">      TB_POSTGRESQL_PASSWORD: $&#123;POSTGRES_PASSWORD&#125;</span><br><span class="line">    depends_on:</span><br><span class="line">      thingsboard:</span><br><span class="line">        condition: service_healthy</span><br><span class="line">    restart: always</span><br><span class="line">    <span class="keyword">healthcheck</span><span class="bash">:</span></span><br><span class="line">      test: [<span class="string">"CMD-SHELL"</span>, <span class="string">"curl -f http://localhost:8081/healthcheck/check || exit 1"</span>]</span><br><span class="line">      interval: <span class="number">10</span>s</span><br><span class="line">      timeout: <span class="number">60</span>s</span><br><span class="line">      start_period: <span class="number">600</span>s</span><br><span class="line">      retries: <span class="number">99999</span></span><br><span class="line"></span><br><span class="line">  xxx-admin:</span><br><span class="line">    image: $&#123;xxx_ADMIN_IMAGE&#125;</span><br><span class="line">    container_name: xxx_admin</span><br><span class="line">    ports:</span><br><span class="line">      - <span class="string">"$&#123;xxx_BACKEND_PORT&#125;:8001"</span></span><br><span class="line">    environment:</span><br><span class="line">      xxx_BACKEND_PORT: <span class="number">8001</span></span><br><span class="line">      IOT_GATEWAY_URL: $&#123;IOT_GATEWAY_URL&#125;</span><br><span class="line">      xxx_MYSQL_HOST: $&#123;MYSQL_HOST&#125;</span><br><span class="line">      xxx_MYSQL_PORT: $&#123;MYSQL_PORT&#125;</span><br><span class="line">      xxx_MYSQL_USERNAME: $&#123;MYSQL_USER&#125;</span><br><span class="line">      xxx_MYSQL_PASSWORD: $&#123;MYSQL_PASSWORD&#125;</span><br><span class="line">      xxx_REDIS_HOST: $&#123;REDIS_HOST&#125;</span><br><span class="line">      xxx_REDIS_PORT: $&#123;REDIS_PORT&#125;</span><br><span class="line">      xxx_REDIS_DATABASE: $&#123;REDIS_DATABASE&#125;</span><br><span class="line">      xxx_REDIS_PASSWORD: $&#123;REDIS_PASSWORD&#125;</span><br><span class="line">    depends_on:</span><br><span class="line">      mysql:</span><br><span class="line">        condition: service_healthy</span><br><span class="line">      redis:</span><br><span class="line">        condition: service_healthy</span><br><span class="line">    restart: unless-stopped</span><br><span class="line">    <span class="keyword">healthcheck</span><span class="bash">:</span></span><br><span class="line">      test: [<span class="string">"CMD-SHELL"</span>, <span class="string">"curl -f http://localhost:8001/healthcheck/check || exit 1"</span>]</span><br><span class="line">      interval: <span class="number">10</span>s</span><br><span class="line">      timeout: <span class="number">60</span>s</span><br><span class="line">      start_period: <span class="number">600</span>s</span><br><span class="line">      retries: <span class="number">99999</span></span><br><span class="line"></span><br><span class="line">  nginx:</span><br><span class="line">    image: $&#123;NGINX_IMAGE&#125;</span><br><span class="line">    container_name: xxx_nginx</span><br><span class="line">    ports:</span><br><span class="line">      - <span class="string">"80:80"</span></span><br><span class="line">      - <span class="string">"443:443"</span></span><br><span class="line">    volumes:</span><br><span class="line">      - ./nginx/conf.d:/etc/nginx/conf.d</span><br><span class="line">      - ./nginx/webroot:/home/webroot</span><br><span class="line">    depends_on:</span><br><span class="line">      thingsboard:</span><br><span class="line">        condition: service_healthy</span><br><span class="line">      iot-gateway:</span><br><span class="line">        condition: service_healthy</span><br><span class="line">      xxx-admin:</span><br><span class="line">        condition: service_healthy</span><br><span class="line">    restart: unless-stopped</span><br><span class="line">    <span class="keyword">healthcheck</span><span class="bash">:</span></span><br><span class="line">      test: [<span class="string">"CMD-SHELL"</span>, <span class="string">"curl -f http://localhost || exit 1"</span>]</span><br><span class="line">      interval: <span class="number">30</span>s</span><br><span class="line">      timeout: <span class="number">10</span>s</span><br><span class="line">      retries: <span class="number">10</span></span><br><span class="line">      start_period: <span class="number">60</span>s</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;After several months of development, the project is about to enter the delivery phase.&lt;/p&gt;
&lt;p&gt;However, the client restricts deployment to</summary>
      
    
    
    
    <category term="Continuous Integration" scheme="https://project256.com/en/categories/continuous-integration/"/>
    
    
    <category term="Continuous Integration" scheme="https://project256.com/en/tags/continuous-integration/"/>
    
  </entry>
  
  <entry>
    <title>Campus Job Search Guide for Senior Students</title>
    <link href="https://project256.com/en/campus-life/%E9%AB%98%E5%B9%B4%E7%BA%A7%E6%A0%A1%E6%8B%9B%E6%B1%82%E8%81%8C%E6%8C%87%E5%8D%97/"/>
    <id>https://project256.com/en/campus-life/%E9%AB%98%E5%B9%B4%E7%BA%A7%E6%A0%A1%E6%8B%9B%E6%B1%82%E8%81%8C%E6%8C%87%E5%8D%97/</id>
    <published>2025-05-23T06:52:39.000Z</published>
    <updated>2026-08-04T09:39:04.564Z</updated>
    
    <content type="html"><![CDATA[<h3 id="Campus-Recruitment-Job-Search-Guide-for-Senior-Students"><a href="#Campus-Recruitment-Job-Search-Guide-for-Senior-Students" class="headerlink" title="Campus Recruitment Job Search Guide for Senior Students"></a><strong>Campus Recruitment Job Search Guide for Senior Students</strong></h3><p>*If you are applying for non-technical positions, please ignore some of the “overly technical” points below. This article was summarized and generated with the assistance of DeepSeek based on a shared presentation document.</p><hr><h4 id="1-Core-Campus-Recruitment-Strategies"><a href="#1-Core-Campus-Recruitment-Strategies" class="headerlink" title="1. Core Campus Recruitment Strategies"></a><strong>1. Core Campus Recruitment Strategies</strong></h4><ol><li><p><strong>Increase Your Personal “Valuation”</strong>  </p><ul><li><strong>Skills Are King</strong>: Focus on skills in high market demand (such as Java, Python), thoroughly master <em>Jianzhi Offer</em> (<em>Coding Interviews</em>), and complete <a href="https://leetcode.cn/problem-list/sDvggqTO/" target="_blank" rel="noopener">LeetCode (at least the Top 75 Frequent Questions)</a>.  </li><li><strong>Project Experience</strong>: No hands-on projects? Learn and build on the spot using university course projects or Bilibili tutorials, upload your code to GitHub, and fill up those green contribution dots!  </li><li><strong>Resume Polishing</strong>: Write project experiences using the STAR method, quantify your achievements (e.g., “Optimized algorithms to improve response speed by 30%”), and eliminate generic “self-evaluations.”</li></ul></li><li><p><strong>Choose the Right Track and Company</strong>  </p><ul><li><strong>Prioritize Top Tech Giants</strong>: Under the Matthew effect, the brand recognition of big tech companies will boost your future job hops.  </li><li><strong>Business Diversity</strong>: Choose companies with multiple business lines (such as BAT), which offer higher fault tolerance and make internal transfers easier.</li></ul></li></ol><hr><h4 id="2-Internships-Taking-a-Detour-to-Land-a-Big-Tech-Job"><a href="#2-Internships-Taking-a-Detour-to-Land-a-Big-Tech-Job" class="headerlink" title="2. Internships: Taking a Detour to Land a Big Tech Job"></a><strong>2. Internships: Taking a Detour to Land a Big Tech Job</strong></h4><ul><li><strong>Why Internships Matter</strong>:  <ul><li>Return offer conversion rates for interns are higher, saving you from the fierce competition of standard campus hiring.  </li><li>Big tech internship experience is crucial for “gilding” your resume.</li></ul></li><li><strong>How to Secure an Internship</strong>:  <ul><li>Keep a close eye on official big tech career sites and referral channels from alumni&#x2F;senior students, applying 3 to 6 months in advance.  </li><li>Take initiative during your internship to fight for a return offer opportunity.</li></ul></li></ul><hr><h4 id="3-Pitfall-Avoidance-Guide"><a href="#3-Pitfall-Avoidance-Guide" class="headerlink" title="3. Pitfall Avoidance Guide"></a><strong>3. Pitfall Avoidance Guide</strong></h4><ol><li><p><strong>Be Cautious About Postgraduate Entrance Exams</strong>:  </p><ul><li>Pursuing a master’s degree &#x3D; a 3-year high-risk future. Unless you truly love academia, prioritize employment to accumulate work experience.  </li><li>Re-taking the exam for a second year risks losing your fresh graduate status, and experienced hiring (social recruitment) has a much higher bar.</li></ul></li><li><p><strong>Refuse to Blindly Follow Trends</strong>:  </p><ul><li>Do not blindly chase popular positions; choose an industry by combining your own skills and interests.  </li><li>Do not borrow money or leverage yourself up to prepare for postgraduate exams; always leave yourself a fallback option.</li></ul></li></ol><hr><h4 id="4-Resume-and-Interview"><a href="#4-Resume-and-Interview" class="headerlink" title="4. Resume and Interview"></a><strong>4. Resume and Interview</strong></h4><ul><li><strong>Resume Winning Tactics</strong>:  <ul><li>Technical roles: PDF + Markdown, concise and clean, with contact information placed at the top.  </li><li>Tech blog &#x2F; GitHub links add bonus points; feature open-source projects first.</li></ul></li><li><strong>Interview Preparation</strong>:  <ul><li>Thoroughly read <em>The Programmer Interview Bible</em>, and memorize technical interview fundamentals (“Ba Gu Wen”) backward and forward.  </li><li>Practice mock interviews and use the “STAR method” to answer project-related questions.</li></ul></li></ul><hr><h4 id="5-Compound-Interest-Mindset-Long-Term-Growth"><a href="#5-Compound-Interest-Mindset-Long-Term-Growth" class="headerlink" title="5. Compound Interest Mindset: Long-Term Growth"></a><strong>5. Compound Interest Mindset: Long-Term Growth</strong></h4><ul><li><strong>Starting Point Determines Your Path</strong>: Try to choose a top company for your first job, where salary and promotions grow with compound interest.  </li><li><strong>Continuous Accumulation</strong>: Write technical blogs, contribute to open-source projects, and build your personal brand.  </li><li><strong>Valleys Are Opportunities</strong>: Hone your inner skills during industry downturns so you can seize the initiative when the market rebounds.</li></ul><hr><h4 id="6-Mindset-Adjustment"><a href="#6-Mindset-Adjustment" class="headerlink" title="6. Mindset Adjustment"></a><strong>6. Mindset Adjustment</strong></h4><ul><li><strong>Don’t Be Scared!</strong>: Mass submitting resumes costs nothing; if you fail an interview, nobody remembers, and the next one will be even better!  </li><li><strong>Action Items</strong>:  <ul><li>Solve 3 coding problems daily, submit 10 resumes weekly, and review your skill tree monthly.</li></ul></li></ul><hr><p><strong>Remember: Job hunting is a marathon; improving yourself is what truly counts!</strong><br><strong>Just apply and get it done!</strong> 💪</p><h2 id="Experience-Campus-Recruitment-in-Advance"><a href="#Experience-Campus-Recruitment-in-Advance" class="headerlink" title="Experience Campus Recruitment in Advance"></a>Experience Campus Recruitment in Advance</h2><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/wechat_miniprogram.png" alt="Welcome to try the Campus Recruitment Simulator Mini Program" title="">                </div>                <div class="image-caption">Welcome to try the Campus Recruitment Simulator Mini Program</div>            </figure><p>Provides the following features for students in IT&#x2F;Information-related majors:</p><ul><li>Career direction selection driven by interest + skills + market demand</li><li>Overviews, skill requirements, salary levels, and campus recruitment preparation advice for 20+ technical and non-technical tracks</li><li>Resume builder that guides you through filling out essential fields to boost resume quality, exportable in Markdown format</li><li>Mock interviews covering all tracks and all difficulty levels</li><li>Resume logs and a mistake logbook for mock interviews</li></ul><p>With campus recruitment as your ultimate goal, prepare early and get a head start.</p><h2 id="Original-Presentation-Documents"><a href="#Original-Presentation-Documents" class="headerlink" title="Original Presentation Documents"></a>Original Presentation Documents</h2><p>This year marks my tenth year sharing at the university, teaching campus recruitment and job search strategies to juniors&#x2F;graduating students with different themes and methods every year.</p><p>While I can’t claim any massive achievements, the fact that the university has invited me to speak for ten consecutive years without getting tired of it, and that I can occasionally lend a helping hand to kids who want to work in the IT industry, is already a great achievement in itself.</p><p>I feel quite humbled; I’ve only done a tiny bit of trivial work for the students.</p><p><a href="https://project256.oss-cn-beijing.aliyuncs.com/uPic/2024%E6%A0%A1%E6%8B%9B%E7%BB%8F%E6%B5%8E%E5%AD%A6%E5%8E%9F%E7%90%86_plus.pdf" target="_blank" rel="noopener">Principles of Campus Recruitment Economics.pdf (2024 Academic and Career Development Education Lecture)</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h3 id=&quot;Campus-Recruitment-Job-Search-Guide-for-Senior-Students&quot;&gt;&lt;a href=&quot;#Campus-Recruitment-Job-Search-Guide-for-Senior-Students&quot; class=&quot;h</summary>
      
    
    
    
    <category term="Campus Life" scheme="https://project256.com/en/categories/campus-life/"/>
    
    
  </entry>
  
  <entry>
    <title>What Is Truly Yours — Focus on Transferable and Compounding Work</title>
    <link href="https://project256.com/en/career/%E4%BB%80%E4%B9%88%E6%89%8D%E6%98%AF%E8%87%AA%E5%B7%B1%E7%9A%84%E2%80%94%E2%80%94%E5%A4%9A%E5%81%9A%E5%8F%AF%E8%BF%81%E7%A7%BB%E4%B8%94%E8%83%BD%E4%BA%A7%E7%94%9F%E5%A4%8D%E5%88%A9%E7%9A%84%E4%BA%8B%E6%83%85/"/>
    <id>https://project256.com/en/career/%E4%BB%80%E4%B9%88%E6%89%8D%E6%98%AF%E8%87%AA%E5%B7%B1%E7%9A%84%E2%80%94%E2%80%94%E5%A4%9A%E5%81%9A%E5%8F%AF%E8%BF%81%E7%A7%BB%E4%B8%94%E8%83%BD%E4%BA%A7%E7%94%9F%E5%A4%8D%E5%88%A9%E7%9A%84%E4%BA%8B%E6%83%85/</id>
    <published>2025-05-09T23:16:21.000Z</published>
    <updated>2026-08-04T09:39:04.562Z</updated>
    
    <content type="html"><![CDATA[<h2 id="【Focus-More-on-Things-That-Are-Transferable-and-Yield-Compounding-Value】"><a href="#【Focus-More-on-Things-That-Are-Transferable-and-Yield-Compounding-Value】" class="headerlink" title="【Focus More on Things That Are Transferable and Yield Compounding Value】"></a>【Focus More on Things That Are Transferable and Yield Compounding Value】</h2><p>Recently, I submitted my application for the Hong Kong Quality Migrant Admission Scheme for the third time. While filling out the forms, I kept reflecting on what materials would be more beneficial for my permanent residency application.</p><p>To my embarrassment, aside from my academic records and degree certificates, my only patent certificate was applied for in 2017 and granted in 2020.</p><p>So what have I been doing all these years? Has my accumulated experience actually been recognized by the outside world?</p><p>If you want to make yourself recognized by the market, go apply for permanent residency, go start a small company, or go interview boldly.</p><h2 id="Demystifying-“Empty-Promises”"><a href="#Demystifying-“Empty-Promises”" class="headerlink" title="Demystifying “Empty Promises”"></a>Demystifying “Empty Promises”</h2><p>To retain or motivate staff, companies love to select “outstanding employees” internally and hand out titles and souvenirs, only for year-end pay not to increase by a single cent.</p><p>Setting aside the money issue for a moment, as far as personal future development is concerned, obsessing over internal honors or titles brings all harm and zero benefit.</p><p>Of course, if you are absolutely set on getting promoted and working at this company until you die, these might be of some use—in which case, just pretend I didn’t say anything.</p><p>When someone tries to sell you empty promises, the best response is to let real data—like your actual working hours and salary—along with your personal skill growth speak for themselves.</p><h2 id="What-Is-Truly-Yours"><a href="#What-Is-Truly-Yours" class="headerlink" title="What Is Truly Yours?"></a>What Is Truly Yours?</h2><p>For better pay or career development, the day will come when you have to step out, whether by switching jobs or starting a business.</p><p>But when switching jobs or starting a business, can you really claim that you worked overtime hard or won XXXX internal awards?</p><p>Don’t wait until you decide to switch jobs or start a business to realize that, besides the physical and mental trauma caused by working overtime, your pockets are empty.</p><p>As a service provider, I need to provide sufficient proof to demonstrate that my capabilities or qualifications meet the client’s needs.</p><p>Either it’s flexing muscles in an interview, or presenting authoritative third-party certified <strong>certificates, transcripts, papers, patents, journal publications, printed works, competition medals, and academic degrees&#x2F;diplomas</strong>.</p><p>While the former seems easier to achieve, it falls right back into the cycle of trading time and health for one-off income.</p><p>Going a step further, I hope to possess the ability to generate a steady income even while sleeping.</p><h2 id="Treat-Yourself-as-a-Spy-Lurking-in-the-Company-Take-Away-as-Much-Knowledge-and-Experience-as-You-Can"><a href="#Treat-Yourself-as-a-Spy-Lurking-in-the-Company-Take-Away-as-Much-Knowledge-and-Experience-as-You-Can" class="headerlink" title="Treat Yourself as a Spy Lurking in the Company: Take Away as Much Knowledge and Experience as You Can"></a>Treat Yourself as a Spy Lurking in the Company: Take Away as Much Knowledge and Experience as You Can</h2><p>Recently, I saw a saying: “Treat yourself as a spy lurking in the company—take away as much knowledge and experience as you can.”</p><p>I find this statement vivid and incisive. While being “exploited,” you must also find ways to “exploit the company.”</p><p>Even if the company cannot guarantee an 8-hour workday or ensure physical and mental health, you can still make yourself more valuable by focusing on doing transferable things.</p><p>Observe how top performers operate, build mutually beneficial relationships, and accumulate high-quality documentation and solutions.</p><p>After all, this world measures our value at every moment. The way out is either to grind to extreme limits or to discover a path that automatically compounds over time.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;【Focus-More-on-Things-That-Are-Transferable-and-Yield-Compounding-Value】&quot;&gt;&lt;a href=&quot;#【Focus-More-on-Things-That-Are-Transferable-and-</summary>
      
    
    
    
    <category term="Career" scheme="https://project256.com/en/categories/career/"/>
    
    
    <category term="Career" scheme="https://project256.com/en/tags/career/"/>
    
  </entry>
  
  <entry>
    <title>Troubleshooting an Infinite Timeout Blocking Execution Issue in a Child Thread</title>
    <link href="https://project256.com/en/java/%E8%AE%B0%E4%B8%80%E6%AC%A1%E5%AD%90%E7%BA%BF%E7%A8%8B%E5%86%85%E6%97%A0%E9%99%90%E8%B6%85%E6%97%B6%E9%98%BB%E5%A1%9E%E6%89%A7%E8%A1%8C%E7%9A%84%E9%97%AE%E9%A2%98%E6%8E%92%E6%9F%A5/"/>
    <id>https://project256.com/en/java/%E8%AE%B0%E4%B8%80%E6%AC%A1%E5%AD%90%E7%BA%BF%E7%A8%8B%E5%86%85%E6%97%A0%E9%99%90%E8%B6%85%E6%97%B6%E9%98%BB%E5%A1%9E%E6%89%A7%E8%A1%8C%E7%9A%84%E9%97%AE%E9%A2%98%E6%8E%92%E6%9F%A5/</id>
    <published>2025-01-17T08:56:12.000Z</published>
    <updated>2026-08-04T09:39:04.563Z</updated>
    
    <content type="html"><![CDATA[<p>One day, I discovered that all threads in the scheduled task thread pool were completely blocked:</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/rOU1il.jpg" alt="Infinite failure blocking" title="">                </div>                <div class="image-caption">Infinite failure blocking</div>            </figure><p>The code is as follows:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 由于任务规模、执行频次较低，直接无界队列。如果高频任务建议new ThreadPoolExecutor</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> ExecutorService royalFlushExecutor = Executors.newFixedThreadPool(RoyalFlush.THREAD_NUM, <span class="keyword">new</span> NamedThreadFactory(<span class="string">"royalFlushExecutor"</span>, <span class="keyword">false</span>));</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> List&lt;StockRoyalFlushDto&gt; <span class="title">fetchRoyalFlushData</span><span class="params">(List&lt;StockBaseInfoEntity&gt; stockBaseInfoEntities)</span> </span>&#123;</span><br><span class="line">    String currentTime = DateUtil.currentTimeStr();</span><br><span class="line"></span><br><span class="line">    List&lt;StockRoyalFlushDto&gt; results = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    List&lt;Future&lt;StockRoyalFlushDto&gt;&gt; futures = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    System.setProperty(<span class="string">"https.protocols"</span>, <span class="string">"TLSv1,TLSv1.1,TLSv1.2"</span>);</span><br><span class="line"></span><br><span class="line">    AtomicInteger current = <span class="keyword">new</span> AtomicInteger(<span class="number">0</span>);</span><br><span class="line">    <span class="keyword">for</span> (StockBaseInfoEntity stockBaseInfoEntity : stockBaseInfoEntities) &#123;</span><br><span class="line">        String url = String.format(String.format(RoyalFlush.AI_URL, stockBaseInfoEntity.getCode() + RoyalFlush.QUERY));</span><br><span class="line">        futures.add(royalFlushExecutor.submit(() -&gt; &#123;</span><br><span class="line">            log.info(<span class="string">"royalFlushData-当前进度:&#123;&#125;/&#123;&#125;-启动时间:&#123;&#125;"</span>, current.getAndIncrement(), stockBaseInfoEntities.size(), currentTime);</span><br><span class="line">            <span class="keyword">return</span> fetchAndParseData(stockBaseInfoEntity, url);</span><br><span class="line">        &#125;));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (Future&lt;StockRoyalFlushDto&gt; future : futures) &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            StockRoyalFlushDto stockRoyalFlushDto = future.get(<span class="number">1</span>, java.util.concurrent.TimeUnit.SECONDS);</span><br><span class="line">            <span class="keyword">if</span> (stockRoyalFlushDto != <span class="keyword">null</span>) &#123;</span><br><span class="line">                results.add(stockRoyalFlushDto);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            log.error(<span class="string">"royalFlushData-任务执行失败:&#123;&#125; e:&#123;&#125;"</span>, e.getMessage(), e.toString());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> results;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>When trying to reproduce the issue, I found that Thread Pool 1 disappeared after a while:</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/bJR2HW.jpg" alt="Thread Pool 1 is gone" title="">                </div>                <div class="image-caption">Thread Pool 1 is gone</div>            </figure><h1 id="Troubleshooting-and-Localization"><a href="#Troubleshooting-and-Localization" class="headerlink" title="Troubleshooting and Localization"></a>Troubleshooting and Localization</h1><p>Using <code>ps aux | grep java</code> can locate the process ID. Of course, right after <code>INFO</code> in the log is the process ID, which was 22385.</p><p>Use the <code>top -H -p</code> command to find the process:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">top -H -p 22385</span><br></pre></td></tr></table></figure><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/GpMJwl.jpg" alt="Stuck thread" title="">                </div>                <div class="image-caption">Stuck thread</div>            </figure><p>Use <code>jstack</code> to view thread states and stack trace information:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jstack 22385 | grep -C 50 22426</span><br></pre></td></tr></table></figure><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/UPmTQW.jpg" alt="jstack results" title="">                </div>                <div class="image-caption">jstack results</div>            </figure><p>We can see that the thread is in the <code>RUNNABLE</code> state. Subconsciously, one might think a <code>RUNNABLE</code> thread is not executing, but <code>RUNNABLE</code> is actually divided into two states: Running and Ready.</p><p>In other words, a thread in the <code>RUNNABLE</code> state might be actively running, or it might not be executing and is waiting for CPU resource allocation.</p><p>Therefore, for a thread in the running state, when it runs halfway through a task and the CPU executing the thread is scheduled to do other things, the thread temporarily stops running. However, its state remains unchanged as <code>RUNNABLE</code> because it could be scheduled back at any time to continue executing the task.</p><h1 id="Source-Code-Investigation-and-Fix"><a href="#Source-Code-Investigation-and-Fix" class="headerlink" title="Source Code Investigation and Fix"></a>Source Code Investigation and Fix</h1><p>After locating the logic entry point, inspecting the underlying code of the dependent method revealed that <code>timeout</code> was -1, meaning no timeout:</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/yMGbTG.jpg" alt="Default timeout is -1, meaning no limit" title="">                </div>                <div class="image-caption">Default timeout is -1, meaning no limit</div>            </figure><p>Add a timeout setting above line 90 of the original code:</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/4jdIMN.jpg" alt="Timeout setting" title="">                </div>                <div class="image-caption">Timeout setting</div>            </figure><p>The issue was successfully resolved. In fact, the official documentation also prompts that modifications must be made to prevent bringing down the system.</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/2QMrYe.jpg" alt="Hutool official documentation prompt" title="">                </div>                <div class="image-caption">Hutool official documentation prompt</div>            </figure><h1 id="Summary"><a href="#Summary" class="headerlink" title="Summary"></a>Summary</h1><ul><li>Always set timeouts for remote calls</li><li>When threads in a thread pool are blocked, even if a timeout is configured when retrieving results, it only allows the retrieval logic to proceed—the executing thread remains blocked there</li><li>In the latest versions of Java, <code>jstack</code> allows querying without converting thread IDs to octal</li><li>It is crucial to log thread information—do not casually use <code>sout</code> to print information</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;One day, I discovered that all threads in the scheduled task thread pool were completely blocked:&lt;/p&gt;
&lt;figure class=&quot;image-bubble&quot;&gt;
     </summary>
      
    
    
    
    <category term="Java" scheme="https://project256.com/en/categories/java/"/>
    
    
    <category term="Java" scheme="https://project256.com/en/tags/java/"/>
    
  </entry>
  
  <entry>
    <title>OOM Troubleshooting and Resolution</title>
    <link href="https://project256.com/en/java/OOM%E6%8E%92%E6%9F%A5%E5%8F%8A%E8%A7%A3%E5%86%B3/"/>
    <id>https://project256.com/en/java/OOM%E6%8E%92%E6%9F%A5%E5%8F%8A%E8%A7%A3%E5%86%B3/</id>
    <published>2025-01-10T07:54:15.000Z</published>
    <updated>2026-08-04T09:39:04.562Z</updated>
    
    <content type="html"><![CDATA[<p>One day, a scheduled task running on a 1C1G small instance failed to execute. Checking the logs revealed an OOM error.</p><p>I previously wrote about a quick combination of Java dump tricks, but that focused more on issues like deadlocks. This article focuses on memory OOM issues.</p><p>The log is as follows:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">2025-01-10 11:55:01.155  INFO 7036 --- [ol-224-thread-1] c.p.bs.service.impl.ScheduleServiceImpl  : fetchAndSaveStockFundFlow schedule job start</span><br><span class="line">Exception in thread &quot;pool-224-thread-1&quot; java.lang.OutOfMemoryError: Java heap space</span><br></pre></td></tr></table></figure><p>The code at the time was written in a brute-force manner: the thread pool was unnamed, all relevant data was retrieved all at once, remote services were called to fetch data, and then everything was dumped into the database in one go.</p><p>Therefore, you could guess with your eyes closed that a small 1G instance is allocated only around 300MB of heap memory by default. Coupled with 5 threads configured to operate simultaneously for each task, OOM was only a matter of time even without a memory leak.</p><h1 id="DUMP"><a href="#DUMP" class="headerlink" title="DUMP"></a>DUMP</h1><p>The first step is to dump the memory snapshot, which is generally done using jmap or jhsdb.</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jmap -dump:live,format&#x3D;b,file&#x3D;heapDump.hprof [PID]</span><br></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jhsdb jmap --binaryheap --pid [PID]</span><br></pre></td></tr></table></figure><p>Although other bloggers recommended using jhsdb, running it resulted in an OOM as well—a true nesting doll scenario.</p><p>After executing jmap, the heapDump.hprof file was generated in the execution directory.</p><p>Next, you need to find a way to export the file from the server. There are many methods, and scp works fine.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">scp username@servername:/remote_path/filename ~/local_destination</span><br></pre></td></tr></table></figure><p>You can also set up a file server using Nginx, which makes it convenient to download other data at any time. (Note data security issues: do not store sensitive data in the directory, as memory dumps can also expose sensitive data.)</p><h1 id="Setting-Up-an-Nginx-File-Server"><a href="#Setting-Up-an-Nginx-File-Server" class="headerlink" title="Setting Up an Nginx File Server"></a>Setting Up an Nginx File Server</h1><p>First, install Nginx</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum install nginx</span><br></pre></td></tr></table></figure><p>Create the public directory used by the file server</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mkdir -p /var/www/</span><br></pre></td></tr></table></figure><p><strong>Note: Please do not create the server directory under the &#x2F;root folder, as it will cause a 403 Permission Denied error.</strong></p><p>Example error:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">2025&#x2F;01&#x2F;10 15:25:15 [error] 30497#30497: *18 &quot;&#x2F;root&#x2F;fileroot&#x2F;index.html&quot; is forbidden (13: Permission denied), client: IP, server: , request: &quot;GET &#x2F; HTTP&#x2F;1.1&quot;, host: &quot;IP&quot;</span><br></pre></td></tr></table></figure><p>Modify permissions and owner. Since the default user is root, sudo is omitted here.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 修改权限和所有者</span></span><br><span class="line">chown -R nginx:nginx /var/www/</span><br><span class="line">chmod -R 755 /var/www/</span><br></pre></td></tr></table></figure><p>Create Nginx configuration: &#x2F;etc&#x2F;nginx&#x2F;conf.d&#x2F;file.conf</p><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">server</span> &#123;</span><br><span class="line">  <span class="attribute">listen</span> <span class="number">80</span>;</span><br><span class="line">  <span class="attribute">location</span> / &#123;</span><br><span class="line">    <span class="attribute">root</span> /var/www/;</span><br><span class="line">    <span class="attribute">autoindex</span> <span class="literal">on</span>;</span><br><span class="line">    <span class="attribute">autoindex_exact_size</span> <span class="literal">off</span>;</span><br><span class="line">    <span class="attribute">autoindex_localtime</span> <span class="literal">on</span>;</span><br><span class="line">    <span class="comment"># 添加跨域设置</span></span><br><span class="line">    <span class="attribute">add_header</span> <span class="string">'Access-Control-Allow-Origin'</span> <span class="string">'*'</span>;</span><br><span class="line">    <span class="attribute">add_header</span> <span class="string">'Access-Control-Allow-Methods'</span> <span class="string">'GET, POST, OPTIONS'</span>;</span><br><span class="line">    <span class="attribute">add_header</span> <span class="string">'Access-Control-Allow-Headers'</span> <span class="string">'Authorization'</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 开启访问日志记录</span></span><br><span class="line">    <span class="attribute">access_log</span> /var/log/nginx/access.log;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Start the service</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl start nginx</span><br></pre></td></tr></table></figure><p>Move the dump result from the first step to the &#x2F;var&#x2F;www directory. Open your browser and enter the IP address to view the contents of the folder.</p><p>If it is a one-time server, simply use systemctl stop nginx to stop Nginx after downloading.</p><h1 id="Analysis"><a href="#Analysis" class="headerlink" title="Analysis"></a>Analysis</h1><p>There are many analysis tools, but IntelliJ IDEA can directly open and analyze hprof files.</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/KmqaUx.jpg" alt="Analyzing hprof" title="">                </div>                <div class="image-caption">Analyzing hprof</div>            </figure><p>You can see which DTOs have a high count. Obviously, the problem stems from repeatedly “retrieving all relevant data at once”, which generated too many StockBaseInfoEntity instances, occupying 159.8MB. As mentioned above, Java’s default maximum heap memory is 25% of the machine’s memory, making it very easy to hit an OOM.</p><h1 id="Code-Optimization"><a href="#Code-Optimization" class="headerlink" title="Code Optimization"></a>Code Optimization</h1><h2 id="Staticizing-Common-Data"><a href="#Staticizing-Common-Data" class="headerlink" title="Staticizing Common Data"></a>Staticizing Common Data</h2><p>Since multiple tasks require the same common data, and the original code repeatedly fetched the common data in full, the memory continuously bloated.</p><p>Therefore, staticizing common data allows multiple read operations to point to the same memory location.</p><p>However, keep in mind: 1. Data needs to be initialized during project cold startup, and 2. Thread safety concerns.</p><h2 id="Batch-Partitioning"><a href="#Batch-Partitioning" class="headerlink" title="Batch Partitioning"></a>Batch Partitioning</h2><p>Change querying all, processing all, and writing all to operating in batches of 1,000 or 100 records at a time.</p><p>You can partition directly using Guava. To avoid adding new dependencies here, native for loops are used:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; stockBaseInfoEntities.size(); i += Base.BATCH_SIZE) &#123;</span><br><span class="line">    Date lowestDate = fetchService.fetchAndSaveBsPointInstruct(stockBaseInfoEntities.subList(i, Math.min(i + Base.BATCH_SIZE, stockBaseInfoEntities.size())));</span><br><span class="line">    <span class="keyword">if</span> (lowestDate == <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">continue</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    fetchService.calcLastSignalDaysAndCloseDiff();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="Naming-the-Thread-Pool"><a href="#Naming-the-Thread-Pool" class="headerlink" title="Naming the Thread Pool"></a>Naming the Thread Pool</h2><p>Here, Hutool’s NamedThreadFactory class was used directly.</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> ExecutorService instructExecutorService = Executors.newFixedThreadPool(BsInstruct.THREAD_NUM, <span class="keyword">new</span> NamedThreadFactory(<span class="string">"InstructExecutor"</span>, <span class="keyword">false</span>));</span><br></pre></td></tr></table></figure><h2 id="Modifying-JAR-Runtime-Parameters"><a href="#Modifying-JAR-Runtime-Parameters" class="headerlink" title="Modifying JAR Runtime Parameters"></a>Modifying JAR Runtime Parameters</h2><p>Adjusting the heap size:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">java -Xms128m -Xmx750m -jar yourapp.jar</span><br></pre></td></tr></table></figure><p>Adding GC-related parameters:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">java -Xms128m -Xmx750m -XX:+PrintGCDetails -jar yourapp.jar</span><br></pre></td></tr></table></figure><h1 id="After-Optimization"><a href="#After-Optimization" class="headerlink" title="After Optimization"></a>After Optimization</h1><p>338MB → 59.6MB</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/xxvmtD.jpg" alt="After optimization" title="">                </div>                <div class="image-caption">After optimization</div>            </figure><h1 id="Appendix-Java-Extra-Options"><a href="#Appendix-Java-Extra-Options" class="headerlink" title="Appendix: Java Extra Options"></a>Appendix: Java Extra Options</h1><p>Running java -X allows you to view all extra options and their explanations.</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br></pre></td><td class="code"><pre><span class="line">java -X</span><br><span class="line"></span><br><span class="line">    -Xbatch           禁用后台编译</span><br><span class="line">    -Xbootclasspath&#x2F;a:&lt;以 : 分隔的目录和 zip&#x2F;jar 文件&gt;</span><br><span class="line">                      附加在引导类路径末尾</span><br><span class="line">    -Xcheck:jni       对 JNI 函数执行其他检查</span><br><span class="line">    -Xcomp            强制在首次调用时编译方法</span><br><span class="line">    -Xdebug           不执行任何操作；已过时，将在未来发行版中删除。</span><br><span class="line">    -Xdiag            显示附加诊断消息</span><br><span class="line">    -Xfuture          启用最严格的检查，预期将来的默认值。</span><br><span class="line">                      此选项已过时，可能会在</span><br><span class="line">                      未来发行版中删除。</span><br><span class="line">    -Xint             仅解释模式执行</span><br><span class="line">    -Xinternalversion</span><br><span class="line">                      显示比 -version 选项更详细的</span><br><span class="line">                      JVM 版本信息</span><br><span class="line">    -Xlog:&lt;opts&gt;      配置或启用采用 Java 虚拟</span><br><span class="line">                      机 (Java Virtual Machine, JVM) 统一记录框架进行事件记录。使用 -Xlog:help</span><br><span class="line">                      可了解详细信息。</span><br><span class="line">    -Xloggc:&lt;file&gt;    将 GC 状态记录在文件中（带时间戳）。</span><br><span class="line">                      此选项已过时，可能会在</span><br><span class="line">                      将来的发行版中删除。它将替换为 -Xlog:gc:&lt;file&gt;。</span><br><span class="line">    -Xmixed           混合模式执行（默认值）</span><br><span class="line">    -Xmn&lt;size&gt;        为年轻代（新生代）设置初始和最大堆大小</span><br><span class="line">                      （以字节为单位）</span><br><span class="line">    -Xms&lt;size&gt;        设置初始 Java 堆大小</span><br><span class="line">    -Xmx&lt;size&gt;        设置最大 Java 堆大小</span><br><span class="line">    -Xnoclassgc       禁用类垃圾收集</span><br><span class="line">    -Xrs              减少 Java&#x2F;VM 对操作系统信号的使用（请参见文档）</span><br><span class="line">    -Xshare:auto      在可能的情况下使用共享类数据（默认值）</span><br><span class="line">    -Xshare:off       不尝试使用共享类数据</span><br><span class="line">    -Xshare:on        要求使用共享类数据，否则将失败。</span><br><span class="line">                      这是一个测试选项，可能导致间歇性</span><br><span class="line">                      故障。不应在生产环境中使用它。</span><br><span class="line">    -XshowSettings    显示所有设置并继续</span><br><span class="line">    -XshowSettings:all</span><br><span class="line">                      显示所有设置并继续</span><br><span class="line">    -XshowSettings:locale</span><br><span class="line">                      显示所有与区域设置相关的设置并继续</span><br><span class="line">    -XshowSettings:properties</span><br><span class="line">                      显示所有属性设置并继续</span><br><span class="line">    -XshowSettings:vm</span><br><span class="line">                      显示所有与 vm 相关的设置并继续</span><br><span class="line">    -XshowSettings:security</span><br><span class="line">                      显示所有安全设置并继续</span><br><span class="line">    -XshowSettings:security:all</span><br><span class="line">                      显示所有安全设置并继续</span><br><span class="line">    -XshowSettings:security:properties</span><br><span class="line">                      显示安全属性并继续</span><br><span class="line">    -XshowSettings:security:providers</span><br><span class="line">                      显示静态安全提供方设置并继续</span><br><span class="line">    -XshowSettings:security:tls</span><br><span class="line">                      显示与 TLS 相关的安全设置并继续</span><br><span class="line">    -XshowSettings:system</span><br><span class="line">                      （仅 Linux）显示主机系统或容器</span><br><span class="line">                      配置并继续</span><br><span class="line">    -Xss&lt;size&gt;        设置 Java 线程堆栈大小</span><br><span class="line">                      实际大小可以舍入到</span><br><span class="line">                      操作系统要求的系统页面大小的倍数。</span><br><span class="line">    -Xverify          设置字节码验证器的模式</span><br><span class="line">                      请注意，选项 -Xverify:none 已过时，</span><br><span class="line">                      可能会在未来发行版中删除。</span><br><span class="line">    --add-reads &lt;module&gt;&#x3D;&lt;target-module&gt;(,&lt;target-module&gt;)*</span><br><span class="line">                      更新 &lt;module&gt; 以读取 &lt;target-module&gt;，而无论</span><br><span class="line">                      模块如何声明。</span><br><span class="line">                      &lt;target-module&gt; 可以是 ALL-UNNAMED，将读取所有未命名</span><br><span class="line">                      模块。</span><br><span class="line">    --add-exports &lt;module&gt;&#x2F;&lt;package&gt;&#x3D;&lt;target-module&gt;(,&lt;target-module&gt;)*</span><br><span class="line">                      更新 &lt;module&gt; 以将 &lt;package&gt; 导出到 &lt;target-module&gt;，</span><br><span class="line">                      而无论模块如何声明。</span><br><span class="line">                      &lt;target-module&gt; 可以是 ALL-UNNAMED，将导出到所有</span><br><span class="line">                      未命名模块。</span><br><span class="line">    --add-opens &lt;module&gt;&#x2F;&lt;package&gt;&#x3D;&lt;target-module&gt;(,&lt;target-module&gt;)*</span><br><span class="line">                      更新 &lt;module&gt; 以在 &lt;target-module&gt; 中打开</span><br><span class="line">                      &lt;package&gt;，而无论模块如何声明。</span><br><span class="line">    --limit-modules &lt;module name&gt;[,&lt;module name&gt;...]</span><br><span class="line">                      限制可观察模块的领域</span><br><span class="line">    --patch-module &lt;module&gt;&#x3D;&lt;file&gt;(:&lt;file&gt;)*</span><br><span class="line">                      使用 JAR 文件或目录中的类和资源</span><br><span class="line">                      覆盖或增强模块。</span><br><span class="line">    --source &lt;version&gt;</span><br><span class="line">                      设置源文件模式中源的版本。</span><br><span class="line">    --finalization&#x3D;&lt;value&gt;</span><br><span class="line">                      控制 JVM 是否执行对象最终处理，</span><br><span class="line">                      其中 &lt;value&gt; 为 &quot;enabled&quot; 或 &quot;disabled&quot; 之一。</span><br><span class="line">                      默认情况下，最终处理处于启用状态。</span><br></pre></td></tr></table></figure><p>GC log options, adapted from <a href="https://www.cnblogs.com/dupengpeng/p/17620200.html" target="_blank" rel="noopener">https://www.cnblogs.com/dupengpeng/p/17620200.html</a> :</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">-XX:+PrintGC &lt;&#x3D;&#x3D;&gt; -verbose:gc  打印简要日志信息</span><br><span class="line">-XX:+PrintGCDetails            打印详细日志信息</span><br><span class="line">-XX:+PrintGCTimeStamps  打印程序启动到GC发生的时间，搭配-XX:+PrintGCDetails使用</span><br><span class="line">-XX:+PrintGCDateStamps  打印GC发生时的时间戳，搭配-XX:+PrintGCDetails使用</span><br><span class="line">-XX:+PrintHeapAtGC  打印GC前后的堆信息，如下图</span><br><span class="line">-Xloggc:&lt;file&gt; 输出GC导指定路径下的文件中</span><br><span class="line">-XX:+TraceClassLoading  监控类的加载</span><br><span class="line">-XX:+PrintGCApplicationStoppedTime  打印GC时线程的停顿时间</span><br><span class="line">-XX:+PrintGCApplicationConcurrentTime  打印垃圾收集之前应用未中断的执行时间</span><br><span class="line">-XX:+PrintReferenceGC 打印回收了多少种不同引用类型的引用</span><br><span class="line">-XX:+PrintTenuringDistribution  打印JVM在每次MinorGC后当前使用的Survivor中对象的年龄分布</span><br><span class="line">-XX:+UseGCLogFileRotation 启用GC日志文件的自动转储</span><br><span class="line">-XX:NumberOfGCLogFiles&#x3D;1  设置GC日志文件的循环数目</span><br><span class="line">-XX:GCLogFileSize&#x3D;1M  设置GC日志文件的大小</span><br></pre></td></tr></table></figure><h1 id="Appendix-2-Viewing-Heap-Information-with-jhsdb-jmap"><a href="#Appendix-2-Viewing-Heap-Information-with-jhsdb-jmap" class="headerlink" title="Appendix 2: Viewing Heap Information with jhsdb jmap"></a>Appendix 2: Viewing Heap Information with jhsdb jmap</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jhsdb jmap --pid [PID] --heap</span><br></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line">Attaching to process ID 31724, please wait...</span><br><span class="line">Debugger attached successfully.</span><br><span class="line">Server compiler detected.</span><br><span class="line">JVM version is 21.0.5+9-LTS-239</span><br><span class="line"></span><br><span class="line">using thread-local object allocation.</span><br><span class="line">Mark Sweep Compact GC</span><br><span class="line"></span><br><span class="line">Heap Configuration:</span><br><span class="line">   MinHeapFreeRatio         &#x3D; 40</span><br><span class="line">   MaxHeapFreeRatio         &#x3D; 70</span><br><span class="line">   MaxHeapSize              &#x3D; 786432000 (750.0MB)</span><br><span class="line">   NewSize                  &#x3D; 44695552 (42.625MB)</span><br><span class="line">   MaxNewSize               &#x3D; 262144000 (250.0MB)</span><br><span class="line">   OldSize                  &#x3D; 89522176 (85.375MB)</span><br><span class="line">   NewRatio                 &#x3D; 2</span><br><span class="line">   SurvivorRatio            &#x3D; 8</span><br><span class="line">   MetaspaceSize            &#x3D; 22020096 (21.0MB)</span><br><span class="line">   CompressedClassSpaceSize &#x3D; 1073741824 (1024.0MB)</span><br><span class="line">   MaxMetaspaceSize         &#x3D; 17592186044415 MB</span><br><span class="line"></span><br><span class="line">Heap Usage:</span><br><span class="line">New Generation (Eden + 1 Survivor Space):</span><br><span class="line">   capacity &#x3D; 63569920 (60.625MB)</span><br><span class="line">   used     &#x3D; 972208 (0.9271697998046875MB)</span><br><span class="line">   free     &#x3D; 62597712 (59.69783020019531MB)</span><br><span class="line">   1.5293522471005154% used</span><br><span class="line">Eden Space:</span><br><span class="line">   capacity &#x3D; 56557568 (53.9375MB)</span><br><span class="line">   used     &#x3D; 972208 (0.9271697998046875MB)</span><br><span class="line">   free     &#x3D; 55585360 (53.01033020019531MB)</span><br><span class="line">   1.7189706601245656% used</span><br><span class="line">From Space:</span><br><span class="line">   capacity &#x3D; 7012352 (6.6875MB)</span><br><span class="line">   used     &#x3D; 0 (0.0MB)</span><br><span class="line">   free     &#x3D; 7012352 (6.6875MB)</span><br><span class="line">   0.0% used</span><br><span class="line">To Space:</span><br><span class="line">   capacity &#x3D; 7012352 (6.6875MB)</span><br><span class="line">   used     &#x3D; 0 (0.0MB)</span><br><span class="line">   free     &#x3D; 7012352 (6.6875MB)</span><br><span class="line">   0.0% used</span><br><span class="line">tenured generation:</span><br><span class="line">   capacity &#x3D; 140910592 (134.3828125MB)</span><br><span class="line">   used     &#x3D; 38475656 (36.69324493408203MB)</span><br><span class="line">   free     &#x3D; 102434936 (97.68956756591797MB)</span><br><span class="line">   27.305013380399394% used</span><br></pre></td></tr></table></figure><h1 id="Appendix-3-Viewing-Class-Memory-Usage-with-jhsdb-jmap-histo"><a href="#Appendix-3-Viewing-Class-Memory-Usage-with-jhsdb-jmap-histo" class="headerlink" title="Appendix 3: Viewing Class Memory Usage with jhsdb jmap histo"></a>Appendix 3: Viewing Class Memory Usage with jhsdb jmap histo</h1><p>Sorted in descending order of memory usage. Note that it will print a large amount of information, so piping to more is recommended.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jhsdb jmap --pid [PID] --histo | more</span><br></pre></td></tr></table></figure><p>Among the large pile of information above, only one is familiar. This entity is the staticized object mentioned earlier, confirming that the memory leak issue has been resolved.</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/ejiQpm.jpg" alt="histo" title="">                </div>                <div class="image-caption">histo</div>            </figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;One day, a scheduled task running on a 1C1G small instance failed to execute. Checking the logs revealed an OOM error.&lt;/p&gt;
&lt;p&gt;I previousl</summary>
      
    
    
    
    <category term="Java" scheme="https://project256.com/en/categories/java/"/>
    
    
    <category term="Java" scheme="https://project256.com/en/tags/java/"/>
    
  </entry>
  
  <entry>
    <title>Automated Build and Deployment of Java Projects</title>
    <link href="https://project256.com/en/java/%E8%87%AA%E5%8A%A8%E5%8C%96%E6%9E%84%E5%BB%BA%E5%8F%8A%E9%83%A8%E7%BD%B2Java%E5%B7%A5%E7%A8%8B/"/>
    <id>https://project256.com/en/java/%E8%87%AA%E5%8A%A8%E5%8C%96%E6%9E%84%E5%BB%BA%E5%8F%8A%E9%83%A8%E7%BD%B2Java%E5%B7%A5%E7%A8%8B/</id>
    <published>2025-01-08T11:51:54.000Z</published>
    <updated>2026-08-04T09:39:04.563Z</updated>
    
    <content type="html"><![CDATA[<p>Recently, I’ve been working on a bunch of Java side projects. Every time, I manually did <code>git push</code> then <code>pull</code>, compiled, packaged, and ran them, which felt pretty low-tech.</p><p>Especially after buying a bunch of cloud servers across different regions, as the scale expanded, the efficiency of manual operations and maintenance dropped drastically.</p><p>This blog site actually uses GitHub Actions to automatically trigger compilation and deployment. Once I finish writing an article locally, a simple <code>git push</code> completes the publishing process.</p><p>Therefore, I plan to use CI tools for automated deployment and O&amp;M for Java-related projects as well. Having worked as an SRE for several years, and with Alibaba Cloud Yunxiao being similar to the internal CI tools I use daily at work, I got up to speed very quickly.</p><h1 id="Java-Project-Maven-Configuration"><a href="#Java-Project-Maven-Configuration" class="headerlink" title="Java Project Maven Configuration"></a>Java Project Maven Configuration</h1><p>The Java project structure is:</p><p>gateway</p><p>|-starter</p><p>|-tb</p><p>The main entry class is in <code>starter</code>, while <code>tb</code> mainly contains business logic.</p><p>I tried a few packaging plugins, but found that executing the JAR packaged from <code>starter</code> did not include the <code>tb</code> module.</p><p>Therefore, I used <code>maven-shade-plugin</code> as the packaging plugin.</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">build</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugins</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.maven.plugins<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>maven-compiler-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">configuration</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">annotationProcessorPaths</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">path</span>&gt;</span></span><br><span class="line">                            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.projectlombok<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">                            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>lombok<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;/<span class="name">path</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">annotationProcessorPaths</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">skip</span>&gt;</span>true<span class="tag">&lt;/<span class="name">skip</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.maven.plugins<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>maven-shade-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">executions</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">execution</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">id</span>&gt;</span>shade-jar-with-dependencies<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">phase</span>&gt;</span>package<span class="tag">&lt;/<span class="name">phase</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">goals</span>&gt;</span></span><br><span class="line">                            <span class="tag">&lt;<span class="name">goal</span>&gt;</span>shade<span class="tag">&lt;/<span class="name">goal</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;/<span class="name">goals</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">configuration</span>&gt;</span></span><br><span class="line">                            <span class="tag">&lt;<span class="name">finalName</span>&gt;</span>starter<span class="tag">&lt;/<span class="name">finalName</span>&gt;</span></span><br><span class="line">                            <span class="tag">&lt;<span class="name">transformers</span>&gt;</span></span><br><span class="line">                                <span class="tag">&lt;<span class="name">transformer</span> <span class="attr">implementation</span>=<span class="string">"org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"</span>&gt;</span></span><br><span class="line">                                    <span class="tag">&lt;<span class="name">manifestEntries</span>&gt;</span></span><br><span class="line">                                        <span class="tag">&lt;<span class="name">Main-Class</span>&gt;</span>com.project256.iot.gateway.Application<span class="tag">&lt;/<span class="name">Main-Class</span>&gt;</span></span><br><span class="line">                                    <span class="tag">&lt;/<span class="name">manifestEntries</span>&gt;</span></span><br><span class="line">                                <span class="tag">&lt;/<span class="name">transformer</span>&gt;</span></span><br><span class="line">                            <span class="tag">&lt;/<span class="name">transformers</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">execution</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">executions</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugins</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">build</span>&gt;</span></span><br></pre></td></tr></table></figure><p><code>finalName</code> is set to <code>starter</code> to specify the file name of the output JAR package, avoiding having to modify the deployment script whenever the package version is upgraded.</p><p><code>Main-Class</code> specifies the application entry class to avoid situations where the startup class cannot be found.</p><h1 id="Writing-the-Deployment-Script"><a href="#Writing-the-Deployment-Script" class="headerlink" title="Writing the Deployment Script"></a>Writing the Deployment Script</h1><p>I had a large language model write it for me—just describe the requirements clearly.</p><p>Although killing the process isn’t very graceful, it works for now.</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">我通过持续集成工具，将starter.jar部署到&#x2F;root&#x2F;app文件夹</span><br><span class="line">但我需要通过deploy.sh停止已经启动的服务，并后台运行java -jar starter.jar</span><br><span class="line">类似nohup，在日志文件中输出日志</span><br><span class="line"></span><br><span class="line">请编写deploy.sh</span><br></pre></td></tr></table></figure><p>Got the script and named it <code>deploy.sh</code>.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置变量</span></span><br><span class="line">APP_NAME=<span class="string">"starter"</span></span><br><span class="line">JAR_PATH=<span class="string">"/root/app/starter.jar"</span></span><br><span class="line">LOG_FILE=<span class="string">"/root/app/starter.log"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 停止已运行的服务</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"Stopping existing <span class="variable">$APP_NAME</span> service..."</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 查找正在运行的 Java 进程并杀死它</span></span><br><span class="line">PID=$(pgrep -f <span class="variable">$APP_NAME</span>)</span><br><span class="line"><span class="keyword">if</span> [ -n <span class="string">"<span class="variable">$PID</span>"</span> ]; <span class="keyword">then</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"Found running <span class="variable">$APP_NAME</span> with PID: <span class="variable">$PID</span>. Stopping it..."</span></span><br><span class="line">    <span class="built_in">kill</span> -9 <span class="variable">$PID</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"<span class="variable">$APP_NAME</span> stopped."</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"<span class="variable">$APP_NAME</span> is not running."</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动新的服务</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"Starting <span class="variable">$APP_NAME</span> service..."</span></span><br><span class="line">nohup java -jar <span class="variable">$JAR_PATH</span> &gt;&gt; <span class="variable">$LOG_FILE</span> 2&gt;&amp;1 &amp;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 输出启动信息</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"<span class="variable">$APP_NAME</span> started. Logs are being written to <span class="variable">$LOG_FILE</span>."</span></span><br></pre></td></tr></table></figure><p>Place it in the <code>resources</code> folder of the <code>starter</code> module. Of course, it can also be placed in other folders to be copied and executed by the CI tool.</p><h1 id="Configuring-Alibaba-Cloud-Yunxiao-Code-Repository"><a href="#Configuring-Alibaba-Cloud-Yunxiao-Code-Repository" class="headerlink" title="Configuring Alibaba Cloud Yunxiao Code Repository"></a>Configuring Alibaba Cloud Yunxiao Code Repository</h1><p>Yunxiao is excessively slow when pulling from GitHub—sometimes taking over 10 minutes without finishing, which blocks downstream processes. Furthermore, pull execution time consumes resource quota (3,000 core-minutes are provided free per month).</p><p>Therefore, I synced a copy of the GitHub code to Code Management and configured <code>.git/config</code> to automatically push to both targets whenever <code>git push</code> is executed.</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/YYlJvV.jpg" alt="Import Code Repository" title="">                </div>                <div class="image-caption">Import Code Repository</div>            </figure><p>For detailed steps, simply refer to the official documentation, and then configure your local RSA public key in the Yunxiao code repository.</p><p>The <code>.git/config</code> configuration is as follows, with two target URLs configured under <code>remote &quot;origin&quot;</code>.</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">[core]</span><br><span class="line">    repositoryformatversion &#x3D; 0</span><br><span class="line">    filemode &#x3D; true</span><br><span class="line">    bare &#x3D; false</span><br><span class="line">    logallrefupdates &#x3D; true</span><br><span class="line">    ignorecase &#x3D; true</span><br><span class="line">    precomposeunicode &#x3D; true</span><br><span class="line">[remote &quot;origin&quot;]</span><br><span class="line">    url &#x3D; git@github.com:SUTFutureCoder&#x2F;XXX.git</span><br><span class="line">    url &#x3D; git@codeup.aliyun.com:xxx&#x2F;SUTFutureCoder&#x2F;XXX.git</span><br><span class="line">    fetch &#x3D; +refs&#x2F;heads&#x2F;*:refs&#x2F;remotes&#x2F;origin&#x2F;*</span><br><span class="line">[branch &quot;main&quot;]</span><br><span class="line">    remote &#x3D; origin</span><br><span class="line">    merge &#x3D; refs&#x2F;heads&#x2F;main</span><br></pre></td></tr></table></figure><h1 id="Configuring-the-Pipeline"><a href="#Configuring-the-Pipeline" class="headerlink" title="Configuring the Pipeline"></a>Configuring the Pipeline</h1><p>You can choose a process template that includes build upload and host deployment, then remove code scanning and unit testing later.</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/kKtdex.jpg" alt="Create New Pipeline" title="">                </div>                <div class="image-caption">Create New Pipeline</div>            </figure><p>Make sure to modify the build &amp; upload configuration to copy the script into the packaging path, as the default configuration will cause an error.</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/DWDnE2.jpg" alt="Build Configuration" title="">                </div>                <div class="image-caption">Build Configuration</div>            </figure><p>The build &amp; upload artifacts from Java can be deployed to single or multiple machines using different strategies.</p><p>Since this is personal development deployed to a single server, there’s no need for any complex SOP—just run it straight through without pausing.</p><p>When collaborating with multiple developers or deploying to a cluster, operational SOPs, canary releases, and rollback strategies must be considered.</p><p>For hosts, simply execute the command following the guide to register them.</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/ZYn40f.jpg" alt="Host Deployment" title="">                </div>                <div class="image-caption">Host Deployment</div>            </figure><h1 id="Logging-into-the-Machine-to-Inspect"><a href="#Logging-into-the-Machine-to-Inspect" class="headerlink" title="Logging into the Machine to Inspect"></a>Logging into the Machine to Inspect</h1><p>After logging into the machine, check the <code>app</code> directory to observe whether the JAR package and script have been deployed correctly.</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/3lyAAa.jpg" alt="Observe Deployment Result" title="">                </div>                <div class="image-caption">Observe Deployment Result</div>            </figure><h1 id="Summary"><a href="#Summary" class="headerlink" title="Summary"></a>Summary</h1><p>Compared to self-hosting Jenkins, using cloud services is more than enough for personal projects.</p><p>Compiling into Docker images is also fine, but for small personal projects, it feels like using a sledgehammer to crack a nut.</p><p>Triggering pipeline runs via Webhook is not recommended unless you can ensure every code push is error-free; this trigger method is essentially no different from blindly going all-in.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Recently, I’ve been working on a bunch of Java side projects. Every time, I manually did &lt;code&gt;git push&lt;/code&gt; then &lt;code&gt;pull&lt;/code&gt;, co</summary>
      
    
    
    
    <category term="Java" scheme="https://project256.com/en/categories/java/"/>
    
    
    <category term="Java" scheme="https://project256.com/en/tags/java/"/>
    
  </entry>
  
  <entry>
    <title>Stock F10 Technical Indicators</title>
    <link href="https://project256.com/en/quantitative-trading/%E8%82%A1%E7%A5%A8F10%E6%8A%80%E6%9C%AF%E6%8C%87%E6%A0%87/"/>
    <id>https://project256.com/en/quantitative-trading/%E8%82%A1%E7%A5%A8F10%E6%8A%80%E6%9C%AF%E6%8C%87%E6%A0%87/</id>
    <published>2025-01-05T08:27:41.000Z</published>
    <updated>2026-08-04T09:39:04.563Z</updated>
    
    <content type="html"><![CDATA[<p>Have any of you ever invested in stocks? How do you pick your stocks?</p><p>Is it by checking Alipay’s “Gold Selection” recommendations? Watching KOL videos with random analysis? Listening to strong recommendations from others? Or just sorting by yield and picking one with your eyes closed?</p><p>To be honest, I’ve done all of these, but they only brought me huge losses in my account, stomach aches in the middle of the night that kept me awake, and a painful daily routine of frantically refreshing apps in hopes that prayers would make the chart go up.</p><p>Looking at it calmly, if Lu Benwei is a “gambling monster”, then these behaviors make one a total “gambling dog” acting like a naive leek.</p><p>The win rate of these behaviors is even lower than the 50% win rate of rolling dice or flipping a coin. Heading stubbornly into the market without adequate learning, reflection, and trial-and-error is worse than donating to charity.</p><p>Therefore, this article will learn and document the meanings of stock F10 technical indicators.</p><p>In my view, technical indicators are not only an important basis for stock selection, but also a dictionary or “slang” for discussing with others.</p><p>If you don’t know what they mean, how can you align underlying logic, match granularity, execute a combined punch, and empower stock selection strategies?</p><p>This article does not cover real-time trend indicators such as MACD, KDJ, and CCI, but only analyzes F10 technical indicators related to financial reports.</p><p><strong>Risk Warning &amp; Disclaimer: The above content does not constitute any financial marketing or investment solicitation, nor does it constitute any investment advice. Before making any investment decision, investors should consider the risk factors associated with investment products based on their own circumstances and, when necessary, consult professional investment advisors for advice.</strong></p><h1 id="What-is-F10-amp-Where-to-Find-It"><a href="#What-is-F10-amp-Where-to-Find-It" class="headerlink" title="What is F10 &amp; Where to Find It"></a>What is F10 &amp; Where to Find It</h1><p>F10 is also known as the fundamental data of a company and stock. It gets its name “F10” because the F10 button in stock trading software brings up the data dashboard interface.</p><p>Open the East Money website, and to the right of the current price and percentage change, you can see basic and important data.</p><p>The fact that this data is placed here means it is extremely important; otherwise, it wouldn’t occupy a top position.</p><p>However, due to space limitations, more data needs to be viewed in the F10 files below.</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/BtGfxm.jpg" alt="F10" title="">                </div>                <div class="image-caption">F10</div>            </figure><h1 id="Indicator-Structure-amp-Connections"><a href="#Indicator-Structure-amp-Connections" class="headerlink" title="Indicator Structure &amp; Connections"></a>Indicator Structure &amp; Connections</h1><p>Multiple indicators are certainly not independent of each other. How indicators are calculated can be viewed via [Financial Analysis] → [DuPont Analysis].</p><p>Indicator data is generally obtained from annual reports. Such indicators are relatively static, meaning they remain unchanged for several months.</p><p>Although fundamental financial indicators provide a data foundation for stock selection, in a volatile stock market, they should be analyzed in combination with dynamic indicators as much as possible.</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/bWsgsi.jpg" alt="DuPont Analysis" title="">                </div>                <div class="image-caption">DuPont Analysis</div>            </figure><h1 id="Indicator-Descriptions"><a href="#Indicator-Descriptions" class="headerlink" title="Indicator Descriptions"></a>Indicator Descriptions</h1><h2 id="Basic-Composition-of-Indicators"><a href="#Basic-Composition-of-Indicators" class="headerlink" title="Basic Composition of Indicators"></a>Basic Composition of Indicators</h2><p>P Total Market Value / Share Price — Price</p><p>E Net Profit / Earnings Per Share — Earning</p><p>B Net Assets — Book Value — The remaining portion of a company’s total assets minus total liabilities</p><p>R Return — Return</p><h2 id="ROE-—-Return-on-Equity"><a href="#ROE-—-Return-on-Equity" class="headerlink" title="ROE — Return on Equity"></a>ROE — Return on Equity</h2><blockquote><p>If I had to choose only one metric to pick stocks, I would choose ROE (Return on Equity). Companies whose ROE can remain consistently above 20% year after year are good companies, and investors should consider buying them. — Warren Buffett</p></blockquote><p>It is generally used to evaluate a company’s ability to generate profits using shareholders’ equity.</p><p><mjx-container class="MathJax" jax="SVG" overflow="overflow"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="5.176ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 2288 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math" data-latex="ROE = \frac{\text{Net Income}}{\text{Shareholders’ Equity}}"><g data-mml-node="mi" data-latex="R"><path data-c="1D445" d="M739 531C739 582 713 621 662 649C621 672 572 683 517 683L235 683C212 683 202 682 202 659C202 652 205 647 211 646C221 645 229 644 234 644C264 643 281 641 286 640C291 639 294 636 294 631C294 629 293 623 290 613L158 82C153 60 143 46 128 41C121 39 103 38 72 38C50 38 41 37 41 15C41 4 47-1 59 0L183 3L309 0C325-1 333 8 333 23C333 33 322 38 301 38C261 38 241 43 241 52C241 52 242 54 244 68L308 327L423 327C492 327 527 298 527 241C527 232 522 210 513 174C502 132 497 104 497 89C497 13 556-22 632-22C660-22 687-9 714 16C741 41 755 68 755 96C755 106 750 111 739 111C732 111 726 106 723 95C712 64 698 41 682 28C666 15 651 8 636 8C613 8 601 27 601 64C601 88 604 125 611 176C614 197 615 212 615 223C615 276 587 315 531 339C625 362 739 429 739 531M609 616C629 603 639 581 639 550C639 530 635 507 626 480C599 398 531 357 422 357L316 357L379 610C384 631 392 642 403 643C408 644 428 644 463 644C528 644 566 642 609 616Z"></path></g><g data-mml-node="mi" data-latex="O" transform="translate(759,0)"><path data-c="1D442" d="M702 263C727 320 740 377 740 435C740 513 717 577 670 628C623 679 561 705 484 705C431 705 378 692 325 666C217 613 138 529 87 415C62 359 49 303 49 246C49 167 72 103 119 53C166 3 228-22 306-22C358-22 410-9 463 16C572 69 651 152 702 263M644 474C644 341 584 208 520 129C476 74 400 12 312 12C205 12 149 105 149 218C149 253 156 300 169 357C196 475 264 579 353 633C396 659 438 672 478 672C586 672 644 586 644 474Z"></path></g><g data-mml-node="mi" data-latex="E" transform="translate(1522,0)"><path data-c="1D438" d="M199 656C199 646 210 641 231 641C271 641 291 637 291 628C291 625 289 618 286 605L156 82C151 60 141 47 126 42C119 40 101 39 70 39C48 39 38 37 38 16C38 5 49 0 70 0L583 0C607 0 609 2 617 19L657 109C684 171 703 217 714 248C712 257 709 263 698 263C691 263 685 258 681 249C656 190 632 145 608 113C568 59 509 39 415 39L270 39C260 39 253 39 249 40C243 40 240 42 240 45C240 47 242 54 245 67L311 334L406 334C453 334 478 328 483 315C485 310 486 303 486 293C486 280 484 264 479 245C477 240 476 236 476 233C476 223 482 218 493 218C502 218 508 226 512 241L568 473C568 483 563 488 552 488C544 488 538 481 535 467C525 428 512 403 495 391C478 379 450 373 409 373L321 373L379 606C387 640 387 641 429 641L568 641C619 641 654 636 673 626C701 611 715 581 715 536C715 519 714 501 711 484C711 482 711 479 710 475L710 466C710 455 715 450 725 450C735 450 741 459 743 478L763 649C766 677 759 680 732 680L233 680C210 680 199 679 199 656Z"></path></g></g></g></svg><mjx-break size="4"> </mjx-break><svg style="vertical-align: -1.108ex;" xmlns="http://www.w3.org/2000/svg" width="18.003ex" height="3.093ex" role="img" focusable="false" viewBox="0 -877 7957.3 1366.9"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math" data-latex="ROE = \frac{\text{Net Income}}{\text{Shareholders’ Equity}}"><g data-mml-node="mo" data-latex="="><path data-c="3D" d="M698 367L80 367C64 367 56 359 56 344C56 329 64 321 80 321L698 321C714 321 722 329 722 344C722 356 711 367 698 367M698 179L80 179C64 179 56 171 56 156C56 141 64 133 80 133L698 133C714 133 722 141 722 156C722 169 711 179 698 179Z"></path></g><g data-mml-node="mfrac" data-latex="\frac{\text{Net Income}}{\text{Shareholders’ Equity}}" transform="translate(1055.8,0)"><g data-mml-node="mtext" transform="translate(1664.3,394) scale(0.707)" data-latex="\text{Net Income}"><path data-c="4E" d="M576 15C585.6 1 589 0 596 0C613 0 614 7 614 30L614 575C614 592 618 606 626 619C637 636 667 644 716 644L716 683L596 680L477 683L477 644C526 644 556 636 567 619C575 606 579 592 579 575L579 166L237 668C230 678 220 683 205 683L33 683L33 644L66 644C106 644 128 642 133 638C134 637 135 632 135 624L135 108C135 91 131 77 123 64C112 47 82 39 33 39L33 0L152 3L272 0L272 39C223 39 193 47 182 64C174 77 170 91 170 108L170 611Z"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(750,0)"></path><path data-c="74" d="M332 126L332 186L300 186L300 128C300 78 282 22 238 22C197 22 177 56 177 124L177 395L316 395L316 433L177 433L177 615L145 615C144 582 141 553 134 527C117 461 78 427 19 424L19 395L102 395L102 126C102 67 120 29 155 10C182-4 207-11 232-11C298-11 332 55 332 126Z" transform="translate(1194,0)"></path><path data-c="20" d="" transform="translate(1583,0)"></path><path data-c="49" d="M180 3C257 3 308 2 333 0L333 39L304 39C268 39 246 41 239 46C232 51 228 63 228 81L228 602C228 620 232 631 239 636C246 641 268 644 304 644L333 644L333 683C308 681 257 680 181 680C104 680 53 681 28 683L28 644L57 644C93 644 115 641 122 636C129 631 133 620 133 602L133 81C133 63 129 51 122 46C115 41 93 39 57 39L28 39L28 0C53 2 104 3 180 3Z" transform="translate(1915,0)"></path><path data-c="6E" d="M314 413C359 413 382 378 382 307L382 79C382 60 379 48 372 44C365 40 343 38 306 38L306 0L421 3L535 0L535 38C503 38 482 39 472 42C462 45 458 52 458 64L458 251C458 298 457 331 454 350C444 411 399 442 320 442C257 442 210 412 179 352L179 442L32 431L32 393C68 393 90 390 98 384C106 378 109 365 109 342L109 79C109 60 105 48 98 44C91 40 69 38 32 38L32 0L147 3L261 0L261 38C224 38 203 40 196 44C189 48 185 60 185 79L185 259C185 340 236 413 314 413Z" transform="translate(2276,0)"></path><path data-c="63" d="M251 416C293 416 325 406 347 386C323 383 306 361 306 338C306 305 322 289 355 289C388 289 404 306 404 339C404 410 327 448 250 448C188 448 137 426 96 380C55 334 34 279 34 216C34 155 55 101 96 56C137 11 187-11 248-11C298-11 337 3 365 32C388 55 403 78 411 102C414 111 415 117 415 121C415 130 409 134 398 134C390 134 385 130 382 122C361 55 320 21 257 21C228 21 200 34 172 61C139 92 123 144 123 218C123 318 161 416 251 416Z" transform="translate(2832,0)"></path><path data-c="6F" d="M249-11C311-11 363 11 406 55C449 99 471 152 471 214C471 277 450 332 408 378C366 424 313 448 250 448C187 448 135 424 92 378C49 332 28 277 28 214C28 152 49 99 92 55C135 11 188-11 249-11M250 21C202 21 166 42 141 85C125 113 117 159 117 222C117 283 125 327 140 355C164 398 200 419 249 419C296 419 332 398 357 357C374 329 382 284 382 222C382 106 348 21 250 21Z" transform="translate(3276,0)"></path><path data-c="6D" d="M315 413C361 413 384 378 384 307L384 79C384 60 381 48 374 44C367 40 344 38 307 38L307 0L423 3L538 0L538 38C501 38 479 40 472 44C465 48 461 60 461 79L461 259C461 339 513 413 590 413C637 413 660 378 660 307L660 79C660 60 656 48 649 44C642 40 619 38 582 38L582 0L698 3L813 0L813 38C781 38 760 39 750 42C740 45 736 52 736 64L736 251C736 298 734 331 731 350C721 411 676 442 597 442C534 442 487 413 455 354C441 413 397 442 322 442C259 442 211 412 179 352L179 442L32 431L32 393C68 393 90 390 98 384C106 378 109 365 109 342L109 79C109 60 105 48 98 44C91 40 69 38 32 38L32 0L148 3L263 0L263 38C226 38 203 40 196 44C189 48 185 60 185 79L185 259C185 340 238 413 315 413Z" transform="translate(3776,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(4609,0)"></path></g><g data-mml-node="mtext" transform="translate(220,-345) scale(0.707)" data-latex="\text{Shareholders’ Equity}"><path data-c="53" d="M252 669C359 669 422 601 440 466C441 456 447 451 456 451C467 451 472 460 472 478L472 677C472 696 467 705 458 705C452 705 446 700 440 690L408 638C364 680 324 705 251 705C142 705 56 617 56 509C56 417 116 349 187 324L317 292C384 275 428 229 428 154C428 79 376 17 301 17C170 17 93 91 88 218C88 227 83 232 73 232C62 232 56 223 56 204L56 6C56-13 61-22 71-22C77-22 83-17 89-7L121 45C164 0 224-22 302-22C359-22 407-1 444 40C481 81 499 130 499 188C499 235 485 278 458 317C431 356 394 380 349 391L223 422C170 434 127 481 127 544C127 614 182 669 252 669Z"></path><path data-c="68" d="M314 415C359 415 382 380 382 309L382 79C382 60 379 48 372 44C365 40 343 39 306 39L306 0L421 3L535 0L535 39C503 39 482 40 472 43C462 46 458 53 458 64L458 253C458 301 457 334 454 353C444 414 399 445 320 445C259 445 213 417 182 361L182 694L32 683L32 645C68 645 90 642 98 636C106 630 109 616 109 593L109 79C109 60 105 48 98 44C91 40 69 39 32 39L32 0L147 3L261 0L261 39C224 39 203 40 196 44C189 48 185 60 185 79L185 261C185 342 236 415 314 415Z" transform="translate(556,0)"></path><path data-c="61" d="M483 91L483 150L451 150L451 91C451 52 440 32 419 32C399 32 387 57 387 77L387 274C387 309 383 335 376 353C352 412 283 448 213 448C136 448 60 405 60 333C60 300 77 283 110 283C143 283 159 299 159 332C159 361 144 378 113 381C135 406 168 419 211 419C273 419 311 362 311 297L311 264C233 259 174 247 133 228C66 197 32 154 32 97C32 71 42 49 61 32C93 3 137-11 193-11C252-11 294 14 320 65C327 27 355-6 398-6C451-6 483 36 483 91M200 18C154 18 116 52 116 98C116 191 213 231 311 236L311 141C311 74 267 18 200 18Z" transform="translate(1112,0)"></path><path data-c="72" d="M364 378C364 417 327 442 288 442C237 442 198 412 172 351L172 442L28 431L28 393C63 393 84 390 92 384C100 378 104 365 104 342L104 79C104 60 101 48 94 44C87 40 65 38 28 38L28 0L143 3C185 4 228 3 271 0L271 38L247 38C214 38 193 41 186 46C179 51 176 63 176 81L176 232C176 275 184 314 199 347C219 390 248 412 287 413C277 403 272 391 272 377C272 346 287 331 318 331C345 331 364 352 364 378Z" transform="translate(1612,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(2004,0)"></path><path data-c="68" d="M314 415C359 415 382 380 382 309L382 79C382 60 379 48 372 44C365 40 343 39 306 39L306 0L421 3L535 0L535 39C503 39 482 40 472 43C462 46 458 53 458 64L458 253C458 301 457 334 454 353C444 414 399 445 320 445C259 445 213 417 182 361L182 694L32 683L32 645C68 645 90 642 98 636C106 630 109 616 109 593L109 79C109 60 105 48 98 44C91 40 69 39 32 39L32 0L147 3L261 0L261 39C224 39 203 40 196 44C189 48 185 60 185 79L185 261C185 342 236 415 314 415Z" transform="translate(2448,0)"></path><path data-c="6F" d="M249-11C311-11 363 11 406 55C449 99 471 152 471 214C471 277 450 332 408 378C366 424 313 448 250 448C187 448 135 424 92 378C49 332 28 277 28 214C28 152 49 99 92 55C135 11 188-11 249-11M250 21C202 21 166 42 141 85C125 113 117 159 117 222C117 283 125 327 140 355C164 398 200 419 249 419C296 419 332 398 357 357C374 329 382 284 382 222C382 106 348 21 250 21Z" transform="translate(3004,0)"></path><path data-c="6C" d="M144 3L255 0L255 39C219 39 197 40 190 44C183 48 180 60 180 79L180 694L33 683L33 645C68 645 90 642 97 636C104 630 108 616 108 593L108 79C108 60 105 48 98 44C91 40 69 39 33 39L33 0Z" transform="translate(3504,0)"></path><path data-c="64" d="M374-11L527 0L527 38C491 38 470 40 462 46C454 52 450 67 450 90L450 694L301 683L301 645C336 645 358 642 366 636C374 630 377 616 377 593L377 390C345 427 305 445 257 445C195 445 142 423 99 378C56 333 34 279 34 216C34 155 54 102 95 57C136 12 186-11 247-11C298-11 341 8 374 47M261 415C304 415 339 396 364 358C371 347 374 336 374 323L374 121C374 108 371 97 364 86C336 41 298 19 251 19C210 19 177 39 151 80C133 110 124 155 124 215C124 324 161 415 261 415Z" transform="translate(3782,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(4338,0)"></path><path data-c="72" d="M364 378C364 417 327 442 288 442C237 442 198 412 172 351L172 442L28 431L28 393C63 393 84 390 92 384C100 378 104 365 104 342L104 79C104 60 101 48 94 44C87 40 65 38 28 38L28 0L143 3C185 4 228 3 271 0L271 38L247 38C214 38 193 41 186 46C179 51 176 63 176 81L176 232C176 275 184 314 199 347C219 390 248 412 287 413C277 403 272 391 272 377C272 346 287 331 318 331C345 331 364 352 364 378Z" transform="translate(4782,0)"></path><path data-c="73" d="M360 130C360 206 308 254 205 274C158 283 128 294 112 305C96 316 88 332 88 351C88 398 123 421 193 421C261 421 298 383 303 306C304 298 309 294 319 294C330 294 336 303 336 321L336 420C336 439 331 448 321 448C310 448 295 430 285 422C260 439 229 448 193 448C104 448 33 408 33 323C33 289 47 260 76 237C111 208 144 202 207 191C272 178 305 149 305 104C305 47 270 18 199 18C130 18 86 64 67 155C64 168 59 175 50 175C39 175 33 165 33 146L33 17C33-2 38-11 48-11C51-11 56-8 64-1C82 15 74 12 92 30C121 3 157-11 199-11C294-11 360 38 360 130Z" transform="translate(5174,0)"></path><path data-c="2019" d="M139 694C108 694 86 669 86 638C86 608 109 583 139 583C154 583 166 588 177 597L178 588C178 526 156 473 113 429C105 420 101 414 101 410C101 400 105 395 114 395C120 395 130 403 144 418C175 454 206 525 206 588C206 639 186 694 139 694Z" transform="translate(5568,0)"></path><path data-c="20" d="" transform="translate(5846,0)"></path><path data-c="45" d="M230 74L230 334L315 334C360 334 388 326 400 311C412 296 418 265 418 218L451 218L451 488L418 488C418 441 412 411 400 396C388 381 360 373 315 373L230 373L230 606C230 642 236 641 275 641L402 641C474 641 522 627 547 598C579 559 583 523 592 450L625 450L596 680L33 680L33 641L61 641C96 641 117 639 124 634C131 629 135 617 135 599L135 81C135 63 131 51 124 46C117 41 96 39 61 39L33 39L33 0L610 0L653 263L620 263C611 208 602 166 591 136C580 106 560 82 530 63C505 47 464 39 406 39L275 39C236 39 230 38 230 74Z" transform="translate(6178,0)"></path><path data-c="71" d="M255 442C193 442 141 419 98 374C55 329 34 276 34 214C34 153 55 100 96 55C137 10 187-13 248-13C297-13 339 8 374 51L374-115C374-134 371-146 364-150C357-154 335-155 298-155L298-194L412-191L527-194L527-155C490-155 468-154 461-150C454-146 450-134 450-115L450 442L422 442L387 358C364 401 320 442 255 442M251 16C210 16 177 40 153 88C134 126 124 168 124 214C124 261 135 303 157 341C183 386 218 409 261 409C326 409 377 342 377 275L377 136C377 75 311 16 251 16Z" transform="translate(6859,0)"></path><path data-c="75" d="M267 18C198 18 185 47 185 112L185 442L32 431L32 393C71 393 94 389 100 382C106 375 109 350 109 306L109 159C109 120 112 90 119 67C134 15 189-11 262-11C316-11 357 14 385 65L385-11L535 0L535 38C499 38 478 40 470 46C462 52 458 66 458 89L458 442L306 431L306 393C341 393 362 390 370 384C378 378 382 365 382 342L382 167C382 88 340 18 267 18Z" transform="translate(7387,0)"></path><path data-c="69" d="M194 601C194 631 169 657 139 657C109 657 83 631 83 601C83 571 108 544 138 544C169 544 194 570 194 601M143 3L247 0L247 39C214 39 194 41 188 45C182 49 180 60 180 78L180 445L37 433L37 395C70 395 91 392 98 387C105 382 108 368 108 345L108 79C108 60 105 48 98 44C91 40 69 39 33 39L33 0Z" transform="translate(7943,0)"></path><path data-c="74" d="M332 126L332 186L300 186L300 128C300 78 282 22 238 22C197 22 177 56 177 124L177 395L316 395L316 433L177 433L177 615L145 615C144 582 141 553 134 527C117 461 78 427 19 424L19 395L102 395L102 126C102 67 120 29 155 10C182-4 207-11 232-11C298-11 332 55 332 126Z" transform="translate(8221,0)"></path><path data-c="79" d="M113-205C168-205 212-167 243-90L416 332C433 372 463 392 508 392L508 431C475 428 449 427 430 428L341 431L341 393C372 392 387 379 387 354C387 348 385 341 381 332L286 98L180 354C177 362 175 368 174 371C174 385 193 392 230 392L230 431L118 428C94 427 61 428 19 431L19 392C72 392 85 394 99 360L246 1L222-55C195-119 176-175 113-175C99-175 87-171 76-164C99-159 110-144 110-120C110-90 95-75 64-75C34-75 19-90 19-121C19-170 64-205 113-205Z" transform="translate(8610,0)"></path></g><rect width="6661.5" height="60" x="120" y="220"></rect></g></g></g></svg></mjx-container></p><p>Assuming a company has a net profit of 2 million RMB and shareholders’ equity of 10 million RMB in a fiscal year, its ROE is:</p><p><mjx-container class="MathJax" jax="SVG" overflow="overflow"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="5.176ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 2288 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math" data-latex="ROE = \frac{\text{RMB }2{,}000{,}000}{\text{RMB }10{,}000{,}000} = 0.2\ \text{or}\ 20%"><g data-mml-node="mi" data-latex="R"><path data-c="1D445" d="M739 531C739 582 713 621 662 649C621 672 572 683 517 683L235 683C212 683 202 682 202 659C202 652 205 647 211 646C221 645 229 644 234 644C264 643 281 641 286 640C291 639 294 636 294 631C294 629 293 623 290 613L158 82C153 60 143 46 128 41C121 39 103 38 72 38C50 38 41 37 41 15C41 4 47-1 59 0L183 3L309 0C325-1 333 8 333 23C333 33 322 38 301 38C261 38 241 43 241 52C241 52 242 54 244 68L308 327L423 327C492 327 527 298 527 241C527 232 522 210 513 174C502 132 497 104 497 89C497 13 556-22 632-22C660-22 687-9 714 16C741 41 755 68 755 96C755 106 750 111 739 111C732 111 726 106 723 95C712 64 698 41 682 28C666 15 651 8 636 8C613 8 601 27 601 64C601 88 604 125 611 176C614 197 615 212 615 223C615 276 587 315 531 339C625 362 739 429 739 531M609 616C629 603 639 581 639 550C639 530 635 507 626 480C599 398 531 357 422 357L316 357L379 610C384 631 392 642 403 643C408 644 428 644 463 644C528 644 566 642 609 616Z"></path></g><g data-mml-node="mi" data-latex="O" transform="translate(759,0)"><path data-c="1D442" d="M702 263C727 320 740 377 740 435C740 513 717 577 670 628C623 679 561 705 484 705C431 705 378 692 325 666C217 613 138 529 87 415C62 359 49 303 49 246C49 167 72 103 119 53C166 3 228-22 306-22C358-22 410-9 463 16C572 69 651 152 702 263M644 474C644 341 584 208 520 129C476 74 400 12 312 12C205 12 149 105 149 218C149 253 156 300 169 357C196 475 264 579 353 633C396 659 438 672 478 672C586 672 644 586 644 474Z"></path></g><g data-mml-node="mi" data-latex="E" transform="translate(1522,0)"><path data-c="1D438" d="M199 656C199 646 210 641 231 641C271 641 291 637 291 628C291 625 289 618 286 605L156 82C151 60 141 47 126 42C119 40 101 39 70 39C48 39 38 37 38 16C38 5 49 0 70 0L583 0C607 0 609 2 617 19L657 109C684 171 703 217 714 248C712 257 709 263 698 263C691 263 685 258 681 249C656 190 632 145 608 113C568 59 509 39 415 39L270 39C260 39 253 39 249 40C243 40 240 42 240 45C240 47 242 54 245 67L311 334L406 334C453 334 478 328 483 315C485 310 486 303 486 293C486 280 484 264 479 245C477 240 476 236 476 233C476 223 482 218 493 218C502 218 508 226 512 241L568 473C568 483 563 488 552 488C544 488 538 481 535 467C525 428 512 403 495 391C478 379 450 373 409 373L321 373L379 606C387 640 387 641 429 641L568 641C619 641 654 636 673 626C701 611 715 581 715 536C715 519 714 501 711 484C711 482 711 479 710 475L710 466C710 455 715 450 725 450C735 450 741 459 743 478L763 649C766 677 759 680 732 680L233 680C210 680 199 679 199 656Z"></path></g></g></g></svg><mjx-break size="4"> </mjx-break><svg style="vertical-align: -1.089ex;" xmlns="http://www.w3.org/2000/svg" width="14.981ex" height="3.26ex" role="img" focusable="false" viewBox="0 -959.4 6621.6 1440.9"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math" data-latex="ROE = \frac{\text{RMB }2{,}000{,}000}{\text{RMB }10{,}000{,}000} = 0.2\ \text{or}\ 20%"><g data-mml-node="mo" data-latex="="><path data-c="3D" d="M698 367L80 367C64 367 56 359 56 344C56 329 64 321 80 321L698 321C714 321 722 329 722 344C722 356 711 367 698 367M698 179L80 179C64 179 56 171 56 156C56 141 64 133 80 133L698 133C714 133 722 141 722 156C722 169 711 179 698 179Z"></path></g><g data-mml-node="mfrac" data-latex="\frac{\text{RMB }2{,}000{,}000}{\text{RMB }10{,}000{,}000}" transform="translate(1055.8,0)"><g data-mml-node="mrow" transform="translate(396.8,476.5) scale(0.707)" data-latex="\text{RMB }2{,}000{,}000"><g data-mml-node="mtext" data-latex="\text{RMB }"><path data-c="52" d="M611 501C611 558 580 604 519 639C468 668 411 683 349 683L35 683L35 644L63 644C98 644 119 641 126 636C133 631 137 620 137 602L137 81C137 63 133 51 126 46C119 41 98 38 63 38L35 38L35-1C62 2 111 3 183 3C256 3 305 2 332-1L332 38L304 38C269 38 248 41 241 46C234 51 230 63 230 81L230 327L340 327C404 327 451 289 462 235C464 224 465 200 465 165C465 138 466 118 467 106C473 22 546-22 634-22C672-22 699-6 714 25C726 48 732 70 732 91C732 104 726 111 715 111C706 111 700 105 699 92C694 36 673 8 638 8C603 8 591 41 585 70C579 104 575 130 572 147L559 226C544 278 507 316 448 339C524 361 611 415 611 501M470 600C491 581 502 548 502 501C502 463 492 431 473 404C450 373 405 357 336 357L230 357L230 609C230 630 236 641 248 642C255 643 274 644 306 644C385 644 426 641 470 600Z"></path><path data-c="4D" d="M175 108L175 622L407 24C413 8 421 0 430 0C439 0 446 7 452 21L689 630L689 81C689 63 685 51 678 46C671 41 650 39 615 39L587 39L587 0C614 2 663 3 733 3C803 3 852 2 879 0L879 39L851 39C816 39 795 41 788 46C781 51 777 63 777 81L777 602C777 620 781 631 788 636C795 641 816 644 851 644L879 644L879 683L708 683C681 683 679 682 671 662L458 115L247 659C238 679 235 683 208 683L37 683L37 644L65 644C100 644 121 641 128 636C135 631 139 620 139 602L139 108C139 91 135 77 127 64C116 47 86 39 37 39L37 0L157 3L277 0L277 39C204 39 175 51 175 108Z" transform="translate(736,0)"></path><path data-c="42" d="M478 358C548 376 624 432 624 512C624 565 599 607 549 640C506 669 456 683 401 683L36 683L36 644L64 644C99 644 120 641 127 636C134 631 138 620 138 602L138 81C138 63 134 51 127 46C120 41 99 39 64 39L36 39L36 0L427 0C484 0 535 16 579 48C627 83 651 129 651 185C651 278 563 342 478 358M521 512C521 428 451 370 367 370L228 370L228 609C228 645 233 644 272 644L394 644C468 644 521 586 521 512M544 186L544 160C542 137 529 111 504 84C478 54 442 39 395 39L272 39C233 39 228 38 228 74L228 340L409 340C490 340 544 270 544 186Z" transform="translate(1653,0)"></path><path data-c="A0" d="" transform="translate(2361,0)"></path></g><g data-mml-node="mn" data-latex="2{,}000{,}000" transform="translate(2693,0)"><path data-c="32" d="M237 666C186 666 143 648 106 612C69 576 50 534 50 483C50 449 75 424 106 424C136 424 161 450 161 480C161 513 137 536 105 536C102 536 100 536 98 535C117 584 161 627 224 627C306 627 352 556 352 470C352 403 318 331 250 255L62 43C49 28 50 29 50 0L421 0L450 180L417 180C409 129 402 100 396 91C391 86 361 84 306 84L139 84L236 179C304 243 390 312 419 365C439 400 449 435 449 470C449 588 357 666 237 666Z"></path><path data-c="2C" d="M139 106C107 106 86 82 86 50C86 20 109-5 139-5C153-5 165-1 174 8L175 0C175-63 154-117 112-160C105-168 101-174 101-178C101-188 105-193 114-193C123-193 135-181 152-158C186-110 203-57 203 0C203 53 185 106 139 106Z" transform="translate(500,0)"></path><path data-c="30" d="M249-22C390-22 460 92 460 320C460 473 428 575 365 625C330 652 291 666 250 666C109 666 39 551 39 320C39 136 88-22 249-22M361 524C368 489 371 425 371 332C371 240 367 172 360 128C347 48 310 8 249 8C226 8 203 17 182 34C155 57 139 104 132 176C129 201 128 253 128 332C128 419 131 480 136 513C145 568 163 603 191 618C213 630 232 636 249 636C314 636 350 583 361 524Z" transform="translate(778,0)"></path><path data-c="30" d="M249-22C390-22 460 92 460 320C460 473 428 575 365 625C330 652 291 666 250 666C109 666 39 551 39 320C39 136 88-22 249-22M361 524C368 489 371 425 371 332C371 240 367 172 360 128C347 48 310 8 249 8C226 8 203 17 182 34C155 57 139 104 132 176C129 201 128 253 128 332C128 419 131 480 136 513C145 568 163 603 191 618C213 630 232 636 249 636C314 636 350 583 361 524Z" transform="translate(1278,0)"></path><path data-c="30" d="M249-22C390-22 460 92 460 320C460 473 428 575 365 625C330 652 291 666 250 666C109 666 39 551 39 320C39 136 88-22 249-22M361 524C368 489 371 425 371 332C371 240 367 172 360 128C347 48 310 8 249 8C226 8 203 17 182 34C155 57 139 104 132 176C129 201 128 253 128 332C128 419 131 480 136 513C145 568 163 603 191 618C213 630 232 636 249 636C314 636 350 583 361 524Z" transform="translate(1778,0)"></path><path data-c="2C" d="M139 106C107 106 86 82 86 50C86 20 109-5 139-5C153-5 165-1 174 8L175 0C175-63 154-117 112-160C105-168 101-174 101-178C101-188 105-193 114-193C123-193 135-181 152-158C186-110 203-57 203 0C203 53 185 106 139 106Z" transform="translate(2278,0)"></path><path data-c="30" d="M249-22C390-22 460 92 460 320C460 473 428 575 365 625C330 652 291 666 250 666C109 666 39 551 39 320C39 136 88-22 249-22M361 524C368 489 371 425 371 332C371 240 367 172 360 128C347 48 310 8 249 8C226 8 203 17 182 34C155 57 139 104 132 176C129 201 128 253 128 332C128 419 131 480 136 513C145 568 163 603 191 618C213 630 232 636 249 636C314 636 350 583 361 524Z" transform="translate(2556,0)"></path><path data-c="30" d="M249-22C390-22 460 92 460 320C460 473 428 575 365 625C330 652 291 666 250 666C109 666 39 551 39 320C39 136 88-22 249-22M361 524C368 489 371 425 371 332C371 240 367 172 360 128C347 48 310 8 249 8C226 8 203 17 182 34C155 57 139 104 132 176C129 201 128 253 128 332C128 419 131 480 136 513C145 568 163 603 191 618C213 630 232 636 249 636C314 636 350 583 361 524Z" transform="translate(3056,0)"></path><path data-c="30" d="M249-22C390-22 460 92 460 320C460 473 428 575 365 625C330 652 291 666 250 666C109 666 39 551 39 320C39 136 88-22 249-22M361 524C368 489 371 425 371 332C371 240 367 172 360 128C347 48 310 8 249 8C226 8 203 17 182 34C155 57 139 104 132 176C129 201 128 253 128 332C128 419 131 480 136 513C145 568 163 603 191 618C213 630 232 636 249 636C314 636 350 583 361 524Z" transform="translate(3556,0)"></path></g></g><g data-mml-node="mrow" transform="translate(220,-345) scale(0.707)" data-latex="\text{RMB }10{,}000{,}000"><g data-mml-node="mtext" data-latex="\text{RMB }"><path data-c="52" d="M611 501C611 558 580 604 519 639C468 668 411 683 349 683L35 683L35 644L63 644C98 644 119 641 126 636C133 631 137 620 137 602L137 81C137 63 133 51 126 46C119 41 98 38 63 38L35 38L35-1C62 2 111 3 183 3C256 3 305 2 332-1L332 38L304 38C269 38 248 41 241 46C234 51 230 63 230 81L230 327L340 327C404 327 451 289 462 235C464 224 465 200 465 165C465 138 466 118 467 106C473 22 546-22 634-22C672-22 699-6 714 25C726 48 732 70 732 91C732 104 726 111 715 111C706 111 700 105 699 92C694 36 673 8 638 8C603 8 591 41 585 70C579 104 575 130 572 147L559 226C544 278 507 316 448 339C524 361 611 415 611 501M470 600C491 581 502 548 502 501C502 463 492 431 473 404C450 373 405 357 336 357L230 357L230 609C230 630 236 641 248 642C255 643 274 644 306 644C385 644 426 641 470 600Z"></path><path data-c="4D" d="M175 108L175 622L407 24C413 8 421 0 430 0C439 0 446 7 452 21L689 630L689 81C689 63 685 51 678 46C671 41 650 39 615 39L587 39L587 0C614 2 663 3 733 3C803 3 852 2 879 0L879 39L851 39C816 39 795 41 788 46C781 51 777 63 777 81L777 602C777 620 781 631 788 636C795 641 816 644 851 644L879 644L879 683L708 683C681 683 679 682 671 662L458 115L247 659C238 679 235 683 208 683L37 683L37 644L65 644C100 644 121 641 128 636C135 631 139 620 139 602L139 108C139 91 135 77 127 64C116 47 86 39 37 39L37 0L157 3L277 0L277 39C204 39 175 51 175 108Z" transform="translate(736,0)"></path><path data-c="42" d="M478 358C548 376 624 432 624 512C624 565 599 607 549 640C506 669 456 683 401 683L36 683L36 644L64 644C99 644 120 641 127 636C134 631 138 620 138 602L138 81C138 63 134 51 127 46C120 41 99 39 64 39L36 39L36 0L427 0C484 0 535 16 579 48C627 83 651 129 651 185C651 278 563 342 478 358M521 512C521 428 451 370 367 370L228 370L228 609C228 645 233 644 272 644L394 644C468 644 521 586 521 512M544 186L544 160C542 137 529 111 504 84C478 54 442 39 395 39L272 39C233 39 228 38 228 74L228 340L409 340C490 340 544 270 544 186Z" transform="translate(1653,0)"></path><path data-c="A0" d="" transform="translate(2361,0)"></path></g><g data-mml-node="mn" data-latex="10{,}000{,}000" transform="translate(2693,0)"><path data-c="31" d="M269 666C228 624 168 603 89 603L89 564C141 564 184 572 217 588L217 82C217 64 213 52 204 47C195 42 170 39 130 39L95 39L95 0C120 2 174 3 257 3C340 3 394 2 419 0L419 39L384 39C343 39 318 42 310 47C302 52 297 64 297 82L297 636C297 660 295 666 269 666Z"></path><path data-c="30" d="M249-22C390-22 460 92 460 320C460 473 428 575 365 625C330 652 291 666 250 666C109 666 39 551 39 320C39 136 88-22 249-22M361 524C368 489 371 425 371 332C371 240 367 172 360 128C347 48 310 8 249 8C226 8 203 17 182 34C155 57 139 104 132 176C129 201 128 253 128 332C128 419 131 480 136 513C145 568 163 603 191 618C213 630 232 636 249 636C314 636 350 583 361 524Z" transform="translate(500,0)"></path><path data-c="2C" d="M139 106C107 106 86 82 86 50C86 20 109-5 139-5C153-5 165-1 174 8L175 0C175-63 154-117 112-160C105-168 101-174 101-178C101-188 105-193 114-193C123-193 135-181 152-158C186-110 203-57 203 0C203 53 185 106 139 106Z" transform="translate(1000,0)"></path><path data-c="30" d="M249-22C390-22 460 92 460 320C460 473 428 575 365 625C330 652 291 666 250 666C109 666 39 551 39 320C39 136 88-22 249-22M361 524C368 489 371 425 371 332C371 240 367 172 360 128C347 48 310 8 249 8C226 8 203 17 182 34C155 57 139 104 132 176C129 201 128 253 128 332C128 419 131 480 136 513C145 568 163 603 191 618C213 630 232 636 249 636C314 636 350 583 361 524Z" transform="translate(1278,0)"></path><path data-c="30" d="M249-22C390-22 460 92 460 320C460 473 428 575 365 625C330 652 291 666 250 666C109 666 39 551 39 320C39 136 88-22 249-22M361 524C368 489 371 425 371 332C371 240 367 172 360 128C347 48 310 8 249 8C226 8 203 17 182 34C155 57 139 104 132 176C129 201 128 253 128 332C128 419 131 480 136 513C145 568 163 603 191 618C213 630 232 636 249 636C314 636 350 583 361 524Z" transform="translate(1778,0)"></path><path data-c="30" d="M249-22C390-22 460 92 460 320C460 473 428 575 365 625C330 652 291 666 250 666C109 666 39 551 39 320C39 136 88-22 249-22M361 524C368 489 371 425 371 332C371 240 367 172 360 128C347 48 310 8 249 8C226 8 203 17 182 34C155 57 139 104 132 176C129 201 128 253 128 332C128 419 131 480 136 513C145 568 163 603 191 618C213 630 232 636 249 636C314 636 350 583 361 524Z" transform="translate(2278,0)"></path><path data-c="2C" d="M139 106C107 106 86 82 86 50C86 20 109-5 139-5C153-5 165-1 174 8L175 0C175-63 154-117 112-160C105-168 101-174 101-178C101-188 105-193 114-193C123-193 135-181 152-158C186-110 203-57 203 0C203 53 185 106 139 106Z" transform="translate(2778,0)"></path><path data-c="30" d="M249-22C390-22 460 92 460 320C460 473 428 575 365 625C330 652 291 666 250 666C109 666 39 551 39 320C39 136 88-22 249-22M361 524C368 489 371 425 371 332C371 240 367 172 360 128C347 48 310 8 249 8C226 8 203 17 182 34C155 57 139 104 132 176C129 201 128 253 128 332C128 419 131 480 136 513C145 568 163 603 191 618C213 630 232 636 249 636C314 636 350 583 361 524Z" transform="translate(3056,0)"></path><path data-c="30" d="M249-22C390-22 460 92 460 320C460 473 428 575 365 625C330 652 291 666 250 666C109 666 39 551 39 320C39 136 88-22 249-22M361 524C368 489 371 425 371 332C371 240 367 172 360 128C347 48 310 8 249 8C226 8 203 17 182 34C155 57 139 104 132 176C129 201 128 253 128 332C128 419 131 480 136 513C145 568 163 603 191 618C213 630 232 636 249 636C314 636 350 583 361 524Z" transform="translate(3556,0)"></path><path data-c="30" d="M249-22C390-22 460 92 460 320C460 473 428 575 365 625C330 652 291 666 250 666C109 666 39 551 39 320C39 136 88-22 249-22M361 524C368 489 371 425 371 332C371 240 367 172 360 128C347 48 310 8 249 8C226 8 203 17 182 34C155 57 139 104 132 176C129 201 128 253 128 332C128 419 131 480 136 513C145 568 163 603 191 618C213 630 232 636 249 636C314 636 350 583 361 524Z" transform="translate(4056,0)"></path></g></g><rect width="5325.8" height="60" x="120" y="220"></rect></g></g></g></svg><mjx-break size="4"> </mjx-break><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="11.063ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 4889.8 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math" data-latex="ROE = \frac{\text{RMB }2{,}000{,}000}{\text{RMB }10{,}000{,}000} = 0.2\ \text{or}\ 20%"><g data-mml-node="mo" data-latex="="><path data-c="3D" d="M698 367L80 367C64 367 56 359 56 344C56 329 64 321 80 321L698 321C714 321 722 329 722 344C722 356 711 367 698 367M698 179L80 179C64 179 56 171 56 156C56 141 64 133 80 133L698 133C714 133 722 141 722 156C722 169 711 179 698 179Z"></path></g><g data-mml-node="mn" data-latex="0.2" transform="translate(1055.8,0)"><path data-c="30" d="M249-22C390-22 460 92 460 320C460 473 428 575 365 625C330 652 291 666 250 666C109 666 39 551 39 320C39 136 88-22 249-22M361 524C368 489 371 425 371 332C371 240 367 172 360 128C347 48 310 8 249 8C226 8 203 17 182 34C155 57 139 104 132 176C129 201 128 253 128 332C128 419 131 480 136 513C145 568 163 603 191 618C213 630 232 636 249 636C314 636 350 583 361 524Z"></path><path data-c="2E" d="M192 53C192 82 168 106 139 106C110 106 86 82 86 53C86 24 110 0 139 0C168 0 192 24 192 53Z" transform="translate(500,0)"></path><path data-c="32" d="M237 666C186 666 143 648 106 612C69 576 50 534 50 483C50 449 75 424 106 424C136 424 161 450 161 480C161 513 137 536 105 536C102 536 100 536 98 535C117 584 161 627 224 627C306 627 352 556 352 470C352 403 318 331 250 255L62 43C49 28 50 29 50 0L421 0L450 180L417 180C409 129 402 100 396 91C391 86 361 84 306 84L139 84L236 179C304 243 390 312 419 365C439 400 449 435 449 470C449 588 357 666 237 666Z" transform="translate(778,0)"></path></g><g data-mml-node="mtext" data-latex="\ " transform="translate(2333.8,0)"><path data-c="A0" d=""></path></g><g data-mml-node="mtext" data-latex="\text{or}" transform="translate(2665.8,0)"><path data-c="6F" d="M249-11C311-11 363 11 406 55C449 99 471 152 471 214C471 277 450 332 408 378C366 424 313 448 250 448C187 448 135 424 92 378C49 332 28 277 28 214C28 152 49 99 92 55C135 11 188-11 249-11M250 21C202 21 166 42 141 85C125 113 117 159 117 222C117 283 125 327 140 355C164 398 200 419 249 419C296 419 332 398 357 357C374 329 382 284 382 222C382 106 348 21 250 21Z"></path><path data-c="72" d="M364 378C364 417 327 442 288 442C237 442 198 412 172 351L172 442L28 431L28 393C63 393 84 390 92 384C100 378 104 365 104 342L104 79C104 60 101 48 94 44C87 40 65 38 28 38L28 0L143 3C185 4 228 3 271 0L271 38L247 38C214 38 193 41 186 46C179 51 176 63 176 81L176 232C176 275 184 314 199 347C219 390 248 412 287 413C277 403 272 391 272 377C272 346 287 331 318 331C345 331 364 352 364 378Z" transform="translate(500,0)"></path></g><g data-mml-node="mtext" data-latex="\ " transform="translate(3557.8,0)"><path data-c="A0" d=""></path></g><g data-mml-node="mn" data-latex="%" transform="translate(3889.8,0)"><path data-c="32" d="M237 666C186 666 143 648 106 612C69 576 50 534 50 483C50 449 75 424 106 424C136 424 161 450 161 480C161 513 137 536 105 536C102 536 100 536 98 535C117 584 161 627 224 627C306 627 352 556 352 470C352 403 318 331 250 255L62 43C49 28 50 29 50 0L421 0L450 180L417 180C409 129 402 100 396 91C391 86 361 84 306 84L139 84L236 179C304 243 390 312 419 365C439 400 449 435 449 470C449 588 357 666 237 666Z"></path><path data-c="30" d="M249-22C390-22 460 92 460 320C460 473 428 575 365 625C330 652 291 666 250 666C109 666 39 551 39 320C39 136 88-22 249-22M361 524C368 489 371 425 371 332C371 240 367 172 360 128C347 48 310 8 249 8C226 8 203 17 182 34C155 57 139 104 132 176C129 201 128 253 128 332C128 419 131 480 136 513C145 568 163 603 191 618C213 630 232 636 249 636C314 636 350 583 361 524Z" transform="translate(500,0)"></path></g></g></g></svg></mjx-container></p><p>This also means that for every 1 RMB of shareholders’ equity used, the company can generate 0.20 RMB in net profit.</p><p>How to view indicators in East Money is shown in the figure below:</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/yijJ3F.jpg" alt="ROE" title="">                </div>                <div class="image-caption">ROE</div>            </figure><h3 id="Application"><a href="#Application" class="headerlink" title="Application"></a>Application</h3><p>A high ROE usually indicates that a company performs well in utilizing shareholders’ equity, making it an important indicator for investors to evaluate a company’s profitability.</p><h2 id="PB-—-Price-to-Book-Ratio"><a href="#PB-—-Price-to-Book-Ratio" class="headerlink" title="PB — Price-to-Book Ratio"></a>PB — Price-to-Book Ratio</h2><p>A financial indicator used to evaluate a company’s stock value, reflecting the market’s valuation of a company’s net assets per share.</p><p><mjx-container class="MathJax" jax="SVG" overflow="overflow"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="3.423ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 1513 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math" data-latex="PB = \frac{\text{Share Price}}{\text{Book Value per Share}}"><g data-mml-node="mi" data-latex="P"><path data-c="1D443" d="M555 683L235 683C212 683 201 682 201 660C201 649 212 644 234 644C254 644 294 647 294 631C294 629 293 623 290 613L158 82C152 60 142 47 128 42C121 40 103 39 72 39C50 39 40 37 40 16C40 5 46 0 59 0L184 3L248 2C259 2 299 0 312 0C328 0 336 8 336 24C336 34 325 39 304 39C264 39 244 43 244 52C244 52 245 55 247 68L307 312L472 312C537 312 599 332 657 371C722 414 754 467 754 530C754 629 660 683 555 683M524 644C611 644 654 614 654 554C654 498 626 423 597 397C559 363 509 346 447 346L313 346L379 610C387 644 387 644 429 644Z"></path></g><g data-mml-node="mi" data-latex="B" transform="translate(754,0)"><path data-c="1D435" d="M756 543C756 634 666 683 568 683L236 683C213 683 203 682 203 659C203 643 223 644 235 644C265 643 283 641 288 640C293 639 295 636 295 631C295 629 294 623 291 613L159 82C154 60 144 47 129 42C122 40 104 39 73 39C52 39 42 31 42 15C42 5 52 0 73 0L426 0C491 0 551 20 608 59C671 102 703 155 703 217C703 296 638 344 567 358C655 379 756 446 756 543M554 644C623 644 658 612 658 547C658 496 638 454 596 420C554 386 507 370 456 370L317 370L377 610C385 644 385 644 427 644M582 299C596 276 603 253 603 228C603 176 583 131 542 94C501 57 455 39 402 39L267 39C257 39 250 39 246 40C240 40 237 42 237 45C237 46 237 49 238 53C269 180 293 276 309 340L493 340C536 340 565 326 582 299Z"></path></g></g></g></svg><mjx-break size="4"> </mjx-break><svg style="vertical-align: -1.091ex;" xmlns="http://www.w3.org/2000/svg" width="18.742ex" height="3.11ex" role="img" focusable="false" viewBox="0 -892.5 8284 1374.7"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math" data-latex="PB = \frac{\text{Share Price}}{\text{Book Value per Share}}"><g data-mml-node="mo" data-latex="="><path data-c="3D" d="M698 367L80 367C64 367 56 359 56 344C56 329 64 321 80 321L698 321C714 321 722 329 722 344C722 356 711 367 698 367M698 179L80 179C64 179 56 171 56 156C56 141 64 133 80 133L698 133C714 133 722 141 722 156C722 169 711 179 698 179Z"></path></g><g data-mml-node="mfrac" data-latex="\frac{\text{Share Price}}{\text{Book Value per Share}}" transform="translate(1055.8,0)"><g data-mml-node="mtext" transform="translate(1839.6,394) scale(0.707)" data-latex="\text{Share Price}"><path data-c="53" d="M252 669C359 669 422 601 440 466C441 456 447 451 456 451C467 451 472 460 472 478L472 677C472 696 467 705 458 705C452 705 446 700 440 690L408 638C364 680 324 705 251 705C142 705 56 617 56 509C56 417 116 349 187 324L317 292C384 275 428 229 428 154C428 79 376 17 301 17C170 17 93 91 88 218C88 227 83 232 73 232C62 232 56 223 56 204L56 6C56-13 61-22 71-22C77-22 83-17 89-7L121 45C164 0 224-22 302-22C359-22 407-1 444 40C481 81 499 130 499 188C499 235 485 278 458 317C431 356 394 380 349 391L223 422C170 434 127 481 127 544C127 614 182 669 252 669Z"></path><path data-c="68" d="M314 415C359 415 382 380 382 309L382 79C382 60 379 48 372 44C365 40 343 39 306 39L306 0L421 3L535 0L535 39C503 39 482 40 472 43C462 46 458 53 458 64L458 253C458 301 457 334 454 353C444 414 399 445 320 445C259 445 213 417 182 361L182 694L32 683L32 645C68 645 90 642 98 636C106 630 109 616 109 593L109 79C109 60 105 48 98 44C91 40 69 39 32 39L32 0L147 3L261 0L261 39C224 39 203 40 196 44C189 48 185 60 185 79L185 261C185 342 236 415 314 415Z" transform="translate(556,0)"></path><path data-c="61" d="M483 91L483 150L451 150L451 91C451 52 440 32 419 32C399 32 387 57 387 77L387 274C387 309 383 335 376 353C352 412 283 448 213 448C136 448 60 405 60 333C60 300 77 283 110 283C143 283 159 299 159 332C159 361 144 378 113 381C135 406 168 419 211 419C273 419 311 362 311 297L311 264C233 259 174 247 133 228C66 197 32 154 32 97C32 71 42 49 61 32C93 3 137-11 193-11C252-11 294 14 320 65C327 27 355-6 398-6C451-6 483 36 483 91M200 18C154 18 116 52 116 98C116 191 213 231 311 236L311 141C311 74 267 18 200 18Z" transform="translate(1112,0)"></path><path data-c="72" d="M364 378C364 417 327 442 288 442C237 442 198 412 172 351L172 442L28 431L28 393C63 393 84 390 92 384C100 378 104 365 104 342L104 79C104 60 101 48 94 44C87 40 65 38 28 38L28 0L143 3C185 4 228 3 271 0L271 38L247 38C214 38 193 41 186 46C179 51 176 63 176 81L176 232C176 275 184 314 199 347C219 390 248 412 287 413C277 403 272 391 272 377C272 346 287 331 318 331C345 331 364 352 364 378Z" transform="translate(1612,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(2004,0)"></path><path data-c="20" d="" transform="translate(2448,0)"></path><path data-c="50" d="M395 312C452 312 503 328 548 360C599 396 624 441 624 495C624 552 598 599 546 635C500 667 447 683 387 683L35 683L35 644L63 644C98 644 119 641 126 636C133 631 137 620 137 602L137 81C137 63 133 51 126 46C119 41 98 39 63 39L35 39L35 0C61 2 111 3 184 3C258 3 308 2 334 0L334 39L306 39C271 39 250 41 243 46C236 51 232 63 232 81L232 312M274 644L362 644C391 644 416 640 437 633C494 612 514 570 514 495C514 467 511 443 504 423C487 373 433 346 362 346L229 346L229 609C229 645 235 644 274 644Z" transform="translate(2780,0)"></path><path data-c="72" d="M364 378C364 417 327 442 288 442C237 442 198 412 172 351L172 442L28 431L28 393C63 393 84 390 92 384C100 378 104 365 104 342L104 79C104 60 101 48 94 44C87 40 65 38 28 38L28 0L143 3C185 4 228 3 271 0L271 38L247 38C214 38 193 41 186 46C179 51 176 63 176 81L176 232C176 275 184 314 199 347C219 390 248 412 287 413C277 403 272 391 272 377C272 346 287 331 318 331C345 331 364 352 364 378Z" transform="translate(3461,0)"></path><path data-c="69" d="M194 601C194 631 169 657 139 657C109 657 83 631 83 601C83 571 108 544 138 544C169 544 194 570 194 601M143 3L247 0L247 39C214 39 194 41 188 45C182 49 180 60 180 78L180 445L37 433L37 395C70 395 91 392 98 387C105 382 108 368 108 345L108 79C108 60 105 48 98 44C91 40 69 39 33 39L33 0Z" transform="translate(3853,0)"></path><path data-c="63" d="M251 416C293 416 325 406 347 386C323 383 306 361 306 338C306 305 322 289 355 289C388 289 404 306 404 339C404 410 327 448 250 448C188 448 137 426 96 380C55 334 34 279 34 216C34 155 55 101 96 56C137 11 187-11 248-11C298-11 337 3 365 32C388 55 403 78 411 102C414 111 415 117 415 121C415 130 409 134 398 134C390 134 385 130 382 122C361 55 320 21 257 21C228 21 200 34 172 61C139 92 123 144 123 218C123 318 161 416 251 416Z" transform="translate(4131,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(4575,0)"></path></g><g data-mml-node="mtext" transform="translate(220,-345) scale(0.707)" data-latex="\text{Book Value per Share}"><path data-c="42" d="M478 358C548 376 624 432 624 512C624 565 599 607 549 640C506 669 456 683 401 683L36 683L36 644L64 644C99 644 120 641 127 636C134 631 138 620 138 602L138 81C138 63 134 51 127 46C120 41 99 39 64 39L36 39L36 0L427 0C484 0 535 16 579 48C627 83 651 129 651 185C651 278 563 342 478 358M521 512C521 428 451 370 367 370L228 370L228 609C228 645 233 644 272 644L394 644C468 644 521 586 521 512M544 186L544 160C542 137 529 111 504 84C478 54 442 39 395 39L272 39C233 39 228 38 228 74L228 340L409 340C490 340 544 270 544 186Z"></path><path data-c="6F" d="M249-11C311-11 363 11 406 55C449 99 471 152 471 214C471 277 450 332 408 378C366 424 313 448 250 448C187 448 135 424 92 378C49 332 28 277 28 214C28 152 49 99 92 55C135 11 188-11 249-11M250 21C202 21 166 42 141 85C125 113 117 159 117 222C117 283 125 327 140 355C164 398 200 419 249 419C296 419 332 398 357 357C374 329 382 284 382 222C382 106 348 21 250 21Z" transform="translate(708,0)"></path><path data-c="6F" d="M249-11C311-11 363 11 406 55C449 99 471 152 471 214C471 277 450 332 408 378C366 424 313 448 250 448C187 448 135 424 92 378C49 332 28 277 28 214C28 152 49 99 92 55C135 11 188-11 249-11M250 21C202 21 166 42 141 85C125 113 117 159 117 222C117 283 125 327 140 355C164 398 200 419 249 419C296 419 332 398 357 357C374 329 382 284 382 222C382 106 348 21 250 21Z" transform="translate(1208,0)"></path><path data-c="6B" d="M422 3C442 4 472 3 511 0L511 39C487 39 469 42 457 47C445 52 431 66 415 89L290 266C287 275 285 278 285 275C285 276 295 285 316 303C351 334 377 354 394 365C425 384 456 394 488 396L488 434C467 432 443 431 416 431C370 431 332 432 301 434L301 396C320 395 330 388 330 375C330 363 323 351 310 339L178 224L178 694L28 683L28 645C64 645 86 642 94 636C102 630 105 616 105 593L105 79C105 60 101 48 94 44C87 40 65 39 28 39L28 0L139 3L251 0L251 39C214 39 193 40 186 44C179 48 175 60 175 79L175 179L233 230L249 206C314 116 347 66 347 57C347 45 335 39 311 39L311 0Z" transform="translate(1708,0)"></path><path data-c="20" d="" transform="translate(2236,0)"></path><path data-c="56" d="M622 581C638 622 674 643 730 644L730 683C687 680 654 679 630 680L513 683L513 645C562 644 586 628 586 599C586 595 584 589 581 581L404 114L218 603C215 613 213 619 213 620C213 636 240 644 294 644L294 683C252 680 204 679 149 680L19 683L19 644C56 644 80 642 91 639C102 636 110 626 116 609L346 2C352-14 361-22 374-22C387-22 396-15 401-1Z" transform="translate(2568,0)"></path><path data-c="61" d="M483 91L483 150L451 150L451 91C451 52 440 32 419 32C399 32 387 57 387 77L387 274C387 309 383 335 376 353C352 412 283 448 213 448C136 448 60 405 60 333C60 300 77 283 110 283C143 283 159 299 159 332C159 361 144 378 113 381C135 406 168 419 211 419C273 419 311 362 311 297L311 264C233 259 174 247 133 228C66 197 32 154 32 97C32 71 42 49 61 32C93 3 137-11 193-11C252-11 294 14 320 65C327 27 355-6 398-6C451-6 483 36 483 91M200 18C154 18 116 52 116 98C116 191 213 231 311 236L311 141C311 74 267 18 200 18Z" transform="translate(3318,0)"></path><path data-c="6C" d="M144 3L255 0L255 39C219 39 197 40 190 44C183 48 180 60 180 79L180 694L33 683L33 645C68 645 90 642 97 636C104 630 108 616 108 593L108 79C108 60 105 48 98 44C91 40 69 39 33 39L33 0Z" transform="translate(3818,0)"></path><path data-c="75" d="M267 18C198 18 185 47 185 112L185 442L32 431L32 393C71 393 94 389 100 382C106 375 109 350 109 306L109 159C109 120 112 90 119 67C134 15 189-11 262-11C316-11 357 14 385 65L385-11L535 0L535 38C499 38 478 40 470 46C462 52 458 66 458 89L458 442L306 431L306 393C341 393 362 390 370 384C378 378 382 365 382 342L382 167C382 88 340 18 267 18Z" transform="translate(4096,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(4652,0)"></path><path data-c="20" d="" transform="translate(5096,0)"></path><path data-c="70" d="M298-13C360-13 413 9 456 54C499 99 521 152 521 215C521 276 501 328 462 372C421 419 371 442 311 442C260 442 216 422 178 383L178 442L28 431L28 393C65 393 87 391 94 386C101 381 105 368 105 347L105-115C105-134 101-146 94-150C87-154 65-155 28-155L28-194L142-191L257-194L257-155C220-155 199-154 192-150C185-146 181-134 181-115L181 44C203 13 246-13 298-13M304 409C345 409 377 386 402 340C421 301 431 260 431 215C431 168 420 125 398 86C372 39 337 16 294 16C252 16 219 36 194 75C185 88 181 101 181 114L181 333C210 384 251 409 304 409Z" transform="translate(5428,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(5984,0)"></path><path data-c="72" d="M364 378C364 417 327 442 288 442C237 442 198 412 172 351L172 442L28 431L28 393C63 393 84 390 92 384C100 378 104 365 104 342L104 79C104 60 101 48 94 44C87 40 65 38 28 38L28 0L143 3C185 4 228 3 271 0L271 38L247 38C214 38 193 41 186 46C179 51 176 63 176 81L176 232C176 275 184 314 199 347C219 390 248 412 287 413C277 403 272 391 272 377C272 346 287 331 318 331C345 331 364 352 364 378Z" transform="translate(6428,0)"></path><path data-c="20" d="" transform="translate(6820,0)"></path><path data-c="53" d="M252 669C359 669 422 601 440 466C441 456 447 451 456 451C467 451 472 460 472 478L472 677C472 696 467 705 458 705C452 705 446 700 440 690L408 638C364 680 324 705 251 705C142 705 56 617 56 509C56 417 116 349 187 324L317 292C384 275 428 229 428 154C428 79 376 17 301 17C170 17 93 91 88 218C88 227 83 232 73 232C62 232 56 223 56 204L56 6C56-13 61-22 71-22C77-22 83-17 89-7L121 45C164 0 224-22 302-22C359-22 407-1 444 40C481 81 499 130 499 188C499 235 485 278 458 317C431 356 394 380 349 391L223 422C170 434 127 481 127 544C127 614 182 669 252 669Z" transform="translate(7152,0)"></path><path data-c="68" d="M314 415C359 415 382 380 382 309L382 79C382 60 379 48 372 44C365 40 343 39 306 39L306 0L421 3L535 0L535 39C503 39 482 40 472 43C462 46 458 53 458 64L458 253C458 301 457 334 454 353C444 414 399 445 320 445C259 445 213 417 182 361L182 694L32 683L32 645C68 645 90 642 98 636C106 630 109 616 109 593L109 79C109 60 105 48 98 44C91 40 69 39 32 39L32 0L147 3L261 0L261 39C224 39 203 40 196 44C189 48 185 60 185 79L185 261C185 342 236 415 314 415Z" transform="translate(7708,0)"></path><path data-c="61" d="M483 91L483 150L451 150L451 91C451 52 440 32 419 32C399 32 387 57 387 77L387 274C387 309 383 335 376 353C352 412 283 448 213 448C136 448 60 405 60 333C60 300 77 283 110 283C143 283 159 299 159 332C159 361 144 378 113 381C135 406 168 419 211 419C273 419 311 362 311 297L311 264C233 259 174 247 133 228C66 197 32 154 32 97C32 71 42 49 61 32C93 3 137-11 193-11C252-11 294 14 320 65C327 27 355-6 398-6C451-6 483 36 483 91M200 18C154 18 116 52 116 98C116 191 213 231 311 236L311 141C311 74 267 18 200 18Z" transform="translate(8264,0)"></path><path data-c="72" d="M364 378C364 417 327 442 288 442C237 442 198 412 172 351L172 442L28 431L28 393C63 393 84 390 92 384C100 378 104 365 104 342L104 79C104 60 101 48 94 44C87 40 65 38 28 38L28 0L143 3C185 4 228 3 271 0L271 38L247 38C214 38 193 41 186 46C179 51 176 63 176 81L176 232C176 275 184 314 199 347C219 390 248 412 287 413C277 403 272 391 272 377C272 346 287 331 318 331C345 331 364 352 364 378Z" transform="translate(8764,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(9156,0)"></path></g><rect width="6988.2" height="60" x="120" y="220"></rect></g></g></g></svg></mjx-container></p><p>The net assets per share are calculated as follows:</p><p><mjx-container class="MathJax" jax="SVG" overflow="overflow"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="21.719ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 9600 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math" data-latex="\text{Book Value per Share} = \frac{\text{Total Assets} - \text{Total Liabilities}}{\text{Total Shares Outstanding}}"><g data-mml-node="mtext" data-latex="\text{Book Value per Share}"><path data-c="42" d="M478 358C548 376 624 432 624 512C624 565 599 607 549 640C506 669 456 683 401 683L36 683L36 644L64 644C99 644 120 641 127 636C134 631 138 620 138 602L138 81C138 63 134 51 127 46C120 41 99 39 64 39L36 39L36 0L427 0C484 0 535 16 579 48C627 83 651 129 651 185C651 278 563 342 478 358M521 512C521 428 451 370 367 370L228 370L228 609C228 645 233 644 272 644L394 644C468 644 521 586 521 512M544 186L544 160C542 137 529 111 504 84C478 54 442 39 395 39L272 39C233 39 228 38 228 74L228 340L409 340C490 340 544 270 544 186Z"></path><path data-c="6F" d="M249-11C311-11 363 11 406 55C449 99 471 152 471 214C471 277 450 332 408 378C366 424 313 448 250 448C187 448 135 424 92 378C49 332 28 277 28 214C28 152 49 99 92 55C135 11 188-11 249-11M250 21C202 21 166 42 141 85C125 113 117 159 117 222C117 283 125 327 140 355C164 398 200 419 249 419C296 419 332 398 357 357C374 329 382 284 382 222C382 106 348 21 250 21Z" transform="translate(708,0)"></path><path data-c="6F" d="M249-11C311-11 363 11 406 55C449 99 471 152 471 214C471 277 450 332 408 378C366 424 313 448 250 448C187 448 135 424 92 378C49 332 28 277 28 214C28 152 49 99 92 55C135 11 188-11 249-11M250 21C202 21 166 42 141 85C125 113 117 159 117 222C117 283 125 327 140 355C164 398 200 419 249 419C296 419 332 398 357 357C374 329 382 284 382 222C382 106 348 21 250 21Z" transform="translate(1208,0)"></path><path data-c="6B" d="M422 3C442 4 472 3 511 0L511 39C487 39 469 42 457 47C445 52 431 66 415 89L290 266C287 275 285 278 285 275C285 276 295 285 316 303C351 334 377 354 394 365C425 384 456 394 488 396L488 434C467 432 443 431 416 431C370 431 332 432 301 434L301 396C320 395 330 388 330 375C330 363 323 351 310 339L178 224L178 694L28 683L28 645C64 645 86 642 94 636C102 630 105 616 105 593L105 79C105 60 101 48 94 44C87 40 65 39 28 39L28 0L139 3L251 0L251 39C214 39 193 40 186 44C179 48 175 60 175 79L175 179L233 230L249 206C314 116 347 66 347 57C347 45 335 39 311 39L311 0Z" transform="translate(1708,0)"></path><path data-c="20" d="" transform="translate(2236,0)"></path><path data-c="56" d="M622 581C638 622 674 643 730 644L730 683C687 680 654 679 630 680L513 683L513 645C562 644 586 628 586 599C586 595 584 589 581 581L404 114L218 603C215 613 213 619 213 620C213 636 240 644 294 644L294 683C252 680 204 679 149 680L19 683L19 644C56 644 80 642 91 639C102 636 110 626 116 609L346 2C352-14 361-22 374-22C387-22 396-15 401-1Z" transform="translate(2568,0)"></path><path data-c="61" d="M483 91L483 150L451 150L451 91C451 52 440 32 419 32C399 32 387 57 387 77L387 274C387 309 383 335 376 353C352 412 283 448 213 448C136 448 60 405 60 333C60 300 77 283 110 283C143 283 159 299 159 332C159 361 144 378 113 381C135 406 168 419 211 419C273 419 311 362 311 297L311 264C233 259 174 247 133 228C66 197 32 154 32 97C32 71 42 49 61 32C93 3 137-11 193-11C252-11 294 14 320 65C327 27 355-6 398-6C451-6 483 36 483 91M200 18C154 18 116 52 116 98C116 191 213 231 311 236L311 141C311 74 267 18 200 18Z" transform="translate(3318,0)"></path><path data-c="6C" d="M144 3L255 0L255 39C219 39 197 40 190 44C183 48 180 60 180 79L180 694L33 683L33 645C68 645 90 642 97 636C104 630 108 616 108 593L108 79C108 60 105 48 98 44C91 40 69 39 33 39L33 0Z" transform="translate(3818,0)"></path><path data-c="75" d="M267 18C198 18 185 47 185 112L185 442L32 431L32 393C71 393 94 389 100 382C106 375 109 350 109 306L109 159C109 120 112 90 119 67C134 15 189-11 262-11C316-11 357 14 385 65L385-11L535 0L535 38C499 38 478 40 470 46C462 52 458 66 458 89L458 442L306 431L306 393C341 393 362 390 370 384C378 378 382 365 382 342L382 167C382 88 340 18 267 18Z" transform="translate(4096,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(4652,0)"></path><path data-c="20" d="" transform="translate(5096,0)"></path><path data-c="70" d="M298-13C360-13 413 9 456 54C499 99 521 152 521 215C521 276 501 328 462 372C421 419 371 442 311 442C260 442 216 422 178 383L178 442L28 431L28 393C65 393 87 391 94 386C101 381 105 368 105 347L105-115C105-134 101-146 94-150C87-154 65-155 28-155L28-194L142-191L257-194L257-155C220-155 199-154 192-150C185-146 181-134 181-115L181 44C203 13 246-13 298-13M304 409C345 409 377 386 402 340C421 301 431 260 431 215C431 168 420 125 398 86C372 39 337 16 294 16C252 16 219 36 194 75C185 88 181 101 181 114L181 333C210 384 251 409 304 409Z" transform="translate(5428,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(5984,0)"></path><path data-c="72" d="M364 378C364 417 327 442 288 442C237 442 198 412 172 351L172 442L28 431L28 393C63 393 84 390 92 384C100 378 104 365 104 342L104 79C104 60 101 48 94 44C87 40 65 38 28 38L28 0L143 3C185 4 228 3 271 0L271 38L247 38C214 38 193 41 186 46C179 51 176 63 176 81L176 232C176 275 184 314 199 347C219 390 248 412 287 413C277 403 272 391 272 377C272 346 287 331 318 331C345 331 364 352 364 378Z" transform="translate(6428,0)"></path><path data-c="20" d="" transform="translate(6820,0)"></path><path data-c="53" d="M252 669C359 669 422 601 440 466C441 456 447 451 456 451C467 451 472 460 472 478L472 677C472 696 467 705 458 705C452 705 446 700 440 690L408 638C364 680 324 705 251 705C142 705 56 617 56 509C56 417 116 349 187 324L317 292C384 275 428 229 428 154C428 79 376 17 301 17C170 17 93 91 88 218C88 227 83 232 73 232C62 232 56 223 56 204L56 6C56-13 61-22 71-22C77-22 83-17 89-7L121 45C164 0 224-22 302-22C359-22 407-1 444 40C481 81 499 130 499 188C499 235 485 278 458 317C431 356 394 380 349 391L223 422C170 434 127 481 127 544C127 614 182 669 252 669Z" transform="translate(7152,0)"></path><path data-c="68" d="M314 415C359 415 382 380 382 309L382 79C382 60 379 48 372 44C365 40 343 39 306 39L306 0L421 3L535 0L535 39C503 39 482 40 472 43C462 46 458 53 458 64L458 253C458 301 457 334 454 353C444 414 399 445 320 445C259 445 213 417 182 361L182 694L32 683L32 645C68 645 90 642 98 636C106 630 109 616 109 593L109 79C109 60 105 48 98 44C91 40 69 39 32 39L32 0L147 3L261 0L261 39C224 39 203 40 196 44C189 48 185 60 185 79L185 261C185 342 236 415 314 415Z" transform="translate(7708,0)"></path><path data-c="61" d="M483 91L483 150L451 150L451 91C451 52 440 32 419 32C399 32 387 57 387 77L387 274C387 309 383 335 376 353C352 412 283 448 213 448C136 448 60 405 60 333C60 300 77 283 110 283C143 283 159 299 159 332C159 361 144 378 113 381C135 406 168 419 211 419C273 419 311 362 311 297L311 264C233 259 174 247 133 228C66 197 32 154 32 97C32 71 42 49 61 32C93 3 137-11 193-11C252-11 294 14 320 65C327 27 355-6 398-6C451-6 483 36 483 91M200 18C154 18 116 52 116 98C116 191 213 231 311 236L311 141C311 74 267 18 200 18Z" transform="translate(8264,0)"></path><path data-c="72" d="M364 378C364 417 327 442 288 442C237 442 198 412 172 351L172 442L28 431L28 393C63 393 84 390 92 384C100 378 104 365 104 342L104 79C104 60 101 48 94 44C87 40 65 38 28 38L28 0L143 3C185 4 228 3 271 0L271 38L247 38C214 38 193 41 186 46C179 51 176 63 176 81L176 232C176 275 184 314 199 347C219 390 248 412 287 413C277 403 272 391 272 377C272 346 287 331 318 331C345 331 364 352 364 378Z" transform="translate(8764,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(9156,0)"></path></g></g></g></svg><mjx-break size="4"> </mjx-break><svg style="vertical-align: -1.11ex;" xmlns="http://www.w3.org/2000/svg" width="24.634ex" height="3.158ex" role="img" focusable="false" viewBox="0 -905 10888.3 1395.6"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math" data-latex="\text{Book Value per Share} = \frac{\text{Total Assets} - \text{Total Liabilities}}{\text{Total Shares Outstanding}}"><g data-mml-node="mo" data-latex="="><path data-c="3D" d="M698 367L80 367C64 367 56 359 56 344C56 329 64 321 80 321L698 321C714 321 722 329 722 344C722 356 711 367 698 367M698 179L80 179C64 179 56 171 56 156C56 141 64 133 80 133L698 133C714 133 722 141 722 156C722 169 711 179 698 179Z"></path></g><g data-mml-node="mfrac" data-latex="\frac{\text{Total Assets} - \text{Total Liabilities}}{\text{Total Shares Outstanding}}" transform="translate(1055.8,0)"><g data-mml-node="mrow" transform="translate(220,398.7) scale(0.707)" data-latex="\text{Total Assets} - \text{Total Liabilities}"><g data-mml-node="mtext" data-latex="\text{Total Assets}"><path data-c="54" d="M653 447L685 447L666 677L55 677L36 447L68 447C75 513 78 572 106 605C132 635 181 638 242 638C271 638 289 638 295 637C313 633 313 624 313 603L313 82C313 69 312 61 309 56C303 45 271 39 213 39L169 39L169 0C199 2 263 3 360 3C458 3 522 2 552 0L552 39L508 39C450 39 418 45 412 56C409 61 408 69 408 82L408 603C408 614 410 623 412 629C414 635 436 638 479 638C540 638 588 635 614 605C642 572 646 513 653 447Z"></path><path data-c="6F" d="M249-11C311-11 363 11 406 55C449 99 471 152 471 214C471 277 450 332 408 378C366 424 313 448 250 448C187 448 135 424 92 378C49 332 28 277 28 214C28 152 49 99 92 55C135 11 188-11 249-11M250 21C202 21 166 42 141 85C125 113 117 159 117 222C117 283 125 327 140 355C164 398 200 419 249 419C296 419 332 398 357 357C374 329 382 284 382 222C382 106 348 21 250 21Z" transform="translate(722,0)"></path><path data-c="74" d="M332 126L332 186L300 186L300 128C300 78 282 22 238 22C197 22 177 56 177 124L177 395L316 395L316 433L177 433L177 615L145 615C144 582 141 553 134 527C117 461 78 427 19 424L19 395L102 395L102 126C102 67 120 29 155 10C182-4 207-11 232-11C298-11 332 55 332 126Z" transform="translate(1222,0)"></path><path data-c="61" d="M483 91L483 150L451 150L451 91C451 52 440 32 419 32C399 32 387 57 387 77L387 274C387 309 383 335 376 353C352 412 283 448 213 448C136 448 60 405 60 333C60 300 77 283 110 283C143 283 159 299 159 332C159 361 144 378 113 381C135 406 168 419 211 419C273 419 311 362 311 297L311 264C233 259 174 247 133 228C66 197 32 154 32 97C32 71 42 49 61 32C93 3 137-11 193-11C252-11 294 14 320 65C327 27 355-6 398-6C451-6 483 36 483 91M200 18C154 18 116 52 116 98C116 191 213 231 311 236L311 141C311 74 267 18 200 18Z" transform="translate(1611,0)"></path><path data-c="6C" d="M144 3L255 0L255 39C219 39 197 40 190 44C183 48 180 60 180 79L180 694L33 683L33 645C68 645 90 642 97 636C104 630 108 616 108 593L108 79C108 60 105 48 98 44C91 40 69 39 33 39L33 0Z" transform="translate(2111,0)"></path><path data-c="20" d="" transform="translate(2389,0)"></path><path data-c="41" d="M583 3L717 0L717 39L695 39C670 39 653 40 646 42C631 47 621 58 615 75L401 694C397 707 390 716 375 716C362 716 353 709 348 694L143 102C129 61 92 39 32 38L32 0L137 3L254 0L254 39C205 39 180 54 180 85C180 92 181 96 182 98L227 225L465 225L516 77C518 71 519 67 519 64C519 47 492 39 437 39L437 0C479 3 528 4 583 3M345 569L451 264L240 264Z" transform="translate(2721,0)"></path><path data-c="73" d="M360 130C360 206 308 254 205 274C158 283 128 294 112 305C96 316 88 332 88 351C88 398 123 421 193 421C261 421 298 383 303 306C304 298 309 294 319 294C330 294 336 303 336 321L336 420C336 439 331 448 321 448C310 448 295 430 285 422C260 439 229 448 193 448C104 448 33 408 33 323C33 289 47 260 76 237C111 208 144 202 207 191C272 178 305 149 305 104C305 47 270 18 199 18C130 18 86 64 67 155C64 168 59 175 50 175C39 175 33 165 33 146L33 17C33-2 38-11 48-11C51-11 56-8 64-1C82 15 74 12 92 30C121 3 157-11 199-11C294-11 360 38 360 130Z" transform="translate(3471,0)"></path><path data-c="73" d="M360 130C360 206 308 254 205 274C158 283 128 294 112 305C96 316 88 332 88 351C88 398 123 421 193 421C261 421 298 383 303 306C304 298 309 294 319 294C330 294 336 303 336 321L336 420C336 439 331 448 321 448C310 448 295 430 285 422C260 439 229 448 193 448C104 448 33 408 33 323C33 289 47 260 76 237C111 208 144 202 207 191C272 178 305 149 305 104C305 47 270 18 199 18C130 18 86 64 67 155C64 168 59 175 50 175C39 175 33 165 33 146L33 17C33-2 38-11 48-11C51-11 56-8 64-1C82 15 74 12 92 30C121 3 157-11 199-11C294-11 360 38 360 130Z" transform="translate(3865,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(4259,0)"></path><path data-c="74" d="M332 126L332 186L300 186L300 128C300 78 282 22 238 22C197 22 177 56 177 124L177 395L316 395L316 433L177 433L177 615L145 615C144 582 141 553 134 527C117 461 78 427 19 424L19 395L102 395L102 126C102 67 120 29 155 10C182-4 207-11 232-11C298-11 332 55 332 126Z" transform="translate(4703,0)"></path><path data-c="73" d="M360 130C360 206 308 254 205 274C158 283 128 294 112 305C96 316 88 332 88 351C88 398 123 421 193 421C261 421 298 383 303 306C304 298 309 294 319 294C330 294 336 303 336 321L336 420C336 439 331 448 321 448C310 448 295 430 285 422C260 439 229 448 193 448C104 448 33 408 33 323C33 289 47 260 76 237C111 208 144 202 207 191C272 178 305 149 305 104C305 47 270 18 199 18C130 18 86 64 67 155C64 168 59 175 50 175C39 175 33 165 33 146L33 17C33-2 38-11 48-11C51-11 56-8 64-1C82 15 74 12 92 30C121 3 157-11 199-11C294-11 360 38 360 130Z" transform="translate(5092,0)"></path></g><g data-mml-node="mo" data-latex="-" transform="translate(5486,0)"><path data-c="2212" d="M698 270L80 270C64 270 56 263 56 250C56 237 64 230 80 230L698 230C714 230 722 237 722 250C722 262 710 270 698 270Z"></path></g><g data-mml-node="mtext" data-latex="\text{Total Liabilities}" transform="translate(6264,0)"><path data-c="54" d="M653 447L685 447L666 677L55 677L36 447L68 447C75 513 78 572 106 605C132 635 181 638 242 638C271 638 289 638 295 637C313 633 313 624 313 603L313 82C313 69 312 61 309 56C303 45 271 39 213 39L169 39L169 0C199 2 263 3 360 3C458 3 522 2 552 0L552 39L508 39C450 39 418 45 412 56C409 61 408 69 408 82L408 603C408 614 410 623 412 629C414 635 436 638 479 638C540 638 588 635 614 605C642 572 646 513 653 447Z"></path><path data-c="6F" d="M249-11C311-11 363 11 406 55C449 99 471 152 471 214C471 277 450 332 408 378C366 424 313 448 250 448C187 448 135 424 92 378C49 332 28 277 28 214C28 152 49 99 92 55C135 11 188-11 249-11M250 21C202 21 166 42 141 85C125 113 117 159 117 222C117 283 125 327 140 355C164 398 200 419 249 419C296 419 332 398 357 357C374 329 382 284 382 222C382 106 348 21 250 21Z" transform="translate(722,0)"></path><path data-c="74" d="M332 126L332 186L300 186L300 128C300 78 282 22 238 22C197 22 177 56 177 124L177 395L316 395L316 433L177 433L177 615L145 615C144 582 141 553 134 527C117 461 78 427 19 424L19 395L102 395L102 126C102 67 120 29 155 10C182-4 207-11 232-11C298-11 332 55 332 126Z" transform="translate(1222,0)"></path><path data-c="61" d="M483 91L483 150L451 150L451 91C451 52 440 32 419 32C399 32 387 57 387 77L387 274C387 309 383 335 376 353C352 412 283 448 213 448C136 448 60 405 60 333C60 300 77 283 110 283C143 283 159 299 159 332C159 361 144 378 113 381C135 406 168 419 211 419C273 419 311 362 311 297L311 264C233 259 174 247 133 228C66 197 32 154 32 97C32 71 42 49 61 32C93 3 137-11 193-11C252-11 294 14 320 65C327 27 355-6 398-6C451-6 483 36 483 91M200 18C154 18 116 52 116 98C116 191 213 231 311 236L311 141C311 74 267 18 200 18Z" transform="translate(1611,0)"></path><path data-c="6C" d="M144 3L255 0L255 39C219 39 197 40 190 44C183 48 180 60 180 79L180 694L33 683L33 645C68 645 90 642 97 636C104 630 108 616 108 593L108 79C108 60 105 48 98 44C91 40 69 39 33 39L33 0Z" transform="translate(2111,0)"></path><path data-c="20" d="" transform="translate(2389,0)"></path><path data-c="4C" d="M230 74L230 601C230 614 231 622 233 627C238 638 267 644 320 644L356 644L356 683C330 681 274 680 187 680C110 680 59 681 33 683L33 644L61 644C96 644 117 641 124 636C131 631 135 620 135 602L135 81C135 63 131 51 124 46C117 41 96 39 61 39L33 39L33 0L554 0L582 263L550 263C545 212 536 171 525 139C503 76 444 39 355 39L274 39C236 39 230 39 230 74Z" transform="translate(2721,0)"></path><path data-c="69" d="M194 601C194 631 169 657 139 657C109 657 83 631 83 601C83 571 108 544 138 544C169 544 194 570 194 601M143 3L247 0L247 39C214 39 194 41 188 45C182 49 180 60 180 78L180 445L37 433L37 395C70 395 91 392 98 387C105 382 108 368 108 345L108 79C108 60 105 48 98 44C91 40 69 39 33 39L33 0Z" transform="translate(3346,0)"></path><path data-c="61" d="M483 91L483 150L451 150L451 91C451 52 440 32 419 32C399 32 387 57 387 77L387 274C387 309 383 335 376 353C352 412 283 448 213 448C136 448 60 405 60 333C60 300 77 283 110 283C143 283 159 299 159 332C159 361 144 378 113 381C135 406 168 419 211 419C273 419 311 362 311 297L311 264C233 259 174 247 133 228C66 197 32 154 32 97C32 71 42 49 61 32C93 3 137-11 193-11C252-11 294 14 320 65C327 27 355-6 398-6C451-6 483 36 483 91M200 18C154 18 116 52 116 98C116 191 213 231 311 236L311 141C311 74 267 18 200 18Z" transform="translate(3624,0)"></path><path data-c="62" d="M298-11C360-11 413 11 456 56C499 101 521 154 521 217C521 278 501 332 460 377C419 422 369 445 308 445C259 445 215 426 178 387L178 694L28 683L28 645C64 645 86 642 94 636C102 630 105 616 105 593L105 0L136 0L169 58C201 12 244-11 298-11M294 19C253 19 219 38 194 77C185 90 181 104 181 117L181 320C181 333 184 344 191 354C220 395 257 415 304 415C345 415 378 395 404 354C422 324 431 279 431 218C431 110 393 19 294 19Z" transform="translate(4124,0)"></path><path data-c="69" d="M194 601C194 631 169 657 139 657C109 657 83 631 83 601C83 571 108 544 138 544C169 544 194 570 194 601M143 3L247 0L247 39C214 39 194 41 188 45C182 49 180 60 180 78L180 445L37 433L37 395C70 395 91 392 98 387C105 382 108 368 108 345L108 79C108 60 105 48 98 44C91 40 69 39 33 39L33 0Z" transform="translate(4680,0)"></path><path data-c="6C" d="M144 3L255 0L255 39C219 39 197 40 190 44C183 48 180 60 180 79L180 694L33 683L33 645C68 645 90 642 97 636C104 630 108 616 108 593L108 79C108 60 105 48 98 44C91 40 69 39 33 39L33 0Z" transform="translate(4958,0)"></path><path data-c="69" d="M194 601C194 631 169 657 139 657C109 657 83 631 83 601C83 571 108 544 138 544C169 544 194 570 194 601M143 3L247 0L247 39C214 39 194 41 188 45C182 49 180 60 180 78L180 445L37 433L37 395C70 395 91 392 98 387C105 382 108 368 108 345L108 79C108 60 105 48 98 44C91 40 69 39 33 39L33 0Z" transform="translate(5236,0)"></path><path data-c="74" d="M332 126L332 186L300 186L300 128C300 78 282 22 238 22C197 22 177 56 177 124L177 395L316 395L316 433L177 433L177 615L145 615C144 582 141 553 134 527C117 461 78 427 19 424L19 395L102 395L102 126C102 67 120 29 155 10C182-4 207-11 232-11C298-11 332 55 332 126Z" transform="translate(5514,0)"></path><path data-c="69" d="M194 601C194 631 169 657 139 657C109 657 83 631 83 601C83 571 108 544 138 544C169 544 194 570 194 601M143 3L247 0L247 39C214 39 194 41 188 45C182 49 180 60 180 78L180 445L37 433L37 395C70 395 91 392 98 387C105 382 108 368 108 345L108 79C108 60 105 48 98 44C91 40 69 39 33 39L33 0Z" transform="translate(5903,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(6181,0)"></path><path data-c="73" d="M360 130C360 206 308 254 205 274C158 283 128 294 112 305C96 316 88 332 88 351C88 398 123 421 193 421C261 421 298 383 303 306C304 298 309 294 319 294C330 294 336 303 336 321L336 420C336 439 331 448 321 448C310 448 295 430 285 422C260 439 229 448 193 448C104 448 33 408 33 323C33 289 47 260 76 237C111 208 144 202 207 191C272 178 305 149 305 104C305 47 270 18 199 18C130 18 86 64 67 155C64 168 59 175 50 175C39 175 33 165 33 146L33 17C33-2 38-11 48-11C51-11 56-8 64-1C82 15 74 12 92 30C121 3 157-11 199-11C294-11 360 38 360 130Z" transform="translate(6625,0)"></path></g></g><g data-mml-node="mtext" transform="translate(904.5,-345) scale(0.707)" data-latex="\text{Total Shares Outstanding}"><path data-c="54" d="M653 447L685 447L666 677L55 677L36 447L68 447C75 513 78 572 106 605C132 635 181 638 242 638C271 638 289 638 295 637C313 633 313 624 313 603L313 82C313 69 312 61 309 56C303 45 271 39 213 39L169 39L169 0C199 2 263 3 360 3C458 3 522 2 552 0L552 39L508 39C450 39 418 45 412 56C409 61 408 69 408 82L408 603C408 614 410 623 412 629C414 635 436 638 479 638C540 638 588 635 614 605C642 572 646 513 653 447Z"></path><path data-c="6F" d="M249-11C311-11 363 11 406 55C449 99 471 152 471 214C471 277 450 332 408 378C366 424 313 448 250 448C187 448 135 424 92 378C49 332 28 277 28 214C28 152 49 99 92 55C135 11 188-11 249-11M250 21C202 21 166 42 141 85C125 113 117 159 117 222C117 283 125 327 140 355C164 398 200 419 249 419C296 419 332 398 357 357C374 329 382 284 382 222C382 106 348 21 250 21Z" transform="translate(722,0)"></path><path data-c="74" d="M332 126L332 186L300 186L300 128C300 78 282 22 238 22C197 22 177 56 177 124L177 395L316 395L316 433L177 433L177 615L145 615C144 582 141 553 134 527C117 461 78 427 19 424L19 395L102 395L102 126C102 67 120 29 155 10C182-4 207-11 232-11C298-11 332 55 332 126Z" transform="translate(1222,0)"></path><path data-c="61" d="M483 91L483 150L451 150L451 91C451 52 440 32 419 32C399 32 387 57 387 77L387 274C387 309 383 335 376 353C352 412 283 448 213 448C136 448 60 405 60 333C60 300 77 283 110 283C143 283 159 299 159 332C159 361 144 378 113 381C135 406 168 419 211 419C273 419 311 362 311 297L311 264C233 259 174 247 133 228C66 197 32 154 32 97C32 71 42 49 61 32C93 3 137-11 193-11C252-11 294 14 320 65C327 27 355-6 398-6C451-6 483 36 483 91M200 18C154 18 116 52 116 98C116 191 213 231 311 236L311 141C311 74 267 18 200 18Z" transform="translate(1611,0)"></path><path data-c="6C" d="M144 3L255 0L255 39C219 39 197 40 190 44C183 48 180 60 180 79L180 694L33 683L33 645C68 645 90 642 97 636C104 630 108 616 108 593L108 79C108 60 105 48 98 44C91 40 69 39 33 39L33 0Z" transform="translate(2111,0)"></path><path data-c="20" d="" transform="translate(2389,0)"></path><path data-c="53" d="M252 669C359 669 422 601 440 466C441 456 447 451 456 451C467 451 472 460 472 478L472 677C472 696 467 705 458 705C452 705 446 700 440 690L408 638C364 680 324 705 251 705C142 705 56 617 56 509C56 417 116 349 187 324L317 292C384 275 428 229 428 154C428 79 376 17 301 17C170 17 93 91 88 218C88 227 83 232 73 232C62 232 56 223 56 204L56 6C56-13 61-22 71-22C77-22 83-17 89-7L121 45C164 0 224-22 302-22C359-22 407-1 444 40C481 81 499 130 499 188C499 235 485 278 458 317C431 356 394 380 349 391L223 422C170 434 127 481 127 544C127 614 182 669 252 669Z" transform="translate(2721,0)"></path><path data-c="68" d="M314 415C359 415 382 380 382 309L382 79C382 60 379 48 372 44C365 40 343 39 306 39L306 0L421 3L535 0L535 39C503 39 482 40 472 43C462 46 458 53 458 64L458 253C458 301 457 334 454 353C444 414 399 445 320 445C259 445 213 417 182 361L182 694L32 683L32 645C68 645 90 642 98 636C106 630 109 616 109 593L109 79C109 60 105 48 98 44C91 40 69 39 32 39L32 0L147 3L261 0L261 39C224 39 203 40 196 44C189 48 185 60 185 79L185 261C185 342 236 415 314 415Z" transform="translate(3277,0)"></path><path data-c="61" d="M483 91L483 150L451 150L451 91C451 52 440 32 419 32C399 32 387 57 387 77L387 274C387 309 383 335 376 353C352 412 283 448 213 448C136 448 60 405 60 333C60 300 77 283 110 283C143 283 159 299 159 332C159 361 144 378 113 381C135 406 168 419 211 419C273 419 311 362 311 297L311 264C233 259 174 247 133 228C66 197 32 154 32 97C32 71 42 49 61 32C93 3 137-11 193-11C252-11 294 14 320 65C327 27 355-6 398-6C451-6 483 36 483 91M200 18C154 18 116 52 116 98C116 191 213 231 311 236L311 141C311 74 267 18 200 18Z" transform="translate(3833,0)"></path><path data-c="72" d="M364 378C364 417 327 442 288 442C237 442 198 412 172 351L172 442L28 431L28 393C63 393 84 390 92 384C100 378 104 365 104 342L104 79C104 60 101 48 94 44C87 40 65 38 28 38L28 0L143 3C185 4 228 3 271 0L271 38L247 38C214 38 193 41 186 46C179 51 176 63 176 81L176 232C176 275 184 314 199 347C219 390 248 412 287 413C277 403 272 391 272 377C272 346 287 331 318 331C345 331 364 352 364 378Z" transform="translate(4333,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(4725,0)"></path><path data-c="73" d="M360 130C360 206 308 254 205 274C158 283 128 294 112 305C96 316 88 332 88 351C88 398 123 421 193 421C261 421 298 383 303 306C304 298 309 294 319 294C330 294 336 303 336 321L336 420C336 439 331 448 321 448C310 448 295 430 285 422C260 439 229 448 193 448C104 448 33 408 33 323C33 289 47 260 76 237C111 208 144 202 207 191C272 178 305 149 305 104C305 47 270 18 199 18C130 18 86 64 67 155C64 168 59 175 50 175C39 175 33 165 33 146L33 17C33-2 38-11 48-11C51-11 56-8 64-1C82 15 74 12 92 30C121 3 157-11 199-11C294-11 360 38 360 130Z" transform="translate(5169,0)"></path><path data-c="20" d="" transform="translate(5563,0)"></path><path data-c="4F" d="M388-22C484-22 564 15 628 88C690 158 721 242 721 339C721 436 690 521 629 592C564 667 484 705 388 705C292 705 212 667 148 592C87 521 56 436 56 339C56 242 87 159 148 88C213 15 293-22 388-22M557 575C593 522 611 448 611 353C611 230 585 140 533 84C488 36 440 12 389 12C337 12 288 36 243 85C192 141 166 230 166 353C166 472 193 558 247 609C293 651 340 672 388 672C456 672 512 640 557 575Z" transform="translate(5895,0)"></path><path data-c="75" d="M267 18C198 18 185 47 185 112L185 442L32 431L32 393C71 393 94 389 100 382C106 375 109 350 109 306L109 159C109 120 112 90 119 67C134 15 189-11 262-11C316-11 357 14 385 65L385-11L535 0L535 38C499 38 478 40 470 46C462 52 458 66 458 89L458 442L306 431L306 393C341 393 362 390 370 384C378 378 382 365 382 342L382 167C382 88 340 18 267 18Z" transform="translate(6673,0)"></path><path data-c="74" d="M332 126L332 186L300 186L300 128C300 78 282 22 238 22C197 22 177 56 177 124L177 395L316 395L316 433L177 433L177 615L145 615C144 582 141 553 134 527C117 461 78 427 19 424L19 395L102 395L102 126C102 67 120 29 155 10C182-4 207-11 232-11C298-11 332 55 332 126Z" transform="translate(7229,0)"></path><path data-c="73" d="M360 130C360 206 308 254 205 274C158 283 128 294 112 305C96 316 88 332 88 351C88 398 123 421 193 421C261 421 298 383 303 306C304 298 309 294 319 294C330 294 336 303 336 321L336 420C336 439 331 448 321 448C310 448 295 430 285 422C260 439 229 448 193 448C104 448 33 408 33 323C33 289 47 260 76 237C111 208 144 202 207 191C272 178 305 149 305 104C305 47 270 18 199 18C130 18 86 64 67 155C64 168 59 175 50 175C39 175 33 165 33 146L33 17C33-2 38-11 48-11C51-11 56-8 64-1C82 15 74 12 92 30C121 3 157-11 199-11C294-11 360 38 360 130Z" transform="translate(7618,0)"></path><path data-c="74" d="M332 126L332 186L300 186L300 128C300 78 282 22 238 22C197 22 177 56 177 124L177 395L316 395L316 433L177 433L177 615L145 615C144 582 141 553 134 527C117 461 78 427 19 424L19 395L102 395L102 126C102 67 120 29 155 10C182-4 207-11 232-11C298-11 332 55 332 126Z" transform="translate(8012,0)"></path><path data-c="61" d="M483 91L483 150L451 150L451 91C451 52 440 32 419 32C399 32 387 57 387 77L387 274C387 309 383 335 376 353C352 412 283 448 213 448C136 448 60 405 60 333C60 300 77 283 110 283C143 283 159 299 159 332C159 361 144 378 113 381C135 406 168 419 211 419C273 419 311 362 311 297L311 264C233 259 174 247 133 228C66 197 32 154 32 97C32 71 42 49 61 32C93 3 137-11 193-11C252-11 294 14 320 65C327 27 355-6 398-6C451-6 483 36 483 91M200 18C154 18 116 52 116 98C116 191 213 231 311 236L311 141C311 74 267 18 200 18Z" transform="translate(8401,0)"></path><path data-c="6E" d="M314 413C359 413 382 378 382 307L382 79C382 60 379 48 372 44C365 40 343 38 306 38L306 0L421 3L535 0L535 38C503 38 482 39 472 42C462 45 458 52 458 64L458 251C458 298 457 331 454 350C444 411 399 442 320 442C257 442 210 412 179 352L179 442L32 431L32 393C68 393 90 390 98 384C106 378 109 365 109 342L109 79C109 60 105 48 98 44C91 40 69 38 32 38L32 0L147 3L261 0L261 38C224 38 203 40 196 44C189 48 185 60 185 79L185 259C185 340 236 413 314 413Z" transform="translate(8901,0)"></path><path data-c="64" d="M374-11L527 0L527 38C491 38 470 40 462 46C454 52 450 67 450 90L450 694L301 683L301 645C336 645 358 642 366 636C374 630 377 616 377 593L377 390C345 427 305 445 257 445C195 445 142 423 99 378C56 333 34 279 34 216C34 155 54 102 95 57C136 12 186-11 247-11C298-11 341 8 374 47M261 415C304 415 339 396 364 358C371 347 374 336 374 323L374 121C374 108 371 97 364 86C336 41 298 19 251 19C210 19 177 39 151 80C133 110 124 155 124 215C124 324 161 415 261 415Z" transform="translate(9457,0)"></path><path data-c="69" d="M194 601C194 631 169 657 139 657C109 657 83 631 83 601C83 571 108 544 138 544C169 544 194 570 194 601M143 3L247 0L247 39C214 39 194 41 188 45C182 49 180 60 180 78L180 445L37 433L37 395C70 395 91 392 98 387C105 382 108 368 108 345L108 79C108 60 105 48 98 44C91 40 69 39 33 39L33 0Z" transform="translate(10013,0)"></path><path data-c="6E" d="M314 413C359 413 382 378 382 307L382 79C382 60 379 48 372 44C365 40 343 38 306 38L306 0L421 3L535 0L535 38C503 38 482 39 472 42C462 45 458 52 458 64L458 251C458 298 457 331 454 350C444 411 399 442 320 442C257 442 210 412 179 352L179 442L32 431L32 393C68 393 90 390 98 384C106 378 109 365 109 342L109 79C109 60 105 48 98 44C91 40 69 38 32 38L32 0L147 3L261 0L261 38C224 38 203 40 196 44C189 48 185 60 185 79L185 259C185 340 236 413 314 413Z" transform="translate(10291,0)"></path><path data-c="67" d="M431 453C393 453 358 438 326 408C296 431 262 442 223 442C137 442 59 378 59 294C59 252 74 218 104 192C85 168 75 141 75 110C75 72 88 43 113 24C71 10 28-26 28-77C28-120 56-154 111-178C153-197 199-206 249-206C300-206 347-197 389-178C444-154 471-120 471-75C471-22 449 17 405 42C359 67 308 70 234 70C190 70 166 70 161 71C133 75 113 102 113 133C113 148 117 162 126 174C154 155 186 145 223 145C309 145 386 209 386 293C386 332 373 364 347 389C372 412 399 423 428 423C423 418 420 410 420 400C420 378 431 367 453 367C474 367 485 378 485 401C485 432 461 453 431 453M223 411C277 411 304 372 304 294C304 215 277 175 223 175C168 175 141 214 141 293C141 372 168 411 223 411M164 4L222 4C274 4 316 1 348-6C391-15 412-39 412-77C412-109 392-134 352-153C321-168 287-175 250-175C214-175 180-168 148-153C107-134 87-109 87-77C87-35 123 4 164 4Z" transform="translate(10847,0)"></path></g><rect width="9592.5" height="60" x="120" y="220"></rect></g></g></g></svg></mjx-container></p><p>Assuming a company’s current stock price is 50 RMB and net assets per share are 25 RMB, its PB is:</p><p><mjx-container class="MathJax" jax="SVG" overflow="overflow"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="3.423ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 1513 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math" data-latex="PB = \frac{\text{RMB }50}{\text{RMB }25} = 2"><g data-mml-node="mi" data-latex="P"><path data-c="1D443" d="M555 683L235 683C212 683 201 682 201 660C201 649 212 644 234 644C254 644 294 647 294 631C294 629 293 623 290 613L158 82C152 60 142 47 128 42C121 40 103 39 72 39C50 39 40 37 40 16C40 5 46 0 59 0L184 3L248 2C259 2 299 0 312 0C328 0 336 8 336 24C336 34 325 39 304 39C264 39 244 43 244 52C244 52 245 55 247 68L307 312L472 312C537 312 599 332 657 371C722 414 754 467 754 530C754 629 660 683 555 683M524 644C611 644 654 614 654 554C654 498 626 423 597 397C559 363 509 346 447 346L313 346L379 610C387 644 387 644 429 644Z"></path></g><g data-mml-node="mi" data-latex="B" transform="translate(754,0)"><path data-c="1D435" d="M756 543C756 634 666 683 568 683L236 683C213 683 203 682 203 659C203 643 223 644 235 644C265 643 283 641 288 640C293 639 295 636 295 631C295 629 294 623 291 613L159 82C154 60 144 47 129 42C122 40 104 39 73 39C52 39 42 31 42 15C42 5 52 0 73 0L426 0C491 0 551 20 608 59C671 102 703 155 703 217C703 296 638 344 567 358C655 379 756 446 756 543M554 644C623 644 658 612 658 547C658 496 638 454 596 420C554 386 507 370 456 370L317 370L377 610C385 644 385 644 427 644M582 299C596 276 603 253 603 228C603 176 583 131 542 94C501 57 455 39 402 39L267 39C257 39 250 39 246 40C240 40 237 42 237 45C237 46 237 49 238 53C269 180 293 276 309 340L493 340C536 340 565 326 582 299Z"></path></g></g></g></svg><mjx-break size="4"> </mjx-break><svg style="vertical-align: -0.816ex;" xmlns="http://www.w3.org/2000/svg" width="9.292ex" height="2.8ex" role="img" focusable="false" viewBox="0 -877 4107.1 1237.5"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math" data-latex="PB = \frac{\text{RMB }50}{\text{RMB }25} = 2"><g data-mml-node="mo" data-latex="="><path data-c="3D" d="M698 367L80 367C64 367 56 359 56 344C56 329 64 321 80 321L698 321C714 321 722 329 722 344C722 356 711 367 698 367M698 179L80 179C64 179 56 171 56 156C56 141 64 133 80 133L698 133C714 133 722 141 722 156C722 169 711 179 698 179Z"></path></g><g data-mml-node="mfrac" data-latex="\frac{\text{RMB }50}{\text{RMB }25}" transform="translate(1055.8,0)"><g data-mml-node="mrow" transform="translate(220,394) scale(0.707)" data-latex="\text{RMB }50"><g data-mml-node="mtext" data-latex="\text{RMB }"><path data-c="52" d="M611 501C611 558 580 604 519 639C468 668 411 683 349 683L35 683L35 644L63 644C98 644 119 641 126 636C133 631 137 620 137 602L137 81C137 63 133 51 126 46C119 41 98 38 63 38L35 38L35-1C62 2 111 3 183 3C256 3 305 2 332-1L332 38L304 38C269 38 248 41 241 46C234 51 230 63 230 81L230 327L340 327C404 327 451 289 462 235C464 224 465 200 465 165C465 138 466 118 467 106C473 22 546-22 634-22C672-22 699-6 714 25C726 48 732 70 732 91C732 104 726 111 715 111C706 111 700 105 699 92C694 36 673 8 638 8C603 8 591 41 585 70C579 104 575 130 572 147L559 226C544 278 507 316 448 339C524 361 611 415 611 501M470 600C491 581 502 548 502 501C502 463 492 431 473 404C450 373 405 357 336 357L230 357L230 609C230 630 236 641 248 642C255 643 274 644 306 644C385 644 426 641 470 600Z"></path><path data-c="4D" d="M175 108L175 622L407 24C413 8 421 0 430 0C439 0 446 7 452 21L689 630L689 81C689 63 685 51 678 46C671 41 650 39 615 39L587 39L587 0C614 2 663 3 733 3C803 3 852 2 879 0L879 39L851 39C816 39 795 41 788 46C781 51 777 63 777 81L777 602C777 620 781 631 788 636C795 641 816 644 851 644L879 644L879 683L708 683C681 683 679 682 671 662L458 115L247 659C238 679 235 683 208 683L37 683L37 644L65 644C100 644 121 641 128 636C135 631 139 620 139 602L139 108C139 91 135 77 127 64C116 47 86 39 37 39L37 0L157 3L277 0L277 39C204 39 175 51 175 108Z" transform="translate(736,0)"></path><path data-c="42" d="M478 358C548 376 624 432 624 512C624 565 599 607 549 640C506 669 456 683 401 683L36 683L36 644L64 644C99 644 120 641 127 636C134 631 138 620 138 602L138 81C138 63 134 51 127 46C120 41 99 39 64 39L36 39L36 0L427 0C484 0 535 16 579 48C627 83 651 129 651 185C651 278 563 342 478 358M521 512C521 428 451 370 367 370L228 370L228 609C228 645 233 644 272 644L394 644C468 644 521 586 521 512M544 186L544 160C542 137 529 111 504 84C478 54 442 39 395 39L272 39C233 39 228 38 228 74L228 340L409 340C490 340 544 270 544 186Z" transform="translate(1653,0)"></path><path data-c="A0" d="" transform="translate(2361,0)"></path></g><g data-mml-node="mn" data-latex="50" transform="translate(2693,0)"><path data-c="35" d="M118 315C123 315 129 319 134 326C164 371 205 393 257 393C292 393 319 373 337 332C348 305 354 264 354 209C354 146 346 102 331 76C306 35 272 14 229 14C162 14 109 62 91 114C94 113 96 114 100 114C130 114 155 137 155 167C155 198 130 219 100 219C65 219 50 200 50 163C50 63 131-22 231-22C292-22 344 0 386 44C428 88 449 141 449 202C449 260 432 310 398 353C361 400 315 423 259 423C212 423 171 408 138 378L138 556C165 548 191 544 218 544C264 544 304 555 338 578C369 597 390 616 402 634C408 642 411 648 411 651C411 661 406 666 396 666C341 645 294 634 256 634C211 634 168 643 127 662C122 664 118 665 114 665C105 665 100 656 100 637L100 345C100 324 100 315 118 315Z"></path><path data-c="30" d="M249-22C390-22 460 92 460 320C460 473 428 575 365 625C330 652 291 666 250 666C109 666 39 551 39 320C39 136 88-22 249-22M361 524C368 489 371 425 371 332C371 240 367 172 360 128C347 48 310 8 249 8C226 8 203 17 182 34C155 57 139 104 132 176C129 201 128 253 128 332C128 419 131 480 136 513C145 568 163 603 191 618C213 630 232 636 249 636C314 636 350 583 361 524Z" transform="translate(500,0)"></path></g></g><g data-mml-node="mrow" transform="translate(220,-345) scale(0.707)" data-latex="\text{RMB }25"><g data-mml-node="mtext" data-latex="\text{RMB }"><path data-c="52" d="M611 501C611 558 580 604 519 639C468 668 411 683 349 683L35 683L35 644L63 644C98 644 119 641 126 636C133 631 137 620 137 602L137 81C137 63 133 51 126 46C119 41 98 38 63 38L35 38L35-1C62 2 111 3 183 3C256 3 305 2 332-1L332 38L304 38C269 38 248 41 241 46C234 51 230 63 230 81L230 327L340 327C404 327 451 289 462 235C464 224 465 200 465 165C465 138 466 118 467 106C473 22 546-22 634-22C672-22 699-6 714 25C726 48 732 70 732 91C732 104 726 111 715 111C706 111 700 105 699 92C694 36 673 8 638 8C603 8 591 41 585 70C579 104 575 130 572 147L559 226C544 278 507 316 448 339C524 361 611 415 611 501M470 600C491 581 502 548 502 501C502 463 492 431 473 404C450 373 405 357 336 357L230 357L230 609C230 630 236 641 248 642C255 643 274 644 306 644C385 644 426 641 470 600Z"></path><path data-c="4D" d="M175 108L175 622L407 24C413 8 421 0 430 0C439 0 446 7 452 21L689 630L689 81C689 63 685 51 678 46C671 41 650 39 615 39L587 39L587 0C614 2 663 3 733 3C803 3 852 2 879 0L879 39L851 39C816 39 795 41 788 46C781 51 777 63 777 81L777 602C777 620 781 631 788 636C795 641 816 644 851 644L879 644L879 683L708 683C681 683 679 682 671 662L458 115L247 659C238 679 235 683 208 683L37 683L37 644L65 644C100 644 121 641 128 636C135 631 139 620 139 602L139 108C139 91 135 77 127 64C116 47 86 39 37 39L37 0L157 3L277 0L277 39C204 39 175 51 175 108Z" transform="translate(736,0)"></path><path data-c="42" d="M478 358C548 376 624 432 624 512C624 565 599 607 549 640C506 669 456 683 401 683L36 683L36 644L64 644C99 644 120 641 127 636C134 631 138 620 138 602L138 81C138 63 134 51 127 46C120 41 99 39 64 39L36 39L36 0L427 0C484 0 535 16 579 48C627 83 651 129 651 185C651 278 563 342 478 358M521 512C521 428 451 370 367 370L228 370L228 609C228 645 233 644 272 644L394 644C468 644 521 586 521 512M544 186L544 160C542 137 529 111 504 84C478 54 442 39 395 39L272 39C233 39 228 38 228 74L228 340L409 340C490 340 544 270 544 186Z" transform="translate(1653,0)"></path><path data-c="A0" d="" transform="translate(2361,0)"></path></g><g data-mml-node="mn" data-latex="25" transform="translate(2693,0)"><path data-c="32" d="M237 666C186 666 143 648 106 612C69 576 50 534 50 483C50 449 75 424 106 424C136 424 161 450 161 480C161 513 137 536 105 536C102 536 100 536 98 535C117 584 161 627 224 627C306 627 352 556 352 470C352 403 318 331 250 255L62 43C49 28 50 29 50 0L421 0L450 180L417 180C409 129 402 100 396 91C391 86 361 84 306 84L139 84L236 179C304 243 390 312 419 365C439 400 449 435 449 470C449 588 357 666 237 666Z"></path><path data-c="35" d="M118 315C123 315 129 319 134 326C164 371 205 393 257 393C292 393 319 373 337 332C348 305 354 264 354 209C354 146 346 102 331 76C306 35 272 14 229 14C162 14 109 62 91 114C94 113 96 114 100 114C130 114 155 137 155 167C155 198 130 219 100 219C65 219 50 200 50 163C50 63 131-22 231-22C292-22 344 0 386 44C428 88 449 141 449 202C449 260 432 310 398 353C361 400 315 423 259 423C212 423 171 408 138 378L138 556C165 548 191 544 218 544C264 544 304 555 338 578C369 597 390 616 402 634C408 642 411 648 411 651C411 661 406 666 396 666C341 645 294 634 256 634C211 634 168 643 127 662C122 664 118 665 114 665C105 665 100 656 100 637L100 345C100 324 100 315 118 315Z" transform="translate(500,0)"></path></g></g><rect width="2811.3" height="60" x="120" y="220"></rect></g></g></g></svg><mjx-break size="4"> </mjx-break><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="3.52ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 1555.8 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math" data-latex="PB = \frac{\text{RMB }50}{\text{RMB }25} = 2"><g data-mml-node="mo" data-latex="="><path data-c="3D" d="M698 367L80 367C64 367 56 359 56 344C56 329 64 321 80 321L698 321C714 321 722 329 722 344C722 356 711 367 698 367M698 179L80 179C64 179 56 171 56 156C56 141 64 133 80 133L698 133C714 133 722 141 722 156C722 169 711 179 698 179Z"></path></g><g data-mml-node="mn" data-latex="2" transform="translate(1055.8,0)"><path data-c="32" d="M237 666C186 666 143 648 106 612C69 576 50 534 50 483C50 449 75 424 106 424C136 424 161 450 161 480C161 513 137 536 105 536C102 536 100 536 98 535C117 584 161 627 224 627C306 627 352 556 352 470C352 403 318 331 250 255L62 43C49 28 50 29 50 0L421 0L450 180L417 180C409 129 402 100 396 91C391 86 361 84 306 84L139 84L236 179C304 243 390 312 419 365C439 400 449 435 449 470C449 588 357 666 237 666Z"></path></g></g></g></svg></mjx-container></p><p>This means investors are willing to pay 2 RMB for every 1 RMB of net assets.</p><h3 id="Application-1"><a href="#Application-1" class="headerlink" title="Application"></a>Application</h3><p>The PB ratio is typically used to assess whether a stock is overvalued or undervalued. Generally speaking, a PB ratio below 1 may indicate that the stock is undervalued, while a value above 1 may indicate it is overvalued.</p><h2 id="ROE-x2F-PB-Ratio-—-Actual-Return-on-Investment-—-Inverse-of-PE"><a href="#ROE-x2F-PB-Ratio-—-Actual-Return-on-Investment-—-Inverse-of-PE" class="headerlink" title="ROE/PB Ratio — Actual Return on Investment — Inverse of PE"></a>ROE/PB Ratio — Actual Return on Investment — Inverse of PE</h2><p>Companies with high ROE and low PB are usually considered to have relatively high investment value because they can provide high returns at lower market prices.</p><p>A high ROE/PB ratio may indicate that the company performs excellently in capital utilization efficiency, while its market valuation remains relatively undervalued.</p><p>$PE = \frac{(PB<em>B)}{(ROE</em>B)} = \frac{PB}{ROE}$</p><h3 id="Application-2"><a href="#Application-2" class="headerlink" title="Application"></a>Application</h3><p>Assuming a company has an ROE of 30% and a price-to-book ratio (PB) of 20, the actual return rate is 1.5%.</p><p>Obviously, a 1.5% return rate is not an attractive deal—it even lags behind fixed-term bank deposits.</p><p>If you want to convince someone to withdraw deposits from a bank or bond fund to trade stocks, the return should at least exceed the 5-year fixed-deposit rate, or even be double, to make the risk worthwhile.</p><p><strong>Therefore, a safety standard for stock investment requires an investment return rate of no less than 8%.</strong></p><h2 id="PE-—-Price-to-Earnings-Ratio"><a href="#PE-—-Price-to-Earnings-Ratio" class="headerlink" title="PE — Price-to-Earnings Ratio"></a>PE — Price-to-Earnings Ratio</h2><h3 id="Static-PE"><a href="#Static-PE" class="headerlink" title="Static PE"></a>Static PE</h3><p>The price-to-earnings ratio directly expresses the relationship between an investor’s input and output.</p><p><mjx-container class="MathJax" jax="SVG" overflow="overflow"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="3.439ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 1520 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math" data-latex="PE = \frac{\text{Current Share Price}}{\text{Earnings per Share}} = \frac{\text{Current Market Capitalization}}{\text{Prior-Year Net Income}}"><g data-mml-node="mi" data-latex="P"><path data-c="1D443" d="M555 683L235 683C212 683 201 682 201 660C201 649 212 644 234 644C254 644 294 647 294 631C294 629 293 623 290 613L158 82C152 60 142 47 128 42C121 40 103 39 72 39C50 39 40 37 40 16C40 5 46 0 59 0L184 3L248 2C259 2 299 0 312 0C328 0 336 8 336 24C336 34 325 39 304 39C264 39 244 43 244 52C244 52 245 55 247 68L307 312L472 312C537 312 599 332 657 371C722 414 754 467 754 530C754 629 660 683 555 683M524 644C611 644 654 614 654 554C654 498 626 423 597 397C559 363 509 346 447 346L313 346L379 610C387 644 387 644 429 644Z"></path></g><g data-mml-node="mi" data-latex="E" transform="translate(754,0)"><path data-c="1D438" d="M199 656C199 646 210 641 231 641C271 641 291 637 291 628C291 625 289 618 286 605L156 82C151 60 141 47 126 42C119 40 101 39 70 39C48 39 38 37 38 16C38 5 49 0 70 0L583 0C607 0 609 2 617 19L657 109C684 171 703 217 714 248C712 257 709 263 698 263C691 263 685 258 681 249C656 190 632 145 608 113C568 59 509 39 415 39L270 39C260 39 253 39 249 40C243 40 240 42 240 45C240 47 242 54 245 67L311 334L406 334C453 334 478 328 483 315C485 310 486 303 486 293C486 280 484 264 479 245C477 240 476 236 476 233C476 223 482 218 493 218C502 218 508 226 512 241L568 473C568 483 563 488 552 488C544 488 538 481 535 467C525 428 512 403 495 391C478 379 450 373 409 373L321 373L379 606C387 640 387 641 429 641L568 641C619 641 654 636 673 626C701 611 715 581 715 536C715 519 714 501 711 484C711 482 711 479 710 475L710 466C710 455 715 450 725 450C735 450 741 459 743 478L763 649C766 677 759 680 732 680L233 680C210 680 199 679 199 656Z"></path></g></g></g></svg><mjx-break size="4"> </mjx-break><svg style="vertical-align: -1.11ex;" xmlns="http://www.w3.org/2000/svg" width="17.465ex" height="3.129ex" role="img" focusable="false" viewBox="0 -892.5 7719.7 1383.2"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math" data-latex="PE = \frac{\text{Current Share Price}}{\text{Earnings per Share}} = \frac{\text{Current Market Capitalization}}{\text{Prior-Year Net Income}}"><g data-mml-node="mo" data-latex="="><path data-c="3D" d="M698 367L80 367C64 367 56 359 56 344C56 329 64 321 80 321L698 321C714 321 722 329 722 344C722 356 711 367 698 367M698 179L80 179C64 179 56 171 56 156C56 141 64 133 80 133L698 133C714 133 722 141 722 156C722 169 711 179 698 179Z"></path></g><g data-mml-node="mfrac" data-latex="\frac{\text{Current Share Price}}{\text{Earnings per Share}}" transform="translate(1055.8,0)"><g data-mml-node="mtext" transform="translate(220,394) scale(0.707)" data-latex="\text{Current Share Price}"><path data-c="43" d="M665 443L665 677C665 696 660 705 650 705C643 705 637 700 631 691L584 622C554 653 522 675 487 689C461 700 433 705 403 705C306 705 223 670 156 598C89 526 56 441 56 342C56 243 89 158 155 87C222 14 305-22 403-22C474-22 536 3 588 53C640 103 665 163 665 234C665 248 659 255 648 255C638 255 633 251 633 242C633 241 633 239 632 236C626 116 540 17 415 17C378 17 341 27 302 47C210 97 166 193 166 341C166 481 215 589 302 636C341 656 378 666 414 666C538 666 609 553 626 435C627 422 634 415 645 415C665 415 665 423 665 443Z"></path><path data-c="75" d="M267 18C198 18 185 47 185 112L185 442L32 431L32 393C71 393 94 389 100 382C106 375 109 350 109 306L109 159C109 120 112 90 119 67C134 15 189-11 262-11C316-11 357 14 385 65L385-11L535 0L535 38C499 38 478 40 470 46C462 52 458 66 458 89L458 442L306 431L306 393C341 393 362 390 370 384C378 378 382 365 382 342L382 167C382 88 340 18 267 18Z" transform="translate(722,0)"></path><path data-c="72" d="M364 378C364 417 327 442 288 442C237 442 198 412 172 351L172 442L28 431L28 393C63 393 84 390 92 384C100 378 104 365 104 342L104 79C104 60 101 48 94 44C87 40 65 38 28 38L28 0L143 3C185 4 228 3 271 0L271 38L247 38C214 38 193 41 186 46C179 51 176 63 176 81L176 232C176 275 184 314 199 347C219 390 248 412 287 413C277 403 272 391 272 377C272 346 287 331 318 331C345 331 364 352 364 378Z" transform="translate(1278,0)"></path><path data-c="72" d="M364 378C364 417 327 442 288 442C237 442 198 412 172 351L172 442L28 431L28 393C63 393 84 390 92 384C100 378 104 365 104 342L104 79C104 60 101 48 94 44C87 40 65 38 28 38L28 0L143 3C185 4 228 3 271 0L271 38L247 38C214 38 193 41 186 46C179 51 176 63 176 81L176 232C176 275 184 314 199 347C219 390 248 412 287 413C277 403 272 391 272 377C272 346 287 331 318 331C345 331 364 352 364 378Z" transform="translate(1670,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(2062,0)"></path><path data-c="6E" d="M314 413C359 413 382 378 382 307L382 79C382 60 379 48 372 44C365 40 343 38 306 38L306 0L421 3L535 0L535 38C503 38 482 39 472 42C462 45 458 52 458 64L458 251C458 298 457 331 454 350C444 411 399 442 320 442C257 442 210 412 179 352L179 442L32 431L32 393C68 393 90 390 98 384C106 378 109 365 109 342L109 79C109 60 105 48 98 44C91 40 69 38 32 38L32 0L147 3L261 0L261 38C224 38 203 40 196 44C189 48 185 60 185 79L185 259C185 340 236 413 314 413Z" transform="translate(2506,0)"></path><path data-c="74" d="M332 126L332 186L300 186L300 128C300 78 282 22 238 22C197 22 177 56 177 124L177 395L316 395L316 433L177 433L177 615L145 615C144 582 141 553 134 527C117 461 78 427 19 424L19 395L102 395L102 126C102 67 120 29 155 10C182-4 207-11 232-11C298-11 332 55 332 126Z" transform="translate(3062,0)"></path><path data-c="20" d="" transform="translate(3451,0)"></path><path data-c="53" d="M252 669C359 669 422 601 440 466C441 456 447 451 456 451C467 451 472 460 472 478L472 677C472 696 467 705 458 705C452 705 446 700 440 690L408 638C364 680 324 705 251 705C142 705 56 617 56 509C56 417 116 349 187 324L317 292C384 275 428 229 428 154C428 79 376 17 301 17C170 17 93 91 88 218C88 227 83 232 73 232C62 232 56 223 56 204L56 6C56-13 61-22 71-22C77-22 83-17 89-7L121 45C164 0 224-22 302-22C359-22 407-1 444 40C481 81 499 130 499 188C499 235 485 278 458 317C431 356 394 380 349 391L223 422C170 434 127 481 127 544C127 614 182 669 252 669Z" transform="translate(3783,0)"></path><path data-c="68" d="M314 415C359 415 382 380 382 309L382 79C382 60 379 48 372 44C365 40 343 39 306 39L306 0L421 3L535 0L535 39C503 39 482 40 472 43C462 46 458 53 458 64L458 253C458 301 457 334 454 353C444 414 399 445 320 445C259 445 213 417 182 361L182 694L32 683L32 645C68 645 90 642 98 636C106 630 109 616 109 593L109 79C109 60 105 48 98 44C91 40 69 39 32 39L32 0L147 3L261 0L261 39C224 39 203 40 196 44C189 48 185 60 185 79L185 261C185 342 236 415 314 415Z" transform="translate(4339,0)"></path><path data-c="61" d="M483 91L483 150L451 150L451 91C451 52 440 32 419 32C399 32 387 57 387 77L387 274C387 309 383 335 376 353C352 412 283 448 213 448C136 448 60 405 60 333C60 300 77 283 110 283C143 283 159 299 159 332C159 361 144 378 113 381C135 406 168 419 211 419C273 419 311 362 311 297L311 264C233 259 174 247 133 228C66 197 32 154 32 97C32 71 42 49 61 32C93 3 137-11 193-11C252-11 294 14 320 65C327 27 355-6 398-6C451-6 483 36 483 91M200 18C154 18 116 52 116 98C116 191 213 231 311 236L311 141C311 74 267 18 200 18Z" transform="translate(4895,0)"></path><path data-c="72" d="M364 378C364 417 327 442 288 442C237 442 198 412 172 351L172 442L28 431L28 393C63 393 84 390 92 384C100 378 104 365 104 342L104 79C104 60 101 48 94 44C87 40 65 38 28 38L28 0L143 3C185 4 228 3 271 0L271 38L247 38C214 38 193 41 186 46C179 51 176 63 176 81L176 232C176 275 184 314 199 347C219 390 248 412 287 413C277 403 272 391 272 377C272 346 287 331 318 331C345 331 364 352 364 378Z" transform="translate(5395,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(5787,0)"></path><path data-c="20" d="" transform="translate(6231,0)"></path><path data-c="50" d="M395 312C452 312 503 328 548 360C599 396 624 441 624 495C624 552 598 599 546 635C500 667 447 683 387 683L35 683L35 644L63 644C98 644 119 641 126 636C133 631 137 620 137 602L137 81C137 63 133 51 126 46C119 41 98 39 63 39L35 39L35 0C61 2 111 3 184 3C258 3 308 2 334 0L334 39L306 39C271 39 250 41 243 46C236 51 232 63 232 81L232 312M274 644L362 644C391 644 416 640 437 633C494 612 514 570 514 495C514 467 511 443 504 423C487 373 433 346 362 346L229 346L229 609C229 645 235 644 274 644Z" transform="translate(6563,0)"></path><path data-c="72" d="M364 378C364 417 327 442 288 442C237 442 198 412 172 351L172 442L28 431L28 393C63 393 84 390 92 384C100 378 104 365 104 342L104 79C104 60 101 48 94 44C87 40 65 38 28 38L28 0L143 3C185 4 228 3 271 0L271 38L247 38C214 38 193 41 186 46C179 51 176 63 176 81L176 232C176 275 184 314 199 347C219 390 248 412 287 413C277 403 272 391 272 377C272 346 287 331 318 331C345 331 364 352 364 378Z" transform="translate(7244,0)"></path><path data-c="69" d="M194 601C194 631 169 657 139 657C109 657 83 631 83 601C83 571 108 544 138 544C169 544 194 570 194 601M143 3L247 0L247 39C214 39 194 41 188 45C182 49 180 60 180 78L180 445L37 433L37 395C70 395 91 392 98 387C105 382 108 368 108 345L108 79C108 60 105 48 98 44C91 40 69 39 33 39L33 0Z" transform="translate(7636,0)"></path><path data-c="63" d="M251 416C293 416 325 406 347 386C323 383 306 361 306 338C306 305 322 289 355 289C388 289 404 306 404 339C404 410 327 448 250 448C188 448 137 426 96 380C55 334 34 279 34 216C34 155 55 101 96 56C137 11 187-11 248-11C298-11 337 3 365 32C388 55 403 78 411 102C414 111 415 117 415 121C415 130 409 134 398 134C390 134 385 130 382 122C361 55 320 21 257 21C228 21 200 34 172 61C139 92 123 144 123 218C123 318 161 416 251 416Z" transform="translate(7914,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(8358,0)"></path></g><g data-mml-node="mtext" transform="translate(375.9,-345) scale(0.707)" data-latex="\text{Earnings per Share}"><path data-c="45" d="M230 74L230 334L315 334C360 334 388 326 400 311C412 296 418 265 418 218L451 218L451 488L418 488C418 441 412 411 400 396C388 381 360 373 315 373L230 373L230 606C230 642 236 641 275 641L402 641C474 641 522 627 547 598C579 559 583 523 592 450L625 450L596 680L33 680L33 641L61 641C96 641 117 639 124 634C131 629 135 617 135 599L135 81C135 63 131 51 124 46C117 41 96 39 61 39L33 39L33 0L610 0L653 263L620 263C611 208 602 166 591 136C580 106 560 82 530 63C505 47 464 39 406 39L275 39C236 39 230 38 230 74Z"></path><path data-c="61" d="M483 91L483 150L451 150L451 91C451 52 440 32 419 32C399 32 387 57 387 77L387 274C387 309 383 335 376 353C352 412 283 448 213 448C136 448 60 405 60 333C60 300 77 283 110 283C143 283 159 299 159 332C159 361 144 378 113 381C135 406 168 419 211 419C273 419 311 362 311 297L311 264C233 259 174 247 133 228C66 197 32 154 32 97C32 71 42 49 61 32C93 3 137-11 193-11C252-11 294 14 320 65C327 27 355-6 398-6C451-6 483 36 483 91M200 18C154 18 116 52 116 98C116 191 213 231 311 236L311 141C311 74 267 18 200 18Z" transform="translate(681,0)"></path><path data-c="72" d="M364 378C364 417 327 442 288 442C237 442 198 412 172 351L172 442L28 431L28 393C63 393 84 390 92 384C100 378 104 365 104 342L104 79C104 60 101 48 94 44C87 40 65 38 28 38L28 0L143 3C185 4 228 3 271 0L271 38L247 38C214 38 193 41 186 46C179 51 176 63 176 81L176 232C176 275 184 314 199 347C219 390 248 412 287 413C277 403 272 391 272 377C272 346 287 331 318 331C345 331 364 352 364 378Z" transform="translate(1181,0)"></path><path data-c="6E" d="M314 413C359 413 382 378 382 307L382 79C382 60 379 48 372 44C365 40 343 38 306 38L306 0L421 3L535 0L535 38C503 38 482 39 472 42C462 45 458 52 458 64L458 251C458 298 457 331 454 350C444 411 399 442 320 442C257 442 210 412 179 352L179 442L32 431L32 393C68 393 90 390 98 384C106 378 109 365 109 342L109 79C109 60 105 48 98 44C91 40 69 38 32 38L32 0L147 3L261 0L261 38C224 38 203 40 196 44C189 48 185 60 185 79L185 259C185 340 236 413 314 413Z" transform="translate(1573,0)"></path><path data-c="69" d="M194 601C194 631 169 657 139 657C109 657 83 631 83 601C83 571 108 544 138 544C169 544 194 570 194 601M143 3L247 0L247 39C214 39 194 41 188 45C182 49 180 60 180 78L180 445L37 433L37 395C70 395 91 392 98 387C105 382 108 368 108 345L108 79C108 60 105 48 98 44C91 40 69 39 33 39L33 0Z" transform="translate(2129,0)"></path><path data-c="6E" d="M314 413C359 413 382 378 382 307L382 79C382 60 379 48 372 44C365 40 343 38 306 38L306 0L421 3L535 0L535 38C503 38 482 39 472 42C462 45 458 52 458 64L458 251C458 298 457 331 454 350C444 411 399 442 320 442C257 442 210 412 179 352L179 442L32 431L32 393C68 393 90 390 98 384C106 378 109 365 109 342L109 79C109 60 105 48 98 44C91 40 69 38 32 38L32 0L147 3L261 0L261 38C224 38 203 40 196 44C189 48 185 60 185 79L185 259C185 340 236 413 314 413Z" transform="translate(2407,0)"></path><path data-c="67" d="M431 453C393 453 358 438 326 408C296 431 262 442 223 442C137 442 59 378 59 294C59 252 74 218 104 192C85 168 75 141 75 110C75 72 88 43 113 24C71 10 28-26 28-77C28-120 56-154 111-178C153-197 199-206 249-206C300-206 347-197 389-178C444-154 471-120 471-75C471-22 449 17 405 42C359 67 308 70 234 70C190 70 166 70 161 71C133 75 113 102 113 133C113 148 117 162 126 174C154 155 186 145 223 145C309 145 386 209 386 293C386 332 373 364 347 389C372 412 399 423 428 423C423 418 420 410 420 400C420 378 431 367 453 367C474 367 485 378 485 401C485 432 461 453 431 453M223 411C277 411 304 372 304 294C304 215 277 175 223 175C168 175 141 214 141 293C141 372 168 411 223 411M164 4L222 4C274 4 316 1 348-6C391-15 412-39 412-77C412-109 392-134 352-153C321-168 287-175 250-175C214-175 180-168 148-153C107-134 87-109 87-77C87-35 123 4 164 4Z" transform="translate(2963,0)"></path><path data-c="73" d="M360 130C360 206 308 254 205 274C158 283 128 294 112 305C96 316 88 332 88 351C88 398 123 421 193 421C261 421 298 383 303 306C304 298 309 294 319 294C330 294 336 303 336 321L336 420C336 439 331 448 321 448C310 448 295 430 285 422C260 439 229 448 193 448C104 448 33 408 33 323C33 289 47 260 76 237C111 208 144 202 207 191C272 178 305 149 305 104C305 47 270 18 199 18C130 18 86 64 67 155C64 168 59 175 50 175C39 175 33 165 33 146L33 17C33-2 38-11 48-11C51-11 56-8 64-1C82 15 74 12 92 30C121 3 157-11 199-11C294-11 360 38 360 130Z" transform="translate(3463,0)"></path><path data-c="20" d="" transform="translate(3857,0)"></path><path data-c="70" d="M298-13C360-13 413 9 456 54C499 99 521 152 521 215C521 276 501 328 462 372C421 419 371 442 311 442C260 442 216 422 178 383L178 442L28 431L28 393C65 393 87 391 94 386C101 381 105 368 105 347L105-115C105-134 101-146 94-150C87-154 65-155 28-155L28-194L142-191L257-194L257-155C220-155 199-154 192-150C185-146 181-134 181-115L181 44C203 13 246-13 298-13M304 409C345 409 377 386 402 340C421 301 431 260 431 215C431 168 420 125 398 86C372 39 337 16 294 16C252 16 219 36 194 75C185 88 181 101 181 114L181 333C210 384 251 409 304 409Z" transform="translate(4189,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(4745,0)"></path><path data-c="72" d="M364 378C364 417 327 442 288 442C237 442 198 412 172 351L172 442L28 431L28 393C63 393 84 390 92 384C100 378 104 365 104 342L104 79C104 60 101 48 94 44C87 40 65 38 28 38L28 0L143 3C185 4 228 3 271 0L271 38L247 38C214 38 193 41 186 46C179 51 176 63 176 81L176 232C176 275 184 314 199 347C219 390 248 412 287 413C277 403 272 391 272 377C272 346 287 331 318 331C345 331 364 352 364 378Z" transform="translate(5189,0)"></path><path data-c="20" d="" transform="translate(5581,0)"></path><path data-c="53" d="M252 669C359 669 422 601 440 466C441 456 447 451 456 451C467 451 472 460 472 478L472 677C472 696 467 705 458 705C452 705 446 700 440 690L408 638C364 680 324 705 251 705C142 705 56 617 56 509C56 417 116 349 187 324L317 292C384 275 428 229 428 154C428 79 376 17 301 17C170 17 93 91 88 218C88 227 83 232 73 232C62 232 56 223 56 204L56 6C56-13 61-22 71-22C77-22 83-17 89-7L121 45C164 0 224-22 302-22C359-22 407-1 444 40C481 81 499 130 499 188C499 235 485 278 458 317C431 356 394 380 349 391L223 422C170 434 127 481 127 544C127 614 182 669 252 669Z" transform="translate(5913,0)"></path><path data-c="68" d="M314 415C359 415 382 380 382 309L382 79C382 60 379 48 372 44C365 40 343 39 306 39L306 0L421 3L535 0L535 39C503 39 482 40 472 43C462 46 458 53 458 64L458 253C458 301 457 334 454 353C444 414 399 445 320 445C259 445 213 417 182 361L182 694L32 683L32 645C68 645 90 642 98 636C106 630 109 616 109 593L109 79C109 60 105 48 98 44C91 40 69 39 32 39L32 0L147 3L261 0L261 39C224 39 203 40 196 44C189 48 185 60 185 79L185 261C185 342 236 415 314 415Z" transform="translate(6469,0)"></path><path data-c="61" d="M483 91L483 150L451 150L451 91C451 52 440 32 419 32C399 32 387 57 387 77L387 274C387 309 383 335 376 353C352 412 283 448 213 448C136 448 60 405 60 333C60 300 77 283 110 283C143 283 159 299 159 332C159 361 144 378 113 381C135 406 168 419 211 419C273 419 311 362 311 297L311 264C233 259 174 247 133 228C66 197 32 154 32 97C32 71 42 49 61 32C93 3 137-11 193-11C252-11 294 14 320 65C327 27 355-6 398-6C451-6 483 36 483 91M200 18C154 18 116 52 116 98C116 191 213 231 311 236L311 141C311 74 267 18 200 18Z" transform="translate(7025,0)"></path><path data-c="72" d="M364 378C364 417 327 442 288 442C237 442 198 412 172 351L172 442L28 431L28 393C63 393 84 390 92 384C100 378 104 365 104 342L104 79C104 60 101 48 94 44C87 40 65 38 28 38L28 0L143 3C185 4 228 3 271 0L271 38L247 38C214 38 193 41 186 46C179 51 176 63 176 81L176 232C176 275 184 314 199 347C219 390 248 412 287 413C277 403 272 391 272 377C272 346 287 331 318 331C345 331 364 352 364 378Z" transform="translate(7525,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(7917,0)"></path></g><rect width="6424" height="60" x="120" y="220"></rect></g></g></g></svg><mjx-break size="4"> </mjx-break><svg style="vertical-align: -0.798ex;" xmlns="http://www.w3.org/2000/svg" width="24.906ex" height="3.006ex" role="img" focusable="false" viewBox="0 -975.7 11008.5 1328.5"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math" data-latex="PE = \frac{\text{Current Share Price}}{\text{Earnings per Share}} = \frac{\text{Current Market Capitalization}}{\text{Prior-Year Net Income}}"><g data-mml-node="mo" data-latex="="><path data-c="3D" d="M698 367L80 367C64 367 56 359 56 344C56 329 64 321 80 321L698 321C714 321 722 329 722 344C722 356 711 367 698 367M698 179L80 179C64 179 56 171 56 156C56 141 64 133 80 133L698 133C714 133 722 141 722 156C722 169 711 179 698 179Z"></path></g><g data-mml-node="mfrac" data-latex="\frac{\text{Current Market Capitalization}}{\text{Prior-Year Net Income}}" transform="translate(1055.8,0)"><g data-mml-node="mtext" transform="translate(220,477.2) scale(0.707)" data-latex="\text{Current Market Capitalization}"><path data-c="43" d="M665 443L665 677C665 696 660 705 650 705C643 705 637 700 631 691L584 622C554 653 522 675 487 689C461 700 433 705 403 705C306 705 223 670 156 598C89 526 56 441 56 342C56 243 89 158 155 87C222 14 305-22 403-22C474-22 536 3 588 53C640 103 665 163 665 234C665 248 659 255 648 255C638 255 633 251 633 242C633 241 633 239 632 236C626 116 540 17 415 17C378 17 341 27 302 47C210 97 166 193 166 341C166 481 215 589 302 636C341 656 378 666 414 666C538 666 609 553 626 435C627 422 634 415 645 415C665 415 665 423 665 443Z"></path><path data-c="75" d="M267 18C198 18 185 47 185 112L185 442L32 431L32 393C71 393 94 389 100 382C106 375 109 350 109 306L109 159C109 120 112 90 119 67C134 15 189-11 262-11C316-11 357 14 385 65L385-11L535 0L535 38C499 38 478 40 470 46C462 52 458 66 458 89L458 442L306 431L306 393C341 393 362 390 370 384C378 378 382 365 382 342L382 167C382 88 340 18 267 18Z" transform="translate(722,0)"></path><path data-c="72" d="M364 378C364 417 327 442 288 442C237 442 198 412 172 351L172 442L28 431L28 393C63 393 84 390 92 384C100 378 104 365 104 342L104 79C104 60 101 48 94 44C87 40 65 38 28 38L28 0L143 3C185 4 228 3 271 0L271 38L247 38C214 38 193 41 186 46C179 51 176 63 176 81L176 232C176 275 184 314 199 347C219 390 248 412 287 413C277 403 272 391 272 377C272 346 287 331 318 331C345 331 364 352 364 378Z" transform="translate(1278,0)"></path><path data-c="72" d="M364 378C364 417 327 442 288 442C237 442 198 412 172 351L172 442L28 431L28 393C63 393 84 390 92 384C100 378 104 365 104 342L104 79C104 60 101 48 94 44C87 40 65 38 28 38L28 0L143 3C185 4 228 3 271 0L271 38L247 38C214 38 193 41 186 46C179 51 176 63 176 81L176 232C176 275 184 314 199 347C219 390 248 412 287 413C277 403 272 391 272 377C272 346 287 331 318 331C345 331 364 352 364 378Z" transform="translate(1670,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(2062,0)"></path><path data-c="6E" d="M314 413C359 413 382 378 382 307L382 79C382 60 379 48 372 44C365 40 343 38 306 38L306 0L421 3L535 0L535 38C503 38 482 39 472 42C462 45 458 52 458 64L458 251C458 298 457 331 454 350C444 411 399 442 320 442C257 442 210 412 179 352L179 442L32 431L32 393C68 393 90 390 98 384C106 378 109 365 109 342L109 79C109 60 105 48 98 44C91 40 69 38 32 38L32 0L147 3L261 0L261 38C224 38 203 40 196 44C189 48 185 60 185 79L185 259C185 340 236 413 314 413Z" transform="translate(2506,0)"></path><path data-c="74" d="M332 126L332 186L300 186L300 128C300 78 282 22 238 22C197 22 177 56 177 124L177 395L316 395L316 433L177 433L177 615L145 615C144 582 141 553 134 527C117 461 78 427 19 424L19 395L102 395L102 126C102 67 120 29 155 10C182-4 207-11 232-11C298-11 332 55 332 126Z" transform="translate(3062,0)"></path><path data-c="20" d="" transform="translate(3451,0)"></path><path data-c="4D" d="M175 108L175 622L407 24C413 8 421 0 430 0C439 0 446 7 452 21L689 630L689 81C689 63 685 51 678 46C671 41 650 39 615 39L587 39L587 0C614 2 663 3 733 3C803 3 852 2 879 0L879 39L851 39C816 39 795 41 788 46C781 51 777 63 777 81L777 602C777 620 781 631 788 636C795 641 816 644 851 644L879 644L879 683L708 683C681 683 679 682 671 662L458 115L247 659C238 679 235 683 208 683L37 683L37 644L65 644C100 644 121 641 128 636C135 631 139 620 139 602L139 108C139 91 135 77 127 64C116 47 86 39 37 39L37 0L157 3L277 0L277 39C204 39 175 51 175 108Z" transform="translate(3783,0)"></path><path data-c="61" d="M483 91L483 150L451 150L451 91C451 52 440 32 419 32C399 32 387 57 387 77L387 274C387 309 383 335 376 353C352 412 283 448 213 448C136 448 60 405 60 333C60 300 77 283 110 283C143 283 159 299 159 332C159 361 144 378 113 381C135 406 168 419 211 419C273 419 311 362 311 297L311 264C233 259 174 247 133 228C66 197 32 154 32 97C32 71 42 49 61 32C93 3 137-11 193-11C252-11 294 14 320 65C327 27 355-6 398-6C451-6 483 36 483 91M200 18C154 18 116 52 116 98C116 191 213 231 311 236L311 141C311 74 267 18 200 18Z" transform="translate(4700,0)"></path><path data-c="72" d="M364 378C364 417 327 442 288 442C237 442 198 412 172 351L172 442L28 431L28 393C63 393 84 390 92 384C100 378 104 365 104 342L104 79C104 60 101 48 94 44C87 40 65 38 28 38L28 0L143 3C185 4 228 3 271 0L271 38L247 38C214 38 193 41 186 46C179 51 176 63 176 81L176 232C176 275 184 314 199 347C219 390 248 412 287 413C277 403 272 391 272 377C272 346 287 331 318 331C345 331 364 352 364 378Z" transform="translate(5200,0)"></path><path data-c="6B" d="M422 3C442 4 472 3 511 0L511 39C487 39 469 42 457 47C445 52 431 66 415 89L290 266C287 275 285 278 285 275C285 276 295 285 316 303C351 334 377 354 394 365C425 384 456 394 488 396L488 434C467 432 443 431 416 431C370 431 332 432 301 434L301 396C320 395 330 388 330 375C330 363 323 351 310 339L178 224L178 694L28 683L28 645C64 645 86 642 94 636C102 630 105 616 105 593L105 79C105 60 101 48 94 44C87 40 65 39 28 39L28 0L139 3L251 0L251 39C214 39 193 40 186 44C179 48 175 60 175 79L175 179L233 230L249 206C314 116 347 66 347 57C347 45 335 39 311 39L311 0Z" transform="translate(5592,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(6120,0)"></path><path data-c="74" d="M332 126L332 186L300 186L300 128C300 78 282 22 238 22C197 22 177 56 177 124L177 395L316 395L316 433L177 433L177 615L145 615C144 582 141 553 134 527C117 461 78 427 19 424L19 395L102 395L102 126C102 67 120 29 155 10C182-4 207-11 232-11C298-11 332 55 332 126Z" transform="translate(6564,0)"></path><path data-c="20" d="" transform="translate(6953,0)"></path><path data-c="43" d="M665 443L665 677C665 696 660 705 650 705C643 705 637 700 631 691L584 622C554 653 522 675 487 689C461 700 433 705 403 705C306 705 223 670 156 598C89 526 56 441 56 342C56 243 89 158 155 87C222 14 305-22 403-22C474-22 536 3 588 53C640 103 665 163 665 234C665 248 659 255 648 255C638 255 633 251 633 242C633 241 633 239 632 236C626 116 540 17 415 17C378 17 341 27 302 47C210 97 166 193 166 341C166 481 215 589 302 636C341 656 378 666 414 666C538 666 609 553 626 435C627 422 634 415 645 415C665 415 665 423 665 443Z" transform="translate(7285,0)"></path><path data-c="61" d="M483 91L483 150L451 150L451 91C451 52 440 32 419 32C399 32 387 57 387 77L387 274C387 309 383 335 376 353C352 412 283 448 213 448C136 448 60 405 60 333C60 300 77 283 110 283C143 283 159 299 159 332C159 361 144 378 113 381C135 406 168 419 211 419C273 419 311 362 311 297L311 264C233 259 174 247 133 228C66 197 32 154 32 97C32 71 42 49 61 32C93 3 137-11 193-11C252-11 294 14 320 65C327 27 355-6 398-6C451-6 483 36 483 91M200 18C154 18 116 52 116 98C116 191 213 231 311 236L311 141C311 74 267 18 200 18Z" transform="translate(8007,0)"></path><path data-c="70" d="M298-13C360-13 413 9 456 54C499 99 521 152 521 215C521 276 501 328 462 372C421 419 371 442 311 442C260 442 216 422 178 383L178 442L28 431L28 393C65 393 87 391 94 386C101 381 105 368 105 347L105-115C105-134 101-146 94-150C87-154 65-155 28-155L28-194L142-191L257-194L257-155C220-155 199-154 192-150C185-146 181-134 181-115L181 44C203 13 246-13 298-13M304 409C345 409 377 386 402 340C421 301 431 260 431 215C431 168 420 125 398 86C372 39 337 16 294 16C252 16 219 36 194 75C185 88 181 101 181 114L181 333C210 384 251 409 304 409Z" transform="translate(8507,0)"></path><path data-c="69" d="M194 601C194 631 169 657 139 657C109 657 83 631 83 601C83 571 108 544 138 544C169 544 194 570 194 601M143 3L247 0L247 39C214 39 194 41 188 45C182 49 180 60 180 78L180 445L37 433L37 395C70 395 91 392 98 387C105 382 108 368 108 345L108 79C108 60 105 48 98 44C91 40 69 39 33 39L33 0Z" transform="translate(9063,0)"></path><path data-c="74" d="M332 126L332 186L300 186L300 128C300 78 282 22 238 22C197 22 177 56 177 124L177 395L316 395L316 433L177 433L177 615L145 615C144 582 141 553 134 527C117 461 78 427 19 424L19 395L102 395L102 126C102 67 120 29 155 10C182-4 207-11 232-11C298-11 332 55 332 126Z" transform="translate(9341,0)"></path><path data-c="61" d="M483 91L483 150L451 150L451 91C451 52 440 32 419 32C399 32 387 57 387 77L387 274C387 309 383 335 376 353C352 412 283 448 213 448C136 448 60 405 60 333C60 300 77 283 110 283C143 283 159 299 159 332C159 361 144 378 113 381C135 406 168 419 211 419C273 419 311 362 311 297L311 264C233 259 174 247 133 228C66 197 32 154 32 97C32 71 42 49 61 32C93 3 137-11 193-11C252-11 294 14 320 65C327 27 355-6 398-6C451-6 483 36 483 91M200 18C154 18 116 52 116 98C116 191 213 231 311 236L311 141C311 74 267 18 200 18Z" transform="translate(9730,0)"></path><path data-c="6C" d="M144 3L255 0L255 39C219 39 197 40 190 44C183 48 180 60 180 79L180 694L33 683L33 645C68 645 90 642 97 636C104 630 108 616 108 593L108 79C108 60 105 48 98 44C91 40 69 39 33 39L33 0Z" transform="translate(10230,0)"></path><path data-c="69" d="M194 601C194 631 169 657 139 657C109 657 83 631 83 601C83 571 108 544 138 544C169 544 194 570 194 601M143 3L247 0L247 39C214 39 194 41 188 45C182 49 180 60 180 78L180 445L37 433L37 395C70 395 91 392 98 387C105 382 108 368 108 345L108 79C108 60 105 48 98 44C91 40 69 39 33 39L33 0Z" transform="translate(10508,0)"></path><path data-c="7A" d="M57 0L384 0L401 191L369 191C364 129 354 87 336 65C318 43 283 32 230 32L122 32L389 395C396 404 399 410 399 414C399 425 389 431 370 431L53 431L41 265L74 265C77 320 87 356 103 374C119 392 152 402 202 402L306 402L38 38C31 29 28 23 28 18C28 1 35 0 57 0Z" transform="translate(10786,0)"></path><path data-c="61" d="M483 91L483 150L451 150L451 91C451 52 440 32 419 32C399 32 387 57 387 77L387 274C387 309 383 335 376 353C352 412 283 448 213 448C136 448 60 405 60 333C60 300 77 283 110 283C143 283 159 299 159 332C159 361 144 378 113 381C135 406 168 419 211 419C273 419 311 362 311 297L311 264C233 259 174 247 133 228C66 197 32 154 32 97C32 71 42 49 61 32C93 3 137-11 193-11C252-11 294 14 320 65C327 27 355-6 398-6C451-6 483 36 483 91M200 18C154 18 116 52 116 98C116 191 213 231 311 236L311 141C311 74 267 18 200 18Z" transform="translate(11230,0)"></path><path data-c="74" d="M332 126L332 186L300 186L300 128C300 78 282 22 238 22C197 22 177 56 177 124L177 395L316 395L316 433L177 433L177 615L145 615C144 582 141 553 134 527C117 461 78 427 19 424L19 395L102 395L102 126C102 67 120 29 155 10C182-4 207-11 232-11C298-11 332 55 332 126Z" transform="translate(11730,0)"></path><path data-c="69" d="M194 601C194 631 169 657 139 657C109 657 83 631 83 601C83 571 108 544 138 544C169 544 194 570 194 601M143 3L247 0L247 39C214 39 194 41 188 45C182 49 180 60 180 78L180 445L37 433L37 395C70 395 91 392 98 387C105 382 108 368 108 345L108 79C108 60 105 48 98 44C91 40 69 39 33 39L33 0Z" transform="translate(12119,0)"></path><path data-c="6F" d="M249-11C311-11 363 11 406 55C449 99 471 152 471 214C471 277 450 332 408 378C366 424 313 448 250 448C187 448 135 424 92 378C49 332 28 277 28 214C28 152 49 99 92 55C135 11 188-11 249-11M250 21C202 21 166 42 141 85C125 113 117 159 117 222C117 283 125 327 140 355C164 398 200 419 249 419C296 419 332 398 357 357C374 329 382 284 382 222C382 106 348 21 250 21Z" transform="translate(12397,0)"></path><path data-c="6E" d="M314 413C359 413 382 378 382 307L382 79C382 60 379 48 372 44C365 40 343 38 306 38L306 0L421 3L535 0L535 38C503 38 482 39 472 42C462 45 458 52 458 64L458 251C458 298 457 331 454 350C444 411 399 442 320 442C257 442 210 412 179 352L179 442L32 431L32 393C68 393 90 390 98 384C106 378 109 365 109 342L109 79C109 60 105 48 98 44C91 40 69 38 32 38L32 0L147 3L261 0L261 38C224 38 203 40 196 44C189 48 185 60 185 79L185 259C185 340 236 413 314 413Z" transform="translate(12897,0)"></path></g><g data-mml-node="mtext" transform="translate(1424.2,-345) scale(0.707)" data-latex="\text{Prior-Year Net Income}"><path data-c="50" d="M395 312C452 312 503 328 548 360C599 396 624 441 624 495C624 552 598 599 546 635C500 667 447 683 387 683L35 683L35 644L63 644C98 644 119 641 126 636C133 631 137 620 137 602L137 81C137 63 133 51 126 46C119 41 98 39 63 39L35 39L35 0C61 2 111 3 184 3C258 3 308 2 334 0L334 39L306 39C271 39 250 41 243 46C236 51 232 63 232 81L232 312M274 644L362 644C391 644 416 640 437 633C494 612 514 570 514 495C514 467 511 443 504 423C487 373 433 346 362 346L229 346L229 609C229 645 235 644 274 644Z"></path><path data-c="72" d="M364 378C364 417 327 442 288 442C237 442 198 412 172 351L172 442L28 431L28 393C63 393 84 390 92 384C100 378 104 365 104 342L104 79C104 60 101 48 94 44C87 40 65 38 28 38L28 0L143 3C185 4 228 3 271 0L271 38L247 38C214 38 193 41 186 46C179 51 176 63 176 81L176 232C176 275 184 314 199 347C219 390 248 412 287 413C277 403 272 391 272 377C272 346 287 331 318 331C345 331 364 352 364 378Z" transform="translate(681,0)"></path><path data-c="69" d="M194 601C194 631 169 657 139 657C109 657 83 631 83 601C83 571 108 544 138 544C169 544 194 570 194 601M143 3L247 0L247 39C214 39 194 41 188 45C182 49 180 60 180 78L180 445L37 433L37 395C70 395 91 392 98 387C105 382 108 368 108 345L108 79C108 60 105 48 98 44C91 40 69 39 33 39L33 0Z" transform="translate(1073,0)"></path><path data-c="6F" d="M249-11C311-11 363 11 406 55C449 99 471 152 471 214C471 277 450 332 408 378C366 424 313 448 250 448C187 448 135 424 92 378C49 332 28 277 28 214C28 152 49 99 92 55C135 11 188-11 249-11M250 21C202 21 166 42 141 85C125 113 117 159 117 222C117 283 125 327 140 355C164 398 200 419 249 419C296 419 332 398 357 357C374 329 382 284 382 222C382 106 348 21 250 21Z" transform="translate(1351,0)"></path><path data-c="72" d="M364 378C364 417 327 442 288 442C237 442 198 412 172 351L172 442L28 431L28 393C63 393 84 390 92 384C100 378 104 365 104 342L104 79C104 60 101 48 94 44C87 40 65 38 28 38L28 0L143 3C185 4 228 3 271 0L271 38L247 38C214 38 193 41 186 46C179 51 176 63 176 81L176 232C176 275 184 314 199 347C219 390 248 412 287 413C277 403 272 391 272 377C272 346 287 331 318 331C345 331 364 352 364 378Z" transform="translate(1851,0)"></path><path data-c="2D" d="M11 187L276 187L276 245L11 245Z" transform="translate(2243,0)"></path><path data-c="59" d="M576 610C576 601 573 592 566 582L407 323L231 609C227 616 225 621 225 624C225 637 248 644 294 644L294 683C251 680 201 679 144 680L11 683L11 644L33 644C81 644 101 640 120 610L328 273L328 81C328 63 324 51 316 46C308 41 288 39 253 39L226 39L226 0C252 2 301 3 374 3C447 3 496 2 522 0L522 39L496 39C461 39 440 41 432 46C424 51 420 64 420 83L420 273L611 582C636 623 679 644 738 644L738 683C695 680 662 679 637 680L519 683L519 644C552 644 576 637 576 610Z" transform="translate(2576,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(3326,0)"></path><path data-c="61" d="M483 91L483 150L451 150L451 91C451 52 440 32 419 32C399 32 387 57 387 77L387 274C387 309 383 335 376 353C352 412 283 448 213 448C136 448 60 405 60 333C60 300 77 283 110 283C143 283 159 299 159 332C159 361 144 378 113 381C135 406 168 419 211 419C273 419 311 362 311 297L311 264C233 259 174 247 133 228C66 197 32 154 32 97C32 71 42 49 61 32C93 3 137-11 193-11C252-11 294 14 320 65C327 27 355-6 398-6C451-6 483 36 483 91M200 18C154 18 116 52 116 98C116 191 213 231 311 236L311 141C311 74 267 18 200 18Z" transform="translate(3770,0)"></path><path data-c="72" d="M364 378C364 417 327 442 288 442C237 442 198 412 172 351L172 442L28 431L28 393C63 393 84 390 92 384C100 378 104 365 104 342L104 79C104 60 101 48 94 44C87 40 65 38 28 38L28 0L143 3C185 4 228 3 271 0L271 38L247 38C214 38 193 41 186 46C179 51 176 63 176 81L176 232C176 275 184 314 199 347C219 390 248 412 287 413C277 403 272 391 272 377C272 346 287 331 318 331C345 331 364 352 364 378Z" transform="translate(4270,0)"></path><path data-c="20" d="" transform="translate(4662,0)"></path><path data-c="4E" d="M576 15C585.6 1 589 0 596 0C613 0 614 7 614 30L614 575C614 592 618 606 626 619C637 636 667 644 716 644L716 683L596 680L477 683L477 644C526 644 556 636 567 619C575 606 579 592 579 575L579 166L237 668C230 678 220 683 205 683L33 683L33 644L66 644C106 644 128 642 133 638C134 637 135 632 135 624L135 108C135 91 131 77 123 64C112 47 82 39 33 39L33 0L152 3L272 0L272 39C223 39 193 47 182 64C174 77 170 91 170 108L170 611Z" transform="translate(4994,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(5744,0)"></path><path data-c="74" d="M332 126L332 186L300 186L300 128C300 78 282 22 238 22C197 22 177 56 177 124L177 395L316 395L316 433L177 433L177 615L145 615C144 582 141 553 134 527C117 461 78 427 19 424L19 395L102 395L102 126C102 67 120 29 155 10C182-4 207-11 232-11C298-11 332 55 332 126Z" transform="translate(6188,0)"></path><path data-c="20" d="" transform="translate(6577,0)"></path><path data-c="49" d="M180 3C257 3 308 2 333 0L333 39L304 39C268 39 246 41 239 46C232 51 228 63 228 81L228 602C228 620 232 631 239 636C246 641 268 644 304 644L333 644L333 683C308 681 257 680 181 680C104 680 53 681 28 683L28 644L57 644C93 644 115 641 122 636C129 631 133 620 133 602L133 81C133 63 129 51 122 46C115 41 93 39 57 39L28 39L28 0C53 2 104 3 180 3Z" transform="translate(6909,0)"></path><path data-c="6E" d="M314 413C359 413 382 378 382 307L382 79C382 60 379 48 372 44C365 40 343 38 306 38L306 0L421 3L535 0L535 38C503 38 482 39 472 42C462 45 458 52 458 64L458 251C458 298 457 331 454 350C444 411 399 442 320 442C257 442 210 412 179 352L179 442L32 431L32 393C68 393 90 390 98 384C106 378 109 365 109 342L109 79C109 60 105 48 98 44C91 40 69 38 32 38L32 0L147 3L261 0L261 38C224 38 203 40 196 44C189 48 185 60 185 79L185 259C185 340 236 413 314 413Z" transform="translate(7270,0)"></path><path data-c="63" d="M251 416C293 416 325 406 347 386C323 383 306 361 306 338C306 305 322 289 355 289C388 289 404 306 404 339C404 410 327 448 250 448C188 448 137 426 96 380C55 334 34 279 34 216C34 155 55 101 96 56C137 11 187-11 248-11C298-11 337 3 365 32C388 55 403 78 411 102C414 111 415 117 415 121C415 130 409 134 398 134C390 134 385 130 382 122C361 55 320 21 257 21C228 21 200 34 172 61C139 92 123 144 123 218C123 318 161 416 251 416Z" transform="translate(7826,0)"></path><path data-c="6F" d="M249-11C311-11 363 11 406 55C449 99 471 152 471 214C471 277 450 332 408 378C366 424 313 448 250 448C187 448 135 424 92 378C49 332 28 277 28 214C28 152 49 99 92 55C135 11 188-11 249-11M250 21C202 21 166 42 141 85C125 113 117 159 117 222C117 283 125 327 140 355C164 398 200 419 249 419C296 419 332 398 357 357C374 329 382 284 382 222C382 106 348 21 250 21Z" transform="translate(8270,0)"></path><path data-c="6D" d="M315 413C361 413 384 378 384 307L384 79C384 60 381 48 374 44C367 40 344 38 307 38L307 0L423 3L538 0L538 38C501 38 479 40 472 44C465 48 461 60 461 79L461 259C461 339 513 413 590 413C637 413 660 378 660 307L660 79C660 60 656 48 649 44C642 40 619 38 582 38L582 0L698 3L813 0L813 38C781 38 760 39 750 42C740 45 736 52 736 64L736 251C736 298 734 331 731 350C721 411 676 442 597 442C534 442 487 413 455 354C441 413 397 442 322 442C259 442 211 412 179 352L179 442L32 431L32 393C68 393 90 390 98 384C106 378 109 365 109 342L109 79C109 60 105 48 98 44C91 40 69 38 32 38L32 0L148 3L263 0L263 38C226 38 203 40 196 44C189 48 185 60 185 79L185 259C185 340 238 413 315 413Z" transform="translate(8770,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(9603,0)"></path></g><rect width="9712.7" height="60" x="120" y="220"></rect></g></g></g></svg></mjx-container></p><h3 id="Dynamic-PE"><a href="#Dynamic-PE" class="headerlink" title="Dynamic PE"></a>Dynamic PE</h3><p>The formula above uses past data for calculation, which is why it is called Static PE. Dynamic PE, on the other hand, forecasts the future of the stock.</p><p>The denominator of Dynamic PE is the forecasted net profit for the coming year, and the forecasting method can also be applied to the PEG calculation below.</p><p>For example, if the stock has already released its Q1 financial report, the denominator in the formula is multiplied by 4; for the semi-annual report, multiplied by 2; and for the Q3 report, multiplied by 4/3.</p><p>Assuming a stock’s closing price on that day is 1,000 RMB, and the earnings per share in the Q1 financial report is 10 RMB, the denominator is multiplied by 4 during calculation:</p><p><mjx-container class="MathJax" jax="SVG" overflow="overflow"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="14.029ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 6201 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math" data-latex="PE(\text{Dynamic}) = \frac{1000}{10*4} = 1"><g data-mml-node="mi" data-latex="P"><path data-c="1D443" d="M555 683L235 683C212 683 201 682 201 660C201 649 212 644 234 644C254 644 294 647 294 631C294 629 293 623 290 613L158 82C152 60 142 47 128 42C121 40 103 39 72 39C50 39 40 37 40 16C40 5 46 0 59 0L184 3L248 2C259 2 299 0 312 0C328 0 336 8 336 24C336 34 325 39 304 39C264 39 244 43 244 52C244 52 245 55 247 68L307 312L472 312C537 312 599 332 657 371C722 414 754 467 754 530C754 629 660 683 555 683M524 644C611 644 654 614 654 554C654 498 626 423 597 397C559 363 509 346 447 346L313 346L379 610C387 644 387 644 429 644Z"></path></g><g data-mml-node="mi" data-latex="E" transform="translate(754,0)"><path data-c="1D438" d="M199 656C199 646 210 641 231 641C271 641 291 637 291 628C291 625 289 618 286 605L156 82C151 60 141 47 126 42C119 40 101 39 70 39C48 39 38 37 38 16C38 5 49 0 70 0L583 0C607 0 609 2 617 19L657 109C684 171 703 217 714 248C712 257 709 263 698 263C691 263 685 258 681 249C656 190 632 145 608 113C568 59 509 39 415 39L270 39C260 39 253 39 249 40C243 40 240 42 240 45C240 47 242 54 245 67L311 334L406 334C453 334 478 328 483 315C485 310 486 303 486 293C486 280 484 264 479 245C477 240 476 236 476 233C476 223 482 218 493 218C502 218 508 226 512 241L568 473C568 483 563 488 552 488C544 488 538 481 535 467C525 428 512 403 495 391C478 379 450 373 409 373L321 373L379 606C387 640 387 641 429 641L568 641C619 641 654 636 673 626C701 611 715 581 715 536C715 519 714 501 711 484C711 482 711 479 710 475L710 466C710 455 715 450 725 450C735 450 741 459 743 478L763 649C766 677 759 680 732 680L233 680C210 680 199 679 199 656Z"></path></g><g data-mml-node="mo" data-latex="(" transform="translate(1520,0)"><path data-c="28" d="M318-248C327-248 332-243 332-234C332-231 330-227 327-223C275-183 233-117 202-26C175 53 161 131 161 208L161 292C161 369 175 447 202 526C233 617 275 683 327 723C330 726 332 730 332 734C332 743 327 748 318 748C317 748 314 747 311 745C251 699 201 631 160 540C121 453 101 371 101 292L101 208C101 129 121 47 160-40C201-131 251-199 311-245C314-247 317-248 318-248Z"></path></g><g data-mml-node="mtext" data-latex="\text{Dynamic}" transform="translate(1909,0)"><path data-c="44" d="M401 0C490 0 563 34 622 103C679 168 707 246 707 336C707 428 679 508 624 575C565 647 490 683 401 683L35 683L35 644L63 644C98 644 119 641 126 636C133 631 137 620 137 602L137 81C137 63 133 51 126 46C119 41 98 39 63 39L35 39L35 0M374 39L274 39C235 39 230 38 230 74L230 609C230 645 235 644 274 644L373 644C447 644 506 614 550 554C583 507 600 435 600 336C600 161 534 39 374 39Z"></path><path data-c="79" d="M113-205C168-205 212-167 243-90L416 332C433 372 463 392 508 392L508 431C475 428 449 427 430 428L341 431L341 393C372 392 387 379 387 354C387 348 385 341 381 332L286 98L180 354C177 362 175 368 174 371C174 385 193 392 230 392L230 431L118 428C94 427 61 428 19 431L19 392C72 392 85 394 99 360L246 1L222-55C195-119 176-175 113-175C99-175 87-171 76-164C99-159 110-144 110-120C110-90 95-75 64-75C34-75 19-90 19-121C19-170 64-205 113-205Z" transform="translate(764,0)"></path><path data-c="6E" d="M314 413C359 413 382 378 382 307L382 79C382 60 379 48 372 44C365 40 343 38 306 38L306 0L421 3L535 0L535 38C503 38 482 39 472 42C462 45 458 52 458 64L458 251C458 298 457 331 454 350C444 411 399 442 320 442C257 442 210 412 179 352L179 442L32 431L32 393C68 393 90 390 98 384C106 378 109 365 109 342L109 79C109 60 105 48 98 44C91 40 69 38 32 38L32 0L147 3L261 0L261 38C224 38 203 40 196 44C189 48 185 60 185 79L185 259C185 340 236 413 314 413Z" transform="translate(1292,0)"></path><path data-c="61" d="M483 91L483 150L451 150L451 91C451 52 440 32 419 32C399 32 387 57 387 77L387 274C387 309 383 335 376 353C352 412 283 448 213 448C136 448 60 405 60 333C60 300 77 283 110 283C143 283 159 299 159 332C159 361 144 378 113 381C135 406 168 419 211 419C273 419 311 362 311 297L311 264C233 259 174 247 133 228C66 197 32 154 32 97C32 71 42 49 61 32C93 3 137-11 193-11C252-11 294 14 320 65C327 27 355-6 398-6C451-6 483 36 483 91M200 18C154 18 116 52 116 98C116 191 213 231 311 236L311 141C311 74 267 18 200 18Z" transform="translate(1848,0)"></path><path data-c="6D" d="M315 413C361 413 384 378 384 307L384 79C384 60 381 48 374 44C367 40 344 38 307 38L307 0L423 3L538 0L538 38C501 38 479 40 472 44C465 48 461 60 461 79L461 259C461 339 513 413 590 413C637 413 660 378 660 307L660 79C660 60 656 48 649 44C642 40 619 38 582 38L582 0L698 3L813 0L813 38C781 38 760 39 750 42C740 45 736 52 736 64L736 251C736 298 734 331 731 350C721 411 676 442 597 442C534 442 487 413 455 354C441 413 397 442 322 442C259 442 211 412 179 352L179 442L32 431L32 393C68 393 90 390 98 384C106 378 109 365 109 342L109 79C109 60 105 48 98 44C91 40 69 38 32 38L32 0L148 3L263 0L263 38C226 38 203 40 196 44C189 48 185 60 185 79L185 259C185 340 238 413 315 413Z" transform="translate(2348,0)"></path><path data-c="69" d="M194 601C194 631 169 657 139 657C109 657 83 631 83 601C83 571 108 544 138 544C169 544 194 570 194 601M143 3L247 0L247 39C214 39 194 41 188 45C182 49 180 60 180 78L180 445L37 433L37 395C70 395 91 392 98 387C105 382 108 368 108 345L108 79C108 60 105 48 98 44C91 40 69 39 33 39L33 0Z" transform="translate(3181,0)"></path><path data-c="63" d="M251 416C293 416 325 406 347 386C323 383 306 361 306 338C306 305 322 289 355 289C388 289 404 306 404 339C404 410 327 448 250 448C188 448 137 426 96 380C55 334 34 279 34 216C34 155 55 101 96 56C137 11 187-11 248-11C298-11 337 3 365 32C388 55 403 78 411 102C414 111 415 117 415 121C415 130 409 134 398 134C390 134 385 130 382 122C361 55 320 21 257 21C228 21 200 34 172 61C139 92 123 144 123 218C123 318 161 416 251 416Z" transform="translate(3459,0)"></path></g><g data-mml-node="mo" data-latex=")" transform="translate(5812,0)"><path data-c="29" d="M78-245C138-199 188-131 229-40C268 47 288 129 288 208L288 292C288 371 268 453 229 540C188 631 138 699 78 745C75 747 72 748 71 748C62 748 57 743 57 734C57 730 59 726 62 723C114 683 156 617 187 526C214 447 228 369 228 292L228 208C228 131 214 53 187-26C156-117 114-183 62-223C59-227 57-231 57-234C57-243 62-248 71-248C72-248 75-247 78-245Z"></path></g></g></g></svg><mjx-break size="4"> </mjx-break><svg style="vertical-align: -0.816ex;" xmlns="http://www.w3.org/2000/svg" width="6.584ex" height="2.773ex" role="img" focusable="false" viewBox="0 -864.9 2910 1225.5"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math" data-latex="PE(\text{Dynamic}) = \frac{1000}{10*4} = 1"><g data-mml-node="mo" data-latex="="><path data-c="3D" d="M698 367L80 367C64 367 56 359 56 344C56 329 64 321 80 321L698 321C714 321 722 329 722 344C722 356 711 367 698 367M698 179L80 179C64 179 56 171 56 156C56 141 64 133 80 133L698 133C714 133 722 141 722 156C722 169 711 179 698 179Z"></path></g><g data-mml-node="mfrac" data-latex="\frac{1000}{10*4}" transform="translate(1055.8,0)"><g data-mml-node="mn" transform="translate(220,394) scale(0.707)" data-latex="1000"><path data-c="31" d="M269 666C228 624 168 603 89 603L89 564C141 564 184 572 217 588L217 82C217 64 213 52 204 47C195 42 170 39 130 39L95 39L95 0C120 2 174 3 257 3C340 3 394 2 419 0L419 39L384 39C343 39 318 42 310 47C302 52 297 64 297 82L297 636C297 660 295 666 269 666Z"></path><path data-c="30" d="M249-22C390-22 460 92 460 320C460 473 428 575 365 625C330 652 291 666 250 666C109 666 39 551 39 320C39 136 88-22 249-22M361 524C368 489 371 425 371 332C371 240 367 172 360 128C347 48 310 8 249 8C226 8 203 17 182 34C155 57 139 104 132 176C129 201 128 253 128 332C128 419 131 480 136 513C145 568 163 603 191 618C213 630 232 636 249 636C314 636 350 583 361 524Z" transform="translate(500,0)"></path><path data-c="30" d="M249-22C390-22 460 92 460 320C460 473 428 575 365 625C330 652 291 666 250 666C109 666 39 551 39 320C39 136 88-22 249-22M361 524C368 489 371 425 371 332C371 240 367 172 360 128C347 48 310 8 249 8C226 8 203 17 182 34C155 57 139 104 132 176C129 201 128 253 128 332C128 419 131 480 136 513C145 568 163 603 191 618C213 630 232 636 249 636C314 636 350 583 361 524Z" transform="translate(1000,0)"></path><path data-c="30" d="M249-22C390-22 460 92 460 320C460 473 428 575 365 625C330 652 291 666 250 666C109 666 39 551 39 320C39 136 88-22 249-22M361 524C368 489 371 425 371 332C371 240 367 172 360 128C347 48 310 8 249 8C226 8 203 17 182 34C155 57 139 104 132 176C129 201 128 253 128 332C128 419 131 480 136 513C145 568 163 603 191 618C213 630 232 636 249 636C314 636 350 583 361 524Z" transform="translate(1500,0)"></path></g><g data-mml-node="mrow" transform="translate(220,-345) scale(0.707)" data-latex="10*4"><g data-mml-node="mn" data-latex="10"><path data-c="31" d="M269 666C228 624 168 603 89 603L89 564C141 564 184 572 217 588L217 82C217 64 213 52 204 47C195 42 170 39 130 39L95 39L95 0C120 2 174 3 257 3C340 3 394 2 419 0L419 39L384 39C343 39 318 42 310 47C302 52 297 64 297 82L297 636C297 660 295 666 269 666Z"></path><path data-c="30" d="M249-22C390-22 460 92 460 320C460 473 428 575 365 625C330 652 291 666 250 666C109 666 39 551 39 320C39 136 88-22 249-22M361 524C368 489 371 425 371 332C371 240 367 172 360 128C347 48 310 8 249 8C226 8 203 17 182 34C155 57 139 104 132 176C129 201 128 253 128 332C128 419 131 480 136 513C145 568 163 603 191 618C213 630 232 636 249 636C314 636 350 583 361 524Z" transform="translate(500,0)"></path></g><g data-mml-node="mo" data-latex="*" transform="translate(1000,0)"><path data-c="2217" d="M404 372C397 372 390 370 385 366L267 279L282 425C285 445 269 462 250 462C231 462 215 445 218 425L233 279L115 366C110 370 103 372 96 372C78 372 63 357 63 339C63 325 70 315 83 309L217 250L83 191C70 185 63 175 63 161C63 143 78 128 96 128C103 128 110 130 115 134L233 221L218 75C215 55 231 39 250 39C269 39 285 55 282 75L267 221L385 134C390 130 397 128 404 128C415 128 424 133 431 142C446 162 435 183 417 191L283 250L417 309C430 315 437 325 437 339C437 357 422 372 404 372Z"></path></g><g data-mml-node="mn" data-latex="4" transform="translate(1500,0)"><path data-c="34" d="M353 677C344 677 336 672 330 663L28 199L28 163L289 163L289 81C289 63 285 51 278 46C271 41 252 39 219 39L194 39L194 0C223 2 269 3 331 3C393 3 439 2 468 0L468 39L443 39C410 39 391 41 384 46C377 51 373 63 373 81L373 163L471 163L471 202L373 202L373 660C373 670 366 677 353 677M295 553L295 202L67 202Z"></path></g></g><rect width="1614.2" height="60" x="120" y="220"></rect></g></g></g></svg><mjx-break size="4"> </mjx-break><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="3.52ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 1555.8 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math" data-latex="PE(\text{Dynamic}) = \frac{1000}{10*4} = 1"><g data-mml-node="mo" data-latex="="><path data-c="3D" d="M698 367L80 367C64 367 56 359 56 344C56 329 64 321 80 321L698 321C714 321 722 329 722 344C722 356 711 367 698 367M698 179L80 179C64 179 56 171 56 156C56 141 64 133 80 133L698 133C714 133 722 141 722 156C722 169 711 179 698 179Z"></path></g><g data-mml-node="mn" data-latex="1" transform="translate(1055.8,0)"><path data-c="31" d="M269 666C228 624 168 603 89 603L89 564C141 564 184 572 217 588L217 82C217 64 213 52 204 47C195 42 170 39 130 39L95 39L95 0C120 2 174 3 257 3C340 3 394 2 419 0L419 39L384 39C343 39 318 42 310 47C302 52 297 64 297 82L297 636C297 660 295 666 269 666Z"></path></g></g></g></svg></mjx-container></p><p>If the Dynamic PE is higher than the Static PE, it may indicate that the stock’s earnings per share and net profit have decreased, which warrants caution.</p><h3 id="Trailing-Twelve-Month-PE-TTM"><a href="#Trailing-Twelve-Month-PE-TTM" class="headerlink" title="Trailing Twelve Month PE (TTM)"></a>Trailing Twelve Month PE (TTM)</h3><p>Similar to Static PE, the denominator of TTM PE is based on the sum of net profits over the last four quarters.</p><p>If the net profit of the last four quarters is used, it is also called PE3; if earnings per share is used, it is called PE4.</p><h3 id="Application-3"><a href="#Application-3" class="headerlink" title="Application"></a>Application</h3><p>Assuming a company has a PE of 10, it will take 10 years of holding the stock to recover the investment cost. This means that buying the stock at the current price, assuming the company maintains constant profitability, will take 10 years for the net profit earned (converted according to held shares) to equal the initial money invested in buying the stock.</p><p>As mentioned above, the actual return on investment is the inverse of PE, <strong>which requires that PE should not exceed 12.5</strong>.</p><h2 id="PEG"><a href="#PEG" class="headerlink" title="PEG"></a>PEG</h2><blockquote><p>If a company’s stock is fairly priced, its price-to-earnings ratio will equal its earnings growth rate. — Peter Lynch</p></blockquote><p>Lynch believed that <strong>stocks with a PEG ratio below 1 may be undervalued, while those above 1 may be overvalued</strong>.</p><p>PEG is a comprehensive indicator that examines both value and growth potential. Therefore, <strong>PEG is particularly suitable for assessing the stock value of growth companies</strong>.</p><p><mjx-container class="MathJax" jax="SVG" overflow="overflow"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="5.217ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 2306 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math" data-latex="PEG = \frac{PE}{\text{Expected Annual Earnings Growth Rate}}"><g data-mml-node="mi" data-latex="P"><path data-c="1D443" d="M555 683L235 683C212 683 201 682 201 660C201 649 212 644 234 644C254 644 294 647 294 631C294 629 293 623 290 613L158 82C152 60 142 47 128 42C121 40 103 39 72 39C50 39 40 37 40 16C40 5 46 0 59 0L184 3L248 2C259 2 299 0 312 0C328 0 336 8 336 24C336 34 325 39 304 39C264 39 244 43 244 52C244 52 245 55 247 68L307 312L472 312C537 312 599 332 657 371C722 414 754 467 754 530C754 629 660 683 555 683M524 644C611 644 654 614 654 554C654 498 626 423 597 397C559 363 509 346 447 346L313 346L379 610C387 644 387 644 429 644Z"></path></g><g data-mml-node="mi" data-latex="E" transform="translate(754,0)"><path data-c="1D438" d="M199 656C199 646 210 641 231 641C271 641 291 637 291 628C291 625 289 618 286 605L156 82C151 60 141 47 126 42C119 40 101 39 70 39C48 39 38 37 38 16C38 5 49 0 70 0L583 0C607 0 609 2 617 19L657 109C684 171 703 217 714 248C712 257 709 263 698 263C691 263 685 258 681 249C656 190 632 145 608 113C568 59 509 39 415 39L270 39C260 39 253 39 249 40C243 40 240 42 240 45C240 47 242 54 245 67L311 334L406 334C453 334 478 328 483 315C485 310 486 303 486 293C486 280 484 264 479 245C477 240 476 236 476 233C476 223 482 218 493 218C502 218 508 226 512 241L568 473C568 483 563 488 552 488C544 488 538 481 535 467C525 428 512 403 495 391C478 379 450 373 409 373L321 373L379 606C387 640 387 641 429 641L568 641C619 641 654 636 673 626C701 611 715 581 715 536C715 519 714 501 711 484C711 482 711 479 710 475L710 466C710 455 715 450 725 450C735 450 741 459 743 478L763 649C766 677 759 680 732 680L233 680C210 680 199 679 199 656Z"></path></g><g data-mml-node="mi" data-latex="G" transform="translate(1520,0)"><path data-c="1D43A" d="M324-22C412-22 481 5 532 59C538 46 564 1 578 1C583 1 586 4 588 8C590 12 597 32 606 68L624 144C630 167 634 184 637 195C648 239 648 238 702 239C715 239 721 247 721 263C721 273 716 278 705 278C686 278 620 274 601 275L462 278C446 278 438 270 438 254C438 245 444 241 456 240C513 237 543 235 546 233C549 231 550 227 550 222C550 215 543 185 530 133C510 61 436 17 343 17C221 17 148 98 148 220C148 239 150 263 153 290C164 372 218 487 261 540C311 603 403 666 505 666C609 666 660 587 660 479C660 470 657 438 657 429C657 420 663 415 676 415C681 415 685 416 688 417C693 424 696 431 698 438L760 691C760 700 755 705 745 705C741 705 735 701 727 692L662 619C623 676 568 705 497 705C442 705 388 692 333 667C222 615 141 533 89 421C63 366 50 310 50 253C50 93 164-22 324-22Z"></path></g></g></g></svg><mjx-break size="4"> </mjx-break><svg style="vertical-align: -1.113ex;" xmlns="http://www.w3.org/2000/svg" width="31.918ex" height="3.097ex" role="img" focusable="false" viewBox="0 -877 14107.7 1368.9"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math" data-latex="PEG = \frac{PE}{\text{Expected Annual Earnings Growth Rate}}"><g data-mml-node="mo" data-latex="="><path data-c="3D" d="M698 367L80 367C64 367 56 359 56 344C56 329 64 321 80 321L698 321C714 321 722 329 722 344C722 356 711 367 698 367M698 179L80 179C64 179 56 171 56 156C56 141 64 133 80 133L698 133C714 133 722 141 722 156C722 169 711 179 698 179Z"></path></g><g data-mml-node="mfrac" data-latex="\frac{PE}{\text{Expected Annual Earnings Growth Rate}}" transform="translate(1055.8,0)"><g data-mml-node="mrow" transform="translate(5988.6,394) scale(0.707)" data-latex="PE"><g data-mml-node="mi" data-latex="P"><path data-c="1D443" d="M555 683L235 683C212 683 201 682 201 660C201 649 212 644 234 644C254 644 294 647 294 631C294 629 293 623 290 613L158 82C152 60 142 47 128 42C121 40 103 39 72 39C50 39 40 37 40 16C40 5 46 0 59 0L184 3L248 2C259 2 299 0 312 0C328 0 336 8 336 24C336 34 325 39 304 39C264 39 244 43 244 52C244 52 245 55 247 68L307 312L472 312C537 312 599 332 657 371C722 414 754 467 754 530C754 629 660 683 555 683M524 644C611 644 654 614 654 554C654 498 626 423 597 397C559 363 509 346 447 346L313 346L379 610C387 644 387 644 429 644Z"></path></g><g data-mml-node="mi" data-latex="E" transform="translate(754,0)"><path data-c="1D438" d="M199 656C199 646 210 641 231 641C271 641 291 637 291 628C291 625 289 618 286 605L156 82C151 60 141 47 126 42C119 40 101 39 70 39C48 39 38 37 38 16C38 5 49 0 70 0L583 0C607 0 609 2 617 19L657 109C684 171 703 217 714 248C712 257 709 263 698 263C691 263 685 258 681 249C656 190 632 145 608 113C568 59 509 39 415 39L270 39C260 39 253 39 249 40C243 40 240 42 240 45C240 47 242 54 245 67L311 334L406 334C453 334 478 328 483 315C485 310 486 303 486 293C486 280 484 264 479 245C477 240 476 236 476 233C476 223 482 218 493 218C502 218 508 226 512 241L568 473C568 483 563 488 552 488C544 488 538 481 535 467C525 428 512 403 495 391C478 379 450 373 409 373L321 373L379 606C387 640 387 641 429 641L568 641C619 641 654 636 673 626C701 611 715 581 715 536C715 519 714 501 711 484C711 482 711 479 710 475L710 466C710 455 715 450 725 450C735 450 741 459 743 478L763 649C766 677 759 680 732 680L233 680C210 680 199 679 199 656Z"></path></g></g><g data-mml-node="mtext" transform="translate(220,-346.3) scale(0.707)" data-latex="\text{Expected Annual Earnings Growth Rate}"><path data-c="45" d="M230 74L230 334L315 334C360 334 388 326 400 311C412 296 418 265 418 218L451 218L451 488L418 488C418 441 412 411 400 396C388 381 360 373 315 373L230 373L230 606C230 642 236 641 275 641L402 641C474 641 522 627 547 598C579 559 583 523 592 450L625 450L596 680L33 680L33 641L61 641C96 641 117 639 124 634C131 629 135 617 135 599L135 81C135 63 131 51 124 46C117 41 96 39 61 39L33 39L33 0L610 0L653 263L620 263C611 208 602 166 591 136C580 106 560 82 530 63C505 47 464 39 406 39L275 39C236 39 230 38 230 74Z"></path><path data-c="78" d="M418 3C441 4 474 3 516 0L516 38C463 38 443 39 422 67L292 235C335 288 364 324 380 343C408 375 447 392 498 393L498 431C470 429 442 428 414 428C377 428 344 429 315 431L315 393C334 391 344 382 344 366C344 357 339 346 330 334L272 262L198 357C193 364 190 369 190 372C190 385 202 392 225 393L225 431L114 428C88 427 56 428 17 431L17 393C53 393 76 390 86 385C96 380 112 363 134 334L229 210C180 147 150 109 139 96C107 57 65 38 12 38L12 0C49 3 77 4 98 3L195 0L195 38C176 41 167 50 167 65C167 69 168 74 170 79C176 92 203 126 251 183L323 89C334 75 340 65 343 59C343 46 331 39 307 38L307 0Z" transform="translate(681,0)"></path><path data-c="70" d="M298-13C360-13 413 9 456 54C499 99 521 152 521 215C521 276 501 328 462 372C421 419 371 442 311 442C260 442 216 422 178 383L178 442L28 431L28 393C65 393 87 391 94 386C101 381 105 368 105 347L105-115C105-134 101-146 94-150C87-154 65-155 28-155L28-194L142-191L257-194L257-155C220-155 199-154 192-150C185-146 181-134 181-115L181 44C203 13 246-13 298-13M304 409C345 409 377 386 402 340C421 301 431 260 431 215C431 168 420 125 398 86C372 39 337 16 294 16C252 16 219 36 194 75C185 88 181 101 181 114L181 333C210 384 251 409 304 409Z" transform="translate(1209,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(1765,0)"></path><path data-c="63" d="M251 416C293 416 325 406 347 386C323 383 306 361 306 338C306 305 322 289 355 289C388 289 404 306 404 339C404 410 327 448 250 448C188 448 137 426 96 380C55 334 34 279 34 216C34 155 55 101 96 56C137 11 187-11 248-11C298-11 337 3 365 32C388 55 403 78 411 102C414 111 415 117 415 121C415 130 409 134 398 134C390 134 385 130 382 122C361 55 320 21 257 21C228 21 200 34 172 61C139 92 123 144 123 218C123 318 161 416 251 416Z" transform="translate(2209,0)"></path><path data-c="74" d="M332 126L332 186L300 186L300 128C300 78 282 22 238 22C197 22 177 56 177 124L177 395L316 395L316 433L177 433L177 615L145 615C144 582 141 553 134 527C117 461 78 427 19 424L19 395L102 395L102 126C102 67 120 29 155 10C182-4 207-11 232-11C298-11 332 55 332 126Z" transform="translate(2653,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(3042,0)"></path><path data-c="64" d="M374-11L527 0L527 38C491 38 470 40 462 46C454 52 450 67 450 90L450 694L301 683L301 645C336 645 358 642 366 636C374 630 377 616 377 593L377 390C345 427 305 445 257 445C195 445 142 423 99 378C56 333 34 279 34 216C34 155 54 102 95 57C136 12 186-11 247-11C298-11 341 8 374 47M261 415C304 415 339 396 364 358C371 347 374 336 374 323L374 121C374 108 371 97 364 86C336 41 298 19 251 19C210 19 177 39 151 80C133 110 124 155 124 215C124 324 161 415 261 415Z" transform="translate(3486,0)"></path><path data-c="20" d="" transform="translate(4042,0)"></path><path data-c="41" d="M583 3L717 0L717 39L695 39C670 39 653 40 646 42C631 47 621 58 615 75L401 694C397 707 390 716 375 716C362 716 353 709 348 694L143 102C129 61 92 39 32 38L32 0L137 3L254 0L254 39C205 39 180 54 180 85C180 92 181 96 182 98L227 225L465 225L516 77C518 71 519 67 519 64C519 47 492 39 437 39L437 0C479 3 528 4 583 3M345 569L451 264L240 264Z" transform="translate(4374,0)"></path><path data-c="6E" d="M314 413C359 413 382 378 382 307L382 79C382 60 379 48 372 44C365 40 343 38 306 38L306 0L421 3L535 0L535 38C503 38 482 39 472 42C462 45 458 52 458 64L458 251C458 298 457 331 454 350C444 411 399 442 320 442C257 442 210 412 179 352L179 442L32 431L32 393C68 393 90 390 98 384C106 378 109 365 109 342L109 79C109 60 105 48 98 44C91 40 69 38 32 38L32 0L147 3L261 0L261 38C224 38 203 40 196 44C189 48 185 60 185 79L185 259C185 340 236 413 314 413Z" transform="translate(5124,0)"></path><path data-c="6E" d="M314 413C359 413 382 378 382 307L382 79C382 60 379 48 372 44C365 40 343 38 306 38L306 0L421 3L535 0L535 38C503 38 482 39 472 42C462 45 458 52 458 64L458 251C458 298 457 331 454 350C444 411 399 442 320 442C257 442 210 412 179 352L179 442L32 431L32 393C68 393 90 390 98 384C106 378 109 365 109 342L109 79C109 60 105 48 98 44C91 40 69 38 32 38L32 0L147 3L261 0L261 38C224 38 203 40 196 44C189 48 185 60 185 79L185 259C185 340 236 413 314 413Z" transform="translate(5680,0)"></path><path data-c="75" d="M267 18C198 18 185 47 185 112L185 442L32 431L32 393C71 393 94 389 100 382C106 375 109 350 109 306L109 159C109 120 112 90 119 67C134 15 189-11 262-11C316-11 357 14 385 65L385-11L535 0L535 38C499 38 478 40 470 46C462 52 458 66 458 89L458 442L306 431L306 393C341 393 362 390 370 384C378 378 382 365 382 342L382 167C382 88 340 18 267 18Z" transform="translate(6236,0)"></path><path data-c="61" d="M483 91L483 150L451 150L451 91C451 52 440 32 419 32C399 32 387 57 387 77L387 274C387 309 383 335 376 353C352 412 283 448 213 448C136 448 60 405 60 333C60 300 77 283 110 283C143 283 159 299 159 332C159 361 144 378 113 381C135 406 168 419 211 419C273 419 311 362 311 297L311 264C233 259 174 247 133 228C66 197 32 154 32 97C32 71 42 49 61 32C93 3 137-11 193-11C252-11 294 14 320 65C327 27 355-6 398-6C451-6 483 36 483 91M200 18C154 18 116 52 116 98C116 191 213 231 311 236L311 141C311 74 267 18 200 18Z" transform="translate(6792,0)"></path><path data-c="6C" d="M144 3L255 0L255 39C219 39 197 40 190 44C183 48 180 60 180 79L180 694L33 683L33 645C68 645 90 642 97 636C104 630 108 616 108 593L108 79C108 60 105 48 98 44C91 40 69 39 33 39L33 0Z" transform="translate(7292,0)"></path><path data-c="20" d="" transform="translate(7570,0)"></path><path data-c="45" d="M230 74L230 334L315 334C360 334 388 326 400 311C412 296 418 265 418 218L451 218L451 488L418 488C418 441 412 411 400 396C388 381 360 373 315 373L230 373L230 606C230 642 236 641 275 641L402 641C474 641 522 627 547 598C579 559 583 523 592 450L625 450L596 680L33 680L33 641L61 641C96 641 117 639 124 634C131 629 135 617 135 599L135 81C135 63 131 51 124 46C117 41 96 39 61 39L33 39L33 0L610 0L653 263L620 263C611 208 602 166 591 136C580 106 560 82 530 63C505 47 464 39 406 39L275 39C236 39 230 38 230 74Z" transform="translate(7902,0)"></path><path data-c="61" d="M483 91L483 150L451 150L451 91C451 52 440 32 419 32C399 32 387 57 387 77L387 274C387 309 383 335 376 353C352 412 283 448 213 448C136 448 60 405 60 333C60 300 77 283 110 283C143 283 159 299 159 332C159 361 144 378 113 381C135 406 168 419 211 419C273 419 311 362 311 297L311 264C233 259 174 247 133 228C66 197 32 154 32 97C32 71 42 49 61 32C93 3 137-11 193-11C252-11 294 14 320 65C327 27 355-6 398-6C451-6 483 36 483 91M200 18C154 18 116 52 116 98C116 191 213 231 311 236L311 141C311 74 267 18 200 18Z" transform="translate(8583,0)"></path><path data-c="72" d="M364 378C364 417 327 442 288 442C237 442 198 412 172 351L172 442L28 431L28 393C63 393 84 390 92 384C100 378 104 365 104 342L104 79C104 60 101 48 94 44C87 40 65 38 28 38L28 0L143 3C185 4 228 3 271 0L271 38L247 38C214 38 193 41 186 46C179 51 176 63 176 81L176 232C176 275 184 314 199 347C219 390 248 412 287 413C277 403 272 391 272 377C272 346 287 331 318 331C345 331 364 352 364 378Z" transform="translate(9083,0)"></path><path data-c="6E" d="M314 413C359 413 382 378 382 307L382 79C382 60 379 48 372 44C365 40 343 38 306 38L306 0L421 3L535 0L535 38C503 38 482 39 472 42C462 45 458 52 458 64L458 251C458 298 457 331 454 350C444 411 399 442 320 442C257 442 210 412 179 352L179 442L32 431L32 393C68 393 90 390 98 384C106 378 109 365 109 342L109 79C109 60 105 48 98 44C91 40 69 38 32 38L32 0L147 3L261 0L261 38C224 38 203 40 196 44C189 48 185 60 185 79L185 259C185 340 236 413 314 413Z" transform="translate(9475,0)"></path><path data-c="69" d="M194 601C194 631 169 657 139 657C109 657 83 631 83 601C83 571 108 544 138 544C169 544 194 570 194 601M143 3L247 0L247 39C214 39 194 41 188 45C182 49 180 60 180 78L180 445L37 433L37 395C70 395 91 392 98 387C105 382 108 368 108 345L108 79C108 60 105 48 98 44C91 40 69 39 33 39L33 0Z" transform="translate(10031,0)"></path><path data-c="6E" d="M314 413C359 413 382 378 382 307L382 79C382 60 379 48 372 44C365 40 343 38 306 38L306 0L421 3L535 0L535 38C503 38 482 39 472 42C462 45 458 52 458 64L458 251C458 298 457 331 454 350C444 411 399 442 320 442C257 442 210 412 179 352L179 442L32 431L32 393C68 393 90 390 98 384C106 378 109 365 109 342L109 79C109 60 105 48 98 44C91 40 69 38 32 38L32 0L147 3L261 0L261 38C224 38 203 40 196 44C189 48 185 60 185 79L185 259C185 340 236 413 314 413Z" transform="translate(10309,0)"></path><path data-c="67" d="M431 453C393 453 358 438 326 408C296 431 262 442 223 442C137 442 59 378 59 294C59 252 74 218 104 192C85 168 75 141 75 110C75 72 88 43 113 24C71 10 28-26 28-77C28-120 56-154 111-178C153-197 199-206 249-206C300-206 347-197 389-178C444-154 471-120 471-75C471-22 449 17 405 42C359 67 308 70 234 70C190 70 166 70 161 71C133 75 113 102 113 133C113 148 117 162 126 174C154 155 186 145 223 145C309 145 386 209 386 293C386 332 373 364 347 389C372 412 399 423 428 423C423 418 420 410 420 400C420 378 431 367 453 367C474 367 485 378 485 401C485 432 461 453 431 453M223 411C277 411 304 372 304 294C304 215 277 175 223 175C168 175 141 214 141 293C141 372 168 411 223 411M164 4L222 4C274 4 316 1 348-6C391-15 412-39 412-77C412-109 392-134 352-153C321-168 287-175 250-175C214-175 180-168 148-153C107-134 87-109 87-77C87-35 123 4 164 4Z" transform="translate(10865,0)"></path><path data-c="73" d="M360 130C360 206 308 254 205 274C158 283 128 294 112 305C96 316 88 332 88 351C88 398 123 421 193 421C261 421 298 383 303 306C304 298 309 294 319 294C330 294 336 303 336 321L336 420C336 439 331 448 321 448C310 448 295 430 285 422C260 439 229 448 193 448C104 448 33 408 33 323C33 289 47 260 76 237C111 208 144 202 207 191C272 178 305 149 305 104C305 47 270 18 199 18C130 18 86 64 67 155C64 168 59 175 50 175C39 175 33 165 33 146L33 17C33-2 38-11 48-11C51-11 56-8 64-1C82 15 74 12 92 30C121 3 157-11 199-11C294-11 360 38 360 130Z" transform="translate(11365,0)"></path><path data-c="20" d="" transform="translate(11759,0)"></path><path data-c="47" d="M667 443L667 677C667 696 662 705 652 705C645 705 639 700 633 691L586 622C568 640 547 657 524 673C491 694 451 705 404 705C306 705 223 670 156 598C89 526 56 441 56 342C56 243 90 158 157 86C224 14 307-22 406-22C495-22 557 5 591 59C599 46 639 1 654 1C663 1 667 10 667 27L667 200C667 218 670 229 676 233C682 237 702 239 735 239L735 278L610 275C547 273 492 274 447 278L447 239L483 239C524 239 549 236 558 231C567 226 571 214 571 196L571 132C571 83 549 50 506 34C474 23 446 17 422 17C383 17 343 27 304 48C210 97 166 193 166 342C166 483 215 589 303 636C342 656 379 666 416 666C539 666 611 553 627 435C629 422 636 415 647 415C667 415 667 423 667 443Z" transform="translate(12091,0)"></path><path data-c="72" d="M364 378C364 417 327 442 288 442C237 442 198 412 172 351L172 442L28 431L28 393C63 393 84 390 92 384C100 378 104 365 104 342L104 79C104 60 101 48 94 44C87 40 65 38 28 38L28 0L143 3C185 4 228 3 271 0L271 38L247 38C214 38 193 41 186 46C179 51 176 63 176 81L176 232C176 275 184 314 199 347C219 390 248 412 287 413C277 403 272 391 272 377C272 346 287 331 318 331C345 331 364 352 364 378Z" transform="translate(12876,0)"></path><path data-c="6F" d="M249-11C311-11 363 11 406 55C449 99 471 152 471 214C471 277 450 332 408 378C366 424 313 448 250 448C187 448 135 424 92 378C49 332 28 277 28 214C28 152 49 99 92 55C135 11 188-11 249-11M250 21C202 21 166 42 141 85C125 113 117 159 117 222C117 283 125 327 140 355C164 398 200 419 249 419C296 419 332 398 357 357C374 329 382 284 382 222C382 106 348 21 250 21Z" transform="translate(13268,0)"></path><path data-c="77" d="M620 332C634 371 662 392 703 393L703 431C672 428 646 427 627 428L531 431L531 393C569 392 588 377 588 349C588 345 587 339 584 330L497 89L403 353C400 362 399 367 399 370C399 385 418 393 457 393L457 431L350 428C330 427 299 428 258 431L258 393C293 393 314 388 321 379C324 376 332 359 343 328L262 102L172 354C169 363 167 368 167 370C167 385 187 393 226 393L226 431L114 428L18 431L18 393C67 393 80 394 91 361L216 13C222-3 230-11 240-11C251-11 259-4 264 11L360 279L457 10C462-4 470-11 481-11C488-10 496-3 504 10Z" transform="translate(13768,0)"></path><path data-c="74" d="M332 126L332 186L300 186L300 128C300 78 282 22 238 22C197 22 177 56 177 124L177 395L316 395L316 433L177 433L177 615L145 615C144 582 141 553 134 527C117 461 78 427 19 424L19 395L102 395L102 126C102 67 120 29 155 10C182-4 207-11 232-11C298-11 332 55 332 126Z" transform="translate(14490,0)"></path><path data-c="68" d="M314 415C359 415 382 380 382 309L382 79C382 60 379 48 372 44C365 40 343 39 306 39L306 0L421 3L535 0L535 39C503 39 482 40 472 43C462 46 458 53 458 64L458 253C458 301 457 334 454 353C444 414 399 445 320 445C259 445 213 417 182 361L182 694L32 683L32 645C68 645 90 642 98 636C106 630 109 616 109 593L109 79C109 60 105 48 98 44C91 40 69 39 32 39L32 0L147 3L261 0L261 39C224 39 203 40 196 44C189 48 185 60 185 79L185 261C185 342 236 415 314 415Z" transform="translate(14879,0)"></path><path data-c="20" d="" transform="translate(15435,0)"></path><path data-c="52" d="M611 501C611 558 580 604 519 639C468 668 411 683 349 683L35 683L35 644L63 644C98 644 119 641 126 636C133 631 137 620 137 602L137 81C137 63 133 51 126 46C119 41 98 38 63 38L35 38L35-1C62 2 111 3 183 3C256 3 305 2 332-1L332 38L304 38C269 38 248 41 241 46C234 51 230 63 230 81L230 327L340 327C404 327 451 289 462 235C464 224 465 200 465 165C465 138 466 118 467 106C473 22 546-22 634-22C672-22 699-6 714 25C726 48 732 70 732 91C732 104 726 111 715 111C706 111 700 105 699 92C694 36 673 8 638 8C603 8 591 41 585 70C579 104 575 130 572 147L559 226C544 278 507 316 448 339C524 361 611 415 611 501M470 600C491 581 502 548 502 501C502 463 492 431 473 404C450 373 405 357 336 357L230 357L230 609C230 630 236 641 248 642C255 643 274 644 306 644C385 644 426 641 470 600Z" transform="translate(15767,0)"></path><path data-c="61" d="M483 91L483 150L451 150L451 91C451 52 440 32 419 32C399 32 387 57 387 77L387 274C387 309 383 335 376 353C352 412 283 448 213 448C136 448 60 405 60 333C60 300 77 283 110 283C143 283 159 299 159 332C159 361 144 378 113 381C135 406 168 419 211 419C273 419 311 362 311 297L311 264C233 259 174 247 133 228C66 197 32 154 32 97C32 71 42 49 61 32C93 3 137-11 193-11C252-11 294 14 320 65C327 27 355-6 398-6C451-6 483 36 483 91M200 18C154 18 116 52 116 98C116 191 213 231 311 236L311 141C311 74 267 18 200 18Z" transform="translate(16503,0)"></path><path data-c="74" d="M332 126L332 186L300 186L300 128C300 78 282 22 238 22C197 22 177 56 177 124L177 395L316 395L316 433L177 433L177 615L145 615C144 582 141 553 134 527C117 461 78 427 19 424L19 395L102 395L102 126C102 67 120 29 155 10C182-4 207-11 232-11C298-11 332 55 332 126Z" transform="translate(17003,0)"></path><path data-c="65" d="M415 251C415 366 348 448 236 448C176 448 126 425 85 378C47 334 28 281 28 220C28 157 49 104 91 58C133 12 185-11 247-11C304-11 348 9 378 48C403 81 415 105 415 121C415 131 409 136 398 136C390 136 385 131 382 120C360 54 317 21 253 21C208 21 173 42 146 85C127 116 118 163 117 227L387 227C409 227 415 229 415 251M326 349C337 316 342 284 342 255L118 255C121 321 138 366 168 389C193 409 215 419 236 419C281 419 311 396 326 349Z" transform="translate(17392,0)"></path></g><rect width="12812" height="60" x="120" y="220"></rect></g></g></g></svg></mjx-container></p><p>PE is calculated based on the “Price-to-Earnings Ratio” indicator, while the G value matches the corresponding value based on the reporting period, similar to the calculation method of Dynamic PE: Q1 report determines growth based on current-year Q1 net profit × 4 compared to the previous year’s net profit; Q2 report determines growth based on current-year interim net profit × 2 compared to the previous year’s net profit, and so forth.</p><h3 id="Application-4"><a href="#Application-4" class="headerlink" title="Application"></a>Application</h3><p>Suppose a stock has a price-to-earnings ratio of 30, and analysts project its earnings growth rate for the next year to be 50%. The PEG ratio for this stock would be just 0.6.</p><h1 id="Summary"><a href="#Summary" class="headerlink" title="Summary"></a>Summary</h1><p>When I use F10 technical indicators, they are generally used for the first round of stock selection to pick out undervalued stocks with growth potential. In the second round, real-time trend technical indicators like MACD, KDJ, and CII are used.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Have any of you ever invested in stocks? How do you pick your stocks?&lt;/p&gt;
&lt;p&gt;Is it by checking Alipay’s “Gold Selection” recommendations?</summary>
      
    
    
    
    <category term="Quantitative Trading" scheme="https://project256.com/en/categories/quantitative-trading/"/>
    
    
    <category term="Quantitative Trading" scheme="https://project256.com/en/tags/quantitative-trading/"/>
    
  </entry>
  
  <entry>
    <title>2025</title>
    <link href="https://project256.com/en/chit-chat/2025/"/>
    <id>https://project256.com/en/chit-chat/2025/</id>
    <published>2024-12-31T16:00:00.000Z</published>
    <updated>2026-08-04T09:39:04.561Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Happy-New-Year-2025"><a href="#Happy-New-Year-2025" class="headerlink" title="Happy New Year 2025!"></a>Happy New Year 2025!</h1><p>I haven’t updated my blog for a long time. On one hand, I was hopelessly addicted to LOL and CS2; on the other hand, I had to deal with tasks of higher ROI. When the pressure got too intense, I went back to obsessively playing LOL for strong stress relief (even though there are plenty of stress monsters there too…).</p><p>In 2024, I only completed 17&#x2F;64 of my planned goals, but did I really not grow at all this year? My therapist and I both felt that the list of plans was very good and comprehensive, but lacked a main line.</p><p>I think that’s true. Even though I made detailed plans across five major directions—“Health,” “Interpersonal,” “Primary Career,” “Value-Add Output,” “Time Management,” “Self-Improvement,” “Finances,” and “Art”—it was hard to see what I truly wanted.</p><p>“I don’t know” and “I’m not clear”—these words are the best excuses to run away. Sooner or later, I will inevitably have to face the goal I truly want. For me, that goal is “freedom”—</p><ul><li>Freedom over my clock-in time</li><li>Freedom of choice</li><li>Freedom in how to face the outcomes of my choices</li><li>Freedom to bask in the sun and take an afternoon nap anywhere…</li></ul><h1 id="The-Responsibilities-and-Obligations-of-Freedom"><a href="#The-Responsibilities-and-Obligations-of-Freedom" class="headerlink" title="The Responsibilities and Obligations of Freedom"></a>The Responsibilities and Obligations of Freedom</h1><p>In 2024, I read a stack of books about the path to financial freedom. I believe that before discussing how to enjoy freedom, bringing one’s finances to a safe threshold and building an oil field that continuously pumps money are responsibilities and obligations that require dedicated effort.</p><p>Can I enjoy the above freedoms right now? Yes, absolutely. I could submit my resignation right now, spend lavishly, and when the money runs out, take out online loans and become a hall-of-famer in “Jieshe” (Addiction Recovery Community).</p><p>However, the unit of measurement for wealth is time, not dollars or any other currency. It refers to the number of days you can sustain your lifestyle after you stop working.</p><p>Once I find and build an oil field that continuously pumps money, no matter where I am in the world, I can generate wealth that matches my past efforts, thereby gaining freedom.</p><p>Of the two ways to achieve freedom above, one is seeking outward: begging the company for a paycheck, begging online loan companies for approval, begging friends for money, begging banks to lower interest rates… The other is seeking inward: finding an answer that suits myself, trying through trial and error, and executing continuously.</p><h1 id="The-Second-Growth-Curve"><a href="#The-Second-Growth-Curve" class="headerlink" title="The Second Growth Curve"></a>The Second Growth Curve</h1><p>In 2024, I consulted many people, asking most frequently whether they had a second career outside of their main job. If not, I would see if there was potential for win-win cooperation.</p><p>As an old-timer who has wandered through every team inside the company, I deeply understand how ridiculous it is to burn all your time and energy showing loyalty to and risking your life for a company. A single instance of standing on the wrong side or force majeure like infighting above can render all past efforts completely wasted.</p><p>Therefore, for the rest of my career working for others, cultivating myself, treating the company as a networking space, reverse-“exploiting” the company, and having the company make up for the surplus value difference will be top priority among priorities.</p><p>Fortunately, from trying out various directions at the beginning of the year to the end of the year, I filtered out directions that didn’t suit me, and put more thought and practice into the ones that did.</p><p>Simply put, one of my directions in 2025 is quantitative trading, and another direction is using replicable tools as leverage to provide convenience and value for more people and myself.</p><p>Over the past few years, I built a bunch of tools for the company, which enabled countless people to achieve high performance. But once I resign, everything disappears except for experience. So I need to build tools for my private domain to serve as leverage I can use for myself.</p><p>Of course, like all other entrepreneurs in the software industry, starting out still requires outsourcing as a means of initial capital accumulation.</p><p>In 2025, I wish everyone can find a development direction that suits them, discover their second growth curve through thinking and practice, and achieve “freedom” in every aspect.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;Happy-New-Year-2025&quot;&gt;&lt;a href=&quot;#Happy-New-Year-2025&quot; class=&quot;headerlink&quot; title=&quot;Happy New Year 2025!&quot;&gt;&lt;/a&gt;Happy New Year 2025!&lt;/h1&gt;&lt;p&gt;</summary>
      
    
    
    
    <category term="Chit-chat" scheme="https://project256.com/en/categories/chit-chat/"/>
    
    
    <category term="Thoughts" scheme="https://project256.com/en/tags/thoughts/"/>
    
  </entry>
  
  <entry>
    <title>Appel&#39;s Moving Average Trading Technique #1</title>
    <link href="https://project256.com/en/quantitative-trading/%E9%98%BF%E4%BD%A9%E5%B0%94%E5%9D%87%E7%BA%BF%E6%93%8D%E7%9B%98%E6%9C%AF-1/"/>
    <id>https://project256.com/en/quantitative-trading/%E9%98%BF%E4%BD%A9%E5%B0%94%E5%9D%87%E7%BA%BF%E6%93%8D%E7%9B%98%E6%9C%AF-1/</id>
    <published>2024-11-02T08:10:47.000Z</published>
    <updated>2026-08-04T09:39:04.564Z</updated>
    
    <content type="html"><![CDATA[<p><em>Appel’s Moving Average Trading Systems</em> is authored by Gerald Appel, the creator of the MACD technical indicator. Skeptical of all second-hand processed knowledge, I decided to pick up and read this book. Thinking in reverse, searching for the title of this book on social media should also yield information that can genuinely help us.</p><p>The very first sentence of the preface made up my mind to read this book cover to cover: “If you have been hurt by naively trusting your brokerage firm, your familiar mutual fund manager, or blindly trusting those popular trading gurus, then this book was written specifically for you.”</p><p>At the same time, the author promises in the preface that the two technical indicators covered in Chapter 1 require only 5 to 10 minutes of analysis time per week—enough for investors to judge whether the market climate is favorable or unfavorable for trading. Even if you stop reading after Chapter 1, you will already have mastered very useful analytical tools capable of enhancing your investment performance.</p><p>Although successful investing is simple—what to buy and sell, and when to buy and sell—there are rarely clear explanations of technical indicators. Let us step onto the “millionaire fast lane” through this book.</p><hr><h1 id="1-Super-Practical-Investment-Strategies"><a href="#1-Super-Practical-Investment-Strategies" class="headerlink" title="#1 Super Practical Investment Strategies"></a>#1 Super Practical Investment Strategies</h1><h2 id="The-Primary-Principle-of-Selecting-Investment-Tools"><a href="#The-Primary-Principle-of-Selecting-Investment-Tools" class="headerlink" title="The Primary Principle of Selecting Investment Tools"></a>The Primary Principle of Selecting Investment Tools</h2><ul><li>What matters is not how much you can earn, but how much you are prepared to lose.</li></ul><p>To make up for losses in the stock market, you must generate a gain percentage greater than the percentage lost. The order in which losses and gains occur does not matter.</p><p>To ensure long-term investment success, preserving principal is far more important than achieving occasional windfall profits.</p><p>Overall, if investors primarily put their money into high-risk funds, their final profits are actually not significant. Over the long run, fund groups with low volatility and high volatility yield essentially the same investment returns, but the former carries lower risk.</p><p><strong>Conclusion: Returns from investing in high-volatility stocks are lower than returns from investing in low-volatility stocks.</strong></p><h2 id="Relative-Strength-Investing-Strategy—Consistently-Holding-the-Optimal-Portfolio"><a href="#Relative-Strength-Investing-Strategy—Consistently-Holding-the-Optimal-Portfolio" class="headerlink" title="Relative Strength Investing Strategy—Consistently Holding the Optimal Portfolio"></a>Relative Strength Investing Strategy—Consistently Holding the Optimal Portfolio</h2><p>The basic principles are as follows:</p><ul><li>Identify leading stocks</li><li>Buy leading stocks</li><li>Hold leading stocks as long as they maintain their leadership position</li><li>When leading stocks begin to slow down their upward speed, sell them and buy new leading stocks</li></ul><p>From a pool of mutual funds, pick and buy at least two, preferably four to five mutual funds that ranked in the top 10% of performance over the past three months (mutual funds among the top ten whose volatility is approximately equal to or does not exceed that of the S&amp;P 500 index).</p><p>Even a portfolio with only two funds offers greater safety than a portfolio with just one fund. It works even better to look for funds that waive purchase and redemption fees as long as they are held for more than 90 days.</p><p>With the release of new quarterly report data, investors should review their portfolio every three months. If the performance of any single fund drops out of the top 10%, sell it and replace it with another fund that has remained in or just entered the top 10%. Continue holding the remaining funds whose current performance stays in the top 10%.</p><p>By following this process, you can rebalance and reallocate your mutual fund portfolio from time to time, holding a mutual fund portfolio that consistently outperforms the industry average in every quarter. Ultimately, your portfolio will consist of the top-performing mutual funds, akin to placing bets on racehorses that lead in every lap.</p><p><strong>Conclusion: Over the long term, even if adopting a more aggressive investment strategy brings additional returns, those extra gains are very limited.</strong></p><p><strong>Conclusion 2: Increasing the volatility of a mutual fund portfolio does not significantly boost returns, whereas the risk level genuinely increases. Therefore, as a general rule, you should stick to focusing your investments on mutual funds with below-average volatility.</strong></p><h2 id="Three-Steps-to-Managing-a-Mutual-Fund-Portfolio"><a href="#Three-Steps-to-Managing-a-Mutual-Fund-Portfolio" class="headerlink" title="Three Steps to Managing a Mutual Fund Portfolio"></a>Three Steps to Managing a Mutual Fund Portfolio</h2><ol><li>You need a data source capable of accessing price and volatility data for at least 500 mutual funds (the more, the better) for the most recent quarter.</li><li>Open a mutual fund investment account capable of portfolio diversification to invest in mutual funds that ranked in the top 10% of performance in the previous quarter and have a volatility lower than the S&amp;P 500 index—with the volatility of these mutual funds not exceeding the average volatility of the entire portfolio at most.</li><li>At the beginning of each new quarter, eliminate funds that have fallen out of the top 10% and replace them with newly entering top-10% mutual funds.</li></ol><h1 id="2-Two-Easy-to-Use-Stock-Market-Indicators"><a href="#2-Two-Easy-to-Use-Stock-Market-Indicators" class="headerlink" title="#2 Two Easy-to-Use Stock Market Indicators"></a>#2 Two Easy-to-Use Stock Market Indicators</h1><h2 id="Monetary-Indicator"><a href="#Monetary-Indicator" class="headerlink" title="Monetary Indicator"></a>Monetary Indicator</h2><p>The stock market performs best when interest rates are steadily falling, and worst when interest rates are steadily rising.</p><h2 id="Nasdaq-x2F-NYSE-Relative-Strength-Indicator"><a href="#Nasdaq-x2F-NYSE-Relative-Strength-Indicator" class="headerlink" title="Nasdaq&#x2F;NYSE Relative Strength Indicator"></a>Nasdaq&#x2F;NYSE Relative Strength Indicator</h2><p>When the relative strength of the Nasdaq Composite Index leads the New York Stock Exchange Index, the probability of a market rise increases significantly.</p><p>The total annualized return of investing with the Nasdaq&#x2F;NYSE ratio method is far higher than the buy-and-hold strategy, yet the time committed to investing is only a little over half of the latter—for those looking to reduce their effort and time spent in the stock market, this indicator can also be used when making standalone buying and selling decisions.</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/yCd8ZV.jpg" alt="project256.oss-cn-beijing.aliyuncs.com/uPic/yCd8ZV.jpg" title="">                </div>                <div class="image-caption">project256.oss-cn-beijing.aliyuncs.com/uPic/yCd8ZV.jpg</div>            </figure><h1 id="3-Moving-Averages-and-Rate-of-Change-ROC-Indicator-Tracking-Trend-and-Momentum"><a href="#3-Moving-Averages-and-Rate-of-Change-ROC-Indicator-Tracking-Trend-and-Momentum" class="headerlink" title="#3 Moving Averages and Rate of Change (ROC) Indicator: Tracking Trend and Momentum"></a>#3 Moving Averages and Rate of Change (ROC) Indicator: Tracking Trend and Momentum</h1><p>Moving average systems are used to smooth out the “noise” of short-term price fluctuations, making it easier for investors to identify and define major market trends.</p><p><strong>Always monitor the slope of the moving average and the height of its fluctuations: the longer the impulse lasts and the steeper the slope, the higher the probability of a sustained trend; if the slope and impulse begin to flatten, the risk of a market reversal becomes imminent. (Crossover above and below signals mark above-average rises or below-average declines, respectively.)</strong></p><p>When studying moving average trading channels, we need to combine them with Elliott Wave Theory (which states that market trends continuously repeat a pattern, where each cycle consists of 5 impulse waves upward and 3 corrective waves downward), as it serves as a highly effective market timing tool.</p><h2 id="Interpretation-and-Application-of-the-Rate-of-Change-ROC-Indicator"><a href="#Interpretation-and-Application-of-the-Rate-of-Change-ROC-Indicator" class="headerlink" title="Interpretation and Application of the Rate of Change (ROC) Indicator"></a>Interpretation and Application of the Rate of Change (ROC) Indicator</h2><p>ROC &#x3D; (Today’s Closing Price - Closing Price n days ago) &#x2F; Closing Price n days ago</p><p>When ROC rises to a relatively high level, it is called an overbought state and is usually regarded as a sell signal. When ROC falls to a relatively low level, it is called an oversold state and is usually regarded as a buy signal.</p><p>When ROC crosses above the zero line from below, it indicates that buyers are strong, serving as a buy signal; crossing from above to below serves as a sell signal.</p><p>A pattern where prices reach new highs while momentum indicators decline is called a bearish divergence (top divergence). The occurrence of a bearish divergence signals an impending bear market, because the momentum pushing the market up fails to keep pace with the rise in market prices, causing upward strength to weaken and leading inevitably to a decline. Conversely, when momentum readings rise continuously while price levels fall to new lows, this reflects that downward momentum is gradually waning. This is called a bullish divergence (bottom divergence), which is a signal of an approaching bull market. However, divergence alone cannot serve as a trading signal and should be analyzed in combination with the ROC moving average.</p><p>The 10-day ROC indicator is very useful for short-term trading, while 21-day to 25-day ROC indicators are commonly used for medium-term trading analysis. Combining short-term and long-term ROC indicators can be extremely advantageous for trading. The specific method is: <strong>use the readings of the short-term ROC indicator to make an initial forecast of the trend direction first, and then use the long-term ROC indicator to confirm the direction.</strong></p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/bZwIQL.jpg" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><p>In most cases, the starting point of a significant market rally is not when the ROC and other momentum indicators reach their lowest readings or are in the oversold territory; rather, it often begins after momentum indicators have already been rising from their lowest readings for some time. For example, when the rally in October 2002 began in the figure above, the 21-day ROC indicator had already constructed a bullish double-bottom pattern—with the second low higher than the previous low.</p><p>However, as in Region C, when prices reached their peak in November, the ROC indicator was in a continuous downtrend. This is a classic bearish divergence, signaling future market weakness.</p><h2 id="Triple-Momentum-Nasdaq-Index-Trading-Model"><a href="#Triple-Momentum-Nasdaq-Index-Trading-Model" class="headerlink" title="Triple-Momentum Nasdaq Index Trading Model"></a>Triple-Momentum Nasdaq Index Trading Model</h2><p>A market timing model based on the principle of “fight when you can win, run when you can’t.” It enables investors to effectively avoid trades with low probability of profit, thereby significantly reducing trading risks.</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/8b0Oo1.jpg" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><p>Maintain three daily-level ROC indicators: a 5-day ROC of the Nasdaq Composite Index closing price, a 15-day ROC indicator, and a 25-day ROC indicator.</p><p>For example, if today’s closing price is 2000 and it was 1900 10 days ago, the value of the 10-day ROC is +5.26% (2000 - 1900 &#x3D; 100; 100 &#x2F; 1900 &#x3D; 0.0526; 0.0526 * 100 &#x3D; +5.26%).</p><p>At the close of each trading day, add up the percentage-based values of the 5-day, 15-day, and 25-day ROC indicators to obtain a composite ROC value—today’s triple-momentum value. For example: +3.0% for 5-day, +4.5% for 15-day, and +6% for 25-day would result in a triple-momentum value of +13.5%, indicating that the index has crossed upward across all timeframes, signaling an impending market rise.</p><p>There is only one rule for buying and selling: <strong>buy when the specific value of the triple-momentum indicator—the sum of the 5-day, 15-day, and 25-day ROC readings—crosses above 4% from below, and sell when it breaks below 4% from above.</strong></p><p><strong>There are no other rules; this is a model so simple it is elegantly perfect.</strong></p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/qm3hl8.jpg" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/pRhlKm.jpg" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><h1 id="4-Beyond-Charts-Powerful-Technical-Graphic-Tools"><a href="#4-Beyond-Charts-Powerful-Technical-Graphic-Tools" class="headerlink" title="#4 Beyond Charts: Powerful Technical Graphic Tools"></a>#4 Beyond Charts: Powerful Technical Graphic Tools</h1><h2 id="Synergy"><a href="#Synergy" class="headerlink" title="Synergy"></a>Synergy</h2><p>Refers to the combined effect produced by adding or blending two or more analytical tools together being greater than the sum of their individual applications—commonly known as 1 + 1 &gt; 2.</p><p>There is no perfect stock market indicator, nor even any near-perfect indicator. Taking a step back, even if a perfect technical indicator existed, its secrets would eventually become common knowledge, and as investors began following it collectively, its effectiveness would diminish day by day. Even the best predictions are imperfect, achieving success only within a certain probability at best. A relatively realistic goal is to make correct decisions as often as possible, developing the ability to quickly recognize mistakes, take appropriate action, and adjust one’s mindset properly—even if it means accepting losses in the stock market (in general, the best way to handle losses in the stock market is to cut losses quickly).</p><p>Successful investors do not hold security positions continuously. They evaluate the probability of success for a given opportunity as carefully as possible and invest only when the odds are favorable. One method to increase the probability of winning is <strong>applying synergy: if multiple indicators mutually confirm expectations of stock market movements, the probability of trading success increases dramatically.</strong> (For example, a single indicator has a 60% success rate, two indicators combined reach 84%, and three indicators reach 93.6%.)</p><h2 id="Price-Forecasting-Based-on-Angular-Changes"><a href="#Price-Forecasting-Based-on-Angular-Changes" class="headerlink" title="Price Forecasting Based on Angular Changes"></a>Price Forecasting Based on Angular Changes</h2><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/Kaj89d.jpg" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><p>If trading volume is relatively light during a steep rally or decline, the angle of price movement can easily change and become flatter. Conversely, a steadily rising or falling market may suddenly experience a change in slope, moving at an almost vertical angle.</p><p>When such a pattern is identified, you should currently be at the early stage of the second segment, at which point you can accurately forecast the magnitude and remaining duration of the market movement. The specific procedure takes two steps: Step 1, calculate the total time along the angle of the first rise or fall, referred to as Segment A; Step 2, once you identify a change in the price movement angle relative to Segment A, immediately proceed to measure the length of the second segment, referred to as Segment B. The specific measurement method is to project out from the starting point of Segment B, plotting a complete trend forecast using the length of Segment A along the angle of Segment B. This enables you to forecast not only the extension length of Segment B, but also the runtime duration of Segment B.</p><hr><p>That’s all for Part 1 for now. Since I haven’t figured out how to apply this to the A-share market, and because studying these might not be as straightforward as directly investing in US stock indices, I won’t continue reading further until I figure it out.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;&lt;em&gt;Appel’s Moving Average Trading Systems&lt;/em&gt; is authored by Gerald Appel, the creator of the MACD technical indicator. Skeptical of al</summary>
      
    
    
    
    <category term="Quantitative Trading" scheme="https://project256.com/en/categories/quantitative-trading/"/>
    
    
    <category term="Quantitative Trading" scheme="https://project256.com/en/tags/quantitative-trading/"/>
    
    <category term="Python" scheme="https://project256.com/en/tags/python/"/>
    
  </entry>
  
  <entry>
    <title>Batch Generating Android APK Packages</title>
    <link href="https://project256.com/en/tools/%E6%89%B9%E9%87%8F%E7%94%9F%E6%88%90%E5%AE%89%E5%8D%93apk%E5%8C%85/"/>
    <id>https://project256.com/en/tools/%E6%89%B9%E9%87%8F%E7%94%9F%E6%88%90%E5%AE%89%E5%8D%93apk%E5%8C%85/</id>
    <published>2024-08-01T15:23:25.000Z</published>
    <updated>2026-08-04T09:39:04.563Z</updated>
    
    <content type="html"><![CDATA[<p>One day in April, I landed a freelance gig to batch-generate Android APK packages. The requirement was that upon opening the app, it would display different background images to showcase the main character’s formidable professional capabilities.</p><p>Since I had never written native Android before—at most using React Native for cross-platform apps—manually creating a bunch of projects and modifying them by hand would be pure manual labor, which was definitely out of the question.</p><h1 id="LLM-First"><a href="#LLM-First" class="headerlink" title="LLM First"></a>LLM First</h1><p>Lately, I’ve been using LLMs to solve repetitive tasks, so I directly asked an LLM how to write this thing.</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">假如你是安卓和Bash脚本开发专家，我需要通过Bash命令生成不定数量的安卓安装包，每个安卓安装包都有不同的图标和图片，请写出Bash脚本。</span><br></pre></td></tr></table></figure><p>Although GPT-4 wrote a lot of Bash script content, errors still popped up in the minor details, so I fed the error messages back to the LLM to resolve them.</p><p>However, keep in mind that longer conversations aren’t necessarily better. Sometimes, when the raw error messages and code get too long, severe hallucinations occur. Therefore, after writing a bit, running a bit, and verifying a bit, I cleared the context and asked anew.</p><h1 id="Learning-Android-and-Bash-Together"><a href="#Learning-Android-and-Bash-Together" class="headerlink" title="Learning Android and Bash Together"></a>Learning Android and Bash Together</h1><p>Finally, after relentless debugging by the LLM and human effort (just about 2 hours), a runnable Bash script was produced:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line">PROJECTS_COUNT=1</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> $(seq 1 <span class="variable">$PROJECTS_COUNT</span>); <span class="keyword">do</span></span><br><span class="line">    PROJECT_NAME=<span class="string">"Dual_Record_<span class="variable">$(openssl rand -hex 3)</span>"</span></span><br><span class="line">    PACKAGE_NAME=<span class="string">"com.example.<span class="variable">$PROJECT_NAME</span>"</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 创建基础的项目结构</span></span><br><span class="line">    mkdir -p <span class="string">"./<span class="variable">$PROJECT_NAME</span>/app/src/main/java/<span class="variable">$PACKAGE_NAME</span>"</span></span><br><span class="line">    mkdir -p <span class="string">"./<span class="variable">$PROJECT_NAME</span>/app/src/main/res/layout"</span></span><br><span class="line">    mkdir -p <span class="string">"./<span class="variable">$PROJECT_NAME</span>/app/src/main/res/mipmap"</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"rootProject.name='<span class="variable">$PROJECT_NAME</span>'"</span> &gt; <span class="string">"./<span class="variable">$PROJECT_NAME</span>/settings.gradle"</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"include ':app'"</span> &gt; <span class="string">"./<span class="variable">$PROJECT_NAME</span>/settings.gradle"</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 复制图标到项目中</span></span><br><span class="line">    cp /Users/project256/Downloads/5a1acc930a4f210f5ee9e73e45bec3c7_1.png <span class="string">"./<span class="variable">$PROJECT_NAME</span>/app/src/main/res/mipmap/ic_launcher.png"</span></span><br><span class="line">    cp /Users/project256/Downloads/5a1acc930a4f210f5ee9e73e45bec3c7_1.png <span class="string">"./<span class="variable">$PROJECT_NAME</span>/app/src/main/res/mipmap/ic_launcher_round.png"</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 创建strings.xml文件并添加app_name字符串资源</span></span><br><span class="line">    mkdir -p <span class="string">"./<span class="variable">$PROJECT_NAME</span>/app/src/main/res/values/"</span></span><br><span class="line">    cat &gt; <span class="string">"./<span class="variable">$PROJECT_NAME</span>/app/src/main/res/values/strings.xml"</span> &lt;&lt;EOF</span><br><span class="line">&lt;?xml version=<span class="string">"1.0"</span> encoding=<span class="string">"utf-8"</span>?&gt;</span><br><span class="line">&lt;resources&gt;</span><br><span class="line">    &lt;string name=<span class="string">"app_name"</span>&gt;APP名字&lt;/string&gt;</span><br><span class="line">&lt;/resources&gt;</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Inside the loop, setting PACKAGE_NAME and PROJECT_NAME</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># This goes in the project-level build.gradle creation step</span></span><br><span class="line">cat &gt; <span class="string">"./<span class="variable">$PROJECT_NAME</span>/build.gradle"</span> &lt;&lt;EOF</span><br><span class="line">buildscript &#123;</span><br><span class="line">    repositories &#123;</span><br><span class="line">        google()</span><br><span class="line">        mavenCentral()</span><br><span class="line">    &#125;</span><br><span class="line">    dependencies &#123;</span><br><span class="line">        // Make sure to use a compatible version</span><br><span class="line">        classpath <span class="string">'com.android.tools.build:gradle:7.4.2'</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">allprojects &#123;</span><br><span class="line">    repositories &#123;</span><br><span class="line">        google()</span><br><span class="line">        mavenCentral()</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">task clean(<span class="built_in">type</span>: Delete) &#123;</span><br><span class="line">    delete rootProject.buildDir</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line"><span class="comment"># Adjusted AndroidManifest.xml creation with android:exported="true"</span></span><br><span class="line">cat &gt; <span class="string">"./<span class="variable">$PROJECT_NAME</span>/app/src/main/AndroidManifest.xml"</span> &lt;&lt;EOF</span><br><span class="line">&lt;?xml version=<span class="string">"1.0"</span> encoding=<span class="string">"utf-8"</span>?&gt;</span><br><span class="line">&lt;manifest xmlns:android=<span class="string">"http://schemas.android.com/apk/res/android"</span></span><br><span class="line">    package=<span class="string">"<span class="variable">$PACKAGE_NAME</span>"</span>&gt;</span><br><span class="line"></span><br><span class="line">    &lt;application</span><br><span class="line">        android:allowBackup=<span class="string">"true"</span></span><br><span class="line">        android:icon=<span class="string">"@mipmap/ic_launcher"</span></span><br><span class="line">        android:label=<span class="string">"@string/app_name"</span></span><br><span class="line">        android:roundIcon=<span class="string">"@mipmap/ic_launcher_round"</span></span><br><span class="line">        android:supportsRtl=<span class="string">"true"</span></span><br><span class="line">        android:theme=<span class="string">"@style/Theme.AppCompat.Light.NoActionBar"</span>&gt;</span><br><span class="line">        &lt;activity android:name=<span class="string">".MainActivity"</span></span><br><span class="line">            android:exported=<span class="string">"true"</span>&gt; &lt;!-- Ensure this line is added --&gt;</span><br><span class="line">            &lt;intent-filter&gt;</span><br><span class="line">                &lt;action android:name=<span class="string">"android.intent.action.MAIN"</span> /&gt;</span><br><span class="line">                &lt;category android:name=<span class="string">"android.intent.category.LAUNCHER"</span> /&gt;</span><br><span class="line">            &lt;/intent-filter&gt;</span><br><span class="line">        &lt;/activity&gt;</span><br><span class="line">    &lt;/application&gt;</span><br><span class="line"></span><br><span class="line">&lt;/manifest&gt;</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 创建一个简单的MainActivity</span></span><br><span class="line">    cat &gt; <span class="string">"./<span class="variable">$PROJECT_NAME</span>/app/src/main/java/<span class="variable">$PACKAGE_NAME</span>/MainActivity.java"</span> &lt;&lt;EOF</span><br><span class="line">package <span class="variable">$PACKAGE_NAME</span>;</span><br><span class="line"></span><br><span class="line">import android.os.Bundle;</span><br><span class="line">import androidx.appcompat.app.AppCompatActivity;</span><br><span class="line"></span><br><span class="line">public class MainActivity extends AppCompatActivity &#123;</span><br><span class="line">    @Override</span><br><span class="line">    protected void onCreate(Bundle savedInstanceState) &#123;</span><br><span class="line">        super.onCreate(savedInstanceState);</span><br><span class="line">        setContentView(R.layout.activity_main);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 创建一个简单的activity_main.xml布局文件</span></span><br><span class="line">    cat &gt; <span class="string">"./<span class="variable">$PROJECT_NAME</span>/app/src/main/res/layout/activity_main.xml"</span> &lt;&lt;EOF</span><br><span class="line">&lt;?xml version=<span class="string">"1.0"</span> encoding=<span class="string">"utf-8"</span>?&gt;</span><br><span class="line">&lt;androidx.constraintlayout.widget.ConstraintLayout xmlns:android=<span class="string">"http://schemas.android.com/apk/res/android"</span></span><br><span class="line">    xmlns:app=<span class="string">"http://schemas.android.com/apk/res-auto"</span></span><br><span class="line">    xmlns:tools=<span class="string">"http://schemas.android.com/tools"</span></span><br><span class="line">    android:layout_width=<span class="string">"match_parent"</span></span><br><span class="line">    android:layout_height=<span class="string">"match_parent"</span></span><br><span class="line">    tools:context=<span class="string">".MainActivity"</span>&gt;</span><br><span class="line"></span><br><span class="line">    &lt;TextView</span><br><span class="line">        android:layout_width=<span class="string">"wrap_content"</span></span><br><span class="line">        android:layout_height=<span class="string">"wrap_content"</span></span><br><span class="line">        android:text=<span class="string">"欢迎使用XX系统_<span class="variable">$PROJECT_NAME</span>!"</span></span><br><span class="line">        app:layout_constraintBottom_toBottomOf=<span class="string">"parent"</span></span><br><span class="line">        app:layout_constraintLeft_toLeftOf=<span class="string">"parent"</span></span><br><span class="line">        app:layout_constraintRight_toRightOf=<span class="string">"parent"</span></span><br><span class="line">        app:layout_constraintTop_toTopOf=<span class="string">"parent"</span> /&gt;</span><br><span class="line">&lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建app模块的build.gradle文件</span></span><br><span class="line">cat &gt; <span class="string">"./<span class="variable">$PROJECT_NAME</span>/app/build.gradle"</span> &lt;&lt;EOF</span><br><span class="line">apply plugin: <span class="string">'com.android.application'</span></span><br><span class="line"></span><br><span class="line">android &#123;</span><br><span class="line">    compileSdkVersion 31</span><br><span class="line">    defaultConfig &#123;</span><br><span class="line">        applicationId <span class="string">"<span class="variable">$PACKAGE_NAME</span>"</span></span><br><span class="line">        minSdkVersion 21</span><br><span class="line">        targetSdkVersion 31</span><br><span class="line">        versionCode 1</span><br><span class="line">        versionName <span class="string">"1.0"</span></span><br><span class="line"></span><br><span class="line">        testInstrumentationRunner <span class="string">"androidx.test.runner.AndroidJUnitRunner"</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    buildTypes &#123;</span><br><span class="line">        release &#123;</span><br><span class="line">            minifyEnabled <span class="literal">false</span></span><br><span class="line">            proguardFiles getDefaultProguardFile(<span class="string">'proguard-android-optimize.txt'</span>), <span class="string">'proguard-rules.pro'</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">dependencies &#123;</span><br><span class="line">    implementation <span class="string">'androidx.appcompat:appcompat:1.3.0'</span></span><br><span class="line">    implementation <span class="string">'androidx.constraintlayout:constraintlayout:2.0.4'</span></span><br><span class="line">    testImplementation <span class="string">'junit:junit:4.13.2'</span></span><br><span class="line">    androidTestImplementation <span class="string">'androidx.test.ext:junit:1.1.3'</span></span><br><span class="line">    androidTestImplementation <span class="string">'androidx.test.espresso:espresso-core:3.4.0'</span></span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 初始化Gradle Wrapper</span></span><br><span class="line">    <span class="built_in">cd</span> <span class="string">"<span class="variable">$PROJECT_NAME</span>"</span></span><br><span class="line">    gradle wrapper --gradle-version=7.5</span><br><span class="line">    <span class="built_in">cd</span> ..</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 构建项目</span></span><br><span class="line">    <span class="built_in">cd</span> <span class="string">"<span class="variable">$PROJECT_NAME</span>"</span></span><br><span class="line">    ./gradlew assembleDebug</span><br><span class="line">    <span class="built_in">cd</span> ..</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 复制APK到脚本执行目录</span></span><br><span class="line">    cp ./<span class="string">"<span class="variable">$PROJECT_NAME</span>"</span>/app/build/outputs/apk/debug/app-debug.apk ./<span class="string">"<span class="variable">$PROJECT_NAME</span>.apk"</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"Project <span class="variable">$PROJECT_NAME</span> has been created and packaged."</span></span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"Finished creating and packaging <span class="variable">$PROJECTS_COUNT</span> projects."</span></span><br></pre></td></tr></table></figure><p>Actually, I haven’t written scripts with Bash in a long time, so I used this project as an opportunity to learn~</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br></pre></td></tr></table></figure><p>The first line specifies the location of the bash interpreter, which is generally read when executing <code>./xxx.sh</code>.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">PROJECTS_COUNT=1</span><br></pre></td></tr></table></figure><p>The second line sets a variable to specify the number of projects to generate.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> $(seq 1 <span class="variable">$PROJECTS_COUNT</span>); <span class="keyword">do</span></span><br><span class="line">…………</span><br><span class="line"><span class="keyword">done</span></span><br></pre></td></tr></table></figure><p>Next, a <code>for</code> statement is written. <code>seq 1 \$PROJECTS_COUNT</code> generates a sequence from 1 to <code>PROJECTS_COUNT</code>. The <code>$</code> symbol in front packages the expression inside the parentheses into a variable, and finally, the specific logic is written between <code>do</code> and <code>done</code>.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">PROJECT_NAME=<span class="string">"Dual_Record_<span class="variable">$(openssl rand -hex 3)</span>"</span></span><br><span class="line">PACKAGE_NAME=<span class="string">"com.example.<span class="variable">$PROJECT_NAME</span>"</span></span><br></pre></td></tr></table></figure><p>Since each package name cannot be repeated, <code>openssl rand -hex 3</code> is used to generate a string of random characters, and the result is appended directly to the end of the string.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建基础的项目结构</span></span><br><span class="line">mkdir -p <span class="string">"./<span class="variable">$PROJECT_NAME</span>/app/src/main/java/<span class="variable">$PACKAGE_NAME</span>"</span></span><br><span class="line">mkdir -p <span class="string">"./<span class="variable">$PROJECT_NAME</span>/app/src/main/res/layout"</span></span><br><span class="line">mkdir -p <span class="string">"./<span class="variable">$PROJECT_NAME</span>/app/src/main/res/mipmap"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"rootProject.name='<span class="variable">$PROJECT_NAME</span>'"</span> &gt; <span class="string">"./<span class="variable">$PROJECT_NAME</span>/settings.gradle"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"include ':app'"</span> &gt; <span class="string">"./<span class="variable">$PROJECT_NAME</span>/settings.gradle"</span></span><br></pre></td></tr></table></figure><p>Android or Java projects generally need to follow fixed project directory conventions, so <code>mkdir -p</code> is required in advance to directly create the directory hierarchy. After creation, project information is written into the <code>settings.gradle</code> project configuration.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 复制图标到项目中</span></span><br><span class="line">cp /Users/project256/Downloads/5a1acc930a4f210f5ee9e73e45bec3c7_1.png <span class="string">"./<span class="variable">$PROJECT_NAME</span>/app/src/main/res/mipmap/ic_launcher.png"</span></span><br><span class="line">cp /Users/project256/Downloads/5a1acc930a4f210f5ee9e73e45bec3c7_1.png <span class="string">"./<span class="variable">$PROJECT_NAME</span>/app/src/main/res/mipmap/ic_launcher_round.png"</span></span><br></pre></td></tr></table></figure><p>Use the <code>cp</code> command to copy the custom icon to the target folder using the specified filename.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建strings.xml文件并添加app_name字符串资源</span></span><br><span class="line">    mkdir -p <span class="string">"./<span class="variable">$PROJECT_NAME</span>/app/src/main/res/values/"</span></span><br><span class="line">    cat &gt; <span class="string">"./<span class="variable">$PROJECT_NAME</span>/app/src/main/res/values/strings.xml"</span> &lt;&lt;EOF</span><br><span class="line">&lt;?xml version=<span class="string">"1.0"</span> encoding=<span class="string">"utf-8"</span>?&gt;</span><br><span class="line">&lt;resources&gt;</span><br><span class="line">    &lt;string name=<span class="string">"app_name"</span>&gt;APP名字&lt;/string&gt;</span><br><span class="line">&lt;/resources&gt;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>Use <code>cat &gt; &quot;file&quot; &lt;&lt;EOF</code> to write multi-line strings to the specified file.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># This goes in the project-level build.gradle creation step</span></span><br><span class="line">cat &gt; <span class="string">"./<span class="variable">$PROJECT_NAME</span>/build.gradle"</span> &lt;&lt;EOF</span><br><span class="line">buildscript &#123;</span><br><span class="line">    repositories &#123;</span><br><span class="line">        google()</span><br><span class="line">        mavenCentral()</span><br><span class="line">    &#125;</span><br><span class="line">    dependencies &#123;</span><br><span class="line">        // Make sure to use a compatible version</span><br><span class="line">        classpath <span class="string">'com.android.tools.build:gradle:7.4.2'</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">allprojects &#123;</span><br><span class="line">    repositories &#123;</span><br><span class="line">        google()</span><br><span class="line">        mavenCentral()</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">task clean(<span class="built_in">type</span>: Delete) &#123;</span><br><span class="line">    delete rootProject.buildDir</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p><code>build.gradle</code> specifies the actions to be executed when compiling the project. Note that the Gradle version should match the local machine.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Adjusted AndroidManifest.xml creation with android:exported="true"</span></span><br><span class="line">cat &gt; <span class="string">"./<span class="variable">$PROJECT_NAME</span>/app/src/main/AndroidManifest.xml"</span> &lt;&lt;EOF</span><br><span class="line">&lt;?xml version=<span class="string">"1.0"</span> encoding=<span class="string">"utf-8"</span>?&gt;</span><br><span class="line">&lt;manifest xmlns:android=<span class="string">"http://schemas.android.com/apk/res/android"</span></span><br><span class="line">    package=<span class="string">"<span class="variable">$PACKAGE_NAME</span>"</span>&gt;</span><br><span class="line"></span><br><span class="line">    &lt;application</span><br><span class="line">        android:allowBackup=<span class="string">"true"</span></span><br><span class="line">        android:icon=<span class="string">"@mipmap/ic_launcher"</span></span><br><span class="line">        android:label=<span class="string">"@string/app_name"</span></span><br><span class="line">        android:roundIcon=<span class="string">"@mipmap/ic_launcher_round"</span></span><br><span class="line">        android:supportsRtl=<span class="string">"true"</span></span><br><span class="line">        android:theme=<span class="string">"@style/Theme.AppCompat.Light.NoActionBar"</span>&gt;</span><br><span class="line">        &lt;activity android:name=<span class="string">".MainActivity"</span></span><br><span class="line">            android:exported=<span class="string">"true"</span>&gt; &lt;!-- Ensure this line is added --&gt;</span><br><span class="line">            &lt;intent-filter&gt;</span><br><span class="line">                &lt;action android:name=<span class="string">"android.intent.action.MAIN"</span> /&gt;</span><br><span class="line">                &lt;category android:name=<span class="string">"android.intent.category.LAUNCHER"</span> /&gt;</span><br><span class="line">            &lt;/intent-filter&gt;</span><br><span class="line">        &lt;/activity&gt;</span><br><span class="line">    &lt;/application&gt;</span><br><span class="line"></span><br><span class="line">&lt;/manifest&gt;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>Write core Android information into <code>AndroidManifest.xml</code>, including icon, theme, and other details.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">    <span class="comment"># 创建一个简单的MainActivity</span></span><br><span class="line">    cat &gt; <span class="string">"./<span class="variable">$PROJECT_NAME</span>/app/src/main/java/<span class="variable">$PACKAGE_NAME</span>/MainActivity.java"</span> &lt;&lt;EOF</span><br><span class="line">package <span class="variable">$PACKAGE_NAME</span>;</span><br><span class="line"></span><br><span class="line">import android.os.Bundle;</span><br><span class="line">import androidx.appcompat.app.AppCompatActivity;</span><br><span class="line"></span><br><span class="line">public class MainActivity extends AppCompatActivity &#123;</span><br><span class="line">    @Override</span><br><span class="line">    protected void onCreate(Bundle savedInstanceState) &#123;</span><br><span class="line">        super.onCreate(savedInstanceState);</span><br><span class="line">        setContentView(R.layout.activity_main);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p><code>MainActivity.java</code> is the application’s entry point; displaying the main view here completes the work.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">    <span class="comment"># 创建一个简单的activity_main.xml布局文件</span></span><br><span class="line">    cat &gt; <span class="string">"./<span class="variable">$PROJECT_NAME</span>/app/src/main/res/layout/activity_main.xml"</span> &lt;&lt;EOF</span><br><span class="line">&lt;?xml version=<span class="string">"1.0"</span> encoding=<span class="string">"utf-8"</span>?&gt;</span><br><span class="line">&lt;androidx.constraintlayout.widget.ConstraintLayout xmlns:android=<span class="string">"http://schemas.android.com/apk/res/android"</span></span><br><span class="line">    xmlns:app=<span class="string">"http://schemas.android.com/apk/res-auto"</span></span><br><span class="line">    xmlns:tools=<span class="string">"http://schemas.android.com/tools"</span></span><br><span class="line">    android:layout_width=<span class="string">"match_parent"</span></span><br><span class="line">    android:layout_height=<span class="string">"match_parent"</span></span><br><span class="line">    tools:context=<span class="string">".MainActivity"</span>&gt;</span><br><span class="line"></span><br><span class="line">    &lt;TextView</span><br><span class="line">        android:layout_width=<span class="string">"wrap_content"</span></span><br><span class="line">        android:layout_height=<span class="string">"wrap_content"</span></span><br><span class="line">        android:text=<span class="string">"欢迎使用XX系统_<span class="variable">$PROJECT_NAME</span>!"</span></span><br><span class="line">        app:layout_constraintBottom_toBottomOf=<span class="string">"parent"</span></span><br><span class="line">        app:layout_constraintLeft_toLeftOf=<span class="string">"parent"</span></span><br><span class="line">        app:layout_constraintRight_toRightOf=<span class="string">"parent"</span></span><br><span class="line">        app:layout_constraintTop_toTopOf=<span class="string">"parent"</span> /&gt;</span><br><span class="line">&lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>Even the simplest Android application needs a layout set up, which is specified in <code>activity_main.xml</code>. Here, only a <code>TextView</code> is set up for display.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建app模块的build.gradle文件</span></span><br><span class="line">cat &gt; <span class="string">"./<span class="variable">$PROJECT_NAME</span>/app/build.gradle"</span> &lt;&lt;EOF</span><br><span class="line">apply plugin: <span class="string">'com.android.application'</span></span><br><span class="line"></span><br><span class="line">android &#123;</span><br><span class="line">    compileSdkVersion 31</span><br><span class="line">    defaultConfig &#123;</span><br><span class="line">        applicationId <span class="string">"<span class="variable">$PACKAGE_NAME</span>"</span></span><br><span class="line">        minSdkVersion 21</span><br><span class="line">        targetSdkVersion 31</span><br><span class="line">        versionCode 1</span><br><span class="line">        versionName <span class="string">"1.0"</span></span><br><span class="line"></span><br><span class="line">        testInstrumentationRunner <span class="string">"androidx.test.runner.AndroidJUnitRunner"</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    buildTypes &#123;</span><br><span class="line">        release &#123;</span><br><span class="line">            minifyEnabled <span class="literal">false</span></span><br><span class="line">            proguardFiles getDefaultProguardFile(<span class="string">'proguard-android-optimize.txt'</span>), <span class="string">'proguard-rules.pro'</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">dependencies &#123;</span><br><span class="line">    implementation <span class="string">'androidx.appcompat:appcompat:1.3.0'</span></span><br><span class="line">    implementation <span class="string">'androidx.constraintlayout:constraintlayout:2.0.4'</span></span><br><span class="line">    testImplementation <span class="string">'junit:junit:4.13.2'</span></span><br><span class="line">    androidTestImplementation <span class="string">'androidx.test.ext:junit:1.1.3'</span></span><br><span class="line">    androidTestImplementation <span class="string">'androidx.test.espresso:espresso-core:3.4.0'</span></span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>The submodule’s <code>build.gradle</code> configures the Android compile version and dependency information; a simple application like this won’t depend on too much.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 初始化Gradle Wrapper</span></span><br><span class="line"><span class="built_in">cd</span> <span class="string">"<span class="variable">$PROJECT_NAME</span>"</span></span><br><span class="line">gradle wrapper --gradle-version=7.5</span><br><span class="line"><span class="built_in">cd</span> ..</span><br><span class="line"></span><br><span class="line"><span class="comment"># 构建项目</span></span><br><span class="line"><span class="built_in">cd</span> <span class="string">"<span class="variable">$PROJECT_NAME</span>"</span></span><br><span class="line">./gradlew assembleDebug</span><br><span class="line"><span class="built_in">cd</span> ..</span><br><span class="line"></span><br><span class="line"><span class="comment"># 复制APK到脚本执行目录</span></span><br><span class="line">cp ./<span class="string">"<span class="variable">$PROJECT_NAME</span>"</span>/app/build/outputs/apk/debug/app-debug.apk ./<span class="string">"<span class="variable">$PROJECT_NAME</span>.apk"</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"Project <span class="variable">$PROJECT_NAME</span> has been created and packaged."</span></span><br></pre></td></tr></table></figure><p>Finally, enter the directory, use Gradle to package and build the project, and copy the APK to the Bash execution directory for convenient collection, upload, and deployment later.</p><p>At this point, a Bash script capable of batch-generating Android APK packages was complete. By scripting it, I had LLMs and machines working for me, saving a ton of time.</p><p>To outsiders, the effort and scale look huge, so you can ask for a higher price. Tool automation really gives side hustles a major boost.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;One day in April, I landed a freelance gig to batch-generate Android APK packages. The requirement was that upon opening the app, it woul</summary>
      
    
    
    
    <category term="Tools" scheme="https://project256.com/en/categories/tools/"/>
    
    
    <category term="Bash" scheme="https://project256.com/en/tags/bash/"/>
    
  </entry>
  
  <entry>
    <title>Reflections on Side Hustles Sparked by AI Writing</title>
    <link href="https://project256.com/en/ai-generative-content/AI%E5%86%99%E4%BD%9C%E5%BC%95%E8%B5%B7%E7%9A%84%E5%AF%B9%E5%89%AF%E4%B8%9A%E6%80%9D%E8%80%83/"/>
    <id>https://project256.com/en/ai-generative-content/AI%E5%86%99%E4%BD%9C%E5%BC%95%E8%B5%B7%E7%9A%84%E5%AF%B9%E5%89%AF%E4%B8%9A%E6%80%9D%E8%80%83/</id>
    <published>2024-04-20T09:49:22.000Z</published>
    <updated>2026-08-04T09:39:04.561Z</updated>
    
    <content type="html"><![CDATA[<p>Whether a side hustle gets off the ground is much like whether trading stocks makes a profit—you can’t say it was all for nothing just because you suffered heavy losses in stocks. If you trade stocks or run a side hustle with a sharp mind, you will also gain deep insights and perspectives on the industry and economy. (This is also why losing money in stocks or selling low and buying high is called “paying tuition.”)</p><h2 id="The-Ultimate-Goal-of-a-Side-Hustle"><a href="#The-Ultimate-Goal-of-a-Side-Hustle" class="headerlink" title="The Ultimate Goal of a Side Hustle"></a>The Ultimate Goal of a Side Hustle</h2><p>I believe the ultimate goal for everyone starting a side hustle is—financial freedom.</p><p>Bodo Schäfer, in his <em>The Road to Financial Freedom</em> book series, defines financial freedom as: “Only when you have enough money to live off the interest alone are you truly wealthy and independent.” He also points out the way to achieve it: “You want to own a money-making machine, rather than spend your whole life being a money-making machine.”</p><p>He also notes that “the target wealth figure is merely a yardstick to measure whether you have truly reached your goal; it is much more about character. What matters is who you become in the process. Life is short, and one shouldn’t waste it away in mediocrity.”</p><p>Regarding the “freedom” in financial freedom, before starting a side hustle, one needs to reflect: outside of your main job, do you still want to trade your labor for income?</p><p>In summary, I believe the path to <em>The Road to Financial Freedom</em> lies in <strong>becoming an expert in developing or using automated tools</strong>. Development allows you to actively explore unfamiliar domains and create value. Naturally, manufacturing shovels for others is the best path. On the other hand, using tools carries a lower cost, where the key lies in selecting the right tools and domains.</p><p><strong>In work and business, the ones who make the most money are never those doing the actual manual labor, but those who aggregate resources to fulfill demand.</strong></p><h2 id="The-Side-Hustle-Impossible-Trinity-and-Principles"><a href="#The-Side-Hustle-Impossible-Trinity-and-Principles" class="headerlink" title="The Side Hustle Impossible Trinity and Principles"></a>The Side Hustle Impossible Trinity and Principles</h2><p>The Impossible Trinity: Low barrier to entry, high ceiling, and sustainability. You can only pick two out of the three.</p><p>Principle: Rapid closed-loop execution and quick positive feedback. As the saying goes, <strong>people change because of pain, and keep taking action because of gain.</strong></p><p>A good side hustle yields earnings in the following four areas:</p><ul><li>Earning money — Income from the side hustle.</li><li>Earning insight — A deeper understanding of business and human nature. This helps in making better decisions and lays the foundation for earning more in the future.</li><li>Earning skills — Enhancing your own skill set, such as AI, writing, community management, etc.</li><li>Earning systems — Building a system through projects and establishing your own methodologies for getting things done.</li></ul><p>A side hustle is not just a way to make money; it should also be viewed as the infrastructure for your personal IP, a bridge converting traffic between public and private domains, enabling long-term commitment.</p><h2 id="On-Article-Writing"><a href="#On-Article-Writing" class="headerlink" title="On Article Writing"></a>On Article Writing</h2><p>Although using AI for writing is an entry point for a side hustle, you must first be able to write clearly yourself. If a person’s artistic sense is terrible to begin with, how can they create works that others appreciate?</p><p>An article in Da Hui’s <em>Progress Hacker</em> WeChat official account discussed AI writing and explained how to write a hit WeChat Official Account article. In fact, this can be applied through transfer learning to all scenarios, such as daily communication, document collaboration, work summaries, and presentations. Below are some excerpts from the article. Special thanks to the original author, Da Hui—his <a href="https://wx.zsxq.com/dweb2/index/group/88858585542822" target="_blank" rel="noopener">Knowledge Planet</a> is currently free for a limited time; he is a very generous guy.</p><p>The recommendation algorithm for WeChat Official Accounts mainly depends on two factors: <strong>the attractiveness of the title and the article’s completion rate</strong>. Simply put, make the title appealing, and make the content good enough for people to read to the end. Furthermore, if you want to get into the recommendation pool, you should spend 80% of your time polishing the title.</p><h3 id="Viral-Titles"><a href="#Viral-Titles" class="headerlink" title="Viral Titles"></a>Viral Titles</h3><p>A title with viral potential satisfies three principles:</p><ul><li>Wide coverage reach — Targeting popular, mass-market platforms everyone knows about.</li><li>High level of pain point — Pain points are essentially fear; the degree of fear, e.g., “It’s impossible to get started now.” The title expresses negative realities, while the article provides solutions.</li><li>Triggering social motivation — After entering and finishing the article, readers find it provides fresh perspectives and solid actionable insights, prompting them to like and share.</li></ul><p>The three steps to creating viral titles are: Copy - Swap - Create.</p><ul><li>Copy — Prioritize finding viral titles that fit your topic and use them directly.</li><li>Swap — Maintain the grammatical structure and replace the nouns in the viral title.</li><li>Create — Create titles yourself or with the help of AI tools.</li></ul><p>When it comes to titles, if you can find one that works, avoid reinventing the wheel from scratch. This isn’t taking shortcuts—it’s replicating success. Just like climbing a mountain, you should try to follow well-trodden paths; relying purely on trial and error makes it easy to fall into a ditch.</p><p><strong>If you can find a viral title that fits your content, use it directly. If it cannot be used directly, keep the structure, make fine adjustments, and replace nouns without changing the verbs. If all else fails, brainstorm yourself or use GPT to generate combinations.</strong></p><p>It all comes down to doing your homework beforehand. Modifying titles plays a huge role in boosting traffic flow.</p><p>When creating content for self-media, treat collecting viral titles as a regular assignment. A good title can increase exposure ten- or even a hundred-fold.</p><p>I have also written some ATA articles on Alibaba’s intranet. Among them, the one with high likes and bookmarks was titled <em>“Poor Bros Training a Conversational LLM — Based on Chinese-LLaMA-Alpaca-2”</em>. The phrase “poor bros” in the title copied an idea from another fellow contributor, referring to training LLMs on a shoestring budget. Although many colleagues had experience using LLMs, they all wanted a step-by-step tutorial to get started with training LLMs, and this article hit that exact pain point.</p><h3 id="Completion-Rate"><a href="#Completion-Rate" class="headerlink" title="Completion Rate"></a>Completion Rate</h3><p>Two key strategies:</p><ul><li>Control article length — Articles that are too long can discourage readers. It is recommended that viral articles be around 800 to 1,000 words long. This length not only satisfies originality requirements but is also concise enough to maintain reader attention. (Analogous to context token limits and amnesia issues in LLMs.)</li><li>Use stories to engage readers — People love listening to stories. By expressing the principles and information you want to convey through a story, readers are more likely to read through the entire piece. (The same applies to public speaking; <em>The Presentation Secrets of Steve Jobs</em> also emphasizes the importance of stories and vision.) If you are not good at storytelling, you can use GPT—which excels at crafting stories—to assist in brainstorming attractive plots and frameworks, making the article livelier and more engaging.</li></ul><h2 id="Writing-Techniques-for-Product-Promotion"><a href="#Writing-Techniques-for-Product-Promotion" class="headerlink" title="Writing Techniques for Product Promotion"></a>Writing Techniques for Product Promotion</h2><h3 id="Techniques-for-Introducing-Products-in-Articles"><a href="#Techniques-for-Introducing-Products-in-Articles" class="headerlink" title="Techniques for Introducing Products in Articles"></a>Techniques for Introducing Products in Articles</h3><p>Six methods:</p><ul><li>Write about users and user stories — <strong>Empathy stemming from shared identity and experience is the greatest source of trust, and trust is the foundation of monetization.</strong> People aren’t inspired by a product, but by a person.</li><li>Write about selling points — Present selling points through storytelling.</li><li>Write about reasons — Because you benefited from it yourself, you want to promote it to more people. “A just cause enjoys abundant support, while an unjust cause finds little support.” When you occupy the moral high ground or “Way” (Dao), your product sells effortlessly. People who want to reach the same state as you will be willing to do this together with you, buy the product, and join your circle.</li><li>Write about scenarios — Build scenarios. Low-end sales pitch selling points; high-end sales pitch emotion and values. High-ticket items or designs with history put immense thought into this.</li><li>Write about pain points — Hit user pain points directly (no end in sight, not wanting to live like this, refusing to be gaslighted&#x2F;manipulated, etc.), deeply binding your product and circle with the user’s mindset, pain points, future, and solutions.</li><li>Write about user testimonials — Paid knowledge is like a deity in a temple: incense burns brighter not because the deity demands things from people, but because it has much to give and can fulfill people’s wishes, spreading its fame through word of mouth. Note that while showcasing success, you should also praise your users. Don’t fill your WeChat Moments exclusively with transaction logs, order screenshots, and chat histories; instead, become the person others aspire to be—that is true personal IP.</li></ul><h3 id="Turn-All-Consumption-into-Means-of-Production"><a href="#Turn-All-Consumption-into-Means-of-Production" class="headerlink" title="Turn All Consumption into Means of Production"></a>Turn All Consumption into Means of Production</h3><p>Write every single minute you spend into an article or a WeChat Moments post. Live life with the mindset of a dedicated problem solver (“test-taker”); the most critical aspect of running self-media is having means of production.</p><p>Whenever you buy access to a community or spend money in a shop, summarize why you were persuaded to buy every time you convert. You can write it down to showcase to others, influence their mindset, and subsequently convert them into buying your own product. Time and energy are extremely precious, so maximize their value.</p><p>Sources of writing topics:</p><ul><li>Write long, detailed responses to user questions on WeChat Moments. This makes them feel genuinely valued, while others with similar questions will also gain insights from the post and may return to buy your products in the future.</li><li>Most people have similar questions—there aren’t endless variations. Put yourself in their shoes, explain things clearly, and help them make better choices. They will be grateful and perceive you as down-to-earth and dependable, making them likely to buy your product later. Sometimes conversions are driven by the product; other times, by the person.</li></ul><p>Don’t focus purely on product selling points; instead, deliver solid, actionable content, offering solutions that solve problems for others or provide universal value.</p><p>After writing, review your work from a third-party perspective: when someone reads your content, do they see a screen full of advertisements, or do they actually walk away with value?</p><h2 id="Free-Knowledge-Planet"><a href="#Free-Knowledge-Planet" class="headerlink" title="Free Knowledge Planet"></a>Free Knowledge Planet</h2><h3 id="Personal-IP-Hook"><a href="#Personal-IP-Hook" class="headerlink" title="Personal IP + Hook"></a>Personal IP + Hook</h3><p>Hook: <strong>What benefits you offer the reader + private domain entryway + self-introduction</strong></p><p>Reasons to use a free Knowledge Planet:</p><ul><li>High open rate for free Planets — In a free Planet with 10k members, each post receives over 1k reads, with an average open rate of over 10%. In contrast, WeChat Official Accounts have an open rate of around 2%.</li><li>Simpler creation and operation than Official Accounts — Write a few sentences or tens of words, and post unlimited times daily. Perfect for fragmented reading and writing.</li><li>Content can be generated as images — Convenient for sharing to WeChat Moments and WeChat groups.</li><li>Unlimited capacity — A single WeChat account has a limit of 10,000 contacts.</li><li>Flexible permission settings — Can be configured so that only the owner, partners, and invited guests can post, creating your own home turf, which works exceptionally well for building trust and converting sales.</li></ul><p>You can also use AI to expand Planet posts or combine multiple Planet posts into a single, content-rich Official Account article.</p><p>Therefore, in terms of open rates, operational difficulty, and distribution efficiency, <strong>a free Knowledge Planet is ideally suited as a content production base and private domain headquarters.</strong></p><h3 id="Running-a-Knowledge-Planet-Successfully"><a href="#Running-a-Knowledge-Planet-Successfully" class="headerlink" title="Running a Knowledge Planet Successfully"></a>Running a Knowledge Planet Successfully</h3><p>Consider three aspects:</p><ul><li>Content — Short content; <strong>frequently post</strong> inspiring short notes. 5 to 20 posts per day.</li><li>Operations — Run lucky draws, daily check-ins, homework assignments, etc. This boosts open rates and retention. Prizes can be books, column access, or WeChat red packets to <strong>gradually deepen connection</strong>.</li><li>User Acquisition — Bringing new users into the Planet via WeChat is very convenient.</li></ul><h3 id="AI-Powered-Knowledge-Planet"><a href="#AI-Powered-Knowledge-Planet" class="headerlink" title="AI-Powered Knowledge Planet"></a>AI-Powered Knowledge Planet</h3><ul><li>Use AI for naming and designing icons</li><li>AI-assisted topic selection — When short on inspiration or unsure what to write, generate topic ideas and content through conversational AI.</li><li>Use AI for data analysis</li></ul><h3 id="Building-Sustainable-Infrastructure"><a href="#Building-Sustainable-Infrastructure" class="headerlink" title="Building Sustainable Infrastructure"></a>Building Sustainable Infrastructure</h3><p>If writing knowledge is only used to earn ad revenue, you will only make short-term money. But if you start from your core personal IP and treat the Official Account as infrastructure—a bridge converting traffic between public and private domains—then it becomes a sustainable business.</p><p>In the process of writing, <strong>accumulating materials, building a user base, formulating SOPs, and constructing a self-media toolkit</strong> means you have earned money, insights, skills, and systems alike.</p><h3 id="Breaking-Out-with-a-Personal-IP"><a href="#Breaking-Out-with-a-Personal-IP" class="headerlink" title="Breaking Out with a Personal IP"></a>Breaking Out with a Personal IP</h3><p>Leverage trending topics and brand reputations; get close to the sunlight, and you will shine yourself. When selling branded products, you naturally become an expert in the eyes of users.</p><p>Personal IP isn’t just empty talk—it must be driven by tangible results and achievements. Gain exposure in the public domain and build retention in the private domain; in essence, personal IP is trust and light.</p><p>Without seeing, there is no believing.</p><p>Without exposure, there is no trust.</p><p>Show up often so your target audience can see the scenarios, the results, the process, the raw materials, and everything they want to see.</p><h2 id="Case-Study-AI-Assisted-Writing"><a href="#Case-Study-AI-Assisted-Writing" class="headerlink" title="Case Study: AI-Assisted Writing"></a>Case Study: AI-Assisted Writing</h2><p>The most lucrative monetization model is old demand + new tools: helping others write is an old demand, and AI is the new tool.</p><p>In the age-old industry of writing assistance, leveraging AI tech to boost efficiency allows you to scale up. Traditional writing assistance is just making a living for hard-earned pennies, but adding AI enables scalable growth.</p><p>With AI, laypeople who master basic prompting techniques and clearly understand user needs can produce high-quality original content.</p><p>Top earners aren’t necessarily vastly superior in capability or cognition. Rather, they entered the market early, gained an early information asymmetry, and through persistent effort, built their own competitive advantage.</p><p>Three classic entry approaches:</p><ul><li>Becoming a writer — Classic employee mindset, selling your own skills and time.</li><li>Taking orders and writing yourself — Self-employed mindset running the full process solo; easy to lose track of priorities.</li><li>Studio &#x2F; Resource aggregator — Running storefronts and creating content on the front end, while hiring writers for delivery on the back end.</li></ul><h3 id="Track-and-Architecture"><a href="#Track-and-Architecture" class="headerlink" title="Track and Architecture"></a>Track and Architecture</h3><p>Whether a business is profitable and how much it makes depends firstly on the track (niche), and secondly on the architecture. The difference lies in the architecture: architecture requires that every current product and investment be frictionless and contribute to future growth.</p><p>Design the top-level architecture and underlying logic well right from the start, and you will immediately appreciate the magic:</p><ul><li>The business becomes a self-growing flywheel</li><li>The founder doesn’t need to fight fires everywhere</li><li>Maintaining daily operations requires very few people</li><li>Self-organizing collaboration with agile execution</li></ul><p>Every project has a lifecycle. If approached with a plan, every project serves as a stepping stone on your path of growth. Without a plan, once the project dividend period ends, starting another project means building from scratch again.</p><p>Running a business means architecting a profit machine. Once the machine is built, only three tasks remain:</p><ul><li>Laying out traffic channels</li><li>Streamlining sales conversion workflows</li><li>Maintaining daily operations</li></ul><p>It is like building a machine: continuously polish and tune it, and it will become a money printer. Take Da Hui’s free Knowledge Planet, for instance—spending just a dozen minutes a day posting content generates income.</p><p>The core secret to earning money is <strong>innovating in customer acquisition and acquiring customers with novel strategies</strong>. Do not treat e-commerce as the primary acquisition channel, but only as a supplementary one.</p><h3 id="Choice-of-Clients-and-Platforms"><a href="#Choice-of-Clients-and-Platforms" class="headerlink" title="Choice of Clients and Platforms"></a>Choice of Clients and Platforms</h3><p>When running a project, if you can go after high-ticket clients, definitely avoid low-ticket ones. High-ticket clients are mostly high-net-worth business clients, making them extremely valuable when retained in your private domain.</p><p>Writing assistance is a non-standardized virtual service; the communication costs and AI generation effort for a 100 RMB order vs. a 1,000 RMB order are virtually the same.</p><p>In the early stage, you can practice with a few low-ticket orders. Once the process runs smoothly, you must transition to high-ticket orders. In writing assistance, an order of 1,000 RMB qualifies as high-ticket.</p><p>Characteristics of high-ticket deals: one is having the ability to pay, and the other is needing to uncover user requirements.</p><p>Across e-commerce platforms, the exact same service commands a higher price on Taobao than on Pinduoduo, Xianyu (Idle Fish) higher than Taobao, and Xiaohongshu (RED) higher than Xianyu. It’s similar to how a bottle of water is priced differently in a hotel, a KTV, or a supermarket. <strong>Non-standard services like writing assistance align more closely with platform positioning, which is fundamentally tied to users’ psychological expectations.</strong> For example, users go to Pinduoduo looking for cheap deals to complete a task without spending much money.</p><p>The way forward for low-ticket services: template-based and automated services to boost acquisition capacity. For instance, hosting scripts are highly reproducible with reusable intros and outros; after building a few templates, you can simply swap out names and make minor tweaks.</p><h3 id="Pitfall-Avoidance"><a href="#Pitfall-Avoidance" class="headerlink" title="Pitfall Avoidance"></a>Pitfall Avoidance</h3><p>The core key is clarifying client requirements; otherwise, you’ll end up with rework. Clients fall into two categories: those with explicit requirements and those with vague requirements.</p><ul><li>Explicit requirements — Write the article directly based on the article type combined with AI prompts.</li><li>Vague requirements — Communicate repeatedly with the user to ensure your understanding is spot-on.</li></ul><p>Once requirements are clear, draft an outline. Always re-confirm the outline with the client.</p><p>Finally, generate the article. To prevent free-loading, add watermarks or provide only a portion of the content, delivering the full document only after payment is confirmed.</p><p>To avoid pitfalls: <strong>Find reliable people, don’t test human nature, and design a system to prevent issues</strong> (reliable people plus a solid reward and penalty system). Encountering free-loaders or ghosting is normal—even blood brothers often fall out when co-founding businesses. Treat it as operational loss: unavoidable, but reducible.</p><h3 id="How-to-Acquire-Customers"><a href="#How-to-Acquire-Customers" class="headerlink" title="How to Acquire Customers"></a>How to Acquire Customers</h3><p>User behavior path: <strong>Attention → Interest → Search → Purchase → Usage → Habit</strong></p><p>Many items end with the purchase—they are never used, nor do they form a habit.</p><p>Writing assistance is essentially virtual service e-commerce. Based on user behavior paths, there are three customer acquisition methods:</p><ul><li>Traditional shelf e-commerce — Taobao, JD.com, Xianyu, where people search for goods via search bars or category browsing.</li><li>Social e-commerce — Pinduoduo, with group buying and bargain price slashes (“Kan Yi Dao”).</li><li>Interest e-commerce — Douyin, WeChat Channels, Xiaohongshu.</li></ul><p>However, humans are lazy. Outside of heavy-decision purchases, active searching will become less frequent, which is why Taobao faces an uphill battle. Still, traditional shelf e-commerce is worth doing early on to get quick feedback while accumulating private-domain users.</p><p>Once you have a base of private-domain users, you can acquire customers directly on WeChat Moments. If you haven’t established an image as a master writer before, share content from a consumer’s perspective. For example: “I needed to write something, but couldn’t get it done after days of trying. Later I met a friend who is amazing at writing, and they finished it for me in no time. If any of you need writing done, feel free to reach out, and I can introduce you to this friend.”</p><p>To maintain sustainable income, follow this route: <strong>Deeply cultivate a specialized domain, acquire customers through content, and offer sales within your private domain—that is how you make money and build your own moat.</strong></p><h3 id="Comprehensive-Skill-Development"><a href="#Comprehensive-Skill-Development" class="headerlink" title="Comprehensive Skill Development"></a>Comprehensive Skill Development</h3><p>Write on WeChat Official Accounts using viral article strategies to attract traffic and leads.</p><p>The rewards fall into three categories:</p><ul><li>Earning money</li><li>Accumulating private-domain users</li><li>Building your own small team</li></ul><p>In the AI era, AI can handle most tasks. However, <strong>emotional resonance and interpersonal skills</strong> cannot be solved by AI in the near future, making them increasingly important and valuable.</p><h3 id="Three-Stages"><a href="#Three-Stages" class="headerlink" title="Three Stages"></a>Three Stages</h3><ul><li><p>Earning 3k RMB&#x2F;month — Spend 3–4 hours writing daily. If you can sit still and enjoy writing, consider this model.</p></li><li><p>Earning over 10k RMB&#x2F;month — Write while partnering with other writers to take on more orders. Orders you can’t write can be outsourced to others while still generating income for yourself.</p></li><li><p>Earning over 50k RMB&#x2F;month — Professional studio approach. Establish robust writing workflows, define clear divisions of labor, and secure stable partner writers, distribution channels, and sales conversions.</p></li></ul><h2 id="Ending-Story"><a href="#Ending-Story" class="headerlink" title="Ending Story"></a>Ending Story</h2><p>There was a study-abroad and immigration agency whose business was processing top-talent visas for a certain region. The company had only two employees. For handling the entire process, they charged around 10,000 RMB total, yet made tens of millions in revenue.</p><p>The highlight lay in their pricing mechanism: if successful, they kept 10,000 RMB; if unsuccessful, they charged only 3,000 RMB as a handling fee.</p><p>The trick was that they could practically do nothing—just collecting documents for show without genuine effort. One person ran ads, and the other received clients. If a client’s documents were complete and succeeded on the first try, it passed right through; if incomplete, they didn’t bother following up. (Personally, I feel this severely damages one’s character, and reputation remains essential. However, if running a WeChat Mini Program with no review mechanism, reputation can be ignored.)</p><p>This model has many variations in business. For example, post-graduate entrance exam prep classes: 10,000 RMB for the gold class and 3,000 RMB for the standard class, with the gold class offering a money-back guarantee if you don’t pass.</p><p>Take my own experience as an example: after my Hong Kong Quality Migrant Admission Scheme application was rejected, I purchased recommendation and self-recommendation letter writing services. There was definitely a full SOP process involved, along with clear traces of AI usage. Most importantly, whether a visa application succeeds is a long-term black box with massive inherent uncertainty. Therefore, I believe this story is very real.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Whether a side hustle gets off the ground is much like whether trading stocks makes a profit—you can’t say it was all for nothing just be</summary>
      
    
    
    
    <category term="AI and Generative Content" scheme="https://project256.com/en/categories/ai-generative-content/"/>
    
    
    <category term="Large Language Models" scheme="https://project256.com/en/tags/large-language-models/"/>
    
  </entry>
  
  <entry>
    <title>Becoming an Expert</title>
    <link href="https://project256.com/en/career/%E4%B8%93%E5%AE%B6%E5%85%BB%E6%88%90/"/>
    <id>https://project256.com/en/career/%E4%B8%93%E5%AE%B6%E5%85%BB%E6%88%90/</id>
    <published>2024-04-09T06:46:53.000Z</published>
    <updated>2026-08-04T09:39:04.562Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Core"><a href="#Core" class="headerlink" title="Core"></a>Core</h2><ul><li>Value Contribution — Plan and identify problems, anticipate directions, and make strategic layouts</li><li>Technical Competency — Abstract cognitive understanding, good architectural design skills, awareness of upstream and downstream architecture and planning, continuous improvement capability, and the ability to anticipate and mitigate system stability risks</li><li>Openness and Growth — How to achieve organizational impact</li></ul><p>Use the STAR method to summarize and present your work.</p><h2 id="Systems-Thinking"><a href="#Systems-Thinking" class="headerlink" title="Systems Thinking"></a>Systems Thinking</h2><p>Enables us to consistently make stage-optimal choices when facing options without missing critical choices. Enhances learning efficiency and allows us to draw broader inferences from single instances.</p><p>Extended reading: <a href="https://wiki.mbalib.com/wiki/%E7%B3%BB%E7%BB%9F%E5%9F%BA%E6%A8%A1" target="_blank" rel="noopener">MBA Lib Encyclopedia — System Archetypes</a></p><h3 id="Key-Points"><a href="#Key-Points" class="headerlink" title="Key Points"></a>Key Points</h3><ul><li>Deconstruction — Break down a complex problem into multiple less complex problems, and then continue breaking those down into simple problems</li><li>Building Logical Relationships: Cause-and-Effect, 5W2H — Interacting, interrelated, and interdependent parts form a complex yet unified whole with a specific purpose</li><li>Prioritization (ROI)</li></ul><h3 id="Thinking"><a href="#Thinking" class="headerlink" title="Thinking"></a>Thinking</h3><ul><li>Deep Thinking — Move from superficial phenomena to the essence of things</li><li>Holistic Thinking — Consider the whole rather than just local parts</li><li>Dynamic Thinking — Understand that every person and business area is constantly changing</li></ul><p>It is a process of Zooming Out and Zooming In.</p><h3 id="The-Iceberg-Model"><a href="#The-Iceberg-Model" class="headerlink" title="The Iceberg Model"></a>The Iceberg Model</h3><ul><li>Events — What is happening right now?</li><li>Patterns — What has changed, and what are the trends?</li><li>Structure — Why is this happening, and how can the cause be explained?</li><li>Values — What philosophy drives this, and what are the underlying assumptions?</li></ul><h3 id="Tools-and-Methods"><a href="#Tools-and-Methods" class="headerlink" title="Tools and Methods"></a>Tools and Methods</h3><h4 id="Causal-Loop-Diagrams"><a href="#Causal-Loop-Diagrams" class="headerlink" title="Causal Loop Diagrams"></a>Causal Loop Diagrams</h4><ul><li>Variables — Factors in the modeled system structure whose values change over time; generally nouns</li><li>Links — Links formed between variables, which are causal links</li><li>Loops — Multiple links can form a loop. If there is a link from variable A to variable B, and there is also a link from variable B back to variable A through a series of other variables, a loop is formed.<ul><li>Reinforcing Loop (Snowball Effect)</li><li>Balancing Loop (Links reach balance without continuous increase. E.g., adjusting shower water temperature to a comfortable level, which exhibits fluctuations and time delays)</li></ul></li></ul><h4 id="5-Basic-Causal-Loop-Archetypes"><a href="#5-Basic-Causal-Loop-Archetypes" class="headerlink" title="5 Basic Causal Loop Archetypes"></a>5 Basic Causal Loop Archetypes</h4><ul><li>Fixes that Fail (A temporary solution brings balance initially, but causes future problems after a time delay. It leads to a balancing loop on one hand and a vicious reinforcing loop on the other, getting worse in the long run)</li><li>Shifting the Burden (Architecture optimization has a time delay in improving delivery efficiency, so temporary solutions may still be used)</li><li>Eroding Goals (Lowering goals and diminishing the determination to achieve them, which speeds up action in the short term)</li><li>Limits to Growth (A reinforcing growth loop on one side meets a ceiling on the other)</li><li>Tragedy of the Commons (E.g., growing traffic through pop-ups decreases user tolerance and threshold, eventually forcing everyone to rely on pop-ups)</li></ul><h3 id="Guiding-Questions"><a href="#Guiding-Questions" class="headerlink" title="Guiding Questions"></a>Guiding Questions</h3><p><strong>Ideally loop 3 times (5 Whys)</strong> — Resourceful in planning, decisive in judgment:</p><ul><li>To solve the practical business problems in my sub-domain, what are the current potential solutions?</li><li>What are the pros and cons of these solutions, and what scenarios are they suitable for?</li><li>What are the characteristics of my specific scenario?</li><li>Which solution best fits my scenario?</li></ul><h3 id="Example"><a href="#Example" class="headerlink" title="Example"></a>Example</h3><p>A cup holds one liter of water. How can you fit two liters of water into the cup?</p><ul><li>First, deconstruct core elements — Look from multiple perspectives for as many possibilities as possible: cup, water, gravity (First Principles)</li><li>Build intrinsic relationships</li><li>Identify which actions contribute the most to the solution</li></ul><p>Description: Target water level → Perceived gap → Faucet adjustment speed → Faucet flow rate → Current water level → Perceived gap…</p><h2 id="Impact"><a href="#Impact" class="headerlink" title="Impact"></a>Impact</h2><ul><li>If you want to go fast, go alone; if you want to go far, go together</li><li>Two hands can’t fight four</li><li>Three cobblers equal one Zhuge Liang (Two heads are better than one)</li></ul><p>Key Points:</p><ul><li>What are the stakeholders’ needs?</li><li>What solution can be built to satisfy the core needs of these stakeholders, convincing them to commit resources and time to make the project successful?</li><li>Project process and mechanism management</li><li>Risk management</li></ul><p>Guiding Questions:</p><ul><li>What is the background of the project?</li><li>What is the core goal of the project?</li><li>What are the obstacles to project execution?</li><li>What potential solutions can help overcome these obstacles?</li><li>Which solution should be chosen to overcome these obstacles?</li></ul><h2 id="Designing-a-Growth-Framework"><a href="#Designing-a-Growth-Framework" class="headerlink" title="Designing a Growth Framework"></a>Designing a Growth Framework</h2><ul><li>Proactively design learning plans</li><li>Practice effective time management</li><li>Leverage feedback mechanisms</li></ul><h3 id="Best-Practices-—-Knowledge"><a href="#Best-Practices-—-Knowledge" class="headerlink" title="Best Practices — Knowledge"></a>Best Practices — Knowledge</h3><p>Can be explained in one sentence</p><ul><li>Time management</li><li>Contextual listening&#x2F;learning</li><li>Topic-based learning</li><li>Structured slide decks</li><li>Detail-oriented articles</li><li>Micro-reflections</li></ul><h3 id="Cognitive-Upgrades-—-Mindset"><a href="#Cognitive-Upgrades-—-Mindset" class="headerlink" title="Cognitive Upgrades — Mindset"></a>Cognitive Upgrades — Mindset</h3><p>Understand underlying principles to drive behavioral change</p><ul><li>Reflective &#x2F; Automatic brain</li><li>Execution capability</li><li>Meetings</li><li>Value spending (bargain hunting)</li><li>Career planning</li><li>Mentors and benefactors</li></ul><h3 id="Expanding-Circle-of-Competence-—-Skills"><a href="#Expanding-Circle-of-Competence-—-Skills" class="headerlink" title="Expanding Circle of Competence — Skills"></a>Expanding Circle of Competence — Skills</h3><p>Repeated learning and practice</p><ul><li>Software architecture</li><li>Management capability</li><li>Leadership</li><li>Presentation skills</li><li>Writing skills</li><li>Developing others and oneself</li></ul><h2 id="Three-Major-Assets"><a href="#Three-Major-Assets" class="headerlink" title="Three Major Assets"></a>Three Major Assets</h2><h3 id="Attention"><a href="#Attention" class="headerlink" title="Attention"></a>Attention</h3><p>Analyze where your attention is spent:</p><ul><li>Mindlessly scrolling feeds with the crowd (Root cause: insecurity. It relaxes you, but also numbs you)</li><li>Unconsciously reading articles or official account posts (Thinking you are learning, while actually wasting time and attention)</li><li>Hobbies and interests (Interests and skills are vital)</li></ul><p>True security, sense of achievement, and belonging come from self-growth and self-accumulation.</p><p>You don’t need to quit completely; allocate attention reasonably and focus on things that yield cumulative value.</p><p><strong>Set learning goals for only 1–2 domains within 60 days, and read books, articles, or courses related only to those 1–2 domains</strong></p><p>Immersive healing for attention deficit:</p><ul><li>Read and understand word for word</li><li>Think and take notes while reading</li><li>Write articles or synthesize structured slide decks</li></ul><h3 id="Good-Habits"><a href="#Good-Habits" class="headerlink" title="Good Habits"></a>Good Habits</h3><p>Practice <strong>pop-up reminders</strong> to cultivate good habits. High achievers are elite simply because they are driven by uninterrupted positive habits.</p><p>Trigger Condition → Daily Habit → Immediate Brain Reward → Daily Habit Consciousness Reinforcement</p><p>When the “off work” event occurs, <strong>trigger a pop-up for yourself</strong> to change daily habits through multiple choice spaces.</p><p>Trigger Condition (Pop-up) → Daily Habit → Achievement&#x2F;Value Incentive → Daily Habit Consciousness Reinforcement</p><h4 id="4-Areas-of-Refinement"><a href="#4-Areas-of-Refinement" class="headerlink" title="4 Areas of Refinement"></a>4 Areas of Refinement</h4><ul><li>Read good books and take notes</li><li>Summarize frequently and practice architecture</li><li>Write frequently and capture details</li><li>Share generously and become a benefactor to others</li></ul><h4 id="3-Achievements"><a href="#3-Achievements" class="headerlink" title="3 Achievements"></a>3 Achievements</h4><ul><li>Sell a single unit of time multiple times</li><li>Apply deep thinking to handle various scenarios</li><li>Enhance learning ability to transform cognition</li></ul><h3 id="Time"><a href="#Time" class="headerlink" title="Time"></a>Time</h3><p>Common activities during the “third 8 hours”:</p><ul><li>Consumption</li><li>Transaction</li><li>Investment</li></ul><p>Lock in daily “uninterrupted time.” <strong>The difference in life is created by the 3rd eight hours</strong> — invest in your own growth.</p><p>Spending 2 hours every day plus weekends yields usable annual “uninterrupted time”: 192 + 2 * 365 &#x3D; 922</p><h2 id="Two-Major-Methodologies"><a href="#Two-Major-Methodologies" class="headerlink" title="Two Major Methodologies"></a>Two Major Methodologies</h2><h3 id="OKR-Goal-Setting-Method"><a href="#OKR-Goal-Setting-Method" class="headerlink" title="OKR Goal Setting Method"></a>OKR Goal Setting Method</h3><p>Focus + Priority + Key Results</p><p>Objectives + Materials + Output</p><h3 id="Wave-Output-Learning-Method"><a href="#Wave-Output-Learning-Method" class="headerlink" title="Wave Output Learning Method"></a>Wave Output Learning Method</h3><ul><li>Read and listen word-for-word — Understand content and author’s thinking</li><li>Two-pass note-taking — Summarize key knowledge points</li><li>Create slide decks — Abstraction and structuring skills</li><li>Write articles — Synthesis and communication skills</li></ul><h2 id="Advanced-Growth-Path"><a href="#Advanced-Growth-Path" class="headerlink" title="Advanced Growth Path"></a>Advanced Growth Path</h2><ul><li>Technical Skills — Professional knowledge and relevant operational capabilities</li><li>Interpersonal Skills — Communication, coordination, and collaborative organizational abilities</li><li>Conceptual Skills — Comprehensive analysis to identify trends, opportunities, directions, and goals</li></ul><h3 id="Management-Capability-Organizational-mechanism-design-culture-building-and-team-structure-layout"><a href="#Management-Capability-Organizational-mechanism-design-culture-building-and-team-structure-layout" class="headerlink" title="Management Capability: Organizational mechanism design, culture building, and team structure layout."></a>Management Capability: Organizational mechanism design, culture building, and team structure layout.</h3><ul><li>Organization Building — Appointment of leads, facilitating synergy, collaboration channels, team communication</li><li>Cultural Soil — Core values, talent development, succession planning, organizational capability, environment of trust</li><li>Set&#x2F;Deconstruct Goals → Progress, Synergy, Efficiency, Risks → Deliver Results</li></ul><h3 id="Leadership"><a href="#Leadership" class="headerlink" title="Leadership"></a>Leadership</h3><p>Basic Requirements</p><ul><li>Take ownership and responsibility</li><li>Honesty and integrity</li><li>Optimistic and confident</li><li>Self-driven</li></ul><p>Definition: <strong>The ability to do things well and do the right things, serving as a role model</strong></p><ul><li>Timely and effective two-way communication — Minimize surprises, build a safe and trusting communication environment</li><li>Execution — Emphasize action; the capability to turn goals and plans into concrete results</li><li>Make decisions and track processes — Pragmatic, standout, proactively contributing value</li><li>Attention to detail — Focus on specialization; strive for excellence to build technical confidence and product quality</li><li>Learn methods and act as a coach — Lead learning by example, build talent pipelines, and mentor others</li><li>Innovation — Emphasize new ideas and methods, maintaining team vitality and creativity</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;Core&quot;&gt;&lt;a href=&quot;#Core&quot; class=&quot;headerlink&quot; title=&quot;Core&quot;&gt;&lt;/a&gt;Core&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Value Contribution — Plan and identify problems, anticip</summary>
      
    
    
    
    <category term="Career" scheme="https://project256.com/en/categories/career/"/>
    
    
    <category term="Career" scheme="https://project256.com/en/tags/career/"/>
    
  </entry>
  
  <entry>
    <title>Suno Music Generation — Coffee and Sunshine</title>
    <link href="https://project256.com/en/ai-generative-content/Suno%E9%9F%B3%E4%B9%90%E7%94%9F%E6%88%90%E2%80%94%E2%80%94Coffee-and-Sunshine/"/>
    <id>https://project256.com/en/ai-generative-content/Suno%E9%9F%B3%E4%B9%90%E7%94%9F%E6%88%90%E2%80%94%E2%80%94Coffee-and-Sunshine/</id>
    <published>2024-04-02T09:08:20.000Z</published>
    <updated>2026-08-04T09:39:04.562Z</updated>
    
    <content type="html"><![CDATA[<p>Recently, generating music with Suno has been very popular, expanding everyone’s capability boundaries once again.</p><p>As long as you have ideas, a sufficient budget, and good artistic taste, you can create your own works.</p><p>I generated a song, “Coffee and Sunshine”, and I really love both the lyrics and the musical style. Tomorrow morning, let’s have a cup of ☕️ to welcome the sunshine~</p><audio src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/Coffee_and_Sunshine_v2.mp3" preload="none" controls loop>  Your browser does not support the audio tag.</audio><p>There is a line of lyrics in it that I particularly like:</p><blockquote><p>They bring me joy and warm my soul, no need for explanation<br>Sippin’ on that java, feelin’ the rays on my face</p></blockquote><h2 id="Lyrics"><a href="#Lyrics" class="headerlink" title="Lyrics"></a>Lyrics</h2><p>[Verse]<br>Wake up in the morning, I stretch and I yawn<br>The sunlight’s streaming through my window, it’s a brand new dawn<br>I stumble to the kitchen, my eyes still half-closed<br>But there’s one thing that I need before anything else, I suppose</p><p>[Chorus]<br>Coffee and sunshine, the perfect combination<br>They bring me joy and warm my soul, no need for explanation<br>Sippin’ on that java, feelin’ the rays on my face<br>Coffee and sunshine, life’s little embrace</p><p>[Verse 2]<br>Take a sip of the brew, the aroma fills the air<br>My senses awaken, problems seem to disappear<br>I step outside, the sun’s rays kiss my skin<br>It’s gonna be a good day, I can’t help but grin</p><p>[Verse 2]<br>The sky is painted in hues of blue and gold<br>A gentle breeze whispers secrets yet untold<br>Nature’s symphony, the birds begin to sing<br>I’m in a sunshine state of mind, let my soul take wing</p><p>[Chorus]<br>Oh, the world is mine, I’m free as the wind<br>With every breath, a new adventure begins</p><p><a href="https://app.suno.ai/song/94323ee5-2539-4f80-b480-1b90438b3b5e" target="_blank" rel="noopener">Suno music link</a></p><h2 id="Generation-Method"><a href="#Generation-Method" class="headerlink" title="Generation Method"></a>Generation Method</h2><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/WCV3mU.jpg" alt="Suno" title="">                </div>                <div class="image-caption">Suno</div>            </figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Recently, generating music with Suno has been very popular, expanding everyone’s capability boundaries once again.&lt;/p&gt;
&lt;p&gt;As long as you </summary>
      
    
    
    
    <category term="AI and Generative Content" scheme="https://project256.com/en/categories/ai-generative-content/"/>
    
    
    <category term="Large Language Models" scheme="https://project256.com/en/tags/large-language-models/"/>
    
  </entry>
  
  <entry>
    <title>The Road to Quantitative Trading: Strategy Practice</title>
    <link href="https://project256.com/en/quantitative-trading/%E9%87%8F%E5%8C%96%E4%BA%A4%E6%98%93%E4%B9%8B%E8%B7%AF%E2%80%94%E2%80%94%E7%AD%96%E7%95%A5%E5%AE%9E%E8%B7%B5/"/>
    <id>https://project256.com/en/quantitative-trading/%E9%87%8F%E5%8C%96%E4%BA%A4%E6%98%93%E4%B9%8B%E8%B7%AF%E2%80%94%E2%80%94%E7%AD%96%E7%95%A5%E5%AE%9E%E8%B7%B5/</id>
    <published>2024-03-31T23:54:43.000Z</published>
    <updated>2026-08-04T09:39:04.564Z</updated>
    
    <content type="html"><![CDATA[<p>The previous section introduced quantitative trading tools and basic research. This article covers the practical implementation of quantitative trading strategies and their performance.</p><p>By writing trading strategies using GPT-4.0, you can enter the financial programming field at zero cost. If the results are not ideal, you can also have it analyze the tabular files returned by the Interactive Brokers API to optimize parameters (watch out for overfitting).</p><p><strong>Disclaimer: The following is based on my personal research, and the code was generated by GPT. It does not constitute any investment advice. Please invest with caution.</strong></p><h2 id="Personal-Repository"><a href="#Personal-Repository" class="headerlink" title="Personal Repository"></a>Personal Repository</h2><p><a href="https://github.com/SUTFutureCoder/Quant" target="_blank" rel="noopener">Quant</a></p><h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><ol><li><p>Register an <a href="https://www.interactivebrokers.com/cn/pagemap/pagemap_newaccounts_v3.php" target="_blank" rel="noopener">Interactive Brokers account</a> and download <a href="https://www.interactivebrokers.com/en/trading/ibgateway-stable.php" target="_blank" rel="noopener">Gateway</a></p></li><li><p>Install the <a href="https://github.com/mementum/backtrader" target="_blank" rel="noopener">backtrader</a> library (quantitative backtesting framework)</p></li><li><p>Install the <a href="https://github.com/erdewit/ib_insync" target="_blank" rel="noopener">ib_insync</a> library (for interfacing with Interactive Brokers Gateway; the developer <a href="https://github.com/erdewit" target="_blank" rel="noopener">erdewit</a> passed away on March 11, 2024, <strong>RIP</strong>)</p></li><li><p>Write a sample SMA strategy using the ib_insync documentation or GPT</p></li><li><p>Run and observe the charts</p></li></ol><h2 id="Recommended-Common-Frameworks"><a href="#Recommended-Common-Frameworks" class="headerlink" title="Recommended Common Frameworks"></a>Recommended Common Frameworks</h2><p><a href="https://github.com/mementum/backtrader" target="_blank" rel="noopener">backtrader</a> is a traditional strategy framework. Rather than diving into AI hype right away, I prefer to master traditional frameworks first. Developed by a German programmer, its code style, structure, and design are very suitable for learning.</p><p><a href="https://www.youtube.com/watch?v=oEG5LP1ZEvc" target="_blank" rel="noopener">qlib</a> AI quantitative framework</p><h2 id="IB-Gateway-Integration"><a href="#IB-Gateway-Integration" class="headerlink" title="IB Gateway Integration"></a>IB Gateway Integration</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> backtrader <span class="keyword">as</span> bt</span><br><span class="line"><span class="keyword">from</span> ib_insync <span class="keyword">import</span> IB, Stock, util</span><br><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">IBStore</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, host=<span class="string">'127.0.0.1'</span>, port=<span class="number">7497</span>, clientId=<span class="number">1</span>)</span>:</span></span><br><span class="line">        self.ib = IB()</span><br><span class="line">        self.ib.connect(host, port, clientId)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 默认回测一年，每一天为一个bar</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">get_data</span><span class="params">(self, symbol, durationStr=<span class="string">'1 Y'</span>, barSizeSetting=<span class="string">'1 day'</span>, whatToShow=<span class="string">'MIDPOINT'</span>)</span>:</span></span><br><span class="line">        contract = Stock(symbol, <span class="string">'SMART'</span>, <span class="string">'USD'</span>)</span><br><span class="line">        bars = self.ib.reqHistoricalData(</span><br><span class="line">            contract, endDateTime=<span class="string">''</span>, durationStr=durationStr,</span><br><span class="line">            barSizeSetting=barSizeSetting, whatToShow=whatToShow, useRTH=<span class="literal">True</span>)</span><br><span class="line">        df = util.df(bars)</span><br><span class="line">        <span class="comment"># 输出csv，用于GPT解释或优化参数</span></span><br><span class="line">        df.to_csv(<span class="string">'~/'</span> + symbol + <span class="string">'.csv'</span>, sep=<span class="string">','</span>, index=<span class="literal">False</span>, header=<span class="literal">True</span>)</span><br><span class="line">        <span class="keyword">return</span> df</span><br><span class="line">      </span><br><span class="line"><span class="comment"># 这里写策略类</span></span><br><span class="line"><span class="comment"># class SmaCross(bt.Strategy):</span></span><br><span class="line">      </span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line">  <span class="comment"># 对接IB gateway API，端口改为IB gateway设置中的端口</span></span><br><span class="line">    ibstore = IBStore(<span class="string">'127.0.0.1'</span>, <span class="number">4002</span>, clientId=<span class="number">1</span>)</span><br><span class="line">    <span class="comment"># 选股，建议多选几种走势的股票</span></span><br><span class="line">    df = ibstore.get_data(<span class="string">'NVDA'</span>) <span class="comment"># NVDA单调递增</span></span><br><span class="line">    <span class="comment"># df = ibstore.get_data('AAPL') # AAPL波动剧烈</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># Ensure the 'date' column is in the correct datetime format</span></span><br><span class="line">    df[<span class="string">'date'</span>] = pd.to_datetime(df[<span class="string">'date'</span>])</span><br><span class="line">    df.set_index(<span class="string">'date'</span>, inplace=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Load data into Backtrader</span></span><br><span class="line">    data = bt.feeds.PandasData(dataname=df)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Initialize and run Cerebro</span></span><br><span class="line">    cerebro = bt.Cerebro(stdstats=<span class="literal">True</span>, cheat_on_open=<span class="literal">True</span>, optreturn=<span class="literal">False</span>)</span><br><span class="line">    cerebro.addstrategy(SmaCross) <span class="comment"># 这里改为你的策略类名</span></span><br><span class="line">    cerebro.broker.set_cash(<span class="number">100000</span>) <span class="comment"># 本金</span></span><br><span class="line">    <span class="comment"># cerebro.broker.setcommission(commission=0.001) #交易手续费</span></span><br><span class="line">    cerebro.adddata(data)</span><br><span class="line">    cerebro.run()</span><br><span class="line">    cerebro.plot()</span><br></pre></td></tr></table></figure><h2 id="SMA-Strategy"><a href="#SMA-Strategy" class="headerlink" title="SMA Strategy"></a>SMA Strategy</h2><h3 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h3><p>SMA (Simple Moving Average) sounds simple, as its name suggests, making it ideal for beginners to quickly run through a backtesting process.</p><p>SMA is calculated by taking the closing prices over a past period of time and averaging them.</p><blockquote><p>For example, to calculate a security’s 20-day SMA, add up the closing prices of the past 20 days and divide by 20.</p><p>Similarly, to calculate a security’s 200-day SMA, add up the closing prices of the past 200 days and divide by 200.</p></blockquote><p>Each data point in an SMA has equal weight. However, investors might consider recent data to be more important than older data and prefer to assign higher weight to recent prices. Therefore, they often favor another form of moving average, the EMA (Exponential Moving Average). EMA can provide a better reference when the market experiences rapid and sharp fluctuations.</p><h3 id="Trend-Identification"><a href="#Trend-Identification" class="headerlink" title="Trend Identification"></a>Trend Identification</h3><p>A security trading above its SMA is in an uptrend, while one trading below its SMA is in a downtrend.</p><p>It can also be used to identify support and resistance levels.</p><h3 id="Crossovers"><a href="#Crossovers" class="headerlink" title="Crossovers"></a>Crossovers</h3><h4 id="Bullish-Crossovers-and-Bearish-Crossovers"><a href="#Bullish-Crossovers-and-Bearish-Crossovers" class="headerlink" title="Bullish Crossovers and Bearish Crossovers"></a>Bullish Crossovers and Bearish Crossovers</h4><p>Investment strategies involving SMA generally rely on bullish crossovers and bearish crossovers.</p><p>A bullish crossover occurs when a security’s price falls below the SMA and then moves back above it. This signifies the end of a pullback and signals the start of an uptrend. Conversely, a bearish crossover signals the beginning of a pullback.</p><p>However, in a range-bound (oscillating) market, this indicator is less effective.</p><h4 id="Golden-Cross-and-Death-Cross"><a href="#Golden-Cross-and-Death-Cross" class="headerlink" title="Golden Cross and Death Cross"></a>Golden Cross and Death Cross</h4><p>A Golden Cross occurs when a short-term SMA crosses above a long-term SMA. For example, when the 50-day SMA crosses above the 200-day SMA, a Golden Cross appears, which is a bullish signal. The opposite is a Death Cross, which is a bearish signal.</p><p>When a Death Cross occurs, investors might consider temporarily pulling out of the market. (Reflecting on the pharma fund I bought—even though it had been in a Death Cross for two years, I was still stubborn enough to gamble on it…)</p><h3 id="Code"><a href="#Code" class="headerlink" title="Code"></a>Code</h3><p>Generated by GPT-4. Note that for simplicity, the code executes all-in buy and sell orders when a crossover occurs.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SmaCross</span><span class="params">(bt.Strategy)</span>:</span></span><br><span class="line">    params = dict(pfast=<span class="number">13</span>, pslow=<span class="number">25</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Define trading strategy</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self)</span>:</span></span><br><span class="line">        sma1 = bt.ind.SMA(period=self.p.pfast)</span><br><span class="line">        sma2 = bt.ind.SMA(period=self.p.pslow)</span><br><span class="line">        self.crossover = bt.ind.CrossOver(sma1, sma2)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># Custom trade tracking</span></span><br><span class="line">        self.trade_data = []</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Execute trades</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">next</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="comment"># Trading the entire portfolio</span></span><br><span class="line">        size = int(self.broker.get_cash() / self.data.close[<span class="number">0</span>])</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> self.position:</span><br><span class="line">            <span class="keyword">if</span> self.crossover &gt; <span class="number">0</span>:</span><br><span class="line">                self.buy(size=size)</span><br><span class="line">                self.entry_bar = len(self)  <span class="comment"># Record entry bar index</span></span><br><span class="line">        <span class="keyword">elif</span> self.crossover &lt; <span class="number">0</span>:</span><br><span class="line">            self.close()</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Record trade details</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">notify_trade</span><span class="params">(self, trade)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> trade.isclosed:</span><br><span class="line">            exit_bar = len(self)</span><br><span class="line">            holding_period = exit_bar - self.entry_bar</span><br><span class="line">            trade_record = &#123;</span><br><span class="line">                <span class="string">'entry'</span>: self.entry_bar,</span><br><span class="line">                <span class="string">'exit'</span>: exit_bar,</span><br><span class="line">                <span class="string">'duration'</span>: holding_period,</span><br><span class="line">                <span class="string">'profit'</span>: trade.pnl</span><br><span class="line">            &#125;</span><br><span class="line">            self.trade_data.append(trade_record)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Caclulating holding periods</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">stop</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="comment"># Calculate and print average holding periods</span></span><br><span class="line">        total_holding = sum([trade[<span class="string">'duration'</span>] <span class="keyword">for</span> trade <span class="keyword">in</span> self.trade_data])</span><br><span class="line">        total_trades = len(self.trade_data)</span><br><span class="line">        avg_holding_period = round(total_holding / total_trades) <span class="keyword">if</span> total_trades &gt; <span class="number">0</span> <span class="keyword">else</span> <span class="number">0</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># Calculating for winners and losers separately</span></span><br><span class="line">        winners = [trade <span class="keyword">for</span> trade <span class="keyword">in</span> self.trade_data <span class="keyword">if</span> trade[<span class="string">'profit'</span>] &gt; <span class="number">0</span>]</span><br><span class="line">        losers = [trade <span class="keyword">for</span> trade <span class="keyword">in</span> self.trade_data <span class="keyword">if</span> trade[<span class="string">'profit'</span>] &lt; <span class="number">0</span>]</span><br><span class="line">        avg_winner_holding = round(sum(trade[<span class="string">'duration'</span>] <span class="keyword">for</span> trade <span class="keyword">in</span> winners) / len(winners))<span class="keyword">if</span> winners <span class="keyword">else</span> <span class="number">0</span></span><br><span class="line">        avg_loser_holding = round(sum(trade[<span class="string">'duration'</span>] <span class="keyword">for</span> trade <span class="keyword">in</span> losers) / len(losers)) <span class="keyword">if</span> losers <span class="keyword">else</span> <span class="number">0</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># Display average holding period statistics</span></span><br><span class="line">        print(<span class="string">'Average Holding Period:'</span>, avg_holding_period)</span><br><span class="line">        print(<span class="string">'Average Winner Holding Period:'</span>, avg_winner_holding)</span><br><span class="line">        print(<span class="string">'Average Loser Holding Period:'</span>, avg_loser_holding)</span><br></pre></td></tr></table></figure><h3 id="Performance"><a href="#Performance" class="headerlink" title="Performance"></a>Performance</h3><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/截屏2024-03-3118.23.16.png" alt="NVDA Stock" title="">                </div>                <div class="image-caption">NVDA Stock</div>            </figure><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/截屏2024-03-3118.25.11.png" alt="AAPL" title="">                </div>                <div class="image-caption">AAPL</div>            </figure><h3 id="Analysis"><a href="#Analysis" class="headerlink" title="Analysis"></a>Analysis</h3><p>SMA performed well on NVDA, a stock in a monotonic uptrend. NVDA rode the AI wave all the way up, but how many such hype waves are there really?</p><p>More common are fluctuating trends like AAPL’s, where SMA actually resulted in a loss of around 8%.</p><h2 id="MACD-Strategy"><a href="#MACD-Strategy" class="headerlink" title="MACD Strategy"></a>MACD Strategy</h2><h3 id="Introduction-1"><a href="#Introduction-1" class="headerlink" title="Introduction"></a>Introduction</h3><p>MACD (Moving Average Convergence&#x2F;Divergence) is known as the moving average convergence&#x2F;divergence indicator. It is visible on most exchange charts and is often dubbed the <strong>King of Technical Indicators</strong>.</p><p>The MACD indicator consists of three main components: the DIF line, the DEA line, and the MACD histogram. The standard parameters are 12, 26, and 9, as shown in the code below.</p><ul><li>The DIF line is the fast exponential moving average (typically 12 days) minus the slow exponential moving average (typically 26 days).</li><li>The DEA line is the 9-day weighted moving average of the DIF line.</li><li>The MACD histogram represents the difference between the DIF line and the DEA line, used to show the degree of divergence between the DIF and DEA lines.</li></ul><h3 id="Crossover-Strategy"><a href="#Crossover-Strategy" class="headerlink" title="Crossover Strategy"></a>Crossover Strategy</h3><p>The basic principle of MACD relies on golden crosses and death crosses:</p><ul><li>A golden cross occurs when the DIF line crosses above the DEA line, indicating strengthening bullish momentum and suggesting a potential market uptrend, which serves as a buy signal.</li><li>A death cross occurs when the DIF line crosses below the DEA line, indicating strengthening bearish momentum and suggesting a potential market downtrend, which serves as a sell signal.</li></ul><h3 id="Divergence-Strategy"><a href="#Divergence-Strategy" class="headerlink" title="Divergence Strategy"></a>Divergence Strategy</h3><h4 id="Red-x2F-Green-Bar-Divergence"><a href="#Red-x2F-Green-Bar-Divergence" class="headerlink" title="Red&#x2F;Green Bar Divergence"></a>Red&#x2F;Green Bar Divergence</h4><p>Red Bar Bearish Divergence: The stock price hits a new high, but the red bars fail to reach a new high. Upward momentum is weakening, which may signal a major correction.</p><p>Green Bar Bullish Divergence: Although the stock price is falling, downward momentum is gradually diminishing. Once bullish momentum gains the upper hand, the market trend will reverse from bearish to bullish.</p><h4 id="Yellow-x2F-White-Line-Divergence"><a href="#Yellow-x2F-White-Line-Divergence" class="headerlink" title="Yellow&#x2F;White Line Divergence"></a>Yellow&#x2F;White Line Divergence</h4><p>The stock price hits a new high&#x2F;low, but the yellow and white lines fail to hit a new high&#x2F;low. This signals a major correction or a bullish reversal.</p><h4 id="Buy-Points"><a href="#Buy-Points" class="headerlink" title="Buy Points"></a>Buy Points</h4><p>Bullish divergence may recur while prices probe lower, which can lead to getting trapped. Confirmation requires multiple recurrences (for stocks in a downtrend, it is best for bullish divergence to occur at least twice).</p><p>Position building should only be considered when the price transitions from a fluctuating trend to sideways consolidation. The optimal buy point is a <strong>golden cross above&#x2F;at the zero line</strong>.</p><p>If no zero-line golden cross occurs:</p><ul><li>Look for support levels after a breakout, i.e., positions where resistance turns into support (pin bar bottoming out with a small bullish candle).</li><li>Look for continuation setups &#x2F; mid-air refueling points (when the white line starts curling upward).</li><li>Wait for a high-volume breakout from the current sideways consolidation range.</li></ul><p>Build positions gradually; do not go all-in on the first day of a pin-bar bottom.</p><h4 id="Sell-Points"><a href="#Sell-Points" class="headerlink" title="Sell Points"></a>Sell Points</h4><p>The stock price hits a new high; while the red and white lines show no bearish divergence, the red bars exhibit bearish divergence, indicating declining upward momentum.</p><p>When high volume is accompanied by stagnant prices, violating the logic of volume-price expansion, strong selling pressure is present.</p><p>This could mean profit-takers or insiders knowing it’s a peak are selling, and institutional money is exiting—a clear sell signal.</p><p>After a double bearish divergence signal appears, a pullback is highly probable to digest the divergence.</p><h4 id="Summary"><a href="#Summary" class="headerlink" title="Summary"></a>Summary</h4><p>When bearish divergence appears, a correction usually follows: in a best-case scenario, a wave adjustment; in a worst-case scenario, a trend reversal.</p><p>When bullish divergence appears, many stocks undergo a long period of accumulation and shakeouts before entering a primary wave of rise.</p><p>The more frequently and longer divergence occurs, the stronger the main uptrend will be.</p><p>The longer the horizontal consolidation, the higher the vertical rise.</p><p><strong>MACD is a trend-following indicator, suitable only for trend stocks rather than sentiment-driven stocks, because it has significant lag. (Just think about those 3 parameters)</strong></p><p>There is no method in the market with a 100% success rate.</p><h3 id="Summary-Formula-by-Xueqiu-Veteran"><a href="#Summary-Formula-by-Xueqiu-Veteran" class="headerlink" title="Summary Formula by Xueqiu Veteran"></a>Summary Formula by Xueqiu Veteran</h3><p><a href="https://xueqiu.com/2096888159/143983760" target="_blank" rel="noopener">One Center, Two Basic Points, Four Basic Principles</a></p><h3 id="Code-1"><a href="#Code-1" class="headerlink" title="Code"></a>Code</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 定义策略</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">DynamicMACDStrategy</span><span class="params">(bt.Strategy)</span>:</span></span><br><span class="line">    params = (</span><br><span class="line">        (<span class="string">'fast'</span>, <span class="number">12</span>),</span><br><span class="line">        (<span class="string">'slow'</span>, <span class="number">26</span>),</span><br><span class="line">        (<span class="string">'signal'</span>, <span class="number">9</span>),</span><br><span class="line">    )</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self)</span>:</span></span><br><span class="line">        self.macd = bt.indicators.MACD(self.data.close,</span><br><span class="line">                                       period_me1=self.params.fast,</span><br><span class="line">                                       period_me2=self.params.slow,</span><br><span class="line">                                       period_signal=self.params.signal)</span><br><span class="line">        self.crossover = bt.indicators.CrossOver(self.macd.macd, self.macd.signal)</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">next</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> self.position:  <span class="comment"># 没有持仓</span></span><br><span class="line">            <span class="keyword">if</span> self.crossover &gt; <span class="number">0</span>:  <span class="comment"># MACD线上穿信号线</span></span><br><span class="line">                self.buy()</span><br><span class="line">        <span class="keyword">elif</span> self.crossover &lt; <span class="number">0</span>:  <span class="comment"># 已有持仓且MACD线下穿信号线</span></span><br><span class="line">            self.sell()</span><br></pre></td></tr></table></figure><h3 id="Performance-1"><a href="#Performance-1" class="headerlink" title="Performance"></a>Performance</h3><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/截屏2024-03-3119.02.12.png" alt="NVDA" title="">                </div>                <div class="image-caption">NVDA</div>            </figure><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/截屏2024-03-3119.03.51.png" alt="AAPL" title="">                </div>                <div class="image-caption">AAPL</div>            </figure><h3 id="Analysis-1"><a href="#Analysis-1" class="headerlink" title="Analysis"></a>Analysis</h3><p>It appears that a monotonic uptrend yields positive returns, whereas frequent fluctuations leave the principal mostly unchanged.</p><h2 id="Enhanced-MACD-Strategy"><a href="#Enhanced-MACD-Strategy" class="headerlink" title="Enhanced MACD Strategy"></a>Enhanced MACD Strategy</h2><h3 id="GPT-4"><a href="#GPT-4" class="headerlink" title="GPT-4"></a>GPT-4</h3><p>From the simple MACD strategy code above, we noticed that it executes all-in buy and sell orders whenever a golden cross or death cross occurs.</p><p>So I posed this question to GPT-4 to see how it would solve it.</p><blockquote><p>I ran this strategy, but the profit is too low—possibly because it buys and sells the entire position at once. Please optimize the strategy code to achieve better profitability through appropriate position-sizing buy and sell strategies.</p></blockquote><h3 id="Code-2"><a href="#Code-2" class="headerlink" title="Code"></a>Code</h3><p>GPT allows specifying capital allocation ratios, stop-loss&#x2F;take-profit ratios, and profit thresholds, which looks much more scientific than going all-in.</p><p>Additionally, it provides a method to search for the fast, slow, and signal parameters. You can execute it with default settings first, then check the three parameters output to the console. Next, input these parameters into <code>cerebro.optstrategy(ImprovedMACDStrategy)</code>, for example: <code>cerebro.optstrategy(ImprovedMACDStrategy, fast=10, slow=27, signal=5)</code>.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ImprovedMACDStrategy</span><span class="params">(bt.Strategy)</span>:</span></span><br><span class="line">    params = (</span><br><span class="line">        (<span class="string">'fast'</span>, <span class="number">12</span>),</span><br><span class="line">        (<span class="string">'slow'</span>, <span class="number">26</span>),</span><br><span class="line">        (<span class="string">'signal'</span>, <span class="number">9</span>),</span><br><span class="line">        (<span class="string">'order_percentage'</span>, <span class="number">0.95</span>),  <span class="comment"># 买入资金比例</span></span><br><span class="line">        (<span class="string">'stop_loss_percentage'</span>, <span class="number">0.95</span>),  <span class="comment"># 止损比例</span></span><br><span class="line">        (<span class="string">'take_profit_percentage'</span>, <span class="number">1.05</span>),  <span class="comment"># 止盈比例</span></span><br><span class="line">        (<span class="string">'partial_sell_percentage'</span>, <span class="number">0.5</span>),  <span class="comment"># 部分卖出比例</span></span><br><span class="line">        (<span class="string">'profit_threshold'</span>, <span class="number">1.02</span>),  <span class="comment"># 盈利阈值，达到则部分卖出</span></span><br><span class="line">    )</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self)</span>:</span></span><br><span class="line">        self.macd = bt.indicators.MACD(self.data.close,</span><br><span class="line">                                       period_me1=self.params.fast,</span><br><span class="line">                                       period_me2=self.params.slow,</span><br><span class="line">                                       period_signal=self.params.signal)</span><br><span class="line">        self.crossover = bt.indicators.CrossOver(self.macd.macd, self.macd.signal)</span><br><span class="line">        self.order = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">notify_order</span><span class="params">(self, order)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> order.status <span class="keyword">in</span> [order.Completed]:</span><br><span class="line">            <span class="keyword">if</span> order.isbuy():</span><br><span class="line">                self.buy_price = order.executed.price</span><br><span class="line">                self.stop_price = self.buy_price * self.params.stop_loss_percentage</span><br><span class="line">                self.limit_price = self.buy_price * self.params.take_profit_percentage</span><br><span class="line">            self.order = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">next</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> self.order:</span><br><span class="line">            <span class="keyword">return</span>  <span class="comment"># 如果有未完成的订单，则不执行任何操作</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> self.position:  <span class="comment"># 没有持仓时</span></span><br><span class="line">            <span class="keyword">if</span> self.crossover &gt; <span class="number">0</span>:  <span class="comment"># MACD线上穿信号线，买入信号</span></span><br><span class="line">                self.order = self.buy(size=(self.broker.get_cash() / self.data.close[<span class="number">0</span>]) * self.params.order_percentage)</span><br><span class="line">        <span class="keyword">else</span>:  <span class="comment"># 已有持仓时</span></span><br><span class="line">            <span class="keyword">if</span> self.data.close[<span class="number">0</span>] / self.buy_price &gt; self.params.profit_threshold:</span><br><span class="line">                <span class="comment"># 达到盈利阈值，部分卖出</span></span><br><span class="line">                self.order = self.sell(size=self.position.size * self.params.partial_sell_percentage)</span><br><span class="line">            <span class="keyword">elif</span> self.crossover &lt; <span class="number">0</span> <span class="keyword">or</span> self.data.close[<span class="number">0</span>] &lt; self.stop_price <span class="keyword">or</span> self.data.close[<span class="number">0</span>] &gt; self.limit_price:</span><br><span class="line">                <span class="comment"># MACD线下穿信号线，或触发止损止盈，全仓卖出</span></span><br><span class="line">                self.order = self.close()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line">    ibstore = IBStore(<span class="string">'127.0.0.1'</span>, <span class="number">4002</span>, clientId=<span class="number">1</span>)</span><br><span class="line">    <span class="comment"># df = ibstore.get_data('NVDA')</span></span><br><span class="line">    df = ibstore.get_data(<span class="string">'AAPL'</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Ensure the 'date' column is in the correct datetime format</span></span><br><span class="line">    df[<span class="string">'date'</span>] = pd.to_datetime(df[<span class="string">'date'</span>])</span><br><span class="line">    df.set_index(<span class="string">'date'</span>, inplace=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Load data into Backtrader</span></span><br><span class="line">    data = bt.feeds.PandasData(dataname=df)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Initialize and run Cerebro</span></span><br><span class="line">    cerebro = bt.Cerebro(stdstats=<span class="literal">True</span>, cheat_on_open=<span class="literal">True</span>, optreturn=<span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 1-默认参数</span></span><br><span class="line">    <span class="comment"># cerebro.optstrategy(ImprovedMACDStrategy)</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 2-范围里面找最优解，查看控制台With Parameters输出。不同股票需要重新计算。</span></span><br><span class="line">    <span class="comment"># cerebro.optstrategy(ImprovedMACDStrategy, fast=range(10, 15), slow=range(20, 30), signal=range(5, 10))</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 3-根据With Parameters输出，配置最优解。这个是AAPL的最优解，不同股票需要重新计算。</span></span><br><span class="line">    <span class="comment"># cerebro.optstrategy(ImprovedMACDStrategy, fast=10, slow=27, signal=5)</span></span><br><span class="line">    cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name=<span class="string">'sharpe'</span>)</span><br><span class="line">    cerebro.addanalyzer(bt.analyzers.Returns, _name=<span class="string">'returns'</span>)</span><br><span class="line">    cerebro.broker.set_cash(<span class="number">100000</span>)</span><br><span class="line">    <span class="comment"># cerebro.broker.setcommission(commission=0.001)</span></span><br><span class="line">    cerebro.adddata(data)  <span class="comment"># 确保这里的data是bt.feeds对象，不是pandas DataFrame</span></span><br><span class="line">    result = cerebro.run()</span><br><span class="line"></span><br><span class="line">    best_params = <span class="literal">None</span></span><br><span class="line">    best_sharpe = float(<span class="string">'-inf'</span>)  <span class="comment"># 初始化为负无穷大</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> run <span class="keyword">in</span> result:</span><br><span class="line">        <span class="keyword">for</span> strategy <span class="keyword">in</span> run:</span><br><span class="line">            sharpe = strategy.analyzers.sharpe.get_analysis()[<span class="string">'sharperatio'</span>]</span><br><span class="line">            params = strategy.params</span><br><span class="line">            <span class="keyword">if</span> sharpe &gt; best_sharpe:</span><br><span class="line">                best_sharpe = sharpe</span><br><span class="line">                best_params = params</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 根据</span></span><br><span class="line">    print(<span class="string">f"Best Sharpe Ratio: <span class="subst">&#123;best_sharpe&#125;</span>"</span>)</span><br><span class="line">    print(<span class="string">f"With Parameters: Fast=<span class="subst">&#123;best_params.fast&#125;</span>, Slow=<span class="subst">&#123;best_params.slow&#125;</span>, Signal=<span class="subst">&#123;best_params.signal&#125;</span>"</span>)</span><br><span class="line">    cerebro.plot()</span><br></pre></td></tr></table></figure><h3 id="Performance-2"><a href="#Performance-2" class="headerlink" title="Performance"></a>Performance</h3><h4 id="Default-Parameters-12-26-9"><a href="#Default-Parameters-12-26-9" class="headerlink" title="Default Parameters (12, 26, 9)"></a>Default Parameters (12, 26, 9)</h4><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/截屏2024-03-3119.11.52.png" alt="NVDA" title="">                </div>                <div class="image-caption">NVDA</div>            </figure><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/截屏2024-03-3119.11.01.png" alt="AAPL" title="">                </div>                <div class="image-caption">AAPL</div>            </figure><h4 id="Optimal-Parameters-for-Individual-Stocks"><a href="#Optimal-Parameters-for-Individual-Stocks" class="headerlink" title="Optimal Parameters for Individual Stocks"></a>Optimal Parameters for Individual Stocks</h4><p><strong>Note: Parameters for different stocks need to be recalculated. If the market is not oscillating&#x2F;range-bound, parameter calculations might not yield optimization benefits.</strong></p><p>NVDA</p><p>Best Sharpe Ratio: 0.8460012912318833<br>With Parameters: Fast&#x3D;13, Slow&#x3D;24, Signal&#x3D;9</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/截屏2024-03-3119.17.03.png" alt="NVDA" title="">                </div>                <div class="image-caption">NVDA</div>            </figure><p>AAPL</p><p>Best Sharpe Ratio: 0.801877924498146<br>With Parameters: Fast&#x3D;10, Slow&#x3D;27, Signal&#x3D;5</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/截屏2024-03-3119.13.48.png" alt="AAPL" title="">                </div>                <div class="image-caption">AAPL</div>            </figure><h2 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>This article built a backtesting toolkit, introduced several common technical indicators, and provided the corresponding code and execution results.</p><p>Next, I will continue to optimize MACD and other strategies to achieve better performance.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;The previous section introduced quantitative trading tools and basic research. This article covers the practical implementation of quanti</summary>
      
    
    
    
    <category term="Quantitative Trading" scheme="https://project256.com/en/categories/quantitative-trading/"/>
    
    
    <category term="Quantitative Trading" scheme="https://project256.com/en/tags/quantitative-trading/"/>
    
  </entry>
  
  <entry>
    <title>The Road to Quantitative Trading — Basic Research</title>
    <link href="https://project256.com/en/quantitative-trading/%E9%87%8F%E5%8C%96%E4%BA%A4%E6%98%93%E4%B9%8B%E8%B7%AF%E2%80%94%E2%80%94%E5%9F%BA%E7%A1%80%E7%A0%94%E7%A9%B6/"/>
    <id>https://project256.com/en/quantitative-trading/%E9%87%8F%E5%8C%96%E4%BA%A4%E6%98%93%E4%B9%8B%E8%B7%AF%E2%80%94%E2%80%94%E5%9F%BA%E7%A1%80%E7%A0%94%E7%A9%B6/</id>
    <published>2024-03-31T07:49:14.000Z</published>
    <updated>2026-08-04T09:39:04.564Z</updated>
    
    <content type="html"><![CDATA[<p>The convenience of the investment market process has fostered irrational investing among people.</p><p>The hellish market from 2022 to 2024 turned my mindset, life, and account into a complete mess. Leaving aside external issues like fund managers playing dead, I also executed some irrational operations that resulted in losses.</p><p>Reflecting on these painful lessons, in order to avoid the continuous impact of market volatility on my mindset and daily life, I turned my focus toward quantitative automated trading.</p><p>As for investment, for someone like me who doesn’t have time to watch the market but wants to achieve returns exceeding index funds, I still trust the ultra-rational decision-making of computers.</p><p><strong>Disclaimer: The following is my personal research content and does not represent any investment advice. Please invest with caution.</strong></p><h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><ol><li><p>Register an <a href="https://www.interactivebrokers.com/cn/pagemap/pagemap_newaccounts_v3.php" target="_blank" rel="noopener">Interactive Brokers account</a> and download <a href="https://www.interactivebrokers.com/en/trading/ibgateway-stable.php" target="_blank" rel="noopener">Gateway</a></p></li><li><p>Install the <a href="https://github.com/mementum/backtrader" target="_blank" rel="noopener">backtrader</a> library (quantitative backtesting framework)</p></li><li><p>Install the <a href="https://github.com/erdewit/ib_insync" target="_blank" rel="noopener">ib_insync</a> library (interfaces with Interactive Brokers Gateway; developer bro <a href="https://github.com/erdewit" target="_blank" rel="noopener">erdewit</a> passed away on March 11, 2024, RIP)</p></li><li><p>Write a sample SMA strategy using the ib_insync documentation or GPT</p></li><li><p>Run it and observe the charts</p></li></ol><h2 id="Recommended-Common-Frameworks"><a href="#Recommended-Common-Frameworks" class="headerlink" title="Recommended Common Frameworks"></a>Recommended Common Frameworks</h2><p><a href="https://github.com/mementum/backtrader" target="_blank" rel="noopener">backtrader </a> Traditional strategy framework </p><p><a href="https://www.youtube.com/watch?v=oEG5LP1ZEvc" target="_blank" rel="noopener">qlib</a> AI quantitative framework</p><h2 id="Turtle-Trading-System"><a href="#Turtle-Trading-System" class="headerlink" title="Turtle Trading System"></a>Turtle Trading System</h2><p>Essentially a trend-following strategy, the rules encompass every aspect of trading, leaving no room for subjective thinking by the trader.</p><p>It has some connection to quantitative trading and can serve as a method for setting relevant parameters. Of course, parameters can also be automatically found via programs.</p><h3 id="Core-Mindset"><a href="#Core-Mindset" class="headerlink" title="Core Mindset"></a>Core Mindset</h3><ul><li>View trading with a long-term perspective</li><li>Avoid outcome bias</li><li>Believe in the power of positive expectancy</li></ul><h3 id="Operational-Level"><a href="#Operational-Level" class="headerlink" title="Operational Level"></a>Operational Level</h3><ul><li>First, master an advantageous system and find a trading strategy with a positive expected value (at least producing positive returns in historical backtests)</li><li>Manage risk and hold your ground</li><li>Unswervingly execute the strategy</li><li>Keep it simple and clear, and catch the trends. Most profits come from 2–3 trades; do not miss a single trend. Otherwise, the year’s hard work will vanish into thin air.</li></ul><h3 id="Steps"><a href="#Steps" class="headerlink" title="Steps"></a>Steps</h3><h4 id="Markets-What-to-Buy-or-Sell"><a href="#Markets-What-to-Buy-or-Sell" class="headerlink" title="Markets: What to Buy or Sell?"></a>Markets: What to Buy or Sell?</h4><p>Generally used in futures markets with sufficient liquidity: 30-year US Treasury bonds, 10-year US Treasury bonds, commodities, and currencies of developed countries.</p><h4 id="Position-Sizing-How-Much-to-Buy-or-Sell"><a href="#Position-Sizing-How-Much-to-Buy-or-Sell" class="headerlink" title="Position Sizing: How Much to Buy or Sell?"></a>Position Sizing: How Much to Buy or Sell?</h4><p>Core idea: Hold smaller positions in high-volatility contracts and larger positions in low-volatility contracts.</p><p>Method: Dynamically build positions using volatility to control the intra-day loss to no more than 1% of total assets.</p><blockquote><p>H: High price of the day</p><p>L: Low price of the day</p><p>C: Closing price of the day</p><p>TR: Daily True Range</p><p>TR &#x3D; MAX(H-L, H-C, C-L)</p></blockquote><p>Calculate N, the 20-day moving average of TR:</p><blockquote><p>N &#x3D; (19 * PDN + TR) &#x2F; 20</p><p>PDN: Previous day’s N value</p><p>TR: Daily True Range</p></blockquote><p>Calculation begins on day 21. For the preceding 20 days, the average value is calculated by summing the TR of the previous 20 days and dividing by 20.</p><blockquote><p>(1% of Account &#x2F; Unit Position Dollar Amount) &#x3D; (N &#x2F; Stock Price)</p><p>Unit Position Dollar Amount &#x3D; (1% of Account &#x2F; N) * Stock Price</p><p>Unit Position Share Count &#x3D; (1% of Account &#x2F; N)</p></blockquote><p>The higher the stock’s volatility, the larger N is, and the smaller the position size.</p><p>The Turtle Trading System requires that positions in a single market cannot exceed 4 units of position size.</p><h4 id="Entry-When-to-Buy-or-Sell"><a href="#Entry-When-to-Buy-or-Sell" class="headerlink" title="Entry: When to Buy or Sell?"></a>Entry: When to Buy or Sell?</h4><blockquote><p>System 1: Short-term system based on a 20-day breakout</p><p>System 2: Long-term system based on a 55-day breakout</p></blockquote><p>For the long term, take action once the 55-day high or low point is breached:</p><blockquote><p>If it exceeds the 55-day high, buy one unit position to start going long</p><p>If it breaks below the 55-day low, sell one unit position to start going short [If you are not familiar with shorting, you can only consider going long]</p></blockquote><h4 id="Building-Positions-Establish-a-Unit-Position-at-the-Breakout-Point"><a href="#Building-Positions-Establish-a-Unit-Position-at-the-Breakout-Point" class="headerlink" title="Building Positions: Establish a Unit Position at the Breakout Point"></a>Building Positions: Establish a Unit Position at the Breakout Point</h4><pre><code>Then gradually expand the position step by step at price intervals of (1/2 N)Add one unit position for every 1/2 N interval until reaching the maximum of 4 unitsGradual position adjustment is clever; if the stock price drops by 1 N, the total loss still remains at 1% of total assets</code></pre><h4 id="Stop-Loss-When-to-Abandon-a-Losing-Position"><a href="#Stop-Loss-When-to-Abandon-a-Losing-Position" class="headerlink" title="Stop-Loss: When to Abandon a Losing Position?"></a>Stop-Loss: When to Abandon a Losing Position?</h4><pre><code>**No single trade can lose more than 2% of total assets**A fluctuation of 1 N corresponds to a 1% fluctuation in total assetsCalculated at 2%, the stop-loss standard is 2 NAfter the final buy order, if the stock price falls by 2 N, stop loss immediately</code></pre><h4 id="Exit-When-to-Exit-a-Profitable-Position"><a href="#Exit-When-to-Exit-a-Profitable-Position" class="headerlink" title="Exit: When to Exit a Profitable Position?"></a>Exit: When to Exit a Profitable Position?</h4><blockquote><p>System 1: Uses the 10-day breakout exit rule</p></blockquote><blockquote><p>System 2: Uses the 20-day breakout exit rule</p></blockquote><p>Exit when a 20-day new low is reached</p><blockquote><p>Pros: Although somewhat counter-intuitive, it guarantees that position holders will not miss massive upward trends</p><p>Cons: Watching huge profits evaporate in an instant</p></blockquote><h4 id="Tactics-How-to-Buy-and-Sell"><a href="#Tactics-How-to-Buy-and-Sell" class="headerlink" title="Tactics: How to Buy and Sell?"></a>Tactics: How to Buy and Sell?</h4><p>If the capital is small, there’s no need to consider this.</p><h2 id="M-Score-Model-for-Detecting-Financial-Fraud"><a href="#M-Score-Model-for-Detecting-Financial-Fraud" class="headerlink" title="M-Score Model for Detecting Financial Fraud"></a>M-Score Model for Detecting Financial Fraud</h2><p>Filter stocks before selection to kick out fraud-tainted stocks.</p><blockquote><p>M &#x3D; -4.84 + 0.92 * DSRI + 0.528 * GMI + 0.404 * AQI + 0.892 * SGI + 0.115 * DEPI - 0.172 * SGAI + 4.679 * TATA - 0.327 * LVGI</p></blockquote><p>A simple linear formula; just calculate the M-value.</p><h3 id="Parameter-Explanations"><a href="#Parameter-Explanations" class="headerlink" title="Parameter Explanations"></a>Parameter Explanations</h3><p>DSRI Days Sales in Receivables Index</p><p>DSRI &#x3D; (Current Period Receivables &#x2F; Current Period Revenue) &#x2F; (Prior Period Receivables &#x2F; Prior Period Revenue)</p><p>GMI Gross Margin Index</p><blockquote><p>GMI &#x3D; (Prior Period Gross Margin) &#x2F; (Current Period Gross Margin)</p><p>Gross Margin &#x3D; (Revenue - Cost of Goods Sold) &#x2F; Revenue</p></blockquote><p>AQI Asset Quality Index</p><blockquote><p>AQI &#x3D; (Current Period Non-Current Assets &#x2F; Current Period Total Assets) &#x2F; (Prior Period Non-Current Assets &#x2F; Prior Period Total Assets)</p><p>Non-Current Assets &#x3D; (Total Assets - Current Assets - Property, Plant &amp; Equipment - Securities)</p></blockquote><p>SGI Sales Growth Index</p><blockquote><p>SGI &#x3D; Current Period Revenue &#x2F; Prior Period Revenue</p></blockquote><p>DEPI Depreciation Index</p><blockquote><p>DEPI &#x3D; Prior Period Depreciation Rate &#x2F; Current Period Depreciation Rate</p><p>Depreciation Rate &#x3D; Accumulated Depreciation &#x2F; (Property, Plant &amp; Equipment + Accumulated Depreciation)</p></blockquote><p>SGAI Sales, General, and Administrative Expenses Index</p><blockquote><p>SGAI &#x3D; (Current Period SGA Expenses &#x2F; Current Period Revenue) &#x2F; (Prior Period SGA Expenses &#x2F; Prior Period Revenue)</p></blockquote><p>TATA Total Accruals to Total Assets</p><blockquote><p>TATA &#x3D; Total Accruals &#x2F; Total Assets</p><p>Accruals &#x3D; Operating Income - Cash Flow from Operations</p></blockquote><p>LVGI Leverage Index</p><blockquote><p>LVGI &#x3D; Current Period Leverage Ratio &#x2F; Prior Period Leverage Ratio</p><p>Leverage Ratio &#x3D; Total Liabilities &#x2F; Total Assets</p></blockquote><p>M Range → Likelihood of Financial Fraud<br>M &lt; -2.22                   Low<br>-2.22 &lt;&#x3D; M &lt;&#x3D; -1.78  Medium<br>-1.78 &lt;&#x3D; M                 High</p><h2 id="Stock-Volatility-Prediction"><a href="#Stock-Volatility-Prediction" class="headerlink" title="Stock Volatility Prediction"></a>Stock Volatility Prediction</h2><p>Python implementation of the GARCH model (Generalized Autoregressive Conditional Heteroskedasticity model).</p><p>Calm periods often follow calm periods, and volatile periods often follow volatile periods → volatility clustering.</p><p>Fat tails and high peaks: the probability of extreme events occurring in financial assets is much higher than that in a normal distribution, which is why the Kelly Criterion should be divided by 2.</p><p>Good ideas are stitched together. Models and tools are component parts to be stitched, and you don’t need to understand every component deeply. A chef doesn’t need to know how to manufacture a kitchen knife; they only need the knife to chop and cook.</p><p>Volatility can predict risk, and position sizing levels can be designed based on risk. Trading strategies can also be adjusted according to volatility.</p><h2 id="Performance-Evaluation-Metrics"><a href="#Performance-Evaluation-Metrics" class="headerlink" title="Performance Evaluation Metrics"></a>Performance Evaluation Metrics</h2><p>Profit and Loss Category</p><blockquote><p>Total number of trades, winning trades, losing trades, win rate</p><p>Measures how frequent trading is and how good the win rate is</p></blockquote><ul><li>Total Profit: Sum of all profits</li><li>Total Loss: Sum of all losses</li><li>Net Profit: Total Profit - Total Loss</li><li>Profit Factor: Total Profit &#x2F; -Total Loss</li><li>Average Trade Profit: Net Profit &#x2F; Number of Trades</li><li>Average Win: Total Profit &#x2F; Number of Trades</li><li>Average Loss: Total Loss &#x2F; Number of Trades</li><li>Cumulative Return: Total return rate</li><li>Average Return: Annualized return rate</li></ul><p>Risk Metrics</p><blockquote><p>Measures the degree of strategy risk, as well as how much pain needs to be endured and for how long</p></blockquote><ul><li>Maximum Drawdown: The peak-to-trough decline of assets over a given period</li><li>Maximum Drawdown Duration: The time taken for assets to move from peak to trough</li><li>Volatility: Variance of returns</li></ul><p>Ratios Category</p><blockquote><p>Sharpe Ratio: (Annualized Return - Risk-Free Rate) &#x2F; Standard Deviation of Return<br>Measures how much volatility must be endured to achieve a given return.<br>The higher the return and the lower the volatility, the better—larger values are preferable.<br>You can observe the Sharpe Ratios of master investors.</p></blockquote><ul><li>Sortino Ratio</li><li>Improvements to the Sharpe Ratio</li><li>Calmar Ratio</li><li>Replacing the variance term in the Sharpe Ratio with Maximum Drawdown</li></ul><p>Other Category</p><blockquote><p>Alpha: Excess return rate</p><p>Your return rate - Market index return rate</p></blockquote><ul><li><p>Beta: Correlation between your return rate and the market index return rate</p></li><li><p>SQN: System Quality Number, a metric to evaluate the quality of quantitative strategies</p></li></ul><p>Python library — pyfolio library</p><p>Option Pricing Formulas</p><blockquote><p>Black-Scholes Formula, Binomial Tree Model </p></blockquote><h2 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>Although investing has not increased my wealth for the time being, I have gradually come to understand the underlying principles of how the world operates, and my mindset has been tempered amidst ups and downs. While researching industries, I have also gained a deeper understanding of certain sectors.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;The convenience of the investment market process has fostered irrational investing among people.&lt;/p&gt;
&lt;p&gt;The hellish market from 2022 to 2</summary>
      
    
    
    
    <category term="Quantitative Trading" scheme="https://project256.com/en/categories/quantitative-trading/"/>
    
    
    <category term="Quantitative Trading" scheme="https://project256.com/en/tags/quantitative-trading/"/>
    
  </entry>
  
  <entry>
    <title>Thoughts on AIGC</title>
    <link href="https://project256.com/en/ai-generative-content/AIGC%E7%9A%84%E6%80%9D%E8%80%83/"/>
    <id>https://project256.com/en/ai-generative-content/AIGC%E7%9A%84%E6%80%9D%E8%80%83/</id>
    <published>2024-03-30T08:26:46.000Z</published>
    <updated>2026-08-04T09:39:04.561Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Preface"><a href="#Preface" class="headerlink" title="Preface"></a>Preface</h2><p>I generally use AIGC (AI-Generated Content) to solve problems that exceed my capability or domain knowledge—for example: drawing video covers, solving Android development issues, using an obscure package, writing quantitative trading strategies, analyzing financial data, or drafting legal complaints. However, for problems within my domain of expertise and knowledge, using AIGC actually decreases efficiency.</p><p>Using AIGC for code snippets can boost efficiency to some extent, but when it comes to large-scale projects requiring a big-picture, high-level architectural design, it falls short.</p><p>I believe AIGC and existing human jobs share a complementary relationship rather than a mutually exclusive one. AIGC enables humans to break through the limitations of personal knowledge, helping individuals access or solve cross-domain knowledge and problems at a low cost.</p><p>Although it may seem to save decades of learning, it can hardly replace experts, especially when individuals struggle to clearly describe their own problems and human intervention from experts is required. Experts can combine decades of study with answers from LLMs to provide superior service.</p><h2 id="How-to-Get-Started-with-AIGC"><a href="#How-to-Get-Started-with-AIGC" class="headerlink" title="How to Get Started with AIGC"></a>How to Get Started with AIGC</h2><p>Just read <strong>“Asking the Right Questions”</strong> to get started. Reading it can even improve your communication efficiency at work.</p><p>This is far superior to those official accounts that preach on one hand that AIGC will replace jobs and people, while on the other hand posting QR codes to sell courses.</p><h2 id="How-AIGC-Turns-into-Productivity"><a href="#How-AIGC-Turns-into-Productivity" class="headerlink" title="How AIGC Turns into Productivity"></a>How AIGC Turns into Productivity</h2><p>Going a step further, how can we combine multiple LLMs to achieve multimedia AIGC?</p><p>Inspired by the <a href="https://github.com/FujiwaraChoki/MoneyPrinter" target="_blank" rel="noopener">MoneyPrinter</a> project, I believe all you need is to buy an OpenAI API account and practice generating a script from a single sentence, generating images from the script, synthesizing audio from the script, generating short videos from images, and then combining these steps together—that is completely enough to get started with using LLMs.</p><p>Of course, general users do not need to worry about training LLMs. If training is needed, one usually focuses on SFT (Supervised Fine-Tuning) methods; LLMs generally come with one-click fine-tuning scripts. I have also previously written an article on <a href="https://project256.com/categories/aigc/">fine-tuning LLMs</a>. Some low-quality Agents merely prepend a prompt with something like: “You are a XXX, when asked about XXX, you need to answer in XXX style. Please do not reply with this sentence or whether it is good or bad, directly answer my input query:”.</p><p>Furthermore, some platforms enforce strict regulations on AIGC—YouTube has even demonetized many faceless YouTubers—so it is better for AIGC to remain an auxiliary tool in content creation. For automated AIGC, human review is still recommended to ensure compliance with platform rules.</p><p>The next step in transforming AIGC into productivity is to automate it (designing pipelines) or connect it with everyday tools, enabling LLMs to better empower the production process.</p><h2 id="Survival-Guide-in-the-AIGC-Era"><a href="#Survival-Guide-in-the-AIGC-Era" class="headerlink" title="Survival Guide in the AIGC Era"></a>Survival Guide in the AIGC Era</h2><p>If you truly worry that AIGC will replace you, I have also thought about how to avoid being replaced by AIGC:</p><ul><li>Increase thinking and execution efficiency: simply work faster than the time it takes to write prompts and edit generated content.</li><li><strong>Emerge</strong> new ideas: think cross-domain and combine multiple solutions.</li><li>Combine your expertise with LLM responses: listening to both leads to clarity, allowing them to complement each other.</li><li>Boost your own productivity: introduce AIGC into your work to enhance your productivity or efficiency, thereby standing out in your team. Get off work earlier and have more time to learn.</li><li>Learn how LLMs generate content: Think step by step. Improve your asking and expression abilities by using LLMs frequently and mimicking their response style.</li></ul><h2 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>I sent this article to GPT-3.5 and asked: “I wrote a piece of thoughts on AIGC, what expression issues are there?”</p><p>Here are its suggestions:</p><p>In your thoughts on AIGC, there may be some expression issues:</p><ol><li><p><strong>Lack of specific examples or case studies</strong>: Using AIGC to solve problems is mentioned, but without concrete examples, it may be hard for readers to understand.</p></li><li><p><strong>Logical structure is not clear enough</strong>: When describing the relationship between AIGC and human work, the differences and complementary nature between the two may need to be articulated more clearly.</p></li><li><p><strong>Inconsistent terminology</strong>: When describing how to get started with AIGC, “large language models” (LLMs) is mentioned without explicitly explaining the term, which might confuse readers.</p></li><li><p><strong>Lack of data support</strong>: When discussing how AIGC improves productivity, no specific data or case studies are provided to support conclusions, reducing persuasiveness.</p></li><li><p><strong>Phrasing is not concise enough</strong>: Expressions in some places are somewhat verbose; simplifying the language could improve readability.</p></li></ol><p>By fixing these issues, your thoughts on AIGC can become clearer and easier to understand.</p><p>I won’t modify the original text, but I believe that after accepting and learning from GPT’s suggestions, I will be able to produce better articles in the future.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;Preface&quot;&gt;&lt;a href=&quot;#Preface&quot; class=&quot;headerlink&quot; title=&quot;Preface&quot;&gt;&lt;/a&gt;Preface&lt;/h2&gt;&lt;p&gt;I generally use AIGC (AI-Generated Content) to sol</summary>
      
    
    
    
    <category term="AI and Generative Content" scheme="https://project256.com/en/categories/ai-generative-content/"/>
    
    
    <category term="Large Language Models" scheme="https://project256.com/en/tags/large-language-models/"/>
    
  </entry>
  
  <entry>
    <title>FrontEndOnlineJudge: An Online Judge System for Frontend Developers</title>
    <link href="https://project256.com/en/projects/FrontEndOnlineJudge%E2%80%94%E2%80%94%E9%9D%A2%E5%90%91%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E8%80%85%E7%9A%84%E5%9C%A8%E7%BA%BF%E8%AF%84%E6%B5%8B%E7%B3%BB%E7%BB%9F/"/>
    <id>https://project256.com/en/projects/FrontEndOnlineJudge%E2%80%94%E2%80%94%E9%9D%A2%E5%90%91%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E8%80%85%E7%9A%84%E5%9C%A8%E7%BA%BF%E8%AF%84%E6%B5%8B%E7%B3%BB%E7%BB%9F/</id>
    <published>2024-03-23T02:45:57.000Z</published>
    <updated>2026-08-04T09:39:04.562Z</updated>
    
    <content type="html"><![CDATA[<p>To provide frontend developers with an online evaluation service for frontend programs, this article proposes an online evaluation system for frontend developers that can automatically render, execute, schedule, and evaluate whether the execution results of frontend programs are correct or incorrect in a server environment.</p><p>An online evaluation system is a practice platform that automatically evaluates whether a program’s execution result is correct or incorrect by running test data, providing users with a way to improve their programming skills.</p><p>Most online evaluation systems target backend developers and can only evaluate backend programming languages running in server environments, lacking the functionality to evaluate frontend programming languages. The correctness of frontend programs still requires manual evaluation in a browser environment, and the evaluation results are somewhat subjective.</p><h2 id="Live-Demo-Demo-server-stability-not-guaranteed"><a href="#Live-Demo-Demo-server-stability-not-guaranteed" class="headerlink" title="Live Demo (Demo server stability not guaranteed)"></a>Live Demo <em>(Demo server stability not guaranteed)</em></h2><p><a href="https://oj.project256.com/login" target="_blank" rel="noopener">https://oj.project256.com/login</a></p><p>Account: test</p><p>Password: 123456</p><h2 id="Code-Repositories"><a href="#Code-Repositories" class="headerlink" title="Code Repositories"></a>Code Repositories</h2><p>This system is mainly divided into three sub-projects:</p><p><a href="https://github.com/SUTFutureCoder/FrontEndOJFrontEnd" target="_blank" rel="noopener">Frontend Pages</a> Based on Vue NPM WebSocket</p><p><a href="https://github.com/SUTFutureCoder/FrontEndOJGolang" target="_blank" rel="noopener">Backend API</a> Based on Golang MySQL WebSocket</p><p><a href="https://github.com/SUTFutureCoder/FrontEndOJGolang" target="_blank" rel="noopener">Evaluation Core</a> Based on Golang ChromeDP WebSocket</p><h2 id="How-to-Run"><a href="#How-to-Run" class="headerlink" title="How to Run"></a>How to Run</h2><h3 id="Installation-and-Deployment"><a href="#Installation-and-Deployment" class="headerlink" title="Installation and Deployment"></a>Installation and Deployment</h3><p>Please refer to DockerFile</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">ENV PATH=<span class="string">"/root/anaconda3/bin:<span class="variable">$&#123;PATH&#125;</span>"</span></span><br><span class="line">ENV GOPROXY https://goproxy.io</span><br><span class="line">ENV GO111MODULE on</span><br><span class="line">WORKDIR /root</span><br><span class="line">RUN apt-get install -y wget</span><br><span class="line"><span class="comment"># install anaconda3 pytorch</span></span><br><span class="line">RUN wget http://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/Anaconda3-2021.05-Linux-x86_64.sh</span><br><span class="line">RUN /bin/bash Anaconda3-2021.05-Linux-x86_64.sh -b</span><br><span class="line">RUN conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/</span><br><span class="line">RUN conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/</span><br><span class="line">RUN conda install pytorch -c pytorch</span><br><span class="line">RUN conda install torchvision -c pytorch</span><br><span class="line">RUN conda install ipython</span><br><span class="line">RUN conda install matplotlib pandas seaborn scipy numpy</span><br><span class="line"><span class="comment"># install google-chrome</span></span><br><span class="line">RUN apt-get update</span><br><span class="line">RUN apt-get -f install -y</span><br><span class="line">RUN wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb</span><br><span class="line">RUN apt install -y ./google-chrome-stable_current_amd64.deb</span><br><span class="line"><span class="comment"># run golang</span></span><br><span class="line">COPY . /root/go/FrontEndOJGolang</span><br><span class="line">WORKDIR /root/go/FrontEndOJGolang</span><br><span class="line">RUN go run main.go</span><br></pre></td></tr></table></figure><h3 id="MySQL-Schema"><a href="#MySQL-Schema" class="headerlink" title="MySQL Schema"></a>MySQL Schema</h3><p>Import caroline.sql</p><h3 id="Backend-API-Configuration"><a href="#Backend-API-Configuration" class="headerlink" title="Backend API Configuration"></a>Backend API Configuration</h3><p>conf&#x2F;app.ini</p><h3 id="Evaluation-Core-Configuration"><a href="#Evaluation-Core-Configuration" class="headerlink" title="Evaluation Core Configuration"></a>Evaluation Core Configuration</h3><p>conf&#x2F;judger.ini</p><h3 id="Default-Password"><a href="#Default-Password" class="headerlink" title="Default Password"></a>Default Password</h3><h4 id="Username"><a href="#Username" class="headerlink" title="Username"></a>Username</h4><p>Administrator</p><h4 id="Password"><a href="#Password" class="headerlink" title="Password"></a>Password</h4><p>654321</p><h2 id="Feature-Preview"><a href="#Feature-Preview" class="headerlink" title="Feature Preview"></a>Feature Preview</h2><h3 id="Problem-List"><a href="#Problem-List" class="headerlink" title="Problem List"></a>Problem List</h3><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/image40.png" alt="Problem List" title="">                </div>                <div class="image-caption">Problem List</div>            </figure><h3 id="Problem-Viewing-and-Submission"><a href="#Problem-Viewing-and-Submission" class="headerlink" title="Problem Viewing and Submission"></a>Problem Viewing and Submission</h3><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/image41.png" alt="Problem Viewing and Submission" title="">                </div>                <div class="image-caption">Problem Viewing and Submission</div>            </figure><h3 id="Submission-Results"><a href="#Submission-Results" class="headerlink" title="Submission Results"></a>Submission Results</h3><p>Real-time communication implemented using WebSocket</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/image42.png" alt="Submission Results Interface" title="">                </div>                <div class="image-caption">Submission Results Interface</div>            </figure><h3 id="UI-Similarity-Comparison"><a href="#UI-Similarity-Comparison" class="headerlink" title="UI Similarity Comparison"></a>UI Similarity Comparison</h3><p>UI similarity comparison based on ResNet</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/image43.png" alt="UI Similarity Comparison" title="">                </div>                <div class="image-caption">UI Similarity Comparison</div>            </figure><h3 id="Contest-List"><a href="#Contest-List" class="headerlink" title="Contest List"></a>Contest List</h3><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/image44.png" alt="Contest List Interface" title="">                </div>                <div class="image-caption">Contest List Interface</div>            </figure><h3 id="Contest-Leaderboard"><a href="#Contest-Leaderboard" class="headerlink" title="Contest Leaderboard"></a>Contest Leaderboard</h3><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/image45.png" alt="Contest Leaderboard Interface" title="">                </div>                <div class="image-caption">Contest Leaderboard Interface</div>            </figure><h3 id="Profile-Dashboard"><a href="#Profile-Dashboard" class="headerlink" title="Profile Dashboard"></a>Profile Dashboard</h3><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/image46.png" alt="Profile Dashboard" title="">                </div>                <div class="image-caption">Profile Dashboard</div>            </figure><h3 id="Problem-Configuration-Interface"><a href="#Problem-Configuration-Interface" class="headerlink" title="Problem Configuration Interface"></a>Problem Configuration Interface</h3><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/image48.png" alt="Problem Configuration Interface" title="">                </div>                <div class="image-caption">Problem Configuration Interface</div>            </figure><h2 id="Code-Flow"><a href="#Code-Flow" class="headerlink" title="Code Flow"></a>Code Flow</h2><h3 id="Use-Case-Diagram"><a href="#Use-Case-Diagram" class="headerlink" title="Use Case Diagram"></a>Use Case Diagram</h3><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/image21.png" alt="Use Case Diagram" title="">                </div>                <div class="image-caption">Use Case Diagram</div>            </figure><h3 id="System-Functional-Structure-Diagram"><a href="#System-Functional-Structure-Diagram" class="headerlink" title="System Functional Structure Diagram"></a>System Functional Structure Diagram</h3><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/image22.png" alt="System Functional Structure Diagram" title="">                </div>                <div class="image-caption">System Functional Structure Diagram</div>            </figure><h3 id="E-R-Diagram"><a href="#E-R-Diagram" class="headerlink" title="E-R Diagram"></a>E-R Diagram</h3><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/image25.png" alt="E-R Diagram" title="">                </div>                <div class="image-caption">E-R Diagram</div>            </figure><h3 id="Activity-Diagram"><a href="#Activity-Diagram" class="headerlink" title="Activity Diagram"></a>Activity Diagram</h3><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/image30.png" alt="Core Activity Diagram" title="">                </div>                <div class="image-caption">Core Activity Diagram</div>            </figure><h3 id="Evaluation-Flowchart"><a href="#Evaluation-Flowchart" class="headerlink" title="Evaluation Flowchart"></a>Evaluation Flowchart</h3><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/image31.png" alt="Core Flowchart" title="">                </div>                <div class="image-caption">Core Flowchart</div>            </figure><h3 id="Delayed-Evaluation-Flowchart"><a href="#Delayed-Evaluation-Flowchart" class="headerlink" title="Delayed Evaluation Flowchart"></a>Delayed Evaluation Flowchart</h3><p>When animations or dynamic effects need to be evaluated and require waiting a certain amount of time before execution, the system supports delaying the evaluation for a specified period.</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/image34.png" alt="Delayed Evaluation Core Flowchart" title="">                </div>                <div class="image-caption">Delayed Evaluation Core Flowchart</div>            </figure><h3 id="UI-Similarity-Comparison-Flowchart"><a href="#UI-Similarity-Comparison-Flowchart" class="headerlink" title="UI Similarity Comparison Flowchart"></a>UI Similarity Comparison Flowchart</h3><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://project256.oss-cn-beijing.aliyuncs.com/uPic/image38.png" alt="UI Similarity Comparison Flowchart" title="">                </div>                <div class="image-caption">UI Similarity Comparison Flowchart</div>            </figure><h2 id="Notes"><a href="#Notes" class="headerlink" title="Notes"></a>Notes</h2><h3 id="About-Practical-Results"><a href="#About-Practical-Results" class="headerlink" title="About Practical Results"></a>About Practical Results</h3><p>This system was designed while the author was pursuing a master’s degree. As it was created quite some time ago, performance and functionality are not guaranteed; it is provided for code reference.</p><h3 id="About-the-Name"><a href="#About-the-Name" class="headerlink" title="About the Name"></a>About the Name</h3><p>At the time, the author got obsessed while replaying Portal 2, so the name Caroline was chosen as the project codename, and tests were referred to using “lab”. For more details, please see the code and database schema export files.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;To provide frontend developers with an online evaluation service for frontend programs, this article proposes an online evaluation system</summary>
      
    
    
    
    <category term="Projects" scheme="https://project256.com/en/categories/projects/"/>
    
    
    <category term="Creative Workshop" scheme="https://project256.com/en/tags/creative-workshop/"/>
    
  </entry>
  
  <entry>
    <title>SSE (Server-Sent Events) Practice and Pitfalls</title>
    <link href="https://project256.com/en/java/SSE-Server-Sent-Event-%E5%AE%9E%E8%B7%B5%E4%B8%8E%E9%81%BF%E5%9D%91/"/>
    <id>https://project256.com/en/java/SSE-Server-Sent-Event-%E5%AE%9E%E8%B7%B5%E4%B8%8E%E9%81%BF%E5%9D%91/</id>
    <published>2024-02-23T14:38:05.000Z</published>
    <updated>2026-08-04T09:39:04.562Z</updated>
    
    <content type="html"><![CDATA[<p>I recently built a large language model chat interface, but traditional request methods only transfer data to the frontend after the model completes its entire output, causing users to think the interface or API has frozen.</p><p>Additionally, due to stateless microservices, using WebSockets for long connections requires handling reconnect logic, which introduces extra complexity. SSE supports reconnection after disconnection by default, is based on the HTTP protocol, and is lightweight and simple for transmitting text data.</p><p>I also encountered some pitfalls during implementation; please read the code comments below.</p><h2 id="JAVA"><a href="#JAVA" class="headerlink" title="JAVA"></a>JAVA</h2><p>Issues encountered:</p><ul><li>Nginx caches messages, preventing them from streaming. Nginx caching needs to be disabled to enable direct streaming.</li><li><code>new SseEmitter()</code> disconnects after 30 seconds by default, so <code>new SseEmitter(0L)</code> needs to be specified to allow unlimited message push duration.</li><li>SSE can only transmit single-line data and cannot recognize spaces and line breaks, so spaces and carriage returns need to be replaced with placeholders.</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> SseEmitter <span class="title">handleSseRequest</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</span> </span>&#123;</span><br><span class="line">  <span class="comment">// 关闭Nginx缓存，直接推流</span></span><br><span class="line">    response.setHeader(<span class="string">"Cache-Control"</span>, <span class="string">"no-cache"</span>);</span><br><span class="line">    response.setHeader(<span class="string">"X-Accel-Buffering"</span>, <span class="string">"no"</span>);</span><br><span class="line">    <span class="comment">// 流式事件流响应</span></span><br><span class="line">    response.setContentType(MediaType.TEXT_EVENT_STREAM_VALUE);</span><br><span class="line">  <span class="comment">// 默认30秒断连，改为0则不限制</span></span><br><span class="line">    SseEmitter emitter = <span class="keyword">new</span> SseEmitter(<span class="number">0L</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 每100毫秒推送一个消息，总共30个消息</span></span><br><span class="line">    Flowable.interval(<span class="number">100</span>, TimeUnit.MILLISECONDS)</span><br><span class="line">            .take(<span class="number">30</span>)</span><br><span class="line">            .map(i -&gt; <span class="string">"SSE COUNTER: "</span> + i + <span class="string">"&amp;#92n&amp;#92n"</span>)</span><br><span class="line">      .subscribe(</span><br><span class="line">              data -&gt; &#123;</span><br><span class="line">                  <span class="keyword">try</span> &#123;</span><br><span class="line">                      emitter.send(SseEmitter.event().data(data));</span><br><span class="line">                  &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                      emitter.completeWithError(e);</span><br><span class="line">                  &#125;</span><br><span class="line">              &#125;,</span><br><span class="line">              emitter::completeWithError,</span><br><span class="line">              emitter::complete</span><br><span class="line">    );</span><br><span class="line">    <span class="keyword">return</span> emitter;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="Frontend"><a href="#Frontend" class="headerlink" title="Frontend"></a>Frontend</h2><p>Issues encountered:</p><ul><li>Since EventSource only supports the GET method, values can be passed via fields when establishing a connection.</li><li>SSE can only transmit single-line data and cannot recognize spaces and line breaks, so spaces and carriage returns need to be replaced with placeholders.</li><li>It is recommended to accumulate received data and continuously parse the full data into HTML to achieve real-time parsing like ChatGPT, improving the user experience.</li></ul><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 由于EventSource只支持get方式，可以建立连接时通过字段传值</span></span><br><span class="line"><span class="keyword">let</span> eventSource = <span class="keyword">new</span> EventSource(target + <span class="string">"?content="</span> + <span class="built_in">encodeURIComponent</span>(content));</span><br><span class="line"><span class="keyword">let</span> sseContent = <span class="string">""</span></span><br><span class="line">eventSource.onmessage = <span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> data = event.data</span><br><span class="line"></span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> ssedata = event.data</span><br><span class="line">    <span class="comment">// SSE只能传输单行数据，且无法识别空格和换行，因此需要将空格和回车替换为占位符</span></span><br><span class="line">    ssedata = ssedata.replaceAll(<span class="string">"&amp;#32;"</span>, <span class="string">" "</span>).replaceAll(<span class="string">"&amp;#92n"</span>, <span class="string">"\n"</span>)</span><br><span class="line">    <span class="comment">// 累加接收数据</span></span><br><span class="line">    sseContent += ssedata</span><br><span class="line">    <span class="comment">// 将Markdown数据持续解析为HTML，实现类似ChatGPT的返回实时解析，体感很好</span></span><br><span class="line">    <span class="built_in">document</span>.getElementById(<span class="string">"msg_"</span> + msgidx).innerHTML = convertMarkdownToHtml(sseContent)</span><br><span class="line">  &#125; <span class="keyword">catch</span>(e) &#123;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 锁定div滚动条到界面底端</span></span><br><span class="line">  <span class="keyword">let</span> objDiv = <span class="built_in">document</span>.getElementsByClassName(<span class="string">"lite-chatbox"</span>).item(<span class="number">0</span>);</span><br><span class="line">  objDiv.scrollTop = objDiv.scrollHeight;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">eventSource.onerror = <span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 直接关闭即可，如网路异常则自动重连</span></span><br><span class="line">  eventSource.close()</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;I recently built a large language model chat interface, but traditional request methods only transfer data to the frontend after the mode</summary>
      
    
    
    
    <category term="Java" scheme="https://project256.com/en/categories/java/"/>
    
    
    <category term="Java" scheme="https://project256.com/en/tags/java/"/>
    
  </entry>
  
</feed>
